openscvx 0.4.1.dev119__tar.gz → 0.4.1.dev121__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.4.1.dev119/openscvx.egg-info → openscvx-0.4.1.dev121}/PKG-INFO +1 -1
- openscvx-0.4.1.dev121/examples/abstract/impulsive.py +103 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/plotting_viser.py +81 -0
- openscvx-0.4.1.dev121/examples/spacecraft/hohmann_transfer.py +205 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/_version.py +3 -3
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/augmented_lagrangian.py +28 -15
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/base.py +104 -5
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/penalized_trust_region.py +61 -1
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/__init__.py +7 -1
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/linearize_discretize.py +49 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/byof.py +14 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/lowering.py +54 -5
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/validation.py +44 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/cvxpy_variables.py +6 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/problem.py +2 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/unified.py +199 -36
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/__init__.py +21 -1
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/plotting.py +151 -31
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/__init__.py +4 -8
- openscvx-0.4.1.dev121/openscvx/plotting/viser/orbits.py +85 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/problem.py +42 -2
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/post_processing.py +14 -2
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/propagation.py +37 -6
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/ptr_solver.py +92 -17
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/augmentation.py +26 -12
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/builder.py +51 -6
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/control.py +47 -2
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lower.py +80 -4
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/preprocessing.py +21 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/problem.py +3 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/unified.py +92 -16
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/caching.py +10 -4
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121/openscvx.egg-info}/PKG-INFO +1 -1
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/SOURCES.txt +5 -0
- openscvx-0.4.1.dev121/tests/hohmann_analytical.py +55 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_variable.py +5 -26
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_augmentation.py +15 -15
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_preprocessing.py +194 -2
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_unified.py +33 -0
- openscvx-0.4.1.dev121/tests/test_impulsive.py +210 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/release-drafter.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/_docs.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/branch-name.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/docs.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/lint.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/nightly.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/release-drafter.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/release.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/tests-integration.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.github/workflows/tests-unit.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/.gitignore +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/CONTRIBUTING.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/LICENSE +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/README.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/constraint_reformulation.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/control_parameterization.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/discretization.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/ocp.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/scvx.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/Foundations/time_dilation.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UnderTheHood/lowering_architecture.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UnderTheHood/vectorization_and_vmapping.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/00_introduction.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/01_hello_world_brachistochrone.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/02_drone_racing_constraints.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/03_obstacle_avoidance_vmap.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/04_viewpoint_constraints.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/05_visualization.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/06_logic.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/UsersGuide/07_lie.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/favicon.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ct-scvx_dark.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ct-scvx_light.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ctcs_dark.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/ctcs_light.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/problem_class_dark.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/images/problem_class_light.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/citation.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/examples.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/getting-started.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/index.md +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/docs/javascripts/mathjax.js +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/abstract/brachistochrone.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/arm/three_link_arm.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_conditional.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_disjoint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/car/dubins_car_stljax.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/cinema_vp.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_double_integrator.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp_nodal.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/dr_vp_polytope.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/drone_racing.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo_utils/acl_logo.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/logo_utils/svg_path_utils.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance_nodal.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/drone/obstacle_avoidance_vmap.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/plotting.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/cinema_vp_realtime_base.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/drone_racing_realtime_base.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/base_problems/obstacle_avoidance_realtime_base.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/cinema_vp_realtime.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/drone_racing_realtime.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/dubins_car_realtime.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/realtime/obstacle_avoidance_realtime.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/rocket/3DoF_pdg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/examples/spacecraft/proxops_cw.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/ctlos_cine.gif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/ctlos_dr.gif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/dtlos_cine.gif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/dtlos_dr.gif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/openscvx_logo.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/openscvx_logo_square.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/oscvx_structure_full_dark.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/figures/video_preview.png +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/1-background@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/2-mars@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/3-moon@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/4-sat1@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/5-space@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@1x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@2x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@3x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/images/layers/6-earth@4x.avif +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/javascripts/parallax.js +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/logo.svg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/stylesheets/custom.css +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/assets/stylesheets/parallax.css +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/home.html +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/main.html +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/partials/parallax/hero.html +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/material/overrides/partials/parallax.html +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/mkdocs.yml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/__main__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/constant_proximal_weight.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/optimization_results.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/algorithms/ramp_proximal_weight.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/config.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/discretization/base.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/expert/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/init/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/init/interpolation.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/integrators/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/integrators/runge_kutta.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/loader.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/cvxpy_constraints.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/dynamics.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/jax_constraints.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/lowered/parameters.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/scp_iteration.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/animated.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/plotly_integration.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/primitives.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/scp.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/plotting/viser/server.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/propagation/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/solvers/base.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/constraint_set.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/arithmetic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/expr.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/adjoint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/se3.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/lie/so3.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/spatial.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/state.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/stl.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/time.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/variable.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/expr/vmap.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/hashing.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/_lowerer.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/_registry.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/arithmetic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/control.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/expr.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/cvxpy/state.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/_lowerer.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/_registry.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/arithmetic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/control.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/expr.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/lie.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/spatial.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/state.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/stl.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/lowerers/jax/vmap.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/_registry.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/lie.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/parser.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/spatial.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/stl.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/parser/tokenizer.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/symbolic/sparsity.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/cache.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/printing.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/profiling.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx/utils/utils.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/dependency_links.txt +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/entry_points.txt +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/requires.txt +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/openscvx.egg-info/top_level.txt +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/pyproject.toml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/scripts/gen_example_pages.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/scripts/gen_ref_pages.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/setup.cfg +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/brachistochrone_analytical.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/fixtures/brachistochrone.json +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/fixtures/brachistochrone.yaml +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_arithmetic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_expr.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_lie.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_node_reference.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_parameters.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_scaling.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_spatial.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/expr/test_vmap.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/__init__.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_array.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_constraint.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_lie.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_linalg.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_load.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_logic.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_math.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_parser.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_spatial.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_stl.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_tokenizer.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/parser/test_vmap.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_hashing.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_lower_cvxpy.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_lower_jax.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/symbolic/test_sparsity.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_autotuning.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_brachistochrone.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_cvxpygen_optional.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_discretization.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_examples.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_expert.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_init.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_integrators.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_loader.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_optimization_results.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_plotting.py +0 -0
- {openscvx-0.4.1.dev119 → openscvx-0.4.1.dev121}/tests/test_propagation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openscvx
|
|
3
|
-
Version: 0.4.1.
|
|
3
|
+
Version: 0.4.1.dev121
|
|
4
4
|
Summary: A general Python-based successive convexification implementation which uses a JAX backend.
|
|
5
5
|
Author-email: Chris Hayner and Griffin Norris <haynec@uw.edu>
|
|
6
6
|
License: Apache Software License
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Impulsive control example with mixed continuous/impulsive inputs.
|
|
2
|
+
|
|
3
|
+
This example demonstrates a simple 1D position/velocity system with:
|
|
4
|
+
|
|
5
|
+
- Continuous acceleration control
|
|
6
|
+
- Impulsive delta-v control applied only at selected nodes
|
|
7
|
+
- Time-dilation for minimum-time objective
|
|
8
|
+
- CTCS bounds on states
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
# Add grandparent directory to path to import examples.plotting
|
|
17
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
18
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
19
|
+
sys.path.append(grandparent_dir)
|
|
20
|
+
|
|
21
|
+
import openscvx as ox
|
|
22
|
+
from openscvx import Problem
|
|
23
|
+
from openscvx.plotting import plot_controls, plot_states
|
|
24
|
+
|
|
25
|
+
n = 10
|
|
26
|
+
|
|
27
|
+
p = ox.State("position", shape=(1,))
|
|
28
|
+
p.max = np.array([1.0])
|
|
29
|
+
p.min = np.array([0.0])
|
|
30
|
+
p.initial = np.array([0.0])
|
|
31
|
+
p.final = np.array([1.0])
|
|
32
|
+
p.guess = np.linspace(p.initial, p.final, n).reshape(-1, 1)
|
|
33
|
+
|
|
34
|
+
v = ox.State("velocity", shape=(1,)) # Scalar speed
|
|
35
|
+
v.max = np.array([1.0])
|
|
36
|
+
v.min = np.array([-1.0])
|
|
37
|
+
v.initial = np.array([0.0])
|
|
38
|
+
v.final = np.array([0.0])
|
|
39
|
+
v.guess = np.linspace(v.initial, v.final, n).reshape(-1, 1)
|
|
40
|
+
|
|
41
|
+
dv = ox.Control(
|
|
42
|
+
"delta_v",
|
|
43
|
+
shape=(1,),
|
|
44
|
+
impulsive=True,
|
|
45
|
+
nodes=[0, n - 1],
|
|
46
|
+
)
|
|
47
|
+
dv.max = np.array([0.2])
|
|
48
|
+
dv.min = np.array([-0.2])
|
|
49
|
+
dv.guess = np.linspace(np.array([0]), np.array([0]), n)
|
|
50
|
+
dv.scaling_min = np.array([-0.2])
|
|
51
|
+
dv.scaling_max = np.array([0.2])
|
|
52
|
+
|
|
53
|
+
a = ox.Control("acceleration", shape=(1,))
|
|
54
|
+
a.max = np.array([0.01])
|
|
55
|
+
a.min = np.array([-0.01])
|
|
56
|
+
a.guess = np.linspace(np.array([0]), np.array([0]), n)
|
|
57
|
+
a.scaling_min = np.array([-1])
|
|
58
|
+
a.scaling_max = np.array([1])
|
|
59
|
+
|
|
60
|
+
dynamics = {
|
|
61
|
+
"position": v,
|
|
62
|
+
"velocity": a,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dynamics_discrete = {
|
|
66
|
+
"position": p,
|
|
67
|
+
"velocity": v + ox.Power(ox.Max(ox.Abs(dv), 1e-6), 1.5) * dv,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
states = [p, v]
|
|
71
|
+
controls = [dv, a]
|
|
72
|
+
|
|
73
|
+
constraints = []
|
|
74
|
+
for state in states:
|
|
75
|
+
constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
|
|
76
|
+
|
|
77
|
+
time = ox.Time(initial=0.0, final=ox.Minimize(10.0), min=0.0, max=20.0)
|
|
78
|
+
|
|
79
|
+
problem = Problem(
|
|
80
|
+
dynamics=dynamics,
|
|
81
|
+
dynamics_discrete=dynamics_discrete,
|
|
82
|
+
states=states,
|
|
83
|
+
controls=controls,
|
|
84
|
+
time=time,
|
|
85
|
+
constraints=constraints,
|
|
86
|
+
N=n,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
problem.algorithm.lam_prox = 1e-2 # Weight on the Trust Reigon
|
|
90
|
+
problem.algorithm.lam_vc = 5e1 # Weight on the Trust Reigon
|
|
91
|
+
problem.algorithm.lam_cost = 1e0 # Weight on the Minimal Time Objective
|
|
92
|
+
problem.algorithm.k_max = 100
|
|
93
|
+
|
|
94
|
+
plotting_dict = {}
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
problem.initialize()
|
|
98
|
+
results = problem.solve()
|
|
99
|
+
results = problem.post_process()
|
|
100
|
+
results.update(plotting_dict)
|
|
101
|
+
|
|
102
|
+
plot_states(results).show()
|
|
103
|
+
plot_controls(results).show()
|
|
@@ -21,6 +21,7 @@ from openscvx.plotting.viser import (
|
|
|
21
21
|
add_animated_vector_norm_plot,
|
|
22
22
|
add_animation_controls,
|
|
23
23
|
add_attitude_frame,
|
|
24
|
+
add_circular_orbit,
|
|
24
25
|
add_ellipsoid_obstacles,
|
|
25
26
|
add_gates,
|
|
26
27
|
add_ghost_trajectory,
|
|
@@ -1174,3 +1175,83 @@ def compute_velocity_colors_realtime(vel: np.ndarray, cmap) -> np.ndarray:
|
|
|
1174
1175
|
dtype=np.uint8,
|
|
1175
1176
|
)
|
|
1176
1177
|
return colors
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
def _as_3d(points: np.ndarray) -> np.ndarray:
|
|
1181
|
+
"""Ensure points are shape (..., 3) by appending z=0 when needed."""
|
|
1182
|
+
points = np.asarray(points, dtype=np.float64)
|
|
1183
|
+
if points.ndim == 1:
|
|
1184
|
+
if points.shape[0] == 2:
|
|
1185
|
+
return np.array([points[0], points[1], 0.0], dtype=np.float64)
|
|
1186
|
+
if points.shape[0] == 3:
|
|
1187
|
+
return points
|
|
1188
|
+
raise ValueError(f"Expected 2D or 3D point, got shape {points.shape}")
|
|
1189
|
+
if points.ndim == 2:
|
|
1190
|
+
if points.shape[1] == 2:
|
|
1191
|
+
z = np.zeros((points.shape[0], 1), dtype=np.float64)
|
|
1192
|
+
return np.concatenate([points, z], axis=1)
|
|
1193
|
+
if points.shape[1] == 3:
|
|
1194
|
+
return points
|
|
1195
|
+
raise ValueError(f"Expected points with 2 or 3 columns, got shape {points.shape}")
|
|
1196
|
+
raise ValueError(f"Expected points with ndim 1 or 2, got ndim {points.ndim}")
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def create_hohmann_transfer_server(
|
|
1200
|
+
results: OptimizationResults,
|
|
1201
|
+
*,
|
|
1202
|
+
r1: float,
|
|
1203
|
+
r2: float,
|
|
1204
|
+
position_key: str = "position",
|
|
1205
|
+
velocity_key: str = "velocity",
|
|
1206
|
+
loop_animation: bool = True,
|
|
1207
|
+
show_grid: bool = False,
|
|
1208
|
+
scene_scale: float = 1.0,
|
|
1209
|
+
orbit_n_points: int = 512,
|
|
1210
|
+
orbit_color_inner: tuple[int, int, int] = (120, 180, 255),
|
|
1211
|
+
orbit_color_outer: tuple[int, int, int] = (255, 180, 120),
|
|
1212
|
+
transfer_point_size: float = 0.10,
|
|
1213
|
+
marker_radius: float = 0.6,
|
|
1214
|
+
) -> viser.ViserServer:
|
|
1215
|
+
"""Create an animated viser server for a planar Hohmann transfer.
|
|
1216
|
+
|
|
1217
|
+
Draws two static circular orbit rings (r1, r2) and animates the transfer
|
|
1218
|
+
trajectory stored in ``results.trajectory``.
|
|
1219
|
+
"""
|
|
1220
|
+
pos = results.trajectory.get(position_key)
|
|
1221
|
+
if pos is None:
|
|
1222
|
+
raise KeyError(f"results.trajectory is missing '{position_key}'")
|
|
1223
|
+
pos_3d = _as_3d(pos) / scene_scale
|
|
1224
|
+
|
|
1225
|
+
vel = results.trajectory.get(velocity_key)
|
|
1226
|
+
vel_3d = None if vel is None else _as_3d(vel)
|
|
1227
|
+
colors = compute_velocity_colors(vel_3d, fallback_length=pos_3d.shape[0])
|
|
1228
|
+
|
|
1229
|
+
traj_time = np.asarray(results.trajectory["time"], dtype=np.float64).flatten()
|
|
1230
|
+
|
|
1231
|
+
server = create_server(pos_3d, show_grid=show_grid)
|
|
1232
|
+
|
|
1233
|
+
# Static orbit rings
|
|
1234
|
+
add_circular_orbit(
|
|
1235
|
+
server,
|
|
1236
|
+
r1 / scene_scale,
|
|
1237
|
+
name="inner_orbit",
|
|
1238
|
+
n_points=orbit_n_points,
|
|
1239
|
+
color=orbit_color_inner,
|
|
1240
|
+
line_width=2.5,
|
|
1241
|
+
)
|
|
1242
|
+
add_circular_orbit(
|
|
1243
|
+
server,
|
|
1244
|
+
r2 / scene_scale,
|
|
1245
|
+
name="outer_orbit",
|
|
1246
|
+
n_points=orbit_n_points,
|
|
1247
|
+
color=orbit_color_outer,
|
|
1248
|
+
line_width=2.5,
|
|
1249
|
+
)
|
|
1250
|
+
|
|
1251
|
+
# Static ghost + animated trail + marker
|
|
1252
|
+
add_ghost_trajectory(server, pos_3d, colors, opacity=0.25, point_size=transfer_point_size)
|
|
1253
|
+
_, update_trail = add_animated_trail(server, pos_3d, colors, point_size=transfer_point_size)
|
|
1254
|
+
_, update_marker = add_position_marker(server, pos_3d, radius=marker_radius)
|
|
1255
|
+
|
|
1256
|
+
add_animation_controls(server, traj_time, [update_trail, update_marker], loop=loop_animation)
|
|
1257
|
+
return server
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Hohmann transfer with impulsive delta-v controls (LEO → GEO).
|
|
2
|
+
|
|
3
|
+
This example mirrors the Hohmann transfer calculation from
|
|
4
|
+
[_Orbital Mechanics & Astrodynamics_](https://orbital-mechanics.space/orbital-maneuvers/hohmann-transfer-example.html)
|
|
5
|
+
by Bryan Weber and embeds it in a trajectory optimization problem that uses impulsive
|
|
6
|
+
delta-v controls.
|
|
7
|
+
|
|
8
|
+
We consider a planar, two-body Earth-centered problem:
|
|
9
|
+
|
|
10
|
+
- Initial circular orbit: 250 km altitude LEO
|
|
11
|
+
- Final circular orbit: GEO with 1 sidereal-day period
|
|
12
|
+
- 2D position/velocity states in an inertial frame (km, km/s)
|
|
13
|
+
- Central gravity with mu = 3.986e5 km^3/s^2
|
|
14
|
+
- Two impulsive delta-v maneuvers applied at the initial and final nodes
|
|
15
|
+
- A scalar cost state that accumulates the L2 norm of each impulse and is
|
|
16
|
+
minimized at the final time (approximate minimum-fuel objective)
|
|
17
|
+
|
|
18
|
+
Continuous dynamics between impulses:
|
|
19
|
+
|
|
20
|
+
x_dot = vx
|
|
21
|
+
y_dot = vy
|
|
22
|
+
vx_dot = -mu * x / r^3
|
|
23
|
+
vy_dot = -mu * y / r^3
|
|
24
|
+
|
|
25
|
+
Discrete dynamics at impulsive nodes:
|
|
26
|
+
|
|
27
|
+
- position unchanged
|
|
28
|
+
- velocity += delta_v
|
|
29
|
+
- cost += ||delta_v||
|
|
30
|
+
|
|
31
|
+
The transfer time is fixed to the Hohmann half-period of the transfer ellipse.
|
|
32
|
+
In the __main__ block we also compute and print the analytic Hohmann Δv and
|
|
33
|
+
propellant mass from Weber's example for comparison.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
import numpy as np
|
|
40
|
+
|
|
41
|
+
# Add grandparent directory to path to import examples.plotting
|
|
42
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
43
|
+
grandparent_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
44
|
+
sys.path.append(grandparent_dir)
|
|
45
|
+
|
|
46
|
+
import openscvx as ox
|
|
47
|
+
from examples.plotting_viser import create_hohmann_transfer_server
|
|
48
|
+
from openscvx import Problem
|
|
49
|
+
from openscvx.plotting import plot_controls, plot_scp_iterations, plot_states
|
|
50
|
+
|
|
51
|
+
# Problem configuration (match Weber's LEO → GEO example)
|
|
52
|
+
n = 15
|
|
53
|
+
|
|
54
|
+
mu = 3.986e5 # km^3 / s^2
|
|
55
|
+
R_E = 6378.0 # km
|
|
56
|
+
r_leo = 250.0 + R_E
|
|
57
|
+
|
|
58
|
+
sidereal_day = 86164.0905 # s
|
|
59
|
+
r_cubed = mu * sidereal_day**2 / (4 * np.pi**2)
|
|
60
|
+
r_geo = r_cubed ** (1.0 / 3.0)
|
|
61
|
+
|
|
62
|
+
r1 = r_leo
|
|
63
|
+
r2 = r_geo
|
|
64
|
+
|
|
65
|
+
# Hohmann transfer half-period for the transfer ellipse (fixed final time)
|
|
66
|
+
a_transfer = 0.5 * (r1 + r2)
|
|
67
|
+
T_transfer = np.pi * np.sqrt(a_transfer**3 / mu)
|
|
68
|
+
|
|
69
|
+
# States: planar position, planar velocity, and scalar accumulated cost
|
|
70
|
+
position = ox.State("position", shape=(2,))
|
|
71
|
+
position.initial = np.array([r1, 0.0])
|
|
72
|
+
position.final = np.array([-r2, 0.0]) # Opposite side for half-period transfer
|
|
73
|
+
position.min = np.array([-(1.5 * r2), -(1.5 * r2)])
|
|
74
|
+
position.max = np.array([1.5 * r2, 1.5 * r2])
|
|
75
|
+
|
|
76
|
+
# Initial guess: follow an approximate Hohmann-like arc that never passes
|
|
77
|
+
# through the origin (to avoid r ≈ 0 causing singular dynamics).
|
|
78
|
+
theta_guess = np.linspace(0.0, np.pi, n)
|
|
79
|
+
radius_guess = np.linspace(r1, r2, n)
|
|
80
|
+
position_guess = np.stack(
|
|
81
|
+
[radius_guess * np.cos(theta_guess), radius_guess * np.sin(theta_guess)],
|
|
82
|
+
axis=1,
|
|
83
|
+
)
|
|
84
|
+
position.guess = position_guess
|
|
85
|
+
|
|
86
|
+
velocity = ox.State("velocity", shape=(2,))
|
|
87
|
+
v_c1 = np.sqrt(mu / r1)
|
|
88
|
+
v_c2 = np.sqrt(mu / r2)
|
|
89
|
+
velocity.initial = np.array([0.0, v_c1])
|
|
90
|
+
velocity.final = np.array([0.0, -v_c2])
|
|
91
|
+
velocity_min_mag = -2.0 * max(v_c1, v_c2)
|
|
92
|
+
velocity_max_mag = 2.0 * max(v_c1, v_c2)
|
|
93
|
+
velocity.min = np.array([velocity_min_mag, velocity_min_mag])
|
|
94
|
+
velocity.max = np.array([velocity_max_mag, velocity_max_mag])
|
|
95
|
+
velocity.guess = np.tile(np.array([0.0, (v_c1 + v_c2) / 2.0]), (n, 1))
|
|
96
|
+
|
|
97
|
+
cost = ox.State("cost", shape=(1,))
|
|
98
|
+
cost.initial = np.array([0.0])
|
|
99
|
+
# Minimize final accumulated impulse norm with a loose upper bound
|
|
100
|
+
cost.final = [("minimize", 10.0)]
|
|
101
|
+
cost.min = np.array([0.0])
|
|
102
|
+
cost.max = np.array([10.0])
|
|
103
|
+
cost.guess = np.zeros((n, 1))
|
|
104
|
+
|
|
105
|
+
# Impulsive delta-v control applied only at the first and last nodes
|
|
106
|
+
dv = ox.Control(
|
|
107
|
+
"delta_v",
|
|
108
|
+
shape=(2,),
|
|
109
|
+
impulsive=True,
|
|
110
|
+
nodes=[0, n - 1],
|
|
111
|
+
)
|
|
112
|
+
dv_bound = 5.0 # km/s
|
|
113
|
+
dv.min = np.array([-dv_bound, -dv_bound])
|
|
114
|
+
dv.max = np.array([dv_bound, dv_bound])
|
|
115
|
+
dv.guess = np.zeros((n, 2))
|
|
116
|
+
|
|
117
|
+
states = [position, velocity, cost]
|
|
118
|
+
controls = [dv]
|
|
119
|
+
|
|
120
|
+
# Continuous dynamics: planar two-body gravity with no continuous thrust.
|
|
121
|
+
# We clamp the radius away from zero for robustness in the linearization.
|
|
122
|
+
r = ox.linalg.Norm(position)
|
|
123
|
+
|
|
124
|
+
dynamics = {
|
|
125
|
+
"position": velocity,
|
|
126
|
+
"velocity": ox.Concat(
|
|
127
|
+
-mu * position[0] / r**3,
|
|
128
|
+
-mu * position[1] / r**3,
|
|
129
|
+
),
|
|
130
|
+
"cost": 0.0,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Discrete dynamics at impulsive nodes: update velocity and accumulate cost.
|
|
134
|
+
# Use a small epsilon inside the norm to avoid NaNs in derivatives at dv = 0.
|
|
135
|
+
eps_impulse = 1e-6
|
|
136
|
+
d_impulse = ox.linalg.Norm(dv + eps_impulse)
|
|
137
|
+
|
|
138
|
+
dynamics_discrete = {
|
|
139
|
+
"position": position,
|
|
140
|
+
"velocity": velocity + dv,
|
|
141
|
+
"cost": cost + d_impulse,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Box constraints on all states
|
|
145
|
+
constraints = []
|
|
146
|
+
for state in states:
|
|
147
|
+
constraints.extend([ox.ctcs(state <= state.max), ox.ctcs(state.min <= state)])
|
|
148
|
+
|
|
149
|
+
time = ox.Time(
|
|
150
|
+
initial=0.0,
|
|
151
|
+
final=T_transfer,
|
|
152
|
+
min=0.0,
|
|
153
|
+
max=T_transfer,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
problem = Problem(
|
|
157
|
+
dynamics=dynamics,
|
|
158
|
+
dynamics_discrete=dynamics_discrete,
|
|
159
|
+
states=states,
|
|
160
|
+
controls=controls,
|
|
161
|
+
time=time,
|
|
162
|
+
constraints=constraints,
|
|
163
|
+
N=n,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
problem.discretizer.ode_solver = "Dopri8"
|
|
167
|
+
problem.settings.prp.dt = 10.0
|
|
168
|
+
|
|
169
|
+
plotting_dict = {}
|
|
170
|
+
|
|
171
|
+
if __name__ == "__main__":
|
|
172
|
+
# Analytic Hohmann values from Weber's example for reference
|
|
173
|
+
v_leo = np.sqrt(mu / r_leo)
|
|
174
|
+
v_geo = np.sqrt(mu / r_geo)
|
|
175
|
+
r_p = r_leo
|
|
176
|
+
r_a = r_geo
|
|
177
|
+
h_t = np.sqrt(2 * mu * r_a * r_p / (r_a + r_p))
|
|
178
|
+
v_tp = h_t / r_p
|
|
179
|
+
v_ta = h_t / r_a
|
|
180
|
+
|
|
181
|
+
Delta_v_analytic = abs(v_geo - v_ta) + abs(v_tp - v_leo)
|
|
182
|
+
I_sp = 450.5 # s
|
|
183
|
+
goes_mass = 5192.0 # kg
|
|
184
|
+
Delta_m_analytic = goes_mass * (1.0 - np.exp(-Delta_v_analytic / (I_sp * 9.81e-3)))
|
|
185
|
+
|
|
186
|
+
print(f"Analytic Hohmann Δv ≈ {Delta_v_analytic:.3f} km/s")
|
|
187
|
+
print(f"Analytic propellant mass ≈ {Delta_m_analytic:.3f} kg")
|
|
188
|
+
|
|
189
|
+
problem.initialize()
|
|
190
|
+
results = problem.solve()
|
|
191
|
+
results = problem.post_process()
|
|
192
|
+
results.update(plotting_dict)
|
|
193
|
+
|
|
194
|
+
# Always show standard Plotly diagnostics.
|
|
195
|
+
plot_states(results).show()
|
|
196
|
+
plot_controls(results).show()
|
|
197
|
+
plot_scp_iterations(results).show()
|
|
198
|
+
|
|
199
|
+
server = create_hohmann_transfer_server(
|
|
200
|
+
results,
|
|
201
|
+
r1=r1,
|
|
202
|
+
r2=r2,
|
|
203
|
+
scene_scale=1000.0, # km -> Mm-ish for nicer viewing
|
|
204
|
+
)
|
|
205
|
+
server.sleep_forever()
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.4.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 4, 1, '
|
|
31
|
+
__version__ = version = '0.4.1.dev121'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 4, 1, 'dev121')
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g87edfed70'
|
|
@@ -130,8 +130,15 @@ class AugmentedLagrangian(AutotuningBase):
|
|
|
130
130
|
weights: Normalized initial weights from the algorithm
|
|
131
131
|
"""
|
|
132
132
|
# Calculate nonlinear penalty for current candidate
|
|
133
|
-
|
|
134
|
-
candidate.x_prop
|
|
133
|
+
candidate_x_prop = (
|
|
134
|
+
candidate.x_prop_plus[1:] if candidate.x_prop_plus is not None else candidate.x_prop
|
|
135
|
+
)
|
|
136
|
+
(
|
|
137
|
+
nonlinear_cost,
|
|
138
|
+
nonlinear_penalty,
|
|
139
|
+
nodal_penalty,
|
|
140
|
+
) = self.calculate_nonlinear_penalty(
|
|
141
|
+
candidate_x_prop,
|
|
135
142
|
candidate.x,
|
|
136
143
|
candidate.u,
|
|
137
144
|
state.lam_vc,
|
|
@@ -155,18 +162,24 @@ class AugmentedLagrangian(AutotuningBase):
|
|
|
155
162
|
lam_prox_k = deepcopy(state.lam_prox)
|
|
156
163
|
|
|
157
164
|
if state.k > 1:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
state_x_prop_plus = state.x_prop_plus()
|
|
166
|
+
state_x_prop = (
|
|
167
|
+
state_x_prop_plus[1:] if state_x_prop_plus is not None else state.x_prop()
|
|
168
|
+
)
|
|
169
|
+
(
|
|
170
|
+
prev_nonlinear_cost,
|
|
171
|
+
prev_nonlinear_penalty,
|
|
172
|
+
prev_nodal_penalty,
|
|
173
|
+
) = self.calculate_nonlinear_penalty(
|
|
174
|
+
state_x_prop,
|
|
175
|
+
state.x,
|
|
176
|
+
state.u,
|
|
177
|
+
state.lam_vc,
|
|
178
|
+
state.lam_vb,
|
|
179
|
+
state.lam_cost,
|
|
180
|
+
nodal_constraints,
|
|
181
|
+
params,
|
|
182
|
+
settings,
|
|
170
183
|
)
|
|
171
184
|
|
|
172
185
|
J_nonlin_prev = prev_nonlinear_cost + prev_nonlinear_penalty + prev_nodal_penalty
|
|
@@ -208,7 +221,7 @@ class AugmentedLagrangian(AutotuningBase):
|
|
|
208
221
|
adaptive_state = "Accept Lower"
|
|
209
222
|
|
|
210
223
|
# Update virtual control weight matrix
|
|
211
|
-
nu = (settings.sim.inv_S_x @ abs(candidate.x[1:] -
|
|
224
|
+
nu = (settings.sim.inv_S_x @ abs(candidate.x[1:] - candidate_x_prop).T).T
|
|
212
225
|
|
|
213
226
|
# Vectorized update: use mask to select between two update rules
|
|
214
227
|
mask = nu > self.ep
|
|
@@ -7,7 +7,7 @@ during SCP iterations.
|
|
|
7
7
|
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
|
-
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union
|
|
10
|
+
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
|
|
@@ -166,7 +166,11 @@ class CandidateIterate:
|
|
|
166
166
|
x: Optional[np.ndarray] = None
|
|
167
167
|
u: Optional[np.ndarray] = None
|
|
168
168
|
V: Optional[np.ndarray] = None
|
|
169
|
+
W: Optional[np.ndarray] = None
|
|
169
170
|
x_prop: Optional[np.ndarray] = None
|
|
171
|
+
x_prop_plus: Optional[np.ndarray] = None
|
|
172
|
+
D_d: Optional[np.ndarray] = None
|
|
173
|
+
E_d: Optional[np.ndarray] = None
|
|
170
174
|
VC: Optional[np.ndarray] = None
|
|
171
175
|
TR: Optional[np.ndarray] = None
|
|
172
176
|
lam_vc: Optional[Union[float, np.ndarray]] = None
|
|
@@ -191,9 +195,18 @@ class DiscretizationResult:
|
|
|
191
195
|
A_d: np.ndarray # (N-1, n_x, n_x)
|
|
192
196
|
B_d: np.ndarray # (N-1, n_x, n_u)
|
|
193
197
|
C_d: np.ndarray # (N-1, n_x, n_u)
|
|
198
|
+
x_prop_plus: Optional[np.ndarray] = None # (N, n_x), discrete dynamics on node states
|
|
199
|
+
D_d: Optional[np.ndarray] = None # (N, n_x, n_x), d(x_prop_plus)/d(x_node)
|
|
200
|
+
E_d: Optional[np.ndarray] = None # (N, n_x, n_u), d(x_prop_plus)/d(u_node)
|
|
194
201
|
|
|
195
202
|
@classmethod
|
|
196
|
-
def from_V(
|
|
203
|
+
def from_V(
|
|
204
|
+
cls,
|
|
205
|
+
V: np.ndarray,
|
|
206
|
+
n_x: int,
|
|
207
|
+
n_u: int,
|
|
208
|
+
N: int,
|
|
209
|
+
) -> "DiscretizationResult":
|
|
197
210
|
"""Unpack the final timestep of a raw discretization matrix ``V``."""
|
|
198
211
|
i1, i2 = n_x, n_x + n_x * n_x
|
|
199
212
|
i3, i4 = i2 + n_x * n_u, i2 + 2 * n_x * n_u
|
|
@@ -206,6 +219,37 @@ class DiscretizationResult:
|
|
|
206
219
|
C_d=V_final[:, i3:i4].reshape(N - 1, n_x, n_u),
|
|
207
220
|
)
|
|
208
221
|
|
|
222
|
+
@classmethod
|
|
223
|
+
def from_VW(
|
|
224
|
+
cls,
|
|
225
|
+
V: np.ndarray,
|
|
226
|
+
W: np.ndarray,
|
|
227
|
+
n_x: int,
|
|
228
|
+
n_u: int,
|
|
229
|
+
N: int,
|
|
230
|
+
) -> "DiscretizationResult":
|
|
231
|
+
"""Unpack continuous and impulsive discretization blocks from ``V`` and ``W``."""
|
|
232
|
+
base = cls.from_V(V=V, n_x=n_x, n_u=n_u, N=N)
|
|
233
|
+
|
|
234
|
+
W_arr = np.asarray(W)
|
|
235
|
+
i_w = n_x + n_x * n_x + n_x * n_u
|
|
236
|
+
i1 = n_x
|
|
237
|
+
i2 = i1 + n_x * n_x
|
|
238
|
+
i3 = i2 + n_x * n_u
|
|
239
|
+
|
|
240
|
+
W_final = W_arr[:, -1].reshape(-1, i_w)
|
|
241
|
+
|
|
242
|
+
return cls(
|
|
243
|
+
V=base.V,
|
|
244
|
+
x_prop=base.x_prop,
|
|
245
|
+
A_d=base.A_d,
|
|
246
|
+
B_d=base.B_d,
|
|
247
|
+
C_d=base.C_d,
|
|
248
|
+
x_prop_plus=W_final[:, :i1],
|
|
249
|
+
D_d=W_final[:, i1:i2].reshape(W_final.shape[0], n_x, n_x),
|
|
250
|
+
E_d=W_final[:, i2:i3].reshape(W_final.shape[0], n_x, n_u),
|
|
251
|
+
)
|
|
252
|
+
|
|
209
253
|
|
|
210
254
|
class AutotuningBase(ABC):
|
|
211
255
|
"""Base class for autotuning methods in SCP algorithms.
|
|
@@ -429,9 +473,25 @@ class AlgorithmState:
|
|
|
429
473
|
self.U.append(cand.u)
|
|
430
474
|
|
|
431
475
|
if cand.V is not None:
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
476
|
+
if cand.W is not None:
|
|
477
|
+
self.discretizations.append(
|
|
478
|
+
DiscretizationResult.from_VW(
|
|
479
|
+
cand.V,
|
|
480
|
+
cand.W,
|
|
481
|
+
n_x=self.n_x,
|
|
482
|
+
n_u=self.n_u,
|
|
483
|
+
N=self.N,
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
else:
|
|
487
|
+
self.discretizations.append(
|
|
488
|
+
DiscretizationResult.from_V(
|
|
489
|
+
cand.V,
|
|
490
|
+
n_x=self.n_x,
|
|
491
|
+
n_u=self.n_u,
|
|
492
|
+
N=self.N,
|
|
493
|
+
)
|
|
494
|
+
)
|
|
435
495
|
if cand.VC is not None:
|
|
436
496
|
self.VC_history.append(cand.VC)
|
|
437
497
|
if cand.TR is not None:
|
|
@@ -473,6 +533,24 @@ class AlgorithmState:
|
|
|
473
533
|
DiscretizationResult.from_V(V, n_x=self.n_x, n_u=self.n_u, N=self.N)
|
|
474
534
|
)
|
|
475
535
|
|
|
536
|
+
def add_impulsive_discretization(
|
|
537
|
+
self,
|
|
538
|
+
W: np.ndarray,
|
|
539
|
+
) -> None:
|
|
540
|
+
"""Attach impulsive discretization data to the latest discretization entry."""
|
|
541
|
+
if not self.discretizations:
|
|
542
|
+
raise ValueError(
|
|
543
|
+
"Cannot attach impulsive discretization before adding the base discretization."
|
|
544
|
+
)
|
|
545
|
+
last = self.discretizations[-1]
|
|
546
|
+
self.discretizations[-1] = DiscretizationResult.from_VW(
|
|
547
|
+
V=last.V,
|
|
548
|
+
W=W,
|
|
549
|
+
n_x=self.n_x,
|
|
550
|
+
n_u=self.n_u,
|
|
551
|
+
N=self.N,
|
|
552
|
+
)
|
|
553
|
+
|
|
476
554
|
@property
|
|
477
555
|
def V_history(self) -> List[np.ndarray]:
|
|
478
556
|
"""Backward-compatible view of raw discretization matrices.
|
|
@@ -563,6 +641,24 @@ class AlgorithmState:
|
|
|
563
641
|
return None
|
|
564
642
|
return self.discretizations[index].C_d
|
|
565
643
|
|
|
644
|
+
def x_prop_plus(self, index: int = -1) -> np.ndarray:
|
|
645
|
+
"""Extract discrete dynamics evaluated at x_prop."""
|
|
646
|
+
if not self.discretizations:
|
|
647
|
+
return None
|
|
648
|
+
return self.discretizations[index].x_prop_plus
|
|
649
|
+
|
|
650
|
+
def D_d(self, index: int = -1) -> np.ndarray:
|
|
651
|
+
"""Extract Jacobian of x_prop_plus w.r.t. x_prop."""
|
|
652
|
+
if not self.discretizations:
|
|
653
|
+
return None
|
|
654
|
+
return self.discretizations[index].D_d
|
|
655
|
+
|
|
656
|
+
def E_d(self, index: int = -1) -> np.ndarray:
|
|
657
|
+
"""Extract Jacobian of x_prop_plus w.r.t. discrete controls."""
|
|
658
|
+
if not self.discretizations:
|
|
659
|
+
return None
|
|
660
|
+
return self.discretizations[index].E_d
|
|
661
|
+
|
|
566
662
|
@property
|
|
567
663
|
def lam_prox(self) -> float:
|
|
568
664
|
"""Get current trust region weight.
|
|
@@ -753,6 +849,7 @@ class Algorithm(ABC):
|
|
|
753
849
|
emitter: callable,
|
|
754
850
|
params: dict,
|
|
755
851
|
settings: "Config",
|
|
852
|
+
discretization_solver_impulsive: Optional[Callable] = None,
|
|
756
853
|
) -> None:
|
|
757
854
|
"""Initialize the algorithm and store compiled infrastructure.
|
|
758
855
|
|
|
@@ -767,6 +864,8 @@ class Algorithm(ABC):
|
|
|
767
864
|
emitter: Callback for emitting iteration progress data
|
|
768
865
|
params: Problem parameters dictionary (for warm-start only)
|
|
769
866
|
settings: Configuration object (for warm-start only)
|
|
867
|
+
discretization_solver_impulsive: Optional solver for discrete/impulsive
|
|
868
|
+
dynamics evaluated on ``(x_prop, u_discrete)``
|
|
770
869
|
"""
|
|
771
870
|
raise NotImplementedError
|
|
772
871
|
|