From 6e4c2aa4d286bb43f091ad13820f564f70e4c0d7 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Thu, 17 Jun 2021 16:40:00 -0700 Subject: [PATCH] acados support (#21319) * acados support * add pyextra to python path * x86 libs --- .pre-commit-config.yaml | 3 +- Pipfile | 2 + Pipfile.lock | 407 ++- README.md | 10 +- launch_chffrplus.sh | 2 +- phonelibs/acados/.gitignore | 1 + phonelibs/acados/build.sh | 40 + .../include/acados_c/condensing_interface.h | 84 + .../include/acados_c/dense_qp_interface.h | 94 + .../acados_c/external_function_interface.h | 92 + .../include/acados_c/ocp_nlp_interface.h | 399 +++ .../include/acados_c/ocp_qp_interface.h | 261 ++ .../acados/include/acados_c/sim_interface.h | 140 + .../acados/include/blasfeo/include/blasfeo.h | 52 + .../blasfeo/include/blasfeo_block_size.h | 371 +++ .../include/blasfeo/include/blasfeo_common.h | 236 ++ .../include/blasfeo/include/blasfeo_d_aux.h | 239 ++ .../blasfeo/include/blasfeo_d_aux_ext_dep.h | 145 + .../include/blasfeo_d_aux_ext_dep_ref.h | 84 + .../blasfeo/include/blasfeo_d_aux_old.h | 75 + .../blasfeo/include/blasfeo_d_aux_ref.h | 206 ++ .../blasfeo/include/blasfeo_d_aux_test.h | 226 ++ .../include/blasfeo/include/blasfeo_d_blas.h | 46 + .../blasfeo/include/blasfeo_d_blas_api.h | 161 + .../blasfeo/include/blasfeo_d_blasfeo_api.h | 342 ++ .../include/blasfeo_d_blasfeo_api_ref.h | 147 + .../include/blasfeo_d_blasfeo_ref_api.h | 270 ++ .../blasfeo/include/blasfeo_d_kernel.h | 1048 ++++++ .../blasfeo/include/blasfeo_i_aux_ext_dep.h | 69 + .../include/blasfeo/include/blasfeo_m_aux.h | 57 + .../include/blasfeo/include/blasfeo_memory.h | 62 + .../include/blasfeo/include/blasfeo_naming.h | 77 + .../include/blasfeo_processor_features.h | 88 + .../include/blasfeo/include/blasfeo_s_aux.h | 166 + .../blasfeo/include/blasfeo_s_aux_ext_dep.h | 141 + .../include/blasfeo_s_aux_ext_dep_ref.h | 82 + .../blasfeo/include/blasfeo_s_aux_old.h | 64 + .../blasfeo/include/blasfeo_s_aux_ref.h | 145 + .../blasfeo/include/blasfeo_s_aux_test.h | 177 ++ .../include/blasfeo/include/blasfeo_s_blas.h | 46 + .../blasfeo/include/blasfeo_s_blas_api.h | 117 + .../blasfeo/include/blasfeo_s_blasfeo_api.h | 268 ++ .../include/blasfeo_s_blasfeo_api_ref.h | 135 + .../include/blasfeo_s_blasfeo_ref_api.h | 235 ++ .../blasfeo/include/blasfeo_s_kernel.h | 682 ++++ .../include/blasfeo/include/blasfeo_stdlib.h | 62 + .../include/blasfeo/include/blasfeo_target.h | 73 + .../include/blasfeo/include/blasfeo_timing.h | 114 + .../blasfeo/include/blasfeo_v_aux_ext_dep.h | 83 + .../acados/include/blasfeo/include/d_blas.h | 77 + .../include/blasfeo/include/d_blas_64.h | 73 + .../acados/include/blasfeo/include/s_blas.h | 77 + .../include/blasfeo/include/s_blas_64.h | 73 + .../include/hpipm/include/hpipm_aux_mem.h | 52 + .../include/hpipm/include/hpipm_aux_string.h | 50 + .../include/hpipm/include/hpipm_common.h | 76 + .../include/hpipm/include/hpipm_d_cast_qcqp.h | 71 + .../include/hpipm/include/hpipm_d_cond.h | 135 + .../include/hpipm/include/hpipm_d_cond_aux.h | 92 + .../include/hpipm/include/hpipm_d_cond_qcqp.h | 129 + .../hpipm/include/hpipm_d_core_qp_ipm.h | 101 + .../hpipm/include/hpipm_d_core_qp_ipm_aux.h | 68 + .../hpipm/include/hpipm_d_dense_qcqp.h | 199 ++ .../hpipm/include/hpipm_d_dense_qcqp_dim.h | 98 + .../hpipm/include/hpipm_d_dense_qcqp_ipm.h | 193 ++ .../hpipm/include/hpipm_d_dense_qcqp_res.h | 107 + .../hpipm/include/hpipm_d_dense_qcqp_sol.h | 85 + .../hpipm/include/hpipm_d_dense_qcqp_utils.h | 82 + .../include/hpipm/include/hpipm_d_dense_qp.h | 207 ++ .../hpipm/include/hpipm_d_dense_qp_dim.h | 92 + .../hpipm/include/hpipm_d_dense_qp_ipm.h | 260 ++ .../hpipm/include/hpipm_d_dense_qp_kkt.h | 72 + .../hpipm/include/hpipm_d_dense_qp_res.h | 105 + .../hpipm/include/hpipm_d_dense_qp_sol.h | 94 + .../hpipm/include/hpipm_d_dense_qp_utils.h | 83 + .../include/hpipm/include/hpipm_d_ocp_qcqp.h | 303 ++ .../hpipm/include/hpipm_d_ocp_qcqp_dim.h | 118 + .../hpipm/include/hpipm_d_ocp_qcqp_ipm.h | 190 ++ .../hpipm/include/hpipm_d_ocp_qcqp_res.h | 114 + .../hpipm/include/hpipm_d_ocp_qcqp_sol.h | 114 + .../hpipm/include/hpipm_d_ocp_qcqp_utils.h | 81 + .../include/hpipm/include/hpipm_d_ocp_qp.h | 306 ++ .../hpipm/include/hpipm_d_ocp_qp_dim.h | 142 + .../hpipm/include/hpipm_d_ocp_qp_ipm.h | 250 ++ .../hpipm/include/hpipm_d_ocp_qp_kkt.h | 66 + .../hpipm/include/hpipm_d_ocp_qp_red.h | 117 + .../hpipm/include/hpipm_d_ocp_qp_res.h | 112 + .../hpipm/include/hpipm_d_ocp_qp_sol.h | 128 + .../hpipm/include/hpipm_d_ocp_qp_utils.h | 82 + .../include/hpipm/include/hpipm_d_part_cond.h | 115 + .../hpipm/include/hpipm_d_part_cond_qcqp.h | 106 + .../include/hpipm/include/hpipm_d_sim_erk.h | 122 + .../include/hpipm/include/hpipm_d_sim_rk.h | 71 + .../hpipm/include/hpipm_d_tree_ocp_qcqp.h | 213 ++ .../hpipm/include/hpipm_d_tree_ocp_qcqp_dim.h | 117 + .../hpipm/include/hpipm_d_tree_ocp_qcqp_ipm.h | 191 ++ .../hpipm/include/hpipm_d_tree_ocp_qcqp_res.h | 108 + .../hpipm/include/hpipm_d_tree_ocp_qcqp_sol.h | 99 + .../include/hpipm_d_tree_ocp_qcqp_utils.h | 84 + .../hpipm/include/hpipm_d_tree_ocp_qp.h | 195 ++ .../hpipm/include/hpipm_d_tree_ocp_qp_dim.h | 111 + .../hpipm/include/hpipm_d_tree_ocp_qp_ipm.h | 209 ++ .../hpipm/include/hpipm_d_tree_ocp_qp_kkt.h | 52 + .../hpipm/include/hpipm_d_tree_ocp_qp_res.h | 106 + .../hpipm/include/hpipm_d_tree_ocp_qp_sol.h | 100 + .../hpipm/include/hpipm_d_tree_ocp_qp_utils.h | 83 + .../include/hpipm/include/hpipm_m_dense_qp.h | 68 + .../hpipm/include/hpipm_m_dense_qp_dim.h | 68 + .../include/hpipm/include/hpipm_m_ocp_qp.h | 49 + .../hpipm/include/hpipm_m_ocp_qp_ipm_hard.h | 115 + .../hpipm/include/hpipm_m_ocp_qp_kkt.h | 45 + .../include/hpipm/include/hpipm_s_cast_qcqp.h | 72 + .../include/hpipm/include/hpipm_s_cond.h | 137 + .../include/hpipm/include/hpipm_s_cond_aux.h | 92 + .../include/hpipm/include/hpipm_s_cond_qcqp.h | 130 + .../hpipm/include/hpipm_s_core_qp_ipm.h | 101 + .../hpipm/include/hpipm_s_core_qp_ipm_aux.h | 68 + .../hpipm/include/hpipm_s_dense_qcqp.h | 200 ++ .../hpipm/include/hpipm_s_dense_qcqp_dim.h | 99 + .../hpipm/include/hpipm_s_dense_qcqp_ipm.h | 204 ++ .../hpipm/include/hpipm_s_dense_qcqp_res.h | 108 + .../hpipm/include/hpipm_s_dense_qcqp_sol.h | 86 + .../hpipm/include/hpipm_s_dense_qcqp_utils.h | 83 + .../include/hpipm/include/hpipm_s_dense_qp.h | 207 ++ .../hpipm/include/hpipm_s_dense_qp_dim.h | 94 + .../hpipm/include/hpipm_s_dense_qp_ipm.h | 260 ++ .../hpipm/include/hpipm_s_dense_qp_kkt.h | 72 + .../hpipm/include/hpipm_s_dense_qp_res.h | 106 + .../hpipm/include/hpipm_s_dense_qp_sol.h | 94 + .../hpipm/include/hpipm_s_dense_qp_utils.h | 84 + .../include/hpipm/include/hpipm_s_ocp_qcqp.h | 303 ++ .../hpipm/include/hpipm_s_ocp_qcqp_dim.h | 119 + .../hpipm/include/hpipm_s_ocp_qcqp_ipm.h | 191 ++ .../hpipm/include/hpipm_s_ocp_qcqp_res.h | 115 + .../hpipm/include/hpipm_s_ocp_qcqp_sol.h | 115 + .../hpipm/include/hpipm_s_ocp_qcqp_utils.h | 82 + .../include/hpipm/include/hpipm_s_ocp_qp.h | 306 ++ .../hpipm/include/hpipm_s_ocp_qp_dim.h | 141 + .../hpipm/include/hpipm_s_ocp_qp_ipm.h | 250 ++ .../hpipm/include/hpipm_s_ocp_qp_kkt.h | 66 + .../hpipm/include/hpipm_s_ocp_qp_red.h | 118 + .../hpipm/include/hpipm_s_ocp_qp_res.h | 114 + .../hpipm/include/hpipm_s_ocp_qp_sol.h | 128 + .../hpipm/include/hpipm_s_ocp_qp_utils.h | 83 + .../include/hpipm/include/hpipm_s_part_cond.h | 115 + .../hpipm/include/hpipm_s_part_cond_qcqp.h | 107 + .../include/hpipm/include/hpipm_s_sim_erk.h | 121 + .../include/hpipm/include/hpipm_s_sim_rk.h | 72 + .../hpipm/include/hpipm_s_tree_ocp_qcqp.h | 213 ++ .../hpipm/include/hpipm_s_tree_ocp_qcqp_dim.h | 118 + .../hpipm/include/hpipm_s_tree_ocp_qcqp_ipm.h | 192 ++ .../hpipm/include/hpipm_s_tree_ocp_qcqp_res.h | 109 + .../hpipm/include/hpipm_s_tree_ocp_qcqp_sol.h | 97 + .../include/hpipm_s_tree_ocp_qcqp_utils.h | 85 + .../hpipm/include/hpipm_s_tree_ocp_qp.h | 196 ++ .../hpipm/include/hpipm_s_tree_ocp_qp_dim.h | 111 + .../hpipm/include/hpipm_s_tree_ocp_qp_ipm.h | 208 ++ .../hpipm/include/hpipm_s_tree_ocp_qp_kkt.h | 54 + .../hpipm/include/hpipm_s_tree_ocp_qp_res.h | 107 + .../hpipm/include/hpipm_s_tree_ocp_qp_sol.h | 98 + .../hpipm/include/hpipm_s_tree_ocp_qp_utils.h | 84 + .../hpipm/include/hpipm_scenario_tree.h | 70 + .../include/hpipm/include/hpipm_timing.h | 67 + .../acados/include/hpipm/include/hpipm_tree.h | 76 + phonelibs/acados/include/qpOASES_e/Bounds.h | 543 ++++ .../acados/include/qpOASES_e/Constants.h | 134 + .../include/qpOASES_e/ConstraintProduct.h | 62 + .../acados/include/qpOASES_e/Constraints.h | 535 ++++ phonelibs/acados/include/qpOASES_e/Flipper.h | 129 + .../acados/include/qpOASES_e/Indexlist.h | 221 ++ phonelibs/acados/include/qpOASES_e/Matrices.h | 287 ++ .../include/qpOASES_e/MessageHandling.h | 544 ++++ phonelibs/acados/include/qpOASES_e/Options.h | 153 + phonelibs/acados/include/qpOASES_e/QProblem.h | 2369 ++++++++++++++ .../acados/include/qpOASES_e/QProblemB.h | 1641 ++++++++++ phonelibs/acados/include/qpOASES_e/Types.h | 310 ++ .../acados/include/qpOASES_e/UnitTesting.h | 79 + phonelibs/acados/include/qpOASES_e/Utils.h | 500 +++ .../include/qpOASES_e/extras/OQPinterface.h | 227 ++ phonelibs/acados/x86_64/lib/libacados.so | Bin 0 -> 521704 bytes phonelibs/acados/x86_64/lib/libblasfeo.so | Bin 0 -> 1265064 bytes phonelibs/acados/x86_64/lib/libhpipm.so | Bin 0 -> 1572648 bytes phonelibs/acados/x86_64/lib/libqpOASES_e.so | 1 + .../acados/x86_64/lib/libqpOASES_e.so.3.1 | Bin 0 -> 262824 bytes pyextra/acados_template/.gitignore | 1 + pyextra/acados_template/__init__.py | 43 + pyextra/acados_template/acados_layout.json | 772 +++++ pyextra/acados_template/acados_model.py | 151 + pyextra/acados_template/acados_ocp.py | 2822 +++++++++++++++++ pyextra/acados_template/acados_ocp_solver.py | 1470 +++++++++ pyextra/acados_template/acados_sim.py | 294 ++ .../acados_template/acados_sim_layout.json | 44 + pyextra/acados_template/acados_sim_solver.py | 407 +++ .../c_templates_tera/CPPLINT.cfg | 1 + .../c_templates_tera/Makefile.in | 485 +++ .../c_templates_tera/acados_mex_create.in.c | 383 +++ .../c_templates_tera/acados_mex_free.in.c | 70 + .../c_templates_tera/acados_mex_set.in.c | 570 ++++ .../c_templates_tera/acados_mex_solve.in.c | 59 + .../c_templates_tera/acados_sim_solver.in.c | 501 +++ .../c_templates_tera/acados_sim_solver.in.h | 98 + .../acados_sim_solver_sfun.in.c | 233 ++ .../c_templates_tera/acados_solver.in.c | 2115 ++++++++++++ .../c_templates_tera/acados_solver.in.h | 132 + .../c_templates_tera/acados_solver_sfun.in.c | 728 +++++ .../c_templates_tera/cost_y_0_fun.in.h | 69 + .../c_templates_tera/cost_y_e_fun.in.h | 69 + .../c_templates_tera/cost_y_fun.in.h | 69 + .../c_templates_tera/external_cost.in.h | 74 + .../c_templates_tera/external_cost_0.in.h | 75 + .../c_templates_tera/external_cost_e.in.h | 74 + .../c_templates_tera/h_constraint.in.h | 70 + .../c_templates_tera/h_e_constraint.in.h | 71 + .../c_templates_tera/main.in.c | 181 ++ .../c_templates_tera/main_mex.in.c | 184 ++ .../c_templates_tera/main_sim.in.c | 130 + .../c_templates_tera/make_main_mex.in.m | 105 + .../c_templates_tera/make_mex.in.m | 110 + .../c_templates_tera/make_sfun.in.m | 320 ++ .../c_templates_tera/make_sfun_sim.in.m | 99 + .../c_templates_tera/mex_solver.in.m | 166 + .../c_templates_tera/model.in.h | 209 ++ .../c_templates_tera/phi_constraint.in.h | 55 + .../c_templates_tera/phi_e_constraint.in.h | 21 + .../generate_c_code_constraint.py | 180 ++ .../generate_c_code_discrete_dynamics.py | 99 + .../generate_c_code_explicit_ode.py | 124 + .../generate_c_code_external_cost.py | 110 + .../acados_template/generate_c_code_gnsf.py | 131 + .../generate_c_code_implicit_ode.py | 136 + .../generate_c_code_nls_cost.py | 113 + .../simulink_default_opts.json | 36 + pyextra/acados_template/utils.py | 438 +++ selfdrive/manager/manager.py | 2 + 234 files changed, 44571 insertions(+), 218 deletions(-) create mode 100644 phonelibs/acados/.gitignore create mode 100755 phonelibs/acados/build.sh create mode 100644 phonelibs/acados/include/acados_c/condensing_interface.h create mode 100644 phonelibs/acados/include/acados_c/dense_qp_interface.h create mode 100644 phonelibs/acados/include/acados_c/external_function_interface.h create mode 100644 phonelibs/acados/include/acados_c/ocp_nlp_interface.h create mode 100644 phonelibs/acados/include/acados_c/ocp_qp_interface.h create mode 100644 phonelibs/acados/include/acados_c/sim_interface.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_block_size.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_common.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_old.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_test.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_blas.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_blas_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_ref_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_d_kernel.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_i_aux_ext_dep.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_m_aux.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_memory.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_naming.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_processor_features.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_old.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_test.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_blas.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_blas_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api_ref.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_ref_api.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_s_kernel.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_stdlib.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_target.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_timing.h create mode 100644 phonelibs/acados/include/blasfeo/include/blasfeo_v_aux_ext_dep.h create mode 100644 phonelibs/acados/include/blasfeo/include/d_blas.h create mode 100644 phonelibs/acados/include/blasfeo/include/d_blas_64.h create mode 100644 phonelibs/acados/include/blasfeo/include/s_blas.h create mode 100644 phonelibs/acados/include/blasfeo/include/s_blas_64.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_aux_mem.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_aux_string.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_common.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_cast_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_cond.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_cond_aux.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_cond_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm_aux.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_red.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_part_cond.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_part_cond_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_sim_erk.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_sim_rk.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_ipm_hard.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_cast_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_cond.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_cond_aux.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_cond_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm_aux.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_red.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_part_cond.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_part_cond_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_sim_erk.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_sim_rk.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_dim.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_ipm.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_kkt.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_res.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_sol.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_utils.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_scenario_tree.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_timing.h create mode 100644 phonelibs/acados/include/hpipm/include/hpipm_tree.h create mode 100644 phonelibs/acados/include/qpOASES_e/Bounds.h create mode 100644 phonelibs/acados/include/qpOASES_e/Constants.h create mode 100644 phonelibs/acados/include/qpOASES_e/ConstraintProduct.h create mode 100644 phonelibs/acados/include/qpOASES_e/Constraints.h create mode 100644 phonelibs/acados/include/qpOASES_e/Flipper.h create mode 100644 phonelibs/acados/include/qpOASES_e/Indexlist.h create mode 100644 phonelibs/acados/include/qpOASES_e/Matrices.h create mode 100644 phonelibs/acados/include/qpOASES_e/MessageHandling.h create mode 100644 phonelibs/acados/include/qpOASES_e/Options.h create mode 100644 phonelibs/acados/include/qpOASES_e/QProblem.h create mode 100644 phonelibs/acados/include/qpOASES_e/QProblemB.h create mode 100644 phonelibs/acados/include/qpOASES_e/Types.h create mode 100644 phonelibs/acados/include/qpOASES_e/UnitTesting.h create mode 100644 phonelibs/acados/include/qpOASES_e/Utils.h create mode 100644 phonelibs/acados/include/qpOASES_e/extras/OQPinterface.h create mode 100644 phonelibs/acados/x86_64/lib/libacados.so create mode 100644 phonelibs/acados/x86_64/lib/libblasfeo.so create mode 100644 phonelibs/acados/x86_64/lib/libhpipm.so create mode 120000 phonelibs/acados/x86_64/lib/libqpOASES_e.so create mode 100644 phonelibs/acados/x86_64/lib/libqpOASES_e.so.3.1 create mode 100644 pyextra/acados_template/.gitignore create mode 100644 pyextra/acados_template/__init__.py create mode 100644 pyextra/acados_template/acados_layout.json create mode 100644 pyextra/acados_template/acados_model.py create mode 100644 pyextra/acados_template/acados_ocp.py create mode 100644 pyextra/acados_template/acados_ocp_solver.py create mode 100644 pyextra/acados_template/acados_sim.py create mode 100644 pyextra/acados_template/acados_sim_layout.json create mode 100644 pyextra/acados_template/acados_sim_solver.py create mode 100644 pyextra/acados_template/c_templates_tera/CPPLINT.cfg create mode 100644 pyextra/acados_template/c_templates_tera/Makefile.in create mode 100644 pyextra/acados_template/c_templates_tera/acados_mex_create.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_mex_free.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_mex_set.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h create mode 100644 pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_solver.in.c create mode 100644 pyextra/acados_template/c_templates_tera/acados_solver.in.h create mode 100644 pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c create mode 100644 pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h create mode 100644 pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h create mode 100644 pyextra/acados_template/c_templates_tera/cost_y_fun.in.h create mode 100644 pyextra/acados_template/c_templates_tera/external_cost.in.h create mode 100644 pyextra/acados_template/c_templates_tera/external_cost_0.in.h create mode 100644 pyextra/acados_template/c_templates_tera/external_cost_e.in.h create mode 100644 pyextra/acados_template/c_templates_tera/h_constraint.in.h create mode 100644 pyextra/acados_template/c_templates_tera/h_e_constraint.in.h create mode 100644 pyextra/acados_template/c_templates_tera/main.in.c create mode 100644 pyextra/acados_template/c_templates_tera/main_mex.in.c create mode 100644 pyextra/acados_template/c_templates_tera/main_sim.in.c create mode 100644 pyextra/acados_template/c_templates_tera/make_main_mex.in.m create mode 100644 pyextra/acados_template/c_templates_tera/make_mex.in.m create mode 100644 pyextra/acados_template/c_templates_tera/make_sfun.in.m create mode 100644 pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m create mode 100644 pyextra/acados_template/c_templates_tera/mex_solver.in.m create mode 100644 pyextra/acados_template/c_templates_tera/model.in.h create mode 100644 pyextra/acados_template/c_templates_tera/phi_constraint.in.h create mode 100644 pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h create mode 100644 pyextra/acados_template/generate_c_code_constraint.py create mode 100644 pyextra/acados_template/generate_c_code_discrete_dynamics.py create mode 100644 pyextra/acados_template/generate_c_code_explicit_ode.py create mode 100644 pyextra/acados_template/generate_c_code_external_cost.py create mode 100644 pyextra/acados_template/generate_c_code_gnsf.py create mode 100644 pyextra/acados_template/generate_c_code_implicit_ode.py create mode 100644 pyextra/acados_template/generate_c_code_nls_cost.py create mode 100644 pyextra/acados_template/simulink_default_opts.json create mode 100644 pyextra/acados_template/utils.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e18856d03..6ff50ca719 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,7 @@ repos: rev: v3.4.0 hooks: - id: check-ast + exclude: '^(pyextra)/' - id: check-json - id: check-xml - id: check-yaml @@ -38,7 +39,7 @@ repos: entry: cppcheck language: system types: [c++] - exclude: '^(phonelibs)|(cereal)|(opendbc)|(panda)|(tools)|(selfdrive/modeld/thneed/debug)|(selfdrive/modeld/test)|(selfdrive/camerad/test)/|(installer)' + exclude: '^(phonelibs)|(pyextra)|(cereal)|(opendbc)|(panda)|(tools)|(selfdrive/modeld/thneed/debug)|(selfdrive/modeld/test)|(selfdrive/camerad/test)/|(installer)' args: - --error-exitcode=1 - --language=c++ diff --git a/Pipfile b/Pipfile index 83cc2d346b..c6e921a2c4 100644 --- a/Pipfile +++ b/Pipfile @@ -74,6 +74,8 @@ parameterized = "*" ft4222 = "*" hypothesis = "*" inputs = "*" +casadi = "*" +future-fstrings = "*" # for acados [packages] atomicwrites = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 3e81bcc7a1..fb6660726e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "bcb10219e933221977adebc26dc3df243463dee3065f6e773fdc328d3c337583" + "sha256": "3cc3a94f113f2dda8a55f12941780f3b1275997852ffbcf2c3a89d19de862dfd" }, "pipfile-spec": 6, "requires": { @@ -489,31 +489,31 @@ }, "protobuf": { "hashes": [ - "sha256:0b5a73fa43efda0df0191c162680ec40cef45463fa8ff69fbeaeeddda4c760f5", - "sha256:1e08da38d56b74962d9cb05d86ca5f25d2e5b78f05fd00db7900cad3faa6de00", - "sha256:38b2c6e2204731cfebdb2452ffb7addf0367172b35cff8ccda338ccea9e7c87a", - "sha256:42239d47f213f1ce12ddca62c0c70856efbac09797d715e5d606b3fbc3f16c44", - "sha256:45cc0197e9f9192693c8a4dbcba8c9227a53a2720fc3826dfe791113d9ff5e5e", - "sha256:4da073b6c1a83e4ff1294156844f21343c5094e295c194d8ecc94703c7a6f42a", - "sha256:4e62be28dcaab52c7e8e8e3fb9a7005dec6374e698f3b22d79276d95c13151e5", - "sha256:50c657a54592c1bec7b24521fdbbbd2f7b51325ba23ab505ed03e8ebf3a5aeff", - "sha256:557e16884d7276caf92c1fb188fe6dfbec47891d3507d4982db1e3d89ffd22de", - "sha256:5a3450acf046716e4a4f02a3f7adfb7b86f1b5b3ae392cec759915e79538d40d", - "sha256:6388e7f300010ea7ac77113c7491c5622645d2447fdf701cbfe026b832d728cd", - "sha256:744f5c9a3b9e7538c4c70f2b0e46f86858b684f5d17bf78643a19a6c21c7936f", - "sha256:751d71dc6559dd794d309811d8dcf179d6a128b38a1655ae7137530f33895cb4", - "sha256:816fe5e8b73c29adb13a57e1653da15f24cbb90e86ea92e6f08abe6d8c0cbdb4", - "sha256:89613ec119fdcad992f65d7a5bfe3170c17edc839982d0089fc0c9242fb8e517", - "sha256:993814b0199f22523a227696a8e20d2cd4b9cda60c76d2fb3969ce0c77eb9e0f", - "sha256:9dc01ddc3b195c4538942790c4b195cf17b52d3785a60c1f6f25b794f51e2071", - "sha256:b667ddbb77ff619fdbd18be75445d4448ee68493c9bddd1b4d0b177025e2f6f6", - "sha256:c2038dec269b65683f16057886c09ca7526471793029bdd43259282e1e0fb668", - "sha256:c303ce92c60d069237cfbd41790fde3d00066ca9500fac464454e20c2f12250c", - "sha256:c49e673436e24e925022c090a98905cfe88d056cb5dde67a8e20ae339acd8140", - "sha256:c5b512c1982f8b427a302db094cf79f8f235284014d01b23fba461aa2c1459a0", - "sha256:f3f057ad59cd4d5ea2b1bb88d36c6f009b8043007cf03d96cbd3b9c06859d4fa" - ], - "version": "==3.17.2" + "sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637", + "sha256:145ce0af55c4259ca74993ddab3479c78af064002ec8227beb3d944405123c71", + "sha256:14c1c9377a7ffbeaccd4722ab0aa900091f52b516ad89c4b0c3bb0a4af903ba5", + "sha256:1556a1049ccec58c7855a78d27e5c6e70e95103b32de9142bae0576e9200a1b0", + "sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539", + "sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87", + "sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e", + "sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde", + "sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d", + "sha256:6ce4d8bf0321e7b2d4395e253f8002a1a5ffbcfd7bcc0a6ba46712c07d47d0b4", + "sha256:6d847c59963c03fd7a0cd7c488cadfa10cda4fff34d8bc8cba92935a91b7a037", + "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b", + "sha256:7a4c97961e9e5b03a56f9a6c82742ed55375c4a25f2692b625d4087d02ed31b9", + "sha256:8727ee027157516e2c311f218ebf2260a18088ffb2d29473e82add217d196b1c", + "sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1", + "sha256:9b7a5c1022e0fa0dbde7fd03682d07d14624ad870ae52054849d8960f04bc764", + "sha256:a22b3a0dbac6544dacbafd4c5f6a29e389a50e3b193e2c70dae6bbf7930f651d", + "sha256:a981222367fb4210a10a929ad5983ae93bd5a050a0824fc35d6371c07b78caf6", + "sha256:ab6bb0e270c6c58e7ff4345b3a803cc59dbee19ddf77a4719c5b635f1d547aa8", + "sha256:c56c050a947186ba51de4f94ab441d7f04fcd44c56df6e922369cc2e1a92d683", + "sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47", + "sha256:f0e59430ee953184a703a324b8ec52f571c6c4259d496a19d1cabcdc19dabc62", + "sha256:ffea251f5cd3c0b9b43c7a7a912777e0bc86263436a87c2555242a348817221b" + ], + "version": "==3.17.3" }, "psutil": { "hashes": [ @@ -826,11 +826,11 @@ }, "tqdm": { "hashes": [ - "sha256:736524215c690621b06fc89d0310a49822d75e599fcd0feb7cc742b98d692493", - "sha256:cd5791b5d7c3f2f1819efc81d36eb719a38e0906a7380365c556779f585ea042" + "sha256:24be966933e942be5f074c29755a95b315c69a91f839a29139bf26ffffe2d3fd", + "sha256:aa0c29f03f298951ac6318f7c8ce584e48fa22ec26396e6411e43d038243bdb2" ], "index": "pypi", - "version": "==4.61.0" + "version": "==4.61.1" }, "typing-extensions": { "hashes": [ @@ -857,11 +857,11 @@ }, "websocket-client": { "hashes": [ - "sha256:3e2bf58191d4619b161389a95bdce84ce9e0b24eb8107e7e590db682c2d0ca81", - "sha256:abf306dc6351dcef07f4d40453037e51cc5d9da2ef60d0fc5d0fe3bcda255372" + "sha256:b68e4959d704768fa20e35c9d508c8dc2bbc041fd8d267c0d7345cffe2824568", + "sha256:e5c333bfa9fa739538b652b6f8c8fc2559f1d364243c8a689d7c0e1d41c2e611" ], "index": "pypi", - "version": "==1.0.1" + "version": "==1.1.0" }, "werkzeug": { "hashes": [ @@ -1020,11 +1020,11 @@ }, "azure-cli-core": { "hashes": [ - "sha256:442762a13cb066155ef19aa483721ea81107a32331582d4c18af86cc0b42d2ec", - "sha256:9ceb8574525733171400e16cfdee279945343d27a348324b6798c5eac036681d" + "sha256:679fbaeab0224cb721d27070feaf61510c3628c4af463af518b59e30735335ae", + "sha256:a4bff7b9ff5e9e658208c712f75376035a482a80b5dae808b8c1489c982da58f" ], "index": "pypi", - "version": "==2.24.2" + "version": "==2.25.0" }, "azure-cli-telemetry": { "hashes": [ @@ -1043,11 +1043,11 @@ }, "azure-core": { "hashes": [ - "sha256:2e3ade1b6d79229ad58d4ed8775a07a0c6323a00328ee202964200f299ba929a", - "sha256:f32bb64aabe61f496255c16dd6c555a027da628109460bf27311cee0caf78f96" + "sha256:197917b98fec661c35392e32abec4f690ac2117371a814e25e57c224ce23cf1f", + "sha256:74631dff314fd44419ac6a3a38e8af68418b08a1b6e6793128555db20501dd07" ], "index": "pypi", - "version": "==1.14.0" + "version": "==1.15.0" }, "azure-mgmt-core": { "hashes": [ @@ -1142,19 +1142,63 @@ }, "boto3": { "hashes": [ - "sha256:52025e0af7935cb7036037978de190e41ad7f6716d1de1e3669976e99d084edf", - "sha256:612aa5dc27b87ae1dc695e194f97af7da0fcc9e97aa80d9740732d78ba117119" + "sha256:8e5af9c7ea16ce1c35b7c3220d073dea9735bb1790107820d475462500ae1eff", + "sha256:e61607211816c194dbe2701db48dcddc87cf19372e6f57a9ebe4dfe93dfe177c" ], "index": "pypi", - "version": "==1.17.87" + "version": "==1.17.95" }, "botocore": { "hashes": [ - "sha256:04a5594ae1886233cb15ab636b51aeecf6b5412231f72744405f11a54a8cda58", - "sha256:3dcc84855349073e0cb706e90a9e1180899deded4b8555698fb4a5a5b3357202" + "sha256:240a9ef007292e986a4e11662f9038435d9d4fd242e083db160c86eb5c24af30", + "sha256:dc215f59735a3abde6c66a61f43f10d95bc18754d310da4e2037b3b8c4d8aa2d" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.20.87" + "version": "==1.20.95" + }, + "casadi": { + "hashes": [ + "sha256:09e103bb597d46aa338fc57bc49270068a1f07be35f9494c9f796dea4b801aeb", + "sha256:13277151efc76b221de8ca6b5ab7b8bbdd2b0e139f282866840adf88dfe53bc9", + "sha256:1c451a07b2440c00d552e040b6285b6e79b677d2978212368b28b86f5d267669", + "sha256:2322748a8d5e88750fd2fc0abcdc56cfbad1a8cd538fe0e7d7b6d8ce0cb3fa62", + "sha256:24fbac649ee26572884029dcd0e108b4a2412cad003a84ed915c4e44a94ecae7", + "sha256:253569c85f881a6a8fe5e1c0758858edb1ecb4c3d8bce4aee4b52e5dc59fc091", + "sha256:292e2768280393bad406256e0ef9c30ddcd4867dbd42148b36f9d92a32d9e199", + "sha256:353a79e50aa84ac5e0d9f04bc3b2d78a2cc8edae3b842d757756449682778944", + "sha256:36db4c84d8f3aad328faaeaeaa454a633c95a854d78ea188791b147888379342", + "sha256:3aec6737c282e7fb5be41f6c7d0649e52ce49efb3508f30bada707e809bbbb5f", + "sha256:4086669280b2335d235c664373db46dcd7f6485dba4663ce1944ea01753c5e8b", + "sha256:4143803af909f284400c02f59de4d97e5ba9319de28366215ef55ef261914f9a", + "sha256:473bb86fa64ac9703d74a474514703b4665fa9a384221ced620b5025e64532a7", + "sha256:4932b2b5361013420189dbc8d30e970672d036b37cb382f1c09c3b6cfe651a37", + "sha256:49a8b713f0ff0bbc2f2af2e71c515cdced238786e25ef504f5982618c84c67a7", + "sha256:54d89442058271007ae8573dfa33360bea10e26603545481090b45e8b90c9d10", + "sha256:55df534d003efdd120c4ebfeb6b252c443d273cdc4b97a394eb0268367477795", + "sha256:5de5c3c1381ac303e71fdef75dace34af6e1d50b46ac081051cd209b8b933837", + "sha256:5f6eb8de31735c14ecc777e3ad77b57767b5f2dbea29265909ef696f51e8be92", + "sha256:6192e2ed81c15a7dab2554f5f69b134df8d1a982f8d9f13e57bdef93364d2120", + "sha256:643e48f92eaf65eb82964816bb7e7064ddb8239959210fa6168e8bce6fe6ef94", + "sha256:6ce7ac8a301a145f98d46db0bfd13bc8b3831a5bb92e8054d531a1f233bb4b93", + "sha256:7309a75b27c57f09b00a61815fb38c40da8e62e3004598e55ea1b8f713d96221", + "sha256:77f33cb95be6a49b93d8d6b81f05193676ae09857699cedf8f1a14a4285d077e", + "sha256:7a624d40c7b5ded7916f6cc65998af4585b4557c9ea65dc1e3a6273ebb2313ec", + "sha256:a06c0b96eb9d3bc88c627eec6e465726934ca0394347dc33efc742b8c91db83d", + "sha256:a4ce51e988570160af9ccfbbb1b9679546cbb1865d3a74ef0276f37fd94d91d9", + "sha256:ab6a600a9b2ea27453d56fd4464ad0db0ae69f5cea42595fcbdaabcd40396440", + "sha256:ab85c7cf772ba54f2718ebe366b836fffff868443f7c0c02389ed0a288cbde1f", + "sha256:ac45b91616e9b8afbe266ca08e80770b28e9e6d7a5852e3677fb37e42bde2047", + "sha256:adf20c34ba2cec1840a026023d93cc6d9b3581dfda6a044f434fc75b50c9a2ce", + "sha256:bd94048388b602fc30fdac2fecb986c034110ed8d2d17af7fd13b0de45c58bd7", + "sha256:c3440c90c31b61ae1df82f6c784643393f723354dc08013f9d5cedf25507c67c", + "sha256:cd630a2e6ec6df6a4977af63080fa8d63a0053ff8c06ea0200959b47ae75201c", + "sha256:d4e49cb46404cef61f83b30bb20ec9597c50ae7f55cfd6b89c17facc74675437", + "sha256:ec26244f9d9047f1bb401f1b86ff4775e1ddf638f4b4992bbc362a27a6f56673", + "sha256:f08a99e98b0a15083f06b1e221f064a29b3ed9e20617dc55aa8e823f2f732ace", + "sha256:fbf39dcd63f1d3b63c300fce59b7ea678bd5ea1d014e1e090a5226600a4132cb" + ], + "index": "pypi", + "version": "==3.5.5" }, "certifi": { "hashes": [ @@ -1469,13 +1513,21 @@ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.18.2" }, + "future-fstrings": { + "hashes": [ + "sha256:6cf41cbe97c398ab5a81168ce0dbb8ad95862d3caf23c21e4430627b90844089", + "sha256:90e49598b553d8746c4dc7d9442e0359d038c3039d802c91c0a55505da318c63" + ], + "index": "pypi", + "version": "==1.2.0" + }, "geoalchemy2": { "hashes": [ - "sha256:567e105e6b0d5fd94a5a869d358b0c47956570bb3f3d19e98086344b1abefed8", - "sha256:56dff13365ac2ea5ab828a697e240984f7ac448a32a3feba25356b4aa8e24d34" + "sha256:5e54d979d49574e176b5eef6a9e7ab0d217a9f1098071dd578dbddc4b1f7ad9d", + "sha256:c32023bc2fb8fbb136f00a0e9c2feba21f3e1040af0f619c888661f6ee72dd28" ], "index": "pypi", - "version": "==0.8.5" + "version": "==0.9.0" }, "git-pylint-commit-hook": { "hashes": [ @@ -1572,27 +1624,27 @@ }, "humanfriendly": { "hashes": [ - "sha256:066562956639ab21ff2676d1fda0b5987e985c534fc76700a19bd54bcb81121d", - "sha256:d5c731705114b9ad673754f3317d9fa4c23212f36b29bdc4272a892eafc9bc72" + "sha256:332da98c24cc150efcc91b5508b19115209272bfdf4b0764a56795932f854271", + "sha256:f7dba53ac7935fd0b4a2fc9a29e316ddd9ea135fb3052d3d0279d10c18ff9c48" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==9.1" + "version": "==9.2" }, "hypothesis": { "hashes": [ - "sha256:cca0e54d769214849755f7a125a2f759615e0386844eb42bd30ca2633d249b61", - "sha256:eeb8311d1477de4974ed3dba5c8e758023c17bb4c804f2e5d5cc4f9a065cbeba" + "sha256:27aa2af763af06b8b61ce65c09626cf1da6d3a6ff155900f3c581837b453313a", + "sha256:9bdee01ae260329b16117e9b0229a839b4a77747a985922653f595bd2a6a541a" ], "index": "pypi", - "version": "==6.13.12" + "version": "==6.14.0" }, "identify": { "hashes": [ - "sha256:3a74cf41f1a0bde21e2f196a1727a608629339d9897226bad07b22e0bbde6faa", - "sha256:7d4a73d58869a484d4ba8892793c80e30aa699e77707f26c1049486ef9be415f" + "sha256:18d0c531ee3dbc112fa6181f34faa179de3f57ea57ae2899754f16a7e0ff6421", + "sha256:5b41f71471bc738e7b586308c3fca172f78940195cb3bf6734c1e66fdac49306" ], "markers": "python_full_version >= '3.6.1'", - "version": "==2.2.8" + "version": "==2.2.10" }, "idna": { "hashes": [ @@ -1648,13 +1700,6 @@ ], "version": "==7.6.3" }, - "isodate": { - "hashes": [ - "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8", - "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81" - ], - "version": "==0.6.0" - }, "isort": { "hashes": [ "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6", @@ -1969,20 +2014,6 @@ "index": "pypi", "version": "==0.5.6" }, - "msrest": { - "hashes": [ - "sha256:72661bc7bedc2dc2040e8f170b6e9ef226ee6d3892e01affd4d26b06474d68d8", - "sha256:c840511c845330e96886011a236440fafc2c9aff7b2df9c0a92041ee2dee3782" - ], - "version": "==0.6.21" - }, - "msrestazure": { - "hashes": [ - "sha256:3de50f56147ef529b31e099a982496690468ecef33f0544cb0fa0cfe1e1de5b9", - "sha256:a06f0dabc9a6f5efe3b6add4bd8fb623aeadacf816b7a35b0f89107e0544d189" - ], - "version": "==0.6.4" - }, "multidict": { "hashes": [ "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a", @@ -2028,31 +2059,32 @@ }, "mypy": { "hashes": [ - "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e", - "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064", - "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c", - "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4", - "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97", - "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df", - "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8", - "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a", - "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56", - "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7", - "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6", - "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5", - "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a", - "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521", - "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564", - "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49", - "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66", - "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a", - "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119", - "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506", - "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c", - "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb" - ], - "index": "pypi", - "version": "==0.812" + "sha256:0190fb77e93ce971954c9e54ea61de2802065174e5e990c9d4c1d0f54fbeeca2", + "sha256:0756529da2dd4d53d26096b7969ce0a47997123261a5432b48cc6848a2cb0bd4", + "sha256:2f9fedc1f186697fda191e634ac1d02f03d4c260212ccb018fabbb6d4b03eee8", + "sha256:353aac2ce41ddeaf7599f1c73fed2b75750bef3b44b6ad12985a991bc002a0da", + "sha256:3f12705eabdd274b98f676e3e5a89f247ea86dc1af48a2d5a2b080abac4e1243", + "sha256:4efc67b9b3e2fddbe395700f91d5b8deb5980bfaaccb77b306310bd0b9e002eb", + "sha256:517e7528d1be7e187a5db7f0a3e479747307c1b897d9706b1c662014faba3116", + "sha256:68a098c104ae2b75e946b107ef69dd8398d54cb52ad57580dfb9fc78f7f997f0", + "sha256:746e0b0101b8efec34902810047f26a8c80e1efbb4fc554956d848c05ef85d76", + "sha256:8be7bbd091886bde9fcafed8dd089a766fa76eb223135fe5c9e9798f78023a20", + "sha256:9236c21194fde5df1b4d8ebc2ef2c1f2a5dc7f18bcbea54274937cae2e20a01c", + "sha256:9ef5355eaaf7a23ab157c21a44c614365238a7bdb3552ec3b80c393697d974e1", + "sha256:9f1d74eeb3f58c7bd3f3f92b8f63cb1678466a55e2c4612bf36909105d0724ab", + "sha256:a26d0e53e90815c765f91966442775cf03b8a7514a4e960de7b5320208b07269", + "sha256:ae94c31bb556ddb2310e4f913b706696ccbd43c62d3331cd3511caef466871d2", + "sha256:b5ba1f0d5f9087e03bf5958c28d421a03a4c1ad260bf81556195dffeccd979c4", + "sha256:b5dfcd22c6bab08dfeded8d5b44bdcb68c6f1ab261861e35c470b89074f78a70", + "sha256:cd01c599cf9f897b6b6c6b5d8b182557fb7d99326bcdf5d449a0fbbb4ccee4b9", + "sha256:e89880168c67cf4fde4506b80ee42f1537ad66ad366c101d388b3fd7d7ce2afd", + "sha256:ebe2bc9cb638475f5d39068d2dbe8ae1d605bb8d8d3ff281c695df1670ab3987", + "sha256:f89bfda7f0f66b789792ab64ce0978e4a991a0e4dd6197349d0767b0f1095b21", + "sha256:fc4d63da57ef0e8cd4ab45131f3fe5c286ce7dd7f032650d0fbc239c6190e167", + "sha256:fd634bc17b1e2d6ce716f0e43446d0d61cdadb1efcad5c56ca211c22b246ebc8" + ], + "index": "pypi", + "version": "==0.902" }, "mypy-extensions": { "hashes": [ @@ -2146,35 +2178,31 @@ "index": "pypi", "version": "==1.20.3" }, - "oauthlib": { - "hashes": [ - "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc", - "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3" - ], - "markers": "python_version >= '3.6'", - "version": "==3.1.1" - }, "opencv-python": { "hashes": [ - "sha256:1a9d6dd0b1d5cb6031c38b6acf2043b2ec1717093b39592c2a0b98f5b31c8d3e", - "sha256:2cc96b8a9011fa3451d71699069a6a8c3578d91d0158c559def997a1a049ae0c", - "sha256:31d6a413d459a18ef280d88e8d9257cc5e5009657022c88bb88694a94ccce3ba", - "sha256:35d4fccd1e339df1e38eb1938af36c1972ab818684a747160403c1562008d867", - "sha256:642ff6fe8d0a8297e248bf21676172381afbda767cd15ad51ab9d10201d99078", - "sha256:68f1baa789a1ae92642ad436d29088051471b2e5cea40705cb8c9f8c9fc3c050", - "sha256:71adc9f528d54fb41bfe275d7038ed4c9c5f1a4468419d4cac12dde6e938f3b8", - "sha256:72f4e4f28169ecb9282570c4a86e992a762ea35ff67b19c05aab2c84eee212ca", - "sha256:7e8e31b0e206c3567cb96408eb0e27314bb1526eb072f9e509db77af0262ecaf", - "sha256:950242774a367efc90e3bed89e6e2fab7ff2867e2eafa440e4775b1ae57ac9f8", - "sha256:a7aa72195cfd325a51fba287fadc61166367a3f89c57e7fe3e041132e192ac06", - "sha256:b236001a5bacd18622db037e581521b38cffae070f345e05a7dd837dc3b7f2ef", - "sha256:c8ee913a45aec73c4bb14c6c72c3c6ddbb0a1dd3ca45338b68bf169220396941", - "sha256:cd231e073579192a8e4c88739e263de23bdaee0be7d85daae3afbda77e477da7", - "sha256:dfa168f827561c52f746d5d80e3b10f31d6428bf6f72652765139129f189110b", - "sha256:fd2e9e70ce30bdfdba6beeffeab117b760e6390e6b606e4b9f4ff03ccd2d7d1f" - ], - "index": "pypi", - "version": "==4.5.2.52" + "sha256:0118a086fad8d77acdf46ac68df49d4167fbb85420f8bcf2615d7b74fc03aae0", + "sha256:050227e5728ea8316ec114aca8f43d56253cbb1c50983e3b136a988254a83118", + "sha256:08327a38564786bf73e387736f080e8ad4c110b394ca4af2ecec8277b305bf44", + "sha256:0a3aef70b7c53bbd22ade86a4318b8a2ad98d3c3ed3d0c315f18bf1a2d868709", + "sha256:10325c3fd571e33a11eb5f0e5d265d73baef22dbb34c977f28df7e22de47b0bc", + "sha256:2436b71346d1eed423577fac8cd3aa9c0832ea97452444dc7f856b2f09600dba", + "sha256:4b8814d3f0cf01e8b8624125f7dcfb095893abcc04083cb4968fa1629bc81161", + "sha256:4e6c2d8320168a4f76822fbb76df3b18688ac5e068d49ac38a4ce39af0f8e1a6", + "sha256:6b2573c6367ec0052b37e375d18638a885dd7a10a5ef8dd726b391969c227f23", + "sha256:6e2070e35f2aaca3d1259093c786d4e373004b36d89a94e81943247c6ed3d4e1", + "sha256:89a2b45429bf945988a17b0404431d9d8fdc9e04fb2450b56fa01f6f9477101d", + "sha256:8cf81f53ac5ad900ca443a8252c4e0bc1256f1c2cb2d8459df2ba1ac014dfa36", + "sha256:9680ab256ab31bdafd74f6cf55eb570e5629b5604d50fd69dd1bd2a8124f0611", + "sha256:a8020cc6145c6934192189058743a55189750df6dff894396edb8b35a380cc48", + "sha256:b3bef3f2a2ab3c201784d12ec6b5c9e61c920c15b6854d8d2f62fd019e3df846", + "sha256:b724a96eeb88842bd2371b1ffe2da73b6295063ba5c029aa34139d25b8315a3f", + "sha256:c446555cbbc4f5e809f9c15ac1b6200024032d9859f5ac5a2ca7669d09e4c91c", + "sha256:d9004e2cc90bb2862cdc1d062fac5163d3def55b200081d4520d3e90b4c7197b", + "sha256:ef3102b70aa59ab3fed69df30465c1b7587d681e963dfff5146de233c75df7ba", + "sha256:f12f39c1e5001e1c00df5873e3eee6f0232b7723a60b7ef438b1e23f1341df0e" + ], + "index": "pypi", + "version": "==4.5.2.54" }, "osmium": { "hashes": [ @@ -2819,14 +2847,6 @@ "index": "pypi", "version": "==2.25.1" }, - "requests-oauthlib": { - "hashes": [ - "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", - "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a", - "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc" - ], - "version": "==1.3.0" - }, "reverse-geocoder": { "hashes": [ "sha256:2a2e781b5f69376d922b78fe8978f1350c84fce0ddb07e02c834ecf98b57c75c" @@ -3008,39 +3028,39 @@ }, "sqlalchemy": { "hashes": [ - "sha256:196fb6bb2733834e506c925d7532f8eabad9d2304deef738a40846e54c31e236", - "sha256:1dd77acbc19bee9c0ba858ff5e4e5d5c60895495c83b4df9bcdf4ad5e9b74f21", - "sha256:216ff28fe803885ceb5b131dcee6507d28d255808dd5bcffcb3b5fa75be2e102", - "sha256:461a4ea803ce0834822f372617a68ac97f9fa1281f2a984624554c651d7c3ae1", - "sha256:4b09191ed22af149c07a880f309b7740f3f782ff13325bae5c6168a6aa57e715", - "sha256:4c5e20666b33b03bf7f14953f0deb93007bf8c1342e985bd7c7cf25f46fac579", - "sha256:4d93b62e98248e3e1ac1e91c2e6ee1e7316f704be1f734338b350b6951e6c175", - "sha256:5732858e56d32fa7e02468f4fd2d8f01ddf709e5b93d035c637762890f8ed8b6", - "sha256:58c02d1771bb0e61bc9ced8f3b36b5714d9ece8fd4bdbe2a44a892574c3bbc3c", - "sha256:651cdb3adcee13624ba22d5ff3e96f91e16a115d2ca489ddc16a8e4c217e8509", - "sha256:6fe1c8dc26bc0005439cb78ebc78772a22cccc773f5a0e67cb3002d791f53f0f", - "sha256:7222f3236c280fab3a2d76f903b493171f0ffc29667538cc388a5d5dd0216a88", - "sha256:7dc3d3285fb682316d580d84e6e0840fdd8ffdc05cb696db74b9dd746c729908", - "sha256:7e45043fe11d503e1c3f9dcf5b42f92d122a814237cd9af68a11dae46ecfcae1", - "sha256:7eb55d5583076c03aaf1510473fad2a61288490809049cb31028af56af7068ee", - "sha256:82922a320d38d7d6aa3a8130523ec7e8c70fa95f7ca7d0fd6ec114b626e4b10b", - "sha256:8e133e2551fa99c75849848a4ac08efb79930561eb629dd7d2dc9b7ee05256e6", - "sha256:949ac299903d2ed8419086f81847381184e2264f3431a33af4679546dcc87f01", - "sha256:a2d225c8863a76d15468896dc5af36f1e196b403eb9c7e0151e77ffab9e7df57", - "sha256:a5f00a2be7d777119e15ccfb5ba0b2a92e8a193959281089d79821a001095f80", - "sha256:b0ad951a6e590bbcfbfeadc5748ef5ec8ede505a8119a71b235f7481cc08371c", - "sha256:b59b2c0a3b1d93027f6b6b8379a50c354483fe1ebe796c6740e157bb2e06d39a", - "sha256:bc89e37c359dcd4d75b744e5e81af128ba678aa2ecea4be957e80e6e958a1612", - "sha256:bde055c019e6e449ebc4ec61abd3e08690abeb028c7ada2a3b95d8e352b7b514", - "sha256:c367ed95d41df584f412a9419b5ece85b0d6c2a08a51ae13ae47ef74ff9a9349", - "sha256:dde05ae0987e43ec84e64d6722ce66305eda2a5e2b7d6fda004b37aabdfbb909", - "sha256:ee6e7ca09ff274c55d19a1e15ee6f884fa0230c0d9b8d22a456e249d08dee5bf", - "sha256:f1c68f7bd4a57ffdb85eab489362828dddf6cd565a4c18eda4c446c1d5d3059d", - "sha256:f63e1f531a8bf52184e2afb53648511f3f8534decb7575b483a583d3cd8d13ed", - "sha256:fdad4a33140b77df61d456922b7974c1f1bb2c35238f6809f078003a620c4734" - ], - "index": "pypi", - "version": "==1.4.17" + "sha256:0653d444d52f2b9a0cba1ea5cd0fc64e616ee3838ee86c1863781b2a8670fc0c", + "sha256:146af9e67d0f821b28779d602372e65d019db01532d8f7101e91202d447c14ec", + "sha256:2129d33b54da4d4771868a3639a07f461adc5887dbd9e0a80dbf560272245525", + "sha256:284b6df04bc30e886998e0fdbd700ef9ffb83bcb484ffc54d4084959240dce91", + "sha256:3690fc0fc671419debdae9b33df1434ac9253155fd76d0f66a01f7b459d56ee6", + "sha256:3a6afb7a55374329601c8fcad277f0a47793386255764431c8f6a231a6947ee9", + "sha256:45bbb935b305e381bcb542bf4d952232282ba76881e3458105e4733ba0976060", + "sha256:495cce8174c670f1d885e2259d710b0120888db2169ea14fc32d1f72e7950642", + "sha256:4cdc91bb3ee5b10e24ec59303131b791f3f82caa4dd8b36064d1918b0f4d0de4", + "sha256:4f375c52fed5f2ecd06be18756f121b3167a1fdc4543d877961fba04b1713214", + "sha256:56958dd833145f1aa75f8987dfe0cf6f149e93aa31967b7004d4eb9cb579fefc", + "sha256:5b827d3d1d982b38d2bab551edf9893c4734b5db9b852b28d3bc809ea7e179f6", + "sha256:5c62fff70348e3f8e4392540d31f3b8c251dc8eb830173692e5d61896d4309d6", + "sha256:5d4b2c23d20acf631456e645227cef014e7f84a111118d530cfa1d6053fd05a9", + "sha256:60cfe1fb59a34569816907cb25bb256c9490824679c46777377bcc01f6813a81", + "sha256:664c6cc84a5d2bad2a4a3984d146b6201b850ba0a7125b2fcd29ca06cddac4b1", + "sha256:70674f2ff315a74061da7af1225770578d23f4f6f74dd2e1964493abd8d804bc", + "sha256:77549e5ae996de50ad9f69f863c91daf04842b14233e133335b900b152bffb07", + "sha256:8924d552decf1a50d57dca4984ebd0778a55ca2cb1c0ef16df8c1fed405ff290", + "sha256:93394d68f02ecbf8c0a4355b6452793000ce0ee7aef79d2c85b491da25a88af7", + "sha256:9a62b06ad450386a2e671d0bcc5cd430690b77a5cd41c54ede4e4bf46d7a4978", + "sha256:c824d14b52000597dfcced0a4e480fd8664b09fed606e746a2c67fe5fbe8dfd9", + "sha256:cc474d0c40cef94d9b68980155d686d5ad43a9ca0834a8729052d3585f289d57", + "sha256:d25210f5f1a6b7b6b357d8fa199fc1d5be828c67cc1af517600c02e5b2727e4c", + "sha256:d76abceeb6f7c564fdbc304b1ce17ec59664ca7ed0fe6dbc6fc6a960c91370e3", + "sha256:e2aa39fdf5bff1c325a8648ac1957a0320c66763a3fa5f0f4a02457b2afcf372", + "sha256:eba098a4962e1ab0d446c814ae67e30da82c446b382cf718306cc90d4e2ad85f", + "sha256:ee3428f6100ff2b07e7ecec6357d865a4d604c801760094883587ecdbf8a3533", + "sha256:f3357948fa439eb5c7241a8856738605d7ab9d9f276ca5c5cc3220455a5f8e6c", + "sha256:ffb18eb56546aa66640fef831e5d0fe1a8dfbf11cdf5b00803826a01dbbbf3b1" + ], + "index": "pypi", + "version": "==1.4.18" }, "subprocess32": { "hashes": [ @@ -3068,11 +3088,11 @@ }, "terminado": { "hashes": [ - "sha256:048ce7b271ad1f94c48130844af1de163e54913b919f8c268c89b36a6d468d7c", - "sha256:46fd07c9dc7db7321922270d544a1f18eaa7a02fd6cd4438314f27a687cabbea" + "sha256:89d5dac2f4e2b39758a0ff9a3b643707c95a020a6df36e70583b88297cd59cbe", + "sha256:c89ace5bffd0e7268bdcf22526830eb787fd146ff9d78691a0528386f92b9ae3" ], "markers": "python_version >= '3.6'", - "version": "==0.10.0" + "version": "==0.10.1" }, "testpath": { "hashes": [ @@ -3084,11 +3104,11 @@ }, "tifffile": { "hashes": [ - "sha256:1cfc55f5b728e200142580a7bf108b72775c4097d007b4111876559fa1fb7432", - "sha256:55aa8baad38e1567c9fe450fff52160e4a21294a612f241c5e414da80f87209b" + "sha256:3201f5ba297b94328954724bd48dbf1b36ec14c4ee4cd5a2ec1aa3f83c486200", + "sha256:a2f83d82800a8d83cbd04340f9d65a6873a970874947a6b823b1b1238e84cba6" ], "markers": "python_version >= '3.7'", - "version": "==2021.4.8" + "version": "==2021.6.14" }, "toml": { "hashes": [ @@ -3153,41 +3173,6 @@ "markers": "python_version >= '3.7'", "version": "==5.0.5" }, - "typed-ast": { - "hashes": [ - "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace", - "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff", - "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266", - "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528", - "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6", - "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808", - "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4", - "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363", - "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341", - "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04", - "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41", - "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e", - "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3", - "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899", - "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805", - "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c", - "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c", - "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39", - "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a", - "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3", - "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7", - "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f", - "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075", - "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0", - "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40", - "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428", - "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927", - "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3", - "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f", - "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65" - ], - "version": "==1.4.3" - }, "typing-extensions": { "hashes": [ "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", diff --git a/README.md b/README.md index 109be45ba8..9a11136ef7 100644 --- a/README.md +++ b/README.md @@ -344,13 +344,13 @@ Directory Structure . ├── cereal # The messaging spec and libs used for all logs ├── common # Library like functionality we've developed here - ├── installer/updater # Manages auto-updates of NEOS + ├── installer/updater # Manages updates of NEOS ├── opendbc # Files showing how to interpret data from cars ├── panda # Code used to communicate on CAN - ├── phonelibs # Libraries used on NEOS devices - ├── pyextra # Libraries used on NEOS devices + ├── phonelibs # External libraries + ├── pyextra # Extra python packages not shipped in NEOS └── selfdrive # Code needed to drive the car - ├── assets # Fonts, images and sounds for UI + ├── assets # Fonts, images, and sounds for UI ├── athena # Allows communication with the app ├── boardd # Daemon to talk to the board ├── camerad # Driver to capture images from the camera sensors @@ -364,7 +364,7 @@ Directory Structure ├── modeld # Driving and monitoring model runners ├── proclogd # Logs information from proc ├── sensord # IMU interface code - ├── test # Unit tests, system tests and a car simulator + ├── test # Unit tests, system tests, and a car simulator └── ui # The UI Licensing diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 1b71e0730c..ec724fd051 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -211,7 +211,7 @@ function launch { # handle pythonpath ln -sfn $(pwd) /data/pythonpath - export PYTHONPATH="$PWD" + export PYTHONPATH="$PWD:$PWD/pyextra" # hardware specific init if [ -f /EON ]; then diff --git a/phonelibs/acados/.gitignore b/phonelibs/acados/.gitignore new file mode 100644 index 0000000000..af1e27921e --- /dev/null +++ b/phonelibs/acados/.gitignore @@ -0,0 +1 @@ +acados/ diff --git a/phonelibs/acados/build.sh b/phonelibs/acados/build.sh new file mode 100755 index 0000000000..144a38c6d3 --- /dev/null +++ b/phonelibs/acados/build.sh @@ -0,0 +1,40 @@ +#!/usr/bin/bash -e + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" + +ARCHNAME="x86_64" +BLAS_TARGET="X64_AUTOMATIC" +if [ -f /TICI ]; then + ARCHNAME="larch64" + BLAS_TARGET="ARMV8A_ARM_CORTEX_A57" +elif [ -f /EON ]; then + ARCHNAME="aarch64" + BLAS_TARGET="ARMV8A_ARM_CORTEX_A57" +fi + +if [ ! -d acados/ ]; then + #git clone https://github.com/acados/acados.git $DIR/acados + git clone https://github.com/commaai/acados.git $DIR/acados +fi +cd acados +git fetch +git checkout 9a1bab3f8fc4814a295fbf424fdc8125c63fdd08 +git submodule update --recursive --init + +# build +mkdir -p build +cd build +cmake -DACADOS_WITH_QPOASES=ON -UBLASFEO_TARGET -DBLASFEO_TARGET=$BLAS_TARGET .. +make -j4 install + +INSTALL_DIR="$DIR/$ARCHNAME" +rm -rf $INSTALL_DIR +mkdir -p $INSTALL_DIR + +rm $DIR/acados/lib/*.json + +cp -r $DIR/acados/include $DIR +cp -r $DIR/acados/lib $INSTALL_DIR +cp -r $DIR/acados/interfaces/acados_template/acados_template $DIR/../../pyextra + +#pip3 install -e $DIR/acados/interfaces/acados_template diff --git a/phonelibs/acados/include/acados_c/condensing_interface.h b/phonelibs/acados/include/acados_c/condensing_interface.h new file mode 100644 index 0000000000..51fe827127 --- /dev/null +++ b/phonelibs/acados/include/acados_c/condensing_interface.h @@ -0,0 +1,84 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_CONDENSING_INTERFACE_H_ +#define INTERFACES_ACADOS_C_CONDENSING_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acados/ocp_qp/ocp_qp_full_condensing.h" +#include "acados/ocp_qp/ocp_qp_partial_condensing.h" + +typedef enum { + PARTIAL_CONDENSING, + FULL_CONDENSING, +} condensing_t; + +typedef struct +{ + condensing_t condensing_type; +} condensing_plan; + +typedef struct +{ + ocp_qp_xcond_config *config; + void *dims; + void *opts; + void *mem; + void *work; +} condensing_module; + +ocp_qp_xcond_config *ocp_qp_condensing_config_create(condensing_plan *plan); +// +void *ocp_qp_condensing_opts_create(ocp_qp_xcond_config *config, void *dims_); +// +acados_size_t ocp_qp_condensing_calculate_size(ocp_qp_xcond_config *config, void *dims_, void *opts_); +// +condensing_module *ocp_qp_condensing_assign(ocp_qp_xcond_config *config, void *dims_, + void *opts_, void *raw_memory); +// +condensing_module *ocp_qp_condensing_create(ocp_qp_xcond_config *config, void *dims_, + void *opts_); +// +int ocp_qp_condense(condensing_module *module, void *qp_in, void *qp_out); +// +int ocp_qp_expand(condensing_module *module, void *qp_in, void *qp_out); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_CONDENSING_INTERFACE_H_ diff --git a/phonelibs/acados/include/acados_c/dense_qp_interface.h b/phonelibs/acados/include/acados_c/dense_qp_interface.h new file mode 100644 index 0000000000..4ecc77381d --- /dev/null +++ b/phonelibs/acados/include/acados_c/dense_qp_interface.h @@ -0,0 +1,94 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_DENSE_QP_INTERFACE_H_ +#define INTERFACES_ACADOS_C_DENSE_QP_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acados/dense_qp/dense_qp_common.h" + +typedef enum { DENSE_QP_HPIPM, DENSE_QP_QORE, DENSE_QP_QPOASES, DENSE_QP_OOQP } dense_qp_solver_t; + +typedef struct +{ + dense_qp_solver_t qp_solver; +} dense_qp_solver_plan; + +typedef struct +{ + qp_solver_config *config; + void *dims; + void *opts; + void *mem; + void *work; +} dense_qp_solver; + +qp_solver_config *dense_qp_config_create(dense_qp_solver_plan *plan); +// +dense_qp_dims *dense_qp_dims_create(); +// +dense_qp_in *dense_qp_in_create(qp_solver_config *config, dense_qp_dims *dims); +// +dense_qp_out *dense_qp_out_create(qp_solver_config *config, dense_qp_dims *dims); +// +void *dense_qp_opts_create(qp_solver_config *config, dense_qp_dims *dims); +// +acados_size_t dense_qp_calculate_size(qp_solver_config *config, dense_qp_dims *dims, void *opts_); +// +dense_qp_solver *dense_qp_assign(qp_solver_config *config, dense_qp_dims *dims, void *opts_, + void *raw_memory); +// +dense_qp_solver *dense_qp_create(qp_solver_config *config, dense_qp_dims *dims, void *opts_); +// +int dense_qp_solve(dense_qp_solver *solver, dense_qp_in *qp_in, dense_qp_out *qp_out); +// +void dense_qp_inf_norm_residuals(dense_qp_dims *dims, dense_qp_in *qp_in, dense_qp_out *qp_out, + double *res); +// +bool dense_qp_set_field_double_array(const char *field, double *arr, dense_qp_in *qp_in); +// +bool dense_qp_set_field_int_array(const char *field, int *arr, dense_qp_in *qp_in); +// +bool dense_qp_get_field_double_array(const char *field, dense_qp_in *qp_in, double *arr); +// +bool dense_qp_get_field_int_array(const char *field, dense_qp_in *qp_in, int *arr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_DENSE_QP_INTERFACE_H_ diff --git a/phonelibs/acados/include/acados_c/external_function_interface.h b/phonelibs/acados/include/acados_c/external_function_interface.h new file mode 100644 index 0000000000..6838975071 --- /dev/null +++ b/phonelibs/acados/include/acados_c/external_function_interface.h @@ -0,0 +1,92 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_EXTERNAL_FUNCTION_INTERFACE_H_ +#define INTERFACES_ACADOS_C_EXTERNAL_FUNCTION_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acados/utils/external_function_generic.h" + + + +/************************************************ + * generic external parametric function + ************************************************/ + +// +void external_function_param_generic_create(external_function_param_generic *fun, int np); +// +void external_function_param_generic_free(external_function_param_generic *fun); + + + +/************************************************ + * casadi external function + ************************************************/ + +// +void external_function_casadi_create(external_function_casadi *fun); +// +void external_function_casadi_free(external_function_casadi *fun); +// +void external_function_casadi_create_array(int size, external_function_casadi *funs); +// +void external_function_casadi_free_array(int size, external_function_casadi *funs); + + + +/************************************************ + * casadi external parametric function + ************************************************/ + +// +void external_function_param_casadi_create(external_function_param_casadi *fun, int np); +// +void external_function_param_casadi_free(external_function_param_casadi *fun); +// +void external_function_param_casadi_create_array(int size, external_function_param_casadi *funs, + int np); +// +void external_function_param_casadi_free_array(int size, external_function_param_casadi *funs); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_EXTERNAL_FUNCTION_INTERFACE_H_ diff --git a/phonelibs/acados/include/acados_c/ocp_nlp_interface.h b/phonelibs/acados/include/acados_c/ocp_nlp_interface.h new file mode 100644 index 0000000000..d4872f82e1 --- /dev/null +++ b/phonelibs/acados/include/acados_c/ocp_nlp_interface.h @@ -0,0 +1,399 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_OCP_NLP_INTERFACE_H_ +#define INTERFACES_ACADOS_C_OCP_NLP_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// acados +#include "acados/ocp_nlp/ocp_nlp_common.h" +#include "acados/ocp_nlp/ocp_nlp_constraints_bgh.h" +#include "acados/sim/sim_erk_integrator.h" +#include "acados/sim/sim_irk_integrator.h" +#include "acados/sim/sim_lifted_irk_integrator.h" +#include "acados/sim/sim_gnsf.h" +// acados_c +#include "acados_c/ocp_qp_interface.h" +#include "acados_c/sim_interface.h" + + +/// Solution methods for optimal control problems. +typedef enum +{ + SQP, + SQP_RTI, + INVALID_NLP_SOLVER, +} ocp_nlp_solver_t; + + +/// Types of the cost function. +typedef enum +{ + LINEAR_LS, + NONLINEAR_LS, + EXTERNAL, + INVALID_COST, +} ocp_nlp_cost_t; + + +/// Types of the system dynamics, discrete or continuous time. +typedef enum +{ + CONTINUOUS_MODEL, + DISCRETE_MODEL, + INVALID_DYNAMICS, +} ocp_nlp_dynamics_t; + + +/// Constraint types +typedef enum +{ + /// Comprises simple bounds, polytopic constraints, + /// general non-linear constraints. + BGH, + + /// Comprises simple bounds, polytopic constraints, + /// general non-linear constraints, and positive definite constraints. + BGP, + + INVALID_CONSTRAINT, +} ocp_nlp_constraints_t; + + +/// Regularization types +typedef enum +{ + NO_REGULARIZE, + MIRROR, + PROJECT, + PROJECT_REDUC_HESS, + CONVEXIFY, + INVALID_REGULARIZE, +} ocp_nlp_reg_t; + + +/// Structure to store the configuration of a non-linear program +typedef struct ocp_nlp_plan +{ + /// QP solver configuration. + ocp_qp_solver_plan ocp_qp_solver_plan; + + /// Simulation solver configuration for each stage. + sim_solver_plan *sim_solver_plan; + + /// Nlp solver type. + ocp_nlp_solver_t nlp_solver; + + /// Regularization type, defaults to no regularization. + ocp_nlp_reg_t regularization; + + /// Cost type for each stage. + ocp_nlp_cost_t *nlp_cost; + + /// Dynamics type for each stage. + ocp_nlp_dynamics_t *nlp_dynamics; + + /// Constraints type for each stage. + ocp_nlp_constraints_t *nlp_constraints; + + /// Horizon length. + int N; + +} ocp_nlp_plan; + + +/// Structure to store the state/configuration for the non-linear programming solver +typedef struct ocp_nlp_solver +{ + ocp_nlp_config *config; + void *dims; + void *opts; + void *mem; + void *work; +} ocp_nlp_solver; + + +/// Constructs an empty plan struct (user nlp configuration), all fields are set to a +/// default/invalid state. +/// +/// \param N Horizon length +ocp_nlp_plan *ocp_nlp_plan_create(int N); + +/// Destructor for plan struct, frees memory. +/// +/// \param plan_ The plan struct to destroy. +void ocp_nlp_plan_destroy(void* plan_); + + +/// Constructs an nlp configuration struct from a plan. +/// +/// \param plan The plan (user nlp configuration). +ocp_nlp_config *ocp_nlp_config_create(ocp_nlp_plan plan); + +/// Desctructor of the nlp configuration. +/// +/// \param config_ The configuration struct. +void ocp_nlp_config_destroy(void *config_); + + +/// Constructs an struct that contains the dimensions of the variables. +/// +/// \param config_ The configuration struct. +ocp_nlp_dims *ocp_nlp_dims_create(void *config_); + +/// Destructor of The dimension struct. +/// +/// \param dims_ The dimension struct. +void ocp_nlp_dims_destroy(void *dims_); + + +/// Constructs an input struct for a non-linear programs. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +ocp_nlp_in *ocp_nlp_in_create(ocp_nlp_config *config, ocp_nlp_dims *dims); + +/// Destructor of the inputs struct. +/// +/// \param in The inputs struct. +void ocp_nlp_in_destroy(void *in); + + +/// Sets the sampling times for the given stage. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param in The inputs struct. +/// \param stage Stage number. +/// \param field Has to be "Ts" (TBC other options). +/// \param value The sampling times (floating point). +void ocp_nlp_in_set(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_in *in, int stage, + const char *field, void *value); + + +/// Sets the function pointers to the dynamics functions for the given stage. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param in The inputs struct. +/// \param stage Stage number. +/// \param fun_type The name of the function type, either impl_ode_fun, +/// impl_ode_fun_jac_x_xdot, impl_ode_jac_x_xdot_u (TBC) +/// \param fun_ptr Function pointer to the dynamics function. +int ocp_nlp_dynamics_model_set(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_in *in, + int stage, const char *fun_type, void *fun_ptr); + + +/// Sets the function pointers to the cost functions for the given stage. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param in The inputs struct. +/// \param stage Stage number. +/// \param field The name of the field, either nls_res_jac, +/// y_ref, W (others TBC) +/// \param value Cost values. +int ocp_nlp_cost_model_set(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_in *in, + int stage, const char *field, void *value); + + +/// Sets the function pointers to the constraints functions for the given stage. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param in The inputs struct. +/// \param stage Stage number. +/// \param field The name of the field, either lb, ub (others TBC) +/// \param value Constraints function or values. +int ocp_nlp_constraints_model_set(ocp_nlp_config *config, ocp_nlp_dims *dims, + ocp_nlp_in *in, int stage, const char *field, void *value); + +/* out */ + +/// Constructs an output struct for the non-linear program. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +ocp_nlp_out *ocp_nlp_out_create(ocp_nlp_config *config, ocp_nlp_dims *dims); + +/// Destructor of the output struct. +/// +/// \param out The output struct. +void ocp_nlp_out_destroy(void *out); + + +/// Sets fields in the output struct of an nlp solver, used to initialize the solver. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param out The output struct. +/// \param stage Stage number. +/// \param field The name of the field, either x, u, pi. +/// \param value Initialization values. +void ocp_nlp_out_set(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_out *out, + int stage, const char *field, void *value); + + +/// Gets values of fields in the output struct of an nlp solver. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param out The output struct. +/// \param stage Stage number. +/// \param field The name of the field, either x, u, z, pi. +/// \param value Pointer to the output memory. +void ocp_nlp_out_get(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_out *out, + int stage, const char *field, void *value); + +// +void ocp_nlp_get_at_stage(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_solver *solver, + int stage, const char *field, void *value); + +// TODO(andrea): remove this once/if the MATLAB interface uses the new setters below? +int ocp_nlp_dims_get_from_attr(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_out *out, + int stage, const char *field); + +void ocp_nlp_constraint_dims_get_from_attr(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_out *out, + int stage, const char *field, int *dims_out); + +void ocp_nlp_cost_dims_get_from_attr(ocp_nlp_config *config, ocp_nlp_dims *dims, ocp_nlp_out *out, + int stage, const char *field, int *dims_out); + + +/* opts */ + +/// Creates an options struct for the non-linear program. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +void *ocp_nlp_solver_opts_create(ocp_nlp_config *config, ocp_nlp_dims *dims); + +/// Destructor of the options. +/// +/// \param opts The options struct. +void ocp_nlp_solver_opts_destroy(void *opts); + +/// Sets an option. +/// +/// \param config The configuration struct. +/// \param opts_ The options struct. +/// \param field Name of the option. +/// \param value Value of the option. +void ocp_nlp_solver_opts_set(ocp_nlp_config *config, void *opts_, const char *field, void* value); + + +void ocp_nlp_solver_opts_set_at_stage(ocp_nlp_config *config, void *opts_, int stage, const char *field, void* value); + + +/// TBC +/// Updates the options. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param opts_ The options struct. +void ocp_nlp_solver_opts_update(ocp_nlp_config *config, ocp_nlp_dims *dims, void *opts_); + + +/* solver */ + +/// Creates an ocp solver. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param opts_ The options struct. +/// \return The solver. +ocp_nlp_solver *ocp_nlp_solver_create(ocp_nlp_config *config, ocp_nlp_dims *dims, void *opts_); + +/// Destructor of the solver. +/// +/// \param solver The solver struct. +void ocp_nlp_solver_destroy(void *solver); + +/// Solves the optimal control problem. Call ocp_nlp_precompute before +/// calling this functions (TBC). +/// +/// \param solver The solver struct. +/// \param nlp_in The inputs struct. +/// \param nlp_out The output struct. +int ocp_nlp_solve(ocp_nlp_solver *solver, ocp_nlp_in *nlp_in, ocp_nlp_out *nlp_out); + +/// Performs precomputations for the solver. Needs to be called before +/// ocl_nlp_solve (TBC). +/// +/// \param solver The solver struct. +/// \param nlp_in The inputs struct. +/// \param nlp_out The output struct. +int ocp_nlp_precompute(ocp_nlp_solver *solver, ocp_nlp_in *nlp_in, ocp_nlp_out *nlp_out); + + +/// Computes cost function value. +/// +/// \param solver The solver struct. +/// \param nlp_in The inputs struct. +/// \param nlp_out The output struct. +void ocp_nlp_eval_cost(ocp_nlp_solver *solver, ocp_nlp_in *nlp_in, ocp_nlp_out *nlp_out); + + +// +void ocp_nlp_eval_param_sens(ocp_nlp_solver *solver, char *field, int stage, int index, ocp_nlp_out *sens_nlp_out); + +/* get */ +/// \param config The configuration struct. +/// \param solver The solver struct. +/// \param field Supports "sqp_iter", "status", "nlp_res", "time_tot", ... +/// \param return_value_ Pointer to the output memory. +void ocp_nlp_get(ocp_nlp_config *config, ocp_nlp_solver *solver, + const char *field, void *return_value_); + +/* set */ +/// Sets the initial guesses for the integrator for the given stage. +/// +/// \param config The configuration struct. +/// \param solver The ocp_nlp_solver struct. +/// \param stage Stage number. +/// \param field Supports "z_guess", "xdot_guess" (IRK), "phi_guess" (GNSF-IRK) +/// \param value The initial guess for the algebraic variables in the integrator (if continuous model is used). +void ocp_nlp_set(ocp_nlp_config *config, ocp_nlp_solver *solver, + int stage, const char *field, void *value); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_OCP_NLP_INTERFACE_H_ diff --git a/phonelibs/acados/include/acados_c/ocp_qp_interface.h b/phonelibs/acados/include/acados_c/ocp_qp_interface.h new file mode 100644 index 0000000000..a567ebeb35 --- /dev/null +++ b/phonelibs/acados/include/acados_c/ocp_qp_interface.h @@ -0,0 +1,261 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_OCP_QP_INTERFACE_H_ +#define INTERFACES_ACADOS_C_OCP_QP_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acados/ocp_qp/ocp_qp_common.h" +#include "acados/ocp_qp/ocp_qp_xcond_solver.h" + + +/// QP solver types (Enumeration). +/// +/// Full list of fields: +/// PARTIAL_CONDENSING_HPIPM +/// PARTIAL_CONDENSING_HPMPC +/// PARTIAL_CONDENSING_OOQP +/// PARTIAL_CONDENSING_OSQP +/// PARTIAL_CONDENSING_QPDUNES +/// FULL_CONDENSING_HPIPM +/// FULL_CONDENSING_QPOASES +/// FULL_CONDENSING_QORE +/// FULL_CONDENSING_OOQP +/// INVALID_QP_SOLVER +/// +/// Note: In this enumeration the partial condensing solvers have to be +/// specified before the full condensing solvers. +typedef enum { + PARTIAL_CONDENSING_HPIPM, +#ifdef ACADOS_WITH_HPMPC + PARTIAL_CONDENSING_HPMPC, +#else + PARTIAL_CONDENSING_HPMPC_NOT_AVAILABLE, +#endif +#ifdef ACADOS_WITH_OOQP + PARTIAL_CONDENSING_OOQP, +#else + PARTIAL_CONDENSING_OOQP_NOT_AVAILABLE, +#endif +#ifdef ACADOS_WITH_OSQP + PARTIAL_CONDENSING_OSQP, +#else + PARTIAL_CONDENSING_OSQP_NOT_AVAILABLE, +#endif +#ifdef ACADOS_WITH_QPDUNES + PARTIAL_CONDENSING_QPDUNES, +#else + PARTIAL_CONDENSING_QPDUNES_NOT_AVAILABLE, +#endif + FULL_CONDENSING_HPIPM, +#ifdef ACADOS_WITH_QPOASES + FULL_CONDENSING_QPOASES, +#else + FULL_CONDENSING_QPOASES_NOT_AVAILABLE, +#endif +#ifdef ACADOS_WITH_QORE + FULL_CONDENSING_QORE, +#else + FULL_CONDENSING_QORE_NOT_AVAILABLE, +#endif +#ifdef ACADOS_WITH_OOQP + FULL_CONDENSING_OOQP, +#else + FULL_CONDENSING_OOQP_NOT_AVAILABLE, +#endif + INVALID_QP_SOLVER, +} ocp_qp_solver_t; + + +/// Struct containing qp solver +typedef struct +{ + ocp_qp_solver_t qp_solver; +} ocp_qp_solver_plan; + + +/// Linear ocp configuration. +typedef struct +{ + ocp_qp_xcond_solver_config *config; + ocp_qp_xcond_solver_dims *dims; + void *opts; + void *mem; + void *work; +} ocp_qp_solver; + + +/// Initializes the qp solver configuration. +/// TBC should this be private/static - no, used in ocp_nlp +void ocp_qp_xcond_solver_config_initialize_from_plan( + ocp_qp_solver_t solver_name, ocp_qp_xcond_solver_config *solver_config); + +/// Constructs a qp solver config and Initializes with default values. +/// +/// \param plan The qp solver plan struct. +ocp_qp_xcond_solver_config *ocp_qp_xcond_solver_config_create(ocp_qp_solver_plan plan); + +/// Destructor for config struct, frees memory. +/// +/// \param config The config object to destroy. +void ocp_qp_xcond_solver_config_free(ocp_qp_xcond_solver_config *config); + + +/// Constructs a struct that contains the dimensions for the variables of the qp. +/// +/// \param N The number of variables. +ocp_qp_dims *ocp_qp_dims_create(int N); + +/// Destructor of The dimension struct. +/// +/// \param dims The dimension struct. +void ocp_qp_dims_free(void *dims); + +// +ocp_qp_xcond_solver_dims *ocp_qp_xcond_solver_dims_create(ocp_qp_xcond_solver_config *config, int N); +// +ocp_qp_xcond_solver_dims *ocp_qp_xcond_solver_dims_create_from_ocp_qp_dims( + ocp_qp_xcond_solver_config *config, ocp_qp_dims *dims); +// +void ocp_qp_xcond_solver_dims_free(ocp_qp_xcond_solver_dims *dims_); + +void ocp_qp_xcond_solver_dims_set(void *config_, ocp_qp_xcond_solver_dims *dims, + int stage, const char *field, int* value); + + +/// Constructs an input object for the qp. +/// +/// \param dims The dimension struct. +ocp_qp_in *ocp_qp_in_create(ocp_qp_dims *dims); + + +void ocp_qp_in_set(ocp_qp_xcond_solver_config *config, ocp_qp_in *in, + int stage, char *field, void *value); + +/// Destructor of the inputs struct. +/// +/// \param in_ The inputs struct. +void ocp_qp_in_free(void *in_); + + +/// Constructs an outputs object for the qp. +/// +/// \param dims The dimension struct. +ocp_qp_out *ocp_qp_out_create(ocp_qp_dims *dims); + +/// Destructor of the output struct. +/// +/// \param out_ The output struct. +void ocp_qp_out_free(void *out_); + + +/// Getter of output struct +void ocp_qp_out_get(ocp_qp_out *out, const char *field, void *value); + + +/// Constructs an options object for the qp. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +void *ocp_qp_xcond_solver_opts_create(ocp_qp_xcond_solver_config *config, + ocp_qp_xcond_solver_dims *dims); + +/// Destructor of the options struct. +/// +/// \param opts The options struct to destroy. +void ocp_qp_xcond_solver_opts_free(ocp_qp_xcond_solver_opts *opts); + + +/// Setter of the options struct. +/// +/// \param opts The options struct. +void ocp_qp_xcond_solver_opts_set(ocp_qp_xcond_solver_config *config, + ocp_qp_xcond_solver_opts *opts, const char *field, void* value); + +/// TBC Should be private/static? +acados_size_t ocp_qp_calculate_size(ocp_qp_xcond_solver_config *config, ocp_qp_xcond_solver_dims *dims, void *opts_); + + +/// TBC Reserves memory? TBC Should this be private? +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param opts_ The options struct. +/// \param raw_memory Pointer to raw memory to assign to qp solver. +ocp_qp_solver *ocp_qp_assign(ocp_qp_xcond_solver_config *config, ocp_qp_xcond_solver_dims *dims, + void *opts_, void *raw_memory); + +/// Creates a qp solver. Reserves memory. +/// +/// \param config The configuration struct. +/// \param dims The dimension struct. +/// \param opts_ The options struct. +ocp_qp_solver *ocp_qp_create(ocp_qp_xcond_solver_config *config, + ocp_qp_xcond_solver_dims *dims, void *opts_); + + +/// Destroys a qp solver. Frees memory. +/// +/// \param solver The qp solver +void ocp_qp_solver_destroy(ocp_qp_solver *solver); + +void ocp_qp_x_cond_solver_free(ocp_qp_xcond_solver_config *config, + ocp_qp_xcond_solver_dims *dims, void *opts_); + + +/// Solves the qp. +/// +/// \param solver The solver. +/// \param qp_in The inputs struct. +/// \param qp_out The output struct. +int ocp_qp_solve(ocp_qp_solver *solver, ocp_qp_in *qp_in, ocp_qp_out *qp_out); + + +/// Calculates the infinity norm of the residuals. +/// +/// \param dims The dimension struct. +/// \param qp_in The inputs struct. +/// \param qp_out The output struct. +/// \param res Output array for the residuals. +void ocp_qp_inf_norm_residuals(ocp_qp_dims *dims, ocp_qp_in *qp_in, ocp_qp_out *qp_out, + double *res); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_OCP_QP_INTERFACE_H_ diff --git a/phonelibs/acados/include/acados_c/sim_interface.h b/phonelibs/acados/include/acados_c/sim_interface.h new file mode 100644 index 0000000000..ba2e67bd6a --- /dev/null +++ b/phonelibs/acados/include/acados_c/sim_interface.h @@ -0,0 +1,140 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef INTERFACES_ACADOS_C_SIM_INTERFACE_H_ +#define INTERFACES_ACADOS_C_SIM_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acados/sim/sim_common.h" + + + +typedef enum +{ + ERK, + IRK, + GNSF, + LIFTED_IRK, + INVALID_SIM_SOLVER, +} sim_solver_t; + + + +typedef struct +{ + sim_solver_t sim_solver; +} sim_solver_plan; + + + +typedef struct +{ + sim_config *config; + void *dims; + void *opts; + void *mem; + void *work; +} sim_solver; + + + +/* config */ +// +sim_config *sim_config_create(sim_solver_plan plan); +// +void sim_config_destroy(void *config); + +/* dims */ +// +void *sim_dims_create(void *config_); +// +void sim_dims_destroy(void *dims); +// +void sim_dims_set(sim_config *config, void *dims, const char *field, const int* value); +// +void sim_dims_get(sim_config *config, void *dims, const char *field, int* value); +// +void sim_dims_get_from_attr(sim_config *config, void *dims, const char *field, int *dims_out); + +/* in */ +// +sim_in *sim_in_create(sim_config *config, void *dims); +// +void sim_in_destroy(void *out); +// +int sim_in_set(void *config_, void *dims_, sim_in *in, const char *field, void *value); + + +/* out */ +// +sim_out *sim_out_create(sim_config *config, void *dims); +// +void sim_out_destroy(void *out); +// +int sim_out_get(void *config, void *dims, sim_out *out, const char *field, void *value); + +/* opts */ +// +void *sim_opts_create(sim_config *config, void *dims); +// +void sim_opts_destroy(void *opts); +// +void sim_opts_set(sim_config *config, void *opts, const char *field, void *value); +// +void sim_opts_get(sim_config *config, void *opts, const char *field, void *value); + +/* solver */ +// +acados_size_t sim_calculate_size(sim_config *config, void *dims, void *opts_); +// +sim_solver *sim_assign(sim_config *config, void *dims, void *opts_, void *raw_memory); +// +sim_solver *sim_solver_create(sim_config *config, void *dims, void *opts_); +// +void sim_solver_destroy(void *solver); +// +int sim_solve(sim_solver *solver, sim_in *in, sim_out *out); +// +int sim_precompute(sim_solver *solver, sim_in *in, sim_out *out); +// +int sim_solver_set(sim_solver *solver, const char *field, void *value); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // INTERFACES_ACADOS_C_SIM_INTERFACE_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo.h b/phonelibs/acados/include/blasfeo/include/blasfeo.h new file mode 100644 index 0000000000..c854918193 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo.h @@ -0,0 +1,52 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#include "blasfeo_processor_features.h" +#include "blasfeo_target.h" +#include "blasfeo_block_size.h" +#include "blasfeo_stdlib.h" +#include "blasfeo_common.h" +#include "blasfeo_d_aux.h" +#include "blasfeo_d_aux_ext_dep.h" +#include "blasfeo_d_kernel.h" +#include "blasfeo_d_blas.h" +#include "blasfeo_s_aux.h" +#include "blasfeo_s_aux_ext_dep.h" +#include "blasfeo_s_kernel.h" +#include "blasfeo_s_blas.h" +#include "blasfeo_i_aux_ext_dep.h" +#include "blasfeo_v_aux_ext_dep.h" +#include "blasfeo_timing.h" +#include "blasfeo_memory.h" diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_block_size.h b/phonelibs/acados/include/blasfeo/include/blasfeo_block_size.h new file mode 100644 index 0000000000..182cc71b14 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_block_size.h @@ -0,0 +1,371 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_BLOCK_SIZE_H_ +#define BLASFEO_BLOCK_SIZE_H_ + + + +#define D_EL_SIZE 8 // double precision +#define S_EL_SIZE 4 // single precision + + + +#if defined( TARGET_X64_INTEL_HASWELL ) +// common +#define CACHE_LINE_SIZE 64 // data cache size: 64 bytes +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB, 8-way +#define L2_CACHE_SIZE (256*1024) // L2 data cache size: 256 kB ; DTLB1 64*4 kB = 256 kB +#define LLC_CACHE_SIZE (6*1024*1024) // LLC cache size: 6 MB ; TLB 1024*4 kB = 4 MB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 12 // max kernel size +#define D_KC 256 // 192 +#define D_NC 72 //96 //72 // 120 // 512 +#define D_MC 1500 // 6000 +// single +#define S_PS 8 // panel size +#define S_PLD 4 // 2 // GCD of panel length +#define S_M_KERNEL 24 // max kernel size +#define S_KC 256 +#define S_NC 144 +#define S_MC 3000 + +#elif defined( TARGET_X64_INTEL_SANDY_BRIDGE ) +// common +#define CACHE_LINE_SIZE 64 // data cache size: 64 bytes +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB, 8-way +#define L2_CACHE_SIZE (256*1024) // L2 data cache size: 256 kB ; DTLB1 64*4 kB = 256 kB +#define LLC_CACHE_SIZE (4*1024*1024) // LLC cache size: 4 MB ; TLB 1024*4 kB = 4 MB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 8 // max kernel size +#define D_KC 256 //320 //256 //320 +#define D_NC 72 //64 //72 //60 // 120 +#define D_MC 1000 // 800 +// single +#define S_PS 8 // panel size +#define S_PLD 4 // 2 // GCD of panel length +#define S_M_KERNEL 16 // max kernel size +#define S_KC 256 +#define S_NC 144 +#define S_MC 2000 + +#elif defined( TARGET_X64_INTEL_CORE ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + +#elif defined( TARGET_X64_AMD_BULLDOZER ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_X86_AMD_JAGUAR ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_X86_AMD_BARCELONA ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined(TARGET_ARMV8A_ARM_CORTEX_A76) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (64*1024) // L1 data cache size: 64 kB, 4-way ; DTLB1 48*4 kB = 192 kB +#define LLC_CACHE_SIZE (1*1024*1024) // LLC cache size: 1 MB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 8 // max kernel size +#define D_KC 512 //256 +#define D_NC 128 //256 +#define D_MC 6000 +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 8 // max kernel size +#define S_KC 512 +#define S_NC 256 +#define S_MC 6000 + + +#elif defined(TARGET_ARMV8A_ARM_CORTEX_A73) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 (64?) kB, 4-way, seen as 8-(16-)way ; DTLB1 48*4 kB = 192 kB +#define LLC_CACHE_SIZE (1*1024*1024) // LLC cache size: 1 MB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 8 // max kernel size +#define D_KC 320 +#define D_NC 256 +#define D_MC 6000 +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 8 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined(TARGET_ARMV8A_ARM_CORTEX_A57) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB, 2-way ; DTLB1 32*4 kB = 128 kB +#define LLC_CACHE_SIZE (1*1024*1024) // LLC cache size: 1 MB // 2 MB ??? +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 8 // max kernel size +#define D_KC 224 //256 //192 +#define D_NC 40 //36 //48 +#define D_MC 512 //488 //600 +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 8 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined(TARGET_ARMV8A_ARM_CORTEX_A55) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB, 4-way ; DTLB1 16*4 kB = 64 kB +#define LLC_CACHE_SIZE (512*1024) // LLC cache size: 512 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 12 // max kernel size +#define D_KC 224 +#define D_NC 160 +#define D_MC 6000 +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 8 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined(TARGET_ARMV8A_ARM_CORTEX_A53) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB, 4-way ??? ; DTLB1 10*4 kB = 40 kB +#define LLC_CACHE_SIZE (256*1024) // LLC cache size: 256 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 12 // max kernel size +#define D_KC 160 +#define D_NC 128 +#define D_MC 6000 +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 8 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_ARMV7A_ARM_CORTEX_A15 ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_ARMV7A_ARM_CORTEX_A7 ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_ARMV7A_ARM_CORTEX_A9 ) +// common +#define CACHE_LINE_SIZE 32 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#elif defined( TARGET_GENERIC ) +// common +#define CACHE_LINE_SIZE 64 +#define L1_CACHE_SIZE (32*1024) // L1 data cache size: 32 kB +// double +#define D_PS 4 // panel size +#define D_PLD 4 // 2 // GCD of panel length +#define D_M_KERNEL 4 // max kernel size +#define D_KC 256 +#define D_NC 128 // TODO these are just dummy +#define D_MC 3000 // TODO these are just dummy + +// single +#define S_PS 4 +#define S_PLD 4 //2 +#define S_M_KERNEL 4 // max kernel size +#define S_KC 256 +#define S_NC 128 // TODO these are just dummy +#define S_MC 3000 // TODO these are just dummy + + +#else +#error "Unknown architecture" +#endif + + + +#define D_CACHE_LINE_EL (CACHE_LINE_SIZE/D_EL_SIZE) +#define D_L1_CACHE_EL (L1_CACHE_SIZE/D_EL_SIZE) +#define D_L2_CACHE_EL (L2_CACHE_SIZE/D_EL_SIZE) +#define D_LLC_CACHE_EL (LLC_CACHE_SIZE/D_EL_SIZE) + +#define S_CACHE_LINE_EL (CACHE_LINE_SIZE/S_EL_SIZE) +#define S_L1_CACHE_EL (L1_CACHE_SIZE/S_EL_SIZE) +#define S_L2_CACHE_EL (L2_CACHE_SIZE/S_EL_SIZE) +#define S_LLC_CACHE_EL (LLC_CACHE_SIZE/S_EL_SIZE) + + + +#endif // BLASFEO_BLOCK_SIZE_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_common.h b/phonelibs/acados/include/blasfeo/include/blasfeo_common.h new file mode 100644 index 0000000000..a648142ba0 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_common.h @@ -0,0 +1,236 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_COMMON_H_ +#define BLASFEO_COMMON_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#if defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__INTEL_LLVM_COMPILER) +#define ALIGNED(VEC, BYTES) VEC __attribute__ ((aligned ( BYTES ))) +#elif defined (_MSC_VER) +#define ALIGNED(VEC, BYTES) __declspec(align( BYTES )) VEC +#else +#define ALIGNED(VEC, BYTES) VEC +#endif + + + + +#if ( defined(LA_HIGH_PERFORMANCE) & defined(MF_PANELMAJ) ) | ( defined(LA_REFERENCE) & defined(MF_PANELMAJ) ) + +#include "blasfeo_block_size.h" + +// matrix structure +struct blasfeo_dmat + { + double *mem; // pointer to passed chunk of memory + double *pA; // pointer to a pm*pn array of doubles, the first is aligned to cache line size + double *dA; // pointer to a min(m,n) (or max???) array of doubles + int m; // rows + int n; // cols + int pm; // packed number or rows + int cn; // packed number or cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + +struct blasfeo_smat + { + float *mem; // pointer to passed chunk of memory + float *pA; // pointer to a pm*pn array of floats, the first is aligned to cache line size + float *dA; // pointer to a min(m,n) (or max???) array of floats + int m; // rows + int n; // cols + int pm; // packed number or rows + int cn; // packed number or cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + +// vector structure +struct blasfeo_dvec + { + double *mem; // pointer to passed chunk of memory + double *pa; // pointer to a pm array of doubles, the first is aligned to cache line size + int m; // size + int pm; // packed size + int memsize; // size of needed memory + }; + +struct blasfeo_svec + { + float *mem; // pointer to passed chunk of memory + float *pa; // pointer to a pm array of floats, the first is aligned to cache line size + int m; // size + int pm; // packed size + int memsize; // size of needed memory + }; + +#define BLASFEO_DMATEL(sA,ai,aj) ((sA)->pA[((ai)-((ai)&(D_PS-1)))*(sA)->cn+(aj)*D_PS+((ai)&(D_PS-1))]) +#define BLASFEO_SMATEL(sA,ai,aj) ((sA)->pA[((ai)-((ai)&(S_PS-1)))*(sA)->cn+(aj)*S_PS+((ai)&(S_PS-1))]) +#define BLASFEO_DVECEL(sa,ai) ((sa)->pa[ai]) +#define BLASFEO_SVECEL(sa,ai) ((sa)->pa[ai]) + +#elif ( defined(LA_HIGH_PERFORMANCE) & defined(MF_COLMAJ) ) | ( defined(LA_REFERENCE) & defined(MF_COLMAJ) ) | defined(LA_EXTERNAL_BLAS_WRAPPER) + +// matrix structure +struct blasfeo_dmat + { + double *mem; // pointer to passed chunk of memory + double *pA; // pointer to a m*n array of doubles + double *dA; // pointer to a min(m,n) (or max???) array of doubles + int m; // rows + int n; // cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + +struct blasfeo_smat + { + float *mem; // pointer to passed chunk of memory + float *pA; // pointer to a m*n array of floats + float *dA; // pointer to a min(m,n) (or max???) array of floats + int m; // rows + int n; // cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + +// vector structure +struct blasfeo_dvec + { + double *mem; // pointer to passed chunk of memory + double *pa; // pointer to a m array of doubles, the first is aligned to cache line size + int m; // size + int memsize; // size of needed memory + }; + +struct blasfeo_svec + { + float *mem; // pointer to passed chunk of memory + float *pa; // pointer to a m array of floats, the first is aligned to cache line size + int m; // size + int memsize; // size of needed memory + }; + +#define BLASFEO_DMATEL(sA,ai,aj) ((sA)->pA[(ai)+(aj)*(sA)->m]) +#define BLASFEO_SMATEL(sA,ai,aj) ((sA)->pA[(ai)+(aj)*(sA)->m]) +#define BLASFEO_DVECEL(sa,ai) ((sa)->pa[ai]) +#define BLASFEO_SVECEL(sa,ai) ((sa)->pa[ai]) + +#else + +#error : wrong LA or MF choice + +#endif + + + +// Explicitly panel-major matrix structure +struct blasfeo_pm_dmat + { + double *mem; // pointer to passed chunk of memory + double *pA; // pointer to a pm*pn array of doubles, the first is aligned to cache line size + double *dA; // pointer to a min(m,n) (or max???) array of doubles + int m; // rows + int n; // cols + int pm; // packed number or rows + int cn; // packed number or cols + int use_dA; // flag to tell if dA can be used + int ps; // panel size + int memsize; // size of needed memory + }; + +struct blasfeo_pm_smat + { + float *mem; // pointer to passed chunk of memory + float *pA; // pointer to a pm*pn array of floats, the first is aligned to cache line size + float *dA; // pointer to a min(m,n) (or max???) array of floats + int m; // rows + int n; // cols + int pm; // packed number or rows + int cn; // packed number or cols + int use_dA; // flag to tell if dA can be used + int ps; // panel size + int memsize; // size of needed memory + }; + +// Explicitly column-major matrix structure +struct blasfeo_cm_dmat + { + double *mem; // pointer to passed chunk of memory + double *pA; // pointer to a m*n array of doubles + double *dA; // pointer to a min(m,n) (or max???) array of doubles + int m; // rows + int n; // cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + +struct blasfeo_cm_smat + { + float *mem; // pointer to passed chunk of memory + float *pA; // pointer to a m*n array of floats + float *dA; // pointer to a min(m,n) (or max???) array of floats + int m; // rows + int n; // cols + int use_dA; // flag to tell if dA can be used + int memsize; // size of needed memory + }; + + +#define BLASFEO_PM_DMATEL(sA,ai,aj) ((sA)->pA[((ai)-((ai)&((sA)->ps-1)))*(sA)->cn+(aj)*((sA)->ps)+((ai)&((sA)->ps-1))]) +#define BLASFEO_PM_SMATEL(sA,ai,aj) ((sA)->pA[((ai)-((ai)&((sA)->ps-1)))*(sA)->cn+(aj)*((sA)->ps)+((ai)&((sA)->ps-1))]) +#define BLASFEO_CM_DMATEL(sA,ai,aj) ((sA)->pA[(ai)+(aj)*(sA)->m]) +#define BLASFEO_CM_SMATEL(sA,ai,aj) ((sA)->pA[(ai)+(aj)*(sA)->m]) + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_COMMON_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux.h new file mode 100644 index 0000000000..7a9415a59d --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux.h @@ -0,0 +1,239 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +/* + * auxiliary algebra operations header + * + * include/blasfeo_aux_lib*.h + * + */ + +#ifndef BLASFEO_D_AUX_H_ +#define BLASFEO_D_AUX_H_ + + + +#include + +#include "blasfeo_common.h" +#include "blasfeo_d_aux_old.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + +// --- memory size calculations +// +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_memsize_dmat(int m, int n); +// returns the memory size (in bytes) needed for the diagonal of a dmat +size_t blasfeo_memsize_diag_dmat(int m, int n); +// returns the memory size (in bytes) needed for a dvec +size_t blasfeo_memsize_dvec(int m); + +// --- creation +// +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_create_dmat(int m, int n, struct blasfeo_dmat *sA, void *memory); +// create a strvec for a vector of size m by using memory passed by a pointer (pointer is not updated) +void blasfeo_create_dvec(int m, struct blasfeo_dvec *sA, void *memory); + +// --- packing +// pack the column-major matrix A into the matrix struct B +void blasfeo_pack_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the lower-triangular column-major matrix A into the matrix struct B +void blasfeo_pack_l_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the upper-triangular column-major matrix A into the matrix struct B +void blasfeo_pack_u_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// transpose and pack the column-major matrix A into the matrix struct B +void blasfeo_pack_tran_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the vector x into the vector structure y +void blasfeo_pack_dvec(int m, double *x, int xi, struct blasfeo_dvec *sy, int yi); +// unpack the matrix structure A into the column-major matrix B +void blasfeo_unpack_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *B, int ldb); +// transpose and unpack the matrix structure A into the column-major matrix B +void blasfeo_unpack_tran_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *B, int ldb); +// pack the vector structure x into the vector y +void blasfeo_unpack_dvec(int m, struct blasfeo_dvec *sx, int xi, double *y, int yi); + +// --- cast +// +//void d_cast_mat2strmat(double *A, struct blasfeo_dmat *sA); // TODO +//void d_cast_diag_mat2strmat(double *dA, struct blasfeo_dmat *sA); // TODO +//void d_cast_vec2vecmat(double *a, struct blasfeo_dvec *sx); // TODO + + +// ge +// --- insert/extract +// +// sA[ai, aj] <= a +void blasfeo_dgein1(double a, struct blasfeo_dmat *sA, int ai, int aj); +// <= sA[ai, aj] +double blasfeo_dgeex1(struct blasfeo_dmat *sA, int ai, int aj); + +// --- set +// A <= alpha +void blasfeo_dgese(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj); + +// --- copy / scale +// B <= A +void blasfeo_dgecp(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// A <= alpha*A +void blasfeo_dgesc(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// B <= alpha*A +void blasfeo_dgecpsc(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A, A lower triangular +void blasfeo_dtrcp_l(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +void blasfeo_dtrcpsc_l(int m, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +void blasfeo_dtrsc_l(int m, double alpha, struct blasfeo_dmat *sA, int ai, int aj); + +// --- sum +// B <= B + alpha*A +void blasfeo_dgead(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// y <= y + alpha*x +void blasfeo_dvecad(int m, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); + +// --- traspositions +// B <= A' +void blasfeo_dgetr(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A', A lower triangular +void blasfeo_dtrtr_l(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A', A upper triangular +void blasfeo_dtrtr_u(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); + +// dia +// diag(A) += alpha +void blasfeo_ddiare(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A) <= alpha*x +void blasfeo_ddiain(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A)[idx] <= alpha*x +void blasfeo_ddiain_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// x <= diag(A) +void blasfeo_ddiaex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +// x <= diag(A)[idx] +void blasfeo_ddiaex_sp(int kmax, double alpha, int *idx, struct blasfeo_dmat *sD, int di, int dj, struct blasfeo_dvec *sx, int xi); +// diag(A) += alpha*x +void blasfeo_ddiaad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A)[idx] += alpha*x +void blasfeo_ddiaad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// diag(A)[idx] = y + alpha*x +void blasfeo_ddiaadin_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, int *idx, struct blasfeo_dmat *sD, int di, int dj); + +// row +void blasfeo_drowin(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_drowex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +void blasfeo_drowad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_drowad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_drowsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +void blasfeo_drowpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +void blasfeo_drowpei(int kmax, int *ipiv, struct blasfeo_dmat *sA); + +// col +void blasfeo_dcolex(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +void blasfeo_dcolin(int kmax, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_dcolad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_dcolsc(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_dcolsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +void blasfeo_dcolpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +void blasfeo_dcolpei(int kmax, int *ipiv, struct blasfeo_dmat *sA); + +// vec +// a <= alpha +void blasfeo_dvecse(int m, double alpha, struct blasfeo_dvec *sx, int xi); +// sx[xi] <= a +void blasfeo_dvecin1(double a, struct blasfeo_dvec *sx, int xi); +// <= sx[xi] +double blasfeo_dvecex1(struct blasfeo_dvec *sx, int xi); +// y <= x +void blasfeo_dveccp(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +// x <= alpha*x +void blasfeo_dvecsc(int m, double alpha, struct blasfeo_dvec *sx, int xi); +// y <= alpha*x +void blasfeo_dveccpsc(int m, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +void blasfeo_dvecad_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +void blasfeo_dvecin_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +void blasfeo_dvecex_sp(int m, double alpha, int *idx, struct blasfeo_dvec *sx, int x, struct blasfeo_dvec *sz, int zi); + +void blasfeo_dveccl(int m, + struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, + struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi); + +void blasfeo_dveccl_mask(int m, + struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, + struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi, + struct blasfeo_dvec *sm, int mi); + +void blasfeo_dvecze(int m, struct blasfeo_dvec *sm, int mi, struct blasfeo_dvec *sv, int vi, struct blasfeo_dvec *se, int ei); +void blasfeo_dvecnrm_inf(int m, struct blasfeo_dvec *sx, int xi, double *ptr_norm); +void blasfeo_dvecnrm_2(int m, struct blasfeo_dvec *sx, int xi, double *ptr_norm); +void blasfeo_dvecpe(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); +void blasfeo_dvecpei(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); + + + + + +/* +* Explicitly panel-major matrix format +*/ + +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_pm_memsize_dmat(int ps, int m, int n); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_pm_create_dmat(int ps, int m, int n, struct blasfeo_pm_dmat *sA, void *memory); +// print +void blasfeo_pm_print_dmat(int m, int n, struct blasfeo_pm_dmat *sA, int ai, int aj); + + + +/* +* Explicitly panel-major matrix format +*/ + +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_cm_memsize_dmat(int m, int n); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_cm_create_dmat(int m, int n, struct blasfeo_pm_dmat *sA, void *memory); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_AUX_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep.h new file mode 100644 index 0000000000..bee9e986f1 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep.h @@ -0,0 +1,145 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +/* + * auxiliary algebra operation external dependancies header + * + * include/blasfeo_d_aux_ext_dep.h + * + * - dynamic memory allocation + * - print + * + */ + +#ifndef BLASFEO_D_AUX_EXT_DEP_H_ +#define BLASFEO_D_AUX_EXT_DEP_H_ + + + +#include + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef EXT_DEP + +/* column-major matrices */ + +// dynamically allocate row*col doubles of memory and set accordingly a pointer to double; set allocated memory to zero +void d_zeros(double **pA, int row, int col); +// dynamically allocate row*col doubles of memory aligned to 64-byte boundaries and set accordingly a pointer to double; set allocated memory to zero +void d_zeros_align(double **pA, int row, int col); +// dynamically allocate size bytes of memory aligned to 64-byte boundaries and set accordingly a pointer to double; set allocated memory to zero +void d_zeros_align_bytes(double **pA, int size); +// free the memory allocated by d_zeros +void d_free(double *pA); +// free the memory allocated by d_zeros_align or d_zeros_align_bytes +void d_free_align(double *pA); +// print a column-major matrix +void d_print_mat(int m, int n, double *A, int lda); +// print the transposed of a column-major matrix +void d_print_tran_mat(int row, int col, double *A, int lda); +// print to file a column-major matrix +void d_print_to_file_mat(FILE *file, int row, int col, double *A, int lda); +// print to file a column-major matrix in exponential format +void d_print_to_file_exp_mat(FILE *file, int row, int col, double *A, int lda); +// print to string a column-major matrix +void d_print_to_string_mat(char **buf_out, int row, int col, double *A, int lda); +// print to file the transposed of a column-major matrix +void d_print_tran_to_file_mat(FILE *file, int row, int col, double *A, int lda); +// print to file the transposed of a column-major matrix in exponential format +void d_print_tran_to_file_exp_mat(FILE *file, int row, int col, double *A, int lda); +// print in exponential notation a column-major matrix +void d_print_exp_mat(int m, int n, double *A, int lda); +// print in exponential notation the transposed of a column-major matrix +void d_print_exp_tran_mat(int row, int col, double *A, int lda); + +/* strmat and strvec */ + +// create a strmat for a matrix of size m*n by dynamically allocating memory +void blasfeo_allocate_dmat(int m, int n, struct blasfeo_dmat *sA); +// create a strvec for a vector of size m by dynamically allocating memory +void blasfeo_allocate_dvec(int m, struct blasfeo_dvec *sa); +// free the memory allocated by blasfeo_allocate_dmat +void blasfeo_free_dmat(struct blasfeo_dmat *sA); +// free the memory allocated by blasfeo_allocate_dvec +void blasfeo_free_dvec(struct blasfeo_dvec *sa); +// print a strmat +void blasfeo_print_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print in exponential notation a strmat +void blasfeo_print_exp_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print to file a strmat +void blasfeo_print_to_file_dmat(FILE *file, int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print to file a strmat in exponential format +void blasfeo_print_to_file_exp_dmat(FILE *file, int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print to string a strmat +void blasfeo_print_to_string_dmat(char **buf_out, int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print the transposed of a strmat +void blasfeo_print_tran_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +// print a strvec +void blasfeo_print_dvec(int m, struct blasfeo_dvec *sa, int ai); +// print in exponential notation a strvec +void blasfeo_print_exp_dvec(int m, struct blasfeo_dvec *sa, int ai); +// print to file a strvec +void blasfeo_print_to_file_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); +// print to string a strvec +void blasfeo_print_to_string_dvec(char **buf_out, int m, struct blasfeo_dvec *sa, int ai); +// print the transposed of a strvec +void blasfeo_print_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); +// print in exponential notation the transposed of a strvec +void blasfeo_print_exp_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); +// print to file the transposed of a strvec +void blasfeo_print_to_file_tran_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); +// print to string the transposed of a strvec +void blasfeo_print_to_string_tran_dvec(char **buf_out, int m, struct blasfeo_dvec *sa, int ai); + +#endif // EXT_DEP + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_AUX_EXT_DEP_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep_ref.h new file mode 100644 index 0000000000..81b811f032 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ext_dep_ref.h @@ -0,0 +1,84 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +/* + * auxiliary algebra operation external dependancies header + * + * include/blasfeo_d_aux_ext_dep.h + * + * - dynamic memory allocation + * - print + * + */ + +#ifndef BLASFEO_D_AUX_EXT_DEP_REF_H_ +#define BLASFEO_D_AUX_EXT_DEP_REF_H_ + + +#include + +#include "blasfeo_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// expose reference BLASFEO for testing +// see blasfeo_d_aux_exp_dep.h for help + +void blasfeo_print_dmat_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_allocate_dmat_ref(int m, int n, struct blasfeo_dmat_ref *sA); +void blasfeo_allocate_dvec_ref(int m, struct blasfeo_dvec_ref *sa); +void blasfeo_free_dmat_ref(struct blasfeo_dmat_ref *sA); +void blasfeo_free_dvec_ref(struct blasfeo_dvec_ref *sa); +void blasfeo_print_dmat_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_print_exp_dmat_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_print_to_file_dmat_ref(FILE *file, int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_print_to_file_exp_dmat_ref(FILE *file, int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_print_to_string_dmat_ref(char **buf_out, int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj); +void blasfeo_print_dvec(int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_exp_dvec(int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_to_file_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_to_string_dvec(char **buf_out, int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_exp_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_to_file_tran_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); +void blasfeo_print_to_string_tran_dvec(char **buf_out, int m, struct blasfeo_dvec *sa, int ai); + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_AUX_EXT_DEP_REF_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_old.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_old.h new file mode 100644 index 0000000000..3a1847a6a0 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_old.h @@ -0,0 +1,75 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +/* + * ----------- TOMOVE + * + * expecting column major matrices + * + */ + +#include "blasfeo_common.h" + + +void dtrcp_l_lib(int m, double alpha, int offsetA, double *A, int sda, int offsetB, double *B, int sdb); +void dgead_lib(int m, int n, double alpha, int offsetA, double *A, int sda, int offsetB, double *B, int sdb); +// TODO remove ??? +void ddiain_sqrt_lib(int kmax, double *x, int offset, double *pD, int sdd); +// TODO ddiaad1 +void ddiareg_lib(int kmax, double reg, int offset, double *pD, int sdd); + + +void dgetr_lib(int m, int n, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +void dtrtr_l_lib(int m, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +void dtrtr_u_lib(int m, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +void ddiaex_lib(int kmax, double alpha, int offset, double *pD, int sdd, double *x); +void ddiaad_lib(int kmax, double alpha, double *x, int offset, double *pD, int sdd); +void ddiain_libsp(int kmax, int *idx, double alpha, double *x, double *pD, int sdd); +void ddiaex_libsp(int kmax, int *idx, double alpha, double *pD, int sdd, double *x); +void ddiaad_libsp(int kmax, int *idx, double alpha, double *x, double *pD, int sdd); +void ddiaadin_libsp(int kmax, int *idx, double alpha, double *x, double *y, double *pD, int sdd); +void drowin_lib(int kmax, double alpha, double *x, double *pD); +void drowex_lib(int kmax, double alpha, double *pD, double *x); +void drowad_lib(int kmax, double alpha, double *x, double *pD); +void drowin_libsp(int kmax, double alpha, int *idx, double *x, double *pD); +void drowad_libsp(int kmax, int *idx, double alpha, double *x, double *pD); +void drowadin_libsp(int kmax, int *idx, double alpha, double *x, double *y, double *pD); +void dcolin_lib(int kmax, double *x, int offset, double *pD, int sdd); +void dcolad_lib(int kmax, double alpha, double *x, int offset, double *pD, int sdd); +void dcolin_libsp(int kmax, int *idx, double *x, double *pD, int sdd); +void dcolad_libsp(int kmax, double alpha, int *idx, double *x, double *pD, int sdd); +void dcolsw_lib(int kmax, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +void dvecin_libsp(int kmax, int *idx, double *x, double *y); +void dvecad_libsp(int kmax, int *idx, double alpha, double *x, double *y); diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ref.h new file mode 100644 index 0000000000..448234044a --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_ref.h @@ -0,0 +1,206 @@ + +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_AUX_REF_H_ +#define BLASFEO_D_AUX_REF_H_ + + + +#include + +#include "blasfeo_common.h" +#include "blasfeo_d_aux_old.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + +// --- memory calculations +// +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_ref_memsize_dmat(int m, int n); +// returns the memory size (in bytes) needed for the diagonal of a dmat +size_t blasfeo_ref_memsize_diag_dmat(int m, int n); +// returns the memory size (in bytes) needed for a dvec +size_t blasfeo_ref_memsize_dvec(int m); + +// --- creation +// +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_ref_create_dmat(int m, int n, struct blasfeo_dmat *sA, void *memory); +// create a strvec for a vector of size m by using memory passed by a pointer (pointer is not updated) +void blasfeo_ref_create_dvec(int m, struct blasfeo_dvec *sA, void *memory); + +// --- packing +// pack the column-major matrix A into the matrix struct B +void blasfeo_ref_pack_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the lower-triangular column-major matrix A into the matrix struct B +void blasfeo_ref_pack_l_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the upper-triangular column-major matrix A into the matrix struct B +void blasfeo_ref_pack_u_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// transpose and pack the column-major matrix A into the matrix struct B +void blasfeo_ref_pack_tran_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sB, int bi, int bj); +// pack the vector x into the vector structure y +void blasfeo_ref_pack_dvec(int m, double *x, int xi, struct blasfeo_dvec *sy, int yi); +// unpack the matrix structure A into the column-major matrix B +void blasfeo_ref_unpack_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *B, int ldb); +// transpose and unpack the matrix structure A into the column-major matrix B +void blasfeo_ref_unpack_tran_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *B, int ldb); +// pack the vector structure x into the vector y +void blasfeo_ref_unpack_dvec(int m, struct blasfeo_dvec *sx, int xi, double *y, int yi); + +// --- cast +// +void ref_d_cast_mat2strmat(double *A, struct blasfeo_dmat *sA); // TODO +void ref_d_cast_diag_mat2strmat(double *dA, struct blasfeo_dmat *sA); // TODO +void ref_d_cast_vec2vecmat(double *a, struct blasfeo_dvec *sx); // TODO + + +// ge +// --- insert/extract +// +// sA[ai, aj] <= a +void blasfeo_ref_dgein1(double a, struct blasfeo_dmat *sA, int ai, int aj); +// <= sA[ai, aj] +double blasfeo_ref_dgeex1(struct blasfeo_dmat *sA, int ai, int aj); + +// --- set +// A <= alpha +void blasfeo_ref_dgese(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj); + +// --- copy / scale +// B <= A +void blasfeo_ref_dgecp(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// A <= alpha*A +void blasfeo_ref_dgesc(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// B <= alpha*A +void blasfeo_ref_dgecpsc(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A, A lower triangular +void blasfeo_ref_dtrcp_l(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +void blasfeo_ref_dtrcpsc_l(int m, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +void blasfeo_ref_dtrsc_l(int m, double alpha, struct blasfeo_dmat *sA, int ai, int aj); + +// --- sum +// B <= B + alpha*A +void blasfeo_ref_dgead(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int yi, int cj); +// y <= y + alpha*x +void blasfeo_ref_dvecad(int m, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); + +// --- traspositions +// B <= A' +void blasfeo_ref_dgetr(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A', A lower triangular +void blasfeo_ref_dtrtr_l(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); +// B <= A', A upper triangular +void blasfeo_ref_dtrtr_u(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj); + +// dia +// diag(A) += alpha +void blasfeo_ref_ddiare(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A) <= alpha*x +void blasfeo_ref_ddiain(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A)[idx] <= alpha*x +void blasfeo_ref_ddiain_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// x <= diag(A) +void blasfeo_ref_ddiaex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +// x <= diag(A)[idx] +void blasfeo_ref_ddiaex_sp(int kmax, double alpha, int *idx, struct blasfeo_dmat *sD, int di, int dj, struct blasfeo_dvec *sx, int xi); +// diag(A) += alpha*x +void blasfeo_ref_ddiaad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// diag(A)[idx] += alpha*x +void blasfeo_ref_ddiaad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// diag(A)[idx] = y + alpha*x +void blasfeo_ref_ddiaadin_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, int *idx, struct blasfeo_dmat *sD, int di, int dj); + +// row +void blasfeo_ref_drowin(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_ref_drowex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +void blasfeo_ref_drowad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_ref_drowad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_ref_drowsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +void blasfeo_ref_drowpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +void blasfeo_ref_drowpei(int kmax, int *ipiv, struct blasfeo_dmat *sA); + +// col +void blasfeo_ref_dcolex(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +void blasfeo_ref_dcolin(int kmax, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_ref_dcolad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_ref_dcolsc(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +void blasfeo_ref_dcolsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +void blasfeo_ref_dcolpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +void blasfeo_ref_dcolpei(int kmax, int *ipiv, struct blasfeo_dmat *sA); + +// vec +// a <= alpha +void blasfeo_ref_dvecse(int m, double alpha, struct blasfeo_dvec *sx, int xi); +// sx[xi] <= a +void blasfeo_ref_dvecin1(double a, struct blasfeo_dvec *sx, int xi); +// <= sx[xi] +double blasfeo_ref_dvecex1(struct blasfeo_dvec *sx, int xi); +// y <= x +void blasfeo_ref_dveccp(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +// x <= alpha*x +void blasfeo_ref_dvecsc(int m, double alpha, struct blasfeo_dvec *sx, int xi); +// y <= alpha*x +void blasfeo_ref_dveccpsc(int m, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +void blasfeo_ref_dvecad_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +void blasfeo_ref_dvecin_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +void blasfeo_ref_dvecex_sp(int m, double alpha, int *idx, struct blasfeo_dvec *sx, int x, struct blasfeo_dvec *sz, int zi); + +void blasfeo_ref_dveccl(int m, + struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, + struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi); + +void blasfeo_ref_dveccl_mask(int m, + struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, + struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi, + struct blasfeo_dvec *sm, int mi); + +void blasfeo_ref_dvecze(int m, struct blasfeo_dvec *sm, int mi, struct blasfeo_dvec *sv, int vi, struct blasfeo_dvec *se, int ei); +void blasfeo_ref_dvecnrm_inf(int m, struct blasfeo_dvec *sx, int xi, double *ptr_norm); +void blasfeo_ref_dvecpe(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); +void blasfeo_ref_dvecpei(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_AUX_REF_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_test.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_test.h new file mode 100644 index 0000000000..1c61635f3f --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_aux_test.h @@ -0,0 +1,226 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +/* + * auxiliary algebra operations header + * + * include/blasfeo_aux_lib*.h + * + */ + +#ifndef BLASFEO_D_AUX_TEST_H_ +#define BLASFEO_D_AUX_TEST_H_ + +#include "blasfeo_common.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +// --- memory calculations +int test_blasfeo_memsize_dmat(int m, int n); +int test_blasfeo_memsize_diag_dmat(int m, int n); +int test_blasfeo_memsize_dvec(int m); + +// --- creation +void test_blasfeo_create_dmat(int m, int n, struct blasfeo_dmat *sA, void *memory); +void test_blasfeo_create_dvec(int m, struct blasfeo_dvec *sA, void *memory); + +// --- conversion +void test_blasfeo_pack_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sA, int ai, int aj); +void test_blasfeo_pack_dvec(int m, double *x, int xi, struct blasfeo_dvec *sa, int ai); +void test_blasfeo_pack_tran_dmat(int m, int n, double *A, int lda, struct blasfeo_dmat *sA, int ai, int aj); +void test_blasfeo_unpack_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *A, int lda); +void test_blasfeo_unpack_dvec(int m, struct blasfeo_dvec *sa, int ai, double *x, int xi); +void test_blasfeo_unpack_tran_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, double *A, int lda); + +// --- cast +void test_d_cast_mat2strmat(double *A, struct blasfeo_dmat *sA); +void test_d_cast_diag_mat2strmat(double *dA, struct blasfeo_dmat *sA); +void test_d_cast_vec2vecmat(double *a, struct blasfeo_dvec *sa); + +// ------ copy / scale + +// B <= A +void test_blasfeo_dgecp(int m, int n, + struct blasfeo_dmat *sA, int ai, int aj, + struct blasfeo_dmat *sB, int bi, int bj); + +// A <= alpha*A +void test_blasfeo_dgesc(int m, int n, + double alpha, + struct blasfeo_dmat *sA, int ai, int aj); + +// B <= alpha*A +void test_blasfeo_dgecpsc(int m, int n, + double alpha, + struct blasfeo_dmat *sA, int ai, int aj, + struct blasfeo_dmat *sB, int bi, int bj); + +// // --- insert/extract +// // +// // <= sA[ai, aj] +// void test_blasfeo_dgein1(double a, struct blasfeo_dmat *sA, int ai, int aj); +// // <= sA[ai, aj] +// double blasfeo_dgeex1(struct blasfeo_dmat *sA, int ai, int aj); +// // sx[xi] <= a +// void test_blasfeo_dvecin1(double a, struct blasfeo_dvec *sx, int xi); +// // <= sx[xi] +// double blasfeo_dvecex1(struct blasfeo_dvec *sx, int xi); +// // A <= alpha + +// // --- set +// void test_blasfeo_dgese(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// // a <= alpha +// void test_blasfeo_dvecse(int m, double alpha, struct blasfeo_dvec *sx, int xi); +// // B <= A + + +// // --- vector +// // y <= x +// void test_blasfeo_dveccp(int m, struct blasfeo_dvec *sa, int ai, struct blasfeo_dvec *sc, int ci); +// // x <= alpha*x +// void test_blasfeo_dvecsc(int m, double alpha, struct blasfeo_dvec *sa, int ai); +// // TODO +// // x <= alpha*x +// void test_blasfeo_dveccpsc(int m, double alpha, struct blasfeo_dvec *sa, int ai, struct blasfeo_dvec *sc, int ci); + + +// // B <= A, A lower triangular +// void test_blasfeo_dtrcp_l(int m, +// struct blasfeo_dmat *sA, int ai, int aj, +// struct blasfeo_dmat *sB, int bi, int bj); + +// void test_blasfeo_dtrcpsc_l(int m, double alpha, +// struct blasfeo_dmat *sA, int ai, int aj, +// struct blasfeo_dmat *sB, int bi, int bj); + +// void test_blasfeo_dtrsc_l(int m, double alpha, +// struct blasfeo_dmat *sA, int ai, int aj); + + +// // B <= B + alpha*A +// void test_blasfeo_dgead(int m, int n, double alpha, +// struct blasfeo_dmat *sA, int ai, int aj, +// struct blasfeo_dmat *sC, int ci, int cj); + +// // y <= y + alpha*x +// void test_blasfeo_dvecad(int m, double alpha, +// struct blasfeo_dvec *sa, int ai, +// struct blasfeo_dvec *sc, int ci); + +// // --- traspositions +// void test_dgetr_lib(int m, int n, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +// void test_blasfeo_dgetr(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +// void test_dtrtr_l_lib(int m, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +// void test_blasfeo_dtrtr_l(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +// void test_dtrtr_u_lib(int m, double alpha, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +// void test_blasfeo_dtrtr_u(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +// void test_blasfeo_ddiare(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj); +// void test_blasfeo_ddiain(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// void test_ddiaex_lib(int kmax, double alpha, int offset, double *pD, int sdd, double *x); +// void test_ddiaad_lib(int kmax, double alpha, double *x, int offset, double *pD, int sdd); +// void test_ddiain_libsp(int kmax, int *idx, double alpha, double *x, double *pD, int sdd); +// void test_blasfeo_ddiain_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// void test_ddiaex_libsp(int kmax, int *idx, double alpha, double *pD, int sdd, double *x); +// void test_blasfeo_ddiaex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +// void test_blasfeo_ddiaex_sp(int kmax, double alpha, int *idx, struct blasfeo_dmat *sD, int di, int dj, struct blasfeo_dvec *sx, int xi); +// void test_blasfeo_ddiaad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// void test_ddiaad_libsp(int kmax, int *idx, double alpha, double *x, double *pD, int sdd); +// void test_blasfeo_ddiaad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// void test_ddiaadin_libsp(int kmax, int *idx, double alpha, double *x, double *y, double *pD, int sdd); +// void test_blasfeo_ddiaadin_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// void test_drowin_lib(int kmax, double alpha, double *x, double *pD); +// void test_blasfeo_drowin(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// void test_drowex_lib(int kmax, double alpha, double *pD, double *x); +// void test_blasfeo_drowex(int kmax, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +// void test_drowad_lib(int kmax, double alpha, double *x, double *pD); +// void test_blasfeo_drowad(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// void test_drowin_libsp(int kmax, double alpha, int *idx, double *x, double *pD); +// void test_drowad_libsp(int kmax, int *idx, double alpha, double *x, double *pD); +// void test_blasfeo_drowad_sp(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dmat *sD, int di, int dj); +// void test_drowadin_libsp(int kmax, int *idx, double alpha, double *x, double *y, double *pD); +// void test_drowsw_lib(int kmax, double *pA, double *pC); +// void test_blasfeo_drowsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +// void test_blasfeo_drowpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +// void test_blasfeo_dcolex(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi); +// void test_dcolin_lib(int kmax, double *x, int offset, double *pD, int sdd); +// void test_blasfeo_dcolin(int kmax, struct blasfeo_dvec *sx, int xi, struct blasfeo_dmat *sA, int ai, int aj); +// void test_dcolad_lib(int kmax, double alpha, double *x, int offset, double *pD, int sdd); +// void test_dcolin_libsp(int kmax, int *idx, double *x, double *pD, int sdd); +// void test_dcolad_libsp(int kmax, double alpha, int *idx, double *x, double *pD, int sdd); +// void test_dcolsw_lib(int kmax, int offsetA, double *pA, int sda, int offsetC, double *pC, int sdc); +// void test_blasfeo_dcolsw(int kmax, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sC, int ci, int cj); +// void test_blasfeo_dcolpe(int kmax, int *ipiv, struct blasfeo_dmat *sA); +// void test_dvecin_libsp(int kmax, int *idx, double *x, double *y); +// void test_dvecad_libsp(int kmax, int *idx, double alpha, double *x, double *y); +// void test_blasfeo_dvecad_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +// void test_blasfeo_dvecin_sp(int m, double alpha, struct blasfeo_dvec *sx, int xi, int *idx, struct blasfeo_dvec *sz, int zi); +// void test_blasfeo_dvecex_sp(int m, double alpha, int *idx, struct blasfeo_dvec *sx, int x, struct blasfeo_dvec *sz, int zi); +// void test_blasfeo_dveccl(int m, struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi); +// void test_blasfeo_dveccl_mask(int m, struct blasfeo_dvec *sxm, int xim, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sxp, int xip, struct blasfeo_dvec *sz, int zi, struct blasfeo_dvec *sm, int mi); +// void test_blasfeo_dvecze(int m, struct blasfeo_dvec *sm, int mi, struct blasfeo_dvec *sv, int vi, struct blasfeo_dvec *se, int ei); +// void test_blasfeo_dvecnrm_inf(int m, struct blasfeo_dvec *sx, int xi, double *ptr_norm); +// void test_blasfeo_dvecpe(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); +// void test_blasfeo_dvecpei(int kmax, int *ipiv, struct blasfeo_dvec *sx, int xi); + +// ext_dep + +void test_blasfeo_allocate_dmat(int m, int n, struct blasfeo_dmat *sA); +void test_blasfeo_allocate_dvec(int m, struct blasfeo_dvec *sa); + +void test_blasfeo_free_dmat(struct blasfeo_dmat *sA); +void test_blasfeo_free_dvec(struct blasfeo_dvec *sa); + +void test_blasfeo_print_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +void test_blasfeo_print_dvec(int m, struct blasfeo_dvec *sa, int ai); +void test_blasfeo_print_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); + +void test_blasfeo_print_to_file_dmat(FILE *file, int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +void test_blasfeo_print_to_file_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); +void test_blasfeo_print_to_file_tran_dvec(FILE *file, int m, struct blasfeo_dvec *sa, int ai); + +void test_blasfeo_print_exp_dmat(int m, int n, struct blasfeo_dmat *sA, int ai, int aj); +void test_blasfeo_print_exp_dvec(int m, struct blasfeo_dvec *sa, int ai); +void test_blasfeo_print_exp_tran_dvec(int m, struct blasfeo_dvec *sa, int ai); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_AUX_TEST_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas.h new file mode 100644 index 0000000000..ea8d006731 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas.h @@ -0,0 +1,46 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_BLAS_H_ +#define BLASFEO_D_BLAS_H_ + + + +#include "blasfeo_d_blasfeo_api.h" +#include "blasfeo_d_blas_api.h" + + + +#endif // BLASFEO_D_BLAS_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas_api.h new file mode 100644 index 0000000000..64dd43f677 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blas_api.h @@ -0,0 +1,161 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef BLASFEO_D_BLAS_API_H_ +#define BLASFEO_D_BLAS_API_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef BLAS_API + + + +#ifdef FORTRAN_BLAS_API + + + +// BLAS 1 +// +void daxpy_(int *n, double *alpha, double *x, int *incx, double *y, int *incy); +// +void dcopy_(int *n, double *x, int *incx, double *y, int *incy); +// +double ddot_(int *n, double *x, int *incx, double *y, int *incy); + +// BLAS 3 +// +void dgemm_(char *ta, char *tb, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc); +// +void dsyrk_(char *uplo, char *ta, int *m, int *k, double *alpha, double *A, int *lda, double *beta, double *C, int *ldc); +// +void dtrmm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); +// +void dtrsm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); + + + +// LAPACK +// +void dgesv_(int *m, int *n, double *A, int *lda, int *ipiv, double *B, int *ldb, int *info); +// +void dgetrf_(int *m, int *n, double *A, int *lda, int *ipiv, int *info); +// +void dgetrf_np_(int *m, int *n, double *A, int *lda, int *info); +// +void dgetrs_(char *trans, int *m, int *n, double *A, int *lda, int *ipiv, double *B, int *ldb, int *info); +// +void dlaswp_(int *n, double *A, int *lda, int *k1, int *k2, int *ipiv, int *incx); +// +void dposv_(char *uplo, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); +// +void dpotrf_(char *uplo, int *m, double *A, int *lda, int *info); +// +void dpotrs_(char *uplo, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); +// +void dtrtrs_(char *uplo, char *trans, char *diag, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); + + + +#else // BLASFEO_API + + + +// BLAS 1 +// +void blas_daxpy(int *n, double *alpha, double *x, int *incx, double *y, int *incy); +// +double blas_ddot(int *n, double *x, int *incx, double *y, int *incy); +// +void blas_dcopy(int *n, double *x, int *incx, double *y, int *incy); + +// BLAS 3 +// +void blas_dgemm(char *ta, char *tb, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc); +// +void blas_dsyrk(char *uplo, char *ta, int *m, int *k, double *alpha, double *A, int *lda, double *beta, double *C, int *ldc); +// +void blas_dtrmm(char *side, char *uplo, char *transa, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); +// +void blas_dtrsm(char *side, char *uplo, char *transa, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); + + + +// LAPACK +// +void blas_dgesv(int *m, int *n, double *A, int *lda, int *ipiv, double *B, int *ldb, int *info); +// +void blas_dgetrf(int *m, int *n, double *A, int *lda, int *ipiv, int *info); +// +void blas_dgetrf_np(int *m, int *n, double *A, int *lda, int *info); +// +void blas_dgetrs(char *trans, int *m, int *n, double *A, int *lda, int *ipiv, double *B, int *ldb, int *info); +// +void blas_dlaswp(int *n, double *A, int *lda, int *k1, int *k2, int *ipiv, int *incx); +// +void blas_dposv(char *uplo, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); +// +void blas_dpotrf(char *uplo, int *m, double *A, int *lda, int *info); +// +void blas_dpotrs(char *uplo, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); +// +void blas_dtrtrs(char *uplo, char *trans, char *diag, int *m, int *n, double *A, int *lda, double *B, int *ldb, int *info); + + + +#endif // BLASFEO_API + + + +#endif // BLAS_API + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_BLAS_API_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api.h new file mode 100644 index 0000000000..f27264e133 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api.h @@ -0,0 +1,342 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_BLASFEO_API_H_ +#define BLASFEO_D_BLASFEO_API_H_ + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +// level 1 BLAS +// + +// z = y + alpha*x +// z[zi:zi+n] = alpha*x[xi:xi+n] + y[yi:yi+n] +// NB: Different arguments semantic compare to equivalent standard BLAS routine +void blasfeo_daxpy(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = beta*y + alpha*x +void blasfeo_daxpby(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = x .* y +void blasfeo_dvecmul(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z += x .* y +void blasfeo_dvecmulacc(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = x .* y, return sum(z) = x^T * y +double blasfeo_dvecmuldot(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// return x^T * y +double blasfeo_ddot(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +// construct givens plane rotation +void blasfeo_drotg(double a, double b, double *c, double *s); +// apply plane rotation [a b] [c -s; s; c] to the aj0 and aj1 columns of A at row index ai +void blasfeo_dcolrot(int m, struct blasfeo_dmat *sA, int ai, int aj0, int aj1, double c, double s); +// apply plane rotation [c s; -s c] [a; b] to the ai0 and ai1 rows of A at column index aj +void blasfeo_drowrot(int m, struct blasfeo_dmat *sA, int ai0, int ai1, int aj, double c, double s); + + + +// +// level 2 BLAS +// + +// dense + +// z <= beta * y + alpha * A * x +void blasfeo_dgemv_n(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z <= beta * y + alpha * A^T * x +void blasfeo_dgemv_t(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(n) +void blasfeo_dtrsv_lnn_mn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A^T ) * x, A (m)x(n) +void blasfeo_dtrsv_ltn_mn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, not_unit +void blasfeo_dtrsv_lnn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, unit +void blasfeo_dtrsv_lnu(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A^T ) * x, A (m)x(m) lower, transposed, not_unit +void blasfeo_dtrsv_ltn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A^T ) * x, A (m)x(m) lower, transposed, unit +void blasfeo_dtrsv_ltu(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A^T ) * x, A (m)x(m) upper, not_transposed, not_unit +void blasfeo_dtrsv_unn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A^T ) * x, A (m)x(m) upper, transposed, not_unit +void blasfeo_dtrsv_utn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= beta * y + alpha * A * x ; A upper triangular +void blasfeo_dtrmv_unn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A^T * x ; A upper triangular +void blasfeo_dtrmv_utn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A * x ; A lower triangular +void blasfeo_dtrmv_lnn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A^T * x ; A lower triangular +void blasfeo_dtrmv_ltn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A * x ; A lower triangular, unit diagonal +void blasfeo_dtrmv_lnu(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A^T * x ; A lower triangular, unit diagonal +void blasfeo_dtrmv_ltu(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z_n <= beta_n * y_n + alpha_n * A * x_n +// z_t <= beta_t * y_t + alpha_t * A^T * x_t +void blasfeo_dgemv_nt(int m, int n, double alpha_n, double alpha_t, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx_n, int xi_n, struct blasfeo_dvec *sx_t, int xi_t, double beta_n, double beta_t, struct blasfeo_dvec *sy_n, int yi_n, struct blasfeo_dvec *sy_t, int yi_t, struct blasfeo_dvec *sz_n, int zi_n, struct blasfeo_dvec *sz_t, int zi_t); +// z <= beta * y + alpha * A * x, where A is symmetric and only the lower triangular patr of A is accessed +void blasfeo_dsymv_l(int m, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +void blasfeo_dsymv_l_mn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); + +// diagonal + +// z <= beta * y + alpha * A * x, A diagonal +void blasfeo_dgemv_d(int m, double alpha, struct blasfeo_dvec *sA, int ai, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); + + + +// +// level 3 BLAS +// + +// dense + +// D <= beta * C + alpha * A * B +void blasfeo_dgemm_nn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T +void blasfeo_dgemm_nt(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_dgemm_tn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B^T +void blasfeo_dgemm_tt(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D lower triangular +void blasfeo_dsyrk_ln(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_dsyrk_ln_mn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#if ( defined(LA_HIGH_PERFORMANCE) & defined(MF_COLMAJ) ) +void blasfeo_dsyrk3_ln(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#endif +// D <= beta * C + alpha * A^T * B ; C, D lower triangular +void blasfeo_dsyrk_lt(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#if ( defined(LA_HIGH_PERFORMANCE) & defined(MF_COLMAJ) ) +void blasfeo_dsyrk3_lt(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#endif +// D <= beta * C + alpha * A * B^T ; C, D upper triangular +void blasfeo_dsyrk_un(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#if ( defined(LA_HIGH_PERFORMANCE) & defined(MF_COLMAJ) ) +void blasfeo_dsyrk3_un(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#endif +// D <= beta * C + alpha * A^T * B ; C, D upper triangular +void blasfeo_dsyrk_ut(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#if ( defined(LA_HIGH_PERFORMANCE) & defined(MF_COLMAJ) ) +void blasfeo_dsyrk3_ut(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +#endif +// D <= alpha * A * B ; A lower triangular +void blasfeo_dtrmm_llnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A lower triangular +void blasfeo_dtrmm_llnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A lower triangular +void blasfeo_dtrmm_lltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A lower triangular +void blasfeo_dtrmm_lltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A upper triangular +void blasfeo_dtrmm_lunn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A upper triangular +void blasfeo_dtrmm_lunu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A upper triangular +void blasfeo_dtrmm_lutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A upper triangular +void blasfeo_dtrmm_lutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_dtrmm_rlnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_dtrmm_rlnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A lower triangular +void blasfeo_dtrmm_rltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A lower triangular +void blasfeo_dtrmm_rltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A upper triangular +void blasfeo_dtrmm_runn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A upper triangular +void blasfeo_dtrmm_runu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A upper triangular +void blasfeo_dtrmm_rutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A upper triangular +void blasfeo_dtrmm_rutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular employint explicit inverse of diagonal +// D <= alpha * A^{-1} * B , with A lower triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_llnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular with unit diagonal +void blasfeo_dtrsm_llnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_lltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular with unit diagonal +void blasfeo_dtrsm_lltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_lunn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular with unit diagonal +void blasfeo_dtrsm_lunu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_lutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular with unit diagonal +void blasfeo_dtrsm_lutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_rlnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular with unit diagonal +void blasfeo_dtrsm_rlnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_rltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular with unit diagonal +void blasfeo_dtrsm_rltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_runn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular with unit diagonal +void blasfeo_dtrsm_runu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_dtrsm_rutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular with unit diagonal +void blasfeo_dtrsm_rutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); + +// diagonal + +// D <= alpha * A * B + beta * C, with A diagonal (stored as strvec) +void dgemm_diag_left_lib(int m, int n, double alpha, double *dA, double *pB, int sdb, double beta, double *pC, int sdc, double *pD, int sdd); +void blasfeo_dgemm_dn(int m, int n, double alpha, struct blasfeo_dvec *sA, int ai, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B + beta * C, with B diagonal (stored as strvec) +void blasfeo_dgemm_nd(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sB, int bi, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); + + + +// +// LAPACK +// + +// D <= chol( C ) ; C, D lower triangular +void blasfeo_dpotrf_l(int m, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_dpotrf_l_mn(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= chol( C ) ; C, D upper triangular +void blasfeo_dpotrf_u(int m, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= chol( C + A * B' ) ; C, D lower triangular +// D <= chol( C + A * B^T ) ; C, D lower triangular +void blasfeo_dsyrk_dpotrf_ln(int m, int k, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_dsyrk_dpotrf_ln_mn(int m, int n, int k, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= lu( C ) ; no pivoting +void blasfeo_dgetrf_np(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= lu( C ) ; row pivoting +void blasfeo_dgetrf_rp(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, int *ipiv); +// D <= qr( C ) +int blasfeo_dgeqrf_worksize(int m, int n); // in bytes +void blasfeo_dgeqrf(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// D <= Q factor, where C is the output of the LQ factorization +int blasfeo_dorglq_worksize(int m, int n, int k); // in bytes +void blasfeo_dorglq(int m, int n, int k, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// D <= lq( C ) +void blasfeo_dgelqf(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +int blasfeo_dgelqf_worksize(int m, int n); // in bytes +// D <= lq( C ), positive diagonal elements +void blasfeo_dgelqf_pd(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// [L, A] <= lq( [L, A] ), positive diagonal elements, array of matrices, with +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_dgelqf_pd_la(int m, int n1, struct blasfeo_dmat *sL, int li, int lj, struct blasfeo_dmat *sA, int ai, int aj, void *work); +// [L, L, A] <= lq( [L, L, A] ), positive diagonal elements, array of matrices, with: +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_dgelqf_pd_lla(int m, int n1, struct blasfeo_dmat *sL0, int l0i, int l0j, struct blasfeo_dmat *sL1, int l1i, int l1j, struct blasfeo_dmat *sA, int ai, int aj, void *work); + + + +// +// BLAS API helper functions +// + +#if ( defined(BLAS_API) & defined(MF_PANELMAJ) ) +// BLAS 3 +void blasfeo_cm_dgemm_nn(int m, int n, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dgemm_nt(int m, int n, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dgemm_tn(int m, int n, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dgemm_tt(int m, int n, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk_ln(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk_lt(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk_un(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk_ut(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk3_ln(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk3_lt(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk3_un(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dsyrk3_ut(int m, int k, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, double beta, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_llnn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_llnu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lltn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lltu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lunn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lunu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lutn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_lutu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rlnn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rlnu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rltn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rltu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_runn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_runu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rutn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrsm_rutu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_llnn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_llnu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lltn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lltu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lunn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lunu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lutn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_lutu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rlnn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rlnu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rltn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rltu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_runn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_runu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rutn(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dtrmm_rutu(int m, int n, double alpha, struct blasfeo_cm_dmat *sA, int ai, int aj, struct blasfeo_cm_dmat *sB, int bi, int bj, struct blasfeo_cm_dmat *sD, int di, int dj); +// LAPACK +void blasfeo_cm_dpotrf_l(int m, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dpotrf_u(int m, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj); +void blasfeo_cm_dgetrf_rp(int m, int n, struct blasfeo_cm_dmat *sC, int ci, int cj, struct blasfeo_cm_dmat *sD, int di, int dj, int *ipiv); +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_BLASFEO_API_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api_ref.h new file mode 100644 index 0000000000..141a1f0f06 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_api_ref.h @@ -0,0 +1,147 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_BLASFEO_API_REF_H_ +#define BLASFEO_D_BLASFEO_API_REF_H_ + +#include "blasfeo_common.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +// expose reference BLASFEO for testing + +// --- level 1 + +void blasfeo_daxpy_ref(int kmax, double alpha, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_daxpby_ref(int kmax, double alpha, struct blasfeo_dvec_ref *sx, int xi, double beta, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dvecmul_ref(int m, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dvecmulacc_ref(int m, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +double blasfeo_dvecmuldot_ref(int m, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +double blasfeo_ddot_ref(int m, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sy, int yi); +void blasfeo_drotg_ref(double a, double b, double *c, double *s); +void blasfeo_dcolrot_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj0, int aj1, double c, double s); +void blasfeo_drowrot_ref(int m, struct blasfeo_dmat_ref *sA, int ai0, int ai1, int aj, double c, double s); + + +// --- level 2 + +// dense +void blasfeo_dgemv_n_ref(int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, double beta, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dgemv_t_ref(int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, double beta, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_lnn_mn_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_ltn_mn_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_lnn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_lnu_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_ltn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_ltu_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_unn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrsv_utn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_unn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_utn_ref(int m, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_lnn_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_ltn_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_lnu_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dtrmv_ltu_ref(int m, int n, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, struct blasfeo_dvec_ref *sz, int zi); +void blasfeo_dgemv_nt_ref(int m, int n, double alpha_n, double alpha_t, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx_n, int xi_n, struct blasfeo_dvec_ref *sx_t, int xi_t, double beta_n, double beta_t, struct blasfeo_dvec_ref *sy_n, int yi_n, struct blasfeo_dvec_ref *sy_t, int yi_t, struct blasfeo_dvec_ref *sz_n, int zi_n, struct blasfeo_dvec_ref *sz_t, int zi_t); +void blasfeo_dsymv_l_ref(int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sx, int xi, double beta, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); + +// diagonal +void blasfeo_dgemv_d_ref(int m, double alpha, struct blasfeo_dvec_ref *sA, int ai, struct blasfeo_dvec_ref *sx, int xi, double beta, struct blasfeo_dvec_ref *sy, int yi, struct blasfeo_dvec_ref *sz, int zi); + + +// --- level 3 + +// dense +void blasfeo_dgemm_nn_ref( int m, int n, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgemm_nt_ref( int m, int n, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgemm_tn_ref(int m, int n, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgemm_tt_ref(int m, int n, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); + +void blasfeo_dsyrk_ln_mn_ref( int m, int n, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_ln_ref( int m, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_lt_ref( int m, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_un_ref( int m, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_ut_ref( int m, int k, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); + +void blasfeo_dtrmm_rutn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrmm_rlnn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); + +void blasfeo_dtrsm_lunu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_lunn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_lutu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_lutn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_llnu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_llnn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_lltu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_lltn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_runu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_runn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rutu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rutn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rlnu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rlnn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rltu_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dtrsm_rltn_ref( int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sD, int di, int dj); + +// diagonal +void dgemm_diag_left_lib_ref(int m, int n, double alpha, double *dA, double *pB, int sdb, double beta, double *pC, int sdc, double *pD, int sdd); +void blasfeo_dgemm_dn_ref(int m, int n, double alpha, struct blasfeo_dvec_ref *sA, int ai, struct blasfeo_dmat_ref *sB, int bi, int bj, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgemm_nd_ref(int m, int n, double alpha, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dvec_ref *sB, int bi, double beta, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); + +// --- lapack + +void blasfeo_dgetrf_nopivot_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgetrf_rowpivot_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj, int *ipiv); +void blasfeo_dpotrf_l_ref(int m, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dpotrf_l_mn_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_dpotrf_ln_ref(int m, int k, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dsyrk_dpotrf_ln_mn_ref(int m, int n, int k, struct blasfeo_dmat_ref *sA, int ai, int aj, struct blasfeo_dmat_ref *sB, int bi, int bj, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgetrf_nopivot_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj); +void blasfeo_dgetrf_rowpivot_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj, int *ipiv); +void blasfeo_dgeqrf_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj, void *work); +void blasfeo_dgelqf_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj, void *work); +void blasfeo_dgelqf_pd_ref(int m, int n, struct blasfeo_dmat_ref *sC, int ci, int cj, struct blasfeo_dmat_ref *sD, int di, int dj, void *work); +void blasfeo_dgelqf_pd_la_ref(int m, int n1, struct blasfeo_dmat_ref *sL, int li, int lj, struct blasfeo_dmat_ref *sA, int ai, int aj, void *work); +void blasfeo_dgelqf_pd_lla_ref(int m, int n1, struct blasfeo_dmat_ref *sL0, int l0i, int l0j, struct blasfeo_dmat_ref *sL1, int l1i, int l1j, struct blasfeo_dmat_ref *sA, int ai, int aj, void *work); + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_BLASFEO_API_REF_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_ref_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_ref_api.h new file mode 100644 index 0000000000..eaac365817 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_blasfeo_ref_api.h @@ -0,0 +1,270 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_BLASFEO_REF_API_H_ +#define BLASFEO_D_BLASFEO_REF_API_H_ + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +// level 1 BLAS +// + +// z = y + alpha*x +// z[zi:zi+n] = alpha*x[xi:xi+n] + y[yi:yi+n] +// NB: Different arguments semantic compare to equivalent standard BLAS routine +void blasfeo_ref_daxpy(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = beta*y + alpha*x +void blasfeo_ref_daxpby(int kmax, double alpha, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = x .* y +void blasfeo_ref_dvecmul(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z += x .* y +void blasfeo_ref_dvecmulacc(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z = x .* y, return sum(z) = x^T * y +double blasfeo_ref_dvecmuldot(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// return x^T * y +double blasfeo_ref_ddot(int m, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sy, int yi); +// construct givens plane rotation +void blasfeo_ref_drotg(double a, double b, double *c, double *s); +// apply plane rotation [a b] [c -s; s; c] to the aj0 and aj1 columns of A at row index ai +void blasfeo_ref_dcolrot(int m, struct blasfeo_dmat *sA, int ai, int aj0, int aj1, double c, double s); +// apply plane rotation [c s; -s c] [a; b] to the ai0 and ai1 rows of A at column index aj +void blasfeo_ref_drowrot(int m, struct blasfeo_dmat *sA, int ai0, int ai1, int aj, double c, double s); + + + +// +// level 2 BLAS +// + +// dense + +// z <= beta * y + alpha * A * x +void blasfeo_ref_dgemv_n(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z <= beta * y + alpha * A' * x +void blasfeo_ref_dgemv_t(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(n) +void blasfeo_ref_dtrsv_lnn_mn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(n) +void blasfeo_ref_dtrsv_ltn_mn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, not_unit +void blasfeo_ref_dtrsv_lnn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, unit +void blasfeo_ref_dtrsv_lnu(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, not_unit +void blasfeo_ref_dtrsv_ltn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, unit +void blasfeo_ref_dtrsv_ltu(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, not_transposed, not_unit +void blasfeo_ref_dtrsv_unn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, transposed, not_unit +void blasfeo_ref_dtrsv_utn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= beta * y + alpha * A * x ; A upper triangular +void blasfeo_ref_dtrmv_unn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A' * x ; A upper triangular +void blasfeo_ref_dtrmv_utn(int m, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A * x ; A lower triangular +void blasfeo_ref_dtrmv_lnn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A' * x ; A lower triangular +void blasfeo_ref_dtrmv_ltn(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A * x ; A lower triangular, unit diagonal +void blasfeo_ref_dtrmv_lnu(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z <= A' * x ; A lower triangular, unit diagonal +void blasfeo_ref_dtrmv_ltu(int m, int n, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, struct blasfeo_dvec *sz, int zi); +// z_n <= beta_n * y_n + alpha_n * A * x_n +// z_t <= beta_t * y_t + alpha_t * A' * x_t +void blasfeo_ref_dgemv_nt(int m, int n, double alpha_n, double alpha_t, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx_n, int xi_n, struct blasfeo_dvec *sx_t, int xi_t, double beta_n, double beta_t, struct blasfeo_dvec *sy_n, int yi_n, struct blasfeo_dvec *sy_t, int yi_t, struct blasfeo_dvec *sz_n, int zi_n, struct blasfeo_dvec *sz_t, int zi_t); +// z <= beta * y + alpha * A * x, where A is symmetric and only the lower triangular patr of A is accessed +void blasfeo_ref_dsymv_l(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); + +// diagonal + +// z <= beta * y + alpha * A * x, A diagonal +void blasfeo_ref_dgemv_d(int m, double alpha, struct blasfeo_dvec *sA, int ai, struct blasfeo_dvec *sx, int xi, double beta, struct blasfeo_dvec *sy, int yi, struct blasfeo_dvec *sz, int zi); + + + +// +// level 3 BLAS +// + +// dense + +// D <= beta * C + alpha * A * B +void blasfeo_ref_dgemm_nn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T +void blasfeo_ref_dgemm_nt(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_ref_dgemm_tn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B^T +void blasfeo_ref_dgemm_tt(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D lower triangular +void blasfeo_ref_dsyrk_ln(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_ref_dsyrk_ln_mn(int m, int n, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D lower triangular +void blasfeo_ref_dsyrk_lt(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D upper triangular +void blasfeo_ref_dsyrk_un(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D upper triangular +void blasfeo_ref_dsyrk_ut(int m, int k, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A lower triangular +void blasfeo_ref_dtrmm_llnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A lower triangular +void blasfeo_ref_dtrmm_llnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A lower triangular +void blasfeo_ref_dtrmm_lltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A lower triangular +void blasfeo_ref_dtrmm_lltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A upper triangular +void blasfeo_ref_dtrmm_lunn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B ; A upper triangular +void blasfeo_ref_dtrmm_lunu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A upper triangular +void blasfeo_ref_dtrmm_lutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^T * B ; A upper triangular +void blasfeo_ref_dtrmm_lutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_ref_dtrmm_rlnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_ref_dtrmm_rlnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A lower triangular +void blasfeo_ref_dtrmm_rltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A lower triangular +void blasfeo_ref_dtrmm_rltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A upper triangular +void blasfeo_ref_dtrmm_runn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A ; A upper triangular +void blasfeo_ref_dtrmm_runu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A upper triangular +void blasfeo_ref_dtrmm_rutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^T ; A upper triangular +void blasfeo_ref_dtrmm_rutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_ref_dtrsm_llnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular with unit diagonal +void blasfeo_ref_dtrsm_llnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_ref_dtrsm_lltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular with unit diagonal +void blasfeo_ref_dtrsm_lltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_lunn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular withunit diagonal +void blasfeo_ref_dtrsm_lunu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_lutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular withunit diagonal +void blasfeo_ref_dtrsm_lutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_rlnn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular with unit diagonal +void blasfeo_ref_dtrsm_rlnu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_rltn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular with unit diagonal +void blasfeo_ref_dtrsm_rltu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_runn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular with unit diagonal +void blasfeo_ref_dtrsm_runu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_dtrsm_rutn(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular with unit diagonal +void blasfeo_ref_dtrsm_rutu(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sD, int di, int dj); + +// diagonal + +// D <= alpha * A * B + beta * C, with A diagonal (stored as strvec) +void dgemm_diag_left_lib(int m, int n, double alpha, double *dA, double *pB, int sdb, double beta, double *pC, int sdc, double *pD, int sdd); +void blasfeo_ref_dgemm_dn(int m, int n, double alpha, struct blasfeo_dvec *sA, int ai, struct blasfeo_dmat *sB, int bi, int bj, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= alpha * A * B + beta * C, with B diagonal (stored as strvec) +void blasfeo_ref_dgemm_nd(int m, int n, double alpha, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dvec *sB, int bi, double beta, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); + + + +// +// LAPACK +// + +// D <= chol( C ) ; C, D lower triangular +void blasfeo_ref_dpotrf_l(int m, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_ref_dpotrf_l_mn(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= chol( C ) ; C, D upper triangular +void blasfeo_ref_dpotrf_u(int m, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= chol( C + A * B' ) ; C, D lower triangular +void blasfeo_ref_dsyrk_dpotrf_ln(int m, int k, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +void blasfeo_ref_dsyrk_dpotrf_ln_mn(int m, int n, int k, struct blasfeo_dmat *sA, int ai, int aj, struct blasfeo_dmat *sB, int bi, int bj, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= lu( C ) ; no pivoting +void blasfeo_ref_dgetrf_np(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj); +// D <= lu( C ) ; row pivoting +void blasfeo_ref_dgetrf_rp(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, int *ipiv); +// D <= qr( C ) +int blasfeo_ref_dgeqrf_worksize(int m, int n); // in bytes +void blasfeo_ref_dgeqrf(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// D <= Q factor, where C is the output of the LQ factorization +int blasfeo_ref_dorglq_worksize(int m, int n, int k); // in bytes +void blasfeo_ref_dorglq(int m, int n, int k, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// D <= lq( C ) +void blasfeo_ref_dgelqf(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +int blasfeo_ref_dgelqf_worksize(int m, int n); // in bytes +// D <= lq( C ), positive diagonal elements +void blasfeo_ref_dgelqf_pd(int m, int n, struct blasfeo_dmat *sC, int ci, int cj, struct blasfeo_dmat *sD, int di, int dj, void *work); +// [L, A] <= lq( [L, A] ), positive diagonal elements, array of matrices, with +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_ref_dgelqf_pd_la(int m, int n1, struct blasfeo_dmat *sL, int li, int lj, struct blasfeo_dmat *sA, int ai, int aj, void *work); +// [L, L, A] <= lq( [L, L, A] ), positive diagonal elements, array of matrices, with: +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_ref_dgelqf_pd_lla(int m, int n1, struct blasfeo_dmat *sL0, int l0i, int l0j, struct blasfeo_dmat *sL1, int l1i, int l1j, struct blasfeo_dmat *sA, int ai, int aj, void *work); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_BLASFEO_REF_API_H_ + diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_d_kernel.h b/phonelibs/acados/include/blasfeo/include/blasfeo_d_kernel.h new file mode 100644 index 0000000000..bf110c2bf8 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_d_kernel.h @@ -0,0 +1,1048 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_D_KERNEL_H_ +#define BLASFEO_D_KERNEL_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// utils +void blasfeo_align_2MB(void *ptr, void **ptr_align); +void blasfeo_align_4096_byte(void *ptr, void **ptr_align); +void blasfeo_align_64_byte(void *ptr, void **ptr_align); + + + +// level 2 BLAS +// 12 +void kernel_dgemv_n_12_lib4(int k, double *alpha, double *A, int sda, double *x, double *beta, double *y, double *z); +void kernel_dgemv_t_12_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *x, double *beta, double *y, double *z); +// 8 +void kernel_dgemv_n_8_lib4(int k, double *alpha, double *A, int sda, double *x, double *beta, double *y, double *z); +void kernel_dgemv_t_8_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *x, double *beta, double *y, double *z); +void kernel_dtrmv_un_8_lib4(int k, double *A, int sda, double *x, double *z); +// 4 +void kernel_dgemv_n_4_lib4(int k, double *alpha, double *A, double *x, double *beta, double *y, double *z); +void kernel_dgemv_n_4_vs_lib4(int k, double *alpha, double *A, double *x, double *beta, double *y, double *z, int k1); +void kernel_dgemv_n_4_gen_lib4(int kmax, double *alpha, double *A, double *x, double *beta, double *y, double *z, int k0, int k1); +void kernel_dgemv_t_4_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *x, double *beta, double *y, double *z); +void kernel_dgemv_t_4_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *x, double *beta, double *y, double *z, int k1); +void kernel_dtrsv_ln_inv_4_lib4(int k, double *A, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_ln_inv_4_vs_lib4(int k, double *A, double *inv_diag_A, double *x, double *y, double *z, int km, int kn); +void kernel_dtrsv_lt_inv_4_lib4(int k, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_lt_inv_3_lib4(int k, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_lt_inv_2_lib4(int k, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_lt_inv_1_lib4(int k, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_lt_one_4_lib4(int k, double *A, int sda, double *x, double *y, double *z); +void kernel_dtrsv_lt_one_3_lib4(int k, double *A, int sda, double *x, double *y, double *z); +void kernel_dtrsv_lt_one_2_lib4(int k, double *A, int sda, double *x, double *y, double *z); +void kernel_dtrsv_lt_one_1_lib4(int k, double *A, int sda, double *x, double *y, double *z); +void kernel_dtrsv_ln_one_4_vs_lib4(int kmax, double *A, double *x, double *y, double *z, int km, int kn); +void kernel_dtrsv_ln_one_4_lib4(int kmax, double *A, double *x, double *y, double *z); +void kernel_dtrsv_un_inv_4_lib4(int kmax, double *A, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_ut_inv_4_lib4(int kmax, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z); +void kernel_dtrsv_ut_inv_4_vs_lib4(int kmax, double *A, int sda, double *inv_diag_A, double *x, double *y, double *z, int m1, int n1); +void kernel_dtrmv_un_4_lib4(int k, double *A, double *x, double *z); +void kernel_dtrmv_ut_4_lib4(int k, double *A, int sda, double *x, double *z); +void kernel_dtrmv_ut_4_vs_lib4(int k, double *A, int sda, double *x, double *z, int km); +void kernel_dgemv_nt_6_lib4(int kmax, double *alpha_n, double *alpha_t, double *A, int sda, double *x_n, double *x_t, double *beta_t, double *y_t, double *z_n, double *z_t); +void kernel_dgemv_nt_4_lib4(int kmax, double *alpha_n, double *alpha_t, double *A, int sda, double *x_n, double *x_t, double *beta_t, double *y_t, double *z_n, double *z_t); +void kernel_dgemv_nt_4_vs_lib4(int kmax, double *alpha_n, double *alpha_t, double *A, int sda, double *x_n, double *x_t, double *beta_t, double *y_t, double *z_n, double *z_t, int km); +void kernel_dsymv_l_4_lib4(int kmax, double *alpha, double *A, int sda, double *x, double *z); +void kernel_dsymv_l_4_gen_lib4(int kmax, double *alpha, int offA, double *A, int sda, double *x, double *z, int km); + + + +// level 3 BLAS +// 12x4 +void kernel_dgemm_nt_12x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nt_12x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // +void kernel_dgemm_nt_12x4_gen_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int k0, int k1); +void kernel_dgemm_nn_12x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_12x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // +void kernel_dgemm_nn_12x4_gen_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dgemm_tt_12x4_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_tt_12x4_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dsyrk_nn_u_12x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dsyrk_nn_u_12x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dsyrk_nt_l_12x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dsyrk_nt_l_12x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // +void kernel_dtrmm_nt_ru_12x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *D, int sdd); // +void kernel_dtrmm_nt_ru_12x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *D, int sdd, int km, int kn); // +void kernel_dtrmm_nn_rl_12x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *D, int sdd); +void kernel_dtrmm_nn_rl_12x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *D, int sdd, int km, int kn); +void kernel_dtrsm_nt_rl_inv_12x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_12x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_rl_one_12x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E); +void kernel_dtrsm_nt_rl_one_12x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, int km, int kn); +void kernel_dtrsm_nt_ru_inv_12x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_ru_inv_12x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_ru_one_12x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E); +void kernel_dtrsm_nt_ru_one_12x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, int km, int kn); +void kernel_dtrsm_nn_ru_inv_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nn_ru_inv_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_inv_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E); +void kernel_dtrsm_nn_ll_inv_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_one_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde); +void kernel_dtrsm_nn_ll_one_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, int km, int kn); +void kernel_dtrsm_nn_lu_inv_12x4_lib4(int kmax, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E); +void kernel_dtrsm_nn_lu_inv_12x4_vs_lib4(int kmax, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +// 4x12 +void kernel_dgemm_nt_4x12_lib4(int k, double *alpha, double *A, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nt_4x12_vs_lib4(int k, double *alpha, double *A, double *B, int sdb, double *beta, double *C, double *D, int km, int kn); // +void kernel_dgemm_nn_4x12_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_4x12_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_tt_4x12_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_tt_4x12_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_tt_4x12_gen_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dtrsm_nt_rl_inv_4x12_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *E, int sed, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_4x12_vs_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *E, int sed, double *inv_diag_E, int km, int kn); +// 8x8 +void kernel_dgemm_nt_8x8l_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // computes [A00 *; A10 A11] +void kernel_dgemm_nt_8x8u_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // computes [A00 *; A10 A11] +void kernel_dgemm_nt_8x8l_vs_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // computes [A00 *; A10 A11] +void kernel_dgemm_nt_8x8u_vs_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // computes [A00 *; A10 A11] +void kernel_dsyrk_nn_u_8x8_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dsyrk_nn_u_8x8_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dsyrk_nt_l_8x8_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // computes [L00 *; A10 L11] +void kernel_dsyrk_nt_l_8x8_vs_lib4(int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // computes [L00 *; A10 L11] +void kernel_dtrsm_nt_rl_inv_8x8l_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sed, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_8x8l_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sed, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_rl_inv_8x8u_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sed, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_8x8u_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sed, double *inv_diag_E, int km, int kn); +// 8x4 +void kernel_dgemm_nt_8x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nt_8x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // +void kernel_dgemm_nt_8x4_gen_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int k0, int k1); +void kernel_dgemm_nn_8x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_8x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_8x4_gen_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dgemm_tt_8x4_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_tt_8x4_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dsyrk_nn_u_8x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dsyrk_nn_u_8x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dsyrk_nt_l_8x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dsyrk_nt_l_8x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, int km, int kn); // +void kernel_dsyrk_nt_l_8x4_gen_lib4(int k, double *alpha, double *A, int sda, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int k0, int k1); +void kernel_dtrmm_nt_ru_8x4_lib4(int k, double *alpha, double *A, int sda, double *B, double *D, int sdd); // +void kernel_dtrmm_nt_ru_8x4_vs_lib4(int k, double *alpha, double *A, int sda, double *B, double *D, int sdd, int km, int kn); // +void kernel_dtrmm_nn_rl_8x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *D, int sdd); +void kernel_dtrmm_nn_rl_8x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *D, int sdd, int km, int kn); +void kernel_dtrmm_nn_rl_8x4_gen_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_dtrsm_nt_rl_inv_8x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_rl_inv_8x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_rl_one_8x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E); +void kernel_dtrsm_nt_rl_one_8x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, int km, int kn); +void kernel_dtrsm_nt_ru_inv_8x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_ru_inv_8x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_ru_one_8x4_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E); +void kernel_dtrsm_nt_ru_one_8x4_vs_lib4(int k, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd, double *E, int km, int kn); +void kernel_dtrsm_nn_ru_inv_8x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dtrsm_nn_ru_inv_8x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_inv_8x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E); +void kernel_dtrsm_nn_ll_inv_8x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_one_8x4_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde); +void kernel_dtrsm_nn_ll_one_8x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int sde, int km, int kn); +void kernel_dtrsm_nn_lu_inv_8x4_lib4(int kmax, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E); +void kernel_dtrsm_nn_lu_inv_8x4_vs_lib4(int kmax, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +// 4x8 +void kernel_dgemm_nt_4x8_lib4(int k, double *alpha, double *A, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nt_4x8_vs_lib4(int k, double *alpha, double *A, double *B, int sdb, double *beta, double *C, double *D, int km, int kn); // +void kernel_dgemm_nn_4x8_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_4x8_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_tt_4x8_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_tt_4x8_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_tt_4x8_gen_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, int sdb, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dtrsm_nt_rl_inv_4x8_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *E, int sed, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_4x8_vs_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *E, int sed, double *inv_diag_E, int km, int kn); +// 8x2 +void kernel_dgemm_nn_8x2_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_8x2_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +// 2x8 +void kernel_dgemm_nn_2x8_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_2x8_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +// 10xX +void kernel_dgemm_nn_10x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_10x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_10x2_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_10x2_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +// 6xX +void kernel_dgemm_nn_8x6_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_8x6_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_6x8_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_6x8_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_6x6_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_6x6_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_6x4_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_6x4_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +void kernel_dgemm_nn_6x2_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd); // +void kernel_dgemm_nn_6x2_vs_lib4(int k, double *alpha, double *A, int sda, int offsetB, double *B, int sdb, double *beta, double *C, int sdc, double *D, int sdd, int m1, int n1); // +// 4x4 +void kernel_dgemm_nt_4x4_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dgemm_nt_4x4_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +void kernel_dgemm_nt_4x4_gen_lib4(int k, double *alpha, double *A, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_dgemm_nn_4x4_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_4x4_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_nn_4x4_gen_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dgemm_tt_4x4_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, double *D); // +void kernel_dgemm_tt_4x4_vs_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_tt_4x4_gen_lib4(int k, double *alpha, int offsetA, double *A, int sda, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_dsyrk_nn_u_4x4_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dsyrk_nn_u_4x4_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dsyrk_nt_l_4x4_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dsyrk_nt_l_4x4_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +void kernel_dsyrk_nt_l_4x4_gen_lib4(int k, double *alpha, double *A, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_dsyrk_nt_u_4x4_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dsyrk_nt_u_4x4_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +void kernel_dsyrk_nt_u_4x4_gen_lib4(int k, double *alpha, double *A, double *B, double *beta, int offsetC, double *C, int sdc, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_dtrmm_nt_ru_4x4_lib4(int k, double *alpha, double *A, double *B, double *D); // +void kernel_dtrmm_nt_ru_4x4_vs_lib4(int k, double *alpha, double *A, double *B, double *D, int km, int kn); // +void kernel_dtrmm_nn_rl_4x4_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *D); +void kernel_dtrmm_nn_rl_4x4_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *D, int m1, int n1); +void kernel_dtrmm_nn_rl_4x4_gen_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, int offsetD, double *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_dtrsm_nt_rl_inv_4x4_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_4x4_vs_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_rl_one_4x4_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E); +void kernel_dtrsm_nt_rl_one_4x4_vs_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, int km, int kn); +void kernel_dtrsm_nt_ru_inv_4x4_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_ru_inv_4x4_vs_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nt_ru_one_4x4_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E); +void kernel_dtrsm_nt_ru_one_4x4_vs_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, int km, int kn); +void kernel_dtrsm_nn_ru_inv_4x4_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nn_ru_inv_4x4_vs_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_inv_4x4_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nn_ll_inv_4x4_vs_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_ll_one_4x4_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E); +void kernel_dtrsm_nn_ll_one_4x4_vs_lib4(int k, double *A, double *B, int sdb, double *beta, double *C, double *D, double *E, int km, int kn); +void kernel_dtrsm_nn_lu_inv_4x4_lib4(int kmax, double *A, double *B, int sdb, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nn_lu_inv_4x4_vs_lib4(int kmax, double *A, double *B, int sdb, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dtrsm_nn_lu_one_4x4_lib4(int kmax, double *A, double *B, int sdb, double *C, double *D, double *E); +void kernel_dtrsm_nn_lu_one_4x4_vs_lib4(int kmax, double *A, double *B, int sdb, double *C, double *D, double *E, int km, int kn); +// 4x2 +void kernel_dgemm_nn_4x2_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_4x2_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +void kernel_dgemm_nt_4x2_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); +void kernel_dgemm_nt_4x2_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int m1, int n1); +void kernel_dsyrk_nt_l_4x2_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dsyrk_nt_l_4x2_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +void kernel_dtrmm_nn_rl_4x2_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *D); +void kernel_dtrmm_nn_rl_4x2_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *D, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_4x2_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dtrsm_nt_rl_inv_4x2_vs_lib4(int k, double *A, double *B, double *beta, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +// 2x4 +void kernel_dgemm_nn_2x4_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dgemm_nn_2x4_vs_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D, int m1, int n1); // +// 2x2 +void kernel_dgemm_nn_2x2_lib4(int k, double *alpha, double *A, int offsetB, double *B, int sdb, double *beta, double *C, double *D); // +void kernel_dsyrk_nt_l_2x2_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dsyrk_nt_l_2x2_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +// diag +void kernel_dgemm_diag_right_4_a0_lib4(int kmax, double *alpha, double *A, int sda, double *B, double *D, int sdd); +void kernel_dgemm_diag_right_4_lib4(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); +void kernel_dgemm_diag_right_3_lib4(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); +void kernel_dgemm_diag_right_2_lib4(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); +void kernel_dgemm_diag_right_1_lib4(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int sdd); +void kernel_dgemm_diag_left_4_a0_lib4(int kmax, double *alpha, double *A, double *B, double *D); +void kernel_dgemm_diag_left_4_lib4(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D); +void kernel_dgemm_diag_left_3_lib4(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D); +void kernel_dgemm_diag_left_2_lib4(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D); +void kernel_dgemm_diag_left_1_lib4(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D); +// low rank update +void kernel_dger4_sub_12r_lib4(int k, double *A, int sda, double *B, double *C, int sdc); +void kernel_dger4_sub_12r_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, int km); +void kernel_dger4_sub_8r_lib4(int k, double *A, int sda, double *B, double *C, int sdc); +void kernel_dger4_sub_8r_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, int km); +void kernel_dger4_sub_4r_lib4(int n, double *A, double *B, double *C); +void kernel_dger4_sub_4r_vs_lib4(int n, double *A, double *B, double *C, int km); + + + +// LAPACK +// 12x4 +void kernel_dpotrf_nt_l_12x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dpotrf_nt_l_12x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nn_l_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nn_l_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nn_m_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nn_m_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nn_r_12x4_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nn_r_12x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_l_12x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nt_l_12x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_m_12x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nt_m_12x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_r_12x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nt_r_12x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +// 8x8 +void kernel_dpotrf_nt_l_8x8_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dpotrf_nt_l_8x8_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +// 8x4 +void kernel_dpotrf_nt_l_8x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dpotrf_nt_l_8x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nn_l_8x4_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nn_l_8x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nn_r_8x4_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nn_r_8x4_vs_lib4(int k, double *A, int sda, double *B, int sdb, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_l_8x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nt_l_8x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_r_8x4_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dgetrf_nt_r_8x4_vs_lib4(int k, double *A, int sda, double *B, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +// 4x4 +void kernel_dpotrf_nt_l_4x4_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D); +void kernel_dpotrf_nt_l_4x4_vs_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D, int km, int kn); +#if defined(TARGET_X64_INTEL_SANDY_BRIDGE) +void kernel_dlauum_nt_4x4_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D); // +void kernel_dlauum_nt_4x4_vs_lib4(int k, double *alpha, double *A, double *B, double *beta, double *C, double *D, int km, int kn); // +#endif +void kernel_dgetrf_nn_4x4_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *inv_diag_D); +void kernel_dgetrf_nn_4x4_vs_lib4(int k, double *A, double *B, int sdb, double *C, double *D, double *inv_diag_D, int km, int kn); +void kernel_dgetrf_nt_4x4_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D); +void kernel_dgetrf_nt_4x4_vs_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D, int km, int kn); +void kernel_dgeqrf_4_lib4(int m, double *pD, int sdd, double *dD); +void kernel_dgeqrf_vs_lib4(int m, int n, int k, int offD, double *pD, int sdd, double *dD); +void kernel_dlarf_4_lib4(int m, int n, double *pV, int sdv, double *tau, double *pC, int sdc); // rank-4 reflector +void kernel_dlarf_t_4_lib4(int m, int n, double *pD, int sdd, double *pVt, double *dD, double *pC0, int sdc, double *pW); +void kernel_dgelqf_4_lib4(int n, double *pD, double *dD); +void kernel_dgelqf_vs_lib4(int m, int n, int k, int offD, double *pD, int sdd, double *dD); +void kernel_dlarft_4_lib4(int kmax, double *pD, double *dD, double *pT); +void kernel_dlarft_3_lib4(int kmax, double *pD, double *dD, double *pT); +void kernel_dlarft_2_lib4(int kmax, double *pD, double *dD, double *pT); +void kernel_dlarft_1_lib4(int kmax, double *pD, double *dD, double *pT); +void kernel_dgelqf_dlarft12_12_lib4(int n, double *pD, int sdd, double *dD, double *pT); +void kernel_dgelqf_dlarft4_12_lib4(int n, double *pD, int sdd, double *dD, double *pT); +void kernel_dgelqf_dlarft4_8_lib4(int n, double *pD, int sdd, double *dD, double *pT); +void kernel_dgelqf_dlarft4_4_lib4(int n, double *pD, double *dD, double *pT); +void kernel_dlarfb12_rn_12_lib4(int kmax, double *pV, int sdd, double *pT, double *pD, double *pK); +void kernel_dlarfb12_rn_4_lib4(int kmax, double *pV, int sdd, double *pT, double *pD, double *pK, int km); +void kernel_dlarfb4_rn_12_lib4(int kmax, double *pV, double *pT, double *pD, int sdd); +void kernel_dlarfb4_rn_8_lib4(int kmax, double *pV, double *pT, double *pD, int sdd); +void kernel_dlarfb4_rn_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb3_rn_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb2_rn_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb1_rn_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb4_rn_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb3_rn_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb2_rn_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb1_rn_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb4_rt_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb3_rt_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb2_rt_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb1_rt_4_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb4_rt_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb3_rt_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb2_rt_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dlarfb1_rt_1_lib4(int kmax, double *pV, double *pT, double *pD); +void kernel_dgelqf_pd_dlarft12_12_lib4(int n, double *pD, int sdd, double *dD, double *pT); +void kernel_dgelqf_pd_dlarft4_8_lib4(int n, double *pD, int sdd, double *dD, double *pT); +void kernel_dgelqf_pd_dlarft4_4_lib4(int n, double *pD, double *dD, double *pT); +void kernel_dgelqf_pd_4_lib4(int n, double *pD, double *dD); +void kernel_dgelqf_pd_vs_lib4(int m, int n, int k, int offD, double *pD, int sdd, double *dD); +void kernel_dgelqf_pd_la_vs_lib4(int m, int n1, int k, int offD, double *pD, int sdd, double *dD, int offA, double *pA, int sda); +void kernel_dlarft_4_la_lib4(int n1, double *dD, double *pA, double *pT); +void kernel_dlarfb4_rn_12_la_lib4(int n1, double *pVA, double *pT, double *pD, int sdd, double *pA, int sda); +void kernel_dlarfb4_rn_8_la_lib4(int n1, double *pVA, double *pT, double *pD, int sdd, double *pA, int sda); +void kernel_dlarfb4_rn_4_la_lib4(int n1, double *pVA, double *pT, double *pD, double *pA); +void kernel_dlarfb4_rn_1_la_lib4(int n1, double *pVA, double *pT, double *pD, double *pA); +void kernel_dgelqf_pd_lla_vs_lib4(int m, int n0, int n1, int k, int offD, double *pD, int sdd, double *dD, int offL, double *pL, int sdl, int offA, double *pA, int sda); +void kernel_dlarft_4_lla_lib4(int n0, int n1, double *dD, double *pL, double *pA, double *pT); +void kernel_dlarfb4_rn_12_lla_lib4(int n0, int n1, double *pVL, double *pVA, double *pT, double *pD, int sdd, double *pL, int sdl, double *pA, int sda); +void kernel_dlarfb4_rn_8_lla_lib4(int n0, int n1, double *pVL, double *pVA, double *pT, double *pD, int sdd, double *pL, int sdl, double *pA, int sda); +void kernel_dlarfb4_rn_4_lla_lib4(int n0, int n1, double *pVL, double *pVA, double *pT, double *pD, double *pL, double *pA); +void kernel_dlarfb4_rn_1_lla_lib4(int n0, int n1, double *pVL, double *pVA, double *pT, double *pD, double *pL, double *pA); +// 4x2 +void kernel_dpotrf_nt_l_4x2_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D); +void kernel_dpotrf_nt_l_4x2_vs_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D, int km, int kn); +// 2x2 +void kernel_dpotrf_nt_l_2x2_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D); +void kernel_dpotrf_nt_l_2x2_vs_lib4(int k, double *A, double *B, double *C, double *D, double *inv_diag_D, int km, int kn); +// 12 +void kernel_dgetrf_pivot_12_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_12_vs_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv, int n); +// 8 +void kernel_dgetrf_pivot_8_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_8_vs_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv, int n); +// 4 +void kernel_dgetrf_pivot_4_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_4_vs_lib4(int m, double *pA, int sda, double *inv_diag_A, int* ipiv, int n1); +// vector +void kernel_drowsw_lib4(int kmax, double *pA, double *pC); + + + +// merged routines +// 12x4 +void kernel_dgemm_dtrsm_nt_rl_inv_12x4_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dgemm_dtrsm_nt_rl_inv_12x4_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dsyrk_dpotrf_nt_l_12x4_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_12x4_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +// 4x12 +void kernel_dgemm_dtrsm_nt_rl_inv_4x12_vs_lib4(int kp, double *Ap, double *Bp, int sdbp, int km_, double *Am, double *Bm, int sdbm, double *C, double *D, double *E, int sde, double *inv_diag_E, int km, int kn); +// 8x8 +void kernel_dsyrk_dpotrf_nt_l_8x8_lib4(int kp, double *Ap, int sdap, double *Bp, int sdbp, int km_, double *Am, int sdam, double *Bm, int sdbm, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dsyrk_dpotrf_nt_l_8x8_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int sdbp, int km_, double *Am, int sdam, double *Bm, int sdbm, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +void kernel_dgemm_dtrsm_nt_rl_inv_8x8l_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int sdb, int km_, double *Am, int sdam, double *Bm, int sdbm, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +void kernel_dgemm_dtrsm_nt_rl_inv_8x8u_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int sdb, int km_, double *Am, int sdam, double *Bm, int sdbm, double *C, int sdc, double *D, int sdd, double *E, int sde, double *inv_diag_E, int km, int kn); +// 8x4 +void kernel_dgemm_dtrsm_nt_rl_inv_8x4_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E, int km, int kn); +void kernel_dgemm_dtrsm_nt_rl_inv_8x4_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *E, double *inv_diag_E); +void kernel_dsyrk_dpotrf_nt_l_8x4_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *inv_diag_D); +void kernel_dsyrk_dpotrf_nt_l_8x4_vs_lib4(int kp, double *Ap, int sdap, double *Bp, int km_, double *Am, int sdam, double *Bm, double *C, int sdc, double *D, int sdd, double *inv_diag_D, int km, int kn); +// 4x8 +void kernel_dgemm_dtrsm_nt_rl_inv_4x8_vs_lib4(int kp, double *Ap, double *Bp, int sdbp, int km_, double *Am, double *Bm, int sdbm, double *C, double *D, double *E, int sde, double *inv_diag_E, int km, int kn); +// 4x4 +void kernel_dgemm_dtrsm_nt_rl_inv_4x4_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dgemm_dtrsm_nt_rl_inv_4x4_vs_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_4x4_vs_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_4x4_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D); +// 4x2 +void kernel_dgemm_dtrsm_nt_rl_inv_4x2_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *E, double *inv_diag_E); +void kernel_dgemm_dtrsm_nt_rl_inv_4x2_vs_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *E, double *inv_diag_E, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_4x2_vs_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_4x2_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D); +// 2x2 +void kernel_dsyrk_dpotrf_nt_l_2x2_vs_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D, int km, int kn); +void kernel_dsyrk_dpotrf_nt_l_2x2_lib4(int kp, double *Ap, double *Bp, int km_, double *Am, double *Bm, double *C, double *D, double *inv_diag_D); + +/* + * + * Auxiliary routines + * + * cpsc copy and scale, scale + * cp copy + * add + * set and scale + * transpose and scale + * set and scale + * + */ + +// copy and scale +void kernel_dgecpsc_8_0_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgecpsc_8_1_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgecpsc_8_2_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgecpsc_8_3_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B, int sdb); + +void kernel_dgecpsc_4_0_lib4(int tri, int kmax, double alpha, double *A, double *B); +void kernel_dgecpsc_4_1_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgecpsc_4_2_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgecpsc_4_3_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); + +void kernel_dgecpsc_3_0_lib4(int tri, int kmax, double alpha, double *A, double *B); +void kernel_dgecpsc_3_2_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgecpsc_3_3_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); + +void kernel_dgecpsc_2_0_lib4(int tri, int kmax, double alpha, double *A, double *B); +void kernel_dgecpsc_2_3_lib4(int tri, int kmax, double alpha, double *A0, int sda, double *B); + +void kernel_dgecpsc_1_0_lib4(int tri, int kmax, double alpha, double *A, double *B); + +// copy only +void kernel_dgecp_8_0_lib4(int tri, int kmax, double *A, int sda, double *B, int sdb); + +void kernel_dgecp_4_0_lib4(int tri, int kmax, double *A, double *B); + +// add +void kernel_dgead_8_0_lib4(int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgead_8_1_lib4(int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgead_8_2_lib4(int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgead_8_3_lib4(int kmax, double alpha, double *A0, int sda, double *B, int sdb); +void kernel_dgead_4_0_lib4(int kmax, double alpha, double *A, double *B); +void kernel_dgead_4_1_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_4_2_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_4_3_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_3_0_lib4(int kmax, double alpha, double *A, double *B); +void kernel_dgead_3_2_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_3_3_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_2_0_lib4(int kmax, double alpha, double *A, double *B); +void kernel_dgead_2_3_lib4(int kmax, double alpha, double *A0, int sda, double *B); +void kernel_dgead_1_0_lib4(int kmax, double alpha, double *A, double *B); + +// set +void kernel_dgeset_4_lib4(int kmax, double alpha, double *A); +void kernel_dtrset_4_lib4(int kmax, double alpha, double *A); + +// traspose +void kernel_dgetr_8_lib4(int tri, int kmax, int kna, double alpha, double *A, int sda, double *C, int sdc); +void kernel_dgetr_4_lib4(int tri, int kmax, int kna, double alpha, double *A, double *C, int sdc); +void kernel_dgetr_3_lib4(int tri, int kmax, int kna, double alpha, double *A, double *C, int sdc); +void kernel_dgetr_2_lib4(int tri, int kmax, int kna, double alpha, double *A, double *C, int sdc); +void kernel_dgetr_1_lib4(int tri, int kmax, int kna, double alpha, double *A, double *C, int sdc); +void kernel_dgetr_4_0_lib4(int m, double *A, int sda, double *B); + + + +// pack +// 12 +void kernel_dpack_nn_12_lib4(int kmax, double *A, int lda, double *B, int sdb); +void kernel_dpack_nn_12_vs_lib4(int kmax, double *A, int lda, double *B, int sdb, int m1); +void kernel_dpack_tt_12_lib4(int kmax, double *A, int lda, double *B, int sdb); +// 8 +void kernel_dpack_nn_8_lib4(int kmax, double *A, int lda, double *B, int sdb); +void kernel_dpack_nn_8_vs_lib4(int kmax, double *A, int lda, double *B, int sdb, int m1); +void kernel_dpack_tt_8_lib4(int kmax, double *A, int lda, double *B, int sdb); +// 4 +void kernel_dpack_nn_4_lib4(int kmax, double *A, int lda, double *B); +void kernel_dpack_nn_4_vs_lib4(int kmax, double *A, int lda, double *B, int m1); +void kernel_dpack_tn_4_p0_lib4(int kmax, double *A, int lda, double *B); +void kernel_dpack_tn_4_lib4(int kmax, double *A, int lda, double *B); +void kernel_dpack_tn_4_vs_lib4(int kmax, double *A, int lda, double *B, int m1); +void kernel_dpack_tt_4_lib4(int kmax, double *A, int lda, double *B, int sdb); +void kernel_dpack_tt_4_vs_lib4(int kmax, double *A, int lda, double *B, int sdb, int m1); +// unpack +// 12 +void kernel_dunpack_nn_12_lib4(int kmax, double *A, int sda, double *B, int ldb); +void kernel_dunpack_nn_12_vs_lib4(int kmax, double *A, int sda, double *B, int ldb, int m1); +void kernel_dunpack_tt_12_lib4(int kmax, double *A, int sda, double *B, int ldb); +// 8 +void kernel_dunpack_nn_8_lib4(int kmax, double *A, int sda, double *B, int ldb); +void kernel_dunpack_nn_8_vs_lib4(int kmax, double *A, int sda, double *B, int ldb, int m1); +void kernel_dunpack_tt_8_lib4(int kmax, double *A, int sda, double *B, int ldb); +// 4 +void kernel_dunpack_nn_4_lib4(int kmax, double *A, double *B, int ldb); +void kernel_dunpack_nn_4_vs_lib4(int kmax, double *A, double *B, int ldb, int m1); +void kernel_dunpack_nt_4_lib4(int kmax, double *A, double *B, int ldb); +void kernel_dunpack_nt_4_vs_lib4(int kmax, double *A, double *B, int ldb, int m1); +void kernel_dunpack_tt_4_lib4(int kmax, double *A, int sda, double *B, int ldb); + +// panel copy +// 12 +void kernel_dpacp_nn_12_lib4(int kmax, int offsetA, double *A, int sda, double *B, int sdb); +void kernel_dpacp_nn_12_vs_lib4(int kmax, int offsetA, double *A, int sda, double *B, int sdb, int m1); +// 8 +void kernel_dpacp_nn_8_lib4(int kmax, int offsetA, double *A, int sda, double *B, int sdb); +void kernel_dpacp_nn_8_vs_lib4(int kmax, int offsetA, double *A, int sda, double *B, int sdb, int m1); +// 4 +void kernel_dpacp_nt_4_lib4(int kmax, double *A, int offsetB, double *B, int sdb); +void kernel_dpacp_tn_4_lib4(int kmax, int offsetA, double *A, int sda, double *B); +void kernel_dpacp_nn_4_lib4(int kmax, int offsetA, double *A, int sda, double *B); +void kernel_dpacp_nn_4_vs_lib4(int kmax, int offsetA, double *A, int sda, double *B, int m1); + + + +/************************************************ +* BLAS API kernels +************************************************/ + +//#if defined(BLAS_API) + +// A, B panel-major bs=4; C, D column-major +// 12x4 +void kernel_dgemm_nt_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_12x4_p0_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *A_p, double *B_p); +void kernel_dsyrk_nt_l_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_u_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_u_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_12x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_12x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_12x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_rl_inv_12x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_12x4_lib44ccc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_12x4_vs_lib44ccc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_12x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_rl_one_12x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_12x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_ru_inv_12x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_12x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_ru_one_12x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dpotrf_nt_l_12x4_lib44cc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *dD); +void kernel_dpotrf_nt_l_12x4_vs_lib44cc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *dD, int m1, int n1); +// 4x12 +void kernel_dgemm_nt_4x12_lib44cc(int kmax, double *alpha, double *A, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x12_vs_lib44cc(int kmax, double *alpha, double *A, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x12_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x12_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x12_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x12_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x12_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x12_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x12_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x12_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +// 8x8 +void kernel_dsyrk_nt_l_8x8_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_8x8_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_u_8x8_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_u_8x8_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dpotrf_nt_l_8x8_lib44cc(int kmax, double *A, int sda, double *B, int sdb, double *C, int ldc, double *D, int ldd, double *dD); +void kernel_dpotrf_nt_l_8x8_vs_lib44cc(int kmax, double *A, int sda, double *B, int sdb, double *C, int ldc, double *D, int ldd, double *dD, int m1, int n1); +// 8x4 +void kernel_dgemm_nt_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_8x4_p0_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *A_p, double *B_p); +void kernel_dgemm_nt_8x4_p_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *A_p); +void kernel_dsyrk_nt_l_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_u_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_u_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_8x4_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_8x4_vs_lib44cc(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_8x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_rl_inv_8x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_8x4_lib44ccc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_8x4_vs_lib44ccc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_8x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_rl_one_8x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_8x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_ru_inv_8x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_8x4_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_ru_one_8x4_vs_lib44cc4(int kmax, double *A, int sda, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dpotrf_nt_l_8x4_lib44cc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *dD); +void kernel_dpotrf_nt_l_8x4_vs_lib44cc(int kmax, double *A, int sda, double *B, double *C, int ldc, double *D, int ldd, double *dD, int m1, int n1); +// 4x8 +void kernel_dgemm_nt_4x8_lib44cc(int kmax, double *alpha, double *A, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x8_vs_lib44cc(int kmax, double *alpha, double *A, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x8_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x8_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x8_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x8_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x8_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x8_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x8_tran_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x8_tran_vs_lib444c(int kmax, double *alpha, double *A, int sda, double *B, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +// 4x4 +void kernel_dgemm_nt_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_l_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_u_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_u_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x4_tran_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x4_tran_vs_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x4_tran_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x4_tran_vs_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x4_tran_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x4_tran_vs_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x4_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x4_vs_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x4_tran_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x4_tran_vs_lib444c(int kmax, double *alpha, double *A, double *B, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_4x4_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_rl_inv_4x4_vs_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_4x4_lib44ccc(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_4x4_vs_lib44ccc(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_4x4_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_rl_one_4x4_vs_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_4x4_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE); +void kernel_dtrsm_nt_ru_inv_4x4_vs_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_4x4_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E); +void kernel_dtrsm_nt_ru_one_4x4_vs_lib44cc4(int kmax, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd, double *E, int m1, int n1); +void kernel_dpotrf_nt_l_4x4_lib44cc(int kmax, double *A, double *B, double *C, int ldc, double *D, int ldd, double *dD); +void kernel_dpotrf_nt_l_4x4_vs_lib44cc(int kmax, double *A, double *B, double *C, int ldc, double *D, int ldd, double *dD, int m1, int n1); +// 4x2 +void kernel_dgemm_nt_4x2_lib44cc(int kmax, double *alpha, double *A, double *B, double *beta, double *C, int ldc, double *D, int ldd); + +// A panel-major bs=4; B, C, D column-major +// 12x4 +void kernel_dgemm_nn_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_l_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_12x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_12x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nn_ll_one_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ll_one_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_rl_inv_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_rl_one_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_rl_one_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ru_one_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_12x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_ru_one_12x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dgetrf_nn_l_12x4_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_l_12x4_vs_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); +void kernel_dgetrf_nn_m_12x4_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_m_12x4_vs_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); +void kernel_dgetrf_nn_r_12x4_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_r_12x4_vs_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); +// 4x12 +void kernel_dgemm_nn_4x12_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x12_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x12_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x12_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_rl_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_ru_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x12_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x12_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +// 8x4 +void kernel_dgemm_nn_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_l_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_8x4_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_8x4_vs_lib4ccc(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nn_ll_one_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ll_one_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_rl_inv_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_rl_one_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_rl_one_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ru_one_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_8x4_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_ru_one_8x4_vs_lib4cccc(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dgetrf_nn_l_8x4_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_l_8x4_vs_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); +void kernel_dgetrf_nn_r_8x4_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_r_8x4_vs_lib4ccc(int kmax, double *A, int sda, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); +// 4x8 +void kernel_dgemm_nn_4x8_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x8_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x8_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x8_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_rl_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_ru_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x8_tran_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x8_tran_vs_lib4c4c(int kmax, double *alpha, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int ldd, int m1, int n1); +// 4x4 +void kernel_dgemm_nn_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dsyrk_nt_l_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dsyrk_nt_l_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nn_rl_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_rl_one_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nn_rl_one_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nn_ru_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nn_ru_one_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nn_ru_one_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_rl_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_rl_one_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_rl_one_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_ru_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x4_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x4_vs_lib4ccc(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dtrmm_nt_ru_one_4x4_tran_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd); +void kernel_dtrmm_nt_ru_one_4x4_tran_vs_lib4c4c(int kmax, double *alpha, double *A, double *B, int ldb, double *beta, double *C, double *D, int ldd, int m1, int n1); +void kernel_dtrsm_nn_ll_one_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ll_one_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_rl_inv_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_rl_one_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_rl_one_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nn_ru_one_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_4x4_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde); +void kernel_dtrsm_nt_ru_one_4x4_vs_lib4cccc(int kmax, double *A, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, double *E, int lde, int m1, int n1); +void kernel_dgetrf_nn_4x4_lib4ccc(int kmax, double *A, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag); +void kernel_dgetrf_nn_4x4_vs_lib4ccc(int kmax, double *A, double *B, int ldb, double *C, int ldc, double *D, int ldd, double *diag, int m1, int n1); + +// B panel-major bs=4; A, C, D column-major +// 12x4 +void kernel_dgemm_nt_12x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_12x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_12x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_12x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x12 +void kernel_dgemm_nt_4x12_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x12_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x12_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x12_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 8x4 +void kernel_dgemm_nt_8x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_8x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_8x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_8x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x8 +void kernel_dgemm_nt_4x8_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x8_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x8_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x8_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x4 +void kernel_dgemm_nt_4x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x4_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x4_vs_libc4cc(int kmax, double *alpha, double *A, int lda, double *B, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); + +// A, C, D panel-major; B, E column-major +// TODO merge with above +// 12x4 +void kernel_dtrsm_nn_rl_inv_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nn_rl_one_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nn_ru_one_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nt_rl_one_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_12x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nt_ru_one_12x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +// 8x4 +void kernel_dtrsm_nn_rl_inv_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nn_rl_one_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nn_ru_one_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nt_rl_one_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_8x4_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde); +void kernel_dtrsm_nt_ru_one_8x4_vs_lib4c44c(int kmax, double *A, int sda, double *B, int ldb, double *beta, double *C, int sdc, double *D, int sdd, double *E, int lde, int m1, int n1); +// 4x4 +void kernel_dtrsm_nn_rl_inv_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE); +void kernel_dtrsm_nn_rl_inv_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_rl_one_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde); +void kernel_dtrsm_nn_rl_one_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nn_ru_inv_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE); +void kernel_dtrsm_nn_ru_inv_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nn_ru_one_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde); +void kernel_dtrsm_nn_ru_one_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_rl_inv_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE); +void kernel_dtrsm_nt_rl_inv_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_rl_one_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde); +void kernel_dtrsm_nt_rl_one_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, int m1, int n1); +void kernel_dtrsm_nt_ru_inv_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE); +void kernel_dtrsm_nt_ru_inv_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, double *dE, int m1, int n1); +void kernel_dtrsm_nt_ru_one_4x4_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde); +void kernel_dtrsm_nt_ru_one_4x4_vs_lib4c44c(int kmax, double *A, double *B, int ldb, double *beta, double *C, double *D, double *E, int lde, int m1, int n1); + +// A, B, C, D column-major +// 12x4 +void kernel_dgemm_nn_12x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_12x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_12x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_12x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_12x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_12x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x12 +void kernel_dgemm_nn_4x12_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x12_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x12_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x12_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x12_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x12_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 8x4 +void kernel_dgemm_nn_8x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_8x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_8x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_8x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_8x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_8x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x8 +void kernel_dgemm_nn_4x8_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x8_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x8_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x8_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x8_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x8_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +// 4x4 +void kernel_dgemm_nn_4x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nn_4x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_nt_4x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_nt_4x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); +void kernel_dgemm_tt_4x4_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd); +void kernel_dgemm_tt_4x4_vs_libcccc(int kmax, double *alpha, double *A, int lda, double *B, int ldb, double *beta, double *C, int ldc, double *D, int ldd, int m1, int n1); + +// A column-major +// 12 +void kernel_dgetrf_pivot_12_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_12_vs_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv, int n); +// 8 +void kernel_dgetrf_pivot_8_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_8_vs_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv, int n); +// 4 +void kernel_dgetrf_pivot_4_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv); +void kernel_dgetrf_pivot_4_vs_lib(int m, double *pA, int lda, double *inv_diag_A, int* ipiv, int n); + +// vector +void kernel_ddot_11_lib(int n, double *x, double *y, double *res); +void kernel_daxpy_11_lib(int n, double *alpha, double *x, double *y); +void kernel_drowsw_lib(int kmax, double *pA, int lda, double *pC, int ldc); + +//#endif // BLAS_API + + + +// larger kernels +// 12 +void kernel_dgemm_nt_12xn_p0_lib44cc(int n, int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, double *A_p, double *B_p); +void kernel_dgemm_nt_12xn_pl_lib44cc(int n, int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, double *A_p, double *B_p); +// 8 +void kernel_dgemm_nt_8xn_p0_lib44cc(int n, int k, double *alpha, double *A, int sda, double *B, int sdb, double *beta, double *C, int ldc, double *D, int ldd, double *A_p, double *B_p); + + + +// aux +void kernel_dvecld_inc1(int kmax, double *x); +void kernel_dveccp_inc1(int kmax, double *x, double *y); + + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_D_KERNEL_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_i_aux_ext_dep.h b/phonelibs/acados/include/blasfeo/include/blasfeo_i_aux_ext_dep.h new file mode 100644 index 0000000000..74c3fb5c0a --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_i_aux_ext_dep.h @@ -0,0 +1,69 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_I_AUX_EXT_DEP_H_ +#define BLASFEO_I_AUX_EXT_DEP_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef EXT_DEP + +// i_aux_extern_depend_lib +void int_zeros(int **pA, int row, int col); +void int_zeros_align(int **pA, int row, int col); +void int_free(int *pA); +void int_free_align(int *pA); +void int_print_mat(int row, int col, int *A, int lda); +int int_print_to_string_mat(char **buf_out, int row, int col, int *A, int lda); + +#endif // EXT_DEP + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_I_AUX_EXT_DEP_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_m_aux.h b/phonelibs/acados/include/blasfeo/include/blasfeo_m_aux.h new file mode 100644 index 0000000000..6248853e29 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_m_aux.h @@ -0,0 +1,57 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_M_AUX_H_ +#define BLASFEO_M_AUX_H_ + +#include "blasfeo_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +void blasfeo_cvt_d2s_vec(int m, struct blasfeo_dvec *vd, int vdi, struct blasfeo_svec *vs, int vsi); +void blasfeo_cvt_s2d_vec(int m, struct blasfeo_svec *vs, int vsi, struct blasfeo_dvec *vd, int vdi); +void blasfeo_cvt_d2s_mat(int m, int n, struct blasfeo_dmat *Md, int mid, int nid, struct blasfeo_smat *Ms, int mis, int nis); +void blasfeo_cvt_s2d_mat(int m, int n, struct blasfeo_smat *Ms, int mis, int nis, struct blasfeo_dmat *Md, int mid, int nid); + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_M_AUX_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_memory.h b/phonelibs/acados/include/blasfeo/include/blasfeo_memory.h new file mode 100644 index 0000000000..da4e7fa090 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_memory.h @@ -0,0 +1,62 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2020 by Gianluca Frison. * +* All rights reserved. * +* * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + +#ifndef BLASFEO_MEMORY_H_ +#define BLASFEO_MEMORY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +int blasfeo_is_init(); +// +void blasfeo_init(); +// +void blasfeo_quit(); +// +void *blasfeo_get_buffer(); + + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_MEMORY_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_naming.h b/phonelibs/acados/include/blasfeo/include/blasfeo_naming.h new file mode 100644 index 0000000000..c289443b17 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_naming.h @@ -0,0 +1,77 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + +/* + * ----------- Naming conventions + * + * (precision)(data) + * + * 1) d(double) + * s(single) + * + * 2) ge(general) + * tr(triangular) + * vec(vector) + * row(row) + * col(column) + * dia(diagonal) + * + * 3) se(set) + * cp(copy) + * sc(scale) + * ad(add) + * tr(transpose) + * in(insert) + * ex(extract) + * pe(premute) + * sw(swap) + * + * f(factorization) + * + * lqf(LQ factorization) + * qrf (factorization) + * trf (LU factorization using partial pivoting with row interchanges.) + * + * 4) _l(lower) / _u(upper) + * _lib8 (hp implementation, 8 rows kernel) + * _lib4 (hp implementation, 4 rows kernel) + * _lib0 (hp interface with reference implentation) + * _lib (reference implementation) + * _libref (reference implementation with dedicated namespace) + * + * 5) _sp(sparse) + * _exp(exponential format) + */ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_processor_features.h b/phonelibs/acados/include/blasfeo/include/blasfeo_processor_features.h new file mode 100644 index 0000000000..65a0c7e80a --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_processor_features.h @@ -0,0 +1,88 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Ian McInerney * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_PROCESSOR_FEATURES_H_ +#define BLASFEO_PROCESSOR_FEATURES_H_ + +/** + * Flags to indicate the different processor features + */ +enum +{ + // x86-64 CPU features + BLASFEO_PROCESSOR_FEATURE_AVX = 0x0001, /// AVX instruction set + BLASFEO_PROCESSOR_FEATURE_AVX2 = 0x0002, /// AVX2 instruction set + BLASFEO_PROCESSOR_FEATURE_FMA = 0x0004, /// FMA instruction set + BLASFEO_PROCESSOR_FEATURE_SSE3 = 0x0008, /// SSE3 instruction set + + // ARM CPU features + BLASFEO_PROCESSOR_FEATURE_VFPv3 = 0x0100, /// VFPv3 instruction set + BLASFEO_PROCESSOR_FEATURE_NEON = 0x0100, /// NEON instruction set + BLASFEO_PROCESSOR_FEATURE_VFPv4 = 0x0100, /// VFPv4 instruction set + BLASFEO_PROCESSOR_FEATURE_NEONv2 = 0x0100, /// NEONv2 instruction set +} BLASFEO_PROCESSOR_FEATURES; + +/** + * Test the features that this processor provides against what the library was compiled with. + * + * @param features - Pointer to an integer to store the supported feature set (using the flags in the BLASFEO_PROCESSOR_FEATURES enum) + * @return 0 if current processor doesn't support all features required for this library, 1 otherwise + */ +int blasfeo_processor_cpu_features( int* features ); + +/** + * Test the features that this processor provides against what the library was compiled with. + * + * @param features - Pointer to an integer to store the supported feature set (using the flags in the BLASFEO_PROCESSOR_FEATURES enum) + * @return 0 if current processor doesn't support all features required for this library, 1 otherwise + */ +void blasfeo_processor_library_features( int* features ); + +/** + * Create a string listing the features the current processor supports. + * + * @param features - Flags from the BLASFEO_PROCESSOR_FEATURES enum indicating the features supported + * @param featureString - Character array to store the feature string in + */ +void blasfeo_processor_feature_string( int features, char* featureString ); + +/** + * Get a string listing the processor features that this library version needs to run. + * + * @param featureString - Character array to store the feature string in + */ +void blasfeo_processor_library_string( char* featureString ); + +#endif // BLASFEO_PROCESSOR_FEATURES_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux.h new file mode 100644 index 0000000000..539268849c --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux.h @@ -0,0 +1,166 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_AUX_H_ +#define BLASFEO_S_AUX_H_ + + + +#include + +#include "blasfeo_s_aux_old.h" +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************ +* d_aux_lib.c +************************************************/ + +// returns the memory size (in bytes) needed for a smat +size_t blasfeo_memsize_smat(int m, int n); +size_t blasfeo_memsize_smat_ps(int ps, int m, int n); +// returns the memory size (in bytes) needed for the diagonal of a smat +size_t blasfeo_memsize_diag_smat(int m, int n); +// returns the memory size (in bytes) needed for a svec +size_t blasfeo_memsize_svec(int m); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_create_smat(int m, int n, struct blasfeo_smat *sA, void *memory); +void blasfeo_create_smat_ps(int ps, int m, int n, struct blasfeo_smat *sA, void *memory); +// create a strvec for a vector of size m by using memory passed by a pointer (pointer is not updated) +void blasfeo_create_svec(int m, struct blasfeo_svec *sA, void *memory); +void blasfeo_pack_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_pack_l_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_pack_u_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_pack_tran_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_pack_svec(int m, float *x, int xi, struct blasfeo_svec *sa, int ai); +void blasfeo_unpack_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); +void blasfeo_unpack_tran_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); +void blasfeo_unpack_svec(int m, struct blasfeo_svec *sa, int ai, float *x, int xi); +//void s_cast_mat2strmat(float *A, struct blasfeo_smat *sA); +//void s_cast_diag_mat2strmat(float *dA, struct blasfeo_smat *sA); +//void s_cast_vec2vecmat(float *a, struct blasfeo_svec *sa); + +// ge +void blasfeo_sgese(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sgecpsc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_sgecp(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_sgesc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sgein1(float a, struct blasfeo_smat *sA, int ai, int aj); +float blasfeo_sgeex1(struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sgead(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_sgetr(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// tr +void blasfeo_strcp_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_strtr_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_strtr_u(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// dia +void blasfeo_sdiare(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sdiaex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_sdiain(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sdiain_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_sdiaex_sp(int kmax, float alpha, int *idx, struct blasfeo_smat *sD, int di, int dj, struct blasfeo_svec *sx, int xi); +void blasfeo_sdiaad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_sdiaad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_sdiaadin_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// row +void blasfeo_srowin(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_srowex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_srowad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_srowad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_srowsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_srowpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +void blasfeo_srowpei(int kmax, int *ipiv, struct blasfeo_smat *sA); +// col +void blasfeo_scolex(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_scolin(int kmax, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_scolad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_scolsc(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_scolsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_scolpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +void blasfeo_scolpei(int kmax, int *ipiv, struct blasfeo_smat *sA); +// vec +void blasfeo_svecse(int m, float alpha, struct blasfeo_svec *sx, int xi); +void blasfeo_sveccp(int m, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_svecsc(int m, float alpha, struct blasfeo_svec *sa, int ai); +void blasfeo_sveccpsc(int m, float alpha, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_svecad(int m, float alpha, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_svecin1(float a, struct blasfeo_svec *sx, int xi); +float blasfeo_svecex1(struct blasfeo_svec *sx, int xi); +void blasfeo_svecad_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +void blasfeo_svecin_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +void blasfeo_svecex_sp(int m, float alpha, int *idx, struct blasfeo_svec *sx, int x, struct blasfeo_svec *sz, int zi); +void blasfeo_sveccl(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi); +void blasfeo_sveccl_mask(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi, struct blasfeo_svec *sm, int mi); +void blasfeo_svecze(int m, struct blasfeo_svec *sm, int mi, struct blasfeo_svec *sv, int vi, struct blasfeo_svec *se, int ei); +void blasfeo_svecnrm_inf(int m, struct blasfeo_svec *sx, int xi, float *ptr_norm); +void blasfeo_svecnrm_2(int m, struct blasfeo_svec *sx, int xi, float *ptr_norm); +void blasfeo_svecpe(int kmax, int *ipiv, struct blasfeo_svec *sx, int xi); +void blasfeo_svecpei(int kmax, int *ipiv, struct blasfeo_svec *sx, int xi); + + + +/* +* Explicitly panel-major matrix format +*/ + +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_pm_memsize_smat(int ps, int m, int n); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_pm_create_smat(int ps, int m, int n, struct blasfeo_pm_smat *sA, void *memory); + + + +/* +* Explicitly column-major matrix format +*/ + +// returns the memory size (in bytes) needed for a dmat +size_t blasfeo_cm_memsize_smat(int m, int n); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_cm_create_smat(int m, int n, struct blasfeo_pm_smat *sA, void *memory); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_AUX_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep.h new file mode 100644 index 0000000000..5209d20d37 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep.h @@ -0,0 +1,141 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_AUX_EXT_DEP_H_ +#define BLASFEO_S_AUX_EXT_DEP_H_ + + + +#include + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef EXT_DEP + +/************************************************ +* s_aux_extern_depend_lib.c +************************************************/ + +/* column-major matrices */ + +// dynamically allocate row*col floats of memory and set accordingly a pointer to float; set allocated memory to zero +void s_zeros(float **pA, int row, int col); +// dynamically allocate row*col floats of memory aligned to 64-byte boundaries and set accordingly a pointer to float; set allocated memory to zero +void s_zeros_align(float **pA, int row, int col); +// dynamically allocate size bytes of memory aligned to 64-byte boundaries and set accordingly a pointer to float; set allocated memory to zero +void s_zeros_align_bytes(float **pA, int size); +// free the memory allocated by d_zeros +void s_free(float *pA); +// free the memory allocated by d_zeros_align or d_zeros_align_bytes +void s_free_align(float *pA); +// print a column-major matrix +void s_print_mat(int m, int n, float *A, int lda); +// print the transposed of a column-major matrix +void s_print_tran_mat(int row, int col, float *A, int lda); +// print to file a column-major matrix +void s_print_to_file_mat(FILE *file, int row, int col, float *A, int lda); +// print to file a column-major matrix in exponential format +void s_print_to_file_exp_mat(FILE *file, int row, int col, float *A, int lda); +// print to string a column-major matrix +void s_print_to_string_mat(char **buf_out, int row, int col, float *A, int lda); +// print to file the transposed of a column-major matrix +void s_print_tran_to_file_mat(FILE *file, int row, int col, float *A, int lda); +// print to file the transposed of a column-major matrix in exponential format +void s_print_tran_to_file_exp_mat(FILE *file, int row, int col, float *A, int lda); +// print in exponential notation a column-major matrix +void s_print_exp_mat(int m, int n, float *A, int lda); +// print in exponential notation the transposed of a column-major matrix +void s_print_exp_tran_mat(int row, int col, float *A, int lda); + +/* strmat and strvec */ + +// create a strmat for a matrix of size m*n by dynamically allocating memory +void blasfeo_allocate_smat(int m, int n, struct blasfeo_smat *sA); +// create a strvec for a vector of size m by dynamically allocating memory +void blasfeo_allocate_svec(int m, struct blasfeo_svec *sa); +// free the memory allocated by blasfeo_allocate_dmat +void blasfeo_free_smat(struct blasfeo_smat *sA); +// free the memory allocated by blasfeo_allocate_dvec +void blasfeo_free_svec(struct blasfeo_svec *sa); +// print a strmat +void blasfeo_print_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print in exponential notation a strmat +void blasfeo_print_exp_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print to file a strmat +void blasfeo_print_to_file_smat(FILE *file, int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print to file a strmat in exponential format +void blasfeo_print_to_file_exp_smat(FILE *file, int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print to string a strmat +void blasfeo_print_to_string_smat(char **buf_out, int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print the transpose of a strmat +void blasfeo_print_tran_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj); +// print a strvec +void blasfeo_print_svec(int m, struct blasfeo_svec *sa, int ai); +// print in exponential notation a strvec +void blasfeo_print_exp_svec(int m, struct blasfeo_svec *sa, int ai); +// print to file a strvec +void blasfeo_print_to_file_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); +// print to string a strvec +void blasfeo_print_to_string_svec(char **buf_out, int m, struct blasfeo_svec *sa, int ai); +// print the transposed of a strvec +void blasfeo_print_tran_svec(int m, struct blasfeo_svec *sa, int ai); +// print in exponential notation the transposed of a strvec +void blasfeo_print_exp_tran_svec(int m, struct blasfeo_svec *sa, int ai); +// print to file the transposed of a strvec +void blasfeo_print_to_file_tran_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); +// print to string the transposed of a strvec +void blasfeo_print_to_string_tran_svec(char **buf_out, int m, struct blasfeo_svec *sa, int ai); + +#endif // EXT_DEP + + + +#ifdef __cplusplus +} +#endif + + + +#endif // BLASFEO_S_AUX_EXT_DEP_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep_ref.h new file mode 100644 index 0000000000..6640e20a40 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ext_dep_ref.h @@ -0,0 +1,82 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_AUX_EXT_DEP_REF_H_ +#define BLASFEO_S_AUX_EXT_DEP_REF_H_ + +#if defined(EXT_DEP) + + + +#include + +#include "blasfeo_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// expose reference BLASFEO for testing +// see blasfeo_s_aux_exp_dep.h for help + +void blasfeo_print_smat_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_allocate_smat_ref(int m, int n, struct blasfeo_smat_ref *sA); +void blasfeo_allocate_svec_ref(int m, struct blasfeo_svec_ref *sa); +void blasfeo_free_smat_ref(struct blasfeo_smat_ref *sA); +void blasfeo_free_svec_ref(struct blasfeo_svec_ref *sa); +void blasfeo_print_smat_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_print_exp_smat_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_print_to_file_smat_ref(FILE *file, int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_print_to_file_exp_smat_ref(FILE *file, int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_print_to_string_smat_ref(char **buf_out, int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj); +void blasfeo_print_svec(int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_exp_svec(int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_to_file_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_to_string_svec(char **buf_out, int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_tran_svec(int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_exp_tran_svec(int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_to_file_tran_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); +void blasfeo_print_to_string_tran_svec(char **buf_out, int m, struct blasfeo_svec *sa, int ai); + + +#ifdef __cplusplus +} +#endif + + + +#endif // EXT_DEP + +#endif // BLASFEO_S_AUX_EXT_DEP_REF_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_old.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_old.h new file mode 100644 index 0000000000..5c6db37bab --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_old.h @@ -0,0 +1,64 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +// TODO remove +// +void strcp_l_lib(int m, float alpha, int offsetA, float *A, int sda, int offsetB, float *B, int sdb); +void sgead_lib(int m, int n, float alpha, int offsetA, float *A, int sda, int offsetB, float *B, int sdb); +void sgetr_lib(int m, int n, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +void strtr_l_lib(int m, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +void strtr_u_lib(int m, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +void sdiareg_lib(int kmax, float reg, int offset, float *pD, int sdd); +void sdiain_sqrt_lib(int kmax, float *x, int offset, float *pD, int sdd); +void sdiaex_lib(int kmax, float alpha, int offset, float *pD, int sdd, float *x); +void sdiaad_lib(int kmax, float alpha, float *x, int offset, float *pD, int sdd); +void sdiain_libsp(int kmax, int *idx, float alpha, float *x, float *pD, int sdd); +void sdiaex_libsp(int kmax, int *idx, float alpha, float *pD, int sdd, float *x); +void sdiaad_libsp(int kmax, int *idx, float alpha, float *x, float *pD, int sdd); +void sdiaadin_libsp(int kmax, int *idx, float alpha, float *x, float *y, float *pD, int sdd); +void srowin_lib(int kmax, float alpha, float *x, float *pD); +void srowex_lib(int kmax, float alpha, float *pD, float *x); +void srowad_lib(int kmax, float alpha, float *x, float *pD); +void srowin_libsp(int kmax, float alpha, int *idx, float *x, float *pD); +void srowad_libsp(int kmax, int *idx, float alpha, float *x, float *pD); +void srowadin_libsp(int kmax, int *idx, float alpha, float *x, float *y, float *pD); +void srowsw_lib(int kmax, float *pA, float *pC); +void scolin_lib(int kmax, float *x, int offset, float *pD, int sdd); +void scolad_lib(int kmax, float alpha, float *x, int offset, float *pD, int sdd); +void scolin_libsp(int kmax, int *idx, float *x, float *pD, int sdd); +void scolad_libsp(int kmax, float alpha, int *idx, float *x, float *pD, int sdd); +void scolsw_lib(int kmax, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +void svecin_libsp(int kmax, int *idx, float *x, float *y); +void svecad_libsp(int kmax, int *idx, float alpha, float *x, float *y); diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ref.h new file mode 100644 index 0000000000..f6e16a9577 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_ref.h @@ -0,0 +1,145 @@ + +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_AUX_REF_H_ +#define BLASFEO_S_AUX_REF_H_ + + + +#include + +#include "blasfeo_s_aux_old.h" +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************ +* d_aux_lib.c +************************************************/ + +// returns the memory size (in bytes) needed for a smat +size_t blasfeo_ref_memsize_smat(int m, int n); +size_t blasfeo_ref_memsize_smat_ps(int ps, int m, int n); +// returns the memory size (in bytes) needed for the diagonal of a smat +size_t blasfeo_ref_memsize_diag_smat(int m, int n); +// returns the memory size (in bytes) needed for a svec +size_t blasfeo_ref_memsize_svec(int m); +// create a strmat for a matrix of size m*n by using memory passed by a pointer (pointer is not updated) +void blasfeo_ref_create_smat(int m, int n, struct blasfeo_smat *sA, void *memory); +void blasfeo_ref_create_smat_ps(int ps, int m, int n, struct blasfeo_smat *sA, void *memory); +// create a strvec for a vector of size m by using memory passed by a pointer (pointer is not updated) +void blasfeo_ref_create_svec(int m, struct blasfeo_svec *sA, void *memory); +void blasfeo_ref_pack_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_pack_l_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sB, int bi, int bj); +void blasfeo_ref_pack_l_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sB, int bi, int bj); +void blasfeo_ref_pack_tran_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_pack_svec(int m, float *x, int xi, struct blasfeo_svec *sa, int ai); +void blasfeo_ref_unpack_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); +void blasfeo_ref_unpack_tran_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); +void blasfeo_ref_unpack_svec(int m, struct blasfeo_svec *sa, int ai, float *x, int xi); +void ref_s_cast_mat2strmat(float *A, struct blasfeo_smat *sA); +void ref_s_cast_diag_mat2strmat(float *dA, struct blasfeo_smat *sA); +void ref_s_cast_vec2vecmat(float *a, struct blasfeo_svec *sa); + +// ge +void blasfeo_ref_sgese(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sgecpsc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_sgecp(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_sgesc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sgein1(float a, struct blasfeo_smat *sA, int ai, int aj); +float blasfeo_ref_sgeex1(struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sgead(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_sgetr(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// tr +void blasfeo_ref_strcp_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_strtr_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_strtr_u(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// dia +void blasfeo_ref_sdiare(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sdiaex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_sdiain(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sdiain_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_sdiaex_sp(int kmax, float alpha, int *idx, struct blasfeo_smat *sD, int di, int dj, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_sdiaad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_sdiaad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_sdiaadin_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// row +void blasfeo_ref_srowin(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_srowex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_srowad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_srowad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_srowsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_srowpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +void blasfeo_ref_srowpei(int kmax, int *ipiv, struct blasfeo_smat *sA); +// col +void blasfeo_ref_scolex(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_scolin(int kmax, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_scolad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_scolsc(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj); +void blasfeo_ref_scolsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void blasfeo_ref_scolpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +void blasfeo_ref_scolpei(int kmax, int *ipiv, struct blasfeo_smat *sA); +// vec +void blasfeo_ref_svecse(int m, float alpha, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_sveccp(int m, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_ref_svecsc(int m, float alpha, struct blasfeo_svec *sa, int ai); +void blasfeo_ref_sveccpsc(int m, float alpha, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_ref_svecad(int m, float alpha, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +void blasfeo_ref_svecin1(float a, struct blasfeo_svec *sx, int xi); +float blasfeo_ref_svecex1(struct blasfeo_svec *sx, int xi); +void blasfeo_ref_svecad_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +void blasfeo_ref_svecin_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +void blasfeo_ref_svecex_sp(int m, float alpha, int *idx, struct blasfeo_svec *sx, int x, struct blasfeo_svec *sz, int zi); +void blasfeo_ref_sveccl(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi); +void blasfeo_ref_sveccl_mask(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi, struct blasfeo_svec *sm, int mi); +void blasfeo_ref_svecze(int m, struct blasfeo_svec *sm, int mi, struct blasfeo_svec *sv, int vi, struct blasfeo_svec *se, int ei); +void blasfeo_ref_svecnrm_inf(int m, struct blasfeo_svec *sx, int xi, float *ptr_norm); +void blasfeo_ref_svecpe(int kmax, int *ipiv, struct blasfeo_svec *sx, int xi); +void blasfeo_ref_svecpei(int kmax, int *ipiv, struct blasfeo_svec *sx, int xi); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_AUX_REF_H_ + diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_test.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_test.h new file mode 100644 index 0000000000..08d9a14a6a --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_aux_test.h @@ -0,0 +1,177 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_AUX_TEST_H_ +#define BLASFEO_S_AUX_TEST_H_ + +#include + +#include "blasfeo_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +/************************************************ +* d_aux_lib.c +************************************************/ + +int test_blasfeo_memsize_smat(int m, int n); +int test_blasfeo_memsize_diag_smat(int m, int n); +int test_blasfeo_memsize_svec(int m); + +void test_blasfeo_create_smat(int m, int n, struct blasfeo_smat *sA, void *memory); +void test_blasfeo_create_svec(int m, struct blasfeo_svec *sA, void *memory); + +void test_blasfeo_pack_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void test_blasfeo_pack_svec(int m, float *x, int xi, struct blasfeo_svec *sa, int ai); +void test_blasfeo_pack_tran_smat(int m, int n, float *A, int lda, struct blasfeo_smat *sA, int ai, int aj); +void test_blasfeo_unpack_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); +void test_blasfeo_unpack_svec(int m, struct blasfeo_svec *sa, int ai, float *x, int xi); +void test_blasfeo_unpack_tran_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj, float *A, int lda); + +void test_s_cast_mat2strmat(float *A, struct blasfeo_smat *sA); +void test_s_cast_diag_mat2strmat(float *dA, struct blasfeo_smat *sA); +void test_s_cast_vec2vecmat(float *a, struct blasfeo_svec *sa); +// copy and scale +void test_blasfeo_sgecpsc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void test_blasfeo_sgecp(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +void test_blasfeo_sgesc(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); + +// void test_blasfeo_sgein1(float a, struct blasfeo_smat *sA, int ai, int aj); +// float test_blasfeo_sgeex1(struct blasfeo_smat *sA, int ai, int aj); +// void test_blasfeo_svecin1(float a, struct blasfeo_svec *sx, int xi); +// float test_blasfeo_svecex1(struct blasfeo_svec *sx, int xi); + +// // A <= alpha +// void test_blasfeo_sgese(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj); +// // a <= alpha +// void test_blasfeo_svecse(int m, float alpha, struct blasfeo_svec *sx, int xi); + + +// void test_blasfeo_sveccp(int m, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); +// void test_blasfeo_svecsc(int m, float alpha, struct blasfeo_svec *sa, int ai); + +// void test_strcp_l_lib(int m, float alpha, int offsetA, float *A, int sda, int offsetB, float *B, int sdb); +// void test_blasfeo_strcp_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); + +// void test_sgead_lib(int m, int n, float alpha, int offsetA, float *A, int sda, int offsetB, float *B, int sdb); +// void test_blasfeo_sgead(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// void test_blasfeo_svecad(int m, float alpha, struct blasfeo_svec *sa, int ai, struct blasfeo_svec *sc, int ci); + +// void test_sgetr_lib(int m, int n, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +// void test_blasfeo_sgetr(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); + +// void test_strtr_l_lib(int m, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +// void test_blasfeo_strtr_l(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// void test_strtr_u_lib(int m, float alpha, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +// void test_blasfeo_strtr_u(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); + +// void test_sdiareg_lib(int kmax, float reg, int offset, float *pD, int sdd); +// void test_blasfeo_sdiaex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +// void test_blasfeo_sdiain(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +// void test_sdiain_sqrt_lib(int kmax, float *x, int offset, float *pD, int sdd); +// void test_sdiaex_lib(int kmax, float alpha, int offset, float *pD, int sdd, float *x); +// void test_sdiaad_lib(int kmax, float alpha, float *x, int offset, float *pD, int sdd); +// void test_sdiain_libsp(int kmax, int *idx, float alpha, float *x, float *pD, int sdd); +// void test_blasfeo_sdiain_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// void test_sdiaex_libsp(int kmax, int *idx, float alpha, float *pD, int sdd, float *x); +// void test_blasfeo_sdiaex_sp(int kmax, float alpha, int *idx, struct blasfeo_smat *sD, int di, int dj, struct blasfeo_svec *sx, int xi); +// void test_blasfeo_sdiaad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +// void test_sdiaad_libsp(int kmax, int *idx, float alpha, float *x, float *pD, int sdd); +// void test_blasfeo_sdiaad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// void test_sdiaadin_libsp(int kmax, int *idx, float alpha, float *x, float *y, float *pD, int sdd); +// void test_blasfeo_sdiaadin_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// void test_srowin_lib(int kmax, float alpha, float *x, float *pD); +// void test_blasfeo_srowin(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +// void test_srowex_lib(int kmax, float alpha, float *pD, float *x); +// void test_blasfeo_srowex(int kmax, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi); +// void test_srowad_lib(int kmax, float alpha, float *x, float *pD); +// void test_blasfeo_srowad(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +// void test_srowin_libsp(int kmax, float alpha, int *idx, float *x, float *pD); +// void test_srowad_libsp(int kmax, int *idx, float alpha, float *x, float *pD); +// void test_blasfeo_srowad_sp(int kmax, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_smat *sD, int di, int dj); +// void test_srowadin_libsp(int kmax, int *idx, float alpha, float *x, float *y, float *pD); +// void test_srowsw_lib(int kmax, float *pA, float *pC); +// void test_blasfeo_srowsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// void test_blasfeo_srowpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +// void test_scolin_lib(int kmax, float *x, int offset, float *pD, int sdd); +// void test_blasfeo_scolin(int kmax, struct blasfeo_svec *sx, int xi, struct blasfeo_smat *sA, int ai, int aj); +// void test_scolad_lib(int kmax, float alpha, float *x, int offset, float *pD, int sdd); +// void test_scolin_libsp(int kmax, int *idx, float *x, float *pD, int sdd); +// void test_scolad_libsp(int kmax, float alpha, int *idx, float *x, float *pD, int sdd); +// void test_scolsw_lib(int kmax, int offsetA, float *pA, int sda, int offsetC, float *pC, int sdc); +// void test_blasfeo_scolsw(int kmax, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sC, int ci, int cj); +// void test_blasfeo_scolpe(int kmax, int *ipiv, struct blasfeo_smat *sA); +// void test_svecin_libsp(int kmax, int *idx, float *x, float *y); +// void test_svecad_libsp(int kmax, int *idx, float alpha, float *x, float *y); +// void test_blasfeo_svecad_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +// void test_blasfeo_svecin_sp(int m, float alpha, struct blasfeo_svec *sx, int xi, int *idx, struct blasfeo_svec *sz, int zi); +// void test_blasfeo_svecex_sp(int m, float alpha, int *idx, struct blasfeo_svec *sx, int x, struct blasfeo_svec *sz, int zi); +// void test_blasfeo_sveccl(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi); +// void test_blasfeo_sveccl_mask(int m, struct blasfeo_svec *sxm, int xim, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sxp, int xip, struct blasfeo_svec *sz, int zi, struct blasfeo_svec *sm, int mi); +// void test_blasfeo_svecze(int m, struct blasfeo_svec *sm, int mi, struct blasfeo_svec *sv, int vi, struct blasfeo_svec *se, int ei); +// void test_blasfeo_svecnrm_inf(int m, struct blasfeo_svec *sx, int xi, float *ptr_norm); +// void test_blasfeo_svecpe(int kmax, int *ipiv, struct blasfeo_svec *sx, int xi); + + +// ext_dep + +void test_blasfeo_allocate_smat(int m, int n, struct blasfeo_smat *sA); +void test_blasfeo_allocate_svec(int m, struct blasfeo_svec *sa); + +void test_blasfeo_free_smat(struct blasfeo_smat *sA); +void test_blasfeo_free_svec(struct blasfeo_svec *sa); + +void test_blasfeo_print_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj); +void test_blasfeo_print_svec(int m, struct blasfeo_svec *sa, int ai); +void test_blasfeo_print_tran_svec(int m, struct blasfeo_svec *sa, int ai); + +void test_blasfeo_print_to_file_smat(FILE *file, int m, int n, struct blasfeo_smat *sA, int ai, int aj); +void test_blasfeo_print_to_file_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); +void test_blasfeo_print_to_file_tran_svec(FILE *file, int m, struct blasfeo_svec *sa, int ai); + +void test_blasfeo_print_exp_smat(int m, int n, struct blasfeo_smat *sA, int ai, int aj); +void test_blasfeo_print_exp_svec(int m, struct blasfeo_svec *sa, int ai); +void test_blasfeo_print_exp_tran_svec(int m, struct blasfeo_svec *sa, int ai); + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_AUX_TEST_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas.h new file mode 100644 index 0000000000..200f1f51b1 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas.h @@ -0,0 +1,46 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_BLAS_H_ +#define BLASFEO_S_BLAS_H_ + + + +#include "blasfeo_s_blasfeo_api.h" +#include "blasfeo_s_blas_api.h" + + + +#endif // BLASFEO_S_BLAS_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas_api.h new file mode 100644 index 0000000000..d0fa2245c5 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blas_api.h @@ -0,0 +1,117 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef BLASFEO_S_BLAS_API_H_ +#define BLASFEO_S_BLAS_API_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef BLAS_API + + + +#ifdef FORTRAN_BLAS_API + + + +// BLAS 1 +// +void saxpy_(int *n, float *alpha, float *x, int *incx, float *y, int *incy); +// +float sdot_(int *n, float *x, int *incx, float *y, int *incy); + +// BLAS 3 +// +void sgemm_(char *ta, char *tb, int *m, int *n, int *k, float *alpha, float *A, int *lda, float *B, int *ldb, float *beta, float *C, int *ldc); +// +void strsm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n, float *alpha, float *A, int *lda, float *B, int *ldb); + + + +// LAPACK +// +void spotrf_(char *uplo, int *m, float *A, int *lda, int *info); + + + +#else // BLASFEO_API + + + +// BLAS 1 +// +void blas_saxpy(int *n, float *alpha, float *x, int *incx, float *y, int *incy); +// +float blas_sdot(int *n, float *x, int *incx, float *y, int *incy); + +// BLAS 3 +// +void blas_sgemm(char *ta, char *tb, int *m, int *n, int *k, float *alpha, float *A, int *lda, float *B, int *ldb, float *beta, float *C, int *ldc); +// +void blas_strsm(char *side, char *uplo, char *transa, char *diag, int *m, int *n, float *alpha, float *A, int *lda, float *B, int *ldb); + + + +// LAPACK +// +void blas_spotrf(char *uplo, int *m, float *A, int *lda, int *info); + + + +#endif // BLASFEO_API + + + +#endif // BLAS_API + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_BLAS_API_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api.h new file mode 100644 index 0000000000..72c19dcdc0 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api.h @@ -0,0 +1,268 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_BLASFEO_API_H_ +#define BLASFEO_S_BLASFEO_API_H_ + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +// +// level 1 BLAS +// + +// z = y + alpha*x +void blasfeo_saxpy(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = beta*y + alpha*x +void blasfeo_saxpby(int kmax, float alpha, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = x .* y +void blasfeo_svecmul(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z += x .* y +void blasfeo_svecmulacc(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = x .* y, return sum(z) = x^T * y +float blasfeo_svecmuldot(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// return x^T * y +float blasfeo_sdot(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi); +// construct givens plane rotation +void blasfeo_srotg(float a, float b, float *c, float *s); +// apply plane rotation [a b] [c -s; s; c] to the aj0 and aj1 columns of A at row index ai +void blasfeo_scolrot(int m, struct blasfeo_smat *sA, int ai, int aj0, int aj1, float c, float s); +// apply plane rotation [c s; -s c] [a; b] to the ai0 and ai1 rows of A at column index aj +void blasfeo_srowrot(int m, struct blasfeo_smat *sA, int ai0, int ai1, int aj, float c, float s); + + + +// +// level 2 BLAS +// + +// dense + +// z <= beta * y + alpha * A * x +void blasfeo_sgemv_n(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z <= beta * y + alpha * A' * x +void blasfeo_sgemv_t(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(n) +void blasfeo_strsv_lnn_mn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(n) +void blasfeo_strsv_ltn_mn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, not_unit +void blasfeo_strsv_lnn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, unit +void blasfeo_strsv_lnu(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, not_unit +void blasfeo_strsv_ltn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, unit +void blasfeo_strsv_ltu(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, not_transposed, not_unit +void blasfeo_strsv_unn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, transposed, not_unit +void blasfeo_strsv_utn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= beta * y + alpha * A * x ; A upper triangular +void blasfeo_strmv_unn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A' * x ; A upper triangular +void blasfeo_strmv_utn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A * x ; A lower triangular +void blasfeo_strmv_lnn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A' * x ; A lower triangular +void blasfeo_strmv_ltn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z_n <= beta_n * y_n + alpha_n * A * x_n +// z_t <= beta_t * y_t + alpha_t * A' * x_t +void blasfeo_sgemv_nt(int m, int n, float alpha_n, float alpha_t, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx_n, int xi_n, struct blasfeo_svec *sx_t, int xi_t, float beta_n, float beta_t, struct blasfeo_svec *sy_n, int yi_n, struct blasfeo_svec *sy_t, int yi_t, struct blasfeo_svec *sz_n, int zi_n, struct blasfeo_svec *sz_t, int zi_t); +// z <= beta * y + alpha * A * x, where A is symmetric and only the lower triangular patr of A is accessed +void blasfeo_ssymv_l(int m, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +void blasfeo_ssymv_l_mn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); + +// diagonal + +// z <= beta * y + alpha * A * x, A diagonal +void blasfeo_sgemv_d(int m, float alpha, struct blasfeo_svec *sA, int ai, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); + + + +// +// level 3 BLAS +// + +// dense + +// D <= beta * C + alpha * A * B +void blasfeo_sgemm_nn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T +void blasfeo_sgemm_nt(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_sgemm_tn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_sgemm_tt(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D lower triangular +void blasfeo_ssyrk_ln(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ssyrk_ln_mn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D lower triangular +void blasfeo_ssyrk_lt(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D upper triangular +void blasfeo_ssyrk_un(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D upper triangular +void blasfeo_ssyrk_ut(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^T ; B upper triangular +void blasfeo_strmm_rutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_strmm_rlnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_strsm_llnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular with unit diagonal +void blasfeo_strsm_llnu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_strsm_lltn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular with unit diagonal +void blasfeo_strsm_lltu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_strsm_lunn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular withunit diagonal +void blasfeo_strsm_lunu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_strsm_lutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular withunit diagonal +void blasfeo_strsm_lutu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_strsm_rlnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular with unit diagonal +void blasfeo_strsm_rlnu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_strsm_rltn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular with unit diagonal +void blasfeo_strsm_rltu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_strsm_runn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular with unit diagonal +void blasfeo_strsm_runu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_strsm_rutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular with unit diagonal +void blasfeo_strsm_rutu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); + +// diagonal + +// D <= alpha * A * B + beta * C, with A diagonal (stored as strvec) +void sgemm_diag_left_ib(int m, int n, float alpha, float *dA, float *pB, int sdb, float beta, float *pC, int sdc, float *pD, int sdd); +void blasfeo_sgemm_dn(int m, int n, float alpha, struct blasfeo_svec *sA, int ai, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A * B + beta * C, with B diagonal (stored as strvec) +void blasfeo_sgemm_nd(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sB, int bi, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); + + + +// +// LAPACK +// + +// D <= chol( C ) ; C, D lower triangular +void blasfeo_spotrf_l(int m, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_spotrf_l_mn(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= chol( C ) ; C, D upper triangular +void blasfeo_spotrf_u(int m, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= chol( C + A * B' ) ; C, D lower triangular +void blasfeo_ssyrk_spotrf_ln(int m, int k, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ssyrk_spotrf_ln_mn(int m, int n, int k, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= lu( C ) ; no pivoting +void blasfeo_sgetrf_np(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= lu( C ) ; row pivoting +void blasfeo_sgetrf_rp(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, int *ipiv); +// D <= qr( C ) +void blasfeo_sgeqrf(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +int blasfeo_sgeqrf_worksize(int m, int n); // in bytes +// D <= Q factor, where C is the output of the LQ factorization +int blasfeo_sorglq_worksize(int m, int n, int k); // in bytes +void blasfeo_sorglq(int m, int n, int k, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +// D <= lq( C ) +void blasfeo_sgelqf(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +int blasfeo_sgelqf_worksize(int m, int n); // in bytes +// D <= lq( C ), positive diagonal elements +void blasfeo_sgelqf_pd(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +// [L, A] <= lq( [L, A] ), positive diagonal elements, array of matrices, with +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_sgelqf_pd_la(int m, int n1, struct blasfeo_smat *sL, int li, int lj, struct blasfeo_smat *sA, int ai, int aj, void *work); +// [L, L, A] <= lq( [L, L, A] ), positive diagonal elements, array of matrices, with: +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_sgelqf_pd_lla(int m, int n1, struct blasfeo_smat *sL0, int l0i, int l0j, struct blasfeo_smat *sL1, int l1i, int l1j, struct blasfeo_smat *sA, int ai, int aj, void *work); + + + + +// +// BLAS API helper functions +// + +#if ( defined(BLAS_API) & defined(MF_PANELMAJ) ) +// BLAS 3 +void blasfeo_cm_sgemm_nn(int m, int n, int k, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, float beta, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_sgemm_nt(int m, int n, int k, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, float beta, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_sgemm_tn(int m, int n, int k, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, float beta, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_sgemm_tt(int m, int n, int k, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, float beta, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_llnn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_llnu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lltn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lltu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lunn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lunu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lutn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_lutu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rlnn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rlnu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rltn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rltu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_runn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_runu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rutn(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_strsm_rutu(int m, int n, float alpha, struct blasfeo_cm_smat *sA, int ai, int aj, struct blasfeo_cm_smat *sB, int bi, int bj, struct blasfeo_cm_smat *sD, int di, int dj); +// LAPACK +void blasfeo_cm_spotrf_l(int m, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +void blasfeo_cm_spotrf_u(int m, struct blasfeo_cm_smat *sC, int ci, int cj, struct blasfeo_cm_smat *sD, int di, int dj); +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_BLASFEO_API_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api_ref.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api_ref.h new file mode 100644 index 0000000000..f429a79dc3 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_api_ref.h @@ -0,0 +1,135 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_BLASFEO_API_REF_H_ +#define BLASFEO_S_BLASFEO_API_REF_H_ + +#include "blasfeo_common.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +// expose reference BLASFEO for testing + +// --- level 1 + +void blasfeo_saxpy_ref(int kmax, float alpha, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_saxpby_ref(int kmax, float alpha, struct blasfeo_svec_ref *sx, int xi, float beta, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_svecmul_ref(int m, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_svecmulacc_ref(int m, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +float blasfeo_svecmuldot_ref(int m, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +float blasfeo_sdot_ref(int m, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sy, int yi); +void blasfeo_srotg_ref(float a, float b, float *c, float *s); +void blasfeo_scolrot_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj0, int aj1, float c, float s); +void blasfeo_srowrot_ref(int m, struct blasfeo_smat_ref *sA, int ai0, int ai1, int aj, float c, float s); + + +// --- level 2 + +// dense +void blasfeo_sgemv_n_ref(int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, float beta, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_sgemv_t_ref(int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, float beta, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_lnn_mn_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_ltn_mn_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_lnn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_lnu_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_ltn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_ltu_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_unn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strsv_utn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_unn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_utn_ref(int m, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_lnn_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_ltn_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_lnu_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_strmv_ltu_ref(int m, int n, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, struct blasfeo_svec_ref *sz, int zi); +void blasfeo_sgemv_nt_ref(int m, int n, float alpha_n, float alpha_t, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx_n, int xi_n, struct blasfeo_svec_ref *sx_t, int xi_t, float beta_n, float beta_t, struct blasfeo_svec_ref *sy_n, int yi_n, struct blasfeo_svec_ref *sy_t, int yi_t, struct blasfeo_svec_ref *sz_n, int zi_n, struct blasfeo_svec_ref *sz_t, int zi_t); +void blasfeo_ssymv_l_ref(int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sx, int xi, float beta, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); + +// diagonal +void blasfeo_sgemv_d_ref(int m, float alpha, struct blasfeo_svec_ref *sA, int ai, struct blasfeo_svec_ref *sx, int xi, float beta, struct blasfeo_svec_ref *sy, int yi, struct blasfeo_svec_ref *sz, int zi); + + +// --- level 3 + +// dense +void blasfeo_sgemm_nn_ref( int m, int n, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgemm_nt_ref( int m, int n, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgemm_tn_ref(int m, int n, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgemm_tt_ref(int m, int n, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); + +void blasfeo_ssyrk_ln_mn_ref( int m, int n, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_ln_ref( int m, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_lt_ref( int m, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_un_ref( int m, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_ut_ref( int m, int k, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); + +void blasfeo_strmm_rutn_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strmm_rlnn_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strsm_rltn_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strsm_rltu_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strsm_rutn_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strsm_llnu_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_strsm_lunn_ref( int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sD, int di, int dj); + +// diagonal +void dgemm_diag_left_lib_ref(int m, int n, float alpha, float *dA, float *pB, int sdb, float beta, float *pC, int sdc, float *pD, int sdd); +void blasfeo_sgemm_dn_ref(int m, int n, float alpha, struct blasfeo_svec_ref *sA, int ai, struct blasfeo_smat_ref *sB, int bi, int bj, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgemm_nd_ref(int m, int n, float alpha, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_svec_ref *sB, int bi, float beta, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); + +// --- lapack + +void blasfeo_sgetrf_nopivot_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgetrf_rowpivot_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj, int *ipiv); +void blasfeo_spotrf_l_ref(int m, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_spotrf_l_mn_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_dpotrf_ln_ref(int m, int k, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_ssyrk_dpotrf_ln_mn_ref(int m, int n, int k, struct blasfeo_smat_ref *sA, int ai, int aj, struct blasfeo_smat_ref *sB, int bi, int bj, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgetrf_nopivot_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj); +void blasfeo_sgetrf_rowpivot_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj, int *ipiv); +void blasfeo_sgeqrf_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj, void *work); +void blasfeo_sgelqf_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj, void *work); +void blasfeo_sgelqf_pd_ref(int m, int n, struct blasfeo_smat_ref *sC, int ci, int cj, struct blasfeo_smat_ref *sD, int di, int dj, void *work); +void blasfeo_sgelqf_pd_la_ref(int m, int n1, struct blasfeo_smat_ref *sL, int li, int lj, struct blasfeo_smat_ref *sA, int ai, int aj, void *work); +void blasfeo_sgelqf_pd_lla_ref(int m, int n1, struct blasfeo_smat_ref *sL0, int l0i, int l0j, struct blasfeo_smat_ref *sL1, int l1i, int l1j, struct blasfeo_smat_ref *sA, int ai, int aj, void *work); + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_BLASFEO_API_REF_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_ref_api.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_ref_api.h new file mode 100644 index 0000000000..db8b28d3b7 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_blasfeo_ref_api.h @@ -0,0 +1,235 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_BLASFEO_REF_API_H_ +#define BLASFEO_S_BLASFEO_REF_API_H_ + + + +#include "blasfeo_common.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +// +// level 1 BLAS +// + +// z = y + alpha*x +void blasfeo_ref_saxpy(int kmax, float alpha, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = beta*y + alpha*x +void blasfeo_ref_saxpby(int kmax, float alpha, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = x .* y +void blasfeo_ref_svecmul(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z += x .* y +void blasfeo_ref_svecmulacc(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z = x .* y, return sum(z) = x^T * y +float blasfeo_ref_svecmuldot(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// return x^T * y +float blasfeo_ref_sdot(int m, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sy, int yi); +// construct givens plane rotation +void blasfeo_ref_srotg(float a, float b, float *c, float *s); +// apply plane rotation [a b] [c -s; s; c] to the aj0 and aj1 columns of A at row index ai +void blasfeo_ref_scolrot(int m, struct blasfeo_smat *sA, int ai, int aj0, int aj1, float c, float s); +// apply plane rotation [c s; -s c] [a; b] to the ai0 and ai1 rows of A at column index aj +void blasfeo_ref_srowrot(int m, struct blasfeo_smat *sA, int ai0, int ai1, int aj, float c, float s); + + + +// +// level 2 BLAS +// + +// dense + +// z <= beta * y + alpha * A * x +void blasfeo_ref_sgemv_n(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z <= beta * y + alpha * A' * x +void blasfeo_ref_sgemv_t(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(n) +void blasfeo_ref_strsv_lnn_mn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(n) +void blasfeo_ref_strsv_ltn_mn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, not_unit +void blasfeo_ref_strsv_lnn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A ) * x, A (m)x(m) lower, not_transposed, unit +void blasfeo_ref_strsv_lnu(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, not_unit +void blasfeo_ref_strsv_ltn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) lower, transposed, unit +void blasfeo_ref_strsv_ltu(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, not_transposed, not_unit +void blasfeo_ref_strsv_unn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= inv( A' ) * x, A (m)x(m) upper, transposed, not_unit +void blasfeo_ref_strsv_utn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= beta * y + alpha * A * x ; A upper triangular +void blasfeo_ref_strmv_unn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A' * x ; A upper triangular +void blasfeo_ref_strmv_utn(int m, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A * x ; A lower triangular +void blasfeo_ref_strmv_lnn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z <= A' * x ; A lower triangular +void blasfeo_ref_strmv_ltn(int m, int n, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, struct blasfeo_svec *sz, int zi); +// z_n <= beta_n * y_n + alpha_n * A * x_n +// z_t <= beta_t * y_t + alpha_t * A' * x_t +void blasfeo_ref_sgemv_nt(int m, int n, float alpha_n, float alpha_t, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx_n, int xi_n, struct blasfeo_svec *sx_t, int xi_t, float beta_n, float beta_t, struct blasfeo_svec *sy_n, int yi_n, struct blasfeo_svec *sy_t, int yi_t, struct blasfeo_svec *sz_n, int zi_n, struct blasfeo_svec *sz_t, int zi_t); +// z <= beta * y + alpha * A * x, where A is symmetric and only the lower triangular patr of A is accessed +void blasfeo_ref_ssymv_l(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); + +// diagonal + +// z <= beta * y + alpha * A * x, A diagonal +void blasfeo_ref_sgemv_d(int m, float alpha, struct blasfeo_svec *sA, int ai, struct blasfeo_svec *sx, int xi, float beta, struct blasfeo_svec *sy, int yi, struct blasfeo_svec *sz, int zi); + + + +// +// level 3 BLAS +// + +// dense + +// D <= beta * C + alpha * A * B +void blasfeo_ref_sgemm_nn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T +void blasfeo_ref_sgemm_nt(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_ref_sgemm_tn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B +void blasfeo_ref_sgemm_tt(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D lower triangular +void blasfeo_ref_ssyrk_ln(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_ssyrk_ln_mn(int m, int n, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D lower triangular +void blasfeo_ref_ssyrk_lt(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A * B^T ; C, D upper triangular +void blasfeo_ref_ssyrk_un(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= beta * C + alpha * A^T * B ; C, D upper triangular +void blasfeo_ref_ssyrk_ut(int m, int k, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^T ; B upper triangular +void blasfeo_ref_strmm_rutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A ; A lower triangular +void blasfeo_ref_strmm_rlnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_ref_strsm_llnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A lower triangular with unit diagonal +void blasfeo_ref_strsm_llnu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular employint explicit inverse of diagonal +void blasfeo_ref_strsm_lltn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A lower triangular with unit diagonal +void blasfeo_ref_strsm_lltu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_lunn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-1} * B , with A upper triangular withunit diagonal +void blasfeo_ref_strsm_lunu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_lutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A^{-T} * B , with A upper triangular withunit diagonal +void blasfeo_ref_strsm_lutu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_rlnn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A lower triangular with unit diagonal +void blasfeo_ref_strsm_rlnu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_rltn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A lower triangular with unit diagonal +void blasfeo_ref_strsm_rltu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_runn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-1} , with A upper triangular with unit diagonal +void blasfeo_ref_strsm_runu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular employing explicit inverse of diagonal +void blasfeo_ref_strsm_rutn(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * B * A^{-T} , with A upper triangular with unit diagonal +void blasfeo_ref_strsm_rutu(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sD, int di, int dj); + +// diagonal + +// D <= alpha * A * B + beta * C, with A diagonal (stored as strvec) +void sgemm_diag_left_ib(int m, int n, float alpha, float *dA, float *pB, int sdb, float beta, float *pC, int sdc, float *pD, int sdd); +void blasfeo_ref_sgemm_dn(int m, int n, float alpha, struct blasfeo_svec *sA, int ai, struct blasfeo_smat *sB, int bi, int bj, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= alpha * A * B + beta * C, with B diagonal (stored as strvec) +void blasfeo_ref_sgemm_nd(int m, int n, float alpha, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_svec *sB, int bi, float beta, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); + + + +// +// LAPACK +// + +// D <= chol( C ) ; C, D lower triangular +void blasfeo_ref_spotrf_l(int m, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_spotrf_l_mn(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= chol( C ) ; C, D upper triangular +void blasfeo_ref_spotrf_u(int m, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= chol( C + A * B' ) ; C, D lower triangular +void blasfeo_ref_ssyrk_spotrf_ln(int m, int k, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +void blasfeo_ref_ssyrk_spotrf_ln_mn(int m, int n, int k, struct blasfeo_smat *sA, int ai, int aj, struct blasfeo_smat *sB, int bi, int bj, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= lu( C ) ; no pivoting +void blasfeo_ref_sgetrf_np(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj); +// D <= lu( C ) ; row pivoting +void blasfeo_ref_sgetrf_rp(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, int *ipiv); +// D <= qr( C ) +void blasfeo_ref_sgeqrf(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +int blasfeo_ref_sgeqrf_worksize(int m, int n); // in bytes +// D <= Q factor, where C is the output of the LQ factorization +int blasfeo_ref_sorglq_worksize(int m, int n, int k); // in bytes +void blasfeo_ref_sorglq(int m, int n, int k, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +// D <= lq( C ) +void blasfeo_ref_sgelqf(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +int blasfeo_ref_sgelqf_worksize(int m, int n); // in bytes +// D <= lq( C ), positive diagonal elements +void blasfeo_ref_sgelqf_pd(int m, int n, struct blasfeo_smat *sC, int ci, int cj, struct blasfeo_smat *sD, int di, int dj, void *work); +// [L, A] <= lq( [L, A] ), positive diagonal elements, array of matrices, with +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_ref_sgelqf_pd_la(int m, int n1, struct blasfeo_smat *sL, int li, int lj, struct blasfeo_smat *sA, int ai, int aj, void *work); +// [L, L, A] <= lq( [L, L, A] ), positive diagonal elements, array of matrices, with: +// L lower triangular, of size (m)x(m) +// A full, of size (m)x(n1) +void blasfeo_ref_sgelqf_pd_lla(int m, int n1, struct blasfeo_smat *sL0, int l0i, int l0j, struct blasfeo_smat *sL1, int l1i, int l1j, struct blasfeo_smat *sA, int ai, int aj, void *work); + + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_BLASFEO_REF_API_H_ + diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_s_kernel.h b/phonelibs/acados/include/blasfeo/include/blasfeo_s_kernel.h new file mode 100644 index 0000000000..dcbde989cc --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_s_kernel.h @@ -0,0 +1,682 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_S_KERNEL_H_ +#define BLASFEO_S_KERNEL_H_ + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// utils +void blasfeo_align_2MB(void *ptr, void **ptr_align); +void blasfeo_align_4096_byte(void *ptr, void **ptr_align); +void blasfeo_align_64_byte(void *ptr, void **ptr_align); + + + +// +// lib8 +// + +// 24x4 +void kernel_sgemm_nt_24x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_nt_24x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_sgemm_nt_24x4_gen_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_sgemm_nn_24x4_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_nn_24x4_vs_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_sgemm_nn_24x4_gen_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_ssyrk_nt_l_24x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_ssyrk_nt_l_24x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_ssyrk_nt_l_20x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_ssyrk_nt_l_20x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_spotrf_nt_l_24x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_24x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_spotrf_nt_l_20x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_20x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_strsm_nt_rl_inv_24x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_24x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int km, int kn); +void kernel_sgemm_strsm_nt_rl_inv_24x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_sgemm_strsm_nt_rl_inv_24x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_20x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_20x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_ssyrk_spotrf_nt_l_24x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_24x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_strmm_nn_rl_24x4_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *D, int sdd); +void kernel_strmm_nn_rl_24x4_vs_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *D, int sdd, int km, int kn); + +// 16x8 +void kernel_sgemm_nt_16x8_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *spil); + +// 16x4 +void kernel_sgemm_nt_16x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_nt_16x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_sgemm_nt_16x4_gen_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_sgemm_nn_16x4_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_nn_16x4_vs_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_sgemm_nn_16x4_gen_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_ssyrk_nt_l_16x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_ssyrk_nt_l_16x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_ssyrk_nt_l_12x4_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_ssyrk_nt_l_12x4_vs_lib8(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); +void kernel_spotrf_nt_l_16x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_16x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_spotrf_nt_l_12x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_12x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_strsm_nt_rl_inv_16x4_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_16x4_vs_lib8(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int km, int kn); +void kernel_sgemm_strsm_nt_rl_inv_16x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_sgemm_strsm_nt_rl_inv_16x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_12x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_12x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_ssyrk_spotrf_nt_l_16x4_vs_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_16x4_lib8(int kp, float *Ap, int sdap, float *Bp, int km_, float *Am, int sdam, float *Bm, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_strmm_nn_rl_16x4_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *D, int sdd); +void kernel_strmm_nn_rl_16x4_vs_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *D, int sdd, int km, int kn); +void kernel_strmm_nn_rl_16x4_gen_lib8(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); + +// 8x8 +void kernel_sgemm_nt_8x8_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_nt_8x8_vs_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); +void kernel_sgemm_nt_8x8_gen_lib8(int k, float *alpha, float *A, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_sgemm_nn_8x8_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D); +void kernel_sgemm_nn_8x8_vs_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D, int km, int kn); +void kernel_sgemm_nn_8x8_gen_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_ssyrk_nt_l_8x8_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_ssyrk_nt_l_8x8_vs_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); +void kernel_spotrf_nt_l_8x8_lib8(int k, float *A, float *B, float *C, float *D, float *inv_diag_D); +void kernel_spotrf_nt_l_8x8_vs_lib8(int k, float *A, float *B, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_strsm_nt_rl_inv_8x8_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_8x8_vs_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_sgemm_strsm_nt_rl_inv_8x8_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E); +void kernel_sgemm_strsm_nt_rl_inv_8x8_vs_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_8x8_vs_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_8x8_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D); + +// 8x4 +void kernel_sgemm_nt_8x4_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_nt_8x4_vs_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); +void kernel_sgemm_nt_8x4_gen_lib8(int k, float *alpha, float *A, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_sgemm_nn_8x4_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D); +void kernel_sgemm_nn_8x4_vs_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D, int km, int kn); +void kernel_sgemm_nn_8x4_gen_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +//void kernel_ssyrk_nt_l_8x4_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_ssyrk_nt_l_8x4_vs_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); +void kernel_spotrf_nt_l_8x4_lib8(int k, float *A, float *B, float *C, float *D, float *inv_diag_D); +void kernel_spotrf_nt_l_8x4_vs_lib8(int k, float *A, float *B, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_strsm_nt_rl_inv_8x4_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_8x4_vs_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_sgemm_strsm_nt_rl_inv_8x4_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E); +void kernel_sgemm_strsm_nt_rl_inv_8x4_vs_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_8x4_vs_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_8x4_lib8(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D); +void kernel_strmm_nn_rl_8x4_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *D); +void kernel_strmm_nn_rl_8x4_vs_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *D, int km, int kn); +void kernel_strmm_nn_rl_8x4_gen_lib8(int k, float *alpha, float *A, int offsetB, float *B, int sdb, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_strmm_nt_ru_8x4_lib8(int k, float *alpha, float *A, float *B, float *D); +void kernel_strmm_nt_ru_8x4_vs_lib8(int k, float *alpha, float *A, float *B, float *D, int km, int kn); + +// 4x8 +void kernel_sgemm_nt_4x8_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_nt_4x8_vs_lib8(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); +void kernel_sgemm_nt_4x8_gen_lib8(int k, float *alpha, float *A, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_strsm_nt_rl_inv_4x8_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_4x8_vs_lib8(int k, float *A, float *B, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); + +// 8 +void kernel_sgemv_n_8_lib8(int k, float *alpha, float *A, float *x, float *beta, float *y, float *z); +void kernel_sgemv_n_8_vs_lib8(int k, float *alpha, float *A, float *x, float *beta, float *y, float *z, int k1); +void kernel_sgemv_n_8_gen_lib8(int kmax, float *alpha, float *A, float *x, float *beta, float *y, float *z, int k0, int k1); +void kernel_sgemv_t_8_lib8(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z); +void kernel_sgemv_t_8_vs_lib8(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z, int k1); +void kernel_sgemv_t_4_lib8(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z); +void kernel_sgemv_t_4_vs_lib8(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z, int k1); +void kernel_strsv_ln_inv_8_lib8(int k, float *A, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_ln_inv_8_vs_lib8(int k, float *A, float *inv_diag_A, float *x, float *y, float *z, int km, int kn); +void kernel_strsv_lt_inv_8_lib8(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_lt_inv_8_vs_lib8(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z, int km, int kn); +void kernel_sgemv_nt_4_lib8(int kmax, float *alpha_n, float *alpha_t, float *A, int sda, float *x_n, float *x_t, float *beta_t, float *y_t, float *z_n, float *z_t); +void kernel_sgemv_nt_4_vs_lib8(int kmax, float *alpha_n, float *alpha_t, float *A, int sda, float *x_n, float *x_t, float *beta_t, float *y_t, float *z_n, float *z_t, int km); +void kernel_ssymv_l_4l_lib8(int kmax, float *alpha, float *A, int sda, float *x, float *z); +void kernel_ssymv_l_4r_lib8(int kmax, float *alpha, float *A, int sda, float *x, float *z); +void kernel_ssymv_l_4l_gen_lib8(int kmax, float *alpha, int offA, float *A, int sda, float *x, float *z, int km); +void kernel_ssymv_l_4r_gen_lib8(int kmax, float *alpha, int offA, float *A, int sda, float *x, float *z, int km); + +// -------- aux + +// ---- copy + +// lib4 +// +void kernel_sgecpsc_4_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgecp_4_0_lib4(int kmax, float *A, float *B); + +void kernel_sgecpsc_4_1_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_4_1_lib4(int kmax, float *A0, int sda, float *B); +void kernel_sgecpsc_4_2_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_4_2_lib4(int kmax, float *A0, int sda, float *B); +void kernel_sgecpsc_4_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_4_3_lib4(int kmax, float *A0, int sda, float *B); + +void kernel_sgecpsc_3_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgecp_3_0_lib4(int kmax, float *A, float *B); +void kernel_sgecpsc_3_2_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_3_2_lib4(int kmax, float *A0, int sda, float *B); +void kernel_sgecpsc_3_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_3_3_lib4(int kmax, float *A0, int sda, float *B); + +void kernel_sgecpsc_2_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgecp_2_0_lib4(int kmax, float *A, float *B); +void kernel_sgecpsc_2_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgecp_2_3_lib4(int kmax, float *A0, int sda, float *B); + +void kernel_sgecpsc_1_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgecp_1_0_lib4(int kmax, float *A, float *B); + +// lib8 +// +void kernel_sgecp_8_0_lib8(int m, float *A, float *B); +void kernel_sgecp_8_0_gen_lib8(int m, float *A, float *B, int m1); +void kernel_sgecp_8_0_gen_u_lib8(int m, float *A, float *B, int m1); + +void kernel_sgesc_8_0_lib8(int m, float *alpha, float *A); +void kernel_sgesc_8_0_gen_lib8(int m, float *alpha, float *A, int m1); +void kernel_sgesc_8_0_gen_u_lib8(int m, float *alpha, float *A, int m1); + +void kernel_sgecpsc_8_0_lib8(int m, float *alpha, float *A, float *B); +void kernel_sgecpsc_8_0_gen_lib8(int m, float *alpha, float *A, float *B, int m1); +void kernel_sgecpsc_8_0_gen_u_lib8(int m, float *alpha, float *A, float *B, int m1); + +void kernel_sgecp_8_1_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_1_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_1_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_1_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_2_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_2_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_2_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_2_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_3_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_3_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_3_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_3_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_4_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_4_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_4_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_4_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_5_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_5_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_5_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_5_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_6_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_6_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_6_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_6_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +void kernel_sgecp_8_7_lib8(int m, float *A, int sda, float *B); +void kernel_sgecp_8_7_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgecpsc_8_7_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgecpsc_8_7_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + +// transpose +// +void kernel_sgetr_8_0_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_0_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_1_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_1_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_2_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_2_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_3_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_3_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_4_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_4_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_5_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_5_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_6_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_6_gen_lib8(int m, float *A, int sda, float *B, int m1); +void kernel_sgetr_8_7_lib8(int m, float *A, int sda, float *B); +void kernel_sgetr_8_7_gen_lib8(int m, float *A, int sda, float *B, int m1); + +// add +// +void kernel_sgead_8_0_lib8(int m, float *alpha, float *A, float *B); +void kernel_sgead_8_0_gen_lib8(int m, float *alpha, float *A, float *B, int m1); +void kernel_sgead_8_1_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_1_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_2_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_2_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_3_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_3_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_4_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_4_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_5_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_5_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_6_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_6_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); +void kernel_sgead_8_7_lib8(int m, float *alpha, float *A, int sda, float *B); +void kernel_sgead_8_7_gen_lib8(int m, float *alpha, float *A, int sda, float *B, int m1); + + +// +// lib4 +// + + + +// level 2 BLAS +// 4 +void kernel_sgemv_n_4_lib4(int k, float *alpha, float *A, float *x, float *beta, float *y, float *z); +void kernel_sgemv_n_4_vs_lib4(int k, float *alpha, float *A, float *x, float *beta, float *y, float *z, int k1); +void kernel_sgemv_n_4_gen_lib4(int kmax, float *alpha, float *A, float *x, float *beta, float *y, float *z, int k0, int k1); +void kernel_sgemv_t_4_lib4(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z); +void kernel_sgemv_t_4_vs_lib4(int k, float *alpha, int offsetA, float *A, int sda, float *x, float *beta, float *y, float *z, int k1); +void kernel_strsv_ln_inv_4_lib4(int k, float *A, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_ln_inv_4_vs_lib4(int k, float *A, float *inv_diag_A, float *x, float *y, float *z, int km, int kn); +void kernel_strsv_lt_inv_4_lib4(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_lt_inv_3_lib4(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_lt_inv_2_lib4(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strsv_lt_inv_1_lib4(int k, float *A, int sda, float *inv_diag_A, float *x, float *y, float *z); +void kernel_strmv_un_4_lib4(int k, float *A, float *x, float *z); +void kernel_strmv_ut_4_lib4(int k, float *A, int sda, float *x, float *z); +void kernel_strmv_ut_4_vs_lib4(int k, float *A, int sda, float *x, float *z, int km); +void kernel_sgemv_nt_6_lib4(int kmax, float *alpha_n, float *alpha_t, float *A, int sda, float *x_n, float *x_t, float *beta_t, float *y_t, float *z_n, float *z_t); +void kernel_sgemv_nt_4_lib4(int kmax, float *alpha_n, float *alpha_t, float *A, int sda, float *x_n, float *x_t, float *beta_t, float *y_t, float *z_n, float *z_t); +void kernel_sgemv_nt_4_vs_lib4(int kmax, float *alpha_n, float *alpha_t, float *A, int sda, float *x_n, float *x_t, float *beta_t, float *y_t, float *z_n, float *z_t, int km); +void kernel_ssymv_l_4_lib4(int kmax, float *alpha, float *A, int sda, float *x_n, float *z_n); +void kernel_ssymv_l_4_gen_lib4(int kmax, float *alpha, int offA, float *A, int sda, float *x_n, float *z_n, int km); + + + +// level 3 BLAS +// 12x4 +void kernel_sgemm_nt_16x4_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nt_16x4_vs_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +void kernel_strsm_nt_rl_inv_16x4_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_16x4_vs_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int m1, int n1); +// 12x4 +void kernel_sgemm_nt_12x4_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nt_12x4_vs_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +void kernel_strsm_nt_rl_inv_12x4_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_12x4_vs_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int m1, int n1); +// 8x8 +void kernel_sgemm_nt_8x8_lib4(int k, float *alpha, float *A, int sda, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nt_8x8_vs_lib4(int k, float *alpha, float *A, int sda, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +void kernel_sgemm_nn_8x8_lib4(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nn_8x8_vs_lib4(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +// 8x4 +void kernel_sgemm_nt_8x4_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nt_8x4_vs_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +void kernel_sgemm_nn_8x4_lib4(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_sgemm_nn_8x4_vs_lib4(int k, float *alpha, float *A, int sda, int offsetB, float *B, int sdb, float *beta, float *C, int sdc, float *D, int sdd, int m1, int n1); // +void kernel_ssyrk_nt_l_8x4_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); // +void kernel_ssyrk_nt_l_8x4_vs_lib4(int k, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, int km, int kn); // +void kernel_strsm_nt_rl_inv_8x4_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_8x4_vs_lib4(int k, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd, float *E, float *inv_diag_E, int m1, int n1); +// 4x4 +void kernel_sgemm_nt_4x4_lib4(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); // +void kernel_sgemm_nt_4x4_vs_lib4(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); // +void kernel_sgemm_nt_4x4_gen_lib4(int k, float *alpha, float *A, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int k0, int k1); +void kernel_sgemm_nn_4x4_lib4(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D); // +void kernel_sgemm_nn_4x4_vs_lib4(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, float *C, float *D, int km, int kn); // +void kernel_sgemm_nn_4x4_gen_lib4(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); // +void kernel_ssyrk_nt_l_4x4_lib4(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D); // +void kernel_ssyrk_nt_l_4x4_vs_lib4(int k, float *alpha, float *A, float *B, float *beta, float *C, float *D, int km, int kn); // +void kernel_ssyrk_nt_l_4x4_gen_lib4(int k, float *alpha, float *A, float *B, float *beta, int offsetC, float *C, int sdc, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_strmm_nt_ru_4x4_lib4(int k, float *alpha, float *A, float *B, float *D); // +void kernel_strmm_nt_ru_4x4_vs_lib4(int k, float *alpha, float *A, float *B, float *D, int km, int kn); // +void kernel_strmm_nn_rl_4x4_lib4(int k, float *alpha, float *A, int offsetB, float *B, int sdb, float *D); +void kernel_strmm_nn_rl_4x4_gen_lib4(int k, float *alpha, float *A, int offsetB, float *B, int sdb, int offsetD, float *D, int sdd, int m0, int m1, int n0, int n1); +void kernel_strsm_nt_rl_inv_4x4_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nt_rl_inv_4x4_vs_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_strsm_nt_rl_one_4x4_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E); +void kernel_strsm_nt_rl_one_4x4_vs_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, int km, int kn); +void kernel_strsm_nt_ru_inv_4x4_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nt_ru_inv_4x4_vs_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_strsm_nt_ru_one_4x4_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E); +void kernel_strsm_nt_ru_one_4x4_vs_lib4(int k, float *A, float *B, float *beta, float *C, float *D, float *E, int km, int kn); +void kernel_strsm_nn_ru_inv_4x4_lib4(int k, float *A, float *B, int sdb, float *beta, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nn_ru_inv_4x4_vs_lib4(int k, float *A, float *B, int sdb, float *beta, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_strsm_nn_ll_one_4x4_lib4(int k, float *A, float *B, int sdb, float *C, float *D, float *E); +void kernel_strsm_nn_ll_one_4x4_vs_lib4(int k, float *A, float *B, int sdb, float *C, float *D, float *E, int km, int kn); +void kernel_strsm_nn_lu_inv_4x4_lib4(int kmax, float *A, float *B, int sdb, float *C, float *D, float *E, float *inv_diag_E); +void kernel_strsm_nn_lu_inv_4x4_vs_lib4(int kmax, float *A, float *B, int sdb, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +// diag +void kernel_sgemm_diag_right_4_a0_lib4(int kmax, float *alpha, float *A, int sda, float *B, float *D, int sdd); +void kernel_sgemm_diag_right_4_lib4(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_diag_right_3_lib4(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_diag_right_2_lib4(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_diag_right_1_lib4(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int sdc, float *D, int sdd); +void kernel_sgemm_diag_left_4_a0_lib4(int kmax, float *alpha, float *A, float *B, float *D); +void kernel_sgemm_diag_left_4_lib4(int kmax, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_diag_left_3_lib4(int kmax, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_diag_left_2_lib4(int kmax, float *alpha, float *A, float *B, float *beta, float *C, float *D); +void kernel_sgemm_diag_left_1_lib4(int kmax, float *alpha, float *A, float *B, float *beta, float *C, float *D); + + + +// LAPACK +// 16x4 +void kernel_spotrf_nt_l_16x4_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_16x4_vs_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int m1, int n1); +// 12x4 +void kernel_spotrf_nt_l_12x4_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_12x4_vs_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int m1, int n1); +// 8x4 +void kernel_spotrf_nt_l_8x4_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D); +void kernel_spotrf_nt_l_8x4_vs_lib4(int k, float *A, int sda, float *B, float *C, int sdc, float *D, int sdd, float *inv_diag_D, int m1, int n1); +// 4x4 +void kernel_spotrf_nt_l_4x4_lib4(int k, float *A, float *B, float *C, float *D, float *inv_diag_D); +void kernel_spotrf_nt_l_4x4_vs_lib4(int k, float *A, float *B, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_sgetrf_nn_4x4_lib4(int k, float *A, float *B, int sdb, float *C, float *D, float *inv_diag_D); +void kernel_sgetrf_nn_4x4_vs_lib4(int k, float *A, float *B, int sdb, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_sgetrf_pivot_4_lib4(int m, float *pA, int sda, float *inv_diag_A, int* ipiv); +void kernel_sgetrf_pivot_4_vs_lib4(int m, int n, float *pA, int sda, float *inv_diag_A, int* ipiv); + + + +// merged routines +// 4x4 +void kernel_sgemm_strsm_nt_rl_inv_4x4_lib4(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E); +void kernel_sgemm_strsm_nt_rl_inv_4x4_vs_lib4(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *E, float *inv_diag_E, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_4x4_vs_lib4(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D, int km, int kn); +void kernel_ssyrk_spotrf_nt_l_4x4_lib4(int kp, float *Ap, float *Bp, int km_, float *Am, float *Bm, float *C, float *D, float *inv_diag_D); + + + +// auxiliary routines +void kernel_strcp_l_4_0_lib4(int kmax, float *A, float *B); +void kernel_strcp_l_4_1_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_4_2_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_4_3_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_3_0_lib4(int kmax, float *A, float *B); +void kernel_strcp_l_3_2_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_3_3_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_2_0_lib4(int kmax, float *A, float *B); +void kernel_strcp_l_2_3_lib4(int kmax, float *A0, int sda, float *B); +void kernel_strcp_l_1_0_lib4(int kmax, float *A, float *B); +void kernel_sgead_4_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgead_4_1_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_4_2_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_4_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_3_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgead_3_2_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_3_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_2_0_lib4(int kmax, float *alpha, float *A, float *B); +void kernel_sgead_2_3_lib4(int kmax, float *alpha, float *A0, int sda, float *B); +void kernel_sgead_1_0_lib4(int kmax, float *alpha, float *A, float *B); +// TODO +void kernel_sgeset_4_lib4(int kmax, float alpha, float *A); +void kernel_strset_4_lib4(int kmax, float alpha, float *A); +void kernel_sgetr_4_lib4(int tri, int kmax, int kna, float alpha, float *A, float *C, int sdc); +void kernel_sgetr_3_lib4(int tri, int kmax, int kna, float alpha, float *A, float *C, int sdc); +void kernel_sgetr_2_lib4(int tri, int kmax, int kna, float alpha, float *A, float *C, int sdc); +void kernel_sgetr_1_lib4(int tri, int kmax, int kna, float alpha, float *A, float *C, int sdc); + + + +// pack +// 24 lib 8 +void kernel_spack_nn_24_lib8(int kmax, float *A, int lda, float *B, int sdb); +void kernel_spack_nn_24_vs_lib8(int kmax, float *A, int lda, float *B, int sdb, int m1); +// 16 lib 8 +void kernel_spack_nn_16_lib8(int kmax, float *A, int lda, float *B, int sdb); +void kernel_spack_nn_16_vs_lib8(int kmax, float *A, int lda, float *B, int sdb, int m1); +// 8 lib 8 +void kernel_spack_nn_8_lib8(int kmax, float *A, int lda, float *B); +void kernel_spack_nn_8_vs_lib8(int kmax, float *A, int lda, float *B, int m1); +void kernel_spack_tn_8_lib8(int kmax, float *A, int lda, float *B); +void kernel_spack_tn_8_vs_lib8(int kmax, float *A, int lda, float *B, int m1); +void kernel_spack_tt_8_lib8(int kmax, float *A, int lda, float *B, int sdb); +void kernel_spack_tt_8_vs_lib8(int kmax, float *A, int lda, float *B, int sdb, int m1); +// 8 lib 4 +void kernel_spack_nn_8_lib4(int kmax, float *A, int lda, float *B, int sdb); +void kernel_spack_nn_8_vs_lib4(int kmax, float *A, int lda, float *B, int sdb, int m1); +//void kernel_spack_tt_8_lib4(int kmax, float *A, int lda, float *B, int sdb); +// 4 +void kernel_spack_nn_4_lib4(int kmax, float *A, int lda, float *B); +void kernel_spack_nn_4_vs_lib4(int kmax, float *A, int lda, float *B, int m1); +void kernel_spack_tn_4_lib4(int kmax, float *A, int lda, float *B); +void kernel_spack_tn_4_vs_lib4(int kmax, float *A, int lda, float *B, int m1); +void kernel_spack_tt_4_lib4(int kmax, float *A, int lda, float *B, int sdb); +void kernel_spack_tt_4_vs_lib4(int kmax, float *A, int lda, float *B, int sdb, int m1); +// unpack +// 8 +void kernel_sunpack_nn_8_lib4(int kmax, float *A, int sda, float *B, int ldb); +void kernel_sunpack_nn_8_vs_lib4(int kmax, float *A, int sda, float *B, int ldb, int m1); +//void kernel_sunpack_tt_8_lib4(int kmax, float *A, int sda, float *B, int ldb); +// 4 +void kernel_sunpack_nn_4_lib4(int kmax, float *A, float *B, int ldb); +void kernel_sunpack_nn_4_vs_lib4(int kmax, float *A, float *B, int ldb, int m1); +void kernel_sunpack_nt_4_lib4(int kmax, float *A, float *B, int ldb); +void kernel_sunpack_nt_4_vs_lib4(int kmax, float *A, float *B, int ldb, int m1); +void kernel_sunpack_tt_4_lib4(int kmax, float *A, int sda, float *B, int ldb); + +// panel copy +// 4 +void kernel_spacp_nt_4_lib4(int kmax, float *A, int offsetB, float *B, int sdb); +void kernel_spacp_tn_4_lib4(int kmax, int offsetA, float *A, int sda, float *B); +void kernel_spacp_nn_4_lib4(int kmax, int offsetA, float *A, int sda, float *B); +void kernel_spacp_nn_4_vs_lib4(int kmax, int offsetA, float *A, int sda, float *B, int m1); + + + +/************************************************ +* BLAS API kernels +************************************************/ + +//#if defined(BLAS_API) + +// A, B panel-major bs=8; C, D column-major +// 24x4 +void kernel_sgemm_nt_24x4_lib88cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_24x4_vs_lib88cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 16x4 +void kernel_sgemm_nt_16x4_lib88cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_16x4_vs_lib88cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 8x8 +void kernel_sgemm_nt_8x8_lib88cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x8_vs_lib88cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_spotrf_nt_l_8x8_lib88cc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *dD); +void kernel_spotrf_nt_l_8x8_vs_lib88cc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *dD, int m1, int n1); +void kernel_strsm_nt_rl_inv_8x8_lib88ccc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nt_rl_inv_8x8_vs_lib88ccc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +// 8x4 +void kernel_sgemm_nt_8x4_lib88cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x4_vs_lib88cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); + +// A, B panel-major bs=4; C, D column-major +// 8x8 +void kernel_sgemm_nt_8x8_lib44cc(int kmax, float *alpha, float *A, int sda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd); +// 8x4 +void kernel_sgemm_nt_8x4_lib44cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x4_vs_lib44cc(int kmax, float *alpha, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_spotrf_nt_l_8x4_lib44cc(int kmax, float *A, int sda, float *B, float *C, int ldc, float *D, int ldd, float *dD); +void kernel_strsm_nt_rl_inv_8x4_lib44ccc(int kmax, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nt_rl_inv_8x4_vs_lib44ccc(int kmax, float *A, int sda, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +// 4x4 +void kernel_sgemm_nt_4x4_lib44cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x4_vs_lib44cc(int kmax, float *alpha, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_strsm_nt_rl_inv_4x4_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, float *dE); +void kernel_strsm_nt_rl_inv_4x4_vs_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, float *dE, int m1, int n1); +void kernel_strsm_nt_rl_inv_4x4_lib44ccc(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nt_rl_inv_4x4_vs_lib44ccc(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nt_rl_one_4x4_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E); +void kernel_strsm_nt_rl_one_4x4_vs_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int m1, int n1); +void kernel_strsm_nt_ru_inv_4x4_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, float *dE); +void kernel_strsm_nt_ru_inv_4x4_vs_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, float *dE, int m1, int n1); +void kernel_strsm_nt_ru_one_4x4_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E); +void kernel_strsm_nt_ru_one_4x4_vs_lib44cc4(int kmax, float *A, float *B, float *beta, float *C, int ldc, float *D, int ldd, float *E, int m1, int n1); +void kernel_spotrf_nt_l_4x4_lib44cc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *dD); +void kernel_spotrf_nt_l_4x4_vs_lib44cc(int kmax, float *A, float *B, float *C, int ldc, float *D, int ldd, float *dD, int m1, int n1); + +// B panel-major bs=8; A, C, D column-major +// 8x8 +void kernel_sgemm_nt_8x8_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x8_vs_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_8x8_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_8x8_vs_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 4x8 +void kernel_sgemm_nt_4x8_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x8_vs_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_4x8_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_4x8_vs_libc8cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); + +// B panel-major bs=4; A, C, D column-major +// 8x8 +void kernel_sgemm_nt_8x8_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x8_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_8x8_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_8x8_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 4x8 +void kernel_sgemm_nt_4x8_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x8_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_4x8_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_4x8_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 4x4 +void kernel_sgemm_nt_4x4_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x4_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_4x4_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_4x4_vs_libc4cc(int kmax, float *alpha, float *A, int lda, float *B, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); + +// A panel-major bs=8; B, C, D column-major +// 24x4 +void kernel_sgemm_nn_24x4_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_24x4_vs_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_24x4_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_24x4_vs_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 16x4 +void kernel_sgemm_nn_16x4_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_16x4_vs_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_16x4_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_16x4_vs_lib8ccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 8x8 +void kernel_sgemm_nn_8x8_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_8x8_vs_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_8x8_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x8_vs_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 8x4 +void kernel_sgemm_nn_8x4_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_8x4_vs_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_8x4_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x4_vs_lib8ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); + +// A panel-major bs=4; B, C, D column-major +// 8x8 +void kernel_sgemm_nn_8x8_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x8_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +// 8x4 +void kernel_sgemm_nn_8x4_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_8x4_vs_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_8x4_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_8x4_vs_lib4ccc(int kmax, float *alpha, float *A, int sda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +// 4x4 +void kernel_sgemm_nn_4x4_lib4ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_4x4_vs_lib4ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_4x4_lib4ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x4_vs_lib4ccc(int kmax, float *alpha, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_strsm_nn_rl_inv_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nn_rl_inv_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nn_rl_one_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde); +void kernel_strsm_nn_rl_one_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, int m1, int n1); +void kernel_strsm_nt_rl_inv_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nt_rl_inv_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nt_rl_one_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde); +void kernel_strsm_nt_rl_one_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, int m1, int n1); +void kernel_strsm_nn_ru_inv_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nn_ru_inv_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nn_ru_one_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde); +void kernel_strsm_nn_ru_one_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, int m1, int n1); +void kernel_strsm_nt_ru_inv_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE); +void kernel_strsm_nt_ru_inv_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nt_ru_one_4x4_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde); +void kernel_strsm_nt_ru_one_4x4_vs_lib4cccc(int kmax, float *A, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, float *E, int lde, int m1, int n1); + +// A, C, D panel-major; B, E column-major +// TODO merge with above +// 4x4 +void kernel_strsm_nn_rl_inv_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE); +void kernel_strsm_nn_rl_inv_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nn_rl_one_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde); +void kernel_strsm_nn_rl_one_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, int m1, int n1); +void kernel_strsm_nn_ru_inv_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE); +void kernel_strsm_nn_ru_inv_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nn_ru_one_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde); +void kernel_strsm_nn_ru_one_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, int m1, int n1); +void kernel_strsm_nt_rl_inv_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE); +void kernel_strsm_nt_rl_inv_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nt_rl_one_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde); +void kernel_strsm_nt_rl_one_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, int m1, int n1); +void kernel_strsm_nt_ru_inv_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE); +void kernel_strsm_nt_ru_inv_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, float *dE, int m1, int n1); +void kernel_strsm_nt_ru_one_4x4_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde); +void kernel_strsm_nt_ru_one_4x4_vs_lib4c44c(int kmax, float *A, float *B, int ldb, float *beta, float *C, float *D, float *E, int lde, int m1, int n1); + +// A, B, C, D column-major +void kernel_sgemm_nn_4x4_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nn_4x4_vs_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_nt_4x4_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_nt_4x4_vs_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); +void kernel_sgemm_tt_4x4_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd); +void kernel_sgemm_tt_4x4_vs_libcccc(int kmax, float *alpha, float *A, int lda, float *B, int ldb, float *beta, float *C, int ldc, float *D, int ldd, int m1, int n1); + +// vector +void kernel_sdot_11_lib(int n, float *x, float *y, float *res); +void kernel_saxpy_11_lib(int n, float *alpha, float *x, float *y); + + +//#endif // BLAS_API + + + +// larger kernels +// 24 +void kernel_sgemm_nt_24xn_p0_lib88cc(int n, int k, float *alpha, float *A, int sda, float *B, int sdb, float *beta, float *C, int ldc, float *D, int ldd, float *A_p, float *B_p); + + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_S_KERNEL_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_stdlib.h b/phonelibs/acados/include/blasfeo/include/blasfeo_stdlib.h new file mode 100644 index 0000000000..9bd248b1d4 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_stdlib.h @@ -0,0 +1,62 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_STDLIB_H_ +#define BLASFEO_STDLIB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + + +#include + +// +void blasfeo_malloc(void **ptr, size_t size); +// +void blasfeo_malloc_align(void **ptr, size_t size); +// +void blasfeo_free(void *ptr); +// +void blasfeo_free_align(void *ptr); + + + +#ifdef __cplusplus +} +#endif + +#endif // BLASFEO_STDLIB_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_target.h b/phonelibs/acados/include/blasfeo/include/blasfeo_target.h new file mode 100644 index 0000000000..51f617a649 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_target.h @@ -0,0 +1,73 @@ +#ifndef TARGET_X64_INTEL_HASWELL +#define TARGET_X64_INTEL_HASWELL +#endif + +#ifndef TARGET_NEED_FEATURE_AVX2 +#define TARGET_NEED_FEATURE_AVX2 1 +#endif + +#ifndef TARGET_NEED_FEATURE_FMA +#define TARGET_NEED_FEATURE_FMA 1 +#endif + +#ifndef TARGET_NEED_FEATURE_SSE3 +/* #undef TARGET_NEED_FEATURE_SSE3 */ +#endif + +#ifndef TARGET_NEED_FEATURE_AVX +/* #undef TARGET_NEED_FEATURE_AVX */ +#endif + +#ifndef TARGET_NEED_FEATURE_VFPv3 +/* #undef TARGET_NEED_FEATURE_VFPv3 */ +#endif + +#ifndef TARGET_NEED_FEATURE_NEON +/* #undef TARGET_NEED_FEATURE_NEON */ +#endif + +#ifndef TARGET_NEED_FEATURE_VFPv4 +/* #undef TARGET_NEED_FEATURE_VFPv4 */ +#endif + +#ifndef TARGET_NEED_FEATURE_NEONv2 +/* #undef TARGET_NEED_FEATURE_NEONv2 */ +#endif + +#ifndef LA_HIGH_PERFORMANCE +#define LA_HIGH_PERFORMANCE +#endif + +#ifndef MF_PANELMAJ +#define MF_PANELMAJ +#endif + +#ifndef EXT_DEP +#define ON 1 +#define OFF 0 +#if ON==ON +#define EXT_DEP +#endif +#undef ON +#undef OFF +#endif + +#ifndef BLAS_API +#define ON 1 +#define OFF 0 +#if OFF==ON +#define BLAS_API +#endif +#undef ON +#undef OFF +#endif + +#ifndef FORTRAN_BLAS_API +#define ON 1 +#define OFF 0 +#if OFF==ON +#define FORTRAN_BLAS_API +#endif +#undef ON +#undef OFF +#endif diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_timing.h b/phonelibs/acados/include/blasfeo/include/blasfeo_timing.h new file mode 100644 index 0000000000..5671b888fe --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_timing.h @@ -0,0 +1,114 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_TIMING_H_ +#define BLASFEO_TIMING_H_ + +//#include + +#if (defined _WIN32 || defined _WIN64) && !(defined __MINGW32__ || defined __MINGW64__) + + /* Use Windows QueryPerformanceCounter for timing. */ + #include + + /** A structure for keeping internal timer data. */ + typedef struct blasfeo_timer_ { + LARGE_INTEGER tic; + LARGE_INTEGER toc; + LARGE_INTEGER freq; + } blasfeo_timer; + +#elif(defined __APPLE__) + + #include + + /** A structure for keeping internal timer data. */ + typedef struct blasfeo_timer_ { + uint64_t tic; + uint64_t toc; + mach_timebase_info_data_t tinfo; + } blasfeo_timer; + +#elif(defined __DSPACE__) + + #include + + typedef struct blasfeo_timer_ { + double time; + } blasfeo_timer; + +#elif(defined __XILINX_NONE_ELF__ || defined __XILINX_ULTRASCALE_NONE_ELF_JAILHOUSE__) + + #include "xtime_l.h" + + typedef struct blasfeo_timer_ { + uint64_t tic; + uint64_t toc; + } blasfeo_timer; + +#else + + /* Use POSIX clock_gettime() for timing on non-Windows machines. */ + #include + + #if __STDC_VERSION__ >= 199901L // C99 Mode + + #include + #include + + typedef struct blasfeo_timer_ { + struct timeval tic; + struct timeval toc; + } blasfeo_timer; + + #else // ANSI C Mode + + /** A structure for keeping internal timer data. */ + typedef struct blasfeo_timer_ { + struct timespec tic; + struct timespec toc; + } blasfeo_timer; + + #endif // __STDC_VERSION__ >= 199901L + +#endif // (defined _WIN32 || defined _WIN64) + +/** A function for measurement of the current time. */ +void blasfeo_tic(blasfeo_timer* t); + +/** A function which returns the elapsed time. */ +double blasfeo_toc(blasfeo_timer* t); + +#endif // BLASFEO_TIMING_H_ diff --git a/phonelibs/acados/include/blasfeo/include/blasfeo_v_aux_ext_dep.h b/phonelibs/acados/include/blasfeo/include/blasfeo_v_aux_ext_dep.h new file mode 100644 index 0000000000..1598551185 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/blasfeo_v_aux_ext_dep.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef BLASFEO_V_AUX_EXT_DEP_H_ +#define BLASFEO_V_AUX_EXT_DEP_H_ + + + +#include "blasfeo_target.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/************************************************ +* d_aux_extern_depend_lib.c +************************************************/ + +#ifdef EXT_DEP + +void v_zeros(void **ptrA, int size); +// dynamically allocate size bytes of memory aligned to 64-byte boundaries and set accordingly a pointer to void; set allocated memory to zero +void v_zeros_align(void **ptrA, int size); +// free the memory allocated by v_zeros +void v_free(void *ptrA); +// free the memory allocated by v_zeros_aligned +void v_free_align(void *ptrA); +// dynamically allocate size bytes of memory and set accordingly a pointer to char; set allocated memory to zero +void c_zeros(char **ptrA, int size); +// dynamically allocate size bytes of memory aligned to 64-byte boundaries and set accordingly a pointer to char; set allocated memory to zero +void c_zeros_align(char **ptrA, int size); +// free the memory allocated by c_zeros +void c_free(char *ptrA); +// free the memory allocated by c_zeros_aligned +void c_free_align(char *ptrA); + +#endif // EXT_DEP + + + +#ifdef __cplusplus +} +#endif + + + +#endif // BLASFEO_V_AUX_EXT_DEP_H_ diff --git a/phonelibs/acados/include/blasfeo/include/d_blas.h b/phonelibs/acados/include/blasfeo/include/d_blas.h new file mode 100644 index 0000000000..d6f8786721 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/d_blas.h @@ -0,0 +1,77 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +// headers to reference BLAS and LAPACK routines employed in BLASFEO WR + +// level 1 +double ddot_(int *m, double *x, int *incx, double *y, int *incy); +void dcopy_(int *m, double *x, int *incx, double *y, int *incy); +void daxpy_(int *m, double *alpha, double *x, int *incx, double *y, int *incy); +void dscal_(int *m, double *alpha, double *x, int *incx); +void drot_(int *m, double *x, int *incx, double *y, int *incy, double *c, double *s); +void drotg_(double *a, double *b, double *c, double *s); + +// level 2 +void dgemv_(char *ta, int *m, int *n, double *alpha, double *A, int *lda, double *x, int *incx, double *beta, double *y, int *incy); +void dsymv_(char *uplo, int *m, double *alpha, double *A, int *lda, double *x, int *incx, double *beta, double *y, int *incy); +void dtrmv_(char *uplo, char *trans, char *diag, int *n, double *A, int *lda, double *x, int *incx); +void dtrsv_(char *uplo, char *trans, char *diag, int *n, double *A, int *lda, double *x, int *incx); +void dger_(int *m, int *n, double *alpha, double *x, int *incx, double *y, int *incy, double *A, int *lda); + +// level 3 +void dgemm_(char *ta, char *tb, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int *ldb, double *beta, double *C, int *ldc); +void dsyrk_(char *uplo, char *trans, int *n, int *k, double *alpha, double *A, int *lda, double *beta, double *C, int *ldc); +void dtrmm_(char *side, char *uplo, char *trans, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); +void dtrsm_(char *side, char *uplo, char *trans, char *diag, int *m, int *n, double *alpha, double *A, int *lda, double *B, int *ldb); + +// lapack +void dpotrf_(char *uplo, int *m, double *A, int *lda, int *info); +void dgetrf_(int *m, int *n, double *A, int *lda, int *ipiv, int *info); +void dgeqrf_(int *m, int *n, double *A, int *lda, double *tau, double *work, int *lwork, int *info); +void dgeqr2_(int *m, int *n, double *A, int *lda, double *tau, double *work, int *info); +void dgelqf_(int *m, int *n, double *A, int *lda, double *tau, double *work, int *lwork, int *info); +void dorglq_(int *m, int *n, int *k, double *A, int *lda, double *tau, double *work, int *lwork, int *info); + + + +#ifdef __cplusplus +} +#endif diff --git a/phonelibs/acados/include/blasfeo/include/d_blas_64.h b/phonelibs/acados/include/blasfeo/include/d_blas_64.h new file mode 100644 index 0000000000..4f40d00dfb --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/d_blas_64.h @@ -0,0 +1,73 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +// headers to reference BLAS and LAPACK routines employed in BLASFEO WR + +// level 1 +double ddot_(long long *m, double *x, long long *incx, double *y, long long *incy); +void dcopy_(long long *m, double *x, long long *incx, double *y, long long *incy); +void daxpy_(long long *m, double *alpha, double *x, long long *incx, double *y, long long *incy); +void dscal_(long long *m, double *alpha, double *x, long long *incx); + +// level 2 +void dgemv_(char *ta, long long *m, long long *n, double *alpha, double *A, long long *lda, double *x, long long *incx, double *beta, double *y, long long *incy); +void dsymv_(char *uplo, long long *m, double *alpha, double *A, long long *lda, double *x, long long *incx, double *beta, double *y, long long *incy); +void dtrmv_(char *uplo, char *trans, char *diag, long long *n, double *A, long long *lda, double *x, long long *incx); +void dtrsv_(char *uplo, char *trans, char *diag, long long *n, double *A, long long *lda, double *x, long long *incx); +void dger_(long long *m, long long *n, double *alpha, double *x, long long *incx, double *y, long long *incy, double *A, long long *lda); + +// level 3 +void dgemm_(char *ta, char *tb, long long *m, long long *n, long long *k, double *alpha, double *A, long long *lda, double *B, long long *ldb, double *beta, double *C, long long *ldc); +void dsyrk_(char *uplo, char *trans, long long *n, long long *k, double *alpha, double *A, long long *lda, double *beta, double *C, long long *ldc); +void dtrmm_(char *side, char *uplo, char *trans, char *diag, long long *m, long long *n, double *alpha, double *A, long long *lda, double *B, long long *ldb); +void dtrsm_(char *side, char *uplo, char *trans, char *diag, long long *m, long long *n, double *alpha, double *A, long long *lda, double *B, long long *ldb); + +// lapack +void dpotrf_(char *uplo, long long *m, double *A, long long *lda, long long *info); +void dgetrf_(long long *m, long long *n, double *A, long long *lda, long long *ipiv, long long *info); +void dgeqrf_(long long *m, long long *n, double *A, long long *lda, double *tau, double *work, long long *lwork, long long *info); +void dgeqr2_(long long *m, long long *n, double *A, long long *lda, double *tau, double *work, long long *info); + + + +#ifdef __cplusplus +} +#endif diff --git a/phonelibs/acados/include/blasfeo/include/s_blas.h b/phonelibs/acados/include/blasfeo/include/s_blas.h new file mode 100644 index 0000000000..58b2bb0a1a --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/s_blas.h @@ -0,0 +1,77 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +// headers to reference BLAS and LAPACK routines employed in BLASFEO WR + +// level 1 +float sdot_(int *m, float *x, int *incx, float *y, int *incy); +void scopy_(int *m, float *x, int *incx, float *y, int *incy); +void saxpy_(int *m, float *alpha, float *x, int *incx, float *y, int *incy); +void sscal_(int *m, float *alpha, float *x, int *incx); +void srot_(int *m, float *x, int *incx, float *y, int *incy, float *c, float *s); +void srotg_(float *a, float *b, float *c, float *s); + +// level 2 +void sgemv_(char *ta, int *m, int *n, float *alpha, float *A, int *lda, float *x, int *incx, float *beta, float *y, int *incy); +void ssymv_(char *uplo, int *m, float *alpha, float *A, int *lda, float *x, int *incx, float *beta, float *y, int *incy); +void strmv_(char *uplo, char *trans, char *diag, int *n, float *A, int *lda, float *x, int *incx); +void strsv_(char *uplo, char *trans, char *diag, int *n, float *A, int *lda, float *x, int *incx); +void sger_(int *m, int *n, float *alpha, float *x, int *incx, float *y, int *incy, float *A, int *lda); + +// level 3 +void sgemm_(char *ta, char *tb, int *m, int *n, int *k, float *alpha, float *A, int *lda, float *B, int *ldb, float *beta, float *C, int *ldc); +void ssyrk_(char *uplo, char *trans, int *n, int *k, float *alpha, float *A, int *lda, float *beta, float *C, int *ldc); +void strmm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n, float *alpha, float *A, int *lda, float *B, int *ldb); +void strsm_(char *side, char *uplo, char *transa, char *diag, int *m, int *n, float *alpha, float *A, int *lda, float *B, int *ldb); + +// lapack +void spotrf_(char *uplo, int *m, float *A, int *lda, int *info); +void sgetrf_(int *m, int *n, float *A, int *lda, int *ipiv, int *info); +void sgeqrf_(int *m, int *n, float *A, int *lda, float *tau, float *work, int *lwork, int *info); +void sgeqr2_(int *m, int *n, float *A, int *lda, float *tau, float *work, int *info); +void sgelqf_(int *m, int *n, float *A, int *lda, float *tau, float *work, int *lwork, int *info); +void sorglq_(int *m, int *n, int *k, float *A, int *lda, float *tau, float *work, int *lwork, int *info); + + + +#ifdef __cplusplus +} +#endif diff --git a/phonelibs/acados/include/blasfeo/include/s_blas_64.h b/phonelibs/acados/include/blasfeo/include/s_blas_64.h new file mode 100644 index 0000000000..b9efab6c23 --- /dev/null +++ b/phonelibs/acados/include/blasfeo/include/s_blas_64.h @@ -0,0 +1,73 @@ +/************************************************************************************************** +* * +* This file is part of BLASFEO. * +* * +* BLASFEO -- BLAS For Embedded Optimization. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + + +// headers to reference BLAS and LAPACK routines employed in BLASFEO WR + +// level 1 +float sdot_(long long *m, float *x, long long *incx, float *y, long long *incy); +void scopy_(long long *m, float *x, long long *incx, float *y, long long *incy); +void saxpy_(long long *m, float *alpha, float *x, long long *incx, float *y, long long *incy); +void sscal_(long long *m, float *alpha, float *x, long long *incx); + +// level 2 +void sgemv_(char *ta, long long *m, long long *n, float *alpha, float *A, long long *lda, float *x, long long *incx, float *beta, float *y, long long *incy); +void ssymv_(char *uplo, long long *m, float *alpha, float *A, long long *lda, float *x, long long *incx, float *beta, float *y, long long *incy); +void strmv_(char *uplo, char *trans, char *diag, long long *n, float *A, long long *lda, float *x, long long *incx); +void strsv_(char *uplo, char *trans, char *diag, long long *n, float *A, long long *lda, float *x, long long *incx); +void sger_(long long *m, long long *n, float *alpha, float *x, long long *incx, float *y, long long *incy, float *A, long long *lda); + +// level 3 +void sgemm_(char *ta, char *tb, long long *m, long long *n, long long *k, float *alpha, float *A, long long *lda, float *B, long long *ldb, float *beta, float *C, long long *ldc); +void ssyrk_(char *uplo, char *trans, long long *n, long long *k, float *alpha, float *A, long long *lda, float *beta, float *C, long long *ldc); +void strmm_(char *side, char *uplo, char *transa, char *diag, long long *m, long long *n, float *alpha, float *A, long long *lda, float *B, long long *ldb); +void strsm_(char *side, char *uplo, char *transa, char *diag, long long *m, long long *n, float *alpha, float *A, long long *lda, float *B, long long *ldb); + +// lapack +void spotrf_(char *uplo, long long *m, float *A, long long *lda, long long *info); +void sgetrf_(long long *m, long long *n, float *A, long long *lda, long long *ipiv, long long *info); +void sgeqrf_(long long *m, long long *n, float *A, long long *lda, float *tau, float *work, long long *lwork, long long *info); +void sgeqr2_(long long *m, long long *n, float *A, long long *lda, float *tau, float *work, long long *info); + + + +#ifdef __cplusplus +} +#endif diff --git a/phonelibs/acados/include/hpipm/include/hpipm_aux_mem.h b/phonelibs/acados/include/hpipm/include/hpipm_aux_mem.h new file mode 100644 index 0000000000..7bd3d7e8bc --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_aux_mem.h @@ -0,0 +1,52 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_AUX_MEM_H_ +#define HPIPM_AUX_MEM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void hpipm_zero_memset(hpipm_size_t memsize, void *mem); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_AUX_MEM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_aux_string.h b/phonelibs/acados/include/hpipm/include/hpipm_aux_string.h new file mode 100644 index 0000000000..804cba5dc1 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_aux_string.h @@ -0,0 +1,50 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_AUX_STRING_H_ +#define HPIPM_AUX_STRING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_STR_LEN 5 +int hpipm_strcmp(char *str1, char *str2); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_AUX_STRING_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_common.h b/phonelibs/acados/include/hpipm/include/hpipm_common.h new file mode 100644 index 0000000000..0cc96a7b50 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_common.h @@ -0,0 +1,76 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_COMMON_H_ +#define HPIPM_COMMON_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef size_t hpipm_size_t; + +enum hpipm_mode + { + SPEED_ABS, // focus on speed, absolute IPM formulation + SPEED, // focus on speed, relative IPM formulation + BALANCE, // balanced mode, relative IPM formulation + ROBUST, // focus on robustness, relative IPM formulation + }; + +enum hpipm_status + { + SUCCESS, // found solution satisfying accuracy tolerance + MAX_ITER, // maximum iteration number reached + MIN_STEP, // minimum step length reached + NAN_SOL, // NaN in solution detected + INCONS_EQ, // unconsistent equality constraints + }; + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_COMMON_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_cast_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_cast_qcqp.h new file mode 100644 index 0000000000..0e4c41f221 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_cast_qcqp.h @@ -0,0 +1,71 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_CAST_QCQP_H_ +#define HPIPM_D_CAST_QCQP_H_ + + + +#include +#include + +#include "hpipm_d_dense_qcqp.h" +#include "hpipm_d_dense_qcqp_sol.h" +#include "hpipm_d_ocp_qcqp.h" +#include "hpipm_d_ocp_qcqp_dim.h" +#include "hpipm_d_ocp_qcqp_sol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_cast_qcqp_compute_dim(struct d_ocp_qcqp_dim *ocp_dim, struct d_dense_qcqp_dim *dense_dim); +// +void d_cast_qcqp_cond(struct d_ocp_qcqp *ocp_qp, struct d_dense_qcqp *dense_qp); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_CAST_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_cond.h b/phonelibs/acados/include/hpipm/include/hpipm_d_cond.h new file mode 100644 index 0000000000..5900a2ab10 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_cond.h @@ -0,0 +1,135 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_COND_H_ +#define HPIPM_D_COND_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp.h" +#include "hpipm_d_dense_qp_sol.h" +#include "hpipm_d_ocp_qp.h" +#include "hpipm_d_ocp_qp_dim.h" +#include "hpipm_d_ocp_qp_sol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_cond_qp_arg + { + int cond_last_stage; // condense last stage + int cond_alg; // condensing algorithm: 0 N2-nx3, 1 N3-nx2 + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution inequality constr (lam t) + int square_root_alg; // square root algorithm (faster but requires RSQ>0) + hpipm_size_t memsize; + }; + + + +struct d_cond_qp_ws + { + struct blasfeo_dmat *Gamma; + struct blasfeo_dmat *GammaQ; + struct blasfeo_dmat *L; + struct blasfeo_dmat *Lx; + struct blasfeo_dmat *AL; + struct blasfeo_dvec *Gammab; + struct blasfeo_dvec *l; + struct blasfeo_dvec *tmp_nbgM; + struct blasfeo_dvec *tmp_nuxM; + int bs; // block size + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_cond_qp_arg_memsize(); +// +void d_cond_qp_arg_create(struct d_cond_qp_arg *cond_arg, void *mem); +// +void d_cond_qp_arg_set_default(struct d_cond_qp_arg *cond_arg); +// condensing algorithm: 0 N2-nx3, 1 N3-nx2 +void d_cond_qp_arg_set_cond_alg(int cond_alg, struct d_cond_qp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 square-root +void d_cond_qp_arg_set_ric_alg(int ric_alg, struct d_cond_qp_arg *cond_arg); +// condense last stage: 0 last stage disregarded, 1 last stage condensed too +void d_cond_qp_arg_set_cond_last_stage(int cond_last_stage, struct d_cond_qp_arg *cond_arg); +// +void d_cond_qp_arg_set_comp_prim_sol(int value, struct d_cond_qp_arg *cond_arg); +// +void d_cond_qp_arg_set_comp_dual_sol_eq(int value, struct d_cond_qp_arg *cond_arg); +// +void d_cond_qp_arg_set_comp_dual_sol_ineq(int value, struct d_cond_qp_arg *cond_arg); + +// +void d_cond_qp_compute_dim(struct d_ocp_qp_dim *ocp_dim, struct d_dense_qp_dim *dense_dim); +// +hpipm_size_t d_cond_qp_ws_memsize(struct d_ocp_qp_dim *ocp_dim, struct d_cond_qp_arg *cond_arg); +// +void d_cond_qp_ws_create(struct d_ocp_qp_dim *ocp_dim, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws, void *mem); +// +void d_cond_qp_cond(struct d_ocp_qp *ocp_qp, struct d_dense_qp *dense_qp, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_qp_cond_lhs(struct d_ocp_qp *ocp_qp, struct d_dense_qp *dense_qp, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_qp_cond_rhs(struct d_ocp_qp *ocp_qp, struct d_dense_qp *dense_qp, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_qp_expand_sol(struct d_ocp_qp *ocp_qp, struct d_dense_qp_sol *dense_qp_sol, struct d_ocp_qp_sol *ocp_qp_sol, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// TODO remove +void d_cond_qp_expand_primal_sol(struct d_ocp_qp *ocp_qp, struct d_dense_qp_sol *dense_qp_sol, struct d_ocp_qp_sol *ocp_qp_sol, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); + +// +void d_cond_qp_update(int *idxc, struct d_ocp_qp *ocp_qp, struct d_dense_qp *dense_qp, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_COND_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_cond_aux.h b/phonelibs/acados/include/hpipm/include/hpipm_d_cond_aux.h new file mode 100644 index 0000000000..73afba3c7e --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_cond_aux.h @@ -0,0 +1,92 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_COND_AUX_H_ +#define HPIPM_D_COND_AUX_H_ + + + +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_cond_BAbt(struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *BAbt2, struct blasfeo_dvec *b, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_BAt(struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *BAbt2, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_b(struct d_ocp_qp *ocp_qp, struct blasfeo_dvec *b, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_RSQrq(struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *RSQrq2, struct blasfeo_dvec *rq, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_RSQ(struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *RSQrq2, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_rq(struct d_ocp_qp *ocp_qp, struct blasfeo_dvec *rq, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_DCtd(struct d_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_dmat *DCt2, struct blasfeo_dvec *d2, struct blasfeo_dvec *d_mask2, int *idxs_rev2, struct blasfeo_dvec *Z2, struct blasfeo_dvec *z, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_DCt(struct d_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_dmat *DCt2, int *idxs_rev2, struct blasfeo_dvec *Z2, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_cond_d(struct d_ocp_qp *ocp_qp, struct blasfeo_dvec *d2, struct blasfeo_dvec *d_mask2, struct blasfeo_dvec *z, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_expand_sol(struct d_ocp_qp *ocp_qp, struct d_dense_qp_sol *dense_qp_sol, struct d_ocp_qp_sol *ocp_qp_so, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_expand_primal_sol(struct d_ocp_qp *ocp_qp, struct d_dense_qp_sol *dense_qp_sol, struct d_ocp_qp_sol *ocp_qp_so, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); + +// +void d_update_cond_BAbt(int *idxc, struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *BAbt2, struct blasfeo_dvec *b, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_update_cond_RSQrq_N2nx3(int *idxc, struct d_ocp_qp *ocp_qp, struct blasfeo_dmat *RSQrq2, struct blasfeo_dvec *rq, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); +// +void d_update_cond_DCtd(int *idxc, struct d_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_dmat *DCt2, struct blasfeo_dvec *d2, int *idxs2, struct blasfeo_dvec *Z2, struct blasfeo_dvec *z, struct d_cond_qp_arg *cond_arg, struct d_cond_qp_ws *cond_ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_COND_AUX_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_cond_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_cond_qcqp.h new file mode 100644 index 0000000000..266567bb8d --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_cond_qcqp.h @@ -0,0 +1,129 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_COND_QCQP_H_ +#define HPIPM_D_COND_QCQP_H_ + + + +#include +#include + +#include "hpipm_d_dense_qcqp.h" +#include "hpipm_d_dense_qcqp_sol.h" +#include "hpipm_d_ocp_qcqp.h" +#include "hpipm_d_ocp_qcqp_dim.h" +#include "hpipm_d_ocp_qcqp_sol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_cond_qcqp_arg + { + struct d_cond_qp_arg *qp_arg; + int cond_last_stage; // condense last stage +// int cond_variant; // TODO + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution equality constr (lam t) + int square_root_alg; // square root algorithm (faster but requires RSQ>0) + hpipm_size_t memsize; + }; + + + +struct d_cond_qcqp_ws + { + struct d_cond_qp_ws *qp_ws; + struct blasfeo_dmat *hess_array; // TODO remove + struct blasfeo_dmat *zero_hess; // TODO remove + struct blasfeo_dvec *grad_array; // TODO remove + struct blasfeo_dvec *zero_grad; // TODO remove + struct blasfeo_dvec *tmp_nvc; + struct blasfeo_dvec *tmp_nuxM; + struct blasfeo_dmat *GammaQ; + struct blasfeo_dmat *tmp_DCt; + struct blasfeo_dmat *tmp_nuM_nxM; +// struct blasfeo_dvec *d_qp; +// struct blasfeo_dvec *d_mask_qp; + hpipm_size_t memsize; + }; + + +// +hpipm_size_t d_cond_qcqp_arg_memsize(); +// +void d_cond_qcqp_arg_create(struct d_cond_qcqp_arg *cond_arg, void *mem); +// +void d_cond_qcqp_arg_set_default(struct d_cond_qcqp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 square-root +void d_cond_qcqp_arg_set_ric_alg(int ric_alg, struct d_cond_qcqp_arg *cond_arg); +// condense last stage: 0 last stage disregarded, 1 last stage condensed too +void d_cond_qcqp_arg_set_cond_last_stage(int cond_last_stage, struct d_cond_qcqp_arg *cond_arg); + +// +void d_cond_qcqp_compute_dim(struct d_ocp_qcqp_dim *ocp_dim, struct d_dense_qcqp_dim *dense_dim); +// +hpipm_size_t d_cond_qcqp_ws_memsize(struct d_ocp_qcqp_dim *ocp_dim, struct d_cond_qcqp_arg *cond_arg); +// +void d_cond_qcqp_ws_create(struct d_ocp_qcqp_dim *ocp_dim, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws, void *mem); +// +void d_cond_qcqp_qc(struct d_ocp_qcqp *ocp_qp, struct blasfeo_dmat *Hq2, int *Hq_nzero2, struct blasfeo_dmat *Ct2, struct blasfeo_dvec *d2, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_qc_lhs(struct d_ocp_qcqp *ocp_qp, struct blasfeo_dmat *Hq2, int *Hq_nzero2, struct blasfeo_dmat *Ct2, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_qc_rhs(struct d_ocp_qcqp *ocp_qp, struct blasfeo_dvec *d2, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_cond(struct d_ocp_qcqp *ocp_qp, struct d_dense_qcqp *dense_qp, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_cond_rhs(struct d_ocp_qcqp *ocp_qp, struct d_dense_qcqp *dense_qp, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_cond_lhs(struct d_ocp_qcqp *ocp_qp, struct d_dense_qcqp *dense_qp, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); +// +void d_cond_qcqp_expand_sol(struct d_ocp_qcqp *ocp_qp, struct d_dense_qcqp_sol *dense_qp_sol, struct d_ocp_qcqp_sol *ocp_qp_sol, struct d_cond_qcqp_arg *cond_arg, struct d_cond_qcqp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_COND_QCQP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm.h new file mode 100644 index 0000000000..f39d9a9b50 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm.h @@ -0,0 +1,101 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_CORE_QP_IPM_ +#define HPIPM_D_CORE_QP_IPM_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct d_core_qp_ipm_workspace + { + double *v; // primal variables + double *pi; // equality constraints multipliers + double *lam; // inequality constraints multipliers + double *t; // inequality constraints slacks + double *t_inv; // inverse of t + double *v_bkp; // backup of primal variables + double *pi_bkp; // backup of equality constraints multipliers + double *lam_bkp; // backup of inequality constraints multipliers + double *t_bkp; // backup of inequality constraints slacks + double *dv; // step in v + double *dpi; // step in pi + double *dlam; // step in lam + double *dt; // step in t + double *res_g; // q-residuals + double *res_b; // b-residuals + double *res_d; // d-residuals + double *res_m; // m-residuals + double *res_m_bkp; // m-residuals + double *Gamma; // Hessian update + double *gamma; // gradient update + double alpha; // step length + double alpha_prim; // step length + double alpha_dual; // step length + double sigma; // centering XXX + double mu; // duality measuere + double mu_aff; // affine duality measuere + double nc_inv; // 1.0/nc, where nc is the total number of inequality constraints + double nc_mask_inv; // 1.0/nc_mask + double lam_min; // min value in lam vector + double t_min; // min value in t vector + double t_min_inv; // inverse of min value in t vector + double tau_min; // min value of barrier parameter + int nv; // number of primal variables + int ne; // number of equality constraints + int nc; // (twice the) number of (two-sided) inequality constraints + int nc_mask; // total number of ineq constr after masking + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam also in solution, or only in Gamma computation + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t d_memsize_core_qp_ipm(int nv, int ne, int nc); +// +void d_create_core_qp_ipm(int nv, int ne, int nc, struct d_core_qp_ipm_workspace *workspace, void *mem); +// +void d_core_qp_ipm(struct d_core_qp_ipm_workspace *workspace); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_D_CORE_QP_IPM_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm_aux.h b/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm_aux.h new file mode 100644 index 0000000000..30cc824bad --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_core_qp_ipm_aux.h @@ -0,0 +1,68 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_CORE_QP_IPM_AUX_ +#define HPIPM_S_CORE_QP_IPM_AUX_ + +#ifdef __cplusplus +extern "C" { +#endif + +// +void d_compute_Gamma_gamma_qp(double *res_d, double *res_m, struct d_core_qp_ipm_workspace *rws); +// +void d_compute_gamma_qp(double *res_d, double *res_m, struct d_core_qp_ipm_workspace *rws); +// +void d_compute_lam_t_qp(double *res_d, double *res_m, double *dlam, double *dt, struct d_core_qp_ipm_workspace *rws); +// +void d_compute_alpha_qp(struct d_core_qp_ipm_workspace *rws); +// +void d_update_var_qp(struct d_core_qp_ipm_workspace *rws); +// +void d_compute_mu_aff_qp(struct d_core_qp_ipm_workspace *rws); +// +void d_backup_res_m(struct d_core_qp_ipm_workspace *rws); +// +void d_compute_centering_correction_qp(struct d_core_qp_ipm_workspace *rws); +// +void d_compute_centering_qp(struct d_core_qp_ipm_workspace *rws); +// +void d_compute_tau_min_qp(struct d_core_qp_ipm_workspace *rws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_S_CORE_QP_IPM_AUX_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp.h new file mode 100644 index 0000000000..3da5716493 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp.h @@ -0,0 +1,199 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QCQP_H_ +#define HPIPM_D_DENSE_QCQP_H_ + + + +#include +#include + +#include "hpipm_d_dense_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qcqp + { + struct d_dense_qcqp_dim *dim; + struct blasfeo_dmat *Hv; // hessian of cost & vector work space + struct blasfeo_dmat *A; // equality constraint matrix + struct blasfeo_dmat *Ct; // inequality constraints matrix + struct blasfeo_dmat *Hq; // hessians of quadratic constraints + struct blasfeo_dvec *gz; // gradient of cost & gradient of slacks + struct blasfeo_dvec *b; // equality constraint vector + struct blasfeo_dvec *d; // inequality constraints vector + struct blasfeo_dvec *d_mask; // inequality constraints mask vector + struct blasfeo_dvec *m; // rhs of complementarity condition + struct blasfeo_dvec *Z; // (diagonal) hessian of slacks + int *idxb; // indices of box constrained variables within [u; x] + int *idxs_rev; // index of soft constraints (reverse storage) + int *Hq_nzero; // for each int, the last 3 bits ...abc, {a,b,c}=0 => {R,S,Q}=0 + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_dense_qcqp_memsize(struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_create(struct d_dense_qcqp_dim *dim, struct d_dense_qcqp *qp, void *memory); + +// +void d_dense_qcqp_set(char *field, void *value, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_H(double *H, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_g(double *g, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_A(double *A, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_b(double *b, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_idxb(int *idxb, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_lb(double *lb, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_lb_mask(double *lb, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ub(double *ub, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ub_mask(double *ub, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_C(double *C, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_lg(double *lg, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_lg_mask(double *lg, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ug(double *ug, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ug_mask(double *ug, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_Hq(double *Hq, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_gq(double *gq, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_uq(double *uq, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_uq_mask(double *uq, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_idxs(int *idxs, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_idxs_rev(int *idxs_rev, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_Zl(double *Zl, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_Zu(double *Zu, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_zl(double *zl, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_zu(double *zu, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ls(double *ls, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_ls_mask(double *ls, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_us(double *us, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_set_us_mask(double *us, struct d_dense_qcqp *qp); + +// getters (COLMAJ) + +void d_dense_qcqp_get_H(struct d_dense_qcqp *qp, double *H); +// +void d_dense_qcqp_get_g(struct d_dense_qcqp *qp, double *g); +// +void d_dense_qcqp_get_A(struct d_dense_qcqp *qp, double *A); +// +void d_dense_qcqp_get_b(struct d_dense_qcqp *qp, double *b); +// +void d_dense_qcqp_get_idxb(struct d_dense_qcqp *qp, int *idxb); +// +void d_dense_qcqp_get_lb(struct d_dense_qcqp *qp, double *lb); +// +void d_dense_qcqp_get_lb_mask(struct d_dense_qcqp *qp, double *lb); +// +void d_dense_qcqp_get_ub(struct d_dense_qcqp *qp, double *ub); +// +void d_dense_qcqp_get_ub_mask(struct d_dense_qcqp *qp, double *ub); +// +void d_dense_qcqp_get_C(struct d_dense_qcqp *qp, double *C); +// +void d_dense_qcqp_get_lg(struct d_dense_qcqp *qp, double *lg); +// +void d_dense_qcqp_get_lg_mask(struct d_dense_qcqp *qp, double *lg); +// +void d_dense_qcqp_get_ug(struct d_dense_qcqp *qp, double *ug); +// +void d_dense_qcqp_get_ug_mask(struct d_dense_qcqp *qp, double *ug); +// +void d_dense_qcqp_get_idxs(struct d_dense_qcqp *qp, int *idxs); +// +void d_dense_qcqp_get_idxs_rev(struct d_dense_qcqp *qp, int *idxs_rev); +// +void d_dense_qcqp_get_Zl(struct d_dense_qcqp *qp, double *Zl); +// +void d_dense_qcqp_get_Zu(struct d_dense_qcqp *qp, double *Zu); +// +void d_dense_qcqp_get_zl(struct d_dense_qcqp *qp, double *zl); +// +void d_dense_qcqp_get_zu(struct d_dense_qcqp *qp, double *zu); +// +void d_dense_qcqp_get_ls(struct d_dense_qcqp *qp, double *ls); +// +void d_dense_qcqp_get_ls_mask(struct d_dense_qcqp *qp, double *ls); +// +void d_dense_qcqp_get_us(struct d_dense_qcqp *qp, double *us); +// +void d_dense_qcqp_get_us_mask(struct d_dense_qcqp *qp, double *us); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_dim.h new file mode 100644 index 0000000000..fa8c574a1e --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_dim.h @@ -0,0 +1,98 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QCQP_DIM_H_ +#define HPIPM_D_DENSE_QCQP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qcqp_dim + { + struct d_dense_qp_dim *qp_dim; // dim of qp approximation + int nv; // number of variables + int ne; // number of equality constraints + int nb; // number of box constraints + int ng; // number of general constraints + int nq; // number of quadratic constraints + int nsb; // number of softened box constraints + int nsg; // number of softened general constraints + int nsq; // number of softened quadratic constraints + int ns; // number of softened constraints (nsb+nsg+nsq) TODO number of slacks + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qcqp_dim_memsize(); +// +void d_dense_qcqp_dim_create(struct d_dense_qcqp_dim *dim, void *memory); +// +void d_dense_qcqp_dim_set(char *field_name, int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nv(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_ne(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nb(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_ng(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nq(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nsb(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nsg(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_nsq(int value, struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_dim_set_ns(int value, struct d_dense_qcqp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_DENSE_QCQP_DIM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_ipm.h new file mode 100644 index 0000000000..fa3f98fa79 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_ipm.h @@ -0,0 +1,193 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QCQP_IPM_H_ +#define HPIPM_D_DENSE_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qcqp_ipm_arg + { + struct d_dense_qp_ipm_arg *qp_arg; + double mu0; // initial value for duality measure + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double reg_dual; // reg of dual hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int scale; // scale hessian + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_res_exit; // compute residuals on exit (only for abs_form==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_dense_qcqp_ipm_ws + { + struct d_dense_qp_ipm_ws *qp_ws; + struct d_dense_qp *qp; + struct d_dense_qp_sol *qp_sol; + struct d_dense_qcqp_res_ws *qcqp_res_ws; + struct d_dense_qcqp_res *qcqp_res; + struct blasfeo_dvec *tmp_nv; + int iter; // iteration number + int status; + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t d_dense_qcqp_ipm_arg_memsize(struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_ipm_arg_create(struct d_dense_qcqp_dim *dim, struct d_dense_qcqp_ipm_arg *arg, void *mem); +// +void d_dense_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set(char *field, void *value, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_iter_max(int *iter_max, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_alpha_min(double *alpha_min, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_mu0(double *mu0, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_tol_stat(double *tol_stat, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_tol_eq(double *tol_eq, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_tol_ineq(double *tol_ineq, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_tol_comp(double *tol_comp, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_reg_prim(double *reg, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_reg_dual(double *reg, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_warm_start(int *warm_start, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_pred_corr(int *pred_corr, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_cond_pred_corr(int *cond_pred_corr, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_comp_res_pred(int *comp_res_pred, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_comp_res_exit(int *comp_res_exit, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_lam_min(double *value, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_t_min(double *value, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_split_step(int *value, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_arg_set_t_lam_min(int *value, struct d_dense_qcqp_ipm_arg *arg); + +// +hpipm_size_t d_dense_qcqp_ipm_ws_memsize(struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_ipm_ws_create(struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp_ipm_arg *arg, struct d_dense_qcqp_ipm_ws *ws, void *mem); +// +void d_dense_qcqp_ipm_get(char *field, struct d_dense_qcqp_ipm_ws *ws, void *value); +// +void d_dense_qcqp_ipm_get_status(struct d_dense_qcqp_ipm_ws *ws, int *status); +// +void d_dense_qcqp_ipm_get_iter(struct d_dense_qcqp_ipm_ws *ws, int *iter); +// +void d_dense_qcqp_ipm_get_max_res_stat(struct d_dense_qcqp_ipm_ws *ws, double *res_stat); +// +void d_dense_qcqp_ipm_get_max_res_eq(struct d_dense_qcqp_ipm_ws *ws, double *res_eq); +// +void d_dense_qcqp_ipm_get_max_res_ineq(struct d_dense_qcqp_ipm_ws *ws, double *res_ineq); +// +void d_dense_qcqp_ipm_get_max_res_comp(struct d_dense_qcqp_ipm_ws *ws, double *res_comp); +// +void d_dense_qcqp_ipm_get_stat(struct d_dense_qcqp_ipm_ws *ws, double **stat); +// +void d_dense_qcqp_ipm_get_stat_m(struct d_dense_qcqp_ipm_ws *ws, int *stat_m); +// +void d_dense_qcqp_init_var(struct d_dense_qcqp *qp, struct d_dense_qcqp_sol *qp_sol, struct d_dense_qcqp_ipm_arg *arg, struct d_dense_qcqp_ipm_ws *ws); +// +void d_dense_qcqp_ipm_solve(struct d_dense_qcqp *qp, struct d_dense_qcqp_sol *qp_sol, struct d_dense_qcqp_ipm_arg *arg, struct d_dense_qcqp_ipm_ws *ws); +#if 0 +// +void d_dense_qcqp_ipm_predict(struct d_dense_qcqp *qp, struct d_dense_qcqp_sol *qp_sol, struct d_dense_qcqp_ipm_arg *arg, struct d_dense_qcqp_ipm_ws *ws); +// +void d_dense_qcqp_ipm_sens(struct d_dense_qcqp *qp, struct d_dense_qcqp_sol *qp_sol, struct d_dense_qcqp_ipm_arg *arg, struct d_dense_qcqp_ipm_ws *ws); +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QCQP_IPM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_res.h new file mode 100644 index 0000000000..a76f16215e --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_res.h @@ -0,0 +1,107 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QCQP_RES_H_ +#define HPIPM_D_DENSE_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qcqp_res + { + struct d_dense_qcqp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // infinity norm of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_dense_qcqp_res_ws + { + struct blasfeo_dvec *tmp_nv; // work space of size nv + struct blasfeo_dvec *tmp_nbgq; // work space of size nbM+ngM+nqM + struct blasfeo_dvec *tmp_ns; // work space of size nsM + struct blasfeo_dvec *q_fun; // value for evaluation of quadr constr + struct blasfeo_dvec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qcqp_res_memsize(struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_res_create(struct d_dense_qcqp_dim *dim, struct d_dense_qcqp_res *res, void *mem); +// +hpipm_size_t d_dense_qcqp_res_ws_memsize(struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_res_ws_create(struct d_dense_qcqp_dim *dim, struct d_dense_qcqp_res_ws *workspace, void *mem); +// +void d_dense_qcqp_res_compute(struct d_dense_qcqp *qp, struct d_dense_qcqp_sol *qp_sol, struct d_dense_qcqp_res *res, struct d_dense_qcqp_res_ws *ws); +// +void d_dense_qcqp_res_compute_inf_norm(struct d_dense_qcqp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_DENSE_QCQP_RES_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_sol.h new file mode 100644 index 0000000000..6c697a8e69 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_sol.h @@ -0,0 +1,85 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QCQP_SOL_H_ +#define HPIPM_D_DENSE_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_dense_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qcqp_sol + { + struct d_dense_qcqp_dim *dim; + struct blasfeo_dvec *v; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + void *misc; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qcqp_sol_memsize(struct d_dense_qcqp_dim *dim); +// +void d_dense_qcqp_sol_create(struct d_dense_qcqp_dim *dim, struct d_dense_qcqp_sol *qp_sol, void *memory); +// +void d_dense_qcqp_sol_get_v(struct d_dense_qcqp_sol *qp_sol, double *v); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QCQP_SOL_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_utils.h new file mode 100644 index 0000000000..a34218bae4 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qcqp_utils.h @@ -0,0 +1,82 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QCQP_UTILS_H_ +#define HPIPM_D_DENSE_QCQP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_dense_qcqp_dim.h" +#include "hpipm_d_dense_qcqp.h" +#include "hpipm_d_dense_qcqp_sol.h" +//#include "hpipm_d_dense_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_dense_qcqp_dim_print(struct d_dense_qcqp_dim *qp_dim); +// +//void d_dense_qcqp_dim_codegen(char *file_name, char *mode, struct d_dense_qcqp_dim *qp_dim); +// +void d_dense_qcqp_print(struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp *qp); +// +//void d_dense_qcqp_codegen(char *file_name, char *mode, struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp *qp); +// +void d_dense_qcqp_sol_print(struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp_sol *dense_qcqp_sol); +// +//void d_dense_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp_ipm_arg *arg); +// +void d_dense_qcqp_res_print(struct d_dense_qcqp_dim *qp_dim, struct d_dense_qcqp_res *dense_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_DENSE_QCQP_UTILS_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp.h new file mode 100644 index 0000000000..02fba5a922 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp.h @@ -0,0 +1,207 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QP_H_ +#define HPIPM_D_DENSE_QP_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qp + { + struct d_dense_qp_dim *dim; + struct blasfeo_dmat *Hv; // hessian of cost & vector work space + struct blasfeo_dmat *A; // equality constraint matrix + struct blasfeo_dmat *Ct; // inequality constraints matrix + struct blasfeo_dvec *gz; // gradient of cost & gradient of slacks + struct blasfeo_dvec *b; // equality constraint vector + struct blasfeo_dvec *d; // inequality constraints vector + struct blasfeo_dvec *d_mask; // inequality constraints mask vector + struct blasfeo_dvec *m; // rhs of complementarity condition + struct blasfeo_dvec *Z; // (diagonal) hessian of slacks + int *idxb; // indices of box constrained variables within [u; x] + int *idxs_rev; // index of soft constraints (reverse storage) + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_dense_qp_memsize(struct d_dense_qp_dim *dim); +// +void d_dense_qp_create(struct d_dense_qp_dim *dim, struct d_dense_qp *qp, void *memory); + +// setters - colmaj +// +void d_dense_qp_set_all(double *H, double *g, double *A, double *b, int *idxb, double *d_lb, double *d_ub, double *C, double *d_lg, double *d_ug, double *Zl, double *Zu, double *zl, double *zu, int *idxs, double *d_ls, double *d_us, struct d_dense_qp *qp); +// +void d_dense_qp_get_all(struct d_dense_qp *qp, double *H, double *g, double *A, double *b, int *idxb, double *d_lb, double *d_ub, double *C, double *d_lg, double *d_ug, double *Zl, double *Zu, double *zl, double *zu, int *idxs, double *d_ls, double *d_us); +// +void d_dense_qp_set(char *field, void *value, struct d_dense_qp *qp); +// +void d_dense_qp_set_H(double *H, struct d_dense_qp *qp); +// +void d_dense_qp_set_g(double *g, struct d_dense_qp *qp); +// +void d_dense_qp_set_A(double *A, struct d_dense_qp *qp); +// +void d_dense_qp_set_b(double *b, struct d_dense_qp *qp); +// +void d_dense_qp_set_idxb(int *idxb, struct d_dense_qp *qp); +// +void d_dense_qp_set_Jb(double *Jb, struct d_dense_qp *qp); +// +void d_dense_qp_set_lb(double *lb, struct d_dense_qp *qp); +// +void d_dense_qp_set_lb_mask(double *lb, struct d_dense_qp *qp); +// +void d_dense_qp_set_ub(double *ub, struct d_dense_qp *qp); +// +void d_dense_qp_set_ub_mask(double *ub, struct d_dense_qp *qp); +// +void d_dense_qp_set_C(double *C, struct d_dense_qp *qp); +// +void d_dense_qp_set_lg(double *lg, struct d_dense_qp *qp); +// +void d_dense_qp_set_lg_mask(double *lg, struct d_dense_qp *qp); +// +void d_dense_qp_set_ug(double *ug, struct d_dense_qp *qp); +// +void d_dense_qp_set_ug_mask(double *ug, struct d_dense_qp *qp); +// +void d_dense_qp_set_idxs(int *idxs, struct d_dense_qp *qp); +// +void d_dense_qp_set_idxs_rev(int *idxs_rev, struct d_dense_qp *qp); +// +void d_dense_qp_set_Jsb(double *Jsb, struct d_dense_qp *qp); +// +void d_dense_qp_set_Jsg(double *Jsg, struct d_dense_qp *qp); +// +void d_dense_qp_set_Zl(double *Zl, struct d_dense_qp *qp); +// +void d_dense_qp_set_Zu(double *Zu, struct d_dense_qp *qp); +// +void d_dense_qp_set_zl(double *zl, struct d_dense_qp *qp); +// +void d_dense_qp_set_zu(double *zu, struct d_dense_qp *qp); +// +void d_dense_qp_set_ls(double *ls, struct d_dense_qp *qp); +// +void d_dense_qp_set_ls_mask(double *ls, struct d_dense_qp *qp); +// +void d_dense_qp_set_us(double *us, struct d_dense_qp *qp); +// +void d_dense_qp_set_us_mask(double *us, struct d_dense_qp *qp); + +// getters - colmaj +// +void d_dense_qp_get_H(struct d_dense_qp *qp, double *H); +// +void d_dense_qp_get_g(struct d_dense_qp *qp, double *g); +// +void d_dense_qp_get_A(struct d_dense_qp *qp, double *A); +// +void d_dense_qp_get_b(struct d_dense_qp *qp, double *b); +// +void d_dense_qp_get_idxb(struct d_dense_qp *qp, int *idxb); +// +void d_dense_qp_get_lb(struct d_dense_qp *qp, double *lb); +// +void d_dense_qp_get_lb_mask(struct d_dense_qp *qp, double *lb); +// +void d_dense_qp_get_ub(struct d_dense_qp *qp, double *ub); +// +void d_dense_qp_get_ub_mask(struct d_dense_qp *qp, double *ub); +// +void d_dense_qp_get_C(struct d_dense_qp *qp, double *C); +// +void d_dense_qp_get_lg(struct d_dense_qp *qp, double *lg); +// +void d_dense_qp_get_lg_mask(struct d_dense_qp *qp, double *lg); +// +void d_dense_qp_get_ug(struct d_dense_qp *qp, double *ug); +// +void d_dense_qp_get_ug_mask(struct d_dense_qp *qp, double *ug); +// +void d_dense_qp_get_idxs(struct d_dense_qp *qp, int *idxs); +// +void d_dense_qp_get_idxs_rev(struct d_dense_qp *qp, int *idxs_rev); +// +void d_dense_qp_get_Zl(struct d_dense_qp *qp, double *Zl); +// +void d_dense_qp_get_Zu(struct d_dense_qp *qp, double *Zu); +// +void d_dense_qp_get_zl(struct d_dense_qp *qp, double *zl); +// +void d_dense_qp_get_zu(struct d_dense_qp *qp, double *zu); +// +void d_dense_qp_get_ls(struct d_dense_qp *qp, double *ls); +// +void d_dense_qp_get_ls_mask(struct d_dense_qp *qp, double *ls); +// +void d_dense_qp_get_us(struct d_dense_qp *qp, double *us); +// +void d_dense_qp_get_us_mask(struct d_dense_qp *qp, double *us); + +// setters - rowmaj +// +void d_dense_qp_set_all_rowmaj(double *H, double *g, double *A, double *b, int *idxb, double *d_lb, double *d_ub, double *C, double *d_lg, double *d_ug, double *Zl, double *Zu, double *zl, double *zu, int *idxs, double *d_ls, double *d_us, struct d_dense_qp *qp); + +// getters - rowmaj +// +void d_dense_qp_get_all_rowmaj(struct d_dense_qp *qp, double *H, double *g, double *A, double *b, int *idxb, double *d_lb, double *d_ub, double *C, double *d_lg, double *d_ug, double *Zl, double *Zu, double *zl, double *zu, int *idxs, double *d_ls, double *d_us); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_dim.h new file mode 100644 index 0000000000..98a551f312 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_dim.h @@ -0,0 +1,92 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QP_DIM_H_ +#define HPIPM_D_DENSE_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qp_dim + { + int nv; // number of variables + int ne; // number of equality constraints + int nb; // number of box constraints + int ng; // number of general constraints + int nsb; // number of softened box constraints + int nsg; // number of softened general constraints + int ns; // number of softened constraints (nsb+nsg) + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qp_dim_memsize(); +// +void d_dense_qp_dim_create(struct d_dense_qp_dim *qp_dim, void *memory); +// +void d_dense_qp_dim_set_all(int nv, int ne, int nb, int ng, int nsb, int nsg, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set(char *field_name, int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_nv(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_ne(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_nb(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_ng(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_nsb(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_nsg(int value, struct d_dense_qp_dim *dim); +// +void d_dense_qp_dim_set_ns(int value, struct d_dense_qp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_DENSE_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_ipm.h new file mode 100644 index 0000000000..e6e5d5d9b5 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_ipm.h @@ -0,0 +1,260 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QP_IPM_H_ +#define HPIPM_D_DENSE_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qp_ipm_arg + { + double mu0; // initial value for duality measure + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double reg_dual; // reg of dual hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + double tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int scale; // scale hessian + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_res_exit; // compute residuals on exit (only for abs_form==1) + int comp_res_pred; // compute residuals of prediction + int kkt_fact_alg; // 0 null-space, 1 schur-complement + int remove_lin_dep_eq; // 0 do not, 1 do check and remove linearly dependent equality constraints + int compute_obj; // compute obj on exit + int split_step; // use different steps for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_dense_qp_ipm_ws + { + struct d_core_qp_ipm_workspace *core_workspace; + struct d_dense_qp_res_ws *res_ws; + struct d_dense_qp_sol *sol_step; + struct d_dense_qp_sol *sol_itref; + struct d_dense_qp *qp_step; + struct d_dense_qp *qp_itref; + struct d_dense_qp_res *res; + struct d_dense_qp_res *res_itref; + struct d_dense_qp_res *res_step; + struct blasfeo_dvec *Gamma; // + struct blasfeo_dvec *gamma; // + struct blasfeo_dvec *Zs_inv; // + struct blasfeo_dmat *Lv; // + struct blasfeo_dmat *AL; // + struct blasfeo_dmat *Le; // + struct blasfeo_dmat *Ctx; // + struct blasfeo_dvec *lv; // + struct blasfeo_dvec *sv; // scale for Lv + struct blasfeo_dvec *se; // scale for Le + struct blasfeo_dvec *tmp_nbg; // work space of size nb+ng + struct blasfeo_dvec *tmp_ns; // work space of size ns + struct blasfeo_dmat *lq0; + struct blasfeo_dmat *lq1; + struct blasfeo_dvec *tmp_m; + struct blasfeo_dmat *A_LQ; + struct blasfeo_dmat *A_Q; + struct blasfeo_dmat *Zt; + struct blasfeo_dmat *ZtH; + struct blasfeo_dmat *ZtHZ; + struct blasfeo_dvec *xy; + struct blasfeo_dvec *Yxy; + struct blasfeo_dvec *xz; + struct blasfeo_dvec *tmp_nv; + struct blasfeo_dvec *tmp_2ns; + struct blasfeo_dvec *tmp_nv2ns; + struct blasfeo_dmat *A_li; // A of linearly independent equality constraints + struct blasfeo_dvec *b_li; // b of linearly independent equality constraints + struct blasfeo_dmat *A_bkp; // pointer to backup A + struct blasfeo_dvec *b_bkp; // pointer to backup b + struct blasfeo_dmat *Ab_LU; + double *stat; // convergence statistics + int *ipiv_v; + int *ipiv_e; + int *ipiv_e1; + void *lq_work0; + void *lq_work1; + void *lq_work_null; + void *orglq_work_null; + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // numer of recorded stat per ipm iter + int scale; + int use_hess_fact; + int use_A_fact; + int status; + int lq_fact; // cache from arg + int mask_constr; // use constr mask + int ne_li; // number of linearly independent equality constraints + int ne_bkp; // ne backup + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t d_dense_qp_ipm_arg_memsize(struct d_dense_qp_dim *dim); +// +void d_dense_qp_ipm_arg_create(struct d_dense_qp_dim *dim, struct d_dense_qp_ipm_arg *arg, void *mem); +// +void d_dense_qp_ipm_arg_set_default(enum hpipm_mode mode, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set(char *field, void *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_iter_max(int *iter_max, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_alpha_min(double *alpha_min, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_mu0(double *mu0, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_tol_stat(double *tol_stat, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_tol_eq(double *tol_eq, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_tol_ineq(double *tol_ineq, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_tol_comp(double *tol_comp, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_reg_prim(double *reg, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_reg_dual(double *reg, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_warm_start(int *warm_start, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_pred_corr(int *pred_corr, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_cond_pred_corr(int *cond_pred_corr, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_comp_res_pred(int *comp_res_pred, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_comp_res_exit(int *comp_res_exit, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_lam_min(double *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_t_min(double *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_tau_min(double *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_kkt_fact_alg(int *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_remove_lin_dep_eq(int *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_compute_obj(int *value, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_arg_set_t_lam_min(int *value, struct d_dense_qp_ipm_arg *arg); + +// +void d_dense_qp_ipm_arg_set_split_step(int *value, struct d_dense_qp_ipm_arg *arg); + +// +hpipm_size_t d_dense_qp_ipm_ws_memsize(struct d_dense_qp_dim *qp_dim, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_ipm_ws_create(struct d_dense_qp_dim *qp_dim, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws, void *mem); +// +void d_dense_qp_ipm_get(char *field, struct d_dense_qp_ipm_ws *ws, void *value); +// +void d_dense_qp_ipm_get_status(struct d_dense_qp_ipm_ws *ws, int *status); +// +void d_dense_qp_ipm_get_iter(struct d_dense_qp_ipm_ws *ws, int *iter); +// +void d_dense_qp_ipm_get_max_res_stat(struct d_dense_qp_ipm_ws *ws, double *res_stat); +// +void d_dense_qp_ipm_get_max_res_eq(struct d_dense_qp_ipm_ws *ws, double *res_eq); +// +void d_dense_qp_ipm_get_max_res_ineq(struct d_dense_qp_ipm_ws *ws, double *res_ineq); +// +void d_dense_qp_ipm_get_max_res_comp(struct d_dense_qp_ipm_ws *ws, double *res_comp); +// +void d_dense_qp_ipm_get_stat(struct d_dense_qp_ipm_ws *ws, double **stat); +// +void d_dense_qp_ipm_get_stat_m(struct d_dense_qp_ipm_ws *ws, int *stat_m); +// +void d_dense_qp_init_var(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_ipm_abs_step(int kk, struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_ipm_delta_step(int kk, struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_ipm_solve(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_ipm_predict(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_ipm_sens(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_compute_step_length(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_kkt.h new file mode 100644 index 0000000000..6d05779f4b --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_kkt.h @@ -0,0 +1,72 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QP_KKT_H_ +#define HPIPM_D_DENSE_QP_KKT_H_ + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_fact_solve_kkt_unconstr_dense_qp(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_fact_solve_kkt_step_dense_qp(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_fact_lq_solve_kkt_step_dense_qp(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_solve_kkt_step_dense_qp(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_remove_lin_dep_eq(struct d_dense_qp *qp, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_restore_lin_dep_eq(struct d_dense_qp *qp, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); +// +void d_dense_qp_compute_obj(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_ipm_arg *arg, struct d_dense_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QP_KKT_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_res.h new file mode 100644 index 0000000000..7c2023257a --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_res.h @@ -0,0 +1,105 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QP_RES_H_ +#define HPIPM_D_DENSE_QP_RES_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qp_res + { + struct d_dense_qp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // max of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_dense_qp_res_ws + { + struct blasfeo_dvec *tmp_nbg; // work space of size nbM+ngM + struct blasfeo_dvec *tmp_ns; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qp_res_memsize(struct d_dense_qp_dim *dim); +// +void d_dense_qp_res_create(struct d_dense_qp_dim *dim, struct d_dense_qp_res *res, void *mem); +// +hpipm_size_t d_dense_qp_res_ws_memsize(struct d_dense_qp_dim *dim); +// +void d_dense_qp_res_ws_create(struct d_dense_qp_dim *dim, struct d_dense_qp_res_ws *workspace, void *mem); +// +void d_dense_qp_res_compute(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_res *res, struct d_dense_qp_res_ws *ws); +// +void d_dense_qp_res_compute_lin(struct d_dense_qp *qp, struct d_dense_qp_sol *qp_sol, struct d_dense_qp_sol *qp_step, struct d_dense_qp_res *res, struct d_dense_qp_res_ws *ws); +// +void d_dense_qp_res_compute_inf_norm(struct d_dense_qp_res *res); +// +void d_dense_qp_res_get_all(struct d_dense_qp_res *res, double *res_g, double *res_ls, double *res_us, double *res_b, double *res_d_lb, double *res_d_ub, double *res_d_lg, double *res_d_ug, double *res_d_ls, double *res_d_us, double *res_m_lb, double *res_m_ub, double *res_m_lg, double *res_m_ug, double *res_m_ls, double *res_m_us); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_DENSE_QP_RES_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_sol.h new file mode 100644 index 0000000000..aaa3fdb0e4 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_sol.h @@ -0,0 +1,94 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_DENSE_QP_SOL_H_ +#define HPIPM_D_DENSE_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_dense_qp_sol + { + struct d_dense_qp_dim *dim; + struct blasfeo_dvec *v; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + void *misc; + double obj; + int valid_obj; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_dense_qp_sol_memsize(struct d_dense_qp_dim *dim); +// +void d_dense_qp_sol_create(struct d_dense_qp_dim *dim, struct d_dense_qp_sol *qp_sol, void *memory); +// +void d_dense_qp_sol_get_all(struct d_dense_qp_sol *qp_sol, double *v, double *ls, double *us, double *pi, double *lam_lb, double *lam_ub, double *lam_lg, double *lam_ug, double *lam_ls, double *lam_us); +// +void d_dense_qp_sol_get(char *field, struct d_dense_qp_sol *sol, void *value); +// +void d_dense_qp_sol_get_v(struct d_dense_qp_sol *sol, double *v); +// +void d_dense_qp_sol_get_valid_obj(struct d_dense_qp_sol *sol, int *valid_obj); +// +void d_dense_qp_sol_get_obj(struct d_dense_qp_sol *sol, double *obj); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_DENSE_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_utils.h new file mode 100644 index 0000000000..ccb77aaca3 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_dense_qp_utils.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_DENSE_QP_UTILS_H_ +#define HPIPM_D_DENSE_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp_dim.h" +#include "hpipm_d_dense_qp.h" +#include "hpipm_d_dense_qp_sol.h" +#include "hpipm_d_dense_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_dense_qp_dim_print(struct d_dense_qp_dim *qp_dim); +// +//void d_dense_qp_dim_codegen(char *file_name, char *mode, struct d_dense_qp_dim *qp_dim); +// +void d_dense_qp_print(struct d_dense_qp_dim *qp_dim, struct d_dense_qp *qp); +// +//void d_dense_qp_codegen(char *file_name, char *mode, struct d_dense_qp_dim *qp_dim, struct d_dense_qp *qp); +// +void d_dense_qp_sol_print(struct d_dense_qp_dim *qp_dim, struct d_dense_qp_sol *dense_qp_sol); +// +//void d_dense_qp_ipm_arg_codegen(char *file_name, char *mode, struct d_dense_qp_dim *qp_dim, struct d_dense_qp_ipm_arg *arg); +// +void d_dense_qp_res_print(struct d_dense_qp_dim *qp_dim, struct d_dense_qp_res *dense_qp_res); +// +void d_dense_qp_arg_print(struct d_dense_qp_dim *qp_dim, struct d_dense_qp_ipm_arg *qp_ipm_arg); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_DENSE_QP_UTILS_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp.h new file mode 100644 index 0000000000..240c9b04be --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp.h @@ -0,0 +1,303 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_H_ +#define HPIPM_D_OCP_QCQP_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qcqp + { + struct d_ocp_qcqp_dim *dim; + struct blasfeo_dmat *BAbt; // dynamics matrix & vector work space + struct blasfeo_dmat *RSQrq; // hessian of cost & vector work space + struct blasfeo_dmat *DCt; // inequality constraints matrix + struct blasfeo_dmat **Hq; // hessians of quadratic constraints + struct blasfeo_dvec *b; // dynamics vector + struct blasfeo_dvec *rqz; // gradient of cost & gradient of slacks + struct blasfeo_dvec *d; // inequality constraints vector + struct blasfeo_dvec *d_mask; // inequality constraints mask vector + struct blasfeo_dvec *m; // rhs of complementarity condition + struct blasfeo_dvec *Z; // (diagonal) hessian of slacks + int **idxb; // indices of box constrained variables within [u; x] + int **idxs_rev; // index of soft constraints (reverse storage) + int **Hq_nzero; // for each int, the last 3 bits ...abc, {a,b,c}=0 => {R,S,Q}=0 + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_ocp_qcqp_strsize(); +// +hpipm_size_t d_ocp_qcqp_memsize(struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_create(struct d_ocp_qcqp_dim *dim, struct d_ocp_qcqp *qp, void *memory); +// +void d_ocp_qcqp_copy_all(struct d_ocp_qcqp *qp_orig, struct d_ocp_qcqp *qp_dest); + +// setters +// +void d_ocp_qcqp_set_all_zero(struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_rhs_zero(struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set(char *field_name, int stage, void *value, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_el(char *field_name, int stage, int index, void *value, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_A(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_B(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_b(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Q(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_S(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_R(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_q(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_r(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lb(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lb_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ub(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ub_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lbx(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lbx_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_el_lbx(int stage, int index, double *elem, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ubx(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ubx_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_el_ubx(int stage, int index, double *elem, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lbu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lbu_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ubu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ubu_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_idxb(int stage, int *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_idxbx(int stage, int *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jbx(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_idxbu(int stage, int *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jbu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_C(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_D(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lg(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lg_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ug(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_ug_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Qq(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Sq(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Rq(int stage, double *mat, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_qq(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_rq(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_uq(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_uq_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Zl(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Zu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_zl(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_zu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lls(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lls_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lus(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_lus_mask(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_idxs(int stage, int *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_idxs_rev(int stage, int *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jsbu(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jsbx(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jsg(int stage, double *vec, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_set_Jsq(int stage, double *vec, struct d_ocp_qcqp *qp); + +// getters +// +void d_ocp_qcqp_get(char *field, int stage, struct d_ocp_qcqp *qp, void *value); +// +void d_ocp_qcqp_get_A(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_B(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_b(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_Q(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_S(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_R(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_q(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_r(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ub(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ub_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lb(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lb_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lbx(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lbx_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ubx(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ubx_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lbu(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lbu_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ubu(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ubu_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_idxb(int stage, struct d_ocp_qcqp *qp, int *vec); +// +//void d_ocp_qcqp_get_idxbx(int stage, struct d_ocp_qcqp *qp, int *vec); +// +//void d_ocp_qcqp_get_Jbx(int stage, struct d_ocp_qcqp *qp, double *vec); +// +//void d_ocp_qcqp_get_idxbu(int stage, struct d_ocp_qcqp *qp, int *vec); +// +//void d_ocp_qcqp_get_Jbu(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_C(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_D(int stage, struct d_ocp_qcqp *qp, double *mat); +// +void d_ocp_qcqp_get_lg(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lg_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ug(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_ug_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_Zl(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_Zu(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_zl(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_zu(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lls(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lls_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lus(int stage, struct d_ocp_qcqp *qp, double *vec); +// +void d_ocp_qcqp_get_lus_mask(int stage, struct d_ocp_qcqp *qp, double *vec); +// XXX only valid if there is one slack per softed constraint !!! +void d_ocp_qcqp_get_idxs(int stage, struct d_ocp_qcqp *qp, int *vec); +// +void d_ocp_qcqp_get_idxs_rev(int stage, struct d_ocp_qcqp *qp, int *vec); +// +//void d_ocp_qcqp_get_Jsbu(int stage, struct d_ocp_qcqp *qp, float *vec); +// +//void d_ocp_qcqp_get_Jsbx(int stage, struct d_ocp_qcqp *qp, float *vec); +// +//void d_ocp_qcqp_get_Jsg(int stage, struct d_ocp_qcqp *qp, float *vec); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_dim.h new file mode 100644 index 0000000000..268628a2b2 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_dim.h @@ -0,0 +1,118 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_DIM_H_ +#define HPIPM_D_OCP_QCQP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qcqp_dim + { + struct d_ocp_qp_dim *qp_dim; // dim of qp approximation + int *nx; // number of states + int *nu; // number of inputs + int *nb; // number of box constraints + int *nbx; // number of (two-sided) state box constraints + int *nbu; // number of (two-sided) input box constraints + int *ng; // number of (two-sided) general constraints + int *nq; // number of (upper) quadratic constraints + int *ns; // number of soft constraints + int *nsbx; // number of (two-sided) soft state box constraints + int *nsbu; // number of (two-sided) soft input box constraints + int *nsg; // number of (two-sided) soft general constraints + int *nsq; // number of (upper) soft quadratic constraints + int N; // horizon length + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qcqp_dim_strsize(); +// +hpipm_size_t d_ocp_qcqp_dim_memsize(int N); +// +void d_ocp_qcqp_dim_create(int N, struct d_ocp_qcqp_dim *qp_dim, void *memory); +// +void d_ocp_qcqp_dim_copy_all(struct d_ocp_qcqp_dim *dim_orig, struct d_ocp_qcqp_dim *dim_dest); +// +void d_ocp_qcqp_dim_set(char *field, int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nx(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nu(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nbx(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nbu(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_ng(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nq(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_ns(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nsbx(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nsbu(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nsg(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_set_nsq(int stage, int value, struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_dim_get(struct d_ocp_qcqp_dim *dim, char *field, int stage, int *value); +// +void d_ocp_qcqp_dim_get_N(struct d_ocp_qcqp_dim *dim, int *value); +// +void d_ocp_qcqp_dim_get_nx(struct d_ocp_qcqp_dim *dim, int stage, int *value); +// +void d_ocp_qcqp_dim_get_nu(struct d_ocp_qcqp_dim *dim, int stage, int *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QCQP_DIM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_ipm.h new file mode 100644 index 0000000000..99f2329dcc --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_ipm.h @@ -0,0 +1,190 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_IPM_H_ +#define HPIPM_D_OCP_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qcqp_ipm_arg + { + struct d_ocp_qp_ipm_arg *qp_arg; + double mu0; // initial value for complementarity slackness + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constrains (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_ocp_qcqp_ipm_ws + { + struct d_ocp_qp_ipm_ws *qp_ws; + struct d_ocp_qp *qp; + struct d_ocp_qp_sol *qp_sol; + struct d_ocp_qcqp_res_ws *qcqp_res_ws; + struct d_ocp_qcqp_res *qcqp_res; + struct blasfeo_dvec *tmp_nuxM; + int iter; // iteration number + int status; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qcqp_ipm_arg_strsize(); +// +hpipm_size_t d_ocp_qcqp_ipm_arg_memsize(struct d_ocp_qcqp_dim *ocp_dim); +// +void d_ocp_qcqp_ipm_arg_create(struct d_ocp_qcqp_dim *ocp_dim, struct d_ocp_qcqp_ipm_arg *arg, void *mem); +// +void d_ocp_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct d_ocp_qcqp_ipm_arg *arg); +// +void d_ocp_qcqp_ipm_arg_set(char *field, void *value, struct d_ocp_qcqp_ipm_arg *arg); +// set maximum number of iterations +void d_ocp_qcqp_ipm_arg_set_iter_max(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// set minimum step lenght +void d_ocp_qcqp_ipm_arg_set_alpha_min(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set initial value of barrier parameter +void d_ocp_qcqp_ipm_arg_set_mu0(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void d_ocp_qcqp_ipm_arg_set_tol_stat(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on equality constr +void d_ocp_qcqp_ipm_arg_set_tol_eq(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on inequality constr +void d_ocp_qcqp_ipm_arg_set_tol_ineq(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void d_ocp_qcqp_ipm_arg_set_tol_comp(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set regularization of primal variables +void d_ocp_qcqp_ipm_arg_set_reg_prim(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void d_ocp_qcqp_ipm_arg_set_warm_start(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void d_ocp_qcqp_ipm_arg_set_pred_corr(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void d_ocp_qcqp_ipm_arg_set_cond_pred_corr(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +void d_ocp_qcqp_ipm_arg_set_ric_alg(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// compute residuals after solution +void d_ocp_qcqp_ipm_arg_set_comp_res_exit(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// compute residuals of prediction +void d_ocp_qcqp_ipm_arg_set_comp_res_pred(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// min value of lam in the solution +void d_ocp_qcqp_ipm_arg_set_lam_min(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// min value of t in the solution +void d_ocp_qcqp_ipm_arg_set_t_min(double *value, struct d_ocp_qcqp_ipm_arg *arg); +// use different step for primal and dual variables +void d_ocp_qcqp_ipm_arg_set_split_step(int *value, struct d_ocp_qcqp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void d_ocp_qcqp_ipm_arg_set_t_lam_min(int *value, struct d_ocp_qcqp_ipm_arg *arg); + +// +hpipm_size_t d_ocp_qcqp_ipm_ws_strsize(); +// +hpipm_size_t d_ocp_qcqp_ipm_ws_memsize(struct d_ocp_qcqp_dim *ocp_dim, struct d_ocp_qcqp_ipm_arg *arg); +// +void d_ocp_qcqp_ipm_ws_create(struct d_ocp_qcqp_dim *ocp_dim, struct d_ocp_qcqp_ipm_arg *arg, struct d_ocp_qcqp_ipm_ws *ws, void *mem); +// +void d_ocp_qcqp_ipm_get(char *field, struct d_ocp_qcqp_ipm_ws *ws, void *value); +// +void d_ocp_qcqp_ipm_get_status(struct d_ocp_qcqp_ipm_ws *ws, int *status); +// +void d_ocp_qcqp_ipm_get_iter(struct d_ocp_qcqp_ipm_ws *ws, int *iter); +// +void d_ocp_qcqp_ipm_get_max_res_stat(struct d_ocp_qcqp_ipm_ws *ws, double *res_stat); +// +void d_ocp_qcqp_ipm_get_max_res_eq(struct d_ocp_qcqp_ipm_ws *ws, double *res_eq); +// +void d_ocp_qcqp_ipm_get_max_res_ineq(struct d_ocp_qcqp_ipm_ws *ws, double *res_ineq); +// +void d_ocp_qcqp_ipm_get_max_res_comp(struct d_ocp_qcqp_ipm_ws *ws, double *res_comp); +// +void d_ocp_qcqp_ipm_get_stat(struct d_ocp_qcqp_ipm_ws *ws, double **stat); +// +void d_ocp_qcqp_ipm_get_stat_m(struct d_ocp_qcqp_ipm_ws *ws, int *stat_m); +// +void d_ocp_qcqp_init_var(struct d_ocp_qcqp *qp, struct d_ocp_qcqp_sol *qp_sol, struct d_ocp_qcqp_ipm_arg *arg, struct d_ocp_qcqp_ipm_ws *ws); +// +void d_ocp_qcqp_ipm_solve(struct d_ocp_qcqp *qp, struct d_ocp_qcqp_sol *qp_sol, struct d_ocp_qcqp_ipm_arg *arg, struct d_ocp_qcqp_ipm_ws *ws); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_OCP_QCQP_IPM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_res.h new file mode 100644 index 0000000000..077f134677 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_res.h @@ -0,0 +1,114 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_RES_H_ +#define HPIPM_D_OCP_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qcqp_res + { + struct d_ocp_qcqp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // max of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_ocp_qcqp_res_ws + { + struct blasfeo_dvec *tmp_nuxM; // work space of size nuM+nxM + struct blasfeo_dvec *tmp_nbgqM; // work space of size nbM+ngM+nqM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + struct blasfeo_dvec *q_fun; // value for evaluation of quadr constr + struct blasfeo_dvec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qcqp_res_memsize(struct d_ocp_qcqp_dim *ocp_dim); +// +void d_ocp_qcqp_res_create(struct d_ocp_qcqp_dim *ocp_dim, struct d_ocp_qcqp_res *res, void *mem); +// +hpipm_size_t d_ocp_qcqp_res_ws_memsize(struct d_ocp_qcqp_dim *ocp_dim); +// +void d_ocp_qcqp_res_ws_create(struct d_ocp_qcqp_dim *ocp_dim, struct d_ocp_qcqp_res_ws *workspace, void *mem); +// +void d_ocp_qcqp_res_compute(struct d_ocp_qcqp *qp, struct d_ocp_qcqp_sol *qp_sol, struct d_ocp_qcqp_res *res, struct d_ocp_qcqp_res_ws *ws); +// +void d_ocp_qcqp_res_compute_inf_norm(struct d_ocp_qcqp_res *res); +// +void d_ocp_qcqp_res_get_max_res_stat(struct d_ocp_qcqp_res *res, double *value); +// +void d_ocp_qcqp_res_get_max_res_eq(struct d_ocp_qcqp_res *res, double *value); +// +void d_ocp_qcqp_res_get_max_res_ineq(struct d_ocp_qcqp_res *res, double *value); +// +void d_ocp_qcqp_res_get_max_res_comp(struct d_ocp_qcqp_res *res, double *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_OCP_QCQP_RES_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_sol.h new file mode 100644 index 0000000000..68adbfce2d --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_sol.h @@ -0,0 +1,114 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_SOL_H_ +#define HPIPM_D_OCP_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qcqp_sol + { + struct d_ocp_qcqp_dim *dim; + struct blasfeo_dvec *ux; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_ocp_qcqp_sol_strsize(); +// +hpipm_size_t d_ocp_qcqp_sol_memsize(struct d_ocp_qcqp_dim *dim); +// +void d_ocp_qcqp_sol_create(struct d_ocp_qcqp_dim *dim, struct d_ocp_qcqp_sol *qp_sol, void *memory); +// +void d_ocp_qcqp_sol_copy_all(struct d_ocp_qcqp_sol *qp_sol_orig, struct d_ocp_qcqp_sol *qp_sol_dest); +// +void d_ocp_qcqp_sol_get(char *field, int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_u(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_x(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_sl(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_su(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_pi(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_lam_lb(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_lam_ub(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_lam_lg(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_get_lam_ug(int stage, struct d_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_ocp_qcqp_sol_set(char *field, int stage, double *vec, struct d_ocp_qcqp_sol *qp_sol); +// +void d_ocp_qcqp_sol_set_u(int stage, double *vec, struct d_ocp_qcqp_sol *qp_sol); +// +void d_ocp_qcqp_sol_set_x(int stage, double *vec, struct d_ocp_qcqp_sol *qp_sol); +// +void d_ocp_qcqp_sol_set_sl(int stage, double *vec, struct d_ocp_qcqp_sol *qp_sol); +// +void d_ocp_qcqp_sol_set_su(int stage, double *vec, struct d_ocp_qcqp_sol *qp_sol); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QCQP_SOL_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_utils.h new file mode 100644 index 0000000000..00248f1dbe --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qcqp_utils.h @@ -0,0 +1,81 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QCQP_UTILS_H_ +#define HPIPM_D_OCP_QCQP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qcqp_dim.h" +#include "hpipm_d_ocp_qp.h" +#include "hpipm_d_ocp_qcqp_sol.h" +#include "hpipm_d_ocp_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_ocp_qcqp_dim_print(struct d_ocp_qcqp_dim *qcqp_dim); +// +void d_ocp_qcqp_dim_codegen(char *file_name, char *mode, struct d_ocp_qcqp_dim *qcqp_dim); +// +void d_ocp_qcqp_print(struct d_ocp_qcqp_dim *qcqp_dim, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_codegen(char *file_name, char *mode, struct d_ocp_qcqp_dim *qcqp_dim, struct d_ocp_qcqp *qp); +// +void d_ocp_qcqp_sol_print(struct d_ocp_qcqp_dim *qcqp_dim, struct d_ocp_qcqp_sol *ocp_qcqp_sol); +// +void d_ocp_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct d_ocp_qcqp_dim *qcqp_dim, struct d_ocp_qcqp_ipm_arg *arg); +// +void d_ocp_qcqp_res_print(struct d_ocp_qcqp_dim *qcqp_dim, struct d_ocp_qcqp_res *ocp_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QCQP_UTILS_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp.h new file mode 100644 index 0000000000..ca26cdba34 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp.h @@ -0,0 +1,306 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_H_ +#define HPIPM_D_OCP_QP_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp + { + struct d_ocp_qp_dim *dim; + struct blasfeo_dmat *BAbt; // dynamics matrix & vector work space + struct blasfeo_dmat *RSQrq; // hessian of cost & vector work space + struct blasfeo_dmat *DCt; // inequality constraints matrix + struct blasfeo_dvec *b; // dynamics vector + struct blasfeo_dvec *rqz; // gradient of cost & gradient of slacks + struct blasfeo_dvec *d; // inequality constraints vector + struct blasfeo_dvec *d_mask; // inequality constraints mask vector + struct blasfeo_dvec *m; // rhs of complementarity condition + struct blasfeo_dvec *Z; // (diagonal) hessian of slacks + int **idxb; // indices of box constrained variables within [u; x] + int **idxs_rev; // index of soft constraints (reverse storage) + int **idxe; // indices of constraints within [bu, bx, g] that are equalities, subset of [0, ..., nbu+nbx+ng-1] + int *diag_H_flag; // flag the fact that Hessian is diagonal + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_ocp_qp_strsize(); +// +hpipm_size_t d_ocp_qp_memsize(struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_create(struct d_ocp_qp_dim *dim, struct d_ocp_qp *qp, void *memory); +// +void d_ocp_qp_copy_all(struct d_ocp_qp *qp_orig, struct d_ocp_qp *qp_dest); + +// setters +// +void d_ocp_qp_set_all_zero(struct d_ocp_qp *qp); +// +void d_ocp_qp_set_rhs_zero(struct d_ocp_qp *qp); +// +void d_ocp_qp_set_all(double **A, double **B, double **b, double **Q, double **S, double **R, double **q, double **r, int **idxbx, double **lbx, double **ubx, int **idxbu, double **lbu, double **ubu, double **C, double **D, double **lg, double **ug, double **Zl, double **Zu, double **zl, double **zu, int **idxs, double **ls, double **us, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_all_rowmaj(double **A, double **B, double **b, double **Q, double **S, double **R, double **q, double **r, int **idxbx, double **lbx, double **ubx, int **idxbu, double **lbu, double **ubu, double **C, double **D, double **lg, double **ug, double **Zl, double **Zu, double **zl, double **zu, int **idxs, double **ls, double **us, struct d_ocp_qp *qp); +// +void d_ocp_qp_set(char *field_name, int stage, void *value, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_el(char *field_name, int stage, int index, void *value, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_A(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_B(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_b(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Q(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_S(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_R(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_q(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_r(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lb(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lb_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ub(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ub_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lbx(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lbx_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_el_lbx(int stage, int index, double *elem, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ubx(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ubx_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_el_ubx(int stage, int index, double *elem, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lbu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lbu_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ubu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ubu_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxb(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxbx(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jbx(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxbu(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jbu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_C(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_D(int stage, double *mat, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lg(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lg_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ug(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_ug_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Zl(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Zu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_zl(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_zu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lls(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lls_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lus(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_lus_mask(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxs(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxs_rev(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jsbu(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jsbx(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jsg(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxe(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxbxe(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxbue(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_idxge(int stage, int *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jbxe(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jbue(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_Jge(int stage, double *vec, struct d_ocp_qp *qp); +// +void d_ocp_qp_set_diag_H_flag(int stage, int *value, struct d_ocp_qp *qp); + +// getters +// +void d_ocp_qp_get(char *field, int stage, struct d_ocp_qp *qp, void *value); +// +void d_ocp_qp_get_A(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_B(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_b(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_Q(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_S(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_R(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_q(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_r(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ub(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ub_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lb(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lb_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lbx(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lbx_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ubx(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ubx_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lbu(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lbu_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ubu(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ubu_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_idxb(int stage, struct d_ocp_qp *qp, int *vec); +// +//void d_ocp_qp_get_idxbx(int stage, struct d_ocp_qp *qp, int *vec); +// +//void d_ocp_qp_get_Jbx(int stage, struct d_ocp_qp *qp, double *vec); +// +//void d_ocp_qp_get_idxbu(int stage, struct d_ocp_qp *qp, int *vec); +// +//void d_ocp_qp_get_Jbu(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_C(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_D(int stage, struct d_ocp_qp *qp, double *mat); +// +void d_ocp_qp_get_lg(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lg_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ug(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_ug_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_Zl(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_Zu(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_zl(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_zu(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lls(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lls_mask(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lus(int stage, struct d_ocp_qp *qp, double *vec); +// +void d_ocp_qp_get_lus_mask(int stage, struct d_ocp_qp *qp, double *vec); +// XXX only valid if there is one slack per softed constraint !!! +void d_ocp_qp_get_idxs(int stage, struct d_ocp_qp *qp, int *vec); +// +void d_ocp_qp_get_idxs_rev(int stage, struct d_ocp_qp *qp, int *vec); +// +//void d_ocp_qp_get_Jsbu(int stage, struct d_ocp_qp *qp, float *vec); +// +//void d_ocp_qp_get_Jsbx(int stage, struct d_ocp_qp *qp, float *vec); +// +//void d_ocp_qp_get_Jsg(int stage, struct d_ocp_qp *qp, float *vec); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_dim.h new file mode 100644 index 0000000000..dcfb801d31 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_dim.h @@ -0,0 +1,142 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_DIM_H_ +#define HPIPM_D_OCP_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp_dim + { + int *nx; // number of states + int *nu; // number of inputs + int *nb; // number of box constraints + int *nbx; // number of state box constraints + int *nbu; // number of input box constraints + int *ng; // number of general constraints + int *ns; // number of soft constraints + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int *nbxe; // number of state box constraints which are equality + int *nbue; // number of input box constraints which are equality + int *nge; // number of general constraints which are equality + int N; // horizon length + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qp_dim_strsize(); +// +hpipm_size_t d_ocp_qp_dim_memsize(int N); +// +void d_ocp_qp_dim_create(int N, struct d_ocp_qp_dim *qp_dim, void *memory); +// +void d_ocp_qp_dim_copy_all(struct d_ocp_qp_dim *dim_orig, struct d_ocp_qp_dim *dim_dest); +// +void d_ocp_qp_dim_set_all(int *nx, int *nu, int *nbx, int *nbu, int *ng, int *nsbx, int *nsbu, int *nsg, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set(char *field, int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nx(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nu(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nbx(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nbu(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_ng(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_ns(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nsbx(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nsbu(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nsg(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nbxe(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nbue(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_set_nge(int stage, int value, struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_dim_get(struct d_ocp_qp_dim *dim, char *field, int stage, int *value); +// +void d_ocp_qp_dim_get_N(struct d_ocp_qp_dim *dim, int *value); +// +void d_ocp_qp_dim_get_nx(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nu(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nbx(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nbu(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_ng(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_ns(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nsbx(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nsbu(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nsg(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nbxe(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nbue(struct d_ocp_qp_dim *dim, int stage, int *value); +// +void d_ocp_qp_dim_get_nge(struct d_ocp_qp_dim *dim, int stage, int *value); + + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_ipm.h new file mode 100644 index 0000000000..d2a2833f22 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_ipm.h @@ -0,0 +1,250 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_IPM_H_ +#define HPIPM_D_OCP_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp_ipm_arg + { + double mu0; // initial value for complementarity slackness + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + double tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constraints (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different steps for primal and dual variables + int var_init_scheme; // variables initialization scheme + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_ocp_qp_ipm_ws + { + double qp_res[4]; // infinity norm of residuals + struct d_core_qp_ipm_workspace *core_workspace; + struct d_ocp_qp_dim *dim; // cache dim + struct d_ocp_qp_res_ws *res_workspace; + struct d_ocp_qp_sol *sol_step; + struct d_ocp_qp_sol *sol_itref; + struct d_ocp_qp *qp_step; + struct d_ocp_qp *qp_itref; + struct d_ocp_qp_res *res_itref; + struct d_ocp_qp_res *res; + struct blasfeo_dvec *Gamma; // hessian update + struct blasfeo_dvec *gamma; // hessian update + struct blasfeo_dvec *tmp_nuxM; // work space of size nxM + struct blasfeo_dvec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + struct blasfeo_dvec *Pb; // Pb + struct blasfeo_dvec *Zs_inv; + struct blasfeo_dvec *tmp_m; + struct blasfeo_dvec *l; // cache linear part for _get_ric_xxx + struct blasfeo_dmat *L; + struct blasfeo_dmat *Ls; + struct blasfeo_dmat *P; + struct blasfeo_dmat *Lh; + struct blasfeo_dmat *AL; + struct blasfeo_dmat *lq0; + struct blasfeo_dmat *tmp_nxM_nxM; + double *stat; // convergence statistics + int *use_hess_fact; + void *lq_work0; + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // number of recorded stat per IPM iter + int use_Pb; + int status; // solver status + int square_root_alg; // cache from arg + int lq_fact; // cache from arg + int mask_constr; // use constr mask + int valid_ric_vec; // meaningful riccati vectors + int valid_ric_p; // form of riccati p: 0 p*inv(L), 1 p + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qp_ipm_arg_strsize(); +// +hpipm_size_t d_ocp_qp_ipm_arg_memsize(struct d_ocp_qp_dim *ocp_dim); +// +void d_ocp_qp_ipm_arg_create(struct d_ocp_qp_dim *ocp_dim, struct d_ocp_qp_ipm_arg *arg, void *mem); +// +void d_ocp_qp_ipm_arg_set_default(enum hpipm_mode mode, struct d_ocp_qp_ipm_arg *arg); +// +void d_ocp_qp_ipm_arg_set(char *field, void *value, struct d_ocp_qp_ipm_arg *arg); +// set maximum number of iterations +void d_ocp_qp_ipm_arg_set_iter_max(int *iter_max, struct d_ocp_qp_ipm_arg *arg); +// set minimum step lenght +void d_ocp_qp_ipm_arg_set_alpha_min(double *alpha_min, struct d_ocp_qp_ipm_arg *arg); +// set initial value of barrier parameter +void d_ocp_qp_ipm_arg_set_mu0(double *mu0, struct d_ocp_qp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void d_ocp_qp_ipm_arg_set_tol_stat(double *tol_stat, struct d_ocp_qp_ipm_arg *arg); +// set exit tolerance on equality constr +void d_ocp_qp_ipm_arg_set_tol_eq(double *tol_eq, struct d_ocp_qp_ipm_arg *arg); +// set exit tolerance on inequality constr +void d_ocp_qp_ipm_arg_set_tol_ineq(double *tol_ineq, struct d_ocp_qp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void d_ocp_qp_ipm_arg_set_tol_comp(double *tol_comp, struct d_ocp_qp_ipm_arg *arg); +// set regularization of primal variables +void d_ocp_qp_ipm_arg_set_reg_prim(double *tol_comp, struct d_ocp_qp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void d_ocp_qp_ipm_arg_set_warm_start(int *warm_start, struct d_ocp_qp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void d_ocp_qp_ipm_arg_set_pred_corr(int *pred_corr, struct d_ocp_qp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void d_ocp_qp_ipm_arg_set_cond_pred_corr(int *value, struct d_ocp_qp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +void d_ocp_qp_ipm_arg_set_ric_alg(int *value, struct d_ocp_qp_ipm_arg *arg); +// dual solution of equality constraints (only for abs_form==1) +void d_ocp_qp_ipm_arg_set_comp_dual_sol_eq(int *value, struct d_ocp_qp_ipm_arg *arg); +// compute residuals after solution +void d_ocp_qp_ipm_arg_set_comp_res_exit(int *value, struct d_ocp_qp_ipm_arg *arg); +// compute residuals of prediction +void d_ocp_qp_ipm_arg_set_comp_res_pred(int *value, struct d_ocp_qp_ipm_arg *arg); +// min value of lam in the solution +void d_ocp_qp_ipm_arg_set_lam_min(double *value, struct d_ocp_qp_ipm_arg *arg); +// min value of t in the solution +void d_ocp_qp_ipm_arg_set_t_min(double *value, struct d_ocp_qp_ipm_arg *arg); +// min value of tau in the solution +void d_ocp_qp_ipm_arg_set_tau_min(double *value, struct d_ocp_qp_ipm_arg *arg); +// set split step: 0 same step, 1 different step for primal and dual variables +void d_ocp_qp_ipm_arg_set_split_step(int *value, struct d_ocp_qp_ipm_arg *arg); +// variables initialization scheme +void d_ocp_qp_ipm_arg_set_var_init_scheme(int *value, struct d_ocp_qp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void d_ocp_qp_ipm_arg_set_t_lam_min(int *value, struct d_ocp_qp_ipm_arg *arg); + +// +hpipm_size_t d_ocp_qp_ipm_ws_strsize(); +// +hpipm_size_t d_ocp_qp_ipm_ws_memsize(struct d_ocp_qp_dim *ocp_dim, struct d_ocp_qp_ipm_arg *arg); +// +void d_ocp_qp_ipm_ws_create(struct d_ocp_qp_dim *ocp_dim, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, void *mem); +// +void d_ocp_qp_ipm_get(char *field, struct d_ocp_qp_ipm_ws *ws, void *value); +// +void d_ocp_qp_ipm_get_status(struct d_ocp_qp_ipm_ws *ws, int *status); +// +void d_ocp_qp_ipm_get_iter(struct d_ocp_qp_ipm_ws *ws, int *iter); +// +void d_ocp_qp_ipm_get_max_res_stat(struct d_ocp_qp_ipm_ws *ws, double *res_stat); +// +void d_ocp_qp_ipm_get_max_res_eq(struct d_ocp_qp_ipm_ws *ws, double *res_eq); +// +void d_ocp_qp_ipm_get_max_res_ineq(struct d_ocp_qp_ipm_ws *ws, double *res_ineq); +// +void d_ocp_qp_ipm_get_max_res_comp(struct d_ocp_qp_ipm_ws *ws, double *res_comp); +// +void d_ocp_qp_ipm_get_stat(struct d_ocp_qp_ipm_ws *ws, double **stat); +// +void d_ocp_qp_ipm_get_stat_m(struct d_ocp_qp_ipm_ws *ws, int *stat_m); +// +void d_ocp_qp_ipm_get_ric_Lr(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *Lr); +// +void d_ocp_qp_ipm_get_ric_Ls(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *Ls); +// +void d_ocp_qp_ipm_get_ric_P(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *P); +// +void d_ocp_qp_ipm_get_ric_lr(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *lr); +// +void d_ocp_qp_ipm_get_ric_p(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *p); +// feedback control gain in the form u = K x + k +void d_ocp_qp_ipm_get_ric_K(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *K); +// feedback control gain in the form u = K x + k +void d_ocp_qp_ipm_get_ric_k(struct d_ocp_qp *qp, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws, int stage, double *k); +// +void d_ocp_qp_init_var(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_ipm_abs_step(int kk, struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_ipm_delta_step(int kk, struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_ipm_solve(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_ipm_predict(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_ipm_sens(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); + + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_OCP_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_kkt.h new file mode 100644 index 0000000000..19dcec32ca --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_kkt.h @@ -0,0 +1,66 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_KKT_H_ +#define HPIPM_D_OCP_QP_KKT_H_ + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + +// +void d_ocp_qp_fact_solve_kkt_unconstr(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_fact_solve_kkt_step(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_fact_lq_solve_kkt_step(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); +// +void d_ocp_qp_solve_kkt_step(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_ipm_arg *arg, struct d_ocp_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif // HPIPM_D_OCP_QP_KKT_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_red.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_red.h new file mode 100644 index 0000000000..f0482b0444 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_red.h @@ -0,0 +1,117 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_RED_H_ +#define HPIPM_D_OCP_QP_RED_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp_reduce_eq_dof_arg + { + double lam_min; + double t_min; + int alias_unchanged; // do not keep copy unchanged stage + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution inequality constr (lam t) + hpipm_size_t memsize; // memory size in bytes + }; + + + +struct d_ocp_qp_reduce_eq_dof_ws + { + struct blasfeo_dvec *tmp_nuxM; + struct blasfeo_dvec *tmp_nbgM; + int *e_imask_ux; + int *e_imask_d; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +void d_ocp_qp_dim_reduce_eq_dof(struct d_ocp_qp_dim *dim, struct d_ocp_qp_dim *dim_red); +// +hpipm_size_t d_ocp_qp_reduce_eq_dof_arg_memsize(); +// +void d_ocp_qp_reduce_eq_dof_arg_create(struct d_ocp_qp_reduce_eq_dof_arg *arg, void *mem); +// +void d_ocp_qp_reduce_eq_dof_arg_set_default(struct d_ocp_qp_reduce_eq_dof_arg *arg); +// +void d_ocp_qp_reduce_eq_dof_arg_set_alias_unchanged(struct d_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void d_ocp_qp_reduce_eq_dof_arg_set_comp_prim_sol(struct d_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void d_ocp_qp_reduce_eq_dof_arg_set_comp_dual_sol_eq(struct d_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void d_ocp_qp_reduce_eq_dof_arg_set_comp_dual_sol_ineq(struct d_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +hpipm_size_t d_ocp_qp_reduce_eq_dof_ws_memsize(struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_reduce_eq_dof_ws_create(struct d_ocp_qp_dim *dim, struct d_ocp_qp_reduce_eq_dof_ws *work, void *mem); +// +void d_ocp_qp_reduce_eq_dof(struct d_ocp_qp *qp, struct d_ocp_qp *qp_red, struct d_ocp_qp_reduce_eq_dof_arg *arg, struct d_ocp_qp_reduce_eq_dof_ws *work); +// +void d_ocp_qp_reduce_eq_dof_lhs(struct d_ocp_qp *qp, struct d_ocp_qp *qp_red, struct d_ocp_qp_reduce_eq_dof_arg *arg, struct d_ocp_qp_reduce_eq_dof_ws *work); +// +void d_ocp_qp_reduce_eq_dof_rhs(struct d_ocp_qp *qp, struct d_ocp_qp *qp_red, struct d_ocp_qp_reduce_eq_dof_arg *arg, struct d_ocp_qp_reduce_eq_dof_ws *work); +// +void d_ocp_qp_restore_eq_dof(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol_red, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_reduce_eq_dof_arg *arg, struct d_ocp_qp_reduce_eq_dof_ws *work); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_RED_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_res.h new file mode 100644 index 0000000000..061c5729cd --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_res.h @@ -0,0 +1,112 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_RES_H_ +#define HPIPM_D_OCP_QP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp_res + { + struct d_ocp_qp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // max of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_ocp_qp_res_ws + { + struct blasfeo_dvec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_ocp_qp_res_memsize(struct d_ocp_qp_dim *ocp_dim); +// +void d_ocp_qp_res_create(struct d_ocp_qp_dim *ocp_dim, struct d_ocp_qp_res *res, void *mem); +// +hpipm_size_t d_ocp_qp_res_ws_memsize(struct d_ocp_qp_dim *ocp_dim); +// +void d_ocp_qp_res_ws_create(struct d_ocp_qp_dim *ocp_dim, struct d_ocp_qp_res_ws *workspace, void *mem); +// +void d_ocp_qp_res_compute(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_res *res, struct d_ocp_qp_res_ws *ws); +// +void d_ocp_qp_res_compute_lin(struct d_ocp_qp *qp, struct d_ocp_qp_sol *qp_sol, struct d_ocp_qp_sol *qp_step, struct d_ocp_qp_res *res, struct d_ocp_qp_res_ws *ws); +// +void d_ocp_qp_res_compute_inf_norm(struct d_ocp_qp_res *res); +// +void d_ocp_qp_res_get_all(struct d_ocp_qp_res *res, double **res_r, double **res_q, double **res_ls, double **res_us, double **res_b, double **res_d_lb, double **res_d_ub, double **res_d_lg, double **res_d_ug, double **res_d_ls, double **res_d_us, double **res_m_lb, double **res_m_ub, double **res_m_lg, double **res_m_ug, double **res_m_ls, double **res_m_us); +// +void d_ocp_qp_res_get_max_res_stat(struct d_ocp_qp_res *res, double *value); +// +void d_ocp_qp_res_get_max_res_eq(struct d_ocp_qp_res *res, double *value); +// +void d_ocp_qp_res_get_max_res_ineq(struct d_ocp_qp_res *res, double *value); +// +void d_ocp_qp_res_get_max_res_comp(struct d_ocp_qp_res *res, double *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_OCP_QP_RES_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_sol.h new file mode 100644 index 0000000000..eb91f3a7b1 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_sol.h @@ -0,0 +1,128 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_SOL_H_ +#define HPIPM_D_OCP_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_ocp_qp_sol + { + struct d_ocp_qp_dim *dim; + struct blasfeo_dvec *ux; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_ocp_qp_sol_strsize(); +// +hpipm_size_t d_ocp_qp_sol_memsize(struct d_ocp_qp_dim *dim); +// +void d_ocp_qp_sol_create(struct d_ocp_qp_dim *dim, struct d_ocp_qp_sol *qp_sol, void *memory); +// +void d_ocp_qp_sol_copy_all(struct d_ocp_qp_sol *qp_sol_orig, struct d_ocp_qp_sol *qp_sol_dest); +// +void d_ocp_qp_sol_get_all(struct d_ocp_qp_sol *qp_sol, double **u, double **x, double **ls, double **us, double **pi, double **lam_lb, double **lam_ub, double **lam_lg, double **lam_ug, double **lam_ls, double **lam_us); +// +void d_ocp_qp_sol_get_all_rowmaj(struct d_ocp_qp_sol *qp_sol, double **u, double **x, double **ls, double **us, double **pi, double **lam_lb, double **lam_ub, double **lam_lg, double **lam_ug, double **lam_ls, double **lam_us); +// +void d_ocp_qp_sol_set_all(double **u, double **x, double **ls, double **us, double **pi, double **lam_lb, double **lam_ub, double **lam_lg, double **lam_ug, double **lam_ls, double **lam_us, struct d_ocp_qp_sol *qp_sol); +// +void d_ocp_qp_sol_get(char *field, int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_u(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_x(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_sl(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_su(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_pi(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_lb(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_lbu(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_lbx(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_ub(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_ubu(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_ubx(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_lg(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_get_lam_ug(int stage, struct d_ocp_qp_sol *qp_sol, double *vec); +// +void d_ocp_qp_sol_set(char *field, int stage, double *vec, struct d_ocp_qp_sol *qp_sol); +// +void d_ocp_qp_sol_set_u(int stage, double *vec, struct d_ocp_qp_sol *qp_sol); +// +void d_ocp_qp_sol_set_x(int stage, double *vec, struct d_ocp_qp_sol *qp_sol); +// +void d_ocp_qp_sol_set_sl(int stage, double *vec, struct d_ocp_qp_sol *qp_sol); +// +void d_ocp_qp_sol_set_su(int stage, double *vec, struct d_ocp_qp_sol *qp_sol); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_utils.h new file mode 100644 index 0000000000..217e9d94ae --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_ocp_qp_utils.h @@ -0,0 +1,82 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_OCP_QP_UTILS_H_ +#define HPIPM_D_OCP_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_ocp_qp_dim.h" +#include "hpipm_d_ocp_qp.h" +#include "hpipm_d_ocp_qp_sol.h" +#include "hpipm_d_ocp_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_ocp_qp_dim_print(struct d_ocp_qp_dim *qp_dim); +// +void d_ocp_qp_dim_codegen(char *file_name, char *mode, struct d_ocp_qp_dim *qp_dim); +// +void d_ocp_qp_print(struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp *qp); +// +void d_ocp_qp_codegen(char *file_name, char *mode, struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp *qp); +// +void d_ocp_qp_sol_print(struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp_sol *ocp_qp_sol); +// +void d_ocp_qp_ipm_arg_print(struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp_ipm_arg *arg); +// +void d_ocp_qp_ipm_arg_codegen(char *file_name, char *mode, struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp_ipm_arg *arg); +// +void d_ocp_qp_res_print(struct d_ocp_qp_dim *qp_dim, struct d_ocp_qp_res *ocp_qp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_UTILS_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond.h b/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond.h new file mode 100644 index 0000000000..1c5f785035 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond.h @@ -0,0 +1,115 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_PART_COND_H_ +#define HPIPM_D_PART_COND_H_ + + + +#include +#include + +#include "hpipm_common.h" +#include "hpipm_d_cond.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_part_cond_qp_arg + { + struct d_cond_qp_arg *cond_arg; + int N2; + hpipm_size_t memsize; + }; + + + +struct d_part_cond_qp_ws + { + struct d_cond_qp_ws *cond_workspace; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_part_cond_qp_arg_memsize(int N2); +// +void d_part_cond_qp_arg_create(int N2, struct d_part_cond_qp_arg *cond_arg, void *mem); +// +void d_part_cond_qp_arg_set_default(struct d_part_cond_qp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 squre-root +void d_part_cond_qp_arg_set_ric_alg(int ric_alg, struct d_part_cond_qp_arg *cond_arg); +// +void d_part_cond_qp_arg_set_comp_prim_sol(int value, struct d_part_cond_qp_arg *cond_arg); +// +void d_part_cond_qp_arg_set_comp_dual_sol_eq(int value, struct d_part_cond_qp_arg *cond_arg); +// +void d_part_cond_qp_arg_set_comp_dual_sol_ineq(int value, struct d_part_cond_qp_arg *cond_arg); + +// +void d_part_cond_qp_compute_block_size(int N, int N2, int *block_size); +// +void d_part_cond_qp_compute_dim(struct d_ocp_qp_dim *ocp_dim, int *block_size, struct d_ocp_qp_dim *part_dense_dim); +// +hpipm_size_t d_part_cond_qp_ws_memsize(struct d_ocp_qp_dim *ocp_dim, int *block_size, struct d_ocp_qp_dim *part_dense_dim, struct d_part_cond_qp_arg *cond_arg); +// +void d_part_cond_qp_ws_create(struct d_ocp_qp_dim *ocp_dim, int *block_size, struct d_ocp_qp_dim *part_dense_dim, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws, void *mem); +// +void d_part_cond_qp_cond(struct d_ocp_qp *ocp_qp, struct d_ocp_qp *part_dense_qp, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws); +// +void d_part_cond_qp_cond_lhs(struct d_ocp_qp *ocp_qp, struct d_ocp_qp *part_dense_qp, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws); +// +void d_part_cond_qp_cond_rhs(struct d_ocp_qp *ocp_qp, struct d_ocp_qp *part_dense_qp, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws); +// +void d_part_cond_qp_expand_sol(struct d_ocp_qp *ocp_qp, struct d_ocp_qp *part_dense_qp, struct d_ocp_qp_sol *part_dense_qp_sol, struct d_ocp_qp_sol *ocp_qp_sol, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws); + +// +void d_part_cond_qp_update(int *idxc, struct d_ocp_qp *ocp_qp, struct d_ocp_qp *part_dense_qp, struct d_part_cond_qp_arg *cond_arg, struct d_part_cond_qp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_PART_COND_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond_qcqp.h new file mode 100644 index 0000000000..2d6624e5bb --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_part_cond_qcqp.h @@ -0,0 +1,106 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_PART_COND_QCQP_H_ +#define HPIPM_D_PART_COND_QCQP_H_ + + + +#include +#include + +#include "hpipm_d_cond_qcqp.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_part_cond_qcqp_arg + { + struct d_cond_qcqp_arg *cond_arg; + int N2; + hpipm_size_t memsize; + }; + + + +struct d_part_cond_qcqp_ws + { + struct d_cond_qcqp_ws *cond_ws; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_part_cond_qcqp_arg_memsize(int N2); +// +void d_part_cond_qcqp_arg_create(int N2, struct d_part_cond_qcqp_arg *cond_arg, void *mem); +// +void d_part_cond_qcqp_arg_set_default(struct d_part_cond_qcqp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 squre-root +void d_part_cond_qcqp_arg_set_ric_alg(int ric_alg, struct d_part_cond_qcqp_arg *cond_arg); + +// +void d_part_cond_qcqp_compute_block_size(int N, int N2, int *block_size); +// +void d_part_cond_qcqp_compute_dim(struct d_ocp_qcqp_dim *ocp_dim, int *block_size, struct d_ocp_qcqp_dim *part_dense_dim); +// +hpipm_size_t d_part_cond_qcqp_ws_memsize(struct d_ocp_qcqp_dim *ocp_dim, int *block_size, struct d_ocp_qcqp_dim *part_dense_dim, struct d_part_cond_qcqp_arg *cond_arg); +// +void d_part_cond_qcqp_ws_create(struct d_ocp_qcqp_dim *ocp_dim, int *block_size, struct d_ocp_qcqp_dim *part_dense_dim, struct d_part_cond_qcqp_arg *cond_arg, struct d_part_cond_qcqp_ws *cond_ws, void *mem); +// +void d_part_cond_qcqp_cond(struct d_ocp_qcqp *ocp_qp, struct d_ocp_qcqp *part_dense_qp, struct d_part_cond_qcqp_arg *cond_arg, struct d_part_cond_qcqp_ws *cond_ws); +// +void d_part_cond_qcqp_cond_lhs(struct d_ocp_qcqp *ocp_qp, struct d_ocp_qcqp *part_dense_qp, struct d_part_cond_qcqp_arg *cond_arg, struct d_part_cond_qcqp_ws *cond_ws); +// +void d_part_cond_qcqp_cond_rhs(struct d_ocp_qcqp *ocp_qp, struct d_ocp_qcqp *part_dense_qp, struct d_part_cond_qcqp_arg *cond_arg, struct d_part_cond_qcqp_ws *cond_ws); +// +void d_part_cond_qcqp_expand_sol(struct d_ocp_qcqp *ocp_qp, struct d_ocp_qcqp *part_dense_qp, struct d_ocp_qcqp_sol *part_dense_qp_sol, struct d_ocp_qcqp_sol *ocp_qp_sol, struct d_part_cond_qcqp_arg *cond_arg, struct d_part_cond_qcqp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_PART_COND_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_sim_erk.h b/phonelibs/acados/include/hpipm/include/hpipm_d_sim_erk.h new file mode 100644 index 0000000000..320b66bb28 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_sim_erk.h @@ -0,0 +1,122 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_SIM_ERK_H_ +#define HPIPM_D_SIM_ERK_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct d_sim_erk_arg + { + struct d_sim_rk_data *rk_data; // integrator data + double h; // step size + int steps; // number of steps +// int for_sens; // compute adjoint sensitivities +// int adj_sens; // compute adjoint sensitivities + hpipm_size_t memsize; + }; + + + +struct d_sim_erk_ws + { + void (*ode)(int t, double *x, double *p, void *ode_args, double *xdot); // function pointer to ode + void (*vde_for)(int t, double *x, double *p, void *ode_args, double *xdot); // function pointer to forward vde + void (*vde_adj)(int t, double *adj_in, void *ode_args, double *adj_out); // function pointer to adjoint vde + void *ode_args; // pointer to ode args + struct d_sim_erk_arg *erk_arg; // erk arg + double *K; // internal variables + double *x_for; // states and forward sensitivities + double *x_traj; // states at all steps + double *l; // adjoint sensitivities + double *p; // parameter + double *x_tmp; // temporary states and forward sensitivities + double *adj_in; + double *adj_tmp; + int nx; // number of states + int np; // number of parameters + int nf; // number of forward sensitivities + int na; // number of adjoint sensitivities + int nf_max; // max number of forward sensitivities + int na_max; // max number of adjoint sensitivities + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_sim_erk_arg_memsize(); +// +void d_sim_erk_arg_create(struct d_sim_erk_arg *erk_arg, void *mem); +// +void d_sim_erk_arg_set_all(struct d_sim_rk_data *rk_data, double h, int steps, struct d_sim_erk_arg *erk_arg); + +// +hpipm_size_t d_sim_erk_ws_memsize(struct d_sim_erk_arg *erk_arg, int nx, int np, int nf_max, int na_max); +// +void d_sim_erk_ws_create(struct d_sim_erk_arg *erk_arg, int nx, int np, int nf_max, int na_max, struct d_sim_erk_ws *work, void *memory); +// +void d_sim_erk_ws_set_all(int nf, int na, double *x, double *fs, double *bs, double *p, void (*ode)(int t, double *x, double *p, void *ode_args, double *xdot), void (*vde_for)(int t, double *x, double *p, void *ode_args, double *xdot), void (*vde_adj)(int t, double *adj_in, void *ode_args, double *adj_out), void *ode_args, struct d_sim_erk_ws *work); +// number of directions for forward sensitivities +void d_sim_erk_ws_set_nf(int *nf, struct d_sim_erk_ws *work); +// parameters (e.g. inputs) +void d_sim_erk_ws_set_p(double *p, struct d_sim_erk_ws *work); +// state +void d_sim_erk_ws_set_x(double *x, struct d_sim_erk_ws *work); +// forward sensitivities +void d_sim_erk_ws_set_fs(double *fs, struct d_sim_erk_ws *work); +// ode funtion +void d_sim_erk_ws_set_ode(void (*ode)(int t, double *x, double *p, void *ode_args, double *xdot), struct d_sim_erk_ws *work); +// forward vde function +void d_sim_erk_ws_set_vde_for(void (*ode)(int t, double *x, double *p, void *ode_args, double *xdot), struct d_sim_erk_ws *work); +// ode_args, passed straight to the ode/vde_for/vde_adj functions +void d_sim_erk_ws_set_ode_args(void *ode_args, struct d_sim_erk_ws *work); +// state +void d_sim_erk_ws_get_x(struct d_sim_erk_ws *work, double *x); +// forward sensitivities +void d_sim_erk_ws_get_fs(struct d_sim_erk_ws *work, double *fs); +// +void d_sim_erk_solve(struct d_sim_erk_arg *arg, struct d_sim_erk_ws *work); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_D_SIM_ERK_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_sim_rk.h b/phonelibs/acados/include/hpipm/include/hpipm_d_sim_rk.h new file mode 100644 index 0000000000..a50dcbb491 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_sim_rk.h @@ -0,0 +1,71 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_SIM_RK_H_ +#define HPIPM_D_SIM_RK_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct d_sim_rk_data + { + double *A_rk; // A in butcher tableau + double *B_rk; // b in butcher tableau + double *C_rk; // c in butcher tableau + int expl; // erk vs irk + int ns; // number of stages + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_sim_rk_data_memsize(int ns); +// +void d_sim_rk_data_create(int ns, struct d_sim_rk_data *rk_data, void *memory); +// +void d_sim_rk_data_init_default(char *field, struct d_sim_rk_data *rk_data); +// +void d_sim_rk_data_set_all(int expl, double *A_rk, double *B_rk, double *C_rk, struct d_sim_rk_data *rk_data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_D_SIM_RK_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp.h new file mode 100644 index 0000000000..f36b51a064 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp.h @@ -0,0 +1,213 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QCQP_H_ +#define HPIPM_D_TREE_OCP_QCQP_H_ + + + +#include +#include + +#include "hpipm_common.h" +#include "hpipm_d_tree_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qcqp + { + struct d_tree_ocp_qcqp_dim *dim; + struct blasfeo_dmat *BAbt; // Nn-1 + struct blasfeo_dmat *RSQrq; // Nn + struct blasfeo_dmat *DCt; // Nn + struct blasfeo_dmat **Hq; // Nn + struct blasfeo_dvec *b; // Nn-1 + struct blasfeo_dvec *rqz; // Nn + struct blasfeo_dvec *d; // Nn + struct blasfeo_dvec *d_mask; // Nn + struct blasfeo_dvec *m; // Nn + struct blasfeo_dvec *Z; // Nn + int **idxb; // indices of box constrained variables within [u; x] // Nn + int **idxs_rev; // index of soft constraints + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_tree_ocp_qcqp_memsize(struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_create(struct d_tree_ocp_qcqp_dim *dim, struct d_tree_ocp_qcqp *qp, void *memory); +// +void d_tree_ocp_qcqp_set_all(double **A, double **B, double **b, double **Q, double **S, double **R, double **q, double **r, int **idxb, double **d_lb, double **d_ub, double **C, double **D, double **d_lg, double **d_ug, double **Zl, double **Zu, double **zl, double **zu, int **idxs, double **d_ls, double **d_us, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set(char *field_name, int node_edge, void *value, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_A(int edge, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_B(int edge, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_b(int edge, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Q(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_S(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_R(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_q(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_r(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lb(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lb_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ub(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ub_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lbx(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lbx_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ubx(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ubx_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lbu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lbu_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ubu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ubu_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_idxb(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_idxbx(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jbx(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_idxbu(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jbu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_C(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_D(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lg(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lg_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ug(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_ug_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Qq(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Sq(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Rq(int node, double *mat, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_qq(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_rq(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_uq(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_uq_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Zl(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Zu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_zl(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_zu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lls(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lls_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lus(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_lus_mask(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_idxs(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_idxs_rev(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jsbu(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jsbx(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jsg(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_set_Jsq(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_idxe(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_idxbxe(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_idxbue(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_idxge(int node, int *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_Jbxe(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_Jbue(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_Jge(int node, double *vec, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_set_diag_H_flag(int node, int *value, struct d_tree_ocp_qcqp *qp); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_TREE_OCP_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_dim.h new file mode 100644 index 0000000000..257b82b252 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_dim.h @@ -0,0 +1,117 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QCQP_DIM_H_ +#define HPIPM_D_TREE_OCP_QCQP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qcqp_dim + { + struct d_tree_ocp_qp_dim *qp_dim; // dim of qp approximation + struct tree *ttree; // tree describing node conndection + int *nx; // number of states // Nn + int *nu; // number of inputs // Nn + int *nb; // number of box constraints // Nn + int *nbx; // number of state box constraints // Nn + int *nbu; // number of input box constraints // Nn + int *ng; // number of general constraints // Nn + int *nq; // number of (upper) quadratic constraints + int *ns; // number of soft constraints // Nn + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int *nsq; // number of (upper) soft quadratic constraints + int Nn; // number of nodes + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qcqp_dim_strsize(); +// +hpipm_size_t d_tree_ocp_qcqp_dim_memsize(int Nn); +// +void d_tree_ocp_qcqp_dim_create(int Nn, struct d_tree_ocp_qcqp_dim *qp_dim, void *memory); +// +void d_tree_ocp_qcqp_dim_set_tree(struct tree *ttree, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set(char *field, int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nx(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nu(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nbx(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nbu(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_ng(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nq(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_ns(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nsbx(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nsbu(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nsg(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_dim_set_nsq(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +//void d_tree_ocp_qcqp_dim_set_nbxe(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +//void d_tree_ocp_qcqp_dim_set_nbue(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); +// +//void d_tree_ocp_qcqp_dim_set_nge(int stage, int value, struct d_tree_ocp_qcqp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_TREE_OCP_QCQP_DIM_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_ipm.h new file mode 100644 index 0000000000..4a0fed1067 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_ipm.h @@ -0,0 +1,191 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QCQP_IPM_H_ +#define HPIPM_D_TREE_OCP_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qcqp_ipm_arg + { + struct d_tree_ocp_qp_ipm_arg *qp_arg; + double mu0; // initial value for complementarity slackness + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol +// int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constrains (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) +// int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_tree_ocp_qcqp_ipm_ws + { + struct d_tree_ocp_qp_ipm_ws *qp_ws; + struct d_tree_ocp_qp *qp; + struct d_tree_ocp_qp_sol *qp_sol; + struct d_tree_ocp_qcqp_res_ws *qcqp_res_ws; + struct d_tree_ocp_qcqp_res *qcqp_res; + struct blasfeo_dvec *tmp_nuxM; + int iter; // iteration number + int status; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qcqp_ipm_arg_strsize(); +// +hpipm_size_t d_tree_ocp_qcqp_ipm_arg_memsize(struct d_tree_ocp_qcqp_dim *ocp_dim); +// +void d_tree_ocp_qcqp_ipm_arg_create(struct d_tree_ocp_qcqp_dim *ocp_dim, struct d_tree_ocp_qcqp_ipm_arg *arg, void *mem); +// +void d_tree_ocp_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct d_tree_ocp_qcqp_ipm_arg *arg); +// +void d_tree_ocp_qcqp_ipm_arg_set(char *field, void *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set maximum number of iterations +void d_tree_ocp_qcqp_ipm_arg_set_iter_max(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set minimum step lenght +void d_tree_ocp_qcqp_ipm_arg_set_alpha_min(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set initial value of barrier parameter +void d_tree_ocp_qcqp_ipm_arg_set_mu0(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void d_tree_ocp_qcqp_ipm_arg_set_tol_stat(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on equality constr +void d_tree_ocp_qcqp_ipm_arg_set_tol_eq(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on inequality constr +void d_tree_ocp_qcqp_ipm_arg_set_tol_ineq(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void d_tree_ocp_qcqp_ipm_arg_set_tol_comp(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set regularization of primal variables +void d_tree_ocp_qcqp_ipm_arg_set_reg_prim(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void d_tree_ocp_qcqp_ipm_arg_set_warm_start(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void d_tree_ocp_qcqp_ipm_arg_set_pred_corr(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void d_tree_ocp_qcqp_ipm_arg_set_cond_pred_corr(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +//void d_tree_ocp_qcqp_ipm_arg_set_ric_alg(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// compute residuals after solution +void d_tree_ocp_qcqp_ipm_arg_set_comp_res_exit(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// compute residuals of prediction +//void d_tree_ocp_qcqp_ipm_arg_set_comp_res_pred(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// min value of lam in the solution +void d_tree_ocp_qcqp_ipm_arg_set_lam_min(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// min value of t in the solution +void d_tree_ocp_qcqp_ipm_arg_set_t_min(double *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// use different step for primal and dual variables +void d_tree_ocp_qcqp_ipm_arg_set_split_step(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void d_tree_ocp_qcqp_ipm_arg_set_t_lam_min(int *value, struct d_tree_ocp_qcqp_ipm_arg *arg); + +// +hpipm_size_t d_tree_ocp_qcqp_ipm_ws_strsize(); +// +hpipm_size_t d_tree_ocp_qcqp_ipm_ws_memsize(struct d_tree_ocp_qcqp_dim *ocp_dim, struct d_tree_ocp_qcqp_ipm_arg *arg); +// +void d_tree_ocp_qcqp_ipm_ws_create(struct d_tree_ocp_qcqp_dim *ocp_dim, struct d_tree_ocp_qcqp_ipm_arg *arg, struct d_tree_ocp_qcqp_ipm_ws *ws, void *mem); +// +void d_tree_ocp_qcqp_ipm_get(char *field, struct d_tree_ocp_qcqp_ipm_ws *ws, void *value); +// +void d_tree_ocp_qcqp_ipm_get_status(struct d_tree_ocp_qcqp_ipm_ws *ws, int *status); +// +void d_tree_ocp_qcqp_ipm_get_iter(struct d_tree_ocp_qcqp_ipm_ws *ws, int *iter); +// +void d_tree_ocp_qcqp_ipm_get_max_res_stat(struct d_tree_ocp_qcqp_ipm_ws *ws, double *res_stat); +// +void d_tree_ocp_qcqp_ipm_get_max_res_eq(struct d_tree_ocp_qcqp_ipm_ws *ws, double *res_eq); +// +void d_tree_ocp_qcqp_ipm_get_max_res_ineq(struct d_tree_ocp_qcqp_ipm_ws *ws, double *res_ineq); +// +void d_tree_ocp_qcqp_ipm_get_max_res_comp(struct d_tree_ocp_qcqp_ipm_ws *ws, double *res_comp); +// +void d_tree_ocp_qcqp_ipm_get_stat(struct d_tree_ocp_qcqp_ipm_ws *ws, double **stat); +// +void d_tree_ocp_qcqp_ipm_get_stat_m(struct d_tree_ocp_qcqp_ipm_ws *ws, int *stat_m); +// +void d_tree_ocp_qcqp_init_var(struct d_tree_ocp_qcqp *qp, struct d_tree_ocp_qcqp_sol *qp_sol, struct d_tree_ocp_qcqp_ipm_arg *arg, struct d_tree_ocp_qcqp_ipm_ws *ws); +// +void d_tree_ocp_qcqp_ipm_solve(struct d_tree_ocp_qcqp *qp, struct d_tree_ocp_qcqp_sol *qp_sol, struct d_tree_ocp_qcqp_ipm_arg *arg, struct d_tree_ocp_qcqp_ipm_ws *ws); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_TREE_OCP_QCQP_IPM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_res.h new file mode 100644 index 0000000000..69eebb1ba4 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_res.h @@ -0,0 +1,108 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QCQP_RES_H_ +#define HPIPM_D_TREE_OCP_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qcqp_res + { + struct d_tree_ocp_qcqp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // max of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_tree_ocp_qcqp_res_ws + { + struct blasfeo_dvec *tmp_nuxM; // work space of size nuM+nxM + struct blasfeo_dvec *tmp_nbgqM; // work space of size nbM+ngM+nqM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + struct blasfeo_dvec *q_fun; // value for evaluation of quadr constr + struct blasfeo_dvec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qcqp_res_memsize(struct d_tree_ocp_qcqp_dim *ocp_dim); +// +void d_tree_ocp_qcqp_res_create(struct d_tree_ocp_qcqp_dim *ocp_dim, struct d_tree_ocp_qcqp_res *res, void *mem); +// +hpipm_size_t d_tree_ocp_qcqp_res_ws_memsize(struct d_tree_ocp_qcqp_dim *ocp_dim); +// +void d_tree_ocp_qcqp_res_ws_create(struct d_tree_ocp_qcqp_dim *ocp_dim, struct d_tree_ocp_qcqp_res_ws *ws, void *mem); +// +void d_tree_ocp_qcqp_res_compute(struct d_tree_ocp_qcqp *qp, struct d_tree_ocp_qcqp_sol *qp_sol, struct d_tree_ocp_qcqp_res *res, struct d_tree_ocp_qcqp_res_ws *ws); +// +void d_tree_ocp_qcqp_res_compute_inf_norm(struct d_tree_ocp_qcqp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_TREE_OCP_QCQP_RES_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_sol.h new file mode 100644 index 0000000000..cde8de5c51 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_sol.h @@ -0,0 +1,99 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QCQP_SOL_H_ +#define HPIPM_D_TREE_OCP_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_tree_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +struct d_tree_ocp_qcqp_sol + { + struct d_tree_ocp_qcqp_dim *dim; + struct blasfeo_dvec *ux; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_tree_ocp_qcqp_sol_memsize(struct d_tree_ocp_qcqp_dim *dim); +// +void d_tree_ocp_qcqp_sol_create(struct d_tree_ocp_qcqp_dim *dim, struct d_tree_ocp_qcqp_sol *qp_sol, void *memory); +// +void d_tree_ocp_qcqp_sol_get(char *field, int node_edge, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_u(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_x(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_sl(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_su(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_pi(int edge, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_lam_lb(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_lam_ub(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_lam_lg(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qcqp_sol_get_lam_ug(int node, struct d_tree_ocp_qcqp_sol *qp_sol, double *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_TREE_OCP_QCQP_SOL_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_utils.h new file mode 100644 index 0000000000..5ef04fcf66 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qcqp_utils.h @@ -0,0 +1,84 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_UTILS_H_ +#define HPIPM_D_TREE_OCP_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_tree_ocp_qcqp_dim.h" +#include "hpipm_d_tree_ocp_qcqp.h" +#include "hpipm_d_tree_ocp_qcqp_sol.h" +#include "hpipm_d_tree_ocp_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_tree_ocp_qcqp_dim_print(struct d_tree_ocp_qcqp_dim *qp_dim); +// +//void d_tree_ocp_qcqp_dim_codegen(char *file_name, char *mode, struct d_tree_ocp_qcqp_dim *qp_dim); +// +void d_tree_ocp_qcqp_print(struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp *qp); +// +//void d_tree_ocp_qcqp_codegen(char *file_name, char *mode, struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp *qp); +// +void d_tree_ocp_qcqp_sol_print(struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp_sol *ocp_qcqp_sol); +// +void d_tree_ocp_qcqp_ipm_arg_print(struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp_ipm_arg *arg); +// +//void d_tree_ocp_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp_ipm_arg *arg); +// +void d_tree_ocp_qcqp_res_print(struct d_tree_ocp_qcqp_dim *qp_dim, struct d_tree_ocp_qcqp_res *ocp_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_UTILS_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp.h new file mode 100644 index 0000000000..1666ad7c71 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp.h @@ -0,0 +1,195 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_H_ +#define HPIPM_D_TREE_OCP_QP_H_ + + + +#include +#include + +#include "hpipm_d_tree_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qp + { + struct d_tree_ocp_qp_dim *dim; + struct blasfeo_dmat *BAbt; // Nn-1 + struct blasfeo_dmat *RSQrq; // Nn + struct blasfeo_dmat *DCt; // Nn + struct blasfeo_dvec *b; // Nn-1 + struct blasfeo_dvec *rqz; // Nn + struct blasfeo_dvec *d; // Nn + struct blasfeo_dvec *d_mask; // Nn + struct blasfeo_dvec *m; // Nn + struct blasfeo_dvec *Z; // Nn + int **idxb; // indices of box constrained variables within [u; x] // Nn +// int **idxs; // index of soft constraints + int **idxs_rev; // index of soft constraints + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_tree_ocp_qp_memsize(struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_create(struct d_tree_ocp_qp_dim *dim, struct d_tree_ocp_qp *qp, void *memory); +// +void d_tree_ocp_qp_set_all(double **A, double **B, double **b, double **Q, double **S, double **R, double **q, double **r, int **idxb, double **d_lb, double **d_ub, double **C, double **D, double **d_lg, double **d_ug, double **Zl, double **Zu, double **zl, double **zu, int **idxs, double **d_ls, double **d_us, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set(char *field_name, int node_edge, void *value, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_A(int edge, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_B(int edge, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_b(int edge, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Q(int node, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_S(int node, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_R(int node, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_q(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_r(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lb(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lb_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ub(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ub_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lbx(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lbx_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ubx(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ubx_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lbu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lbu_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ubu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ubu_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_idxb(int node, int *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_idxbx(int node, int *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Jbx(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_idxbu(int node, int *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Jbu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_C(int node, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_D(int node, double *mat, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lg(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lg_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ug(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_ug_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Zl(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Zu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_zl(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_zu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lls(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lls_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lus(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_lus_mask(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_idxs(int node, int *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_idxs_rev(int node, int *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Jsbu(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Jsbx(int node, double *vec, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_set_Jsg(int node, double *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_idxe(int node, int *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_idxbxe(int node, int *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_idxbue(int node, int *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_idxge(int node, int *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_Jbxe(int node, double *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_Jbue(int node, double *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_Jge(int node, double *vec, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_set_diag_H_flag(int node, int *value, struct d_tree_ocp_qp *qp); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_dim.h new file mode 100644 index 0000000000..659b664594 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_dim.h @@ -0,0 +1,111 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_DIM_H_ +#define HPIPM_D_TREE_OCP_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qp_dim + { + struct tree *ttree; // tree describing node conndection + int *nx; // number of states // Nn + int *nu; // number of inputs // Nn + int *nb; // number of box constraints // Nn + int *nbx; // number of state box constraints // Nn + int *nbu; // number of input box constraints // Nn + int *ng; // number of general constraints // Nn + int *ns; // number of soft constraints // Nn + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int Nn; // number of nodes + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qp_dim_strsize(); +// +hpipm_size_t d_tree_ocp_qp_dim_memsize(int Nn); +// +void d_tree_ocp_qp_dim_create(int Nn, struct d_tree_ocp_qp_dim *qp_dim, void *memory); +// +void d_tree_ocp_qp_dim_set_all(struct tree *ttree, int *nx, int *nu, int *nbx, int *nbu, int *ng, int *nsbx, int *nsbu, int *nsg, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_tree(struct tree *ttree, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set(char *field, int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nx(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nu(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nbx(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nbu(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_ng(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_ns(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nsbx(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nsbu(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nsg(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nbxe(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nbue(int stage, int value, struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_dim_set_nge(int stage, int value, struct d_tree_ocp_qp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_ipm.h new file mode 100644 index 0000000000..fb634208ce --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_ipm.h @@ -0,0 +1,209 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_D_TREE_OCP_QP_IPM_H_ +#define HPIPM_D_TREE_OCP_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qp_ipm_arg + { + double mu0; // initial value for duality measure + double alpha_min; // exit cond on step length + double res_g_max; // exit cond on inf norm of residuals + double res_b_max; // exit cond on inf norm of residuals + double res_d_max; // exit cond on inf norm of residuals + double res_m_max; // exit cond on inf norm of residuals + double reg_prim; // reg of primal hessian + double lam_min; // min value in lam vector + double t_min; // min value in t vector + double tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol==1) + int split_step; // use different steps for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct d_tree_ocp_qp_ipm_ws + { + struct d_core_qp_ipm_workspace *core_workspace; + struct d_tree_ocp_qp_res_ws *res_workspace; + struct d_tree_ocp_qp_sol *sol_step; + struct d_tree_ocp_qp_sol *sol_itref; + struct d_tree_ocp_qp *qp_step; + struct d_tree_ocp_qp *qp_itref; + struct d_tree_ocp_qp_res *res_itref; + struct d_tree_ocp_qp_res *res; + struct blasfeo_dvec *Gamma; // hessian update + struct blasfeo_dvec *gamma; // hessian update + struct blasfeo_dvec *tmp_nxM; // work space of size nxM + struct blasfeo_dvec *tmp_nbgM; // work space of size nbgM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + struct blasfeo_dvec *Pb; // Pb + struct blasfeo_dvec *Zs_inv; + struct blasfeo_dmat *L; + struct blasfeo_dmat *Lh; + struct blasfeo_dmat *AL; + struct blasfeo_dmat *lq0; + struct blasfeo_dvec *tmp_m; + double *stat; // convergence statistics + int *use_hess_fact; + void *lq_work0; + double qp_res[4]; // infinity norm of residuals + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // number of recorded stat per IPM iter + int use_Pb; + int status; // solver status + int lq_fact; // cache from arg + int mask_constr; // use constr mask + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qp_ipm_arg_memsize(struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_ipm_arg_create(struct d_tree_ocp_qp_dim *dim, struct d_tree_ocp_qp_ipm_arg *arg, void *mem); +// +void d_tree_ocp_qp_ipm_arg_set_default(enum hpipm_mode mode, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_iter_max(int *iter_max, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_alpha_min(double *alpha_min, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_mu0(double *mu0, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_tol_stat(double *tol_stat, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_tol_eq(double *tol_eq, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_tol_ineq(double *tol_ineq, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_tol_comp(double *tol_comp, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_reg_prim(double *reg, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_warm_start(int *warm_start, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_pred_corr(int *pred_corr, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_cond_pred_corr(int *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_comp_dual_sol_eq(int *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_comp_res_exit(int *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_lam_min(double *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_t_min(double *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_tau_min(double *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_split_step(int *value, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_arg_set_t_lam_min(int *value, struct d_tree_ocp_qp_ipm_arg *arg); + +// +hpipm_size_t d_tree_ocp_qp_ipm_ws_memsize(struct d_tree_ocp_qp_dim *dim, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_ipm_ws_create(struct d_tree_ocp_qp_dim *dim, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws, void *mem); +// +void d_tree_ocp_qp_ipm_get_status(struct d_tree_ocp_qp_ipm_ws *ws, int *status); +// +void d_tree_ocp_qp_ipm_get_iter(struct d_tree_ocp_qp_ipm_ws *ws, int *iter); +// +void d_tree_ocp_qp_ipm_get_max_res_stat(struct d_tree_ocp_qp_ipm_ws *ws, double *res_stat); +// +void d_tree_ocp_qp_ipm_get_max_res_eq(struct d_tree_ocp_qp_ipm_ws *ws, double *res_eq); +// +void d_tree_ocp_qp_ipm_get_max_res_ineq(struct d_tree_ocp_qp_ipm_ws *ws, double *res_ineq); +// +void d_tree_ocp_qp_ipm_get_max_res_comp(struct d_tree_ocp_qp_ipm_ws *ws, double *res_comp); +// +void d_tree_ocp_qp_ipm_get_stat(struct d_tree_ocp_qp_ipm_ws *ws, double **stat); +// +void d_tree_ocp_qp_ipm_get_stat_m(struct d_tree_ocp_qp_ipm_ws *ws, int *stat_m); +// +void d_tree_ocp_qp_init_var(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_ipm_abs_step(int kk, struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_ipm_delta_step(int kk, struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_ipm_solve(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_kkt.h new file mode 100644 index 0000000000..4afd52fe4b --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_kkt.h @@ -0,0 +1,52 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + +// +void d_tree_ocp_qp_fact_solve_kkt_unconstr(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_fact_solve_kkt_step(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_fact_lq_solve_kkt_step(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); +// +void d_tree_ocp_qp_solve_kkt_step(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_ipm_arg *arg, struct d_tree_ocp_qp_ipm_ws *ws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_res.h new file mode 100644 index 0000000000..fe499080ea --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_res.h @@ -0,0 +1,106 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_RES_H_ +#define HPIPM_D_TREE_OCP_QP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct d_tree_ocp_qp_res + { + struct d_tree_ocp_qp_dim *dim; + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + double res_max[4]; // max of residuals + double res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct d_tree_ocp_qp_res_ws + { + struct blasfeo_dvec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_dvec *tmp_nsM; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t d_tree_ocp_qp_res_memsize(struct d_tree_ocp_qp_dim *ocp_dim); +// +void d_tree_ocp_qp_res_create(struct d_tree_ocp_qp_dim *ocp_dim, struct d_tree_ocp_qp_res *res, void *mem); +// +hpipm_size_t d_tree_ocp_qp_res_ws_memsize(struct d_tree_ocp_qp_dim *ocp_dim); +// +void d_tree_ocp_qp_res_ws_create(struct d_tree_ocp_qp_dim *ocp_dim, struct d_tree_ocp_qp_res_ws *ws, void *mem); +// +void d_tree_ocp_qp_res_get_all(struct d_tree_ocp_qp_res *res, double **res_r, double **res_q, double **res_ls, double **res_us, double **res_b, double **res_d_lb, double **res_d_ub, double **res_d_lg, double **res_d_ug, double **res_d_ls, double **res_d_us, double **res_m_lb, double **res_m_ub, double **res_m_lg, double **res_m_ug, double **res_m_ls, double **res_m_us); +// +void d_tree_ocp_qp_res_compute(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_res *res, struct d_tree_ocp_qp_res_ws *ws); +// +void d_tree_ocp_qp_res_compute_lin(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, struct d_tree_ocp_qp_sol *qp_step, struct d_tree_ocp_qp_res *res, struct d_tree_ocp_qp_res_ws *ws); +// +void d_tree_ocp_qp_res_compute_inf_norm(struct d_tree_ocp_qp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_TREE_OCP_QP_RES_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_sol.h new file mode 100644 index 0000000000..343d5e8ca6 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_sol.h @@ -0,0 +1,100 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_SOL_H_ +#define HPIPM_D_TREE_OCP_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_d_tree_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +struct d_tree_ocp_qp_sol + { + struct d_tree_ocp_qp_dim *dim; + struct blasfeo_dvec *ux; + struct blasfeo_dvec *pi; + struct blasfeo_dvec *lam; + struct blasfeo_dvec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t d_tree_ocp_qp_sol_memsize(struct d_tree_ocp_qp_dim *dim); +// +void d_tree_ocp_qp_sol_create(struct d_tree_ocp_qp_dim *dim, struct d_tree_ocp_qp_sol *qp_sol, void *memory); +// +void d_tree_ocp_qp_sol_get_all(struct d_tree_ocp_qp *qp, struct d_tree_ocp_qp_sol *qp_sol, double **u, double **x, double **ls, double **us, double **pi, double **lam_lb, double **lam_ub, double **lam_lg, double **lam_ug, double **lam_ls, double **lam_us); +// +void d_tree_ocp_qp_sol_get(char *field, int node_edge, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_u(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_x(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_sl(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_su(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_pi(int edge, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_lam_lb(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_lam_ub(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_lam_lg(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); +// +void d_tree_ocp_qp_sol_get_lam_ug(int node, struct d_tree_ocp_qp_sol *qp_sol, double *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_utils.h new file mode 100644 index 0000000000..b689fdc0fa --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_d_tree_ocp_qp_utils.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_D_TREE_OCP_QP_UTILS_H_ +#define HPIPM_D_TREE_OCP_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_d_tree_ocp_qp_dim.h" +#include "hpipm_d_tree_ocp_qp.h" +#include "hpipm_d_tree_ocp_qp_sol.h" +#include "hpipm_d_tree_ocp_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void d_tree_ocp_qp_dim_print(struct d_tree_ocp_qp_dim *qp_dim); +// +//void d_tree_ocp_qp_dim_codegen(char *file_name, char *mode, struct d_tree_ocp_qp_dim *qp_dim); +// +void d_tree_ocp_qp_print(struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp *qp); +// +//void d_tree_ocp_qp_codegen(char *file_name, char *mode, struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp *qp); +// +void d_tree_ocp_qp_sol_print(struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp_sol *ocp_qp_sol); +// +void d_tree_ocp_qp_ipm_arg_print(struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp_ipm_arg *arg); +// +//void d_tree_ocp_qp_ipm_arg_codegen(char *file_name, char *mode, struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp_ipm_arg *arg); +// +void d_tree_ocp_qp_res_print(struct d_tree_ocp_qp_dim *qp_dim, struct d_tree_ocp_qp_res *ocp_qp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_TREE_OCP_QP_UTILS_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp.h new file mode 100644 index 0000000000..8bc101003e --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp.h @@ -0,0 +1,68 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + + +#ifndef HPIPM_M_DENSE_QP_H_ +#define HPIPM_M_DENSE_QP_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp.h" +#include "hpipm_s_dense_qp.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +void cvt_d2s_dense_qp(struct d_dense_qp *qpd, struct s_dense_qp *qps); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_M_DENSE_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp_dim.h new file mode 100644 index 0000000000..4610321b97 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_m_dense_qp_dim.h @@ -0,0 +1,68 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + + +#ifndef HPIPM_M_DENSE_QP_DIM_H_ +#define HPIPM_M_DENSE_QP_DIM_H_ + + + +#include +#include + +#include "hpipm_d_dense_qp_dim.h" +#include "hpipm_s_dense_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +void cvt_d2s_dense_qp_dim(struct d_dense_qp_dim *qpd, struct s_dense_qp_dim *qps); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_M_DENSE_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp.h new file mode 100644 index 0000000000..95c3dad1aa --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp.h @@ -0,0 +1,49 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void m_cvt_d_ocp_qp_to_s_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_ipm_hard.h b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_ipm_hard.h new file mode 100644 index 0000000000..1c44acee2e --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_ipm_hard.h @@ -0,0 +1,115 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +struct m_ipm_hard_ocp_qp_workspace + { + struct d_ipm_hard_core_qp_workspace *core_workspace; + struct blasfeo_dvec *dux; + struct blasfeo_dvec *dpi; + struct blasfeo_dvec *dt_lb; + struct blasfeo_dvec *dt_lg; + struct blasfeo_svec *sdux; // XXX + struct blasfeo_svec *sdpi; // XXX + struct blasfeo_dvec *res_g; // q-residuals + struct blasfeo_dvec *res_b; // b-residuals + struct blasfeo_dvec *res_d; // d-residuals XXX remove ??? + struct blasfeo_dvec *res_d_lb; // d-residuals + struct blasfeo_dvec *res_d_ub; // d-residuals + struct blasfeo_dvec *res_d_lg; // d-residuals + struct blasfeo_dvec *res_d_ug; // d-residuals + struct blasfeo_dvec *res_m; // m-residuals + struct blasfeo_dvec *res_m_lb; // m-residuals + struct blasfeo_dvec *res_m_ub; // m-residuals + struct blasfeo_dvec *res_m_lg; // m-residuals + struct blasfeo_dvec *res_m_ug; // m-residuals + struct blasfeo_svec *sres_g; // q-residuals // XXX + struct blasfeo_svec *sres_b; // b-residuals // XXX + struct blasfeo_dvec *Qx_lb; // hessian update + struct blasfeo_dvec *Qx_lg; // hessian update + struct blasfeo_dvec *qx_lb; // gradient update + struct blasfeo_dvec *qx_lg; // gradient update + struct blasfeo_svec *sQx_lb; // hessian update // XXX + struct blasfeo_svec *sQx_lg; // hessian update // XXX + struct blasfeo_svec *sqx_lb; // gradient update // XXX + struct blasfeo_svec *sqx_lg; // gradient update // XXX + struct blasfeo_dvec *tmp_nbM; // work space of size nbM + struct blasfeo_svec *tmp_nxM; // work space of size nxM // XXX + struct blasfeo_dvec *tmp_ngM; // work space of size ngM + struct blasfeo_svec *Pb; // Pb // XXX + struct blasfeo_smat *L; // XXX + struct blasfeo_smat *AL; // XXX + struct blasfeo_svec *sSx; // scaling + struct blasfeo_svec *sSi; // scaling inverted + double *stat; // convergence statistics + double res_mu; // mu-residual + int iter; // iteration number + int compute_Pb; + int scale; + }; + + + +struct m_ipm_hard_ocp_qp_arg + { + double alpha_min; // exit cond on step length + double mu_max; // exit cond on duality measure + double mu0; // initial value for duality measure + int iter_max; // exit cond in iter number + }; + + + +// +hpipm_size_t m_memsize_ipm_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct m_ipm_hard_ocp_qp_arg *arg); +// +void m_create_ipm_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct m_ipm_hard_ocp_qp_arg *arg, struct m_ipm_hard_ocp_qp_workspace *ws, void *mem); +// +void m_solve_ipm_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct d_ocp_qp_sol *qp_sol, struct m_ipm_hard_ocp_qp_workspace *ws); +// +void m_solve_ipm2_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct d_ocp_qp_sol *qp_sol, struct m_ipm_hard_ocp_qp_workspace *ws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_kkt.h new file mode 100644 index 0000000000..032fe95b1f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_m_ocp_qp_kkt.h @@ -0,0 +1,45 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +void m_fact_solve_kkt_step_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct m_ipm_hard_ocp_qp_workspace *ws); +void m_solve_kkt_step_hard_ocp_qp(struct d_ocp_qp *d_qp, struct s_ocp_qp *s_qp, struct m_ipm_hard_ocp_qp_workspace *ws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_cast_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_cast_qcqp.h new file mode 100644 index 0000000000..ba01ecb3bd --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_cast_qcqp.h @@ -0,0 +1,72 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_CAST_QCQP_H_ +#define HPIPM_S_CAST_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_dense_qcqp.h" +#include "hpipm_s_dense_qcqp_sol.h" +#include "hpipm_s_ocp_qcqp.h" +#include "hpipm_s_ocp_qcqp_dim.h" +#include "hpipm_s_ocp_qcqp_sol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_cast_qcqp_compute_dim(struct s_ocp_qcqp_dim *ocp_dim, struct s_dense_qcqp_dim *dense_dim); +// +void s_cast_qcqp_cond(struct s_ocp_qcqp *ocp_qp, struct s_dense_qcqp *dense_qp); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_CAST_QCQP_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_cond.h b/phonelibs/acados/include/hpipm/include/hpipm_s_cond.h new file mode 100644 index 0000000000..30116798b7 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_cond.h @@ -0,0 +1,137 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_COND_H_ +#define HPIPM_S_COND_H_ + + + +#include +#include + +#include "hpipm_s_dense_qp.h" +#include "hpipm_s_dense_qp_sol.h" +#include "hpipm_s_ocp_qp.h" +#include "hpipm_s_ocp_qp_dim.h" +#include "hpipm_s_ocp_qp_sol.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_cond_qp_arg + { + int cond_last_stage; // condense last stage + int cond_alg; // condensing algorithm: 0 N2-nx3, 1 N3-nx2 + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution inequality constr (lam t) + int square_root_alg; // square root algorithm (faster but requires RSQ>0) + hpipm_size_t memsize; + }; + + + +struct s_cond_qp_ws + { + struct blasfeo_smat *Gamma; + struct blasfeo_smat *GammaQ; + struct blasfeo_smat *L; + struct blasfeo_smat *Lx; + struct blasfeo_smat *AL; + struct blasfeo_svec *Gammab; + struct blasfeo_svec *l; + struct blasfeo_svec *tmp_nbgM; + struct blasfeo_svec *tmp_nuxM; + int bs; // block size + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_cond_qp_arg_memsize(); +// +void s_cond_qp_arg_create(struct s_cond_qp_arg *cond_arg, void *mem); +// +void s_cond_qp_arg_set_default(struct s_cond_qp_arg *cond_arg); +// condensing algorithm: 0 N2-nx3, 1 N3-nx2 +void s_cond_qp_arg_set_cond_alg(int cond_alg, struct s_cond_qp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 square-root +void s_cond_qp_arg_set_ric_alg(int ric_alg, struct s_cond_qp_arg *cond_arg); +// condense last stage: 0 last stage disregarded, 1 last stage condensed too +void s_cond_qp_arg_set_cond_last_stage(int cond_last_stage, struct s_cond_qp_arg *cond_arg); +// +void s_cond_qp_arg_set_comp_prim_sol(int value, struct s_cond_qp_arg *cond_arg); +// +void s_cond_qp_arg_set_comp_dual_sol_eq(int value, struct s_cond_qp_arg *cond_arg); +// +void s_cond_qp_arg_set_comp_dual_sol_ineq(int value, struct s_cond_qp_arg *cond_arg); + +// +void s_cond_qp_compute_dim(struct s_ocp_qp_dim *ocp_dim, struct s_dense_qp_dim *dense_dim); +// +hpipm_size_t s_cond_qp_ws_memsize(struct s_ocp_qp_dim *ocp_dim, struct s_cond_qp_arg *cond_arg); +// +void s_cond_qp_ws_create(struct s_ocp_qp_dim *ocp_dim, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws, void *mem); +// +void s_cond_qp_cond(struct s_ocp_qp *ocp_qp, struct s_dense_qp *dense_qp, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_qp_cond_lhs(struct s_ocp_qp *ocp_qp, struct s_dense_qp *dense_qp, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_qp_cond_rhs(struct s_ocp_qp *ocp_qp, struct s_dense_qp *dense_qp, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_qp_expand_sol(struct s_ocp_qp *ocp_qp, struct s_dense_qp_sol *dense_qp_sol, struct s_ocp_qp_sol *ocp_qp_sol, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// TODO remove +void s_cond_qp_expand_primal_sol(struct s_ocp_qp *ocp_qp, struct s_dense_qp_sol *dense_qp_sol, struct s_ocp_qp_sol *ocp_qp_sol, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); + +// +void s_cond_qp_update(int *idxc, struct s_ocp_qp *ocp_qp, struct s_dense_qp *dense_qp, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_COND_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_cond_aux.h b/phonelibs/acados/include/hpipm/include/hpipm_s_cond_aux.h new file mode 100644 index 0000000000..003472ab9f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_cond_aux.h @@ -0,0 +1,92 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_COND_AUX_H_ +#define HPIPM_S_COND_AUX_H_ + + + +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_cond_BAbt(struct s_ocp_qp *ocp_qp, struct blasfeo_smat *BAbt2, struct blasfeo_svec *b2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_BAt(struct s_ocp_qp *ocp_qp, struct blasfeo_smat *BAbt2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_b(struct s_ocp_qp *ocp_qp, struct blasfeo_svec *b2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_RSQrq(struct s_ocp_qp *ocp_qp, struct blasfeo_smat *RSQrq2, struct blasfeo_svec *rq2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_RSQ(struct s_ocp_qp *ocp_qp, struct blasfeo_smat *RSQrq2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_rq(struct s_ocp_qp *ocp_qp, struct blasfeo_svec *rq2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_DCtd(struct s_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_smat *DCt2, struct blasfeo_svec *d2, struct blasfeo_svec *d_mask2, int *idxs_rev2, struct blasfeo_svec *Z2, struct blasfeo_svec *z2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_DCt(struct s_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_smat *DCt2, int *idxs_rev2, struct blasfeo_svec *Z2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_cond_d(struct s_ocp_qp *ocp_qp, struct blasfeo_svec *d2, struct blasfeo_svec *d_mask2, struct blasfeo_svec *z2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_expand_sol(struct s_ocp_qp *ocp_qp, struct s_dense_qp_sol *dense_qp_sol, struct s_ocp_qp_sol *ocp_qp_sol, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_expand_primal_sol(struct s_ocp_qp *ocp_qp, struct s_dense_qp_sol *dense_qp_sol, struct s_ocp_qp_sol *ocp_qp_sol, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); + +// +void s_update_cond_BAbt(int *idxc, struct s_ocp_qp *ocp_qp, struct blasfeo_smat *BAbt2, struct blasfeo_svec *b2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_update_cond_RSQrq_N2nx3(int *idxc, struct s_ocp_qp *ocp_qp, struct blasfeo_smat *RSQrq2, struct blasfeo_svec *rq2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); +// +void s_update_cond_DCtd(int *idxc, struct s_ocp_qp *ocp_qp, int *idxb2, struct blasfeo_smat *DCt2, struct blasfeo_svec *d2, int *idxs2, struct blasfeo_svec *Z2, struct blasfeo_svec *z2, struct s_cond_qp_arg *cond_arg, struct s_cond_qp_ws *cond_ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_COND_AUX_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_cond_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_cond_qcqp.h new file mode 100644 index 0000000000..c36678abd5 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_cond_qcqp.h @@ -0,0 +1,130 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_COND_QCQP_H_ +#define HPIPM_S_COND_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_dense_qcqp.h" +#include "hpipm_s_dense_qcqp_sol.h" +#include "hpipm_s_ocp_qcqp.h" +#include "hpipm_s_ocp_qcqp_dim.h" +#include "hpipm_s_ocp_qcqp_sol.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_cond_qcqp_arg + { + struct s_cond_qp_arg *qp_arg; + int cond_last_stage; // condense last stage +// int cond_variant; // TODO + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution equality constr (lam t) + int square_root_alg; // square root algorithm (faster but requires RSQ>0) + hpipm_size_t memsize; + }; + + + +struct s_cond_qcqp_ws + { + struct s_cond_qp_ws *qp_ws; + struct blasfeo_smat *hess_array; // TODO remove + struct blasfeo_smat *zero_hess; // TODO remove + struct blasfeo_svec *zero_grad; // TODO remove + struct blasfeo_svec *grad_array; // TODO remove + struct blasfeo_svec *tmp_nvc; + struct blasfeo_svec *tmp_nuxM; + struct blasfeo_smat *GammaQ; + struct blasfeo_smat *tmp_DCt; + struct blasfeo_smat *tmp_nuM_nxM; +// struct blasfeo_svec *d_qp; +// struct blasfeo_svec *d_mask_qp; + hpipm_size_t memsize; + }; + + +// +hpipm_size_t s_cond_qcqp_arg_memsize(); +// +void s_cond_qcqp_arg_create(struct s_cond_qcqp_arg *cond_arg, void *mem); +// +void s_cond_qcqp_arg_set_default(struct s_cond_qcqp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 square-root +void s_cond_qcqp_arg_set_ric_alg(int ric_alg, struct s_cond_qcqp_arg *cond_arg); +// condense last stage: 0 last stage disregarded, 1 last stage condensed too +void s_cond_qcqp_arg_set_cond_last_stage(int cond_last_stage, struct s_cond_qcqp_arg *cond_arg); + +// +void s_cond_qcqp_compute_dim(struct s_ocp_qcqp_dim *ocp_dim, struct s_dense_qcqp_dim *dense_dim); +// +hpipm_size_t s_cond_qcqp_ws_memsize(struct s_ocp_qcqp_dim *ocp_dim, struct s_cond_qcqp_arg *cond_arg); +// +void s_cond_qcqp_ws_create(struct s_ocp_qcqp_dim *ocp_dim, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws, void *mem); +// +void s_cond_qcqp_qc(struct s_ocp_qcqp *ocp_qp, struct blasfeo_smat *Hq2, int *Hq_nzero2, struct blasfeo_smat *Ct2, struct blasfeo_svec *d2, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_qc_lhs(struct s_ocp_qcqp *ocp_qp, struct blasfeo_smat *Hq2, int *Hq_nzero2, struct blasfeo_smat *Ct2, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_qc_rhs(struct s_ocp_qcqp *ocp_qp, struct blasfeo_svec *d2, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_cond(struct s_ocp_qcqp *ocp_qp, struct s_dense_qcqp *dense_qp, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_cond_rhs(struct s_ocp_qcqp *ocp_qp, struct s_dense_qcqp *dense_qp, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_cond_lhs(struct s_ocp_qcqp *ocp_qp, struct s_dense_qcqp *dense_qp, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); +// +void s_cond_qcqp_expand_sol(struct s_ocp_qcqp *ocp_qp, struct s_dense_qcqp_sol *dense_qp_sol, struct s_ocp_qcqp_sol *ocp_qp_sol, struct s_cond_qcqp_arg *cond_arg, struct s_cond_qcqp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_COND_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm.h new file mode 100644 index 0000000000..480392c7d9 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm.h @@ -0,0 +1,101 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_CORE_QP_IPM_ +#define HPIPM_S_CORE_QP_IPM_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct s_core_qp_ipm_workspace + { + float *v; // primal variables + float *pi; // equality constraints multipliers + float *lam; // inequality constraints multipliers + float *t; // inequality constraints slacks + float *t_inv; // inverse of t + float *v_bkp; // backup of primal variables + float *pi_bkp; // backup of equality constraints multipliers + float *lam_bkp; // backup of inequality constraints multipliers + float *t_bkp; // backup of inequality constraints slacks + float *dv; // step in v + float *dpi; // step in pi + float *dlam; // step in lam + float *dt; // step in t + float *res_g; // q-residuals + float *res_b; // b-residuals + float *res_d; // d-residuals + float *res_m; // m-residuals + float *res_m_bkp; // m-residuals + float *Gamma; // Hessian update + float *gamma; // gradient update + float alpha_prim; // step length + float alpha_dual; // step length + float alpha; // step length + float sigma; // centering XXX + float mu; // duality measuere + float mu_aff; // affine duality measuere + float nc_inv; // 1.0/nc, where nc is the total number of constraints + float nc_mask_inv; // 1.0/nc_mask + float lam_min; // min value in lam vector + float t_min; // min value in t vector + float t_min_inv; // inverse of min value in t vector + float tau_min; // min value of barrier parameter + int nv; // number of primal variables + int ne; // number of equality constraints + int nc; // (twice the) number of (two-sided) inequality constraints + int nc_mask; // total number of ineq constr after masking + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam also in solution, or only in Gamma computation + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t s_memsize_core_qp_ipm(int nv, int ne, int nc); +// +void s_create_core_qp_ipm(int nv, int ne, int nc, struct s_core_qp_ipm_workspace *workspace, void *mem); +// +void s_core_qp_ipm(struct s_core_qp_ipm_workspace *workspace); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_S_CORE_QP_IPM_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm_aux.h b/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm_aux.h new file mode 100644 index 0000000000..1ac3d7ede9 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_core_qp_ipm_aux.h @@ -0,0 +1,68 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_CORE_QP_IPM_AUX_ +#define HPIPM_S_CORE_QP_IPM_AUX_ + +#ifdef __cplusplus +extern "C" { +#endif + +// +void s_compute_Gamma_gamma_qp(float *res_d, float *res_m, struct s_core_qp_ipm_workspace *rws); +// +void s_compute_gamma_qp(float *res_d, float *res_m, struct s_core_qp_ipm_workspace *rws); +// +void s_compute_lam_t_qp(float *res_d, float *res_m, float *dlam, float *dt, struct s_core_qp_ipm_workspace *rws); +// +void s_compute_alpha_qp(struct s_core_qp_ipm_workspace *rws); +// +void s_update_var_qp(struct s_core_qp_ipm_workspace *rws); +// +void s_compute_mu_aff_qp(struct s_core_qp_ipm_workspace *rws); +// +void s_backup_res_m(struct s_core_qp_ipm_workspace *rws); +// +void s_compute_centering_correction_qp(struct s_core_qp_ipm_workspace *rws); +// +void s_compute_centering_qp(struct s_core_qp_ipm_workspace *rws); +// +void s_compute_tau_min_qp(struct s_core_qp_ipm_workspace *rws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_S_CORE_QP_IPM_AUX_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp.h new file mode 100644 index 0000000000..d03c065375 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp.h @@ -0,0 +1,200 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QCQP_H_ +#define HPIPM_S_DENSE_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_dense_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qcqp + { + struct s_dense_qcqp_dim *dim; + struct blasfeo_smat *Hv; // hessian of cost & vector work space + struct blasfeo_smat *A; // equality constraint matrix + struct blasfeo_smat *Ct; // inequality constraints matrix + struct blasfeo_smat *Hq; // hessians of quadratic constraints + struct blasfeo_svec *gz; // gradient of cost & gradient of slacks + struct blasfeo_svec *b; // equality constraint vector + struct blasfeo_svec *d; // inequality constraints vector + struct blasfeo_svec *d_mask; // inequality constraints mask vector + struct blasfeo_svec *m; // rhs of complementarity condition + struct blasfeo_svec *Z; // (diagonal) hessian of slacks + int *idxb; // indices of box constrained variables within [u; x] + int *idxs_rev; // index of soft constraints (reverse storage) + int *Hq_nzero; // for each int, the last 3 bits ...abc, {a,b,c}=0 => {R,S,Q}=0 + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_dense_qcqp_memsize(struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_create(struct s_dense_qcqp_dim *dim, struct s_dense_qcqp *qp, void *memory); + +// +void s_dense_qcqp_set(char *field, void *value, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_H(float *H, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_g(float *g, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_A(float *A, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_b(float *b, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_idxb(int *idxb, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_lb(float *lb, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_lb_mask(float *lb, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ub(float *ub, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ub_mask(float *ub, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_C(float *C, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_lg(float *lg, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_lg_mask(float *lg, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ug(float *ug, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ug_mask(float *ug, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_Hq(float *Hq, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_gq(float *gq, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_uq(float *uq, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_uq_mask(float *uq, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_idxs(int *idxs, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_idxs_rev(int *idxs_rev, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_Zl(float *Zl, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_Zu(float *Zu, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_zl(float *zl, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_zu(float *zu, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ls(float *ls, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_ls_mask(float *ls, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_us(float *us, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_set_us_mask(float *us, struct s_dense_qcqp *qp); + +// getters (COLMAJ) + +void s_dense_qcqp_get_H(struct s_dense_qcqp *qp, float *H); +// +void s_dense_qcqp_get_g(struct s_dense_qcqp *qp, float *g); +// +void s_dense_qcqp_get_A(struct s_dense_qcqp *qp, float *A); +// +void s_dense_qcqp_get_b(struct s_dense_qcqp *qp, float *b); +// +void s_dense_qcqp_get_idxb(struct s_dense_qcqp *qp, int *idxb); +// +void s_dense_qcqp_get_lb(struct s_dense_qcqp *qp, float *lb); +// +void s_dense_qcqp_get_lb_mask(struct s_dense_qcqp *qp, float *lb); +// +void s_dense_qcqp_get_ub(struct s_dense_qcqp *qp, float *ub); +// +void s_dense_qcqp_get_ub_mask(struct s_dense_qcqp *qp, float *ub); +// +void s_dense_qcqp_get_C(struct s_dense_qcqp *qp, float *C); +// +void s_dense_qcqp_get_lg(struct s_dense_qcqp *qp, float *lg); +// +void s_dense_qcqp_get_lg_mask(struct s_dense_qcqp *qp, float *lg); +// +void s_dense_qcqp_get_ug(struct s_dense_qcqp *qp, float *ug); +// +void s_dense_qcqp_get_ug_mask(struct s_dense_qcqp *qp, float *ug); +// +void s_dense_qcqp_get_idxs(struct s_dense_qcqp *qp, int *idxs); +// +void s_dense_qcqp_get_idxs_rev(struct s_dense_qcqp *qp, int *idxs_rev); +// +void s_dense_qcqp_get_Zl(struct s_dense_qcqp *qp, float *Zl); +// +void s_dense_qcqp_get_Zu(struct s_dense_qcqp *qp, float *Zu); +// +void s_dense_qcqp_get_zl(struct s_dense_qcqp *qp, float *zl); +// +void s_dense_qcqp_get_zu(struct s_dense_qcqp *qp, float *zu); +// +void s_dense_qcqp_get_ls(struct s_dense_qcqp *qp, float *ls); +// +void s_dense_qcqp_get_ls_mask(struct s_dense_qcqp *qp, float *ls); +// +void s_dense_qcqp_get_us(struct s_dense_qcqp *qp, float *us); +// +void s_dense_qcqp_get_us_mask(struct s_dense_qcqp *qp, float *us); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QCQP_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_dim.h new file mode 100644 index 0000000000..04908c2c3a --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_dim.h @@ -0,0 +1,99 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QCQP_DIM_H_ +#define HPIPM_S_DENSE_QCQP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qcqp_dim + { + struct s_dense_qp_dim *qp_dim; // dim of qp approximation + int nv; // number of variables + int ne; // number of equality constraints + int nb; // number of box constraints + int ng; // number of general constraints + int nq; // number of quadratic constraints + int nsb; // number of softened box constraints + int nsg; // number of softened general constraints + int nsq; // number of softened quadratic constraints + int ns; // number of softened constraints (nsb+nsg+nsq) TODO number of slacks + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qcqp_dim_memsize(); +// +void s_dense_qcqp_dim_create(struct s_dense_qcqp_dim *dim, void *memory); +// +void s_dense_qcqp_dim_set(char *fiels_name, int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nv(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_ne(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nb(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_ng(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nq(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nsb(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nsg(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_nsq(int value, struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_dim_set_ns(int value, struct s_dense_qcqp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_DENSE_QCQP_DIM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_ipm.h new file mode 100644 index 0000000000..8f85768ee3 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_ipm.h @@ -0,0 +1,204 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QCQP_IPM_H_ +#define HPIPM_S_DENSE_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qcqp_ipm_arg + { + struct s_dense_qp_ipm_arg *qp_arg; + float mu0; // initial value for duality measure + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float reg_dual; // reg of dual hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int scale; // scale hessian + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_res_exit; // compute residuals on exit (only for abs_form==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_dense_qcqp_ipm_ws + { +// float qp_res[4]; // infinity norm of residuals + struct s_dense_qp_ipm_ws *qp_ws; + struct s_dense_qp *qp; + struct s_dense_qp_sol *qp_sol; + struct s_dense_qcqp_res_ws *qcqp_res_ws; + struct s_dense_qcqp_res *qcqp_res; + struct blasfeo_svec *tmp_nv; +// float *stat; // convergence statistics +// void *lq_work0; +// void *lq_work1; + int iter; // iteration number +// int stat_max; // iterations saved in stat +// int stat_m; // numer of recorded stat per ipm iter +// int scale; +// int use_hess_fact; + int status; + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t s_dense_qcqp_ipm_arg_memsize(struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_ipm_arg_create(struct s_dense_qcqp_dim *dim, struct s_dense_qcqp_ipm_arg *arg, void *mem); +// +void s_dense_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set(char *field, void *value, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_iter_max(int *iter_max, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_alpha_min(float *alpha_min, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_mu0(float *mu0, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_tol_stat(float *tol_stat, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_tol_eq(float *tol_eq, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_tol_ineq(float *tol_ineq, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_tol_comp(float *tol_comp, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_reg_prim(float *reg, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_reg_dual(float *reg, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_warm_start(int *warm_start, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_pred_corr(int *pred_corr, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_cond_pred_corr(int *cond_pred_corr, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_comp_res_pred(int *comp_res_pred, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_comp_res_exit(int *comp_res_exit, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_lam_min(float *value, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_t_min(float *value, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_split_step(int *value, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_arg_set_t_lam_min(int *value, struct s_dense_qcqp_ipm_arg *arg); + +// +hpipm_size_t s_dense_qcqp_ipm_ws_memsize(struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_ipm_ws_create(struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp_ipm_arg *arg, struct s_dense_qcqp_ipm_ws *ws, void *mem); +// +void s_dense_qcqp_ipm_get(char *field, struct s_dense_qcqp_ipm_ws *ws, void *value); +// +void s_dense_qcqp_ipm_get_status(struct s_dense_qcqp_ipm_ws *ws, int *status); +// +void s_dense_qcqp_ipm_get_iter(struct s_dense_qcqp_ipm_ws *ws, int *iter); +// +void s_dense_qcqp_ipm_get_max_res_stat(struct s_dense_qcqp_ipm_ws *ws, float *res_stat); +// +void s_dense_qcqp_ipm_get_max_res_eq(struct s_dense_qcqp_ipm_ws *ws, float *res_eq); +// +void s_dense_qcqp_ipm_get_max_res_ineq(struct s_dense_qcqp_ipm_ws *ws, float *res_ineq); +// +void s_dense_qcqp_ipm_get_max_res_comp(struct s_dense_qcqp_ipm_ws *ws, float *res_comp); +// +void s_dense_qcqp_ipm_get_stat(struct s_dense_qcqp_ipm_ws *ws, float **stat); +// +void s_dense_qcqp_ipm_get_stat_m(struct s_dense_qcqp_ipm_ws *ws, int *stat_m); +#if 0 +// +void s_dense_qcqp_init_var(struct s_dense_qcqp *qp, struct s_dense_qcqp_sol *qp_sol, struct s_dense_qcqp_ipm_arg *arg, struct s_dense_qcqp_ipm_ws *ws); +#endif +// +void s_dense_qcqp_ipm_solve(struct s_dense_qcqp *qp, struct s_dense_qcqp_sol *qp_sol, struct s_dense_qcqp_ipm_arg *arg, struct s_dense_qcqp_ipm_ws *ws); +#if 0 +// +void s_dense_qcqp_ipm_predict(struct s_dense_qcqp *qp, struct s_dense_qcqp_sol *qp_sol, struct s_dense_qcqp_ipm_arg *arg, struct s_dense_qcqp_ipm_ws *ws); +// +void s_dense_qcqp_ipm_sens(struct s_dense_qcqp *qp, struct s_dense_qcqp_sol *qp_sol, struct s_dense_qcqp_ipm_arg *arg, struct s_dense_qcqp_ipm_ws *ws); +#endif + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QCQP_IPM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_res.h new file mode 100644 index 0000000000..779658cc8f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_res.h @@ -0,0 +1,108 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QCQP_RES_H_ +#define HPIPM_S_DENSE_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qcqp_res + { + struct s_dense_qcqp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // infinity norm of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_dense_qcqp_res_ws + { + struct blasfeo_svec *tmp_nv; // work space of size nv + struct blasfeo_svec *tmp_nbgq; // work space of size nbM+ngM+nqM + struct blasfeo_svec *tmp_ns; // work space of size nsM + struct blasfeo_svec *q_fun; // value for evaluation of quadr constr + struct blasfeo_svec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qcqp_res_memsize(struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_res_create(struct s_dense_qcqp_dim *dim, struct s_dense_qcqp_res *res, void *mem); +// +hpipm_size_t s_dense_qcqp_res_ws_memsize(struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_res_ws_create(struct s_dense_qcqp_dim *dim, struct s_dense_qcqp_res_ws *workspace, void *mem); +// +void s_dense_qcqp_res_compute(struct s_dense_qcqp *qp, struct s_dense_qcqp_sol *qp_sol, struct s_dense_qcqp_res *res, struct s_dense_qcqp_res_ws *ws); +// +void s_dense_qcqp_res_compute_inf_norm(struct s_dense_qcqp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_DENSE_QCQP_RES_H_ + + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_sol.h new file mode 100644 index 0000000000..197a690e52 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_sol.h @@ -0,0 +1,86 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QCQP_SOL_H_ +#define HPIPM_S_DENSE_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_dense_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qcqp_sol + { + struct s_dense_qcqp_dim *dim; + struct blasfeo_svec *v; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + void *misc; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qcqp_sol_memsize(struct s_dense_qcqp_dim *dim); +// +void s_dense_qcqp_sol_create(struct s_dense_qcqp_dim *dim, struct s_dense_qcqp_sol *qp_sol, void *memory); +// +void s_dense_qcqp_sol_get_v(struct s_dense_qcqp_sol *qp_sol, float *v); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QCQP_SOL_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_utils.h new file mode 100644 index 0000000000..4f5aae26eb --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qcqp_utils.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QCQP_UTILS_H_ +#define HPIPM_S_DENSE_QCQP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_dense_qcqp_dim.h" +#include "hpipm_s_dense_qcqp.h" +#include "hpipm_s_dense_qcqp_sol.h" +//#include "hpipm_s_dense_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_dense_qcqp_dim_print(struct s_dense_qcqp_dim *qp_dim); +// +//void s_dense_qcqp_dim_codegen(char *file_name, char *mode, struct s_dense_qcqp_dim *qp_dim); +// +void s_dense_qcqp_print(struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp *qp); +// +//void s_dense_qcqp_codegen(char *file_name, char *mode, struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp *qp); +// +void s_dense_qcqp_sol_print(struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp_sol *dense_qcqp_sol); +// +//void s_dense_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp_ipm_arg *arg); +// +void s_dense_qcqp_res_print(struct s_dense_qcqp_dim *qp_dim, struct s_dense_qcqp_res *dense_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_DENSE_QCQP_UTILS_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp.h new file mode 100644 index 0000000000..3c2517fe1b --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp.h @@ -0,0 +1,207 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QP_H_ +#define HPIPM_S_DENSE_QP_H_ + + + +#include +#include + +#include "hpipm_s_dense_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qp + { + struct s_dense_qp_dim *dim; + struct blasfeo_smat *Hv; // hessian & gradient + struct blasfeo_smat *A; // dynamics matrix + struct blasfeo_smat *Ct; // constraints matrix + struct blasfeo_svec *gz; // gradient & gradient of slacks + struct blasfeo_svec *b; // dynamics vector + struct blasfeo_svec *d; // constraints vector + struct blasfeo_svec *d_mask; // inequality constraints mask vector + struct blasfeo_svec *m; // rhs of complementarity condition + struct blasfeo_svec *Z; // (diagonal) hessian of slacks + int *idxb; // indices of box constrained variables within [u; x] + int *idxs_rev; // index of soft constraints (reverse storage) + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_dense_qp_memsize(struct s_dense_qp_dim *dim); +// +void s_dense_qp_create(struct s_dense_qp_dim *dim, struct s_dense_qp *qp, void *memory); + +// setters - colmaj +// +void s_dense_qp_set_all(float *H, float *g, float *A, float *b, int *idxb, float *d_lb, float *d_ub, float *C, float *d_lg, float *d_ug, float *Zl, float *Zu, float *zl, float *zu, int *idxs, float *d_ls, float *d_us, struct s_dense_qp *qp); +// +void s_dense_qp_get_all(struct s_dense_qp *qp, float *H, float *g, float *A, float *b, int *idxb, float *d_lb, float *d_ub, float *C, float *d_lg, float *d_ug, float *Zl, float *Zu, float *zl, float *zu, int *idxs, float *d_ls, float *d_us); +// +void s_dense_qp_set(char *field, void *value, struct s_dense_qp *qp); +// +void s_dense_qp_set_H(float *H, struct s_dense_qp *qp); +// +void s_dense_qp_set_g(float *g, struct s_dense_qp *qp); +// +void s_dense_qp_set_A(float *A, struct s_dense_qp *qp); +// +void s_dense_qp_set_b(float *b, struct s_dense_qp *qp); +// +void s_dense_qp_set_idxb(int *idxb, struct s_dense_qp *qp); +// +void s_dense_qp_set_Jb(float *Jb, struct s_dense_qp *qp); +// +void s_dense_qp_set_lb(float *lb, struct s_dense_qp *qp); +// +void s_dense_qp_set_lb_mask(float *lb, struct s_dense_qp *qp); +// +void s_dense_qp_set_ub(float *ub, struct s_dense_qp *qp); +// +void s_dense_qp_set_ub_mask(float *ub, struct s_dense_qp *qp); +// +void s_dense_qp_set_C(float *C, struct s_dense_qp *qp); +// +void s_dense_qp_set_lg(float *lg, struct s_dense_qp *qp); +// +void s_dense_qp_set_lg_mask(float *lg, struct s_dense_qp *qp); +// +void s_dense_qp_set_ug(float *ug, struct s_dense_qp *qp); +// +void s_dense_qp_set_ug_mask(float *ug, struct s_dense_qp *qp); +// +void s_dense_qp_set_idxs(int *idxs, struct s_dense_qp *qp); +// +void s_dense_qp_set_idxs_rev(int *idxs_rev, struct s_dense_qp *qp); +// +void s_dense_qp_set_Jsb(float *Jsb, struct s_dense_qp *qp); +// +void s_dense_qp_set_Jsg(float *Jsg, struct s_dense_qp *qp); +// +void s_dense_qp_set_Zl(float *Zl, struct s_dense_qp *qp); +// +void s_dense_qp_set_Zu(float *Zu, struct s_dense_qp *qp); +// +void s_dense_qp_set_zl(float *zl, struct s_dense_qp *qp); +// +void s_dense_qp_set_zu(float *zu, struct s_dense_qp *qp); +// +void s_dense_qp_set_ls(float *ls, struct s_dense_qp *qp); +// +void s_dense_qp_set_ls_mask(float *ls, struct s_dense_qp *qp); +// +void s_dense_qp_set_us(float *us, struct s_dense_qp *qp); +// +void s_dense_qp_set_us_mask(float *us, struct s_dense_qp *qp); + +// getters - colmaj +// +void s_dense_qp_get_H(struct s_dense_qp *qp, float *H); +// +void s_dense_qp_get_g(struct s_dense_qp *qp, float *g); +// +void s_dense_qp_get_A(struct s_dense_qp *qp, float *A); +// +void s_dense_qp_get_b(struct s_dense_qp *qp, float *b); +// +void s_dense_qp_get_idxb(struct s_dense_qp *qp, int *idxb); +// +void s_dense_qp_get_lb(struct s_dense_qp *qp, float *lb); +// +void s_dense_qp_get_lb_mask(struct s_dense_qp *qp, float *lb); +// +void s_dense_qp_get_ub(struct s_dense_qp *qp, float *ub); +// +void s_dense_qp_get_ub_mask(struct s_dense_qp *qp, float *ub); +// +void s_dense_qp_get_C(struct s_dense_qp *qp, float *C); +// +void s_dense_qp_get_lg(struct s_dense_qp *qp, float *lg); +// +void s_dense_qp_get_lg_mask(struct s_dense_qp *qp, float *lg); +// +void s_dense_qp_get_ug(struct s_dense_qp *qp, float *ug); +// +void s_dense_qp_get_ug_mask(struct s_dense_qp *qp, float *ug); +// +void s_dense_qp_get_idxs(struct s_dense_qp *qp, int *idxs); +// +void s_dense_qp_get_idxs_rev(struct s_dense_qp *qp, int *idxs_rev); +// +void s_dense_qp_get_Zl(struct s_dense_qp *qp, float *Zl); +// +void s_dense_qp_get_Zu(struct s_dense_qp *qp, float *Zu); +// +void s_dense_qp_get_zl(struct s_dense_qp *qp, float *zl); +// +void s_dense_qp_get_zu(struct s_dense_qp *qp, float *zu); +// +void s_dense_qp_get_ls(struct s_dense_qp *qp, float *ls); +// +void s_dense_qp_get_ls_mask(struct s_dense_qp *qp, float *ls); +// +void s_dense_qp_get_us(struct s_dense_qp *qp, float *us); +// +void s_dense_qp_get_us_mask(struct s_dense_qp *qp, float *us); + +// setters - rowmaj +// +void s_dense_qp_set_all_rowmaj(float *H, float *g, float *A, float *b, int *idxb, float *d_lb, float *d_ub, float *C, float *d_lg, float *d_ug, float *Zl, float *Zu, float *zl, float *zu, int *idxs, float *d_ls, float *d_us, struct s_dense_qp *qp); + +// getters - rowmaj +// +void s_dense_qp_get_all_rowmaj(struct s_dense_qp *qp, float *H, float *g, float *A, float *b, int *idxb, float *d_lb, float *d_ub, float *C, float *d_lg, float *d_ug, float *Zl, float *Zu, float *zl, float *zu, int *idxs, float *d_ls, float *d_us); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_dim.h new file mode 100644 index 0000000000..b979d24432 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_dim.h @@ -0,0 +1,94 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QP_DIM_H_ +#define HPIPM_S_DENSE_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qp_dim + { + int nv; // number of variables + int ne; // number of equality constraints + int nb; // number of box constraints + int ng; // number of general constraints + int nsb; // number of softened box constraints + int nsg; // number of softened general constraints + int ns; // number of softened constraints (nsb+nsg) + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qp_dim_memsize(); +// +void s_dense_qp_dim_create(struct s_dense_qp_dim *qp_dim, void *memory); +// +void s_dense_qp_dim_set_all(int nv, int ne, int nb, int ng, int nsb, int nsg, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set(char *fiels_name, int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_nv(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_ne(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_nb(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_ng(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_nsb(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_nsg(int value, struct s_dense_qp_dim *dim); +// +void s_dense_qp_dim_set_ns(int value, struct s_dense_qp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_DENSE_QP_DIM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_ipm.h new file mode 100644 index 0000000000..f2d56d4529 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_ipm.h @@ -0,0 +1,260 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QP_IPM_H_ +#define HPIPM_S_DENSE_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qp_ipm_arg + { + float mu0; // initial value for duality measure + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float reg_dual; // reg of dual hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + float tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int scale; // scale hessian + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_res_exit; // compute residuals on exit (only for abs_form==1) + int comp_res_pred; // compute residuals of prediction + int kkt_fact_alg; // 0 null-space, 1 schur-complement + int remove_lin_dep_eq; // 0 do not, 1 do check and remove linearly dependent equality constraints + int compute_obj; // compute obj on exit + int split_step; // use different steps for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_dense_qp_ipm_ws + { + struct s_core_qp_ipm_workspace *core_workspace; + struct s_dense_qp_res_ws *res_ws; + struct s_dense_qp_sol *sol_step; + struct s_dense_qp_sol *sol_itref; + struct s_dense_qp *qp_step; + struct s_dense_qp *qp_itref; + struct s_dense_qp_res *res; + struct s_dense_qp_res *res_itref; + struct s_dense_qp_res *res_step; + struct blasfeo_svec *Gamma; // + struct blasfeo_svec *gamma; // + struct blasfeo_svec *Zs_inv; // + struct blasfeo_smat *Lv; // + struct blasfeo_smat *AL; // + struct blasfeo_smat *Le; // + struct blasfeo_smat *Ctx; // + struct blasfeo_svec *lv; // + struct blasfeo_svec *sv; // scale for Lv + struct blasfeo_svec *se; // scale for Le + struct blasfeo_svec *tmp_nbg; // work space of size nb+ng + struct blasfeo_svec *tmp_ns; // work space of size ns + struct blasfeo_smat *lq0; + struct blasfeo_smat *lq1; + struct blasfeo_svec *tmp_m; + struct blasfeo_smat *A_LQ; + struct blasfeo_smat *A_Q; + struct blasfeo_smat *Zt; + struct blasfeo_smat *ZtH; + struct blasfeo_smat *ZtHZ; + struct blasfeo_svec *xy; + struct blasfeo_svec *Yxy; + struct blasfeo_svec *xz; + struct blasfeo_svec *tmp_nv; + struct blasfeo_svec *tmp_2ns; + struct blasfeo_svec *tmp_nv2ns; + struct blasfeo_smat *A_li; // A of linearly independent equality constraints + struct blasfeo_svec *b_li; // b of linearly independent equality constraints + struct blasfeo_smat *A_bkp; // pointer to backup A + struct blasfeo_svec *b_bkp; // pointer to backup b + struct blasfeo_smat *Ab_LU; + float *stat; // convergence statistics + int *ipiv_v; + int *ipiv_e; + int *ipiv_e1; + void *lq_work0; + void *lq_work1; + void *lq_work_null; + void *orglq_work_null; + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // numer of recorded stat per ipm iter + int scale; + int use_hess_fact; + int use_A_fact; + int status; + int lq_fact; // cache from arg + int mask_constr; // use constr mask + int ne_li; // number of linearly independent equality constraints + int ne_bkp; // ne backup + hpipm_size_t memsize; // memory size (in bytes) of workspace + }; + + + +// +hpipm_size_t s_dense_qp_ipm_arg_memsize(struct s_dense_qp_dim *qp_dim); +// +void s_dense_qp_ipm_arg_create(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_ipm_arg *arg, void *mem); +// +void s_dense_qp_ipm_arg_set_default(enum hpipm_mode mode, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set(char *field, void *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_iter_max(int *iter_max, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_alpha_min(float *alpha_min, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_mu0(float *mu0, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_tol_stat(float *tol_stat, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_tol_eq(float *tol_eq, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_tol_ineq(float *tol_ineq, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_tol_comp(float *tol_comp, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_reg_prim(float *reg, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_reg_dual(float *reg, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_warm_start(int *warm_start, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_pred_corr(int *pred_corr, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_cond_pred_corr(int *cond_pred_corr, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_comp_res_pred(int *comp_res_pred, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_comp_res_exit(int *comp_res_exit, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_lam_min(float *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_t_min(float *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_tau_min(float *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_kkt_fact_alg(int *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_remove_lin_dep_eq(int *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_compute_obj(int *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_split_step(int *value, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_arg_set_t_lam_min(int *value, struct s_dense_qp_ipm_arg *arg); + +// +hpipm_size_t s_dense_qp_ipm_ws_memsize(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_ipm_ws_create(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws, void *mem); +// +void s_dense_qp_ipm_get(char *field, struct s_dense_qp_ipm_ws *ws, void *value); +// +void s_dense_qp_ipm_get_status(struct s_dense_qp_ipm_ws *ws, int *status); +// +void s_dense_qp_ipm_get_iter(struct s_dense_qp_ipm_ws *ws, int *iter); +// +void s_dense_qp_ipm_get_max_res_stat(struct s_dense_qp_ipm_ws *ws, float *res_stat); +// +void s_dense_qp_ipm_get_max_res_eq(struct s_dense_qp_ipm_ws *ws, float *res_eq); +// +void s_dense_qp_ipm_get_max_res_ineq(struct s_dense_qp_ipm_ws *ws, float *res_ineq); +// +void s_dense_qp_ipm_get_max_res_comp(struct s_dense_qp_ipm_ws *ws, float *res_comp); +// +void s_dense_qp_ipm_get_stat(struct s_dense_qp_ipm_ws *ws, float **stat); +// +void s_dense_qp_ipm_get_stat_m(struct s_dense_qp_ipm_ws *ws, int *stat_m); +// +void s_dense_qp_init_var(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_ipm_abs_step(int kk, struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_ipm_delta_step(int kk, struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_ipm_solve(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_ipm_predict(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_ipm_sens(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_compute_step_length(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_kkt.h new file mode 100644 index 0000000000..260dc0ab21 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_kkt.h @@ -0,0 +1,72 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QP_KKT_H_ +#define HPIPM_S_DENSE_QP_KKT_H_ + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_fact_solve_kkt_unconstr_dense_qp(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_fact_solve_kkt_step_dense_qp(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_fact_lq_solve_kkt_step_dense_qp(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_solve_kkt_step_dense_qp(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_remove_lin_dep_eq(struct s_dense_qp *qp, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_restore_lin_dep_eq(struct s_dense_qp *qp, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); +// +void s_dense_qp_compute_obj(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_ipm_arg *arg, struct s_dense_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QP_KKT_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_res.h new file mode 100644 index 0000000000..06b609c537 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_res.h @@ -0,0 +1,106 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QP_RES_H_ +#define HPIPM_S_DENSE_QP_RES_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qp_res + { + struct s_dense_qp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // max of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_dense_qp_res_ws + { + struct blasfeo_svec *tmp_nbg; // work space of size nbM+ngM + struct blasfeo_svec *tmp_ns; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qp_res_memsize(struct s_dense_qp_dim *dim); +// +void s_dense_qp_res_create(struct s_dense_qp_dim *dim, struct s_dense_qp_res *res, void *mem); +// +hpipm_size_t s_dense_qp_res_ws_memsize(struct s_dense_qp_dim *dim); +// +void s_dense_qp_res_ws_create(struct s_dense_qp_dim *dim, struct s_dense_qp_res_ws *workspace, void *mem); +// +void s_dense_qp_res_compute(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_res *res, struct s_dense_qp_res_ws *ws); +// +void s_dense_qp_res_compute_lin(struct s_dense_qp *qp, struct s_dense_qp_sol *qp_sol, struct s_dense_qp_sol *qp_step, struct s_dense_qp_res *res, struct s_dense_qp_res_ws *ws); +// +void s_dense_qp_res_compute_inf_norm(struct s_dense_qp_res *res); +// +void s_dense_qp_res_get_all(struct s_dense_qp_res *res, float *res_g, float *res_ls, float *res_us, float *res_b, float *res_d_lb, float *res_d_ub, float *res_d_lg, float *res_d_ug, float *res_d_ls, float *res_d_us, float *res_m_lb, float *res_m_ub, float *res_m_lg, float *res_m_ug, float *res_m_ls, float *res_m_us); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_D_DENSE_QP_RES_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_sol.h new file mode 100644 index 0000000000..1f40076378 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_sol.h @@ -0,0 +1,94 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_DENSE_QP_SOL_H_ +#define HPIPM_S_DENSE_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_dense_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_dense_qp_sol + { + struct s_dense_qp_dim *dim; + struct blasfeo_svec *v; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + void *misc; + float obj; + int valid_obj; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_dense_qp_sol_memsize(struct s_dense_qp_dim *dim); +// +void s_dense_qp_sol_create(struct s_dense_qp_dim *dim, struct s_dense_qp_sol *qp_sol, void *memory); +// +void s_dense_qp_sol_get_all(struct s_dense_qp_sol *qp_sol, float *v, float *ls, float *us, float *pi, float *lam_lb, float *lam_ub, float *lam_lg, float *lam_ug, float *lam_ls, float *lam_us); +// +void s_dense_qp_sol_get(char *field, struct s_dense_qp_sol *sol, void *value); +// +void s_dense_qp_sol_get_v(struct s_dense_qp_sol *sol, float *v); +// +void s_dense_qp_sol_get_valid_obj(struct s_dense_qp_sol *sol, int *valid_obj); +// +void s_dense_qp_sol_get_obj(struct s_dense_qp_sol *sol, float *obj); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_DENSE_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_utils.h new file mode 100644 index 0000000000..3dd93259a5 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_dense_qp_utils.h @@ -0,0 +1,84 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_DENSE_QP_UTILS_H_ +#define HPIPM_S_DENSE_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_dense_qp_dim.h" +#include "hpipm_s_dense_qp.h" +#include "hpipm_s_dense_qp_sol.h" +#include "hpipm_s_dense_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_dense_qp_dim_print(struct s_dense_qp_dim *qp_dim); +// +//void s_dense_qp_dim_codegen(char *file_name, char *mode, struct s_dense_qp_dim *qp_dim); +// +void s_dense_qp_print(struct s_dense_qp_dim *qp_dim, struct s_dense_qp *qp); +// +//void s_dense_qp_codegen(char *file_name, char *mode, struct s_dense_qp_dim *qp_dim, struct s_dense_qp *qp); +// +void s_dense_qp_sol_print(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_sol *dense_qp_sol); +// +//void s_dense_qp_ipm_arg_codegen(char *file_name, char *mode, struct s_dense_qp_dim *qp_dim, struct s_dense_qp_ipm_arg *arg); +// +void s_dense_qp_res_print(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_res *dense_qp_res); +// +void s_dense_qp_arg_print(struct s_dense_qp_dim *qp_dim, struct s_dense_qp_ipm_arg *qp_ipm_arg); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_DENSE_QP_UTILS_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp.h new file mode 100644 index 0000000000..b90b2ac633 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp.h @@ -0,0 +1,303 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_H_ +#define HPIPM_S_OCP_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qcqp + { + struct s_ocp_qcqp_dim *dim; + struct blasfeo_smat *BAbt; // dynamics matrix & vector work space + struct blasfeo_smat *RSQrq; // hessian of cost & vector work space + struct blasfeo_smat *DCt; // inequality constraints matrix + struct blasfeo_smat **Hq; // hessians of quadratic constraints + struct blasfeo_svec *b; // dynamics vector + struct blasfeo_svec *rqz; // gradient of cost & gradient of slacks + struct blasfeo_svec *d; // inequality constraints vector + struct blasfeo_svec *d_mask; // inequality constraints mask vector + struct blasfeo_svec *m; // rhs of complementarity condition + struct blasfeo_svec *Z; // (diagonal) hessian of slacks + int **idxb; // indices of box constrained variables within [u; x] + int **idxs_rev; // index of soft constraints (reverse storage) + int **Hq_nzero; // for each int, the last 3 bits ...abc, {a,b,c}=0 => {R,S,Q}=0 + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_ocp_qcqp_strsize(); +// +hpipm_size_t s_ocp_qcqp_memsize(struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_create(struct s_ocp_qcqp_dim *dim, struct s_ocp_qcqp *qp, void *memory); +// +void s_ocp_qcqp_copy_all(struct s_ocp_qcqp *qp_orig, struct s_ocp_qcqp *qp_dest); + +// setters +// +void s_ocp_qcqp_set_all_zero(struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_rhs_zero(struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set(char *fiels_name, int stage, void *value, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_el(char *fiels_name, int stage, int index, void *value, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_A(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_B(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_b(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Q(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_S(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_R(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_q(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_r(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lb(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lb_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ub(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ub_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lbx(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lbx_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_el_lbx(int stage, int index, float *elem, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ubx(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ubx_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_el_ubx(int stage, int index, float *elem, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lbu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lbu_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ubu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ubu_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_idxb(int stage, int *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_idxbx(int stage, int *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jbx(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_idxbu(int stage, int *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jbu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_C(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_D(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lg(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lg_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ug(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_ug_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Qq(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Sq(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Rq(int stage, float *mat, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_qq(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_rq(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_uq(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_uq_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Zl(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Zu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_zl(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_zu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lls(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lls_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lus(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_lus_mask(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_idxs(int stage, int *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_idxs_rev(int stage, int *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jsbu(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jsbx(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jsg(int stage, float *vec, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_set_Jsq(int stage, float *vec, struct s_ocp_qcqp *qp); + +// getters +// +void s_ocp_qcqp_get(char *field, int stage, struct s_ocp_qcqp *qp, void *value); +// +void s_ocp_qcqp_get_A(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_B(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_b(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_Q(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_S(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_R(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_q(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_r(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ub(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ub_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lb(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lb_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lbx(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lbx_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ubx(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ubx_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lbu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lbu_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ubu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ubu_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_idxb(int stage, struct s_ocp_qcqp *qp, int *vec); +// +//void s_ocp_qcqp_get_idxbx(int stage, struct s_ocp_qcqp *qp, int *vec); +// +//void s_ocp_qcqp_get_Jbx(int stage, struct s_ocp_qcqp *qp, float *vec); +// +//void s_ocp_qcqp_get_idxbu(int stage, struct s_ocp_qcqp *qp, int *vec); +// +//void s_ocp_qcqp_get_Jbu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_C(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_D(int stage, struct s_ocp_qcqp *qp, float *mat); +// +void s_ocp_qcqp_get_lg(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lg_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ug(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_ug_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_Zl(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_Zu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_zl(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_zu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lls(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lls_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lus(int stage, struct s_ocp_qcqp *qp, float *vec); +// +void s_ocp_qcqp_get_lus_mask(int stage, struct s_ocp_qcqp *qp, float *vec); +// XXX only valid if there is one slack per softed constraint !!! +void s_ocp_qcqp_get_idxs(int stage, struct s_ocp_qcqp *qp, int *vec); +// +void s_ocp_qcqp_get_idxs_rev(int stage, struct s_ocp_qcqp *qp, int *vec); +// +//void s_ocp_qcqp_get_Jsbu(int stage, struct s_ocp_qcqp *qp, float *vec); +// +//void s_ocp_qcqp_get_Jsbx(int stage, struct s_ocp_qcqp *qp, float *vec); +// +//void s_ocp_qcqp_get_Jsg(int stage, struct s_ocp_qcqp *qp, float *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_OCP_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_dim.h new file mode 100644 index 0000000000..c09903f074 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_dim.h @@ -0,0 +1,119 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_DIM_H_ +#define HPIPM_S_OCP_QCQP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qcqp_dim + { + struct s_ocp_qp_dim *qp_dim; // dim of qp approximation + int *nx; // number of states + int *nu; // number of inputs + int *nb; // number of box constraints + int *nbx; // number of (two-sided) state box constraints + int *nbu; // number of (two-sided) input box constraints + int *ng; // number of (two-sided) general constraints + int *nq; // number of (upper) quadratic constraints + int *ns; // number of soft constraints + int *nsbx; // number of (two-sided) soft state box constraints + int *nsbu; // number of (two-sided) soft input box constraints + int *nsg; // number of (two-sided) soft general constraints + int *nsq; // number of (upper) soft quadratic constraints + int N; // horizon length + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qcqp_dim_strsize(); +// +hpipm_size_t s_ocp_qcqp_dim_memsize(int N); +// +void s_ocp_qcqp_dim_create(int N, struct s_ocp_qcqp_dim *qp_dim, void *memory); +// +void s_ocp_qcqp_dim_copy_all(struct s_ocp_qcqp_dim *dim_orig, struct s_ocp_qcqp_dim *dim_dest); +// +void s_ocp_qcqp_dim_set(char *field, int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nx(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nu(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nbx(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nbu(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_ng(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nq(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_ns(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nsbx(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nsbu(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nsg(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_set_nsq(int stage, int value, struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_dim_get(struct s_ocp_qcqp_dim *dim, char *field, int stage, int *value); +// +void s_ocp_qcqp_dim_get_N(struct s_ocp_qcqp_dim *dim, int *value); +// +void s_ocp_qcqp_dim_get_nx(struct s_ocp_qcqp_dim *dim, int stage, int *value); +// +void s_ocp_qcqp_dim_get_nu(struct s_ocp_qcqp_dim *dim, int stage, int *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_OCP_QCQP_DIM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_ipm.h new file mode 100644 index 0000000000..c14fc1c9fd --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_ipm.h @@ -0,0 +1,191 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_IPM_H_ +#define HPIPM_S_OCP_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qcqp_ipm_arg + { + struct s_ocp_qp_ipm_arg *qp_arg; + float mu0; // initial value for complementarity slackness + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constraints (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_ocp_qcqp_ipm_ws + { + struct s_ocp_qp_ipm_ws *qp_ws; + struct s_ocp_qp *qp; + struct s_ocp_qp_sol *qp_sol; + struct s_ocp_qcqp_res_ws *qcqp_res_ws; + struct s_ocp_qcqp_res *qcqp_res; + struct blasfeo_svec *tmp_nuxM; + int iter; // iteration number + int status; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qcqp_ipm_arg_strsize(); +// +hpipm_size_t s_ocp_qcqp_ipm_arg_memsize(struct s_ocp_qcqp_dim *ocp_dim); +// +void s_ocp_qcqp_ipm_arg_create(struct s_ocp_qcqp_dim *ocp_dim, struct s_ocp_qcqp_ipm_arg *arg, void *mem); +// +void s_ocp_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct s_ocp_qcqp_ipm_arg *arg); +// +void s_ocp_qcqp_ipm_arg_set(char *field, void *value, struct s_ocp_qcqp_ipm_arg *arg); +// set maximum number of iterations +void s_ocp_qcqp_ipm_arg_set_iter_max(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// set minimum step lenght +void s_ocp_qcqp_ipm_arg_set_alpha_min(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set initial value of barrier parameter +void s_ocp_qcqp_ipm_arg_set_mu0(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void s_ocp_qcqp_ipm_arg_set_tol_stat(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on equality constr +void s_ocp_qcqp_ipm_arg_set_tol_eq(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on inequality constr +void s_ocp_qcqp_ipm_arg_set_tol_ineq(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void s_ocp_qcqp_ipm_arg_set_tol_comp(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set regularization of primal variables +void s_ocp_qcqp_ipm_arg_set_reg_prim(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void s_ocp_qcqp_ipm_arg_set_warm_start(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void s_ocp_qcqp_ipm_arg_set_pred_corr(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void s_ocp_qcqp_ipm_arg_set_cond_pred_corr(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +void s_ocp_qcqp_ipm_arg_set_ric_alg(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// compute residuals after solution +void s_ocp_qcqp_ipm_arg_set_comp_res_exit(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// compute residuals of prediction +void s_ocp_qcqp_ipm_arg_set_comp_res_pred(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// min value of lam in the solution +void s_ocp_qcqp_ipm_arg_set_lam_min(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// min value of t in the solution +void s_ocp_qcqp_ipm_arg_set_t_min(float *value, struct s_ocp_qcqp_ipm_arg *arg); +// use different step for primal and dual variables +void s_ocp_qcqp_ipm_arg_set_split_step(int *value, struct s_ocp_qcqp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void s_ocp_qcqp_ipm_arg_set_t_lam_min(int *value, struct s_ocp_qcqp_ipm_arg *arg); + +// +hpipm_size_t s_ocp_qcqp_ipm_ws_strsize(); +// +hpipm_size_t s_ocp_qcqp_ipm_ws_memsize(struct s_ocp_qcqp_dim *ocp_dim, struct s_ocp_qcqp_ipm_arg *arg); +// +void s_ocp_qcqp_ipm_ws_create(struct s_ocp_qcqp_dim *ocp_dim, struct s_ocp_qcqp_ipm_arg *arg, struct s_ocp_qcqp_ipm_ws *ws, void *mem); +// +void s_ocp_qcqp_ipm_get(char *field, struct s_ocp_qcqp_ipm_ws *ws, void *value); +// +void s_ocp_qcqp_ipm_get_status(struct s_ocp_qcqp_ipm_ws *ws, int *status); +// +void s_ocp_qcqp_ipm_get_iter(struct s_ocp_qcqp_ipm_ws *ws, int *iter); +// +void s_ocp_qcqp_ipm_get_max_res_stat(struct s_ocp_qcqp_ipm_ws *ws, float *res_stat); +// +void s_ocp_qcqp_ipm_get_max_res_eq(struct s_ocp_qcqp_ipm_ws *ws, float *res_eq); +// +void s_ocp_qcqp_ipm_get_max_res_ineq(struct s_ocp_qcqp_ipm_ws *ws, float *res_ineq); +// +void s_ocp_qcqp_ipm_get_max_res_comp(struct s_ocp_qcqp_ipm_ws *ws, float *res_comp); +// +void s_ocp_qcqp_ipm_get_stat(struct s_ocp_qcqp_ipm_ws *ws, float **stat); +// +void s_ocp_qcqp_ipm_get_stat_m(struct s_ocp_qcqp_ipm_ws *ws, int *stat_m); +// +void s_ocp_qcqp_init_var(struct s_ocp_qcqp *qp, struct s_ocp_qcqp_sol *qp_sol, struct s_ocp_qcqp_ipm_arg *arg, struct s_ocp_qcqp_ipm_ws *ws); +// +void s_ocp_qcqp_ipm_solve(struct s_ocp_qcqp *qp, struct s_ocp_qcqp_sol *qp_sol, struct s_ocp_qcqp_ipm_arg *arg, struct s_ocp_qcqp_ipm_ws *ws); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_OCP_QCQP_IPM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_res.h new file mode 100644 index 0000000000..1ceeec93b7 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_res.h @@ -0,0 +1,115 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_RES_H_ +#define HPIPM_S_OCP_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qcqp_res + { + struct s_ocp_qcqp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // max of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_ocp_qcqp_res_ws + { + struct blasfeo_svec *tmp_nuxM; // work space of size nuM+nxM + struct blasfeo_svec *tmp_nbgqM; // work space of size nbM+ngM+nqM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + struct blasfeo_svec *q_fun; // value for evaluation of quadr constr + struct blasfeo_svec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qcqp_res_memsize(struct s_ocp_qcqp_dim *ocp_dim); +// +void s_ocp_qcqp_res_create(struct s_ocp_qcqp_dim *ocp_dim, struct s_ocp_qcqp_res *res, void *mem); +// +hpipm_size_t s_ocp_qcqp_res_ws_memsize(struct s_ocp_qcqp_dim *ocp_dim); +// +void s_ocp_qcqp_res_ws_create(struct s_ocp_qcqp_dim *ocp_dim, struct s_ocp_qcqp_res_ws *workspace, void *mem); +// +void s_ocp_qcqp_res_compute(struct s_ocp_qcqp *qp, struct s_ocp_qcqp_sol *qp_sol, struct s_ocp_qcqp_res *res, struct s_ocp_qcqp_res_ws *ws); +// +void s_ocp_qcqp_res_compute_inf_norm(struct s_ocp_qcqp_res *res); +// +void s_ocp_qcqp_res_get_max_res_stat(struct s_ocp_qcqp_res *res, float *value); +// +void s_ocp_qcqp_res_get_max_res_eq(struct s_ocp_qcqp_res *res, float *value); +// +void s_ocp_qcqp_res_get_max_res_ineq(struct s_ocp_qcqp_res *res, float *value); +// +void s_ocp_qcqp_res_get_max_res_comp(struct s_ocp_qcqp_res *res, float *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_OCP_QCQP_RES_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_sol.h new file mode 100644 index 0000000000..3d58022cc9 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_sol.h @@ -0,0 +1,115 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_SOL_H_ +#define HPIPM_S_OCP_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qcqp_sol + { + struct s_ocp_qcqp_dim *dim; + struct blasfeo_svec *ux; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_ocp_qcqp_sol_strsize(); +// +hpipm_size_t s_ocp_qcqp_sol_memsize(struct s_ocp_qcqp_dim *dim); +// +void s_ocp_qcqp_sol_create(struct s_ocp_qcqp_dim *dim, struct s_ocp_qcqp_sol *qp_sol, void *memory); +// +void s_ocp_qcqp_sol_copy_all(struct s_ocp_qcqp_sol *qp_sol_orig, struct s_ocp_qcqp_sol *qp_sol_dest); +// +void s_ocp_qcqp_sol_get(char *field, int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_u(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_x(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_sl(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_su(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_pi(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_lam_lb(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_lam_ub(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_lam_lg(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_get_lam_ug(int stage, struct s_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_ocp_qcqp_sol_set(char *field, int stage, float *vec, struct s_ocp_qcqp_sol *qp_sol); +// +void s_ocp_qcqp_sol_set_u(int stage, float *vec, struct s_ocp_qcqp_sol *qp_sol); +// +void s_ocp_qcqp_sol_set_x(int stage, float *vec, struct s_ocp_qcqp_sol *qp_sol); +// +void s_ocp_qcqp_sol_set_sl(int stage, float *vec, struct s_ocp_qcqp_sol *qp_sol); +// +void s_ocp_qcqp_sol_set_su(int stage, float *vec, struct s_ocp_qcqp_sol *qp_sol); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_OCP_QCQP_SOL_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_utils.h new file mode 100644 index 0000000000..d64e3aabe7 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qcqp_utils.h @@ -0,0 +1,82 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QCQP_UTILS_H_ +#define HPIPM_S_OCP_QCQP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qcqp_dim.h" +#include "hpipm_s_ocp_qp.h" +#include "hpipm_s_ocp_qcqp_sol.h" +#include "hpipm_s_ocp_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_ocp_qcqp_dim_print(struct s_ocp_qcqp_dim *qcqp_dim); +// +void s_ocp_qcqp_dim_codegen(char *file_name, char *mode, struct s_ocp_qcqp_dim *qcqp_dim); +// +void s_ocp_qcqp_print(struct s_ocp_qcqp_dim *qcqp_dim, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_codegen(char *file_name, char *mode, struct s_ocp_qcqp_dim *qcqp_dim, struct s_ocp_qcqp *qp); +// +void s_ocp_qcqp_sol_print(struct s_ocp_qcqp_dim *qcqp_dim, struct s_ocp_qcqp_sol *ocp_qcqp_sol); +// +void s_ocp_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct s_ocp_qcqp_dim *qcqp_dim, struct s_ocp_qcqp_ipm_arg *arg); +// +void s_ocp_qcqp_res_print(struct s_ocp_qcqp_dim *qcqp_dim, struct s_ocp_qcqp_res *ocp_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_OCP_QCQP_UTILS_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp.h new file mode 100644 index 0000000000..b49191f192 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp.h @@ -0,0 +1,306 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_H_ +#define HPIPM_S_OCP_QP_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp + { + struct s_ocp_qp_dim *dim; + struct blasfeo_smat *BAbt; // dynamics matrix & vector work space + struct blasfeo_smat *RSQrq; // hessian of cost & vector work space + struct blasfeo_smat *DCt; // inequality constraints matrix + struct blasfeo_svec *b; // dynamics vector + struct blasfeo_svec *rqz; // gradient of cost & gradient of slacks + struct blasfeo_svec *d; // inequality constraints vector + struct blasfeo_svec *d_mask; // inequality constraints mask vector + struct blasfeo_svec *m; // rhs of complementarity condition + struct blasfeo_svec *Z; // (diagonal) hessian of slacks + int **idxb; // indices of box constrained variables within [u; x] + int **idxs_rev; // index of soft constraints (reverse storage) + int **idxe; // indices of constraints within [bu, bx, g] that are equalities, subset of [0, ..., nbu+nbx+ng-1] + int *diag_H_flag; // flag the fact that Hessian is diagonal + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_ocp_qp_strsize(); +// +hpipm_size_t s_ocp_qp_memsize(struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_create(struct s_ocp_qp_dim *dim, struct s_ocp_qp *qp, void *memory); +// +void s_ocp_qp_copy_all(struct s_ocp_qp *qp_orig, struct s_ocp_qp *qp_dest); + +// setters +// +void s_ocp_qp_set_all_zero(struct s_ocp_qp *qp); +// +void s_ocp_qp_set_rhs_zero(struct s_ocp_qp *qp); +// +void s_ocp_qp_set_all(float **A, float **B, float **b, float **Q, float **S, float **R, float **q, float **r, int **idxbx, float **lbx, float **ubx, int **idxbu, float **lbu, float **ubu, float **C, float **D, float **lg, float **ug, float **Zl, float **Zu, float **zl, float **zu, int **idxs, float **ls, float **us, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_all_rowmaj(float **A, float **B, float **b, float **Q, float **S, float **R, float **q, float **r, int **idxbx, float **lbx, float **ubx, int **idxbu, float **lbu, float **ubu, float **C, float **D, float **lg, float **ug, float **Zl, float **Zu, float **zl, float **zu, int **idxs, float **ls, float **us, struct s_ocp_qp *qp); +// +void s_ocp_qp_set(char *fiels_name, int stage, void *value, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_el(char *fiels_name, int stage, int index, void *value, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_A(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_B(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_b(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Q(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_S(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_R(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_q(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_r(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lb(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lb_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ub(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ub_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lbx(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lbx_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_el_lbx(int stage, int index, float *elem, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ubx(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ubx_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_el_ubx(int stage, int index, float *elem, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lbu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lbu_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ubu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ubu_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxb(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxbx(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jbx(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxbu(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jbu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_C(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_D(int stage, float *mat, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lg(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lg_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ug(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_ug_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Zl(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Zu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_zl(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_zu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lls(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lls_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lus(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_lus_mask(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxs(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxs_rev(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jsbu(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jsbx(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jsg(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxe(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxbxe(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxbue(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_idxge(int stage, int *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jbxe(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jbue(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_Jge(int stage, float *vec, struct s_ocp_qp *qp); +// +void s_ocp_qp_set_diag_H_flag(int stage, int *value, struct s_ocp_qp *qp); + +// getters +// +void s_ocp_qp_get(char *field, int stage, struct s_ocp_qp *qp, void *value); +// +void s_ocp_qp_get_A(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_B(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_b(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_Q(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_S(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_R(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_q(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_r(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ub(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ub_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lb(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lb_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lbx(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lbx_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ubx(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ubx_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lbu(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lbu_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ubu(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ubu_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_idxb(int stage, struct s_ocp_qp *qp, int *vec); +// +//void s_ocp_qp_get_idxbx(int stage, struct s_ocp_qp *qp, int *vec); +// +//void s_ocp_qp_get_Jbx(int stage, struct s_ocp_qp *qp, float *vec); +// +//void s_ocp_qp_get_idxbu(int stage, struct s_ocp_qp *qp, int *vec); +// +//void s_ocp_qp_get_Jbu(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_C(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_D(int stage, struct s_ocp_qp *qp, float *mat); +// +void s_ocp_qp_get_lg(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lg_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ug(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_ug_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_Zl(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_Zu(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_zl(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_zu(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lls(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lls_mask(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lus(int stage, struct s_ocp_qp *qp, float *vec); +// +void s_ocp_qp_get_lus_mask(int stage, struct s_ocp_qp *qp, float *vec); +// XXX only valid if there is one slack per softed constraint !!! +void s_ocp_qp_get_idxs(int stage, struct s_ocp_qp *qp, int *vec); +// +void s_ocp_qp_get_idxs_rev(int stage, struct s_ocp_qp *qp, int *vec); +// +//void s_ocp_qp_get_Jsbu(int stage, struct s_ocp_qp *qp, float *vec); +// +//void s_ocp_qp_get_Jsbx(int stage, struct s_ocp_qp *qp, float *vec); +// +//void s_ocp_qp_get_Jsg(int stage, struct s_ocp_qp *qp, float *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_OCP_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_dim.h new file mode 100644 index 0000000000..bce80243b3 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_dim.h @@ -0,0 +1,141 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_DIM_H_ +#define HPIPM_S_OCP_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp_dim + { + int *nx; // number of states + int *nu; // number of inputs + int *nb; // number of box constraints + int *nbx; // number of state box constraints + int *nbu; // number of input box constraints + int *ng; // number of general constraints + int *ns; // number of soft constraints + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int *nbxe; // number of state box constraints which are equality + int *nbue; // number of input box constraints which are equality + int *nge; // number of general constraints which are equality + int N; // horizon length + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qp_dim_strsize(); +// +hpipm_size_t s_ocp_qp_dim_memsize(int N); +// +void s_ocp_qp_dim_create(int N, struct s_ocp_qp_dim *qp_dim, void *memory); +// +void s_ocp_qp_dim_copy_all(struct s_ocp_qp_dim *dim_orig, struct s_ocp_qp_dim *dim_dest); +// +void s_ocp_qp_dim_set_all(int *nx, int *nu, int *nbx, int *nbu, int *ng, int *nsbx, int *nsbu, int *nsg, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set(char *field, int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nx(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nu(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nbx(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nbu(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_ng(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_ns(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nsbx(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nsbu(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nsg(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nbxe(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nbue(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_set_nge(int stage, int value, struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_dim_get(struct s_ocp_qp_dim *dim, char *field, int stage, int *value); +// +void s_ocp_qp_dim_get_N(struct s_ocp_qp_dim *dim, int *value); +// +void s_ocp_qp_dim_get_nx(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nu(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nbx(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nbu(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_ng(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_ns(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nsbx(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nsbu(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nsg(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nbxe(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nbue(struct s_ocp_qp_dim *dim, int stage, int *value); +// +void s_ocp_qp_dim_get_nge(struct s_ocp_qp_dim *dim, int stage, int *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_OCP_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_ipm.h new file mode 100644 index 0000000000..11f3c47be6 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_ipm.h @@ -0,0 +1,250 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_IPM_H_ +#define HPIPM_S_OCP_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp_ipm_arg + { + float mu0; // initial value for complementarity slackness + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + float tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constraints (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) + int comp_res_pred; // compute residuals of prediction + int split_step; // use different steps for primal and dual variables + int var_init_scheme; // variables initialization scheme + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_ocp_qp_ipm_ws + { + struct s_core_qp_ipm_workspace *core_workspace; + struct s_ocp_qp_res_ws *res_workspace; + struct s_ocp_qp_dim *dim; // cache dim + struct s_ocp_qp_sol *sol_step; + struct s_ocp_qp_sol *sol_itref; + struct s_ocp_qp *qp_step; + struct s_ocp_qp *qp_itref; + struct s_ocp_qp_res *res; + struct s_ocp_qp_res *res_itref; + struct blasfeo_svec *Gamma; // hessian update + struct blasfeo_svec *gamma; // hessian update + struct blasfeo_svec *tmp_nuxM; // work space of size nxM + struct blasfeo_svec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + struct blasfeo_svec *Pb; // Pb + struct blasfeo_svec *Zs_inv; + struct blasfeo_svec *tmp_m; + struct blasfeo_svec *l; // cache linear part for _get_ric_xxx + struct blasfeo_smat *L; + struct blasfeo_smat *Ls; + struct blasfeo_smat *P; + struct blasfeo_smat *Lh; + struct blasfeo_smat *AL; + struct blasfeo_smat *lq0; + struct blasfeo_smat *tmp_nxM_nxM; + float *stat; // convergence statistics + int *use_hess_fact; + void *lq_work0; + float qp_res[4]; // infinity norm of residuals + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // number of recorded stat per IPM iter + int use_Pb; + int status; // solver status + int square_root_alg; // cache from arg + int lq_fact; // cache from arg + int mask_constr; // use constr mask + int valid_ric_vec; // meaningful riccati vectors + int valid_ric_p; // form of riccati p: 0 p*inv(L), 1 p + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qp_ipm_arg_strsize(); +// +hpipm_size_t s_ocp_qp_ipm_arg_memsize(struct s_ocp_qp_dim *ocp_dim); +// +void s_ocp_qp_ipm_arg_create(struct s_ocp_qp_dim *ocp_dim, struct s_ocp_qp_ipm_arg *arg, void *mem); +// +void s_ocp_qp_ipm_arg_set_default(enum hpipm_mode mode, struct s_ocp_qp_ipm_arg *arg); +// +void s_ocp_qp_ipm_arg_set(char *field, void *value, struct s_ocp_qp_ipm_arg *arg); +// set maximum number of iterations +void s_ocp_qp_ipm_arg_set_iter_max(int *iter_max, struct s_ocp_qp_ipm_arg *arg); +// set minimum step lenght +void s_ocp_qp_ipm_arg_set_alpha_min(float *alpha_min, struct s_ocp_qp_ipm_arg *arg); +// set initial value of barrier parameter +void s_ocp_qp_ipm_arg_set_mu0(float *mu0, struct s_ocp_qp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void s_ocp_qp_ipm_arg_set_tol_stat(float *tol_stat, struct s_ocp_qp_ipm_arg *arg); +// set exit tolerance on equality constr +void s_ocp_qp_ipm_arg_set_tol_eq(float *tol_eq, struct s_ocp_qp_ipm_arg *arg); +// set exit tolerance on inequality constr +void s_ocp_qp_ipm_arg_set_tol_ineq(float *tol_ineq, struct s_ocp_qp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void s_ocp_qp_ipm_arg_set_tol_comp(float *tol_comp, struct s_ocp_qp_ipm_arg *arg); +// set regularization of primal variables +void s_ocp_qp_ipm_arg_set_reg_prim(float *tol_comp, struct s_ocp_qp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void s_ocp_qp_ipm_arg_set_warm_start(int *warm_start, struct s_ocp_qp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void s_ocp_qp_ipm_arg_set_pred_corr(int *pred_corr, struct s_ocp_qp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void s_ocp_qp_ipm_arg_set_cond_pred_corr(int *value, struct s_ocp_qp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +void s_ocp_qp_ipm_arg_set_ric_alg(int *alg, struct s_ocp_qp_ipm_arg *arg); +// dual solution of equality constraints (only for abs_form==1) +void s_ocp_qp_ipm_arg_set_comp_dual_sol_eq(int *value, struct s_ocp_qp_ipm_arg *arg); +// compute residuals after solution +void s_ocp_qp_ipm_arg_set_comp_res_exit(int *value, struct s_ocp_qp_ipm_arg *arg); +// compute residuals of prediction +void s_ocp_qp_ipm_arg_set_comp_res_pred(int *alg, struct s_ocp_qp_ipm_arg *arg); +// min value of lam in the solution +void s_ocp_qp_ipm_arg_set_lam_min(float *value, struct s_ocp_qp_ipm_arg *arg); +// min value of t in the solution +void s_ocp_qp_ipm_arg_set_t_min(float *value, struct s_ocp_qp_ipm_arg *arg); +// min value of tau in the solution +void s_ocp_qp_ipm_arg_set_tau_min(float *value, struct s_ocp_qp_ipm_arg *arg); +// set split step: 0 same step, 1 different step for primal and dual variables +void s_ocp_qp_ipm_arg_set_split_step(int *value, struct s_ocp_qp_ipm_arg *arg); +// variables initialization scheme +void s_ocp_qp_ipm_arg_set_var_init_scheme(int *value, struct s_ocp_qp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void s_ocp_qp_ipm_arg_set_t_lam_min(int *value, struct s_ocp_qp_ipm_arg *arg); + +// +hpipm_size_t s_ocp_qp_ipm_ws_strsize(); +// +hpipm_size_t s_ocp_qp_ipm_ws_memsize(struct s_ocp_qp_dim *ocp_dim, struct s_ocp_qp_ipm_arg *arg); +// +void s_ocp_qp_ipm_ws_create(struct s_ocp_qp_dim *ocp_dim, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, void *mem); +// +void s_ocp_qp_ipm_get(char *field, struct s_ocp_qp_ipm_ws *ws, void *value); +// +void s_ocp_qp_ipm_get_status(struct s_ocp_qp_ipm_ws *ws, int *status); +// +void s_ocp_qp_ipm_get_iter(struct s_ocp_qp_ipm_ws *ws, int *iter); +// +void s_ocp_qp_ipm_get_max_res_stat(struct s_ocp_qp_ipm_ws *ws, float *res_stat); +// +void s_ocp_qp_ipm_get_max_res_eq(struct s_ocp_qp_ipm_ws *ws, float *res_eq); +// +void s_ocp_qp_ipm_get_max_res_ineq(struct s_ocp_qp_ipm_ws *ws, float *res_ineq); +// +void s_ocp_qp_ipm_get_max_res_comp(struct s_ocp_qp_ipm_ws *ws, float *res_comp); +// +void s_ocp_qp_ipm_get_stat(struct s_ocp_qp_ipm_ws *ws, float **stat); +// +void s_ocp_qp_ipm_get_stat_m(struct s_ocp_qp_ipm_ws *ws, int *stat_m); +// +void s_ocp_qp_ipm_get_ric_Lr(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *Lr); +// +void s_ocp_qp_ipm_get_ric_Ls(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *Ls); +// +void s_ocp_qp_ipm_get_ric_P(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *P); +// +void s_ocp_qp_ipm_get_ric_lr(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *lr); +// +void s_ocp_qp_ipm_get_ric_p(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *p); +// feedback control gain in the form u = K x + k +void s_ocp_qp_ipm_get_ric_K(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *K); +// feedback control gain in the form u = K x + k +void s_ocp_qp_ipm_get_ric_k(struct s_ocp_qp *qp, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws, int stage, float *k); +// +void s_ocp_qp_init_var(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_ipm_abs_step(int kk, struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_ipm_delta_step(int kk, struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_ipm_solve(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_ipm_predict(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_ipm_sens(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_OCP_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_kkt.h new file mode 100644 index 0000000000..3eb1f4aae8 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_kkt.h @@ -0,0 +1,66 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + +#ifndef HPIPM_S_OCP_QP_KKT_H_ +#define HPIPM_S_OCP_QP_KKT_H_ + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + +// +void s_ocp_qp_fact_solve_kkt_unconstr(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_fact_solve_kkt_step(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_fact_lq_solve_kkt_step(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); +// +void s_ocp_qp_solve_kkt_step(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_ipm_arg *arg, struct s_ocp_qp_ipm_ws *ws); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif // HPIPM_S_OCP_QP_KKT_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_red.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_red.h new file mode 100644 index 0000000000..5a7b3b0703 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_red.h @@ -0,0 +1,118 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_RED_H_ +#define HPIPM_S_OCP_QP_RED_H_ + + + +#include +#include + +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp_reduce_eq_dof_arg + { + float lam_min; + float t_min; + int alias_unchanged; // do not keep copy unchanged stage + int comp_prim_sol; // primal solution (v) + int comp_dual_sol_eq; // dual solution equality constr (pi) + int comp_dual_sol_ineq; // dual solution inequality constr (lam t) + hpipm_size_t memsize; // memory size in bytes + }; + + + +struct s_ocp_qp_reduce_eq_dof_ws + { + struct blasfeo_svec *tmp_nuxM; + struct blasfeo_svec *tmp_nbgM; + int *e_imask_ux; + int *e_imask_d; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +void s_ocp_qp_dim_reduce_eq_dof(struct s_ocp_qp_dim *dim, struct s_ocp_qp_dim *dim_red); +// +hpipm_size_t s_ocp_qp_reduce_eq_dof_arg_memsize(); +// +void s_ocp_qp_reduce_eq_dof_arg_create(struct s_ocp_qp_reduce_eq_dof_arg *arg, void *mem); +// +void s_ocp_qp_reduce_eq_dof_arg_set_default(struct s_ocp_qp_reduce_eq_dof_arg *arg); +// +void s_ocp_qp_reduce_eq_dof_arg_set_alias_unchanged(struct s_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void s_ocp_qp_reduce_eq_dof_arg_set_comp_prim_sol(struct s_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void s_ocp_qp_reduce_eq_dof_arg_set_comp_dual_sol_eq(struct s_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +void s_ocp_qp_reduce_eq_dof_arg_set_comp_dual_sol_ineq(struct s_ocp_qp_reduce_eq_dof_arg *arg, int value); +// +hpipm_size_t s_ocp_qp_reduce_eq_dof_ws_memsize(struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_reduce_eq_dof_ws_create(struct s_ocp_qp_dim *dim, struct s_ocp_qp_reduce_eq_dof_ws *work, void *mem); +// +void s_ocp_qp_reduce_eq_dof(struct s_ocp_qp *qp, struct s_ocp_qp *qp_red, struct s_ocp_qp_reduce_eq_dof_arg *arg, struct s_ocp_qp_reduce_eq_dof_ws *work); +// +void s_ocp_qp_reduce_eq_dof_lhs(struct s_ocp_qp *qp, struct s_ocp_qp *qp_red, struct s_ocp_qp_reduce_eq_dof_arg *arg, struct s_ocp_qp_reduce_eq_dof_ws *work); +// +void s_ocp_qp_reduce_eq_dof_rhs(struct s_ocp_qp *qp, struct s_ocp_qp *qp_red, struct s_ocp_qp_reduce_eq_dof_arg *arg, struct s_ocp_qp_reduce_eq_dof_ws *work); +// +void s_ocp_qp_restore_eq_dof(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol_red, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_reduce_eq_dof_arg *arg, struct s_ocp_qp_reduce_eq_dof_ws *work); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_OCP_QP_RED_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_res.h new file mode 100644 index 0000000000..821585da65 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_res.h @@ -0,0 +1,114 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_RES_H_ +#define HPIPM_S_OCP_QP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp_res + { + struct s_ocp_qp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // max of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_ocp_qp_res_ws + { + struct blasfeo_svec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_ocp_qp_res_memsize(struct s_ocp_qp_dim *ocp_dim); +// +void s_ocp_qp_res_create(struct s_ocp_qp_dim *ocp_dim, struct s_ocp_qp_res *res, void *mem); +// +hpipm_size_t s_ocp_qp_res_ws_memsize(struct s_ocp_qp_dim *ocp_dim); +// +void s_ocp_qp_res_ws_create(struct s_ocp_qp_dim *ocp_dim, struct s_ocp_qp_res_ws *workspace, void *mem); +// +void s_ocp_qp_res_compute(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_res *res, struct s_ocp_qp_res_ws *ws); +// +void s_ocp_qp_res_compute_lin(struct s_ocp_qp *qp, struct s_ocp_qp_sol *qp_sol, struct s_ocp_qp_sol *qp_step, struct s_ocp_qp_res *res, struct s_ocp_qp_res_ws *ws); +// +void s_ocp_qp_res_compute_inf_norm(struct s_ocp_qp_res *res); +// +void s_ocp_qp_res_get_all(struct s_ocp_qp_res *res, float **res_r, float **res_q, float **res_ls, float **res_us, float **res_b, float **res_d_lb, float **res_d_ub, float **res_d_lg, float **res_d_ug, float **res_d_ls, float **res_d_us, float **res_m_lb, float **res_m_ub, float **res_m_lg, float **res_m_ug, float **res_m_ls, float **res_m_us); +// +void s_ocp_qp_res_get_max_res_stat(struct s_ocp_qp_res *res, float *value); +// +void s_ocp_qp_res_get_max_res_eq(struct s_ocp_qp_res *res, float *value); +// +void s_ocp_qp_res_get_max_res_ineq(struct s_ocp_qp_res *res, float *value); +// +void s_ocp_qp_res_get_max_res_comp(struct s_ocp_qp_res *res, float *value); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_OCP_QP_RES_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_sol.h new file mode 100644 index 0000000000..94dfa0d003 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_sol.h @@ -0,0 +1,128 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_SOL_H_ +#define HPIPM_S_OCP_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_ocp_qp_sol + { + struct s_ocp_qp_dim *dim; + struct blasfeo_svec *ux; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_ocp_qp_sol_strsize(); +// +hpipm_size_t s_ocp_qp_sol_memsize(struct s_ocp_qp_dim *dim); +// +void s_ocp_qp_sol_create(struct s_ocp_qp_dim *dim, struct s_ocp_qp_sol *qp_sol, void *memory); +// +void s_ocp_qp_sol_copy_all(struct s_ocp_qp_sol *qp_sol_orig, struct s_ocp_qp_sol *qp_sol_dest); +// +void s_qp_sol_get_all(struct s_ocp_qp_sol *qp_sol, float **u, float **x, float **ls, float **us, float **pi, float **lam_lb, float **lam_ub, float **lam_lg, float **lam_ug, float **lam_ls, float **lam_us); +// +void s_qp_sol_get_all_rowmaj(struct s_ocp_qp_sol *qp_sol, float **u, float **x, float **ls, float **us, float **pi, float **lam_lb, float **lam_ub, float **lam_lg, float **lam_ug, float **lam_ls, float **lam_us); +// +void s_ocp_qp_sol_set_all(float **u, float **x, float **ls, float **us, float **pi, float **lam_lb, float **lam_ub, float **lam_lg, float **lam_ug, float **lam_ls, float **lam_us, struct s_ocp_qp_sol *qp_sol); +// +void s_ocp_qp_sol_get(char *field, int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_u(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_x(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_sl(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_su(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_pi(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_lb(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_lbu(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_lbx(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_ub(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_ubu(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_ubx(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_lg(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_get_lam_ug(int stage, struct s_ocp_qp_sol *qp_sol, float *vec); +// +void s_ocp_qp_sol_set(char *field, int stage, float *vec, struct s_ocp_qp_sol *qp_sol); +// +void s_ocp_qp_sol_set_u(int stage, float *vec, struct s_ocp_qp_sol *qp_sol); +// +void s_ocp_qp_sol_set_x(int stage, float *vec, struct s_ocp_qp_sol *qp_sol); +// +void s_ocp_qp_sol_set_sl(int stage, float *vec, struct s_ocp_qp_sol *qp_sol); +// +void s_ocp_qp_sol_set_su(int stage, float *vec, struct s_ocp_qp_sol *qp_sol); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_OCP_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_utils.h new file mode 100644 index 0000000000..a4f832a5ea --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_ocp_qp_utils.h @@ -0,0 +1,83 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_OCP_QP_UTILS_H_ +#define HPIPM_S_OCP_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_ocp_qp_dim.h" +#include "hpipm_s_ocp_qp.h" +#include "hpipm_s_ocp_qp_sol.h" +#include "hpipm_s_ocp_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_ocp_qp_dim_print(struct s_ocp_qp_dim *qp_dim); +// +void s_ocp_qp_dim_codegen(char *file_name, char *mode, struct s_ocp_qp_dim *qp_dim); +// +void s_ocp_qp_print(struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp *qp); +// +void s_ocp_qp_codegen(char *file_name, char *mode, struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp *qp); +// +void s_ocp_qp_sol_print(struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp_sol *ocp_qp_sol); +// +void s_ocp_qp_ipm_arg_print(struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp_ipm_arg *arg); +// +void s_ocp_qp_ipm_arg_codegen(char *file_name, char *mode, struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp_ipm_arg *arg); +// +void s_ocp_qp_res_print(struct s_ocp_qp_dim *qp_dim, struct s_ocp_qp_res *ocp_qp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_D_OCP_QP_UTILS_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond.h b/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond.h new file mode 100644 index 0000000000..e40511e69f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond.h @@ -0,0 +1,115 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_PART_COND_H_ +#define HPIPM_S_PART_COND_H_ + + + +#include +#include + +#include "hpipm_s_cond.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_part_cond_qp_arg + { + struct s_cond_qp_arg *cond_arg; + int N2; + hpipm_size_t memsize; + }; + + + +struct s_part_cond_qp_ws + { + struct s_cond_qp_ws *cond_workspace; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_part_cond_qp_arg_memsize(int N2); +// +void s_part_cond_qp_arg_create(int N2, struct s_part_cond_qp_arg *cond_arg, void *mem); +// +void s_part_cond_qp_arg_set_default(struct s_part_cond_qp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 squre-root +void s_part_cond_qp_arg_set_ric_alg(int ric_alg, struct s_part_cond_qp_arg *cond_arg); +// +void s_part_cond_qp_arg_set_comp_prim_sol(int value, struct s_part_cond_qp_arg *cond_arg); +// +void s_part_cond_qp_arg_set_comp_dual_sol_eq(int value, struct s_part_cond_qp_arg *cond_arg); +// +void s_part_cond_qp_arg_set_comp_dual_sol_ineq(int value, struct s_part_cond_qp_arg *cond_arg); + +// +void s_part_cond_qp_compute_block_size(int N, int N2, int *block_size); +// +void s_part_cond_qp_compute_dim(struct s_ocp_qp_dim *ocp_dim, int *block_size, struct s_ocp_qp_dim *part_dense_dim); +// +hpipm_size_t s_part_cond_qp_ws_memsize(struct s_ocp_qp_dim *ocp_dim, int *block_size, struct s_ocp_qp_dim *part_dense_dim, struct s_part_cond_qp_arg *cond_arg); +// +void s_part_cond_qp_ws_create(struct s_ocp_qp_dim *ocp_dim, int *block_size, struct s_ocp_qp_dim *part_dense_dim, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws, void *mem); +// +void s_part_cond_qp_cond(struct s_ocp_qp *ocp_qp, struct s_ocp_qp *part_dense_qp, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws); +// +void s_part_cond_qp_cond_lhs(struct s_ocp_qp *ocp_qp, struct s_ocp_qp *part_dense_qp, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws); +// +void s_part_cond_qp_cond_rhs(struct s_ocp_qp *ocp_qp, struct s_ocp_qp *part_dense_qp, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws); +// +void s_part_cond_qp_expand_sol(struct s_ocp_qp *ocp_qp, struct s_ocp_qp *part_dense_qp, struct s_ocp_qp_sol *part_dense_qp_sol, struct s_ocp_qp_sol *ocp_qp_sol, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws); + +// +void s_part_cond_qp_update(int *idxc, struct s_ocp_qp *ocp_qp, struct s_ocp_qp *part_dense_qp, struct s_part_cond_qp_arg *cond_arg, struct s_part_cond_qp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_PART_COND_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond_qcqp.h new file mode 100644 index 0000000000..311f7000bf --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_part_cond_qcqp.h @@ -0,0 +1,107 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_PART_COND_QCQP_H_ +#define HPIPM_S_PART_COND_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_cond_qcqp.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_part_cond_qcqp_arg + { + struct s_cond_qcqp_arg *cond_arg; + int N2; + hpipm_size_t memsize; + }; + + + +struct s_part_cond_qcqp_ws + { + struct s_cond_qcqp_ws *cond_ws; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_part_cond_qcqp_arg_memsize(int N2); +// +void s_part_cond_qcqp_arg_create(int N2, struct s_part_cond_qcqp_arg *cond_arg, void *mem); +// +void s_part_cond_qcqp_arg_set_default(struct s_part_cond_qcqp_arg *cond_arg); +// set riccati-like algorithm: 0 classical, 1 squre-root +void s_part_cond_qcqp_arg_set_ric_alg(int ric_alg, struct s_part_cond_qcqp_arg *cond_arg); + +// +void s_part_cond_qcqp_compute_block_size(int N, int N2, int *block_size); +// +void s_part_cond_qcqp_compute_dim(struct s_ocp_qcqp_dim *ocp_dim, int *block_size, struct s_ocp_qcqp_dim *part_dense_dim); +// +hpipm_size_t s_part_cond_qcqp_ws_memsize(struct s_ocp_qcqp_dim *ocp_dim, int *block_size, struct s_ocp_qcqp_dim *part_dense_dim, struct s_part_cond_qcqp_arg *cond_arg); +// +void s_part_cond_qcqp_ws_create(struct s_ocp_qcqp_dim *ocp_dim, int *block_size, struct s_ocp_qcqp_dim *part_dense_dim, struct s_part_cond_qcqp_arg *cond_arg, struct s_part_cond_qcqp_ws *cond_ws, void *mem); +// +void s_part_cond_qcqp_cond(struct s_ocp_qcqp *ocp_qp, struct s_ocp_qcqp *part_dense_qp, struct s_part_cond_qcqp_arg *cond_arg, struct s_part_cond_qcqp_ws *cond_ws); +// +void s_part_cond_qcqp_cond_lhs(struct s_ocp_qcqp *ocp_qp, struct s_ocp_qcqp *part_dense_qp, struct s_part_cond_qcqp_arg *cond_arg, struct s_part_cond_qcqp_ws *cond_ws); +// +void s_part_cond_qcqp_cond_rhs(struct s_ocp_qcqp *ocp_qp, struct s_ocp_qcqp *part_dense_qp, struct s_part_cond_qcqp_arg *cond_arg, struct s_part_cond_qcqp_ws *cond_ws); +// +void s_part_cond_qcqp_expand_sol(struct s_ocp_qcqp *ocp_qp, struct s_ocp_qcqp *part_dense_qp, struct s_ocp_qcqp_sol *part_dense_qp_sol, struct s_ocp_qcqp_sol *ocp_qp_sol, struct s_part_cond_qcqp_arg *cond_arg, struct s_part_cond_qcqp_ws *cond_ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_D_PART_COND_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_sim_erk.h b/phonelibs/acados/include/hpipm/include/hpipm_s_sim_erk.h new file mode 100644 index 0000000000..1a05ad1a28 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_sim_erk.h @@ -0,0 +1,121 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_SIM_ERK_H_ +#define HPIPM_S_SIM_ERK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct s_sim_erk_arg + { + struct s_sim_rk_data *rk_data; // integrator data + float h; // step size + int steps; // number of steps +// int for_sens; // compute adjoint sensitivities +// int adj_sens; // compute adjoint sensitivities + hpipm_size_t memsize; + }; + + + +struct s_sim_erk_ws + { + void (*ode)(int t, float *x, float *p, void *ode_args, float *xdot); // function pointer to ode + void (*vde_for)(int t, float *x, float *p, void *ode_args, float *xdot); // function pointer to forward vde + void (*vde_adj)(int t, float *adj_in, void *ode_args, float *adj_out); // function pointer to adjoint vde + void *ode_args; // pointer to ode args + struct s_sim_erk_arg *erk_arg; // erk arg + float *K; // internal variables + float *x_for; // states and forward sensitivities + float *x_traj; // states at all steps + float *l; // adjoint sensitivities + float *p; // parameter + float *x_tmp; // temporary states and forward sensitivities + float *adj_in; + float *adj_tmp; + int nx; // number of states + int np; // number of parameters + int nf; // number of forward sensitivities + int na; // number of adjoint sensitivities + int nf_max; // max number of forward sensitivities + int na_max; // max number of adjoint sensitivities + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_sim_erk_arg_memsize(); +// +void s_sim_erk_arg_create(struct s_sim_erk_arg *erk_arg, void *mem); +// +void s_sim_erk_arg_set_all(struct s_sim_rk_data *rk_data, float h, int steps, struct s_sim_erk_arg *erk_arg); + +// +hpipm_size_t s_sim_erk_ws_memsize(struct s_sim_erk_arg *erk_arg, int nx, int np, int nf_max, int na_max); +// +void s_sim_erk_ws_create(struct s_sim_erk_arg *erk_arg, int nx, int np, int nf_max, int na_max, struct s_sim_erk_ws *work, void *memory); +// +void s_sim_erk_ws_set_all(int nf, int na, float *x, float *fs, float *bs, float *p, void (*ode)(int t, float *x, float *p, void *ode_args, float *xdot), void (*vde_for)(int t, float *x, float *p, void *ode_args, float *xdot), void (*vde_adj)(int t, float *adj_in, void *ode_args, float *adj_out), void *ode_args, struct s_sim_erk_ws *work); +// number of directions for forward sensitivities +void s_sim_erk_ws_set_nf(int *nf, struct s_sim_erk_ws *work); +// parameters (e.g. inputs) +void s_sim_erk_ws_set_p(float *p, struct s_sim_erk_ws *work); +// state +void s_sim_erk_ws_set_x(float *x, struct s_sim_erk_ws *work); +// forward sensitivities +void s_sim_erk_ws_set_fs(float *fs, struct s_sim_erk_ws *work); +// ode funtion +void s_sim_erk_ws_set_ode(void (*ode)(int t, float *x, float *p, void *ode_args, float *xdot), struct s_sim_erk_ws *work); +// forward vde function +void s_sim_erk_ws_set_vde_for(void (*ode)(int t, float *x, float *p, void *ode_args, float *xdot), struct s_sim_erk_ws *work); +// ode_args, passed straight to the ode/vde_for/vde_adj functions +void s_sim_erk_ws_set_ode_args(void *ode_args, struct s_sim_erk_ws *work); +// state +void s_sim_erk_ws_get_x(struct s_sim_erk_ws *work, float *x); +// forward sensitivities +void s_sim_erk_ws_get_fs(struct s_sim_erk_ws *work, float *fs); +// +void s_sim_erk_solve(struct s_sim_erk_arg *arg, struct s_sim_erk_ws *work); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_D_SIM_ERK_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_sim_rk.h b/phonelibs/acados/include/hpipm/include/hpipm_s_sim_rk.h new file mode 100644 index 0000000000..53acd71498 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_sim_rk.h @@ -0,0 +1,72 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_SIM_RK_H_ +#define HPIPM_S_SIM_RK_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct s_sim_rk_data + { + float *A_rk; // A in butcher tableau + float *B_rk; // b in butcher tableau + float *C_rk; // c in butcher tableau + int expl; // erk vs irk + int ns; // number of stages + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_sim_rk_data_memsize(int ns); +// +void s_sim_rk_data_create(int ns, struct s_sim_rk_data *rk_data, void *memory); +// +void s_sim_rk_data_init_default(char *field, struct s_sim_rk_data *rk_data); +// +void s_sim_rk_data_set_all(int expl, float *A_rk, float *B_rk, float *C_rk, struct s_sim_rk_data *rk_data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_S_SIM_RK_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp.h new file mode 100644 index 0000000000..450e992624 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp.h @@ -0,0 +1,213 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_H_ +#define HPIPM_S_TREE_OCP_QCQP_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qcqp + { + struct s_tree_ocp_qcqp_dim *dim; + struct blasfeo_smat *BAbt; // Nn-1 + struct blasfeo_smat *RSQrq; // Nn + struct blasfeo_smat *DCt; // Nn + struct blasfeo_smat **Hq; // Nn + struct blasfeo_svec *b; // Nn-1 + struct blasfeo_svec *rqz; // Nn + struct blasfeo_svec *d; // Nn + struct blasfeo_svec *d_mask; // Nn + struct blasfeo_svec *m; // Nn + struct blasfeo_svec *Z; // Nn + int **idxb; // indices of box constrained variables within [u; x] // Nn + int **idxs_rev; // index of soft constraints + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_tree_ocp_qcqp_memsize(struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_create(struct s_tree_ocp_qcqp_dim *dim, struct s_tree_ocp_qcqp *qp, void *memory); +// +void s_tree_ocp_qcqp_set_all(float **A, float **B, float **b, float **Q, float **S, float **R, float **q, float **r, int **idxb, float **d_lb, float **d_ub, float **C, float **D, float **d_lg, float **d_ug, float **Zl, float **Zu, float **zl, float **zu, int **idxs, float **d_ls, float **d_us, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set(char *field_name, int node_edge, void *value, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_A(int edge, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_B(int edge, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_b(int edge, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Q(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_S(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_R(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_q(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_r(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lb(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lb_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ub(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ub_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lbx(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lbx_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ubx(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ubx_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lbu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lbu_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ubu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ubu_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_idxb(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_idxbx(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jbx(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_idxbu(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jbu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_C(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_D(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lg(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lg_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ug(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_ug_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Qq(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Sq(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Rq(int node, float *mat, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_qq(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_rq(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_uq(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_uq_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Zl(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Zu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_zl(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_zu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lls(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lls_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lus(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_lus_mask(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_idxs(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_idxs_rev(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jsbu(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jsbx(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jsg(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_set_Jsq(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_idxe(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_idxbxe(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_idxbue(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_idxge(int node, int *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_Jbxe(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_Jbue(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_Jge(int node, float *vec, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_set_diag_H_flag(int node, int *value, struct s_tree_ocp_qcqp *qp); + + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_TREE_OCP_QCQP_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_dim.h new file mode 100644 index 0000000000..7a49d51995 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_dim.h @@ -0,0 +1,118 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_DIM_H_ +#define HPIPM_S_TREE_OCP_QCQP_DIM_H_ + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qcqp_dim + { + struct s_tree_ocp_qp_dim *qp_dim; // dim of qp approximation + struct tree *ttree; // tree describing node conndection + int *nx; // number of states // Nn + int *nu; // number of inputs // Nn + int *nb; // number of box constraints // Nn + int *nbx; // number of state box constraints // Nn + int *nbu; // number of input box constraints // Nn + int *ng; // number of general constraints // Nn + int *nq; // number of (upper) quadratic constraints + int *ns; // number of soft constraints // Nn + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int *nsq; // number of (upper) soft quadratic constraints + int Nn; // number of nodes + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qcqp_dim_strsize(); +// +hpipm_size_t s_tree_ocp_qcqp_dim_memsize(int Nn); +// +void s_tree_ocp_qcqp_dim_create(int Nn, struct s_tree_ocp_qcqp_dim *qp_dim, void *memory); +// +void s_tree_ocp_qcqp_dim_set_tree(struct tree *ttree, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set(char *field, int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nx(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nu(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nbx(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nbu(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_ng(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nq(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_ns(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nsbx(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nsbu(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nsg(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_dim_set_nsq(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +//void s_tree_ocp_qcqp_dim_set_nbxe(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +//void s_tree_ocp_qcqp_dim_set_nbue(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); +// +//void s_tree_ocp_qcqp_dim_set_nge(int stage, int value, struct s_tree_ocp_qcqp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_TREE_OCP_QCQP_DIM_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_ipm.h new file mode 100644 index 0000000000..69d65914c5 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_ipm.h @@ -0,0 +1,192 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_IPM_H_ +#define HPIPM_S_TREE_OCP_QCQP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qcqp_ipm_arg + { + struct s_tree_ocp_qp_ipm_arg *qp_arg; + float mu0; // initial value for complementarity slackness + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol +// int square_root_alg; // 0 classical Riccati, 1 square-root Riccati + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq (for square_root_alg==1) + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution of equality constraints (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol_eq==1) +// int comp_res_pred; // compute residuals of prediction + int split_step; // use different step for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_tree_ocp_qcqp_ipm_ws + { + struct s_tree_ocp_qp_ipm_ws *qp_ws; + struct s_tree_ocp_qp *qp; + struct s_tree_ocp_qp_sol *qp_sol; + struct s_tree_ocp_qcqp_res_ws *qcqp_res_ws; + struct s_tree_ocp_qcqp_res *qcqp_res; + struct blasfeo_svec *tmp_nuxM; + int iter; // iteration number + int status; + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qcqp_ipm_arg_strsize(); +// +hpipm_size_t s_tree_ocp_qcqp_ipm_arg_memsize(struct s_tree_ocp_qcqp_dim *ocp_dim); +// +void s_tree_ocp_qcqp_ipm_arg_create(struct s_tree_ocp_qcqp_dim *ocp_dim, struct s_tree_ocp_qcqp_ipm_arg *arg, void *mem); +// +void s_tree_ocp_qcqp_ipm_arg_set_default(enum hpipm_mode mode, struct s_tree_ocp_qcqp_ipm_arg *arg); +// +void s_tree_ocp_qcqp_ipm_arg_set(char *field, void *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set maximum number of iterations +void s_tree_ocp_qcqp_ipm_arg_set_iter_max(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set minimum step lenght +void s_tree_ocp_qcqp_ipm_arg_set_alpha_min(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set initial value of barrier parameter +void s_tree_ocp_qcqp_ipm_arg_set_mu0(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on stationarity condition +void s_tree_ocp_qcqp_ipm_arg_set_tol_stat(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on equality constr +void s_tree_ocp_qcqp_ipm_arg_set_tol_eq(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on inequality constr +void s_tree_ocp_qcqp_ipm_arg_set_tol_ineq(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set exit tolerance on complementarity condition +void s_tree_ocp_qcqp_ipm_arg_set_tol_comp(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set regularization of primal variables +void s_tree_ocp_qcqp_ipm_arg_set_reg_prim(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set warm start: 0 no warm start, 1 primal var +void s_tree_ocp_qcqp_ipm_arg_set_warm_start(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// Mehrotra's predictor-corrector IPM algorithm: 0 no predictor-corrector, 1 use predictor-corrector +void s_tree_ocp_qcqp_ipm_arg_set_pred_corr(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// conditional predictor-corrector: 0 no conditinal predictor-corrector, 1 conditional predictor-corrector +void s_tree_ocp_qcqp_ipm_arg_set_cond_pred_corr(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// set riccati algorithm: 0 classic, 1 square-root +//void s_tree_ocp_qcqp_ipm_arg_set_ric_alg(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// compute residuals after solution +void s_tree_ocp_qcqp_ipm_arg_set_comp_res_exit(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// compute residuals of prediction +//void s_tree_ocp_qcqp_ipm_arg_set_comp_res_pred(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// min value of lam in the solution +void s_tree_ocp_qcqp_ipm_arg_set_lam_min(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// min value of t in the solution +void s_tree_ocp_qcqp_ipm_arg_set_t_min(float *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// use different step for primal and dual variables +void s_tree_ocp_qcqp_ipm_arg_set_split_step(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); +// clip t and lam: 0 no, 1 in Gamma computation, 2 in solution +void s_tree_ocp_qcqp_ipm_arg_set_t_lam_min(int *value, struct s_tree_ocp_qcqp_ipm_arg *arg); + +// +hpipm_size_t s_tree_ocp_qcqp_ipm_ws_strsize(); +// +hpipm_size_t s_tree_ocp_qcqp_ipm_ws_memsize(struct s_tree_ocp_qcqp_dim *ocp_dim, struct s_tree_ocp_qcqp_ipm_arg *arg); +// +void s_tree_ocp_qcqp_ipm_ws_create(struct s_tree_ocp_qcqp_dim *ocp_dim, struct s_tree_ocp_qcqp_ipm_arg *arg, struct s_tree_ocp_qcqp_ipm_ws *ws, void *mem); +// +void s_tree_ocp_qcqp_ipm_get(char *field, struct s_tree_ocp_qcqp_ipm_ws *ws, void *value); +// +void s_tree_ocp_qcqp_ipm_get_status(struct s_tree_ocp_qcqp_ipm_ws *ws, int *status); +// +void s_tree_ocp_qcqp_ipm_get_iter(struct s_tree_ocp_qcqp_ipm_ws *ws, int *iter); +// +void s_tree_ocp_qcqp_ipm_get_max_res_stat(struct s_tree_ocp_qcqp_ipm_ws *ws, float *res_stat); +// +void s_tree_ocp_qcqp_ipm_get_max_res_eq(struct s_tree_ocp_qcqp_ipm_ws *ws, float *res_eq); +// +void s_tree_ocp_qcqp_ipm_get_max_res_ineq(struct s_tree_ocp_qcqp_ipm_ws *ws, float *res_ineq); +// +void s_tree_ocp_qcqp_ipm_get_max_res_comp(struct s_tree_ocp_qcqp_ipm_ws *ws, float *res_comp); +// +void s_tree_ocp_qcqp_ipm_get_stat(struct s_tree_ocp_qcqp_ipm_ws *ws, float **stat); +// +void s_tree_ocp_qcqp_ipm_get_stat_m(struct s_tree_ocp_qcqp_ipm_ws *ws, int *stat_m); +// +void s_tree_ocp_qcqp_init_var(struct s_tree_ocp_qcqp *qp, struct s_tree_ocp_qcqp_sol *qp_sol, struct s_tree_ocp_qcqp_ipm_arg *arg, struct s_tree_ocp_qcqp_ipm_ws *ws); +// +void s_tree_ocp_qcqp_ipm_solve(struct s_tree_ocp_qcqp *qp, struct s_tree_ocp_qcqp_sol *qp_sol, struct s_tree_ocp_qcqp_ipm_arg *arg, struct s_tree_ocp_qcqp_ipm_ws *ws); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_TREE_OCP_QCQP_IPM_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_res.h new file mode 100644 index 0000000000..d0d84a6f23 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_res.h @@ -0,0 +1,109 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_RES_H_ +#define HPIPM_S_TREE_OCP_QCQP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qcqp_res + { + struct s_tree_ocp_qcqp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // max of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_tree_ocp_qcqp_res_ws + { + struct blasfeo_svec *tmp_nuxM; // work space of size nuM+nxM + struct blasfeo_svec *tmp_nbgqM; // work space of size nbM+ngM+nqM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + struct blasfeo_svec *q_fun; // value for evaluation of quadr constr + struct blasfeo_svec *q_adj; // value for adjoint of quadr constr + int use_q_fun; // reuse cached value for evaluation of quadr constr + int use_q_adj; // reuse cached value for adjoint of quadr constr + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qcqp_res_memsize(struct s_tree_ocp_qcqp_dim *ocp_dim); +// +void s_tree_ocp_qcqp_res_create(struct s_tree_ocp_qcqp_dim *ocp_dim, struct s_tree_ocp_qcqp_res *res, void *mem); +// +hpipm_size_t s_tree_ocp_qcqp_res_ws_memsize(struct s_tree_ocp_qcqp_dim *ocp_dim); +// +void s_tree_ocp_qcqp_res_ws_create(struct s_tree_ocp_qcqp_dim *ocp_dim, struct s_tree_ocp_qcqp_res_ws *ws, void *mem); +// +void s_tree_ocp_qcqp_res_compute(struct s_tree_ocp_qcqp *qp, struct s_tree_ocp_qcqp_sol *qp_sol, struct s_tree_ocp_qcqp_res *res, struct s_tree_ocp_qcqp_res_ws *ws); +// +void s_tree_ocp_qcqp_res_compute_inf_norm(struct s_tree_ocp_qcqp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_TREE_OCP_QCQP_RES_H_ + + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_sol.h new file mode 100644 index 0000000000..47f038c6b3 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_sol.h @@ -0,0 +1,97 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_SOL_H_ +#define HPIPM_S_TREE_OCP_QCQP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qcqp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +struct s_tree_ocp_qcqp_sol + { + struct s_tree_ocp_qcqp_dim *dim; + struct blasfeo_svec *ux; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_tree_ocp_qcqp_sol_memsize(struct s_tree_ocp_qcqp_dim *dim); +// +void s_tree_ocp_qcqp_sol_create(struct s_tree_ocp_qcqp_dim *dim, struct s_tree_ocp_qcqp_sol *qp_sol, void *memory); +// +void s_tree_ocp_qcqp_sol_get_u(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_x(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_sl(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_su(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_pi(int edge, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_lam_lb(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_lam_ub(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_lam_lg(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qcqp_sol_get_lam_ug(int node, struct s_tree_ocp_qcqp_sol *qp_sol, float *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_TREE_OCP_QCQP_SOL_H_ + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_utils.h new file mode 100644 index 0000000000..79528de1ce --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qcqp_utils.h @@ -0,0 +1,85 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QCQP_UTILS_H_ +#define HPIPM_S_TREE_OCP_QCQP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qcqp_dim.h" +#include "hpipm_s_tree_ocp_qcqp.h" +#include "hpipm_s_tree_ocp_qcqp_sol.h" +#include "hpipm_s_tree_ocp_qcqp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_tree_ocp_qcqp_dim_print(struct s_tree_ocp_qcqp_dim *qp_dim); +// +//void s_tree_ocp_qcqp_dim_codegen(char *file_name, char *mode, struct s_tree_ocp_qcqp_dim *qp_dim); +// +void s_tree_ocp_qcqp_print(struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp *qp); +// +//void s_tree_ocp_qcqp_codegen(char *file_name, char *mode, struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp *qp); +// +void s_tree_ocp_qcqp_sol_print(struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp_sol *ocp_qcqp_sol); +// +void s_tree_ocp_qcqp_ipm_arg_print(struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp_ipm_arg *arg); +// +//void s_tree_ocp_qcqp_ipm_arg_codegen(char *file_name, char *mode, struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp_ipm_arg *arg); +// +void s_tree_ocp_qcqp_res_print(struct s_tree_ocp_qcqp_dim *qp_dim, struct s_tree_ocp_qcqp_res *ocp_qcqp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_TREE_OCP_QCQP_UTILS_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp.h new file mode 100644 index 0000000000..722b930b9a --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp.h @@ -0,0 +1,196 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QP_H_ +#define HPIPM_S_TREE_OCP_QP_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qp + { + struct s_tree_ocp_qp_dim *dim; + struct blasfeo_smat *BAbt; // Nn-1 + struct blasfeo_smat *RSQrq; // Nn + struct blasfeo_smat *DCt; // Nn + struct blasfeo_svec *b; // Nn-1 + struct blasfeo_svec *rqz; // Nn + struct blasfeo_svec *d; // Nn + struct blasfeo_svec *d_mask; // Nn + struct blasfeo_svec *m; // Nn + struct blasfeo_svec *Z; // Nn + int **idxb; // indices of box constrained variables within [u; x] // Nn +// int **idxs; // index of soft constraints + int **idxs_rev; // index of soft constraints + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_tree_ocp_qp_memsize(struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_create(struct s_tree_ocp_qp_dim *dim, struct s_tree_ocp_qp *qp, void *memory); +// +void s_tree_ocp_qp_set_all(float **A, float **B, float **b, float **Q, float **S, float **R, float **q, float **r, int **idxb, float **d_lb, float **d_ub, float **C, float **D, float **d_lg, float **d_ug, float **Zl, float **Zu, float **zl, float **zu, int **idxs, float **d_ls, float **d_us, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set(char *field_name, int node_edge, void *value, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_A(int edge, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_B(int edge, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_b(int edge, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Q(int node, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_S(int node, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_R(int node, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_q(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_r(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lb(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lb_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ub(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ub_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lbx(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lbx_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ubx(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ubx_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lbu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lbu_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ubu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ubu_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_idxb(int node, int *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_idxbx(int node, int *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Jbx(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_idxbu(int node, int *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Jbu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_C(int node, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_D(int node, float *mat, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lg(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lg_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ug(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_ug_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Zl(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Zu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_zl(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_zu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lls(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lls_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lus(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_lus_mask(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_idxs(int node, int *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_idxs_rev(int node, int *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Jsbu(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Jsbx(int node, float *vec, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_set_Jsg(int node, float *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_idxe(int node, int *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_idxbxe(int node, int *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_idxbue(int node, int *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_idxge(int node, int *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_Jbxe(int node, float *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_Jbue(int node, float *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_Jge(int node, float *vec, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_set_diag_H_flag(int node, int *value, struct s_tree_ocp_qp *qp); + + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_TREE_OCP_QP_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_dim.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_dim.h new file mode 100644 index 0000000000..90df57182b --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_dim.h @@ -0,0 +1,111 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QP_DIM_H_ +#define HPIPM_S_TREE_OCP_QP_DIM_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qp_dim + { + struct tree *ttree; // tree describing node conndection + int *nx; // number of states // Nn + int *nu; // number of inputs // Nn + int *nb; // number of box constraints // Nn + int *nbx; // number of state box constraints // Nn + int *nbu; // number of input box constraints // Nn + int *ng; // number of general constraints // Nn + int *ns; // number of soft constraints // Nn + int *nsbx; // number of soft state box constraints + int *nsbu; // number of soft input box constraints + int *nsg; // number of soft general constraints + int Nn; // number of nodes + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qp_dim_strsize(); +// +hpipm_size_t s_tree_ocp_qp_dim_memsize(int Nn); +// +void s_tree_ocp_qp_dim_create(int Nn, struct s_tree_ocp_qp_dim *qp_dim, void *memory); +// +void s_tree_ocp_qp_dim_set_all(struct tree *ttree, int *nx, int *nu, int *nbx, int *nbu, int *ng, int *nsbx, int *nsbu, int *nsg, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_tree(struct tree *ttree, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set(char *field, int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nx(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nu(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nbx(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nbu(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_ng(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_ns(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nsbx(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nsbu(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nsg(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nbxe(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nbue(int stage, int value, struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_dim_set_nge(int stage, int value, struct s_tree_ocp_qp_dim *dim); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_TREE_OCP_QP_DIM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_ipm.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_ipm.h new file mode 100644 index 0000000000..f8c26d3173 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_ipm.h @@ -0,0 +1,208 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + + +#ifndef HPIPM_S_TREE_OCP_QP_IPM_H_ +#define HPIPM_S_TREE_OCP_QP_IPM_H_ + + + +#include +#include + +#include +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qp_ipm_arg + { + float mu0; // initial value for duality measure + float alpha_min; // exit cond on step length + float res_g_max; // exit cond on inf norm of residuals + float res_b_max; // exit cond on inf norm of residuals + float res_d_max; // exit cond on inf norm of residuals + float res_m_max; // exit cond on inf norm of residuals + float reg_prim; // reg of primal hessian + float lam_min; // min value in lam vector + float t_min; // min value in t vector + float tau_min; // min value of barrier parameter + int iter_max; // exit cond in iter number + int stat_max; // iterations saved in stat + int stat_m; // number of recorded stat per IPM iter + int pred_corr; // use Mehrotra's predictor-corrector IPM algirthm + int cond_pred_corr; // conditional Mehrotra's predictor-corrector + int itref_pred_max; // max number of iterative refinement steps for predictor step + int itref_corr_max; // max number of iterative refinement steps for corrector step + int warm_start; // 0 no warm start, 1 warm start primal sol, 2 warm start primal and dual sol + int lq_fact; // 0 syrk+potrf, 1 mix, 2 lq + int abs_form; // absolute IPM formulation + int comp_dual_sol_eq; // dual solution (only for abs_form==1) + int comp_res_exit; // compute residuals on exit (only for abs_form==1 and comp_dual_sol==1) + int split_step; // use different steps for primal and dual variables + int t_lam_min; // clip t and lam: 0 no, 1 in Gamma computation, 2 in solution + int mode; + hpipm_size_t memsize; + }; + + + +struct s_tree_ocp_qp_ipm_ws + { + struct s_core_qp_ipm_workspace *core_workspace; + struct s_tree_ocp_qp_res_ws *res_workspace; + struct s_tree_ocp_qp_sol *sol_step; + struct s_tree_ocp_qp_sol *sol_itref; + struct s_tree_ocp_qp *qp_step; + struct s_tree_ocp_qp *qp_itref; + struct s_tree_ocp_qp_res *res_itref; + struct s_tree_ocp_qp_res *res; + struct blasfeo_svec *Gamma; // hessian update + struct blasfeo_svec *gamma; // hessian update + struct blasfeo_svec *tmp_nxM; // work space of size nxM + struct blasfeo_svec *tmp_nbgM; // work space of size nbgM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + struct blasfeo_svec *Pb; // Pb + struct blasfeo_svec *Zs_inv; + struct blasfeo_smat *L; + struct blasfeo_smat *Lh; + struct blasfeo_smat *AL; + struct blasfeo_smat *lq0; + struct blasfeo_svec *tmp_m; + float *stat; // convergence statistics + int *use_hess_fact; + void *lq_work0; + float qp_res[4]; // infinity norm of residuals + int iter; // iteration number + int stat_max; // iterations saved in stat + int stat_m; // number of recorded stat per IPM iter + int use_Pb; + int status; // solver status + int lq_fact; // cache from arg + int mask_constr; // use constr mask + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qp_ipm_arg_memsize(struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_ipm_arg_create(struct s_tree_ocp_qp_dim *dim, struct s_tree_ocp_qp_ipm_arg *arg, void *mem); +// +void s_tree_ocp_qp_ipm_arg_set_default(enum hpipm_mode mode, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_iter_max(int *iter_max, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_alpha_min(float *alpha_min, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_mu0(float *mu0, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_tol_stat(float *tol_stat, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_tol_eq(float *tol_eq, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_tol_ineq(float *tol_ineq, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_tol_comp(float *tol_comp, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_reg_prim(float *reg, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_warm_start(int *warm_start, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_pred_corr(int *pred_corr, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_cond_pred_corr(int *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_comp_dual_sol_eq(int *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_comp_res_exit(int *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_lam_min(float *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_t_min(float *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_tau_min(float *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_split_step(int *value, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_arg_set_t_lam_min(int *value, struct s_tree_ocp_qp_ipm_arg *arg); + +// +hpipm_size_t s_tree_ocp_qp_ipm_ws_memsize(struct s_tree_ocp_qp_dim *dim, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_ipm_ws_create(struct s_tree_ocp_qp_dim *dim, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws, void *mem); +// +void s_tree_ocp_qp_ipm_get_status(struct s_tree_ocp_qp_ipm_ws *ws, int *status); +// +void s_tree_ocp_qp_ipm_get_iter(struct s_tree_ocp_qp_ipm_ws *ws, int *iter); +// +void s_tree_ocp_qp_ipm_get_max_res_stat(struct s_tree_ocp_qp_ipm_ws *ws, float *res_stat); +// +void s_tree_ocp_qp_ipm_get_max_res_eq(struct s_tree_ocp_qp_ipm_ws *ws, float *res_eq); +// +void s_tree_ocp_qp_ipm_get_max_res_ineq(struct s_tree_ocp_qp_ipm_ws *ws, float *res_ineq); +// +void s_tree_ocp_qp_ipm_get_max_res_comp(struct s_tree_ocp_qp_ipm_ws *ws, float *res_comp); +// +void s_tree_ocp_qp_ipm_get_stat(struct s_tree_ocp_qp_ipm_ws *ws, float **stat); +// +void s_tree_ocp_qp_ipm_get_stat_m(struct s_tree_ocp_qp_ipm_ws *ws, int *stat_m); +// +void s_tree_ocp_qp_init_var(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_ipm_abs_step(int kk, struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_ipm_delta_step(int kk, struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_ipm_solve(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_TREE_OCP_QP_IPM_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_kkt.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_kkt.h new file mode 100644 index 0000000000..bcf7354783 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_kkt.h @@ -0,0 +1,54 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +// +void s_tree_ocp_qp_fact_solve_kkt_unconstr(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_fact_solve_kkt_step(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_fact_lq_solve_kkt_step(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); +// +void s_tree_ocp_qp_solve_kkt_step(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_ipm_arg *arg, struct s_tree_ocp_qp_ipm_ws *ws); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_res.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_res.h new file mode 100644 index 0000000000..5c6797204f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_res.h @@ -0,0 +1,107 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QP_RES_H_ +#define HPIPM_S_TREE_OCP_QP_RES_H_ + + + +#include +#include + +#include +#include +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct s_tree_ocp_qp_res + { + struct s_tree_ocp_qp_dim *dim; + struct blasfeo_svec *res_g; // q-residuals + struct blasfeo_svec *res_b; // b-residuals + struct blasfeo_svec *res_d; // d-residuals + struct blasfeo_svec *res_m; // m-residuals + float res_max[4]; // max of residuals + float res_mu; // mu-residual + hpipm_size_t memsize; + }; + + + +struct s_tree_ocp_qp_res_ws + { + struct blasfeo_svec *tmp_nbgM; // work space of size nbM+ngM + struct blasfeo_svec *tmp_nsM; // work space of size nsM + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t s_tree_ocp_qp_res_memsize(struct s_tree_ocp_qp_dim *ocp_dim); +// +void s_tree_ocp_qp_res_create(struct s_tree_ocp_qp_dim *ocp_dim, struct s_tree_ocp_qp_res *res, void *mem); +// +hpipm_size_t s_tree_ocp_qp_res_ws_memsize(struct s_tree_ocp_qp_dim *ocp_dim); +// +void s_tree_ocp_qp_res_ws_create(struct s_tree_ocp_qp_dim *ocp_dim, struct s_tree_ocp_qp_res_ws *ws, void *mem); +// +void s_tree_ocp_qp_res_get_all(struct s_tree_ocp_qp_res *res, float **res_r, float **res_q, float **res_ls, float **res_us, float **res_b, float **res_d_lb, float **res_d_ub, float **res_d_lg, float **res_d_ug, float **res_d_ls, float **res_d_us, float **res_m_lb, float **res_m_ub, float **res_m_lg, float **res_m_ug, float **res_m_ls, float **res_m_us); +// +void s_tree_ocp_qp_res_compute(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_res *res, struct s_tree_ocp_qp_res_ws *ws); +// +void s_tree_ocp_qp_res_compute_lin(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, struct s_tree_ocp_qp_sol *qp_step, struct s_tree_ocp_qp_res *res, struct s_tree_ocp_qp_res_ws *ws); +// +void s_tree_ocp_qp_res_compute_inf_norm(struct s_tree_ocp_qp_res *res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_S_TREE_OCP_QP_RES_H_ + + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_sol.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_sol.h new file mode 100644 index 0000000000..71e887675b --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_sol.h @@ -0,0 +1,98 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QP_SOL_H_ +#define HPIPM_S_TREE_OCP_QP_SOL_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qp_dim.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + +struct s_tree_ocp_qp_sol + { + struct s_tree_ocp_qp_dim *dim; + struct blasfeo_svec *ux; + struct blasfeo_svec *pi; + struct blasfeo_svec *lam; + struct blasfeo_svec *t; + void *misc; + hpipm_size_t memsize; // memory size in bytes + }; + + + +// +hpipm_size_t s_tree_ocp_qp_sol_memsize(struct s_tree_ocp_qp_dim *dim); +// +void s_tree_ocp_qp_sol_create(struct s_tree_ocp_qp_dim *dim, struct s_tree_ocp_qp_sol *qp_sol, void *memory); +// +void s_tree_ocp_qp_sol_get_all(struct s_tree_ocp_qp *qp, struct s_tree_ocp_qp_sol *qp_sol, float **u, float **x, float **ls, float **us, float **pi, float **lam_lb, float **lam_ub, float **lam_lg, float **lam_ug, float **lam_ls, float **lam_us); +// +void s_tree_ocp_qp_sol_get_u(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_x(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_sl(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_su(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_pi(int edge, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_lam_lb(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_lam_ub(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_lam_lg(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); +// +void s_tree_ocp_qp_sol_get_lam_ug(int node, struct s_tree_ocp_qp_sol *qp_sol, float *vec); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + + +#endif // HPIPM_S_TREE_OCP_QP_SOL_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_utils.h b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_utils.h new file mode 100644 index 0000000000..ec1747568f --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_s_tree_ocp_qp_utils.h @@ -0,0 +1,84 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_S_TREE_OCP_QP_UTILS_H_ +#define HPIPM_S_TREE_OCP_QP_UTILS_H_ + + + +#include +#include + +#include "hpipm_s_tree_ocp_qp_dim.h" +#include "hpipm_s_tree_ocp_qp.h" +#include "hpipm_s_tree_ocp_qp_sol.h" +#include "hpipm_s_tree_ocp_qp_ipm.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +// +void s_tree_ocp_qp_dim_print(struct s_tree_ocp_qp_dim *qp_dim); +// +//void s_tree_ocp_qp_dim_codegen(char *file_name, char *mode, struct s_tree_ocp_qp_dim *qp_dim); +// +void s_tree_ocp_qp_print(struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp *qp); +// +//void s_tree_ocp_qp_codegen(char *file_name, char *mode, struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp *qp); +// +void s_tree_ocp_qp_sol_print(struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp_sol *ocp_qp_sol); +// +void s_tree_ocp_qp_ipm_arg_print(struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp_ipm_arg *arg); +// +//void s_tree_ocp_qp_ipm_arg_codegen(char *file_name, char *mode, struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp_ipm_arg *arg); +// +void s_tree_ocp_qp_res_print(struct s_tree_ocp_qp_dim *qp_dim, struct s_tree_ocp_qp_res *ocp_qp_res); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + + +#endif // HPIPM_S_TREE_OCP_QP_UTILS_H_ + + diff --git a/phonelibs/acados/include/hpipm/include/hpipm_scenario_tree.h b/phonelibs/acados/include/hpipm/include/hpipm_scenario_tree.h new file mode 100644 index 0000000000..a3c77a670a --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_scenario_tree.h @@ -0,0 +1,70 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_SCENARIO_TREE_H_ +#define HPIPM_SCENARIO_TREE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct sctree + { + struct node *root; // pointer to root + int *kids; // pointer to array of kids + int Nn; // numer of nodes + int md; // number of realizations + int Nr; // robust horizion + int Nh; // control horizion + hpipm_size_t memsize; + }; + + + +// +hpipm_size_t sctree_memsize(int md, int Nr, int Nh); +// +void sctree_create(int md, int Nr, int Nh, struct sctree *st, void *memory); +// +void sctree_cast_to_tree(struct sctree *st, struct tree *tt); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_SCENARIO_TREE_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_timing.h b/phonelibs/acados/include/hpipm/include/hpipm_timing.h new file mode 100644 index 0000000000..bd0f2dbcb3 --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_timing.h @@ -0,0 +1,67 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + +#ifndef HPIPM_TIMING_H_ +#define HPIPM_TIMING_H_ + + + +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + + + +typedef blasfeo_timer hpipm_timer; + + + +// +void hpipm_tic(hpipm_timer *t); +// +double hpipm_toc(hpipm_timer *t); + + + +#ifdef __cplusplus +} // #extern "C" +#endif + + +#endif // HPIPM_TIMING_H_ diff --git a/phonelibs/acados/include/hpipm/include/hpipm_tree.h b/phonelibs/acados/include/hpipm/include/hpipm_tree.h new file mode 100644 index 0000000000..f9d69ecddd --- /dev/null +++ b/phonelibs/acados/include/hpipm/include/hpipm_tree.h @@ -0,0 +1,76 @@ +/************************************************************************************************** +* * +* This file is part of HPIPM. * +* * +* HPIPM -- High-Performance Interior Point Method. * +* Copyright (C) 2019 by Gianluca Frison. * +* Developed at IMTEK (University of Freiburg) under the supervision of Moritz Diehl. * +* All rights reserved. * +* * +* The 2-Clause BSD License * +* * +* Redistribution and use in source and binary forms, with or without * +* modification, are permitted provided that the following conditions are met: * +* * +* 1. Redistributions of source code must retain the above copyright notice, this * +* list of conditions and the following disclaimer. * +* 2. Redistributions in binary form must reproduce the above copyright notice, * +* this list of conditions and the following disclaimer in the documentation * +* and/or other materials provided with the distribution. * +* * +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * +* * +* Author: Gianluca Frison, gianluca.frison (at) imtek.uni-freiburg.de * +* * +**************************************************************************************************/ + + +#ifndef HPIPM_TREE_H_ +#define HPIPM_TREE_H_ + +#include "hpipm_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +struct node + { + int *kids; // 64 bits + int idx; // 32 bits + int dad; // 32 bits + int nkids; // 32 bits + int stage; // 32 bits + int real; // 32 bits + int idxkid; // 32 bits // XXX needed ??? + // total 256 bits + }; + + + +struct tree + { + struct node *root; // pointer to root + int *kids; // pointer to array of kids + int Nn; // numer of nodes + hpipm_size_t memsize; + }; + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // HPIPM_TREE_H_ diff --git a/phonelibs/acados/include/qpOASES_e/Bounds.h b/phonelibs/acados/include/qpOASES_e/Bounds.h new file mode 100644 index 0000000000..4e41c1d163 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Bounds.h @@ -0,0 +1,543 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Bounds.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the Bounds class designed to manage working sets of + * bounds within a QProblem. + */ + + +#ifndef QPOASES_BOUNDS_H +#define QPOASES_BOUNDS_H + + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Manages working sets of bounds (= box constraints). + * + * This class manages working sets of bounds (= box constraints) + * by storing index sets and other status information. + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + Indexlist *freee; /**< Index list of free variables. */ + Indexlist *fixed; /**< Index list of fixed variables. */ + + Indexlist *shiftedFreee; /**< Memory for shifting free variables. */ + Indexlist *shiftedFixed; /**< Memory for shifting fixed variables. */ + + Indexlist *rotatedFreee; /**< Memory for rotating free variables. */ + Indexlist *rotatedFixed; /**< Memory for rotating fixed variables. */ + + SubjectToType *type; /**< Type of bounds. */ + SubjectToStatus *status; /**< Status of bounds. */ + + SubjectToType *typeTmp; /**< Temp memory for type of bounds. */ + SubjectToStatus *statusTmp; /**< Temp memory for status of bounds. */ + + BooleanType noLower; /**< This flag indicates if there is no lower bound on any variable. */ + BooleanType noUpper; /**< This flag indicates if there is no upper bound on any variable. */ + + int n; /**< Total number of bounds. */ +} Bounds; + +int Bounds_calculateMemorySize( int n); + +char *Bounds_assignMemory(int n, Bounds **mem, void *raw_memory); + +Bounds *Bounds_createMemory( int n ); + +/** Constructor which takes the number of bounds. */ +void BoundsCON( Bounds* _THIS, + int _n /**< Number of bounds. */ + ); + +/** Copies all members from given rhs object. + * \return SUCCESSFUL_RETURN */ +void BoundsCPY( Bounds* FROM, + Bounds* TO + ); + + +/** Initialises object with given number of bounds. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue Bounds_init( Bounds* _THIS, + int _n /**< Number of bounds. */ + ); + + +/** Initially adds number of a new (i.e. not yet in the list) bound to + * given index set. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_BOUND_FAILED \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_INVALID_ARGUMENTS */ +returnValue Bounds_setupBound( Bounds* _THIS, + int number, /**< Number of new bound. */ + SubjectToStatus _status /**< Status of new bound. */ + ); + +/** Initially adds all numbers of new (i.e. not yet in the list) bounds to + * to the index set of free bounds; the order depends on the SujectToType + * of each index. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_BOUND_FAILED */ +returnValue Bounds_setupAllFree( Bounds* _THIS + ); + +/** Initially adds all numbers of new (i.e. not yet in the list) bounds to + * to the index set of fixed bounds (on their lower bounds); + * the order depends on the SujectToType of each index. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_BOUND_FAILED */ +returnValue Bounds_setupAllLower( Bounds* _THIS + ); + +/** Initially adds all numbers of new (i.e. not yet in the list) bounds to + * to the index set of fixed bounds (on their upper bounds); + * the order depends on the SujectToType of each index. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_BOUND_FAILED */ +returnValue Bounds_setupAllUpper( Bounds* _THIS + ); + + +/** Moves index of a bound from index list of fixed to that of free bounds. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_BOUND_FAILED \n + RET_INDEX_OUT_OF_BOUNDS */ +returnValue Bounds_moveFixedToFree( Bounds* _THIS, + int number /**< Number of bound to be freed. */ + ); + +/** Moves index of a bound from index list of free to that of fixed bounds. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_BOUND_FAILED \n + RET_INDEX_OUT_OF_BOUNDS */ +returnValue Bounds_moveFreeToFixed( Bounds* _THIS, + int number, /**< Number of bound to be fixed. */ + SubjectToStatus _status /**< Status of bound to be fixed. */ + ); + +/** Flip fixed bound. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_BOUND_FAILED \n + RET_INDEX_OUT_OF_BOUNDS */ +returnValue Bounds_flipFixed( Bounds* _THIS, + int number + ); + +/** Swaps the indices of two free bounds within the index set. + * \return SUCCESSFUL_RETURN \n + RET_SWAPINDEX_FAILED */ +returnValue Bounds_swapFree( Bounds* _THIS, + int number1, /**< Number of first bound. */ + int number2 /**< Number of second bound. */ + ); + + +/** Returns number of variables. + * \return Number of variables. */ +static inline int Bounds_getNV( Bounds* _THIS + ); + +/** Returns number of implicitly fixed variables. + * \return Number of implicitly fixed variables. */ +static inline int Bounds_getNFV( Bounds* _THIS + ); + +/** Returns number of bounded (but possibly free) variables. + * \return Number of bounded (but possibly free) variables. */ +static inline int Bounds_getNBV( Bounds* _THIS + ); + +/** Returns number of unbounded variables. + * \return Number of unbounded variables. */ +static inline int Bounds_getNUV( Bounds* _THIS + ); + +/** Returns number of free variables. + * \return Number of free variables. */ +static inline int Bounds_getNFR( Bounds* _THIS + ); + +/** Returns number of fixed variables. + * \return Number of fixed variables. */ +static inline int Bounds_getNFX( Bounds* _THIS + ); + + +/** Returns a pointer to free variables index list. + * \return Pointer to free variables index list. */ +static inline Indexlist* Bounds_getFree( Bounds* _THIS + ); + +/** Returns a pointer to fixed variables index list. + * \return Pointer to fixed variables index list. */ +static inline Indexlist* Bounds_getFixed( Bounds* _THIS + ); + + +/** Returns number of bounds with given SubjectTo type. + * \return Number of bounds with given type. */ +static inline int Bounds_getNumberOfType( Bounds* _THIS, + SubjectToType _type /**< Type of bound. */ + ); + + +/** Returns type of bound. + * \return Type of bound \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline SubjectToType Bounds_getType( Bounds* _THIS, + int i /**< Number of bound. */ + ); + +/** Returns status of bound. + * \return Status of bound \n + ST_UNDEFINED */ +static inline SubjectToStatus Bounds_getStatus( Bounds* _THIS, + int i /**< Number of bound. */ + ); + + +/** Sets type of bound. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue Bounds_setType( Bounds* _THIS, + int i, /**< Number of bound. */ + SubjectToType value /**< Type of bound. */ + ); + +/** Sets status of bound. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue Bounds_setStatus( Bounds* _THIS, + int i, /**< Number of bound. */ + SubjectToStatus value /**< Status of bound. */ + ); + + +/** Sets status of lower bounds. */ +static inline void Bounds_setNoLower( Bounds* _THIS, + BooleanType _status /**< Status of lower bounds. */ + ); + +/** Sets status of upper bounds. */ +static inline void Bounds_setNoUpper( Bounds* _THIS, + BooleanType _status /**< Status of upper bounds. */ + ); + + +/** Returns status of lower bounds. + * \return BT_TRUE if there is no lower bound on any variable. */ +static inline BooleanType Bounds_hasNoLower( Bounds* _THIS + ); + +/** Returns status of upper bounds. + * \return BT_TRUE if there is no upper bound on any variable. */ +static inline BooleanType Bounds_hasNoUpper( Bounds* _THIS + ); + + +/** Shifts forward type and status of all bounds by a given + * offset. This offset has to lie within the range [0,n/2] and has to + * be an integer divisor of the total number of bounds n. + * Type and status of the first \ bounds is thrown away, + * type and status of the last \ bounds is doubled, + * e.g. for offset = 2: \n + * shift( {b1,b2,b3,b4,b5,b6} ) = {b3,b4,b5,b6,b5,b6} + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_INVALID_ARGUMENTS \n + RET_SHIFTING_FAILED */ +returnValue Bounds_shift( Bounds* _THIS, + int offset /**< Shift offset within the range [0,n/2] and integer divisor of n. */ + ); + +/** Rotates forward type and status of all bounds by a given + * offset. This offset has to lie within the range [0,n]. + * Example for offset = 2: \n + * rotate( {b1,b2,b3,b4,b5,b6} ) = {b3,b4,b5,b6,b1,b2} + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_ROTATING_FAILED */ +returnValue Bounds_rotate( Bounds* _THIS, + int offset /**< Rotation offset within the range [0,n]. */ + ); + + +/** Prints information on bounds object + * (in particular, lists of free and fixed bounds. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_CORRUPTED */ +returnValue Bounds_print( Bounds* _THIS + ); + + +/** Initially adds all numbers of new (i.e. not yet in the list) bounds to + * to the index set corresponding to the desired status; + * the order depends on the SujectToType of each index. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_BOUND_FAILED */ +returnValue Bounds_setupAll( Bounds* _THIS, + SubjectToStatus _status /**< Desired initial status for all bounds. */ + ); + + +/** Adds the index of a new bound to index set. + * \return SUCCESSFUL_RETURN \n + RET_ADDINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Bounds_addIndex( Bounds* _THIS, + Indexlist* const indexlist, /**< Index list to which the new index shall be added. */ + int newnumber, /**< Number of new bound. */ + SubjectToStatus newstatus /**< Status of new bound. */ + ); + +/** Removes the index of a bound from index set. + * \return SUCCESSFUL_RETURN \n + RET_REMOVEINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Bounds_removeIndex( Bounds* _THIS, + Indexlist* const indexlist, /**< Index list from which the new index shall be removed. */ + int removenumber /**< Number of bound to be removed. */ + ); + +/** Swaps the indices of two constraints or bounds within the index set. + * \return SUCCESSFUL_RETURN \n + RET_SWAPINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Bounds_swapIndex( Bounds* _THIS, + Indexlist* const indexlist, /**< Index list in which the indices shold be swapped. */ + int number1, /**< Number of first bound. */ + int number2 /**< Number of second bound. */ + ); + + + +/* + * g e t N u m b e r O f T y p e + */ +static inline int Bounds_getNumberOfType( Bounds* _THIS, SubjectToType _type ) +{ + int i; + int numberOfType = 0; + + if ( _THIS->type != 0 ) + { + for( i=0; i<_THIS->n; ++i ) + if ( _THIS->type[i] == _type ) + ++numberOfType; + } + + return numberOfType; +} + + +/* + * g e t T y p e + */ +static inline SubjectToType Bounds_getType( Bounds* _THIS, int i ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + return _THIS->type[i]; + + return ST_UNKNOWN; +} + + +/* + * g e t S t a t u s + */ +static inline SubjectToStatus Bounds_getStatus( Bounds* _THIS, int i ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + return _THIS->status[i]; + + return ST_UNDEFINED; +} + + +/* + * s e t T y p e + */ +static inline returnValue Bounds_setType( Bounds* _THIS, int i, SubjectToType value ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + { + _THIS->type[i] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +/* + * s e t S t a t u s + */ +static inline returnValue Bounds_setStatus( Bounds* _THIS, int i, SubjectToStatus value ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + { + _THIS->status[i] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +/* + * s e t N o L o w e r + */ +static inline void Bounds_setNoLower( Bounds* _THIS, BooleanType _status ) +{ + _THIS->noLower = _status; +} + + +/* + * s e t N o U p p e r + */ +static inline void Bounds_setNoUpper( Bounds* _THIS, BooleanType _status ) +{ + _THIS->noUpper = _status; +} + + +/* + * h a s N o L o w e r + */ +static inline BooleanType Bounds_hasNoLower( Bounds* _THIS ) +{ + return _THIS->noLower; +} + + +/* + * h a s N o U p p p e r + */ +static inline BooleanType Bounds_hasNoUpper( Bounds* _THIS ) +{ + return _THIS->noUpper; +} + + + +/* + * g e t N V + */ +static inline int Bounds_getNV( Bounds* _THIS ) +{ + return _THIS->n; +} + + +/* + * g e t N F V + */ +static inline int Bounds_getNFV( Bounds* _THIS ) +{ + return Bounds_getNumberOfType( _THIS,ST_EQUALITY ); +} + + +/* + * g e t N B V + */ +static inline int Bounds_getNBV( Bounds* _THIS ) +{ + return Bounds_getNumberOfType( _THIS,ST_BOUNDED ); +} + + +/* + * g e t N U V + */ +static inline int Bounds_getNUV( Bounds* _THIS ) +{ + return Bounds_getNumberOfType( _THIS,ST_UNBOUNDED ); +} + + +/* + * g e t N F R + */ +static inline int Bounds_getNFR( Bounds* _THIS ) +{ + return Indexlist_getLength( _THIS->freee ); +} + + +/* + * g e t N F X + */ +static inline int Bounds_getNFX( Bounds* _THIS ) +{ + return Indexlist_getLength( _THIS->fixed ); +} + + +/* + * g e t F r e e + */ +static inline Indexlist* Bounds_getFree( Bounds* _THIS ) +{ + return _THIS->freee; +} + + +/* + * g e t F i x e d + */ +static inline Indexlist* Bounds_getFixed( Bounds* _THIS ) +{ + return _THIS->fixed; +} + + +END_NAMESPACE_QPOASES + +#endif /* QPOASES_BOUNDS_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Constants.h b/phonelibs/acados/include/qpOASES_e/Constants.h new file mode 100644 index 0000000000..0e3dcd19f4 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Constants.h @@ -0,0 +1,134 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Constants.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Definition of all global constants. + */ + + +#ifndef QPOASES_CONSTANTS_H +#define QPOASES_CONSTANTS_H + + +#include + +#ifdef __CODE_GENERATION__ + + #define CONVERTTOSTRINGAUX(x) #x + #define CONVERTTOSTRING(x) CONVERTTOSTRINGAUX(x) + + #ifndef QPOASES_CUSTOM_INTERFACE + #include "acado_qpoases3_interface.h" + #else + #include CONVERTTOSTRING(QPOASES_CUSTOM_INTERFACE) + #endif + +#endif + + +BEGIN_NAMESPACE_QPOASES + + +#ifndef __EXTERNAL_DIMENSIONS__ + + /*#define QPOASES_NVMAX 50 + #define QPOASES_NCMAX 100*/ + #define QPOASES_NVMAX 287 + #define QPOASES_NCMAX 709 + +#endif /* __EXTERNAL_DIMENSIONS__ */ + + +/** Maximum number of variables within a QP formulation. + * Note: this value has to be positive! */ +#define NVMAX QPOASES_NVMAX + +/** Maximum number of constraints within a QP formulation. + * Note: this value has to be positive! */ +#define NCMAX QPOASES_NCMAX + +#if ( QPOASES_NVMAX > QPOASES_NCMAX ) +#define NVCMAX QPOASES_NVMAX +#else +#define NVCMAX QPOASES_NCMAX +#endif + +#if ( QPOASES_NVMAX > QPOASES_NCMAX ) +#define NVCMIN QPOASES_NCMAX +#else +#define NVCMIN QPOASES_NVMAX +#endif + + +/** Maximum number of QPs in a sequence solved by means of the OQP interface. + * Note: this value has to be positive! */ +#define NQPMAX 1000 + + +/** Numerical value of machine precision (min eps, s.t. 1+eps > 1). + * Note: this value has to be positive! */ +#ifndef __CODE_GENERATION__ + + #ifdef __USE_SINGLE_PRECISION__ + static const real_t QPOASES_EPS = 1.193e-07; + #else + static const real_t QPOASES_EPS = 2.221e-16; + #endif /* __USE_SINGLE_PRECISION__ */ + +#endif /* __CODE_GENERATION__ */ + + +/** Numerical value of zero (for situations in which it would be + * unreasonable to compare with 0.0). + * Note: this value has to be positive! */ +static const real_t QPOASES_ZERO = 1.0e-25; + +/** Numerical value of infinity (e.g. for non-existing bounds). + * Note: this value has to be positive! */ +static const real_t QPOASES_INFTY = 1.0e20; + +/** Tolerance to used for isEqual, isZero etc. + * Note: this value has to be positive! */ +static const real_t QPOASES_TOL = 1.0e-25; + + +/** Maximum number of characters within a string. + * Note: this value should be at least 41! */ +#define QPOASES_MAX_STRING_LENGTH 160 + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_CONSTANTS_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/ConstraintProduct.h b/phonelibs/acados/include/qpOASES_e/ConstraintProduct.h new file mode 100644 index 0000000000..ddfcfbe5dc --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/ConstraintProduct.h @@ -0,0 +1,62 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/ConstraintProduct.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches (thanks to D. Kwame Minde Kufoalor) + * \version 3.1embedded + * \date 2009-2015 + * + * Declaration of the ConstraintProduct interface which allows to specify a + * user-defined function for evaluating the constraint product at the + * current iterate to speed-up QP solution in case of a specially structured + * constraint matrix. + */ + + + +#ifndef QPOASES_CONSTRAINT_PRODUCT_H +#define QPOASES_CONSTRAINT_PRODUCT_H + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Interface for specifying user-defined evaluations of constraint products. + * + * An interface which allows to specify a user-defined function for evaluating the + * constraint product at the current iterate to speed-up QP solution in case + * of a specially structured constraint matrix. + * + * \author Hans Joachim Ferreau (thanks to Kwame Minde Kufoalor) + * \version 3.1embedded + * \date 2009-2015 + */ +typedef int(*ConstraintProduct)( int, const real_t* const, real_t* const ); + + +END_NAMESPACE_QPOASES + +#endif /* QPOASES_CONSTRAINT_PRODUCT_H */ diff --git a/phonelibs/acados/include/qpOASES_e/Constraints.h b/phonelibs/acados/include/qpOASES_e/Constraints.h new file mode 100644 index 0000000000..8aca10d5d9 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Constraints.h @@ -0,0 +1,535 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Constraints.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the Constraints class designed to manage working sets of + * constraints within a QProblem. + */ + + +#ifndef QPOASES_CONSTRAINTS_H +#define QPOASES_CONSTRAINTS_H + + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Manages working sets of constraints. + * + * This class manages working sets of constraints by storing + * index sets and other status information. + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + Indexlist *active; /**< Index list of active constraints. */ + Indexlist *inactive; /**< Index list of inactive constraints. */ + + Indexlist *shiftedActive; /**< Memory for shifting active constraints. */ + Indexlist *shiftedInactive; /**< Memory for shifting inactive constraints. */ + + Indexlist *rotatedActive; /**< Memory for rotating active constraints. */ + Indexlist *rotatedInactive; /**< Memory for rotating inactive constraints. */ + + SubjectToType *type; /**< Type of constraints. */ + SubjectToStatus *status; /**< Status of constraints. */ + + SubjectToType *typeTmp; /**< Temp memory for type of constraints. */ + SubjectToStatus *statusTmp; /**< Temp memory for status of constraints. */ + + BooleanType noLower; /**< This flag indicates if there is no lower bound on any variable. */ + BooleanType noUpper; /**< This flag indicates if there is no upper bound on any variable. */ + + int n; /**< Total number of constraints. */ +} Constraints; + +int Constraints_calculateMemorySize( int n); + +char *Constraints_assignMemory(int n, Constraints **mem, void *raw_memory); + +Constraints *Constraints_createMemory( int n ); + +/** Constructor which takes the number of constraints. */ +void ConstraintsCON( Constraints* _THIS, + int _n /**< Number of constraints. */ + ); + +/** Copies all members from given rhs object. + * \return SUCCESSFUL_RETURN */ +void ConstraintsCPY( Constraints* FROM, + Constraints* TO + ); + + +/** Initialises object with given number of constraints. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue Constraints_init( Constraints* _THIS, + int _n /**< Number of constraints. */ + ); + + +/** Initially adds number of a new (i.e. not yet in the list) constraint to + * a given index set. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_CONSTRAINT_FAILED \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_INVALID_ARGUMENTS */ +returnValue Constraints_setupConstraint( Constraints* _THIS, + int number, /**< Number of new constraint. */ + SubjectToStatus _status /**< Status of new constraint. */ + ); + +/** Initially adds all enabled numbers of new (i.e. not yet in the list) constraints to + * to the index set of inactive constraints; the order depends on the SujectToType + * of each index. Only disabled constraints are added to index set of disabled constraints! + * \return SUCCESSFUL_RETURN \n + RET_SETUP_CONSTRAINT_FAILED */ +returnValue Constraints_setupAllInactive( Constraints* _THIS + ); + +/** Initially adds all enabled numbers of new (i.e. not yet in the list) constraints to + * to the index set of active constraints (on their lower bounds); the order depends on the SujectToType + * of each index. Only disabled constraints are added to index set of disabled constraints! + * \return SUCCESSFUL_RETURN \n + RET_SETUP_CONSTRAINT_FAILED */ +returnValue Constraints_setupAllLower( Constraints* _THIS + ); + +/** Initially adds all enabled numbers of new (i.e. not yet in the list) constraints to + * to the index set of active constraints (on their upper bounds); the order depends on the SujectToType + * of each index. Only disabled constraints are added to index set of disabled constraints! + * \return SUCCESSFUL_RETURN \n + RET_SETUP_CONSTRAINT_FAILED */ +returnValue Constraints_setupAllUpper( Constraints* _THIS + ); + + +/** Moves index of a constraint from index list of active to that of inactive constraints. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_CONSTRAINT_FAILED */ +returnValue Constraints_moveActiveToInactive( Constraints* _THIS, + int number /**< Number of constraint to become inactive. */ + ); + +/** Moves index of a constraint from index list of inactive to that of active constraints. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_CONSTRAINT_FAILED */ +returnValue Constraints_moveInactiveToActive( Constraints* _THIS, + int number, /**< Number of constraint to become active. */ + SubjectToStatus _status /**< Status of constraint to become active. */ + ); + +/** Flip fixed constraint. + * \return SUCCESSFUL_RETURN \n + RET_MOVING_CONSTRAINT_FAILED \n + RET_INDEX_OUT_OF_BOUNDS */ +returnValue Constraints_flipFixed( Constraints* _THIS, + int number + ); + + +/** Returns the number of constraints. + * \return Number of constraints. */ +static inline int Constraints_getNC( Constraints* _THIS + ); + +/** Returns the number of implicit equality constraints. + * \return Number of implicit equality constraints. */ +static inline int Constraints_getNEC( Constraints* _THIS + ); + +/** Returns the number of "real" inequality constraints. + * \return Number of "real" inequality constraints. */ +static inline int Constraints_getNIC( Constraints* _THIS + ); + +/** Returns the number of unbounded constraints (i.e. without any bounds). + * \return Number of unbounded constraints (i.e. without any bounds). */ +static inline int Constraints_getNUC( Constraints* _THIS + ); + +/** Returns the number of active constraints. + * \return Number of active constraints. */ +static inline int Constraints_getNAC( Constraints* _THIS + ); + +/** Returns the number of inactive constraints. + * \return Number of inactive constraints. */ +static inline int Constraints_getNIAC( Constraints* _THIS + ); + + +/** Returns a pointer to active constraints index list. + * \return Pointer to active constraints index list. */ +static inline Indexlist* Constraints_getActive( Constraints* _THIS + ); + +/** Returns a pointer to inactive constraints index list. + * \return Pointer to inactive constraints index list. */ +static inline Indexlist* Constraints_getInactive( Constraints* _THIS + ); + + +/** Returns number of constraints with given SubjectTo type. + * \return Number of constraints with given type. */ +static inline int Constraints_getNumberOfType( Constraints* _THIS, + SubjectToType _type /**< Type of constraints' bound. */ + ); + + +/** Returns type of constraints' bound. + * \return Type of constraints' bound \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline SubjectToType Constraints_getType( Constraints* _THIS, + int i /**< Number of constraints' bound. */ + ); + +/** Returns status of constraints' bound. + * \return Status of constraints' bound \n + ST_UNDEFINED */ +static inline SubjectToStatus Constraints_getStatus( Constraints* _THIS, + int i /**< Number of constraints' bound. */ + ); + + +/** Sets type of constraints' bound. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue Constraints_setType( Constraints* _THIS, + int i, /**< Number of constraints' bound. */ + SubjectToType value /**< Type of constraints' bound. */ + ); + +/** Sets status of constraints' bound. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue Constraints_setStatus( Constraints* _THIS, + int i, /**< Number of constraints' bound. */ + SubjectToStatus value /**< Status of constraints' bound. */ + ); + + +/** Sets status of lower constraints' bounds. */ +static inline void Constraints_setNoLower( Constraints* _THIS, + BooleanType _status /**< Status of lower constraints' bounds. */ + ); + +/** Sets status of upper constraints' bounds. */ +static inline void Constraints_setNoUpper( Constraints* _THIS, + BooleanType _status /**< Status of upper constraints' bounds. */ + ); + + +/** Returns status of lower constraints' bounds. + * \return BT_TRUE if there is no lower constraints' bound on any variable. */ +static inline BooleanType Constraints_hasNoLower( Constraints* _THIS + ); + +/** Returns status of upper bounds. + * \return BT_TRUE if there is no upper constraints' bound on any variable. */ +static inline BooleanType Constraints_hasNoUpper( Constraints* _THIS + ); + + +/** Shifts forward type and status of all constraints by a given + * offset. This offset has to lie within the range [0,n/2] and has to + * be an integer divisor of the total number of constraints n. + * Type and status of the first \ constraints is thrown away, + * type and status of the last \ constraints is doubled, + * e.g. for offset = 2: \n + * shift( {c/b1,c/b2,c/b3,c/b4,c/b5,c/b6} ) = {c/b3,c/b4,c/b5,c/b6,c/b5,c/b6} + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_INVALID_ARGUMENTS \n + RET_SHIFTING_FAILED */ +returnValue Constraints_shift( Constraints* _THIS, + int offset /**< Shift offset within the range [0,n/2] and integer divisor of n. */ + ); + +/** Rotates forward type and status of all constraints by a given + * offset. This offset has to lie within the range [0,n]. + * Example for offset = 2: \n + * rotate( {c1,c2,c3,c4,c5,c6} ) = {c3,c4,c5,c6,c1,c2} + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS \n + RET_ROTATING_FAILED */ +returnValue Constraints_rotate( Constraints* _THIS, + int offset /**< Rotation offset within the range [0,n]. */ + ); + + +/** Prints information on constraints object + * (in particular, lists of inactive and active constraints. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_CORRUPTED */ +returnValue Constraints_print( Constraints* _THIS + ); + + +/** Initially adds all numbers of new (i.e. not yet in the list) bounds to + * to the index set corresponding to the desired status; + * the order depends on the SujectToType of each index. + * \return SUCCESSFUL_RETURN \n + RET_SETUP_CONSTRAINT_FAILED */ +returnValue Constraints_setupAll( Constraints* _THIS, + SubjectToStatus _status /**< Desired initial status for all bounds. */ + ); + + +/** Adds the index of a new constraint to index set. + * \return SUCCESSFUL_RETURN \n + RET_ADDINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Constraints_addIndex( Constraints* _THIS, + Indexlist* const indexlist, /**< Index list to which the new index shall be added. */ + int newnumber, /**< Number of new constraint. */ + SubjectToStatus newstatus /**< Status of new constraint. */ + ); + +/** Removes the index of a constraint from index set. + * \return SUCCESSFUL_RETURN \n + RET_REMOVEINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Constraints_removeIndex( Constraints* _THIS, + Indexlist* const indexlist, /**< Index list from which the new index shall be removed. */ + int removenumber /**< Number of constraint to be removed. */ + ); + +/** Swaps the indices of two constraints or bounds within the index set. + * \return SUCCESSFUL_RETURN \n + RET_SWAPINDEX_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue Constraints_swapIndex( Constraints* _THIS, + Indexlist* const indexlist, /**< Index list in which the indices shold be swapped. */ + int number1, /**< Number of first constraint. */ + int number2 /**< Number of second constraint. */ + ); + + + +/* + * g e t N u m b e r O f T y p e + */ +static inline int Constraints_getNumberOfType( Constraints* _THIS, SubjectToType _type ) +{ + int i; + int numberOfType = 0; + + if ( _THIS->type != 0 ) + { + for( i=0; i<_THIS->n; ++i ) + if ( _THIS->type[i] == _type ) + ++numberOfType; + } + + return numberOfType; +} + + +/* + * g e t T y p e + */ +static inline SubjectToType Constraints_getType( Constraints* _THIS, int i ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + return _THIS->type[i]; + + return ST_UNKNOWN; +} + + +/* + * g e t S t a t u s + */ +static inline SubjectToStatus Constraints_getStatus( Constraints* _THIS, int i ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + return _THIS->status[i]; + + return ST_UNDEFINED; +} + + +/* + * s e t T y p e + */ +static inline returnValue Constraints_setType( Constraints* _THIS, int i, SubjectToType value ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + { + _THIS->type[i] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +/* + * s e t S t a t u s + */ +static inline returnValue Constraints_setStatus( Constraints* _THIS, int i, SubjectToStatus value ) +{ + if ( ( i >= 0 ) && ( i < _THIS->n ) ) + { + _THIS->status[i] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +/* + * s e t N o L o w e r + */ +static inline void Constraints_setNoLower( Constraints* _THIS, BooleanType _status ) +{ + _THIS->noLower = _status; +} + + +/* + * s e t N o U p p e r + */ +static inline void Constraints_setNoUpper( Constraints* _THIS, BooleanType _status ) +{ + _THIS->noUpper = _status; +} + + +/* + * h a s N o L o w e r + */ +static inline BooleanType Constraints_hasNoLower( Constraints* _THIS ) +{ + return _THIS->noLower; +} + + +/* + * h a s N o U p p p e r + */ +static inline BooleanType Constraints_hasNoUpper( Constraints* _THIS ) +{ + return _THIS->noUpper; +} + + + +/* + * g e t N C + */ +static inline int Constraints_getNC( Constraints* _THIS ) +{ + return _THIS->n; +} + + +/* + * g e t N E C + */ +static inline int Constraints_getNEC( Constraints* _THIS ) +{ + return Constraints_getNumberOfType( _THIS,ST_EQUALITY ); +} + + +/* + * g e t N I C + */ +static inline int Constraints_getNIC( Constraints* _THIS ) +{ + return Constraints_getNumberOfType( _THIS,ST_BOUNDED ); +} + + +/* + * g e t N U C + */ +static inline int Constraints_getNUC( Constraints* _THIS ) +{ + return Constraints_getNumberOfType( _THIS,ST_UNBOUNDED ); +} + + +/* + * g e t N A C + */ +static inline int Constraints_getNAC( Constraints* _THIS ) +{ + return Indexlist_getLength( _THIS->active ); +} + + +/* + * g e t N I A C + */ +static inline int Constraints_getNIAC( Constraints* _THIS ) +{ + return Indexlist_getLength( _THIS->inactive ); +} + + + +/* + * g e t A c t i v e + */ +static inline Indexlist* Constraints_getActive( Constraints* _THIS ) +{ + return _THIS->active; +} + + +/* + * g e t I n a c t i v e + */ +static inline Indexlist* Constraints_getInactive( Constraints* _THIS ) +{ + return _THIS->inactive; +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_CONSTRAINTS_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Flipper.h b/phonelibs/acados/include/qpOASES_e/Flipper.h new file mode 100644 index 0000000000..63526a30f3 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Flipper.h @@ -0,0 +1,129 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Flipper.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the Options class designed to manage user-specified + * options for solving a QProblem. + */ + + +#ifndef QPOASES_FLIPPER_H +#define QPOASES_FLIPPER_H + + +#include +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Auxiliary class for storing a copy of the current matrix factorisations. + * + * This auxiliary class stores a copy of the current matrix factorisations. It + * is used by the classe QProblemB and QProblem in case flipping bounds are enabled. + * + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + Bounds *bounds; /**< Data structure for problem's bounds. */ + Constraints *constraints; /**< Data structure for problem's constraints. */ + + real_t *R; /**< Cholesky factor of H (i.e. H = R^T*R). */ + real_t *Q; /**< Orthonormal quadratic matrix, A = [0 T]*Q'. */ + real_t *T; /**< Reverse triangular matrix, A = [0 T]*Q'. */ + + unsigned int nV; /**< Number of variables. */ + unsigned int nC; /**< Number of constraints. */ +} Flipper; + +int Flipper_calculateMemorySize( unsigned int nV, unsigned int nC ); + +char *Flipper_assignMemory( unsigned int nV, unsigned int nC, Flipper **mem, void *raw_memory ); + +Flipper *Flipper_createMemory( unsigned int nV, unsigned int nC ); + +/** Constructor which takes the number of bounds and constraints. */ +void FlipperCON( Flipper* _THIS, + unsigned int _nV, /**< Number of bounds. */ + unsigned int _nC /**< Number of constraints. */ + ); + +/** Copy constructor (deep copy). */ +void FlipperCPY( Flipper* FROM, + Flipper* TO + ); + +/** Initialises object with given number of bounds and constraints. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue Flipper_init( Flipper* _THIS, + unsigned int _nV, /**< Number of bounds. */ + unsigned int _nC /**< Number of constraints. */ + ); + + +/** Copies current values to non-null arguments (assumed to be allocated with consistent size). + * \return SUCCESSFUL_RETURN */ +returnValue Flipper_get( Flipper* _THIS, + Bounds* const _bounds, /**< Pointer to new bounds. */ + real_t* const R, /**< New matrix R. */ + Constraints* const _constraints, /**< Pointer to new constraints. */ + real_t* const _Q, /**< New matrix Q. */ + real_t* const _T /**< New matrix T. */ + ); + +/** Assigns new values to non-null arguments. + * \return SUCCESSFUL_RETURN */ +returnValue Flipper_set( Flipper* _THIS, + const Bounds* const _bounds, /**< Pointer to new bounds. */ + const real_t* const _R, /**< New matrix R. */ + const Constraints* const _constraints, /**< Pointer to new constraints. */ + const real_t* const _Q, /**< New matrix Q. */ + const real_t* const _T /**< New matrix T. */ + ); + +/** Returns dimension of matrix T. + * \return Dimension of matrix T. */ +unsigned int Flipper_getDimT( Flipper* _THIS ); + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_FLIPPER_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Indexlist.h b/phonelibs/acados/include/qpOASES_e/Indexlist.h new file mode 100644 index 0000000000..c3026a7ffc --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Indexlist.h @@ -0,0 +1,221 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Indexlist.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the Indexlist class designed to manage index lists of + * constraints and bounds within a SubjectTo object. + */ + + +#ifndef QPOASES_INDEXLIST_H +#define QPOASES_INDEXLIST_H + + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Stores and manages index lists. + * + * This class manages index lists of active/inactive bounds/constraints. + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + int *number; /**< Array to store numbers of constraints or bounds. */ + int *iSort; /**< Index list to sort vector \a number */ + + int length; /**< Length of index list. */ + int first; /**< Physical index of first element. */ + int last; /**< Physical index of last element. */ + int lastusedindex; /**< Physical index of last entry in index list. */ + int physicallength; /**< Physical length of index list. */ +} Indexlist; + +int Indexlist_calculateMemorySize( int n); + +char *Indexlist_assignMemory(int n, Indexlist **mem, void *raw_memory); + +Indexlist *Indexlist_createMemory( int n ); + +/** Constructor which takes the desired physical length of the index list. */ +void IndexlistCON( Indexlist* _THIS, + int n /**< Physical length of index list. */ + ); + +/** Copies all members from given rhs object. + * \return SUCCESSFUL_RETURN */ +void IndexlistCPY( Indexlist* FROM, + Indexlist* TO + ); + +/** Initialises index list of desired physical length. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue Indexlist_init( Indexlist* _THIS, + int n /**< Physical length of index list. */ + ); + +/** Creates an array of all numbers within the index set in correct order. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_CORRUPTED */ +returnValue Indexlist_getNumberArray( Indexlist* _THIS, + int** const numberarray /**< Output: Array of numbers (NULL on error). */ + ); + +/** Creates an array of all numbers within the index set in correct order. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_CORRUPTED */ +returnValue Indexlist_getISortArray( Indexlist* _THIS, + int** const iSortArray /**< Output: iSort Array. */ + ); + + +/** Determines the index within the index list at which a given number is stored. + * \return >= 0: Index of given number. \n + -1: Number not found. */ +int Indexlist_getIndex( Indexlist* _THIS, + int givennumber /**< Number whose index shall be determined. */ + ); + +/** Returns the number stored at a given physical index. + * \return >= 0: Number stored at given physical index. \n + -RET_INDEXLIST_OUTOFBOUNDS */ +static inline int Indexlist_getNumber( Indexlist* _THIS, + int physicalindex /**< Physical index of the number to be returned. */ + ); + + +/** Returns the current length of the index list. + * \return Current length of the index list. */ +static inline int Indexlist_getLength( Indexlist* _THIS + ); + +/** Returns last number within the index list. + * \return Last number within the index list. */ +static inline int Indexlist_getLastNumber( Indexlist* _THIS + ); + + +/** Adds number to index list. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_MUST_BE_REORDERD \n + RET_INDEXLIST_EXCEEDS_MAX_LENGTH */ +returnValue Indexlist_addNumber( Indexlist* _THIS, + int addnumber /**< Number to be added. */ + ); + +/** Removes number from index list. + * \return SUCCESSFUL_RETURN */ +returnValue Indexlist_removeNumber( Indexlist* _THIS, + int removenumber /**< Number to be removed. */ + ); + +/** Swaps two numbers within index list. + * \return SUCCESSFUL_RETURN */ +returnValue Indexlist_swapNumbers( Indexlist* _THIS, + int number1, /**< First number for swapping. */ + int number2 /**< Second number for swapping. */ + ); + +/** Determines if a given number is contained in the index set. + * \return BT_TRUE iff number is contain in the index set */ +static inline BooleanType Indexlist_isMember( Indexlist* _THIS, + int _number /**< Number to be tested for membership. */ + ); + + +/** Find first index j between -1 and length in sorted list of indices + * iSort such that numbers[iSort[j]] <= i < numbers[iSort[j+1]]. Uses + * bisection. + * \return j. */ +int Indexlist_findInsert( Indexlist* _THIS, + int i + ); + + + +/* + * g e t N u m b e r + */ +static inline int Indexlist_getNumber( Indexlist* _THIS, int physicalindex ) +{ + /* consistency check */ + if ( ( physicalindex < 0 ) || ( physicalindex > _THIS->length ) ) + return -RET_INDEXLIST_OUTOFBOUNDS; + + return _THIS->number[physicalindex]; +} + + +/* + * g e t L e n g t h + */ +static inline int Indexlist_getLength( Indexlist* _THIS ) +{ + return _THIS->length; +} + + +/* + * g e t L a s t N u m b e r + */ +static inline int Indexlist_getLastNumber( Indexlist* _THIS ) +{ + return _THIS->number[_THIS->length-1]; +} + + +/* + * g e t L a s t N u m b e r + */ +static inline BooleanType Indexlist_isMember( Indexlist* _THIS, int _number ) +{ + if ( Indexlist_getIndex( _THIS,_number ) >= 0 ) + return BT_TRUE; + else + return BT_FALSE; +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_INDEXLIST_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Matrices.h b/phonelibs/acados/include/qpOASES_e/Matrices.h new file mode 100644 index 0000000000..1d6da8c3c1 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Matrices.h @@ -0,0 +1,287 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Matrices.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2009-2015 + * + * Various matrix classes: Abstract base matrix class, dense and sparse matrices, + * including symmetry exploiting specializations. + */ + + + +#ifndef QPOASES_MATRICES_H +#define QPOASES_MATRICES_H + +#ifdef __USE_SINGLE_PRECISION__ + + // single precision + #define GEMM sgemm_ + #define GEMV sgemv_ +// #define SYR ssyr_ +// #define SYR2 ssyr2_ + #define POTRF spotrf_ + +#else + + // double precision + #define GEMM dgemm_ + #define GEMV dgemv_ +// #define SYR dsyr_ +// #define SYR2 dsyr2_ + #define POTRF dpotrf_ + +#endif /* __USE_SINGLE_PRECISION__ */ + + +#ifdef EXTERNAL_BLAS + // double precision + void dgemm_(char *ta, char *tb, int *m, int *n, int *k, double *alpha, double *A, int *lda, double *B, int ldb, double *beta, double *C, int *ldc); + void dgemv_(char *ta, int *m, int *n, double *alpha, double *A, int *lda, double *x, int *incx, double *beta, double *y, int *incy); + void dpotrf_(char *uplo, int *m, double *A, int *lda, int *info); + // single precision + void sgemm_(char *ta, char *tb, int *m, int *n, int *k, float *alpha, float *A, int *lda, float *B, int ldb, float *beta, float *C, int *ldc); + void sgemv_(char *ta, int *m, int *n, float *alpha, float *A, int *lda, float *x, int *incx, float *beta, float *y, int *incy); + void spotrf_(char *uplo, int *m, float *A, int *lda, int *info); +#else + /** Performs one of the matrix-matrix operation in double precision. */ + void dgemm_ ( const char*, const char*, const unsigned long*, const unsigned long*, const unsigned long*, + const double*, const double*, const unsigned long*, const double*, const unsigned long*, + const double*, double*, const unsigned long* ); + /** Performs one of the matrix-matrix operation in single precision. */ + void sgemm_ ( const char*, const char*, const unsigned long*, const unsigned long*, const unsigned long*, + const float*, const float*, const unsigned long*, const float*, const unsigned long*, + const float*, float*, const unsigned long* ); + + /** Calculates the Cholesky factorization of a real symmetric positive definite matrix in double precision. */ + void dpotrf_ ( const char *, const unsigned long *, double *, const unsigned long *, long * ); + /** Calculates the Cholesky factorization of a real symmetric positive definite matrix in single precision. */ + void spotrf_ ( const char *, const unsigned long *, float *, const unsigned long *, long * ); + +#endif + + /** Performs a symmetric rank 1 operation in double precision. */ +// void dsyr_ ( const char *, const unsigned long *, const double *, const double *, +// const unsigned long *, double *, const unsigned long *); + /** Performs a symmetric rank 1 operation in single precision. */ +// void ssyr_ ( const char *, const unsigned long *, const float *, const float *, +// const unsigned long *, float *, const unsigned long *); + + /** Performs a symmetric rank 2 operation in double precision. */ +// void dsyr2_ ( const char *, const unsigned long *, const double *, const double *, +// const unsigned long *, const double *, const unsigned long *, double *, const unsigned long *); + /** Performs a symmetric rank 2 operation in single precision. */ +// void ssyr2_ ( const char *, const unsigned long *, const float *, const float *, +// const unsigned long *, const float *, const unsigned long *, float *, const unsigned long *); + + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Interfaces matrix-vector operations tailored to general dense matrices. + * + * Dense matrix class (row major format). + * + * \author Andreas Potschka, Christian Kirches, Hans Joachim Ferreau + * \version 3.1embedded + * \date 2011-2015 + */ +typedef struct +{ + real_t *val; /**< Vector of entries. */ + int nRows; /**< Number of rows. */ + int nCols; /**< Number of columns. */ + int leaDim; /**< Leading dimension. */ +} DenseMatrix; + +int DenseMatrix_calculateMemorySize( int m, int n ); + +char *DenseMatrix_assignMemory( int m, int n, DenseMatrix **mem, void *raw_memory ); + +DenseMatrix *DenseMatrix_createMemory( int m, int n ); + +/** Constructor from vector of values. + * Caution: Data pointer must be valid throughout lifetime + */ +void DenseMatrixCON( DenseMatrix* _THIS, + int m, /**< Number of rows. */ + int n, /**< Number of columns. */ + int lD, /**< Leading dimension. */ + real_t *v /**< Values. */ + ); + +void DenseMatrixCPY( DenseMatrix* FROM, + DenseMatrix* TO + ); + + +/** Frees all internal memory. */ +void DenseMatrix_free( DenseMatrix* _THIS ); + +/** Constructor from vector of values. + * Caution: Data pointer must be valid throughout lifetime + */ +returnValue DenseMatrix_init( DenseMatrix* _THIS, + int m, /**< Number of rows. */ + int n, /**< Number of columns. */ + int lD, /**< Leading dimension. */ + real_t *v /**< Values. */ + ); + + +/** Returns i-th diagonal entry. + * \return i-th diagonal entry */ +real_t DenseMatrix_diag( DenseMatrix* _THIS, + int i /**< Index. */ + ); + +/** Checks whether matrix is square and diagonal. + * \return BT_TRUE iff matrix is square and diagonal; \n + * BT_FALSE otherwise. */ +BooleanType DenseMatrix_isDiag( DenseMatrix* _THIS ); + +/** Get the N-norm of the matrix + * \return N-norm of the matrix + */ +real_t DenseMatrix_getNorm( DenseMatrix* _THIS, + int type /**< Norm type, 1: one-norm, 2: Euclidean norm. */ + ); + +/** Get the N-norm of a row + * \return N-norm of row \a rNum + */ +real_t DenseMatrix_getRowNorm( DenseMatrix* _THIS, + int rNum, /**< Row number. */ + int type /**< Norm type, 1: one-norm, 2: Euclidean norm. */ + ); + +/** Retrieve indexed entries of matrix row multiplied by alpha. + * \return SUCCESSFUL_RETURN */ +returnValue DenseMatrix_getRow( DenseMatrix* _THIS, + int rNum, /**< Row number. */ + const Indexlist* const icols, /**< Index list specifying columns. */ + real_t alpha, /**< Scalar factor. */ + real_t *row /**< Output row vector. */ + ); + +/** Retrieve indexed entries of matrix column multiplied by alpha. + * \return SUCCESSFUL_RETURN */ + returnValue DenseMatrix_getCol( DenseMatrix* _THIS, + int cNum, /**< Column number. */ + const Indexlist* const irows, /**< Index list specifying rows. */ + real_t alpha, /**< Scalar factor. */ + real_t *col /**< Output column vector. */ + ); + +/** Evaluate Y=alpha*A*X + beta*Y. + * \return SUCCESSFUL_RETURN. */ +returnValue DenseMatrix_times( DenseMatrix* _THIS, + int xN, /**< Number of vectors to multiply. */ + real_t alpha, /**< Scalar factor for matrix vector product. */ + const real_t *x, /**< Input vector to be multiplied. */ + int xLD, /**< Leading dimension of input x. */ + real_t beta, /**< Scalar factor for y. */ + real_t *y, /**< Output vector of results. */ + int yLD /**< Leading dimension of output y. */ + ); + +/** Evaluate Y=alpha*A'*X + beta*Y. + * \return SUCCESSFUL_RETURN. */ +returnValue DenseMatrix_transTimes( DenseMatrix* _THIS, + int xN, /**< Number of vectors to multiply. */ + real_t alpha, /**< Scalar factor for matrix vector product. */ + const real_t *x, /**< Input vector to be multiplied. */ + int xLD, /**< Leading dimension of input x. */ + real_t beta, /**< Scalar factor for y. */ + real_t *y, /**< Output vector of results. */ + int yLD /**< Leading dimension of output y. */ + ); + +/** Evaluate matrix vector product with submatrix given by Indexlist. + * \return SUCCESSFUL_RETURN */ + returnValue DenseMatrix_subTimes( DenseMatrix* _THIS, + const Indexlist* const irows, /**< Index list specifying rows. */ + const Indexlist* const icols, /**< Index list specifying columns. */ + int xN, /**< Number of vectors to multiply. */ + real_t alpha, /**< Scalar factor for matrix vector product. */ + const real_t *x, /**< Input vector to be multiplied. */ + int xLD, /**< Leading dimension of input x. */ + real_t beta, /**< Scalar factor for y. */ + real_t *y, /**< Output vector of results. */ + int yLD, /**< Leading dimension of output y. */ + BooleanType yCompr /**< Compressed storage for y. */ + ); + +/** Evaluate matrix transpose vector product. + * \return SUCCESSFUL_RETURN */ +returnValue DenseMatrix_subTransTimes( DenseMatrix* _THIS, + const Indexlist* const irows, /**< Index list specifying rows. */ + const Indexlist* const icols, /**< Index list specifying columns. */ + int xN, /**< Number of vectors to multiply. */ + real_t alpha, /**< Scalar factor for matrix vector product. */ + const real_t *x, /**< Input vector to be multiplied. */ + int xLD, /**< Leading dimension of input x. */ + real_t beta, /**< Scalar factor for y. */ + real_t *y, /**< Output vector of results. */ + int yLD /**< Leading dimension of output y. */ + ); + +/** Adds given offset to diagonal of matrix. + * \return SUCCESSFUL_RETURN \n + RET_NO_DIAGONAL_AVAILABLE */ +returnValue DenseMatrix_addToDiag( DenseMatrix* _THIS, + real_t alpha /**< Diagonal offset. */ + ); + +/** Prints matrix to screen. + * \return SUCCESSFUL_RETURN */ +returnValue DenseMatrix_print( DenseMatrix* _THIS + ); + +static inline real_t* DenseMatrix_getVal( DenseMatrix* _THIS ) { return _THIS->val; } + +/** Compute bilinear form y = x'*H*x using submatrix given by index list. + * \return SUCCESSFUL_RETURN */ +returnValue DenseMatrix_bilinear( DenseMatrix* _THIS, + const Indexlist* const icols, /**< Index list specifying columns of x. */ + int xN, /**< Number of vectors to multiply. */ + const real_t *x, /**< Input vector to be multiplied (uncompressed). */ + int xLD, /**< Leading dimension of input x. */ + real_t *y, /**< Output vector of results (compressed). */ + int yLD /**< Leading dimension of output y. */ + ); + + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_MATRICES_H */ diff --git a/phonelibs/acados/include/qpOASES_e/MessageHandling.h b/phonelibs/acados/include/qpOASES_e/MessageHandling.h new file mode 100644 index 0000000000..fe5524948a --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/MessageHandling.h @@ -0,0 +1,544 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/MessageHandling.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches (thanks to Leonard Wirsching) + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the MessageHandling class including global return values. + */ + + +#ifndef QPOASES_MESSAGEHANDLING_H +#define QPOASES_MESSAGEHANDLING_H + + +#include +#include +#include +#include + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** Default file to display messages. */ +#define stdFile stderr + + +/** + * \brief Defines all symbols for global return values. + * + * The enumeration returnValueType defines all symbols for global return values. + * Important: All return values are assumed to be nonnegative! + * + * \author Hans Joachim Ferreau + */ +typedef enum +{ +TERMINAL_LIST_ELEMENT = -1, /**< Terminal list element, internal usage only! */ +/* miscellaneous */ +SUCCESSFUL_RETURN = 0, /**< Successful return. */ +RET_DIV_BY_ZERO, /**< Division by zero. */ +RET_INDEX_OUT_OF_BOUNDS, /**< Index out of bounds. */ +RET_INVALID_ARGUMENTS, /**< At least one of the arguments is invalid. */ +RET_ERROR_UNDEFINED, /**< Error number undefined. */ +RET_WARNING_UNDEFINED, /**< Warning number undefined. */ +RET_INFO_UNDEFINED, /**< Info number undefined. */ +RET_EWI_UNDEFINED, /**< Error/warning/info number undefined. */ +RET_AVAILABLE_WITH_LINUX_ONLY, /**< This function is available under Linux only. */ +RET_UNKNOWN_BUG, /**< The error occurred is not yet known. */ +RET_PRINTLEVEL_CHANGED, /**< Print level changed. (10) */ +RET_NOT_YET_IMPLEMENTED, /**< Requested function is not yet implemented in this version of qpOASES. */ +/* Indexlist */ +RET_INDEXLIST_MUST_BE_REORDERD, /**< Index list has to be reordered. */ +RET_INDEXLIST_EXCEEDS_MAX_LENGTH, /**< Index list exceeds its maximal physical length. */ +RET_INDEXLIST_CORRUPTED, /**< Index list corrupted. */ +RET_INDEXLIST_OUTOFBOUNDS, /**< Physical index is out of bounds. */ +RET_INDEXLIST_ADD_FAILED, /**< Adding indices from another index set failed. */ +RET_INDEXLIST_INTERSECT_FAILED, /**< Intersection with another index set failed. */ +/* SubjectTo / Bounds / Constraints */ +RET_INDEX_ALREADY_OF_DESIRED_STATUS, /**< Index is already of desired status. (18) */ +RET_ADDINDEX_FAILED, /**< Adding index to index set failed. */ +RET_REMOVEINDEX_FAILED, /**< Removing index from index set failed. (20) */ +RET_SWAPINDEX_FAILED, /**< Cannot swap between different indexsets. */ +RET_NOTHING_TO_DO, /**< Nothing to do. */ +RET_SETUP_BOUND_FAILED, /**< Setting up bound index failed. */ +RET_SETUP_CONSTRAINT_FAILED, /**< Setting up constraint index failed. */ +RET_MOVING_BOUND_FAILED, /**< Moving bound between index sets failed. */ +RET_MOVING_CONSTRAINT_FAILED, /**< Moving constraint between index sets failed. */ +RET_SHIFTING_FAILED, /**< Shifting of bounds/constraints failed. */ +RET_ROTATING_FAILED, /**< Rotating of bounds/constraints failed. */ +/* QProblem */ +RET_QPOBJECT_NOT_SETUP, /**< The QP object has not been setup correctly, use another constructor. */ +RET_QP_ALREADY_INITIALISED, /**< QProblem has already been initialised. (30) */ +RET_NO_INIT_WITH_STANDARD_SOLVER, /**< Initialisation via extern QP solver is not yet implemented. */ +RET_RESET_FAILED, /**< Reset failed. */ +RET_INIT_FAILED, /**< Initialisation failed. */ +RET_INIT_FAILED_TQ, /**< Initialisation failed due to TQ factorisation. */ +RET_INIT_FAILED_CHOLESKY, /**< Initialisation failed due to Cholesky decomposition. */ +RET_INIT_FAILED_HOTSTART, /**< Initialisation failed! QP could not be solved! */ +RET_INIT_FAILED_INFEASIBILITY, /**< Initial QP could not be solved due to infeasibility! */ +RET_INIT_FAILED_UNBOUNDEDNESS, /**< Initial QP could not be solved due to unboundedness! */ +RET_INIT_FAILED_REGULARISATION, /**< Initialisation failed as Hessian matrix could not be regularised. */ +RET_INIT_SUCCESSFUL, /**< Initialisation done. (40) */ +RET_OBTAINING_WORKINGSET_FAILED, /**< Failed to obtain working set for auxiliary QP. */ +RET_SETUP_WORKINGSET_FAILED, /**< Failed to setup working set for auxiliary QP. */ +RET_SETUP_AUXILIARYQP_FAILED, /**< Failed to setup auxiliary QP for initialised homotopy. */ +RET_NO_CHOLESKY_WITH_INITIAL_GUESS, /**< Externally computed Cholesky factor cannot be combined with an initial guess. */ +RET_NO_EXTERN_SOLVER, /**< No extern QP solver available. */ +RET_QP_UNBOUNDED, /**< QP is unbounded. */ +RET_QP_INFEASIBLE, /**< QP is infeasible. */ +RET_QP_NOT_SOLVED, /**< Problems occurred while solving QP with standard solver. */ +RET_QP_SOLVED, /**< QP successfully solved. */ +RET_UNABLE_TO_SOLVE_QP, /**< Problems occurred while solving QP. (50) */ +RET_INITIALISATION_STARTED, /**< Starting problem initialisation... */ +RET_HOTSTART_FAILED, /**< Unable to perform homotopy due to internal error. */ +RET_HOTSTART_FAILED_TO_INIT, /**< Unable to initialise problem. */ +RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED, /**< Unable to perform homotopy as previous QP is not solved. */ +RET_ITERATION_STARTED, /**< Iteration... */ +RET_SHIFT_DETERMINATION_FAILED, /**< Determination of shift of the QP data failed. */ +RET_STEPDIRECTION_DETERMINATION_FAILED, /**< Determination of step direction failed. */ +RET_STEPLENGTH_DETERMINATION_FAILED, /**< Determination of step direction failed. */ +RET_OPTIMAL_SOLUTION_FOUND, /**< Optimal solution of neighbouring QP found. */ +RET_HOMOTOPY_STEP_FAILED, /**< Unable to perform homotopy step. (60) */ +RET_HOTSTART_STOPPED_INFEASIBILITY, /**< Premature homotopy termination because QP is infeasible. */ +RET_HOTSTART_STOPPED_UNBOUNDEDNESS, /**< Premature homotopy termination because QP is unbounded. */ +RET_WORKINGSET_UPDATE_FAILED, /**< Unable to update working sets according to initial guesses. */ +RET_MAX_NWSR_REACHED, /**< Maximum number of working set recalculations performed. */ +RET_CONSTRAINTS_NOT_SPECIFIED, /**< Problem does comprise constraints! You also have to specify new constraints' bounds. */ +RET_INVALID_FACTORISATION_FLAG, /**< Invalid factorisation flag. */ +RET_UNABLE_TO_SAVE_QPDATA, /**< Unable to save QP data. */ +RET_STEPDIRECTION_FAILED_TQ, /**< Abnormal termination due to TQ factorisation. */ +RET_STEPDIRECTION_FAILED_CHOLESKY, /**< Abnormal termination due to Cholesky factorisation. */ +RET_CYCLING_DETECTED, /**< Cycling detected. (70) */ +RET_CYCLING_NOT_RESOLVED, /**< Cycling cannot be resolved, QP probably infeasible. */ +RET_CYCLING_RESOLVED, /**< Cycling probably resolved. */ +RET_STEPSIZE, /**< For displaying performed stepsize. */ +RET_STEPSIZE_NONPOSITIVE, /**< For displaying non-positive stepsize. */ +RET_SETUPSUBJECTTOTYPE_FAILED, /**< Setup of SubjectToTypes failed. */ +RET_ADDCONSTRAINT_FAILED, /**< Addition of constraint to working set failed. */ +RET_ADDCONSTRAINT_FAILED_INFEASIBILITY, /**< Addition of constraint to working set failed (due to QP infeasibility). */ +RET_ADDBOUND_FAILED, /**< Addition of bound to working set failed. */ +RET_ADDBOUND_FAILED_INFEASIBILITY, /**< Addition of bound to working set failed (due to QP infeasibility). */ +RET_REMOVECONSTRAINT_FAILED, /**< Removal of constraint from working set failed. (80) */ +RET_REMOVEBOUND_FAILED, /**< Removal of bound from working set failed. */ +RET_REMOVE_FROM_ACTIVESET, /**< Removing from active set... */ +RET_ADD_TO_ACTIVESET, /**< Adding to active set... */ +RET_REMOVE_FROM_ACTIVESET_FAILED, /**< Removing from active set failed. */ +RET_ADD_TO_ACTIVESET_FAILED, /**< Adding to active set failed. */ +RET_CONSTRAINT_ALREADY_ACTIVE, /**< Constraint is already active. */ +RET_ALL_CONSTRAINTS_ACTIVE, /**< All constraints are active, no further constraint can be added. */ +RET_LINEARLY_DEPENDENT, /**< New bound/constraint is linearly dependent. */ +RET_LINEARLY_INDEPENDENT, /**< New bound/constraint is linearly independent. */ +RET_LI_RESOLVED, /**< Linear independence of active constraint matrix successfully resolved. (90) */ +RET_ENSURELI_FAILED, /**< Failed to ensure linear independence of active constraint matrix. */ +RET_ENSURELI_FAILED_TQ, /**< Abnormal termination due to TQ factorisation. */ +RET_ENSURELI_FAILED_NOINDEX, /**< QP is infeasible. */ +RET_ENSURELI_FAILED_CYCLING, /**< QP is infeasible. */ +RET_BOUND_ALREADY_ACTIVE, /**< Bound is already active. */ +RET_ALL_BOUNDS_ACTIVE, /**< All bounds are active, no further bound can be added. */ +RET_CONSTRAINT_NOT_ACTIVE, /**< Constraint is not active. */ +RET_BOUND_NOT_ACTIVE, /**< Bound is not active. */ +RET_HESSIAN_NOT_SPD, /**< Projected Hessian matrix not positive definite. */ +RET_HESSIAN_INDEFINITE, /**< Hessian matrix is indefinite. (100) */ +RET_MATRIX_SHIFT_FAILED, /**< Unable to update matrices or to transform vectors. */ +RET_MATRIX_FACTORISATION_FAILED, /**< Unable to calculate new matrix factorisations. */ +RET_PRINT_ITERATION_FAILED, /**< Unable to print information on current iteration. */ +RET_NO_GLOBAL_MESSAGE_OUTPUTFILE, /**< No global message output file initialised. */ +RET_DISABLECONSTRAINTS_FAILED, /**< Unable to disbable constraints. */ +RET_ENABLECONSTRAINTS_FAILED, /**< Unable to enbable constraints. */ +RET_ALREADY_ENABLED, /**< Bound or constraint is already enabled. */ +RET_ALREADY_DISABLED, /**< Bound or constraint is already disabled. */ +RET_NO_HESSIAN_SPECIFIED, /**< No Hessian matrix has been specified. */ +RET_USING_REGULARISATION, /**< Using regularisation as Hessian matrix is not positive definite. (110) */ +RET_EPS_MUST_BE_POSITVE, /**< Eps for regularisation must be sufficiently positive. */ +RET_REGSTEPS_MUST_BE_POSITVE, /**< Maximum number of regularisation steps must be non-negative. */ +RET_HESSIAN_ALREADY_REGULARISED, /**< Hessian has been already regularised. */ +RET_CANNOT_REGULARISE_IDENTITY, /**< Identity Hessian matrix cannot be regularised. */ +RET_CANNOT_REGULARISE_SPARSE, /**< Sparse matrix cannot be regularised as diagonal entry is missing. */ +RET_NO_REGSTEP_NWSR, /**< No additional regularisation step could be performed due to limits. */ +RET_FEWER_REGSTEPS_NWSR, /**< Fewer additional regularisation steps have been performed due to limits. */ +RET_CHOLESKY_OF_ZERO_HESSIAN, /**< Cholesky decomposition of (unregularised) zero Hessian matrix. */ +RET_ZERO_HESSIAN_ASSUMED, /**< Zero Hessian matrix assumed as null pointer passed without specifying hessianType. */ +RET_CONSTRAINTS_ARE_NOT_SCALED, /**< (no longer in use) (120) */ +RET_INITIAL_BOUNDS_STATUS_NYI, /**< (no longer in use) */ +RET_ERROR_IN_CONSTRAINTPRODUCT, /**< Error in user-defined constraint product function. */ +RET_FIX_BOUNDS_FOR_LP, /**< All initial bounds must be fixed when solving an (unregularised) LP. */ +RET_USE_REGULARISATION_FOR_LP, /**< Set options.enableRegularisation=BT_TRUE for solving LPs. */ +/* SQProblem */ +RET_UPDATEMATRICES_FAILED, /**< Unable to update QP matrices. */ +RET_UPDATEMATRICES_FAILED_AS_QP_NOT_SOLVED, /**< Unable to update matrices as previous QP is not solved. */ +/* Utils */ +RET_UNABLE_TO_OPEN_FILE, /**< Unable to open file. */ +RET_UNABLE_TO_WRITE_FILE, /**< Unable to write into file. */ +RET_UNABLE_TO_READ_FILE, /**< Unable to read from file. */ +RET_FILEDATA_INCONSISTENT, /**< File contains inconsistent data. (130) */ +/* Options */ +RET_OPTIONS_ADJUSTED, /**< Options needed to be adjusted for consistency reasons. */ +/* SolutionAnalysis */ +RET_UNABLE_TO_ANALYSE_QPROBLEM, /**< Unable to analyse (S)QProblem(B) object. */ +/* Benchmark */ +RET_NWSR_SET_TO_ONE, /**< Maximum number of working set changes was set to 1. */ +RET_UNABLE_TO_READ_BENCHMARK, /**< Unable to read benchmark data. */ +RET_BENCHMARK_ABORTED, /**< Benchmark aborted. */ +RET_INITIAL_QP_SOLVED, /**< Initial QP solved. */ +RET_QP_SOLUTION_STARTED, /**< Solving QP... */ +RET_BENCHMARK_SUCCESSFUL, /**< Benchmark terminated successfully. */ +/* Sparse matrices */ +RET_NO_DIAGONAL_AVAILABLE, /**< Sparse matrix does not have entries on full diagonal. */ +RET_DIAGONAL_NOT_INITIALISED, /**< Diagonal data of sparse matrix has not been initialised. (140) */ +/* Dropping of infeasible constraints */ +RET_ENSURELI_DROPPED, /**< Linear independence resolved by dropping blocking constraint. */ +/* Simple exitflags */ +RET_SIMPLE_STATUS_P1, /**< QP problem could not be solved within given number of iterations. */ +RET_SIMPLE_STATUS_P0, /**< QP problem solved. */ +RET_SIMPLE_STATUS_M1, /**< QP problem could not be solved due to an internal error. */ +RET_SIMPLE_STATUS_M2, /**< QP problem is infeasible (and thus could not be solved). */ +RET_SIMPLE_STATUS_M3 /**< QP problem is unbounded (and thus could not be solved). (146) */ +} returnValue; + + +/** + * \brief Data structure for entries in global message list. + * + * Data structure for entries in global message list. + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + returnValue key; /**< Global return value. */ + const char* data; /**< Corresponding message. */ + VisibilityStatus globalVisibilityStatus; /**< Determines if message can be printed. + * If this value is set to VS_HIDDEN, no message is printed! */ +} ReturnValueList; + + + +/** + * \brief Handles all kind of error messages, warnings and other information. + * + * This class handles all kinds of messages (errors, warnings, infos) initiated + * by qpOASES modules and stores the corresponding global preferences. + * + * \author Hans Joachim Ferreau (thanks to Leonard Wirsching) + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + VisibilityStatus errorVisibility; /**< Error messages visible? */ + VisibilityStatus warningVisibility; /**< Warning messages visible? */ + VisibilityStatus infoVisibility; /**< Info messages visible? */ + + FILE* outputFile; /**< Output file for messages. */ + + int errorCount; /**< Counts number of errors (for nicer output only). */ +} MessageHandling; + + + +/** Constructor which takes the desired output file and desired visibility states. */ +void MessageHandlingCON( MessageHandling* _THIS, + FILE* _outputFile, /**< Output file. */ + VisibilityStatus _errorVisibility, /**< Visibility status for error messages. */ + VisibilityStatus _warningVisibility,/**< Visibility status for warning messages. */ + VisibilityStatus _infoVisibility /**< Visibility status for info messages. */ + ); + +void MessageHandlingCPY( MessageHandling* FROM, + MessageHandling* TO + ); + + +/** Prints an error message(a simplified macro THROWERROR is also provided). \n + * Errors are definied as abnormal events which cause an immediate termination of the current (sub) function. + * Errors of a sub function should be commented by the calling function by means of a warning message + * (if this error does not cause an error of the calling function, either)! + * \return Error number returned by sub function call + */ +returnValue MessageHandling_throwError( MessageHandling* _THIS, + returnValue Enumber, /**< Error number returned by sub function call. */ + const char* additionaltext, /**< Additional error text (0, if none). */ + const char* functionname, /**< Name of function which caused the error. */ + const char* filename, /**< Name of file which caused the error. */ + const unsigned long linenumber, /**< Number of line which caused the error.incompatible binary file */ + VisibilityStatus localVisibilityStatus /**< Determines (locally) if error message can be printed to stderr. + * If GLOBAL visibility status of the message is set to VS_HIDDEN, + * no message is printed, anyway! */ + ); + +/** Prints a warning message (a simplified macro THROWWARNING is also provided). + * Warnings are definied as abnormal events which does NOT cause an immediate termination of the current (sub) function. + * \return Warning number returned by sub function call + */ +returnValue MessageHandling_throwWarning( MessageHandling* _THIS, + returnValue Wnumber, /**< Warning number returned by sub function call. */ + const char* additionaltext, /**< Additional warning text (0, if none). */ + const char* functionname, /**< Name of function which caused the warning. */ + const char* filename, /**< Name of file which caused the warning. */ + const unsigned long linenumber, /**< Number of line which caused the warning. */ + VisibilityStatus localVisibilityStatus /**< Determines (locally) if warning message can be printed to stderr. + * If GLOBAL visibility status of the message is set to VS_HIDDEN, + * no message is printed, anyway! */ + ); + +/** Prints a info message (a simplified macro THROWINFO is also provided). + * \return Info number returned by sub function call + */ +returnValue MessageHandling_throwInfo( MessageHandling* _THIS, + returnValue Inumber, /**< Info number returned by sub function call. */ + const char* additionaltext, /**< Additional warning text (0, if none). */ + const char* functionname, /**< Name of function which submitted the info. */ + const char* filename, /**< Name of file which submitted the info. */ + const unsigned long linenumber, /**< Number of line which submitted the info. */ + VisibilityStatus localVisibilityStatus /**< Determines (locally) if info message can be printed to stderr. + * If GLOBAL visibility status of the message is set to VS_HIDDEN, + * no message is printed, anyway! */ + ); + + +/** Resets all preferences to default values. + * \return SUCCESSFUL_RETURN */ +returnValue MessageHandling_reset( MessageHandling* _THIS ); + + +/** Prints a complete list of all messages to output file. + * \return SUCCESSFUL_RETURN */ +returnValue MessageHandling_listAllMessages( MessageHandling* _THIS ); + + +/** Returns visibility status for error messages. + * \return Visibility status for error messages. */ +static inline VisibilityStatus MessageHandling_getErrorVisibilityStatus( MessageHandling* _THIS ); + +/** Returns visibility status for warning messages. + * \return Visibility status for warning messages. */ +static inline VisibilityStatus MessageHandling_getWarningVisibilityStatus( MessageHandling* _THIS ); + +/** Returns visibility status for info messages. + * \return Visibility status for info messages. */ +static inline VisibilityStatus MessageHandling_getInfoVisibilityStatus( MessageHandling* _THIS ); + +/** Returns pointer to output file. + * \return Pointer to output file. */ +static inline FILE* MessageHandling_getOutputFile( MessageHandling* _THIS ); + +/** Returns error count value. + * \return Error count value. */ +static inline int MessageHandling_getErrorCount( MessageHandling* _THIS ); + + +/** Changes visibility status for error messages. */ +static inline void MessageHandling_setErrorVisibilityStatus( MessageHandling* _THIS, + VisibilityStatus _errorVisibility /**< New visibility status for error messages. */ + ); + +/** Changes visibility status for warning messages. */ +static inline void MessageHandling_setWarningVisibilityStatus( MessageHandling* _THIS, + VisibilityStatus _warningVisibility /**< New visibility status for warning messages. */ + ); + +/** Changes visibility status for info messages. */ +static inline void MessageHandling_setInfoVisibilityStatus( MessageHandling* _THIS, + VisibilityStatus _infoVisibility /**< New visibility status for info messages. */ + ); + +/** Changes output file for messages. */ +static inline void MessageHandling_setOutputFile( MessageHandling* _THIS, + FILE* _outputFile /**< New output file for messages. */ + ); + +/** Changes error count. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENT */ +static inline returnValue MessageHandling_setErrorCount( MessageHandling* _THIS, + int _errorCount /**< New error count value. */ + ); + +/** Provides message text corresponding to given \a returnValue. + * \return String containing message text. */ +const char* MessageHandling_getErrorCodeMessage( MessageHandling* _THIS, + const returnValue _returnValue + ); + + +returnValue MessageHandling_throwMessage( MessageHandling* _THIS, + returnValue RETnumber, /**< Error/warning/info number returned by sub function call. */ + const char* additionaltext, /**< Additional warning text (0, if none). */ + const char* functionname, /**< Name of function which caused the error/warning/info. */ + const char* filename, /**< Name of file which caused the error/warning/info. */ + const unsigned long linenumber, /**< Number of line which caused the error/warning/info. */ + VisibilityStatus localVisibilityStatus, /**< Determines (locally) if info message can be printed to stderr. + * If GLOBAL visibility status of the message is set to VS_HIDDEN, + * no message is printed, anyway! */ + const char* RETstring /**< Leading string of error/warning/info message. */ + ); + + +#ifndef __FILE__ + /** Ensures that __FILE__ macro is defined. */ + #define __FILE__ 0 +#endif + +#ifndef __LINE__ + /** Ensures that __LINE__ macro is defined. */ + #define __LINE__ 0 +#endif + +/** Define __FUNC__ macro providing current function for debugging. */ +/*#define __FUNC__ 0*/ +#define __FUNC__ ("(no function name provided)") +/*#define __FUNC__ __func__*/ +/*#define __FUNC__ __FUNCTION__*/ + + +/** Short version of throwError with default values, only returnValue is needed */ +#define THROWERROR(retval) ( MessageHandling_throwError( qpOASES_getGlobalMessageHandler(),(retval),0,__FUNC__,__FILE__,__LINE__,VS_VISIBLE) ) + +/** Short version of throwWarning with default values, only returnValue is needed */ +#define THROWWARNING(retval) ( MessageHandling_throwWarning( qpOASES_getGlobalMessageHandler(),(retval),0,__FUNC__,__FILE__,__LINE__,VS_VISIBLE) ) + +/** Short version of throwInfo with default values, only returnValue is needed */ +#define THROWINFO(retval) ( MessageHandling_throwInfo( qpOASES_getGlobalMessageHandler(),(retval),0,__FUNC__,__FILE__,__LINE__,VS_VISIBLE) ) + + +/** Returns a pointer to global message handler. + * \return Pointer to global message handler. + */ +MessageHandling* qpOASES_getGlobalMessageHandler( ); + + +/* + * g e t E r r o r V i s i b i l i t y S t a t u s + */ +static inline VisibilityStatus MessageHandling_getErrorVisibilityStatus( MessageHandling* _THIS ) +{ + return _THIS->errorVisibility; +} + + +/* + * g e t W a r n i n g V i s i b i l i t y S t a t u s + */ +static inline VisibilityStatus MessageHandling_getWarningVisibilityStatus( MessageHandling* _THIS ) +{ + return _THIS->warningVisibility; +} + + +/* + * g e t I n f o V i s i b i l i t y S t a t u s + */ +static inline VisibilityStatus MessageHandling_getInfoVisibilityStatus( MessageHandling* _THIS ) +{ + return _THIS->infoVisibility; +} + + +/* + * g e t O u t p u t F i l e + */ +static inline FILE* MessageHandling_getOutputFile( MessageHandling* _THIS ) +{ + return _THIS->outputFile; +} + + +/* + * g e t E r r o r C o u n t + */ +static inline int MessageHandling_getErrorCount( MessageHandling* _THIS ) +{ + return _THIS->errorCount; +} + + +/* + * s e t E r r o r V i s i b i l i t y S t a t u s + */ +static inline void MessageHandling_setErrorVisibilityStatus( MessageHandling* _THIS, VisibilityStatus _errorVisibility ) +{ + _THIS->errorVisibility = _errorVisibility; +} + + +/* + * s e t W a r n i n g V i s i b i l i t y S t a t u s + */ +static inline void MessageHandling_setWarningVisibilityStatus( MessageHandling* _THIS, VisibilityStatus _warningVisibility ) +{ + _THIS->warningVisibility = _warningVisibility; +} + + +/* + * s e t I n f o V i s i b i l i t y S t a t u s + */ +static inline void MessageHandling_setInfoVisibilityStatus( MessageHandling* _THIS, VisibilityStatus _infoVisibility ) +{ + _THIS->infoVisibility = _infoVisibility; +} + + +/* + * s e t O u t p u t F i l e + */ +static inline void MessageHandling_setOutputFile( MessageHandling* _THIS, FILE* _outputFile ) +{ + _THIS->outputFile = _outputFile; +} + + +/* + * s e t E r r o r C o u n t + */ +static inline returnValue MessageHandling_setErrorCount( MessageHandling* _THIS, int _errorCount ) +{ + if ( _errorCount >= 0 ) + { + _THIS->errorCount = _errorCount; + return SUCCESSFUL_RETURN; + } + else + return RET_INVALID_ARGUMENTS; +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_MESSAGEHANDLING_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Options.h b/phonelibs/acados/include/qpOASES_e/Options.h new file mode 100644 index 0000000000..b471ee0668 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Options.h @@ -0,0 +1,153 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Options.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the Options class designed to manage user-specified + * options for solving a QProblem. + */ + + +#ifndef QPOASES_OPTIONS_H +#define QPOASES_OPTIONS_H + + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** + * \brief Manages all user-specified options for solving QPs. + * + * This class manages all user-specified options used for solving + * quadratic programs. + * + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + PrintLevel printLevel; /**< Print level. */ + + BooleanType enableRamping; /**< Specifies whether ramping shall be enabled or not. */ + BooleanType enableFarBounds; /**< Specifies whether far bounds shall be used or not. */ + BooleanType enableFlippingBounds; /**< Specifies whether flipping bounds shall be used or not. */ + BooleanType enableRegularisation; /**< Specifies whether Hessian matrix shall be regularised in case semi-definiteness is detected. */ + BooleanType enableFullLITests; /**< Specifies whether condition-hardened LI test shall be used or not. */ + BooleanType enableNZCTests; /**< Specifies whether nonzero curvature tests shall be used. */ + int enableDriftCorrection; /**< Specifies the frequency of drift corrections (0 = off). */ + int enableCholeskyRefactorisation; /**< Specifies the frequency of full refactorisation of proj. Hessian (otherwise updates). */ + BooleanType enableEqualities; /**< Specifies whether equalities shall be always treated as active constraints. */ + + real_t terminationTolerance; /**< Termination tolerance. */ + real_t boundTolerance; /**< Lower/upper (constraints') bound tolerance (an inequality constraint whose lower and upper bounds differ by less is regarded to be an equality constraint). */ + real_t boundRelaxation; /**< Offset for relaxing (constraints') bounds at beginning of an initial homotopy. It is also as initial value for far bounds. */ + real_t epsNum; /**< Numerator tolerance for ratio tests. */ + real_t epsDen; /**< Denominator tolerance for ratio tests. */ + real_t maxPrimalJump; /**< Maximum allowed jump in primal variables in nonzero curvature tests. */ + real_t maxDualJump; /**< Maximum allowed jump in dual variables in linear independence tests. */ + + real_t initialRamping; /**< Start value for Ramping Strategy. */ + real_t finalRamping; /**< Final value for Ramping Strategy. */ + real_t initialFarBounds; /**< Initial size of Far Bounds. */ + real_t growFarBounds; /**< Factor to grow Far Bounds. */ + SubjectToStatus initialStatusBounds; /**< Initial status of bounds at first iteration. */ + real_t epsFlipping; /**< Tolerance of squared Cholesky diagonal factor which triggers flipping bound. */ + int numRegularisationSteps; /**< Maximum number of successive regularisation steps. */ + real_t epsRegularisation; /**< Scaling factor of identity matrix used for Hessian regularisation. */ + int numRefinementSteps; /**< Maximum number of iterative refinement steps. */ + real_t epsIterRef; /**< Early termination tolerance for iterative refinement. */ + real_t epsLITests; /**< Tolerance for linear independence tests. */ + real_t epsNZCTests; /**< Tolerance for nonzero curvature tests. */ + + BooleanType enableDropInfeasibles; /**< ... */ + int dropBoundPriority; /**< ... */ + int dropEqConPriority; /**< ... */ + int dropIneqConPriority; /**< ... */ +} Options; + + +void OptionsCON( Options* _THIS + ); + +/** Copies all members from given rhs object. + * \return SUCCESSFUL_RETURN */ +void OptionsCPY( Options* FROM, + Options* TO + ); + + +/** Sets all options to default values. + * \return SUCCESSFUL_RETURN */ +returnValue Options_setToDefault( Options* _THIS + ); + +/** Sets all options to values resulting in maximum reliabilty. + * \return SUCCESSFUL_RETURN */ +returnValue Options_setToReliable( Options* _THIS + ); + +/** Sets all options to values resulting in minimum solution time. + * \return SUCCESSFUL_RETURN */ +returnValue Options_setToMPC( Options* _THIS + ); + +/** Same as setToMPC( ), for ensuring backwards compatibility. + * \return SUCCESSFUL_RETURN */ +returnValue Options_setToFast( Options* _THIS + ); + + +/** Ensures that all options have consistent values by automatically + * adjusting inconsistent ones. + * Note: This routine cannot (and does not try to) ensure that values + * are set to reasonable values that make the QP solution work! + * \return SUCCESSFUL_RETURN \n + * RET_OPTIONS_ADJUSTED */ +returnValue Options_ensureConsistency( Options* _THIS + ); + + +/** Prints values of all options. + * \return SUCCESSFUL_RETURN */ +returnValue Options_print( Options* _THIS + ); + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_OPTIONS_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/QProblem.h b/phonelibs/acados/include/qpOASES_e/QProblem.h new file mode 100644 index 0000000000..3c61a4d596 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/QProblem.h @@ -0,0 +1,2369 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/QProblem.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the QProblem class which is able to use the newly + * developed online active set strategy for parametric quadratic programming. + */ + + + +#ifndef QPOASES_QPROBLEM_H +#define QPOASES_QPROBLEM_H + + +#include +#include +#include +#include +#include +#include + + +BEGIN_NAMESPACE_QPOASES + +typedef struct { + Bounds *auxiliaryBounds; + Constraints *auxiliaryConstraints; + + real_t *ub_new_far; + real_t *lb_new_far; + real_t *ubA_new_far; + real_t *lbA_new_far; + + real_t *g_new; + real_t *lb_new; + real_t *ub_new; + real_t *lbA_new; + real_t *ubA_new; + + real_t *g_new2; + real_t *lb_new2; + real_t *ub_new2; + real_t *lbA_new2; + real_t *ubA_new2; + + real_t *delta_xFX5; + real_t *delta_xFR5; + real_t *delta_yAC5; + real_t *delta_yFX5; + + real_t *Hx; + + real_t *_H; + + real_t *g_original; + real_t *lb_original; + real_t *ub_original; + real_t *lbA_original; + real_t *ubA_original; + + real_t *delta_xFR; + real_t *delta_xFX; + real_t *delta_yAC; + real_t *delta_yFX; + real_t *delta_g; + real_t *delta_lb; + real_t *delta_ub; + real_t *delta_lbA; + real_t *delta_ubA; + + real_t *gMod; + + real_t *aFR; + real_t *wZ; + + real_t *delta_g2; + real_t *delta_xFX2; + real_t *delta_xFR2; + real_t *delta_yAC2; + real_t *delta_yFX2; + real_t *nul; + real_t *Arow; + + real_t *xiC; + real_t *xiC_TMP; + real_t *xiB; + real_t *Arow2; + real_t *num; + + real_t *w; + real_t *tmp; + + real_t *delta_g3; + real_t *delta_xFX3; + real_t *delta_xFR3; + real_t *delta_yAC3; + real_t *delta_yFX3; + real_t *nul2; + + real_t *xiC2; + real_t *xiC_TMP2; + real_t *xiB2; + real_t *num2; + + real_t *Hz; + real_t *z; + real_t *ZHz; + real_t *r; + + real_t *tmp2; + real_t *Hz2; + real_t *z2; + real_t *r2; + real_t *rhs; + + real_t *delta_xFX4; + real_t *delta_xFR4; + real_t *delta_yAC4; + real_t *delta_yFX4; + real_t *nul3; + real_t *ek; + real_t *x_W; + real_t *As; + real_t *Ax_W; + + real_t *num3; + real_t *den; + real_t *delta_Ax_l; + real_t *delta_Ax_u; + real_t *delta_Ax; + real_t *delta_x; + + real_t *_A; + + real_t *grad; + real_t *AX; +} QProblem_ws; + +int QProblem_ws_calculateMemorySize( unsigned int nV, unsigned int nC ); + +char *QProblem_ws_assignMemory( unsigned int nV, unsigned int nC, QProblem_ws **mem, void *raw_memory ); + +QProblem_ws *QProblem_ws_createMemory( unsigned int nV, unsigned int nC ); + +/** + * \brief Implements the online active set strategy for QPs with general constraints. + * + * A class for setting up and solving quadratic programs. The main feature is + * the possibily to use the newly developed online active set strategy for + * parametric quadratic programming. + * + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + QProblem_ws *ws; /**< Workspace */ + Bounds *bounds; /**< Data structure for problem's bounds. */ + Constraints *constraints; /**< Data structure for problem's constraints. */ + Flipper *flipper; /**< Struct for making a temporary copy of the matrix factorisations. */ + + DenseMatrix* H; /**< Hessian matrix pointer. */ + DenseMatrix* A; /**< Constraint matrix pointer. */ + + Options options; /**< Struct containing all user-defined options for solving QPs. */ + TabularOutput tabularOutput; /**< Struct storing information for tabular output (printLevel == PL_TABULAR). */ + + real_t *g; /**< Gradient. */ + + real_t *lb; /**< Lower bound vector (on variables). */ + real_t *ub; /**< Upper bound vector (on variables). */ + real_t *lbA; /**< Lower constraints' bound vector. */ + real_t *ubA; /**< Upper constraints' bound vector. */ + + real_t *R; /**< Cholesky factor of H (i.e. H = R^T*R). */ + + real_t *T; /**< Reverse triangular matrix, A = [0 T]*Q'. */ + real_t *Q; /**< Orthonormal quadratic matrix, A = [0 T]*Q'. */ + + real_t *Ax; /**< Stores the current A*x \n + * (for increased efficiency only). */ + real_t *Ax_l; /**< Stores the current distance to lower constraints' bounds A*x-lbA \n + * (for increased efficiency only). */ + real_t *Ax_u; /**< Stores the current distance to lower constraints' bounds ubA-A*x \n + * (for increased efficiency only). */ + + real_t *x; /**< Primal solution vector. */ + real_t *y; /**< Dual solution vector. */ + + real_t *delta_xFR_TMP; /**< Temporary for determineStepDirection */ + real_t *tempA; /**< Temporary for determineStepDirection. */ + real_t *tempB; /**< Temporary for determineStepDirection. */ + real_t *ZFR_delta_xFRz; /**< Temporary for determineStepDirection. */ + real_t *delta_xFRy; /**< Temporary for determineStepDirection. */ + real_t *delta_xFRz; /**< Temporary for determineStepDirection. */ + real_t *delta_yAC_TMP; /**< Temporary for determineStepDirection. */ + + ConstraintProduct constraintProduct; /**< Pointer to user-defined constraint product function. */ + + real_t tau; /**< Last homotopy step length. */ + real_t regVal; /**< Holds the offset used to regularise Hessian matrix (zero by default). */ + + real_t ramp0; /**< Start value for Ramping Strategy. */ + real_t ramp1; /**< Final value for Ramping Strategy. */ + + QProblemStatus status; /**< Current status of the solution process. */ + HessianType hessianType; /**< Type of Hessian matrix. */ + + BooleanType haveCholesky; /**< Flag indicating whether Cholesky decomposition has already been setup. */ + BooleanType infeasible; /**< QP infeasible? */ + BooleanType unbounded; /**< QP unbounded? */ + + int rampOffset; /**< Offset index for Ramping. */ + unsigned int count; /**< Counts the number of hotstart function calls (internal usage only!). */ + + int sizeT; /**< Matrix T is stored in a (sizeT x sizeT) array. */ +} QProblem; + +int QProblem_calculateMemorySize( unsigned int nV, unsigned int nC ); + +char *QProblem_assignMemory( unsigned int nV, unsigned int nC, QProblem **mem, void *raw_memory ); + +QProblem *QProblem_createMemory( unsigned int nV, unsigned int nC ); + + +/** Constructor which takes the QP dimension and Hessian type + * information. If the Hessian is the zero (i.e. HST_ZERO) or the + * identity matrix (i.e. HST_IDENTITY), respectively, no memory + * is allocated for it and a NULL pointer can be passed for it + * to the init() functions. */ +void QProblemCON( QProblem* _THIS, + int _nV, /**< Number of variables. */ + int _nC, /**< Number of constraints. */ + HessianType _hessianType /**< Type of Hessian matrix. */ + ); + +/** Copies all members from given rhs object. + * \return SUCCESSFUL_RETURN */ +void QProblemCPY( QProblem* FROM, + QProblem* TO + ); + + +/** Clears all data structures of QProblem except for QP data. + * \return SUCCESSFUL_RETURN \n + RET_RESET_FAILED */ +returnValue QProblem_reset( QProblem* _THIS ); + + +/** Initialises a QP problem with given QP data and tries to solve it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_initM( QProblem* _THIS, + DenseMatrix *_H, /**< Hessian matrix. */ + const real_t* const _g, /**< Gradient vector. */ + DenseMatrix *_A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA, /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + + +/** Initialises a QP problem with given QP data and tries to solve it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_init( QProblem* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + real_t* const _A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA, /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + +/** Initialises a QP problem with given QP data to be read from files and tries to solve it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_UNABLE_TO_READ_FILE */ +returnValue QProblem_initF( QProblem* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix is stored. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient vector is stored. */ + const char* const A_file, /**< Name of file where constraint matrix is stored. */ + const char* const lb_file, /**< Name of file where lower bound vector. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bound vector. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file, /**< Name of file where upper constraints' bound vector. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + +/** Initialises a QP problem with given QP data and tries to solve it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB/gC empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB/gC by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB/gC from yOpt != 0, \n + * 4. 0, 0, gB/gC: starts with xOpt = 0, yOpt = 0 and gB/gC, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB/gC from yOpt != 0, \n + * 6. xOpt, 0, gB/gC: starts with xOpt, yOpt = 0 and gB/gC, \n + * 7. xOpt, yOpt, gB/gC: starts with xOpt, yOpt and gB/gC (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_initMW( QProblem* _THIS, + DenseMatrix *_H, /**< Hessian matrix. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + DenseMatrix *_A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA, /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + * Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). */ + Constraints* const guessedConstraints, /**< Optimal working set of constraints for solution (xOpt,yOpt). */ + const real_t* const _R /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. + The Cholesky factor must be stored in a real_t array of size nV*nV + in row-major format. Note: Only used if xOpt/yOpt and gB are NULL! \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + +/** Initialises a QP problem with given QP data and tries to solve it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB/gC empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB/gC by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB/gC from yOpt != 0, \n + * 4. 0, 0, gB/gC: starts with xOpt = 0, yOpt = 0 and gB/gC, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB/gC from yOpt != 0, \n + * 6. xOpt, 0, gB/gC: starts with xOpt, yOpt = 0 and gB/gC, \n + * 7. xOpt, yOpt, gB/gC: starts with xOpt, yOpt and gB/gC (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_initW( QProblem* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + real_t* const _A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA, /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + * Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). */ + Constraints* const guessedConstraints, /**< Optimal working set of constraints for solution (xOpt,yOpt). */ + const real_t* const _R /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. + The Cholesky factor must be stored in a real_t array of size nV*nV + in row-major format. Note: Only used if xOpt/yOpt and gB are NULL! \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + +/** Initialises a QP problem with given QP data to be ream from files and tries to solve it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB/gC empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB/gC by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB/gC from yOpt != 0, \n + * 4. 0, 0, gB/gC: starts with xOpt = 0, yOpt = 0 and gB/gC, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB/gC from yOpt != 0, \n + * 6. xOpt, 0, gB/gC: starts with xOpt, yOpt = 0 and gB/gC, \n + * 7. xOpt, yOpt, gB/gC: starts with xOpt, yOpt and gB/gC (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_UNABLE_TO_READ_FILE */ +returnValue QProblem_initFW( QProblem* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix is stored. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient vector is stored. */ + const char* const A_file, /**< Name of file where constraint matrix is stored. */ + const char* const lb_file, /**< Name of file where lower bound vector. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bound vector. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file, /**< Name of file where upper constraints' bound vector. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). */ + Constraints* const guessedConstraints, /**< Optimal working set of constraints for solution (xOpt,yOpt). */ + const char* const R_file /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. + The Cholesky factor must be stored in a real_t array of size nV*nV + in row-major format. Note: Only used if xOpt/yOpt and gB are NULL! \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + +/** Solves an initialised QP sequence using the online active set strategy. + * QP solution is started from previous solution. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblem_hotstart( QProblem* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const lbA_new, /**< Lower constraints' bounds of neighbouring QP to be solved. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const ubA_new, /**< Upper constraints' bounds of neighbouring QP to be solved. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves an initialised QP sequence using the online active set strategy, + * where QP data is read from files. QP solution is started from previous solution. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_hotstartF( QProblem* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file, /**< Name of file where upper constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves an initialised QP sequence using the online active set strategy. + * By default, QP solution is started from previous solution. If a guess + * for the working set is provided, an initialised homotopy is performed. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_SETUP_AUXILIARYQP_FAILED */ +returnValue QProblem_hotstartW( QProblem* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const lbA_new, /**< Lower constraints' bounds of neighbouring QP to be solved. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const ubA_new, /**< Upper constraints' bounds of neighbouring QP to be solved. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set of bounds is kept!) */ + Constraints* const guessedConstraints /**< Optimal working set of constraints for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set of constraints is kept!) */ + ); + +/** Solves an initialised QP sequence using the online active set strategy, + * where QP data is read from files. + * By default, QP solution is started from previous solution. If a guess + * for the working set is provided, an initialised homotopy is performed. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_SETUP_AUXILIARYQP_FAILED \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_hotstartFW( QProblem* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file, /**< Name of file where upper constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set of bounds is kept!) */ + Constraints* const guessedConstraints /**< Optimal working set of constraints for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set of constraints is kept!) */ + ); + + +/** Solves using the current working set + * \return SUCCESSFUL_RETURN \n + * RET_STEPDIRECTION_FAILED_TQ \n + * RET_STEPDIRECTION_FAILED_CHOLESKY \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblem_solveCurrentEQP ( QProblem* _THIS, + const int n_rhs, /**< Number of consecutive right hand sides */ + const real_t* g_in, /**< Gradient of neighbouring QP to be solved. */ + const real_t* lb_in, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* ub_in, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* lbA_in, /**< Lower constraints' bounds of neighbouring QP to be solved. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* ubA_in, /**< Upper constraints' bounds of neighbouring QP to be solved. \n */ + real_t* x_out, /**< Output: Primal solution */ + real_t* y_out /**< Output: Dual solution */ + ); + + + +/** Returns current constraints object of the QP (deep copy). + * \return SUCCESSFUL_RETURN \n + RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblem_getConstraints( QProblem* _THIS, + Constraints* _constraints /** Output: Constraints object. */ + ); + + +/** Returns the number of constraints. + * \return Number of constraints. */ +static inline int QProblem_getNC( QProblem* _THIS ); + +/** Returns the number of (implicitly defined) equality constraints. + * \return Number of (implicitly defined) equality constraints. */ +static inline int QProblem_getNEC( QProblem* _THIS ); + +/** Returns the number of active constraints. + * \return Number of active constraints. */ +static inline int QProblem_getNAC( QProblem* _THIS ); + +/** Returns the number of inactive constraints. + * \return Number of inactive constraints. */ +static inline int QProblem_getNIAC( QProblem* _THIS ); + +/** Returns the dimension of null space. + * \return Dimension of null space. */ +int QProblem_getNZ( QProblem* _THIS ); + + +/** Returns the dual solution vector (deep copy). + * \return SUCCESSFUL_RETURN \n + RET_QP_NOT_SOLVED */ +returnValue QProblem_getDualSolution( QProblem* _THIS, + real_t* const yOpt /**< Output: Dual solution vector (if QP has been solved). */ + ); + + +/** Defines user-defined routine for calculating the constraint product A*x + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblem_setConstraintProduct( QProblem* _THIS, + ConstraintProduct _constraintProduct + ); + + +/** Prints concise list of properties of the current QP. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblem_printProperties( QProblem* _THIS ); + + + +/** Writes a vector with the state of the working set +* \return SUCCESSFUL_RETURN */ +returnValue QProblem_getWorkingSet( QProblem* _THIS, + real_t* workingSet /** Output: array containing state of the working set. */ + ); + +/** Writes a vector with the state of the working set of bounds + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblem_getWorkingSetBounds( QProblem* _THIS, + real_t* workingSetB /** Output: array containing state of the working set of bounds. */ + ); + +/** Writes a vector with the state of the working set of constraints + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblem_getWorkingSetConstraints( QProblem* _THIS, + real_t* workingSetC /** Output: array containing state of the working set of constraints. */ + ); + + +/** Returns current bounds object of the QP (deep copy). + * \return SUCCESSFUL_RETURN \n + RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblem_getBounds( QProblem* _THIS, + Bounds* _bounds /** Output: Bounds object. */ + ); + + +/** Returns the number of variables. + * \return Number of variables. */ +static inline int QProblem_getNV( QProblem* _THIS ); + +/** Returns the number of free variables. + * \return Number of free variables. */ +static inline int QProblem_getNFR( QProblem* _THIS ); + +/** Returns the number of fixed variables. + * \return Number of fixed variables. */ +static inline int QProblem_getNFX( QProblem* _THIS ); + +/** Returns the number of implicitly fixed variables. + * \return Number of implicitly fixed variables. */ +static inline int QProblem_getNFV( QProblem* _THIS ); + + +/** Returns the optimal objective function value. + * \return finite value: Optimal objective function value (QP was solved) \n + +infinity: QP was not yet solved */ +real_t QProblem_getObjVal( QProblem* _THIS ); + +/** Returns the objective function value at an arbitrary point x. + * \return Objective function value at point x */ +real_t QProblem_getObjValX( QProblem* _THIS, + const real_t* const _x /**< Point at which the objective function shall be evaluated. */ + ); + +/** Returns the primal solution vector. + * \return SUCCESSFUL_RETURN \n + RET_QP_NOT_SOLVED */ +returnValue QProblem_getPrimalSolution( QProblem* _THIS, + real_t* const xOpt /**< Output: Primal solution vector (if QP has been solved). */ + ); + + +/** Returns status of the solution process. + * \return Status of solution process. */ +static inline QProblemStatus QProblem_getStatus( QProblem* _THIS ); + + +/** Returns if the QProblem object is initialised. + * \return BT_TRUE: QProblem initialised \n + BT_FALSE: QProblem not initialised */ +static inline BooleanType QProblem_isInitialised( QProblem* _THIS ); + +/** Returns if the QP has been solved. + * \return BT_TRUE: QProblem solved \n + BT_FALSE: QProblem not solved */ +static inline BooleanType QProblem_isSolved( QProblem* _THIS ); + +/** Returns if the QP is infeasible. + * \return BT_TRUE: QP infeasible \n + BT_FALSE: QP feasible (or not known to be infeasible!) */ +static inline BooleanType QProblem_isInfeasible( QProblem* _THIS ); + +/** Returns if the QP is unbounded. + * \return BT_TRUE: QP unbounded \n + BT_FALSE: QP unbounded (or not known to be unbounded!) */ +static inline BooleanType QProblem_isUnbounded( QProblem* _THIS ); + + +/** Returns Hessian type flag (type is not determined due to _THIS call!). + * \return Hessian type. */ +static inline HessianType QProblem_getHessianType( QProblem* _THIS ); + +/** Changes the print level. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblem_setHessianType( QProblem* _THIS, + HessianType _hessianType /**< New Hessian type. */ + ); + +/** Returns if the QP has been internally regularised. + * \return BT_TRUE: Hessian is internally regularised for QP solution \n + BT_FALSE: No internal Hessian regularisation is used for QP solution */ +static inline BooleanType QProblem_usingRegularisation( QProblem* _THIS ); + +/** Returns current options struct. + * \return Current options struct. */ +static inline Options QProblem_getOptions( QProblem* _THIS ); + +/** Overrides current options with given ones. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblem_setOptions( QProblem* _THIS, + Options _options /**< New options. */ + ); + +/** Returns the print level. + * \return Print level. */ +static inline PrintLevel QProblem_getPrintLevel( QProblem* _THIS ); + +/** Changes the print level. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_setPrintLevel( QProblem* _THIS, + PrintLevel _printlevel /**< New print level. */ + ); + + +/** Returns the current number of QP problems solved. + * \return Number of QP problems solved. */ +static inline unsigned int QProblem_getCount( QProblem* _THIS ); + +/** Resets QP problem counter (to zero). + * \return SUCCESSFUL_RETURN. */ +static inline returnValue QProblem_resetCounter( QProblem* _THIS ); + + +/** Prints a list of all options and their current values. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblem_printOptions( QProblem* _THIS ); + + +/** Solves a QProblem whose QP data is assumed to be stored in the member variables. + * A guess for its primal/dual optimal solution vectors and the corresponding + * working sets of bounds and constraints can be provided. + * Note: This function is internally called by all init functions! + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_TQ \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED */ +returnValue QProblem_solveInitialQP( QProblem* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector.*/ + const real_t* const yOpt, /**< Optimal dual solution vector. */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). */ + Constraints* const guessedConstraints, /**< Optimal working set of constraints for solution (xOpt,yOpt). */ + const real_t* const _R, /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + * Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + * Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves QProblem using online active set strategy. + * Note: This function is internally called by all hotstart functions! + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblem_solveQP( QProblem* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const lbA_new, /**< Lower constraints' bounds of neighbouring QP to be solved. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const ubA_new, /**< Upper constraints' bounds of neighbouring QP to be solved. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + int nWSRperformed, /**< Number of working set recalculations already performed to solve + this QP within previous solveQP() calls. This number is + always zero, except for successive calls from solveRegularisedQP() + or when using the far bound strategy. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + +/** Solves QProblem using online active set strategy. + * Note: This function is internally called by all hotstart functions! + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblem_solveRegularisedQP( QProblem* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const lbA_new, /**< Lower constraints' bounds of neighbouring QP to be solved. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const ubA_new, /**< Upper constraints' bounds of neighbouring QP to be solved. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + int nWSRperformed, /**< Number of working set recalculations already performed to solve + this QP within previous solveRegularisedQP() calls. This number is + always zero, except for successive calls when using the far bound strategy. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + +/** Determines type of existing constraints and bounds (i.e. implicitly fixed, unbounded etc.). + * \return SUCCESSFUL_RETURN \n + RET_SETUPSUBJECTTOTYPE_FAILED */ +returnValue QProblem_setupSubjectToType( QProblem* _THIS ); + +/** Determines type of new constraints and bounds (i.e. implicitly fixed, unbounded etc.). + * \return SUCCESSFUL_RETURN \n + RET_SETUPSUBJECTTOTYPE_FAILED */ +returnValue QProblem_setupSubjectToTypeNew( QProblem* _THIS, + const real_t* const lb_new, /**< New lower bounds. */ + const real_t* const ub_new, /**< New upper bounds. */ + const real_t* const lbA_new, /**< New lower constraints' bounds. */ + const real_t* const ubA_new /**< New upper constraints' bounds. */ + ); + +/** Computes the Cholesky decomposition of the projected Hessian (i.e. R^T*R = Z^T*H*Z). + * Note: If Hessian turns out not to be positive definite, the Hessian type + * is set to HST_SEMIDEF accordingly. + * \return SUCCESSFUL_RETURN \n + * RET_HESSIAN_NOT_SPD \n + * RET_INDEXLIST_CORRUPTED */ +returnValue QProblem_computeProjectedCholesky( QProblem* _THIS ); + +/** Computes initial Cholesky decomposition of the projected Hessian making + * use of the function setupCholeskyDecomposition() or setupCholeskyDecompositionProjected(). + * \return SUCCESSFUL_RETURN \n + * RET_HESSIAN_NOT_SPD \n + * RET_INDEXLIST_CORRUPTED */ +returnValue QProblem_setupInitialCholesky( QProblem* _THIS ); + +/** Initialises TQ factorisation of A (i.e. A*Q = [0 T]) if NO constraint is active. + * \return SUCCESSFUL_RETURN \n + RET_INDEXLIST_CORRUPTED */ +returnValue QProblem_setupTQfactorisation( QProblem* _THIS ); + + +/** Obtains the desired working set for the auxiliary initial QP in + * accordance with the user specifications + * (assumes that member AX has already been initialised!) + * \return SUCCESSFUL_RETURN \n + RET_OBTAINING_WORKINGSET_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_obtainAuxiliaryWorkingSet( QProblem* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + const real_t* const yOpt, /**< Optimal dual solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + Bounds* const guessedBounds, /**< Guessed working set of bounds for solution (xOpt,yOpt). */ + Constraints* const guessedConstraints, /**< Guessed working set for solution (xOpt,yOpt). */ + Bounds* auxiliaryBounds, /**< Input: Allocated bound object. \n + * Ouput: Working set of constraints for auxiliary QP. */ + Constraints* auxiliaryConstraints /**< Input: Allocated bound object. \n + * Ouput: Working set for auxiliary QP. */ + ); + +/** Sets up bound and constraints data structures according to auxiliaryBounds/Constraints. + * (If the working set shall be setup afresh, make sure that + * bounds and constraints data structure have been resetted + * and the TQ factorisation has been initialised!) + * \return SUCCESSFUL_RETURN \n + RET_SETUP_WORKINGSET_FAILED \n + RET_INVALID_ARGUMENTS \n + RET_UNKNOWN_BUG */ +returnValue QProblem_setupAuxiliaryWorkingSet( QProblem* _THIS, + Bounds* const auxiliaryBounds, /**< Working set of bounds for auxiliary QP. */ + Constraints* const auxiliaryConstraints, /**< Working set of constraints for auxiliary QP. */ + BooleanType setupAfresh /**< Flag indicating if given working set shall be + * setup afresh or by updating the current one. */ + ); + +/** Sets up the optimal primal/dual solution of the auxiliary initial QP. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_setupAuxiliaryQPsolution( QProblem* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector. + * If a NULL pointer is passed, all entries are set to zero. */ + const real_t* const yOpt /**< Optimal dual solution vector. + * If a NULL pointer is passed, all entries are set to zero. */ + ); + +/** Sets up gradient of the auxiliary initial QP for given + * optimal primal/dual solution and given initial working set + * (assumes that members X, Y and BOUNDS, CONSTRAINTS have already been initialised!). + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_setupAuxiliaryQPgradient( QProblem* _THIS ); + +/** Sets up (constraints') bounds of the auxiliary initial QP for given + * optimal primal/dual solution and given initial working set + * (assumes that members X, Y and BOUNDS, CONSTRAINTS have already been initialised!). + * \return SUCCESSFUL_RETURN \n + RET_UNKNOWN_BUG */ +returnValue QProblem_setupAuxiliaryQPbounds( QProblem* _THIS, + Bounds* const auxiliaryBounds, /**< Working set of bounds for auxiliary QP. */ + Constraints* const auxiliaryConstraints, /**< Working set of constraints for auxiliary QP. */ + BooleanType useRelaxation /**< Flag indicating if inactive (constraints') bounds shall be relaxed. */ + ); + + +/** Adds a constraint to active set. + * \return SUCCESSFUL_RETURN \n + RET_ADDCONSTRAINT_FAILED \n + RET_ADDCONSTRAINT_FAILED_INFEASIBILITY \n + RET_ENSURELI_FAILED */ +returnValue QProblem_addConstraint( QProblem* _THIS, + int number, /**< Number of constraint to be added to active set. */ + SubjectToStatus C_status, /**< Status of new active constraint. */ + BooleanType updateCholesky, /**< Flag indicating if Cholesky decomposition shall be updated. */ + BooleanType ensureLI /**< Ensure linear independence by exchange rules by default. */ + ); + +/** Checks if new active constraint to be added is linearly dependent from + * from row of the active constraints matrix. + * \return RET_LINEARLY_DEPENDENT \n + RET_LINEARLY_INDEPENDENT \n + RET_INDEXLIST_CORRUPTED */ +returnValue QProblem_addConstraint_checkLI( QProblem* _THIS, + int number /**< Number of constraint to be added to active set. */ + ); + +/** Ensures linear independence of constraint matrix when a new constraint is added. + * To _THIS end a bound or constraint is removed simultaneously if necessary. + * \return SUCCESSFUL_RETURN \n + RET_LI_RESOLVED \n + RET_ENSURELI_FAILED \n + RET_ENSURELI_FAILED_TQ \n + RET_ENSURELI_FAILED_NOINDEX \n + RET_REMOVE_FROM_ACTIVESET */ +returnValue QProblem_addConstraint_ensureLI( QProblem* _THIS, + int number, /**< Number of constraint to be added to active set. */ + SubjectToStatus C_status /**< Status of new active bound. */ + ); + +/** Adds a bound to active set. + * \return SUCCESSFUL_RETURN \n + RET_ADDBOUND_FAILED \n + RET_ADDBOUND_FAILED_INFEASIBILITY \n + RET_ENSURELI_FAILED */ +returnValue QProblem_addBound( QProblem* _THIS, + int number, /**< Number of bound to be added to active set. */ + SubjectToStatus B_status, /**< Status of new active bound. */ + BooleanType updateCholesky, /**< Flag indicating if Cholesky decomposition shall be updated. */ + BooleanType ensureLI /**< Ensure linear independence by exchange rules by default. */ + ); + +/** Checks if new active bound to be added is linearly dependent from + * from row of the active constraints matrix. + * \return RET_LINEARLY_DEPENDENT \n + RET_LINEARLY_INDEPENDENT */ +returnValue QProblem_addBound_checkLI( QProblem* _THIS, + int number /**< Number of bound to be added to active set. */ + ); + +/** Ensures linear independence of constraint matrix when a new bound is added. + * To _THIS end a bound or constraint is removed simultaneously if necessary. + * \return SUCCESSFUL_RETURN \n + RET_LI_RESOLVED \n + RET_ENSURELI_FAILED \n + RET_ENSURELI_FAILED_TQ \n + RET_ENSURELI_FAILED_NOINDEX \n + RET_REMOVE_FROM_ACTIVESET */ +returnValue QProblem_addBound_ensureLI( QProblem* _THIS, + int number, /**< Number of bound to be added to active set. */ + SubjectToStatus B_status /**< Status of new active bound. */ + ); + +/** Removes a constraint from active set. + * \return SUCCESSFUL_RETURN \n + RET_CONSTRAINT_NOT_ACTIVE \n + RET_REMOVECONSTRAINT_FAILED \n + RET_HESSIAN_NOT_SPD */ +returnValue QProblem_removeConstraint( QProblem* _THIS, + int number, /**< Number of constraint to be removed from active set. */ + BooleanType updateCholesky, /**< Flag indicating if Cholesky decomposition shall be updated. */ + BooleanType allowFlipping, /**< Flag indicating if flipping bounds are allowed. */ + BooleanType ensureNZC /**< Flag indicating if non-zero curvature is ensured by exchange rules. */ + ); + +/** Removes a bounds from active set. + * \return SUCCESSFUL_RETURN \n + RET_BOUND_NOT_ACTIVE \n + RET_HESSIAN_NOT_SPD \n + RET_REMOVEBOUND_FAILED */ +returnValue QProblem_removeBound( QProblem* _THIS, + int number, /**< Number of bound to be removed from active set. */ + BooleanType updateCholesky, /**< Flag indicating if Cholesky decomposition shall be updated. */ + BooleanType allowFlipping, /**< Flag indicating if flipping bounds are allowed. */ + BooleanType ensureNZC /**< Flag indicating if non-zero curvature is ensured by exchange rules. */ + ); + + +/** Performs robustified ratio test yield the maximum possible step length + * along the homotopy path. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_performPlainRatioTest( QProblem* _THIS, + int nIdx, /**< Number of ratios to be checked. */ + const int* const idxList, /**< Array containing the indices of all ratios to be checked. */ + const real_t* const num, /**< Array containing all numerators for performing the ratio test. */ + const real_t* const den, /**< Array containing all denominators for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t, /**< Output: Maximum possible step length along the homotopy path. */ + int* BC_idx /**< Output: Index of blocking constraint. */ + ); + + +/** Ensure non-zero curvature by primal jump. + * \return SUCCESSFUL_RETURN \n + * RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblem_ensureNonzeroCurvature( QProblem* _THIS, + BooleanType removeBoundNotConstraint, /**< SubjectTo to be removed is a bound. */ + int remIdx, /**< Index of bound/constraint to be removed. */ + BooleanType* exchangeHappened, /**< Output: Exchange was necessary to ensure. */ + BooleanType* addBoundNotConstraint, /**< SubjectTo to be added is a bound. */ + int* addIdx, /**< Index of bound/constraint to be added. */ + SubjectToStatus* addStatus /**< Status of bound/constraint to be added. */ + ); + + +/** Solves the system Ta = b or T^Ta = b where T is a reverse upper triangular matrix. + * \return SUCCESSFUL_RETURN \n + RET_DIV_BY_ZERO */ +returnValue QProblem_backsolveT( QProblem* _THIS, + const real_t* const b, /**< Right hand side vector. */ + BooleanType transposed, /**< Indicates if the transposed system shall be solved. */ + real_t* const a /**< Output: Solution vector */ + ); + + +/** Determines step direction of the shift of the QP data. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_determineDataShift( QProblem* _THIS, + const real_t* const g_new, /**< New gradient vector. */ + const real_t* const lbA_new, /**< New lower constraints' bounds. */ + const real_t* const ubA_new, /**< New upper constraints' bounds. */ + const real_t* const lb_new, /**< New lower bounds. */ + const real_t* const ub_new, /**< New upper bounds. */ + real_t* const delta_g, /**< Output: Step direction of gradient vector. */ + real_t* const delta_lbA, /**< Output: Step direction of lower constraints' bounds. */ + real_t* const delta_ubA, /**< Output: Step direction of upper constraints' bounds. */ + real_t* const delta_lb, /**< Output: Step direction of lower bounds. */ + real_t* const delta_ub, /**< Output: Step direction of upper bounds. */ + BooleanType* Delta_bC_isZero, /**< Output: Indicates if active constraints' bounds are to be shifted. */ + BooleanType* Delta_bB_isZero /**< Output: Indicates if active bounds are to be shifted. */ + ); + +/** Determines step direction of the homotopy path. + * \return SUCCESSFUL_RETURN \n + RET_STEPDIRECTION_FAILED_TQ \n + RET_STEPDIRECTION_FAILED_CHOLESKY */ +returnValue QProblem_determineStepDirection( QProblem* _THIS, + const real_t* const delta_g, /**< Step direction of gradient vector. */ + const real_t* const delta_lbA, /**< Step direction of lower constraints' bounds. */ + const real_t* const delta_ubA, /**< Step direction of upper constraints' bounds. */ + const real_t* const delta_lb, /**< Step direction of lower bounds. */ + const real_t* const delta_ub, /**< Step direction of upper bounds. */ + BooleanType Delta_bC_isZero, /**< Indicates if active constraints' bounds are to be shifted. */ + BooleanType Delta_bB_isZero, /**< Indicates if active bounds are to be shifted. */ + real_t* const delta_xFX, /**< Output: Primal homotopy step direction of fixed variables. */ + real_t* const delta_xFR, /**< Output: Primal homotopy step direction of free variables. */ + real_t* const delta_yAC, /**< Output: Dual homotopy step direction of active constraints' multiplier. */ + real_t* const delta_yFX /**< Output: Dual homotopy step direction of fixed variables' multiplier. */ + ); + +/** Determines the maximum possible step length along the homotopy path + * and performs _THIS step (without changing working set). + * \return SUCCESSFUL_RETURN \n + * RET_ERROR_IN_CONSTRAINTPRODUCT \n + * RET_QP_INFEASIBLE */ +returnValue QProblem_performStep( QProblem* _THIS, + const real_t* const delta_g, /**< Step direction of gradient. */ + const real_t* const delta_lbA, /**< Step direction of lower constraints' bounds. */ + const real_t* const delta_ubA, /**< Step direction of upper constraints' bounds. */ + const real_t* const delta_lb, /**< Step direction of lower bounds. */ + const real_t* const delta_ub, /**< Step direction of upper bounds. */ + const real_t* const delta_xFX, /**< Primal homotopy step direction of fixed variables. */ + const real_t* const delta_xFR, /**< Primal homotopy step direction of free variables. */ + const real_t* const delta_yAC, /**< Dual homotopy step direction of active constraints' multiplier. */ + const real_t* const delta_yFX, /**< Dual homotopy step direction of fixed variables' multiplier. */ + int* BC_idx, /**< Output: Index of blocking constraint. */ + SubjectToStatus* BC_status, /**< Output: Status of blocking constraint. */ + BooleanType* BC_isBound /**< Output: Indicates if blocking constraint is a bound. */ + ); + +/** Updates the active set. + * \return SUCCESSFUL_RETURN \n + RET_REMOVE_FROM_ACTIVESET_FAILED \n + RET_ADD_TO_ACTIVESET_FAILED */ +returnValue QProblem_changeActiveSet( QProblem* _THIS, + int BC_idx, /**< Index of blocking constraint. */ + SubjectToStatus BC_status, /**< Status of blocking constraint. */ + BooleanType BC_isBound /**< Indicates if blocking constraint is a bound. */ + ); + + +/** Compute relative length of homotopy in data space for termination + * criterion. + * \return Relative length in data space. */ +real_t QProblem_getRelativeHomotopyLength( QProblem* _THIS, + const real_t* const g_new, /**< Final gradient. */ + const real_t* const lb_new, /**< Final lower variable bounds. */ + const real_t* const ub_new, /**< Final upper variable bounds. */ + const real_t* const lbA_new, /**< Final lower constraint bounds. */ + const real_t* const ubA_new /**< Final upper constraint bounds. */ + ); + + +/** Ramping Strategy to avoid ties. Modifies homotopy start without + * changing current active set. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_performRamping( QProblem* _THIS ); + + +/** ... */ +returnValue QProblem_updateFarBounds( QProblem* _THIS, + real_t curFarBound, /**< ... */ + int nRamp, /**< ... */ + const real_t* const lb_new, /**< ... */ + real_t* const lb_new_far, /**< ... */ + const real_t* const ub_new, /**< ... */ + real_t* const ub_new_far, /**< ... */ + const real_t* const lbA_new, /**< ... */ + real_t* const lbA_new_far, /**< ... */ + const real_t* const ubA_new, /**< ... */ + real_t* const ubA_new_far /**< ... */ + ); + +/** ... */ +returnValue QProblemBCPY_updateFarBounds( QProblem* _THIS, + real_t curFarBound, /**< ... */ + int nRamp, /**< ... */ + const real_t* const lb_new, /**< ... */ + real_t* const lb_new_far, /**< ... */ + const real_t* const ub_new, /**< ... */ + real_t* const ub_new_far /**< ... */ + ); + + + +/** Performs robustified ratio test yield the maximum possible step length + * along the homotopy path. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_performRatioTestC( QProblem* _THIS, + int nIdx, /**< Number of ratios to be checked. */ + const int* const idxList, /**< Array containing the indices of all ratios to be checked. */ + Constraints* const subjectTo, /**< Constraint object corresponding to ratios to be checked. */ + const real_t* const num, /**< Array containing all numerators for performing the ratio test. */ + const real_t* const den, /**< Array containing all denominators for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t, /**< Output: Maximum possible step length along the homotopy path. */ + int* BC_idx /**< Output: Index of blocking constraint. */ + ); + + +/** Drift correction at end of each active set iteration + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_performDriftCorrection( QProblem* _THIS ); + + +/** Updates QP vectors, working sets and internal data structures in order to + start from an optimal solution corresponding to initial guesses of the working + set for bounds and constraints. + * \return SUCCESSFUL_RETURN \n + * RET_SETUP_AUXILIARYQP_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_setupAuxiliaryQP( QProblem* _THIS, + Bounds* const guessedBounds, /**< Initial guess for working set of bounds. */ + Constraints* const guessedConstraints /**< Initial guess for working set of constraints. */ + ); + +/** Determines if it is more efficient to refactorise the matrices when + * hotstarting or not (i.e. better to update the existing factorisations). + * \return BT_TRUE iff matrices shall be refactorised afresh + */ +BooleanType QProblem_shallRefactorise( QProblem* _THIS, + Bounds* const guessedBounds, /**< Guessed new working set of bounds. */ + Constraints* const guessedConstraints /**< Guessed new working set of constraints. */ + ); + +/** Setups internal QP data. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS \n + RET_UNKNONW_BUG */ +returnValue QProblem_setupQPdataM( QProblem* _THIS, + DenseMatrix *_H, /**< Hessian matrix. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + DenseMatrix *_A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + ); + + +/** Sets up dense internal QP data. If the current Hessian is trivial + * (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS \n + RET_UNKNONW_BUG */ +returnValue QProblem_setupQPdata( QProblem* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + real_t* const _A, /**< Constraint matrix. */ + const real_t* const _lb, /**< Lower bound vector (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bound vector (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + const real_t* const _lbA, /**< Lower constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const real_t* const _ubA /**< Upper constraints' bound vector. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + ); + +/** Sets up internal QP data by loading it from files. If the current Hessian + * is trivial (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS \n + RET_UNKNONW_BUG */ +returnValue QProblem_setupQPdataFromFile( QProblem* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix, of neighbouring QP to be solved, is stored. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const A_file, /**< Name of file where constraint matrix, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file /**< Name of file where upper constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + ); + +/** Loads new QP vectors from files (internal members are not affected!). + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblem_loadQPvectorsFromFile( QProblem* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + const char* const lbA_file, /**< Name of file where lower constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no lower constraints' bounds exist, a NULL pointer can be passed. */ + const char* const ubA_file, /**< Name of file where upper constraints' bounds, of neighbouring QP to be solved, is stored. \n + If no upper constraints' bounds exist, a NULL pointer can be passed. */ + real_t* const g_new, /**< Output: Gradient of neighbouring QP to be solved. */ + real_t* const lb_new, /**< Output: Lower bounds of neighbouring QP to be solved */ + real_t* const ub_new, /**< Output: Upper bounds of neighbouring QP to be solved */ + real_t* const lbA_new, /**< Output: Lower constraints' bounds of neighbouring QP to be solved */ + real_t* const ubA_new /**< Output: Upper constraints' bounds of neighbouring QP to be solved */ + ); + + +/** Prints concise information on the current iteration. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblem_printIteration( QProblem* _THIS, + int iter, /**< Number of current iteration. */ + int BC_idx, /**< Index of blocking constraint. */ + SubjectToStatus BC_status, /**< Status of blocking constraint. */ + BooleanType BC_isBound, /**< Indicates if blocking constraint is a bound. */ + real_t homotopyLength, /**< Current homotopy distance. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + +/** Sets constraint matrix of the QP. \n + Note: Also internal vector Ax is recomputed! + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblem_setAM( QProblem* _THIS, + DenseMatrix *A_new /**< New constraint matrix. */ + ); + +/** Sets dense constraint matrix of the QP. \n + Note: Also internal vector Ax is recomputed! + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblem_setA( QProblem* _THIS, + real_t* const A_new /**< New dense constraint matrix (with correct dimension!). */ + ); + + +/** Sets constraints' lower bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblem_setLBA( QProblem* _THIS, + const real_t* const lbA_new /**< New constraints' lower bound vector (with correct dimension!). */ + ); + +/** Changes single entry of lower constraints' bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP \n + * RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblem_setLBAn( QProblem* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of lower constraints' bound vector (with correct dimension!). */ + ); + +/** Sets constraints' upper bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblem_setUBA( QProblem* _THIS, + const real_t* const ubA_new /**< New constraints' upper bound vector (with correct dimension!). */ + ); + +/** Changes single entry of upper constraints' bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP \n + * RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblem_setUBAn( QProblem* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of upper constraints' bound vector (with correct dimension!). */ + ); + + +/** Decides if lower bounds are smaller than upper bounds + * + * \return SUCCESSFUL_RETURN \n + * RET_QP_INFEASIBLE */ +returnValue QProblem_areBoundsConsistent( QProblem* _THIS, + const real_t* const lb, /**< Vector of lower bounds*/ + const real_t* const ub, /**< Vector of upper bounds*/ + const real_t* const lbA, /**< Vector of lower constraints*/ + const real_t* const ubA /**< Vector of upper constraints*/ + ); + + +/** Drops the blocking bound/constraint that led to infeasibility, or finds another + * bound/constraint to drop according to drop priorities. + * \return SUCCESSFUL_RETURN \n + */ +returnValue QProblem_dropInfeasibles ( QProblem* _THIS, + int BC_number, /**< Number of the bound or constraint to be added */ + SubjectToStatus BC_status, /**< New status of the bound or constraint to be added */ + BooleanType BC_isBound, /**< Whether a bound or a constraint is to be added */ + real_t *xiB, + real_t *xiC + ); + + +/** If Hessian type has been set by the user, nothing is done. + * Otherwise the Hessian type is set to HST_IDENTITY, HST_ZERO, or + * HST_POSDEF (default), respectively. + * \return SUCCESSFUL_RETURN \n + RET_HESSIAN_INDEFINITE */ +returnValue QProblem_determineHessianType( QProblem* _THIS ); + +/** Computes the Cholesky decomposition of the (simply projected) Hessian + * (i.e. R^T*R = Z^T*H*Z). It only works in the case where Z is a simple + * projection matrix! + * Note: If Hessian turns out not to be positive definite, the Hessian type + * is set to HST_SEMIDEF accordingly. + * \return SUCCESSFUL_RETURN \n + * RET_HESSIAN_NOT_SPD \n + * RET_INDEXLIST_CORRUPTED */ +returnValue QProblemBCPY_computeCholesky( QProblem* _THIS ); + +/** Obtains the desired working set for the auxiliary initial QP in + * accordance with the user specifications + * \return SUCCESSFUL_RETURN \n + RET_OBTAINING_WORKINGSET_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemBCPY_obtainAuxiliaryWorkingSet( QProblem* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + const real_t* const yOpt, /**< Optimal dual solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + Bounds* const guessedBounds, /**< Guessed working set for solution (xOpt,yOpt). */ + Bounds* auxiliaryBounds /**< Input: Allocated bound object. \n + * Output: Working set for auxiliary QP. */ + ); + + +/** Solves the system Ra = b or R^Ta = b where R is an upper triangular matrix. + * \return SUCCESSFUL_RETURN \n + RET_DIV_BY_ZERO */ +returnValue QProblem_backsolveR( QProblem* _THIS, + const real_t* const b, /**< Right hand side vector. */ + BooleanType transposed, /**< Indicates if the transposed system shall be solved. */ + real_t* const a /**< Output: Solution vector */ + ); + +/** Solves the system Ra = b or R^Ta = b where R is an upper triangular matrix. \n + * Special variant for the case that _THIS function is called from within "removeBound()". + * \return SUCCESSFUL_RETURN \n + RET_DIV_BY_ZERO */ +returnValue QProblem_backsolveRrem( QProblem* _THIS, + const real_t* const b, /**< Right hand side vector. */ + BooleanType transposed, /**< Indicates if the transposed system shall be solved. */ + BooleanType removingBound, /**< Indicates if function is called from "removeBound()". */ + real_t* const a /**< Output: Solution vector */ + ); + + +/** Determines step direction of the shift of the QP data. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemBCPY_determineDataShift( QProblem* _THIS, + const real_t* const g_new, /**< New gradient vector. */ + const real_t* const lb_new, /**< New lower bounds. */ + const real_t* const ub_new, /**< New upper bounds. */ + real_t* const delta_g, /**< Output: Step direction of gradient vector. */ + real_t* const delta_lb, /**< Output: Step direction of lower bounds. */ + real_t* const delta_ub, /**< Output: Step direction of upper bounds. */ + BooleanType* Delta_bB_isZero /**< Output: Indicates if active bounds are to be shifted. */ + ); + + +/** Sets up internal QP data. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemBCPY_setupQPdataM( QProblem* _THIS, + DenseMatrix *_H, /**< Hessian matrix.*/ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Sets up internal QP data. If the current Hessian is trivial + * (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS \n + RET_NO_HESSIAN_SPECIFIED */ +returnValue QProblemBCPY_setupQPdata( QProblem* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Sets up internal QP data by loading it from files. If the current Hessian + * is trivial (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS \n + RET_NO_HESSIAN_SPECIFIED */ +returnValue QProblemBCPY_setupQPdataFromFile( QProblem* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix, of neighbouring QP to be solved, is stored. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Loads new QP vectors from files (internal members are not affected!). + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemBCPY_loadQPvectorsFromFile( QProblem* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + real_t* const g_new, /**< Output: Gradient of neighbouring QP to be solved. */ + real_t* const lb_new, /**< Output: Lower bounds of neighbouring QP to be solved */ + real_t* const ub_new /**< Output: Upper bounds of neighbouring QP to be solved */ + ); + + +/** Sets internal infeasibility flag and throws given error in case the far bound + * strategy is not enabled (as QP might actually not be infeasible in _THIS case). + * \return RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_ENSURELI_FAILED_CYCLING \n + RET_ENSURELI_FAILED_NOINDEX */ +returnValue QProblem_setInfeasibilityFlag( QProblem* _THIS, + returnValue returnvalue, /**< Returnvalue to be tunneled. */ + BooleanType doThrowError /**< Flag forcing to throw an error. */ + ); + + +/** Determines if next QP iteration can be performed within given CPU time limit. + * \return BT_TRUE: CPU time limit is exceeded, stop QP solution. \n + BT_FALSE: Sufficient CPU time for next QP iteration. */ +BooleanType QProblem_isCPUtimeLimitExceeded( QProblem* _THIS, + const real_t* const cputime, /**< Maximum CPU time allowed for QP solution. */ + real_t starttime, /**< Start time of current QP solution. */ + int nWSR /**< Number of working set recalculations performed so far. */ + ); + + +/** Regularise Hessian matrix by adding a scaled identity matrix to it. + * \return SUCCESSFUL_RETURN \n + RET_HESSIAN_ALREADY_REGULARISED */ +returnValue QProblem_regulariseHessian( QProblem* _THIS ); + + +/** Sets Hessian matrix of the QP. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblem_setHM( QProblem* _THIS, + DenseMatrix* H_new /**< New Hessian matrix. */ + ); + +/** Sets dense Hessian matrix of the QP. + * If a null pointer is passed and + * a) hessianType is HST_IDENTITY, nothing is done, + * b) hessianType is not HST_IDENTITY, Hessian matrix is set to zero. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblem_setH( QProblem* _THIS, + real_t* const H_new /**< New dense Hessian matrix (with correct dimension!). */ + ); + +/** Changes gradient vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblem_setG( QProblem* _THIS, + const real_t* const g_new /**< New gradient vector (with correct dimension!). */ + ); + +/** Changes lower bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblem_setLB( QProblem* _THIS, + const real_t* const lb_new /**< New lower bound vector (with correct dimension!). */ + ); + +/** Changes single entry of lower bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblem_setLBn( QProblem* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of lower bound vector. */ + ); + +/** Changes upper bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblem_setUB( QProblem* _THIS, + const real_t* const ub_new /**< New upper bound vector (with correct dimension!). */ + ); + +/** Changes single entry of upper bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblem_setUBn( QProblem* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of upper bound vector. */ + ); + + + +/** Compute relative length of homotopy in data space for termination + * criterion. + * \return Relative length in data space. */ +real_t QProblemBCPY_getRelativeHomotopyLength( QProblem* _THIS, + const real_t* const g_new, /**< Final gradient. */ + const real_t* const lb_new, /**< Final lower variable bounds. */ + const real_t* const ub_new /**< Final upper variable bounds. */ + ); + + + +/** Performs robustified ratio test yield the maximum possible step length + * along the homotopy path. + * \return SUCCESSFUL_RETURN */ +returnValue QProblem_performRatioTestB( QProblem* _THIS, + int nIdx, /**< Number of ratios to be checked. */ + const int* const idxList, /**< Array containing the indices of all ratios to be checked. */ + Bounds* const subjectTo, /**< Bound object corresponding to ratios to be checked. */ + const real_t* const num, /**< Array containing all numerators for performing the ratio test. */ + const real_t* const den, /**< Array containing all denominators for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t, /**< Output: Maximum possible step length along the homotopy path. */ + int* BC_idx /**< Output: Index of blocking constraint. */ + ); + +/** Checks whether given ratio is blocking, i.e. limits the maximum step length + * along the homotopy path to a value lower than given one. + * \return SUCCESSFUL_RETURN */ +static inline BooleanType QProblem_isBlocking( QProblem* _THIS, + real_t num, /**< Numerator for performing the ratio test. */ + real_t den, /**< Denominator for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t /**< Input: Current maximum step length along the homotopy path, + * Output: Updated maximum possible step length along the homotopy path. */ + ); + + +/** ... + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE */ +returnValue QProblem_writeQpDataIntoMatFile( QProblem* _THIS, + const char* const filename /**< Mat file name. */ + ); + +/** ... +* \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE */ +returnValue QProblem_writeQpWorkspaceIntoMatFile( QProblem* _THIS, + const char* const filename /**< Mat file name. */ + ); + + +/* + * g e t B o u n d s + */ +static inline returnValue QProblem_getBounds( QProblem* _THIS, Bounds* _bounds ) +{ + int nV = QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + _bounds = _THIS->bounds; + + return SUCCESSFUL_RETURN; +} + + +/* + * g e t N V + */ +static inline int QProblem_getNV( QProblem* _THIS ) +{ + return Bounds_getNV( _THIS->bounds ); +} + + +/* + * g e t N F R + */ +static inline int QProblem_getNFR( QProblem* _THIS ) +{ + return Bounds_getNFR( _THIS->bounds ); +} + + +/* + * g e t N F X + */ +static inline int QProblem_getNFX( QProblem* _THIS ) +{ + return Bounds_getNFX( _THIS->bounds ); +} + + +/* + * g e t N F V + */ +static inline int QProblem_getNFV( QProblem* _THIS ) +{ + return Bounds_getNFV( _THIS->bounds ); +} + + +/* + * g e t S t a t u s + */ +static inline QProblemStatus QProblem_getStatus( QProblem* _THIS ) +{ + return _THIS->status; +} + + +/* + * i s I n i t i a l i s e d + */ +static inline BooleanType QProblem_isInitialised( QProblem* _THIS ) +{ + if ( _THIS->status == QPS_NOTINITIALISED ) + return BT_FALSE; + else + return BT_TRUE; +} + + +/* + * i s S o l v e d + */ +static inline BooleanType QProblem_isSolved( QProblem* _THIS ) +{ + if ( _THIS->status == QPS_SOLVED ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * i s I n f e a s i b l e + */ +static inline BooleanType QProblem_isInfeasible( QProblem* _THIS ) +{ + return _THIS->infeasible; +} + + +/* + * i s U n b o u n d e d + */ +static inline BooleanType QProblem_isUnbounded( QProblem* _THIS ) +{ + return _THIS->unbounded; +} + + +/* + * g e t H e s s i a n T y p e + */ +static inline HessianType QProblem_getHessianType( QProblem* _THIS ) +{ + return _THIS->hessianType; +} + + +/* + * s e t H e s s i a n T y p e + */ +static inline returnValue QProblem_setHessianType( QProblem* _THIS, HessianType _hessianType ) +{ + _THIS->hessianType = _hessianType; + return SUCCESSFUL_RETURN; +} + + +/* + * u s i n g R e g u l a r i s a t i o n + */ +static inline BooleanType QProblem_usingRegularisation( QProblem* _THIS ) +{ + if ( _THIS->regVal > QPOASES_ZERO ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * g e t O p t i o n s + */ +static inline Options QProblem_getOptions( QProblem* _THIS ) +{ + return _THIS->options; +} + + +/* + * s e t O p t i o n s + */ +static inline returnValue QProblem_setOptions( QProblem* _THIS, + Options _options + ) +{ + OptionsCPY( &_options,&(_THIS->options) ); + Options_ensureConsistency( &(_THIS->options) ); + + QProblem_setPrintLevel( _THIS,_THIS->options.printLevel ); + + return SUCCESSFUL_RETURN; +} + + +/* + * g e t P r i n t L e v e l + */ +static inline PrintLevel QProblem_getPrintLevel( QProblem* _THIS ) +{ + return _THIS->options.printLevel; +} + + +/* + * g e t C o u n t + */ +static inline unsigned int QProblem_getCount( QProblem* _THIS ) +{ + return _THIS->count; +} + + +/* + * r e s e t C o u n t e r + */ +static inline returnValue QProblem_resetCounter( QProblem* _THIS ) +{ + _THIS->count = 0; + return SUCCESSFUL_RETURN; +} + + + +/***************************************************************************** + * P R O T E C T E D * + *****************************************************************************/ + + +/* + * s e t H + */ +static inline returnValue QProblem_setHM( QProblem* _THIS, DenseMatrix* H_new ) +{ + if ( H_new == 0 ) + return QProblem_setH( _THIS,(real_t*)0 ); + else + return QProblem_setH( _THIS,DenseMatrix_getVal(H_new) ); +} + + +/* + * s e t H + */ +static inline returnValue QProblem_setH( QProblem* _THIS, real_t* const H_new ) +{ + /* if null pointer is passed, Hessian is set to zero matrix + * (or stays identity matrix) */ + if ( H_new == 0 ) + { + if ( _THIS->hessianType == HST_IDENTITY ) + return SUCCESSFUL_RETURN; + + _THIS->hessianType = HST_ZERO; + + _THIS->H = 0; + } + else + { + DenseMatrixCON( _THIS->H,QProblem_getNV( _THIS ),QProblem_getNV( _THIS ),QProblem_getNV( _THIS ),H_new ); + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t G + */ +static inline returnValue QProblem_setG( QProblem* _THIS, const real_t* const g_new ) +{ + unsigned int nV = (unsigned int)QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( g_new == 0 ) + return THROWERROR( RET_INVALID_ARGUMENTS ); + + memcpy( _THIS->g,g_new,nV*sizeof(real_t) ); + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B + */ +static inline returnValue QProblem_setLB( QProblem* _THIS, const real_t* const lb_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( lb_new != 0 ) + { + memcpy( _THIS->lb,lb_new,nV*sizeof(real_t) ); + } + else + { + /* if no lower bounds are specified, set them to -infinity */ + for( i=0; ilb[i] = -QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B + */ +static inline returnValue QProblem_setLBn( QProblem* _THIS, int number, real_t value ) +{ + int nV = QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nV ) ) + { + _THIS->lb[number] = value; + return SUCCESSFUL_RETURN; + } + else + { + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); + } +} + + +/* + * s e t U B + */ +static inline returnValue QProblem_setUB( QProblem* _THIS, const real_t* const ub_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ub_new != 0 ) + { + memcpy( _THIS->ub,ub_new,nV*sizeof(real_t) ); + } + else + { + /* if no upper bounds are specified, set them to infinity */ + for( i=0; iub[i] = QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t U B + */ +static inline returnValue QProblem_setUBn( QProblem* _THIS, int number, real_t value ) +{ + int nV = QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nV ) ) + { + _THIS->ub[number] = value; + + return SUCCESSFUL_RETURN; + } + else + { + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); + } +} + + + +/* + * i s B l o c k i n g + */ +static inline BooleanType QProblem_isBlocking( QProblem* _THIS, + real_t num, + real_t den, + real_t epsNum, + real_t epsDen, + real_t* t + ) +{ + if ( ( den >= epsDen ) && ( num >= epsNum ) ) + { + if ( num < (*t)*den ) + return BT_TRUE; + } + + return BT_FALSE; +} + + + +/* + * g e t C o n s t r a i n t s + */ +static inline returnValue QProblem_getConstraints( QProblem* _THIS, Constraints* _constraints ) +{ + int nV = QProblem_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + ConstraintsCPY( _THIS->constraints,_constraints ); + + return SUCCESSFUL_RETURN; +} + + + +/* + * g e t N C + */ +static inline int QProblem_getNC( QProblem* _THIS ) +{ + return Constraints_getNC( _THIS->constraints ); +} + + +/* + * g e t N E C + */ +static inline int QProblem_getNEC( QProblem* _THIS ) +{ + return Constraints_getNEC( _THIS->constraints ); +} + + +/* + * g e t N A C + */ +static inline int QProblem_getNAC( QProblem* _THIS ) +{ + return Constraints_getNAC( _THIS->constraints ); +} + + +/* + * g e t N I A C + */ +static inline int QProblem_getNIAC( QProblem* _THIS ) +{ + return Constraints_getNIAC( _THIS->constraints ); +} + + + +/***************************************************************************** + * P R O T E C T E D * + *****************************************************************************/ + + +/* + * s e t A + */ +static inline returnValue QProblem_setAM( QProblem* _THIS, DenseMatrix *A_new ) +{ + if ( A_new == 0 ) + return QProblem_setA( _THIS,(real_t*)0 ); + else + return QProblem_setA( _THIS,DenseMatrix_getVal(A_new) ); +} + + +/* + * s e t A + */ +static inline returnValue QProblem_setA( QProblem* _THIS, real_t* const A_new ) +{ + int j; + int nV = QProblem_getNV( _THIS ); + int nC = QProblem_getNC( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( A_new == 0 ) + return THROWERROR( RET_INVALID_ARGUMENTS ); + + DenseMatrixCON( _THIS->A,QProblem_getNC( _THIS ),QProblem_getNV( _THIS ),QProblem_getNV( _THIS ),A_new ); + + DenseMatrix_times( _THIS->A,1, 1.0, _THIS->x, nV, 0.0, _THIS->Ax, nC); + + for( j=0; jAx_u[j] = _THIS->ubA[j] - _THIS->Ax[j]; + _THIS->Ax_l[j] = _THIS->Ax[j] - _THIS->lbA[j]; + + /* (ckirches) disable constraints with empty rows */ + if ( qpOASES_isZero( DenseMatrix_getRowNorm( _THIS->A,j,2 ),QPOASES_ZERO ) == BT_TRUE ) + Constraints_setType( _THIS->constraints,j,ST_DISABLED ); + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B A + */ +static inline returnValue QProblem_setLBA( QProblem* _THIS, const real_t* const lbA_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblem_getNV( _THIS ); + unsigned int nC = (unsigned int)QProblem_getNC( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( lbA_new != 0 ) + { + memcpy( _THIS->lbA,lbA_new,nC*sizeof(real_t) ); + } + else + { + /* if no lower constraints' bounds are specified, set them to -infinity */ + for( i=0; ilbA[i] = -QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B A + */ +static inline returnValue QProblem_setLBAn( QProblem* _THIS, int number, real_t value ) +{ + int nV = QProblem_getNV( _THIS ); + int nC = QProblem_getNC( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nC ) ) + { + _THIS->lbA[number] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +/* + * s e t U B A + */ +static inline returnValue QProblem_setUBA( QProblem* _THIS, const real_t* const ubA_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblem_getNV( _THIS ); + unsigned int nC = (unsigned int)QProblem_getNC( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ubA_new != 0 ) + { + memcpy( _THIS->ubA,ubA_new,nC*sizeof(real_t) ); + } + else + { + /* if no upper constraints' bounds are specified, set them to infinity */ + for( i=0; iubA[i] = QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t U B A + */ +static inline returnValue QProblem_setUBAn( QProblem* _THIS, int number, real_t value ) +{ + int nV = QProblem_getNV( _THIS ); + int nC = QProblem_getNC( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nC ) ) + { + _THIS->ubA[number] = value; + return SUCCESSFUL_RETURN; + } + else + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_QPROBLEM_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/QProblemB.h b/phonelibs/acados/include/qpOASES_e/QProblemB.h new file mode 100644 index 0000000000..ee5157dda7 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/QProblemB.h @@ -0,0 +1,1641 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/QProblemB.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of the QProblemB class which is able to use the newly + * developed online active set strategy for parametric quadratic programming + * for problems with (simple) bounds only. + */ + + + +#ifndef QPOASES_QPROBLEMB_H +#define QPOASES_QPROBLEMB_H + + +#include +#include +#include +#include + + +BEGIN_NAMESPACE_QPOASES + +typedef struct { + Bounds *emptyBounds; + Bounds *auxiliaryBounds; + + real_t *ub_new_far; + real_t *lb_new_far; + + real_t *g_new; + real_t *lb_new; + real_t *ub_new; + + real_t *g_new2; + real_t *lb_new2; + real_t *ub_new2; + + real_t *Hx; + + real_t *_H; + + real_t *g_original; + real_t *lb_original; + real_t *ub_original; + + real_t *delta_xFR; + real_t *delta_xFX; + real_t *delta_yFX; + real_t *delta_g; + real_t *delta_lb; + real_t *delta_ub; + + real_t *gMod; + + real_t *num; + real_t *den; + + real_t *rhs; + real_t *r; +} QProblemB_ws; + +int QProblemB_ws_calculateMemorySize( unsigned int nV ); + +char *QProblemB_ws_assignMemory( unsigned int nV, QProblemB_ws **mem, void *raw_memory ); + +QProblemB_ws *QProblemB_ws_createMemory( unsigned int nV ); + + +/** + * \brief Implements the online active set strategy for box-constrained QPs. + * + * Class for setting up and solving quadratic programs with bounds (= box constraints) only. + * The main feature is the possibility to use the newly developed online active set strategy + * for parametric quadratic programming. + * + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + */ +typedef struct +{ + QProblemB_ws *ws; + Bounds *bounds; /**< Data structure for problem's bounds. */ + Flipper *flipper; /**< Struct for making a temporary copy of the matrix factorisations. */ + + DenseMatrix* H; /**< Hessian matrix pointer. */ + + Options options; /**< Struct containing all user-defined options for solving QPs. */ + TabularOutput tabularOutput; /**< Struct storing information for tabular output (printLevel == PL_TABULAR). */ + + real_t *g; /**< Gradient. */ + real_t *lb; /**< Lower bound vector (on variables). */ + real_t *ub; /**< Upper bound vector (on variables). */ + + real_t *R; /**< Cholesky factor of H (i.e. H = R^T*R). */ + + real_t *x; /**< Primal solution vector. */ + real_t *y; /**< Dual solution vector. */ + + real_t *delta_xFR_TMP; /**< Temporary for determineStepDirection */ + + real_t tau; /**< Last homotopy step length. */ + real_t regVal; /**< Holds the offset used to regularise Hessian matrix (zero by default). */ + + real_t ramp0; /**< Start value for Ramping Strategy. */ + real_t ramp1; /**< Final value for Ramping Strategy. */ + + QProblemStatus status; /**< Current status of the solution process. */ + HessianType hessianType; /**< Type of Hessian matrix. */ + + BooleanType haveCholesky; /**< Flag indicating whether Cholesky decomposition has already been setup. */ + BooleanType infeasible; /**< QP infeasible? */ + BooleanType unbounded; /**< QP unbounded? */ + + int rampOffset; /**< Offset index for Ramping. */ + unsigned int count; /**< Counts the number of hotstart function calls (internal usage only!). */ +} QProblemB; + +int QProblemB_calculateMemorySize( unsigned int nV ); + +char *QProblemB_assignMemory( unsigned int nV, QProblemB **mem, void *raw_memory ); + +QProblemB *QProblemB_createMemory( unsigned int nV ); + + +/** Constructor which takes the QP dimension and Hessian type + * information. If the Hessian is the zero (i.e. HST_ZERO) or the + * identity matrix (i.e. HST_IDENTITY), respectively, no memory + * is allocated for it and a NULL pointer can be passed for it + * to the init() functions. */ +void QProblemBCON( QProblemB* _THIS, + int _nV, /**< Number of variables. */ + HessianType _hessianType /**< Type of Hessian matrix. */ + ); + +void QProblemBCPY( QProblemB* FROM, + QProblemB* TO + ); + + +/** Clears all data structures of QProblemB except for QP data. + * \return SUCCESSFUL_RETURN \n + RET_RESET_FAILED */ +returnValue QProblemB_reset( QProblemB* _THIS ); + + +/** Initialises a simply bounded QP problem with given QP data and tries to solve it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_initM( QProblemB* _THIS, + DenseMatrix *_H, /**< Hessian matrix. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + +/** Initialises a simply bounded QP problem with given QP data and tries to solve it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_init( QProblemB* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + +/** Initialises a simply bounded QP problem with given QP data to be read from files and solves it + * using at most nWSR iterations. + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_UNABLE_TO_READ_FILE */ +returnValue QProblemB_initF( QProblemB* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix is stored. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient vector is stored. */ + const char* const lb_file, /**< Name of file where lower bound vector. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bound vector. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation (if pointer passed). */ + ); + +/** Initialises a simply bounded QP problem with given QP data and tries to solve it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB from yOpt != 0, \n + * 4. 0, 0, gB: starts with xOpt = 0, yOpt = 0 and gB, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB from yOpt != 0, \n + * 6. xOpt, 0, gB: starts with xOpt, yOpt = 0 and gB, \n + * 7. xOpt, yOpt, gB: starts with xOpt, yOpt and gB (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_initMW( QProblemB* _THIS, + DenseMatrix *_H, /**< Hessian matrix. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, all bounds are assumed inactive!) */ + const real_t* const _R /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. + The Cholesky factor must be stored in a real_t array of size nV*nV + in row-major format. Note: Only used if xOpt/yOpt and gB are NULL! \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + +/** Initialises a simply bounded QP problem with given QP data and tries to solve it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB from yOpt != 0, \n + * 4. 0, 0, gB: starts with xOpt = 0, yOpt = 0 and gB, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB from yOpt != 0, \n + * 6. xOpt, 0, gB: starts with xOpt, yOpt = 0 and gB, \n + * 7. xOpt, yOpt, gB: starts with xOpt, yOpt and gB (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_initW( QProblemB* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub, /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, all bounds are assumed inactive!) */ + const real_t* const _R /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. + The Cholesky factor must be stored in a real_t array of size nV*nV + in row-major format. Note: Only used if xOpt/yOpt and gB are NULL! \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + +/** Initialises a simply bounded QP problem with given QP data to be read from files and solves it + * using at most nWSR iterations. Depending on the parameter constellation it: \n + * 1. 0, 0, 0 : starts with xOpt = 0, yOpt = 0 and gB empty (or all implicit equality bounds), \n + * 2. xOpt, 0, 0 : starts with xOpt, yOpt = 0 and obtain gB by "clipping", \n + * 3. 0, yOpt, 0 : starts with xOpt = 0, yOpt and obtain gB from yOpt != 0, \n + * 4. 0, 0, gB: starts with xOpt = 0, yOpt = 0 and gB, \n + * 5. xOpt, yOpt, 0 : starts with xOpt, yOpt and obtain gB from yOpt != 0, \n + * 6. xOpt, 0, gB: starts with xOpt, yOpt = 0 and gB, \n + * 7. xOpt, yOpt, gB: starts with xOpt, yOpt and gB (assume them to be consistent!) + * + * Note: This function internally calls solveInitialQP for initialisation! + * + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED \n + RET_UNABLE_TO_READ_FILE */ +returnValue QProblemB_initFW( QProblemB* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix is stored. \n + If Hessian matrix is trivial, a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient vector is stored. */ + const char* const lb_file, /**< Name of file where lower bound vector. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bound vector. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations when using initial homotopy. \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP initialisation. \n + Output: CPU time spent for QP initialisation. */ + const real_t* const xOpt, /**< Optimal primal solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old primal solution is kept!) */ + const real_t* const yOpt, /**< Optimal dual solution vector. A NULL pointer can be passed. \n + (If a null pointer is passed, the old dual solution is kept!) */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, all bounds are assumed inactive!) */ + const char* const R_file /**< Name of the file where a pre-computed (upper triangular) Cholesky factor + of the Hessian matrix is stored. \n + (If a null pointer is passed, Cholesky decomposition is computed internally!) */ + ); + + +/** Solves an initialised QP sequence using the online active set strategy. + * By default, QP solution is started from previous solution. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblemB_hotstart( QProblemB* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves an initialised QP sequence using the online active set strategy, + * where QP data is read from files. QP solution is started from previous solution. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_hotstartF( QProblemB* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves an initialised QP sequence using the online active set strategy. + * By default, QP solution is started from previous solution. If a guess + * for the working set is provided, an initialised homotopy is performed. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_SETUP_AUXILIARYQP_FAILED */ +returnValue QProblemB_hotstartW( QProblemB* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + Bounds* const guessedBounds /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set is kept!) */ + ); + +/** Solves an initialised QP sequence using the online active set strategy, + * where QP data is read from files. + * By default, QP solution is started from previous solution. If a guess + * for the working set is provided, an initialised homotopy is performed. + * + * Note: This function internally calls solveQP/solveRegularisedQP + * for solving an initialised QP! + * + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS \n + RET_SETUP_AUXILIARYQP_FAILED */ +returnValue QProblemB_hotstartFW( QProblemB* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + Bounds* const guessedBounds /**< Optimal working set of bounds for solution (xOpt,yOpt). \n + (If a null pointer is passed, the previous working set is kept!) */ + ); + + +/** Writes a vector with the state of the working set + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblemB_getWorkingSet( QProblemB* _THIS, + real_t* workingSet /** Output: array containing state of the working set. */ + ); + +/** Writes a vector with the state of the working set of bounds + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblemB_getWorkingSetBounds( QProblemB* _THIS, + real_t* workingSetB /** Output: array containing state of the working set of bounds. */ + ); + +/** Writes a vector with the state of the working set of constraints + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue QProblemB_getWorkingSetConstraints( QProblemB* _THIS, + real_t* workingSetC /** Output: array containing state of the working set of constraints. */ + ); + + +/** Returns current bounds object of the QP (deep copy). + * \return SUCCESSFUL_RETURN \n + RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblemB_getBounds( QProblemB* _THIS, + Bounds* _bounds /** Output: Bounds object. */ + ); + + +/** Returns the number of variables. + * \return Number of variables. */ +static inline int QProblemB_getNV( QProblemB* _THIS ); + +/** Returns the number of free variables. + * \return Number of free variables. */ +static inline int QProblemB_getNFR( QProblemB* _THIS ); + +/** Returns the number of fixed variables. + * \return Number of fixed variables. */ +static inline int QProblemB_getNFX( QProblemB* _THIS ); + +/** Returns the number of implicitly fixed variables. + * \return Number of implicitly fixed variables. */ +static inline int QProblemB_getNFV( QProblemB* _THIS ); + +/** Returns the dimension of null space. + * \return Dimension of null space. */ +int QProblemB_getNZ( QProblemB* _THIS ); + + +/** Returns the optimal objective function value. + * \return finite value: Optimal objective function value (QP was solved) \n + +infinity: QP was not yet solved */ +real_t QProblemB_getObjVal( QProblemB* _THIS ); + +/** Returns the objective function value at an arbitrary point x. + * \return Objective function value at point x */ +real_t QProblemB_getObjValX( QProblemB* _THIS, + const real_t* const _x /**< Point at which the objective function shall be evaluated. */ + ); + +/** Returns the primal solution vector. + * \return SUCCESSFUL_RETURN \n + RET_QP_NOT_SOLVED */ +returnValue QProblemB_getPrimalSolution( QProblemB* _THIS, + real_t* const xOpt /**< Output: Primal solution vector (if QP has been solved). */ + ); + +/** Returns the dual solution vector. + * \return SUCCESSFUL_RETURN \n + RET_QP_NOT_SOLVED */ +returnValue QProblemB_getDualSolution( QProblemB* _THIS, + real_t* const yOpt /**< Output: Dual solution vector (if QP has been solved). */ + ); + + +/** Returns status of the solution process. + * \return Status of solution process. */ +static inline QProblemStatus QProblemB_getStatus( QProblemB* _THIS ); + + +/** Returns if the QProblem object is initialised. + * \return BT_TRUE: QProblemB initialised \n + BT_FALSE: QProblemB not initialised */ +static inline BooleanType QProblemB_isInitialised( QProblemB* _THIS ); + +/** Returns if the QP has been solved. + * \return BT_TRUE: QProblemB solved \n + BT_FALSE: QProblemB not solved */ +static inline BooleanType QProblemB_isSolved( QProblemB* _THIS ); + +/** Returns if the QP is infeasible. + * \return BT_TRUE: QP infeasible \n + BT_FALSE: QP feasible (or not known to be infeasible!) */ +static inline BooleanType QProblemB_isInfeasible( QProblemB* _THIS ); + +/** Returns if the QP is unbounded. + * \return BT_TRUE: QP unbounded \n + BT_FALSE: QP unbounded (or not known to be unbounded!) */ +static inline BooleanType QProblemB_isUnbounded( QProblemB* _THIS ); + + +/** Returns Hessian type flag (type is not determined due to _THIS call!). + * \return Hessian type. */ +static inline HessianType QProblemB_getHessianType( QProblemB* _THIS ); + +/** Changes the print level. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblemB_setHessianType( QProblemB* _THIS, + HessianType _hessianType /**< New Hessian type. */ + ); + +/** Returns if the QP has been internally regularised. + * \return BT_TRUE: Hessian is internally regularised for QP solution \n + BT_FALSE: No internal Hessian regularisation is used for QP solution */ +static inline BooleanType QProblemB_usingRegularisation( QProblemB* _THIS ); + +/** Returns current options struct. + * \return Current options struct. */ +static inline Options QProblemB_getOptions( QProblemB* _THIS ); + +/** Overrides current options with given ones. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblemB_setOptions( QProblemB* _THIS, + Options _options /**< New options. */ + ); + +/** Returns the print level. + * \return Print level. */ +static inline PrintLevel QProblemB_getPrintLevel( QProblemB* _THIS ); + +/** Changes the print level. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_setPrintLevel( QProblemB* _THIS, + PrintLevel _printlevel /**< New print level. */ + ); + +/** Returns the current number of QP problems solved. + * \return Number of QP problems solved. */ +static inline unsigned int QProblemB_getCount( QProblemB* _THIS ); + +/** Resets QP problem counter (to zero). + * \return SUCCESSFUL_RETURN. */ +static inline returnValue QProblemB_resetCounter( QProblemB* _THIS ); + + +/** Prints concise list of properties of the current QP. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblemB_printProperties( QProblemB* _THIS ); + +/** Prints a list of all options and their current values. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblemB_printOptions( QProblemB* _THIS ); + + +/** If Hessian type has been set by the user, nothing is done. + * Otherwise the Hessian type is set to HST_IDENTITY, HST_ZERO, or + * HST_POSDEF (default), respectively. + * \return SUCCESSFUL_RETURN \n + RET_HESSIAN_INDEFINITE */ +returnValue QProblemB_determineHessianType( QProblemB* _THIS ); + +/** Determines type of existing constraints and bounds (i.e. implicitly fixed, unbounded etc.). + * \return SUCCESSFUL_RETURN \n + RET_SETUPSUBJECTTOTYPE_FAILED */ +returnValue QProblemB_setupSubjectToType( QProblemB* _THIS ); + +/** Determines type of new constraints and bounds (i.e. implicitly fixed, unbounded etc.). + * \return SUCCESSFUL_RETURN \n + RET_SETUPSUBJECTTOTYPE_FAILED */ +returnValue QProblemB_setupSubjectToTypeNew( QProblemB* _THIS, + const real_t* const lb_new, /**< New lower bounds. */ + const real_t* const ub_new /**< New upper bounds. */ + ); + +/** Computes the Cholesky decomposition of the (simply projected) Hessian + * (i.e. R^T*R = Z^T*H*Z). It only works in the case where Z is a simple + * projection matrix! + * Note: If Hessian turns out not to be positive definite, the Hessian type + * is set to HST_SEMIDEF accordingly. + * \return SUCCESSFUL_RETURN \n + * RET_HESSIAN_NOT_SPD \n + * RET_INDEXLIST_CORRUPTED */ +returnValue QProblemB_computeCholesky( QProblemB* _THIS ); + +/** Computes initial Cholesky decomposition of the projected Hessian making + * use of the function setupCholeskyDecomposition() or setupCholeskyDecompositionProjected(). + * \return SUCCESSFUL_RETURN \n + * RET_HESSIAN_NOT_SPD \n + * RET_INDEXLIST_CORRUPTED */ +returnValue QProblemB_setupInitialCholesky( QProblemB* _THIS ); + + +/** Obtains the desired working set for the auxiliary initial QP in + * accordance with the user specifications + * \return SUCCESSFUL_RETURN \n + RET_OBTAINING_WORKINGSET_FAILED \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_obtainAuxiliaryWorkingSet( QProblemB* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + const real_t* const yOpt, /**< Optimal dual solution vector. + * If a NULL pointer is passed, all entries are assumed to be zero. */ + Bounds* const guessedBounds, /**< Guessed working set for solution (xOpt,yOpt). */ + Bounds* auxiliaryBounds /**< Input: Allocated bound object. \n + * Ouput: Working set for auxiliary QP. */ + ); + +/** Decides if lower bounds are smaller than upper bounds + * + * \return SUCCESSFUL_RETURN \n + * RET_QP_INFEASIBLE */ +returnValue QProblemB_areBoundsConsistent( QProblemB* _THIS, + const real_t* const lb, /**< Vector of lower bounds*/ + const real_t* const ub /**< Vector of upper bounds*/ + ); + +/** Solves the system Ra = b or R^Ta = b where R is an upper triangular matrix. + * \return SUCCESSFUL_RETURN \n + RET_DIV_BY_ZERO */ +returnValue QProblemB_backsolveR( QProblemB* _THIS, + const real_t* const b, /**< Right hand side vector. */ + BooleanType transposed, /**< Indicates if the transposed system shall be solved. */ + real_t* const a /**< Output: Solution vector */ + ); + +/** Solves the system Ra = b or R^Ta = b where R is an upper triangular matrix. \n + * Special variant for the case that _THIS function is called from within "removeBound()". + * \return SUCCESSFUL_RETURN \n + RET_DIV_BY_ZERO */ +returnValue QProblemB_backsolveRrem( QProblemB* _THIS, + const real_t* const b, /**< Right hand side vector. */ + BooleanType transposed, /**< Indicates if the transposed system shall be solved. */ + BooleanType removingBound, /**< Indicates if function is called from "removeBound()". */ + real_t* const a /**< Output: Solution vector */ + ); + + +/** Determines step direction of the shift of the QP data. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_determineDataShift( QProblemB* _THIS, + const real_t* const g_new, /**< New gradient vector. */ + const real_t* const lb_new, /**< New lower bounds. */ + const real_t* const ub_new, /**< New upper bounds. */ + real_t* const delta_g, /**< Output: Step direction of gradient vector. */ + real_t* const delta_lb, /**< Output: Step direction of lower bounds. */ + real_t* const delta_ub, /**< Output: Step direction of upper bounds. */ + BooleanType* Delta_bB_isZero/**< Output: Indicates if active bounds are to be shifted. */ + ); + + +/** Sets up internal QP data. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_setupQPdataM( QProblemB* _THIS, + DenseMatrix *_H, /**< Hessian matrix.*/ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Sets up internal QP data. If the current Hessian is trivial + * (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS \n + RET_NO_HESSIAN_SPECIFIED */ +returnValue QProblemB_setupQPdata( QProblemB* _THIS, + real_t* const _H, /**< Hessian matrix. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const real_t* const _g, /**< Gradient vector. */ + const real_t* const _lb, /**< Lower bounds (on variables). \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const _ub /**< Upper bounds (on variables). \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Sets up internal QP data by loading it from files. If the current Hessian + * is trivial (i.e. HST_ZERO or HST_IDENTITY) but a non-trivial one is given, + * memory for Hessian is allocated and it is set to the given one. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS \n + RET_NO_HESSIAN_SPECIFIED */ +returnValue QProblemB_setupQPdataFromFile( QProblemB* _THIS, + const char* const H_file, /**< Name of file where Hessian matrix, of neighbouring QP to be solved, is stored. \n + If Hessian matrix is trivial,a NULL pointer can be passed. */ + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + ); + +/** Loads new QP vectors from files (internal members are not affected!). + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE \n + RET_INVALID_ARGUMENTS */ +returnValue QProblemB_loadQPvectorsFromFile( QProblemB* _THIS, + const char* const g_file, /**< Name of file where gradient, of neighbouring QP to be solved, is stored. */ + const char* const lb_file, /**< Name of file where lower bounds, of neighbouring QP to be solved, is stored. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const char* const ub_file, /**< Name of file where upper bounds, of neighbouring QP to be solved, is stored. \n + If no upper bounds exist, a NULL pointer can be passed. */ + real_t* const g_new, /**< Output: Gradient of neighbouring QP to be solved. */ + real_t* const lb_new, /**< Output: Lower bounds of neighbouring QP to be solved */ + real_t* const ub_new /**< Output: Upper bounds of neighbouring QP to be solved */ + ); + + +/** Sets internal infeasibility flag and throws given error in case the far bound + * strategy is not enabled (as QP might actually not be infeasible in _THIS case). + * \return RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_ENSURELI_FAILED_CYCLING \n + RET_ENSURELI_FAILED_NOINDEX */ +returnValue QProblemB_setInfeasibilityFlag( QProblemB* _THIS, + returnValue returnvalue, /**< Returnvalue to be tunneled. */ + BooleanType doThrowError /**< Flag forcing to throw an error. */ + ); + + +/** Determines if next QP iteration can be performed within given CPU time limit. + * \return BT_TRUE: CPU time limit is exceeded, stop QP solution. \n + BT_FALSE: Sufficient CPU time for next QP iteration. */ +BooleanType QProblemB_isCPUtimeLimitExceeded( QProblemB* _THIS, + const real_t* const cputime, /**< Maximum CPU time allowed for QP solution. */ + real_t starttime, /**< Start time of current QP solution. */ + int nWSR /**< Number of working set recalculations performed so far. */ + ); + + +/** Regularise Hessian matrix by adding a scaled identity matrix to it. + * \return SUCCESSFUL_RETURN \n + RET_HESSIAN_ALREADY_REGULARISED */ +returnValue QProblemB_regulariseHessian( QProblemB* _THIS ); + + +/** Sets Hessian matrix of the QP. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblemB_setHM( QProblemB* _THIS, + DenseMatrix* H_new /**< New Hessian matrix. */ + ); + +/** Sets dense Hessian matrix of the QP. + * If a null pointer is passed and + * a) hessianType is HST_IDENTITY, nothing is done, + * b) hessianType is not HST_IDENTITY, Hessian matrix is set to zero. + * \return SUCCESSFUL_RETURN */ +static inline returnValue QProblemB_setH( QProblemB* _THIS, + real_t* const H_new /**< New dense Hessian matrix (with correct dimension!). */ + ); + +/** Changes gradient vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +static inline returnValue QProblemB_setG( QProblemB* _THIS, + const real_t* const g_new /**< New gradient vector (with correct dimension!). */ + ); + +/** Changes lower bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblemB_setLB( QProblemB* _THIS, + const real_t* const lb_new /**< New lower bound vector (with correct dimension!). */ + ); + +/** Changes single entry of lower bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP \n + * RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblemB_setLBn( QProblemB* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of lower bound vector. */ + ); + +/** Changes upper bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP */ +static inline returnValue QProblemB_setUB( QProblemB* _THIS, + const real_t* const ub_new /**< New upper bound vector (with correct dimension!). */ + ); + +/** Changes single entry of upper bound vector of the QP. + * \return SUCCESSFUL_RETURN \n + * RET_QPOBJECT_NOT_SETUP \n + * RET_INDEX_OUT_OF_BOUNDS */ +static inline returnValue QProblemB_setUBn( QProblemB* _THIS, + int number, /**< Number of entry to be changed. */ + real_t value /**< New value for entry of upper bound vector. */ + ); + + +/** Computes parameters for the Givens matrix G for which [x,y]*G = [z,0] + * \return SUCCESSFUL_RETURN */ +static inline void QProblemB_computeGivens( real_t xold, /**< Matrix entry to be normalised. */ + real_t yold, /**< Matrix entry to be annihilated. */ + real_t* xnew, /**< Output: Normalised matrix entry. */ + real_t* ynew, /**< Output: Annihilated matrix entry. */ + real_t* c, /**< Output: Cosine entry of Givens matrix. */ + real_t* s /**< Output: Sine entry of Givens matrix. */ + ); + +/** Applies Givens matrix determined by c and s (cf. computeGivens). + * \return SUCCESSFUL_RETURN */ +static inline void QProblemB_applyGivens( real_t c, /**< Cosine entry of Givens matrix. */ + real_t s, /**< Sine entry of Givens matrix. */ + real_t nu, /**< Further factor: s/(1+c). */ + real_t xold, /**< Matrix entry to be transformed corresponding to + * the normalised entry of the original matrix. */ + real_t yold, /**< Matrix entry to be transformed corresponding to + * the annihilated entry of the original matrix. */ + real_t* xnew, /**< Output: Transformed matrix entry corresponding to + * the normalised entry of the original matrix. */ + real_t* ynew /**< Output: Transformed matrix entry corresponding to + * the annihilated entry of the original matrix. */ + ); + + + +/** Compute relative length of homotopy in data space for termination + * criterion. + * \return Relative length in data space. */ +real_t QProblemB_getRelativeHomotopyLength( QProblemB* _THIS, + const real_t* const g_new, /**< Final gradient. */ + const real_t* const lb_new, /**< Final lower variable bounds. */ + const real_t* const ub_new /**< Final upper variable bounds. */ + ); + +/** Ramping Strategy to avoid ties. Modifies homotopy start without + * changing current active set. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_performRamping( QProblemB* _THIS ); + + +/** ... */ +returnValue QProblemB_updateFarBounds( QProblemB* _THIS, + real_t curFarBound, /**< ... */ + int nRamp, /**< ... */ + const real_t* const lb_new, /**< ... */ + real_t* const lb_new_far, /**< ... */ + const real_t* const ub_new, /**< ... */ + real_t* const ub_new_far /**< ... */ + ); + + + +/** Performs robustified ratio test yield the maximum possible step length + * along the homotopy path. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_performRatioTestB( QProblemB* _THIS, + int nIdx, /**< Number of ratios to be checked. */ + const int* const idxList, /**< Array containing the indices of all ratios to be checked. */ + Bounds* const subjectTo, /**< Bound object corresponding to ratios to be checked. */ + const real_t* const num, /**< Array containing all numerators for performing the ratio test. */ + const real_t* const den, /**< Array containing all denominators for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t, /**< Output: Maximum possible step length along the homotopy path. */ + int* BC_idx /**< Output: Index of blocking constraint. */ + ); + +/** Checks whether given ratio is blocking, i.e. limits the maximum step length + * along the homotopy path to a value lower than given one. + * \return SUCCESSFUL_RETURN */ +static inline BooleanType QProblemB_isBlocking( QProblemB* _THIS, + real_t num, /**< Numerator for performing the ratio test. */ + real_t den, /**< Denominator for performing the ratio test. */ + real_t epsNum, /**< Numerator tolerance. */ + real_t epsDen, /**< Denominator tolerance. */ + real_t* t /**< Input: Current maximum step length along the homotopy path, + * Output: Updated maximum possible step length along the homotopy path. */ + ); + + +/** Solves a QProblemB whose QP data is assumed to be stored in the member variables. + * A guess for its primal/dual optimal solution vectors and the corresponding + * optimal working set can be provided. + * Note: This function is internally called by all init functions! + * \return SUCCESSFUL_RETURN \n + RET_INIT_FAILED \n + RET_INIT_FAILED_CHOLESKY \n + RET_INIT_FAILED_HOTSTART \n + RET_INIT_FAILED_INFEASIBILITY \n + RET_INIT_FAILED_UNBOUNDEDNESS \n + RET_MAX_NWSR_REACHED */ +returnValue QProblemB_solveInitialQP( QProblemB* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector.*/ + const real_t* const yOpt, /**< Optimal dual solution vector. */ + Bounds* const guessedBounds, /**< Optimal working set of bounds for solution (xOpt,yOpt). */ + const real_t* const _R, /**< Pre-computed (upper triangular) Cholesky factor of Hessian matrix. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + * Output: Number of performed working set recalculations. */ + real_t* const cputime /**< Input: Maximum CPU time allowed for QP solution. \n + * Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + ); + +/** Solves an initialised QProblemB using online active set strategy. + * Note: This function is internally called by all hotstart functions! + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblemB_solveQP( QProblemB* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + int nWSRperformed, /**< Number of working set recalculations already performed to solve + this QP within previous solveQP() calls. This number is + always zero, except for successive calls from solveRegularisedQP() + or when using the far bound strategy. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + +/** Solves an initialised QProblemB using online active set strategy. + * Note: This function is internally called by all hotstart functions! + * \return SUCCESSFUL_RETURN \n + RET_MAX_NWSR_REACHED \n + RET_HOTSTART_FAILED_AS_QP_NOT_INITIALISED \n + RET_HOTSTART_FAILED \n + RET_SHIFT_DETERMINATION_FAILED \n + RET_STEPDIRECTION_DETERMINATION_FAILED \n + RET_STEPLENGTH_DETERMINATION_FAILED \n + RET_HOMOTOPY_STEP_FAILED \n + RET_HOTSTART_STOPPED_INFEASIBILITY \n + RET_HOTSTART_STOPPED_UNBOUNDEDNESS */ +returnValue QProblemB_solveRegularisedQP( QProblemB* _THIS, + const real_t* const g_new, /**< Gradient of neighbouring QP to be solved. */ + const real_t* const lb_new, /**< Lower bounds of neighbouring QP to be solved. \n + If no lower bounds exist, a NULL pointer can be passed. */ + const real_t* const ub_new, /**< Upper bounds of neighbouring QP to be solved. \n + If no upper bounds exist, a NULL pointer can be passed. */ + int* nWSR, /**< Input: Maximum number of working set recalculations; \n + Output: Number of performed working set recalculations. */ + real_t* const cputime, /**< Input: Maximum CPU time allowed for QP solution. \n + Output: CPU time spent for QP solution (or to perform nWSR iterations). */ + int nWSRperformed, /**< Number of working set recalculations already performed to solve + this QP within previous solveRegularisedQP() calls. This number is + always zero, except for successive calls when using the far bound strategy. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + +/** Sets up bound data structure according to auxiliaryBounds. + * (If the working set shall be setup afresh, make sure that + * bounds data structure has been resetted!) + * \return SUCCESSFUL_RETURN \n + RET_SETUP_WORKINGSET_FAILED \n + RET_INVALID_ARGUMENTS \n + RET_UNKNOWN_BUG */ +returnValue QProblemB_setupAuxiliaryWorkingSet( QProblemB* _THIS, + Bounds* const auxiliaryBounds, /**< Working set for auxiliary QP. */ + BooleanType setupAfresh /**< Flag indicating if given working set shall be + * setup afresh or by updating the current one. */ + ); + +/** Sets up the optimal primal/dual solution of the auxiliary initial QP. + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_setupAuxiliaryQPsolution( QProblemB* _THIS, + const real_t* const xOpt, /**< Optimal primal solution vector. + * If a NULL pointer is passed, all entries are set to zero. */ + const real_t* const yOpt /**< Optimal dual solution vector. + * If a NULL pointer is passed, all entries are set to zero. */ + ); + +/** Sets up gradient of the auxiliary initial QP for given + * optimal primal/dual solution and given initial working set + * (assumes that members X, Y and BOUNDS have already been (ialised!). + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_setupAuxiliaryQPgradient( QProblemB* _THIS ); + +/** Sets up bounds of the auxiliary initial QP for given + * optimal primal/dual solution and given initial working set + * (assumes that members X, Y and BOUNDS have already been initialised!). + * \return SUCCESSFUL_RETURN \n + RET_UNKNOWN_BUG */ +returnValue QProblemB_setupAuxiliaryQPbounds( QProblemB* _THIS, + BooleanType useRelaxation /**< Flag indicating if inactive bounds shall be relaxed. */ + ); + + +/** Updates QP vectors, working sets and internal data structures in order to + start from an optimal solution corresponding to initial guesses of the working + set for bounds + * \return SUCCESSFUL_RETURN \n + * RET_SETUP_AUXILIARYQP_FAILED */ +returnValue QProblemB_setupAuxiliaryQP( QProblemB* _THIS, + Bounds* const guessedBounds /**< Initial guess for working set of bounds. */ + ); + +/** Determines step direction of the homotopy path. + * \return SUCCESSFUL_RETURN \n + RET_STEPDIRECTION_FAILED_CHOLESKY */ +returnValue QProblemB_determineStepDirection( QProblemB* _THIS, + const real_t* const delta_g, /**< Step direction of gradient vector. */ + const real_t* const delta_lb, /**< Step direction of lower bounds. */ + const real_t* const delta_ub, /**< Step direction of upper bounds. */ + BooleanType Delta_bB_isZero, /**< Indicates if active bounds are to be shifted. */ + real_t* const delta_xFX, /**< Output: Primal homotopy step direction of fixed variables. */ + real_t* const delta_xFR, /**< Output: Primal homotopy step direction of free variables. */ + real_t* const delta_yFX /**< Output: Dual homotopy step direction of fixed variables' multiplier. */ + ); + +/** Determines the maximum possible step length along the homotopy path + * and performs _THIS step (without changing working set). + * \return SUCCESSFUL_RETURN \n + * RET_QP_INFEASIBLE \n + */ +returnValue QProblemB_performStep( QProblemB* _THIS, + const real_t* const delta_g, /**< Step direction of gradient. */ + const real_t* const delta_lb, /**< Step direction of lower bounds. */ + const real_t* const delta_ub, /**< Step direction of upper bounds. */ + const real_t* const delta_xFX, /**< Primal homotopy step direction of fixed variables. */ + const real_t* const delta_xFR, /**< Primal homotopy step direction of free variables. */ + const real_t* const delta_yFX, /**< Dual homotopy step direction of fixed variables' multiplier. */ + int* BC_idx, /**< Output: Index of blocking constraint. */ + SubjectToStatus* BC_status /**< Output: Status of blocking constraint. */ + ); + +/** Updates active set. + * \return SUCCESSFUL_RETURN \n + RET_REMOVE_FROM_ACTIVESET_FAILED \n + RET_ADD_TO_ACTIVESET_FAILED */ +returnValue QProblemB_changeActiveSet( QProblemB* _THIS, + int BC_idx, /**< Index of blocking constraint. */ + SubjectToStatus BC_status /**< Status of blocking constraint. */ + ); + +/** Drift correction at end of each active set iteration + * \return SUCCESSFUL_RETURN */ +returnValue QProblemB_performDriftCorrection( QProblemB* _THIS ); + +/** Determines if it is more efficient to refactorise the matrices when + * hotstarting or not (i.e. better to update the existing factorisations). + * \return BT_TRUE iff matrices shall be refactorised afresh + */ +BooleanType QProblemB_shallRefactorise( QProblemB* _THIS, + Bounds* const guessedBounds /**< Guessed new working set. */ + ); + + +/** Adds a bound to active set (specialised version for the case where no constraints exist). + * \return SUCCESSFUL_RETURN \n + RET_ADDBOUND_FAILED */ +returnValue QProblemB_addBound( QProblemB* _THIS, + int number, /**< Number of bound to be added to active set. */ + SubjectToStatus B_status, /**< Status of new active bound. */ + BooleanType updateCholesky /**< Flag indicating if Cholesky decomposition shall be updated. */ + ); + +/** Removes a bounds from active set (specialised version for the case where no constraints exist). + * \return SUCCESSFUL_RETURN \n + RET_HESSIAN_NOT_SPD \n + RET_REMOVEBOUND_FAILED */ +returnValue QProblemB_removeBound( QProblemB* _THIS, + int number, /**< Number of bound to be removed from active set. */ + BooleanType updateCholesky /**< Flag indicating if Cholesky decomposition shall be updated. */ + ); + + +/** Prints concise information on the current iteration. + * \return SUCCESSFUL_RETURN \n */ +returnValue QProblemB_printIteration( QProblemB* _THIS, + int iter, /**< Number of current iteration. */ + int BC_idx, /**< Index of blocking bound. */ + SubjectToStatus BC_status, /**< Status of blocking bound. */ + real_t homotopyLength, /**< Current homotopy distance. */ + BooleanType isFirstCall /**< Indicating whether this is the first call for current QP. */ + ); + + + +/* + * g e t B o u n d s + */ +static inline returnValue QProblemB_getBounds( QProblemB* _THIS, Bounds* _bounds ) +{ + int nV = QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + _bounds = _THIS->bounds; + + return SUCCESSFUL_RETURN; +} + + +/* + * g e t N V + */ +static inline int QProblemB_getNV( QProblemB* _THIS ) +{ + return Bounds_getNV( _THIS->bounds ); +} + + +/* + * g e t N F R + */ +static inline int QProblemB_getNFR( QProblemB* _THIS ) +{ + return Bounds_getNFR( _THIS->bounds ); +} + + +/* + * g e t N F X + */ +static inline int QProblemB_getNFX( QProblemB* _THIS ) +{ + return Bounds_getNFX( _THIS->bounds ); +} + + +/* + * g e t N F V + */ +static inline int QProblemB_getNFV( QProblemB* _THIS ) +{ + return Bounds_getNFV( _THIS->bounds ); +} + + +/* + * g e t S t a t u s + */ +static inline QProblemStatus QProblemB_getStatus( QProblemB* _THIS ) +{ + return _THIS->status; +} + + +/* + * i s I n i t i a l i s e d + */ +static inline BooleanType QProblemB_isInitialised( QProblemB* _THIS ) +{ + if ( _THIS->status == QPS_NOTINITIALISED ) + return BT_FALSE; + else + return BT_TRUE; +} + + +/* + * i s S o l v e d + */ +static inline BooleanType QProblemB_isSolved( QProblemB* _THIS ) +{ + if ( _THIS->status == QPS_SOLVED ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * i s I n f e a s i b l e + */ +static inline BooleanType QProblemB_isInfeasible( QProblemB* _THIS ) +{ + return _THIS->infeasible; +} + + +/* + * i s U n b o u n d e d + */ +static inline BooleanType QProblemB_isUnbounded( QProblemB* _THIS ) +{ + return _THIS->unbounded; +} + + +/* + * g e t H e s s i a n T y p e + */ +static inline HessianType QProblemB_getHessianType( QProblemB* _THIS ) +{ + return _THIS->hessianType; +} + + +/* + * s e t H e s s i a n T y p e + */ +static inline returnValue QProblemB_setHessianType( QProblemB* _THIS, HessianType _hessianType ) +{ + _THIS->hessianType = _hessianType; + return SUCCESSFUL_RETURN; +} + + +/* + * u s i n g R e g u l a r i s a t i o n + */ +static inline BooleanType QProblemB_usingRegularisation( QProblemB* _THIS ) +{ + if ( _THIS->regVal > QPOASES_ZERO ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * g e t O p t i o n s + */ +static inline Options QProblemB_getOptions( QProblemB* _THIS ) +{ + return _THIS->options; +} + + +/* + * s e t O p t i o n s + */ +static inline returnValue QProblemB_setOptions( QProblemB* _THIS, + Options _options + ) +{ + OptionsCPY( &_options,&(_THIS->options) ); + Options_ensureConsistency( &(_THIS->options) ); + + QProblemB_setPrintLevel( _THIS,_THIS->options.printLevel ); + + return SUCCESSFUL_RETURN; +} + + +/* + * g e t P r i n t L e v e l + */ +static inline PrintLevel QProblemB_getPrintLevel( QProblemB* _THIS ) +{ + return _THIS->options.printLevel; +} + + + +/* + * g e t C o u n t + */ +static inline unsigned int QProblemB_getCount( QProblemB* _THIS ) +{ + return _THIS->count; +} + + +/* + * r e s e t C o u n t e r + */ +static inline returnValue QProblemB_resetCounter( QProblemB* _THIS ) +{ + _THIS->count = 0; + return SUCCESSFUL_RETURN; +} + + + +/***************************************************************************** + * P R O T E C T E D * + *****************************************************************************/ + + +/* + * s e t H + */ +static inline returnValue QProblemB_setHM( QProblemB* _THIS, DenseMatrix* H_new ) +{ + if ( H_new == 0 ) + return QProblemB_setH( _THIS,(real_t*)0 ); + else + return QProblemB_setH( _THIS,DenseMatrix_getVal(H_new) ); +} + + +/* + * s e t H + */ +static inline returnValue QProblemB_setH( QProblemB* _THIS, real_t* const H_new ) +{ + /* if null pointer is passed, Hessian is set to zero matrix + * (or stays identity matrix) */ + if ( H_new == 0 ) + { + if ( _THIS->hessianType == HST_IDENTITY ) + return SUCCESSFUL_RETURN; + + _THIS->hessianType = HST_ZERO; + + _THIS->H = 0; + } + else + { + DenseMatrixCON( _THIS->H,QProblemB_getNV( _THIS ),QProblemB_getNV( _THIS ),QProblemB_getNV( _THIS ),H_new ); + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t G + */ +static inline returnValue QProblemB_setG( QProblemB* _THIS, const real_t* const g_new ) +{ + unsigned int nV = (unsigned int)QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( g_new == 0 ) + return THROWERROR( RET_INVALID_ARGUMENTS ); + + memcpy( _THIS->g,g_new,nV*sizeof(real_t) ); + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B + */ +static inline returnValue QProblemB_setLB( QProblemB* _THIS, const real_t* const lb_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( lb_new != 0 ) + { + memcpy( _THIS->lb,lb_new,nV*sizeof(real_t) ); + } + else + { + /* if no lower bounds are specified, set them to -infinity */ + for( i=0; ilb[i] = -QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t L B + */ +static inline returnValue QProblemB_setLBn( QProblemB* _THIS, int number, real_t value ) +{ + int nV = QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nV ) ) + { + _THIS->lb[number] = value; + return SUCCESSFUL_RETURN; + } + else + { + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); + } +} + + +/* + * s e t U B + */ +static inline returnValue QProblemB_setUB( QProblemB* _THIS, const real_t* const ub_new ) +{ + unsigned int i; + unsigned int nV = (unsigned int)QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ub_new != 0 ) + { + memcpy( _THIS->ub,ub_new,nV*sizeof(real_t) ); + } + else + { + /* if no upper bounds are specified, set them to infinity */ + for( i=0; iub[i] = QPOASES_INFTY; + } + + return SUCCESSFUL_RETURN; +} + + +/* + * s e t U B + */ +static inline returnValue QProblemB_setUBn( QProblemB* _THIS, int number, real_t value ) +{ + int nV = QProblemB_getNV( _THIS ); + + if ( nV == 0 ) + return THROWERROR( RET_QPOBJECT_NOT_SETUP ); + + if ( ( number >= 0 ) && ( number < nV ) ) + { + _THIS->ub[number] = value; + + return SUCCESSFUL_RETURN; + } + else + { + return THROWERROR( RET_INDEX_OUT_OF_BOUNDS ); + } +} + + +/* + * c o m p u t e G i v e n s + */ +static inline void QProblemB_computeGivens( real_t xold, real_t yold, + real_t* xnew, real_t* ynew, real_t* c, real_t* s + ) +{ + real_t t, mu; + + if ( fabs( yold ) <= QPOASES_ZERO ) + { + *c = 1.0; + *s = 0.0; + + *xnew = xold; + *ynew = yold; + } + else + { + mu = fabs( xold ); + if ( fabs( yold ) > mu ) + mu = fabs( yold ); + + t = mu * sqrt( (xold/mu)*(xold/mu) + (yold/mu)*(yold/mu) ); + + if ( xold < 0.0 ) + t = -t; + + *c = xold/t; + *s = yold/t; + *xnew = t; + *ynew = 0.0; + } + + return; +} + + +/* + * a p p l y G i v e n s + */ +static inline void QProblemB_applyGivens( real_t c, real_t s, real_t nu, real_t xold, real_t yold, + real_t* xnew, real_t* ynew + ) +{ + #ifdef __USE_THREE_MULTS_GIVENS__ + + /* Givens plane rotation requiring only three multiplications, + * cf. Hammarling, S.: A note on modifications to the givens plane rotation. + * J. Inst. Maths Applics, 13:215-218, 1974. */ + *xnew = xold*c + yold*s; + *ynew = (*xnew+xold)*nu - yold; + + #else + + /* Usual Givens plane rotation requiring four multiplications. */ + *xnew = c*xold + s*yold; + *ynew = -s*xold + c*yold; + + #endif + + return; +} + + +/* + * i s B l o c k i n g + */ +static inline BooleanType QProblemB_isBlocking( QProblemB* _THIS, + real_t num, + real_t den, + real_t epsNum, + real_t epsDen, + real_t* t + ) +{ + if ( ( den >= epsDen ) && ( num >= epsNum ) ) + { + if ( num < (*t)*den ) + return BT_TRUE; + } + + return BT_FALSE; +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_QPROBLEMB_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Types.h b/phonelibs/acados/include/qpOASES_e/Types.h new file mode 100644 index 0000000000..1e452097f8 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Types.h @@ -0,0 +1,310 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Types.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of all non-built-in types (except for classes). + */ + + +#ifndef QPOASES_TYPES_H +#define QPOASES_TYPES_H + +#ifdef USE_ACADOS_TYPES +#include "acados/utils/types.h" +#endif + +/* If your compiler does not support the snprintf() function, + * uncomment the following line and try to compile again. */ +/* #define __NO_SNPRINTF__ */ + + +/* Uncomment the following line for setting the __DSPACE__ flag. */ +/* #define __DSPACE__ */ + +/* Uncomment the following line for setting the __XPCTARGET__ flag. */ +/* #define __XPCTARGET__ */ + + +/* Uncomment the following line for setting the __NO_FMATH__ flag. */ +/* #define __NO_FMATH__ */ + +/* Uncomment the following line to enable debug information. */ +/* #define __DEBUG__ */ + +/* Uncomment the following line to enable suppress any kind of console output. */ +/* #define __SUPPRESSANYOUTPUT__ */ + + +/** Forces to always include all implicitly fixed bounds and all equality constraints + * into the initial working set when setting up an auxiliary QP. */ +#define __ALWAYS_INITIALISE_WITH_ALL_EQUALITIES__ + +/* Uncomment the following line to activate the use of an alternative Givens + * plane rotation requiring only three multiplications. */ +/* #define __USE_THREE_MULTS_GIVENS__ */ + +/* Uncomment the following line to activate the use of single precision arithmetic. */ +/* #define __USE_SINGLE_PRECISION__ */ + +/* The inline keyword is skipped by default as it is not part of the C90 standard. + * However, by uncommenting the following line, use of the inline keyword can be enforced. */ +/* #define __USE_INLINE__ */ + + +/* Work-around for Borland BCC 5.5 compiler. */ +#ifdef __BORLANDC__ +#if __BORLANDC__ < 0x0561 + #define __STDC__ 1 +#endif +#endif + + +/* Work-around for Microsoft compilers. */ +#ifdef _MSC_VER + #define __NO_SNPRINTF__ + #pragma warning( disable : 4061 4100 4250 4514 4996 ) +#endif + + +/* Apply pre-processor settings when using qpOASES within auto-generated code. */ +#ifdef __CODE_GENERATION__ + #define __NO_COPYRIGHT__ + #define __EXTERNAL_DIMENSIONS__ +#endif /* __CODE_GENERATION__ */ + + +/* Avoid using static variables declaration within functions. */ +#ifdef __NO_STATIC__ + #define myStatic +#else + #define myStatic static +#endif /* __NO_STATIC__ */ + + +/* Skip inline keyword if not specified otherwise. */ +#ifndef __USE_INLINE__ + #define inline +#endif + + +/* Avoid any printing on embedded platforms. */ +#if defined(__DSPACE__) || defined(__XPCTARGET__) + #define __SUPPRESSANYOUTPUT__ + #define __NO_SNPRINTF__ +#endif + + +#ifdef __NO_SNPRINTF__ + #if (!defined(_MSC_VER)) || defined(__DSPACE__) || defined(__XPCTARGET__) + /* If snprintf is not available, provide an empty implementation... */ + int snprintf( char* s, size_t n, const char* format, ... ); + #else + /* ... or substitute snprintf by _snprintf for Microsoft compilers. */ + #define snprintf _snprintf + #endif +#endif /* __NO_SNPRINTF__ */ + + +/** Macro for switching on/off the beginning of the qpOASES namespace definition. */ +#define BEGIN_NAMESPACE_QPOASES + +/** Macro for switching on/off the end of the qpOASES namespace definition. */ +#define END_NAMESPACE_QPOASES + +/** Macro for switching on/off the use of the qpOASES namespace. */ +#define USING_NAMESPACE_QPOASES + +/** Macro for switching on/off references to the qpOASES namespace. */ +#define REFER_NAMESPACE_QPOASES /*::*/ + + +/** Macro for accessing the Cholesky factor R. */ +#define RR( I,J ) _THIS->R[(I)+nV*(J)] + +/** Macro for accessing the orthonormal matrix Q of the QT factorisation. */ +#define QQ( I,J ) _THIS->Q[(I)+nV*(J)] + +/** Macro for accessing the triangular matrix T of the QT factorisation. */ +#define TT( I,J ) _THIS->T[(I)*nVC_min+(J)] + + + +BEGIN_NAMESPACE_QPOASES + + +/** Defines real_t for facilitating switching between double and float. */ + +#ifndef USE_ACADOS_TYPES +#ifndef __CODE_GENERATION__ + + #ifdef __USE_SINGLE_PRECISION__ + typedef float real_t; + #else + typedef double real_t; + #endif /* __USE_SINGLE_PRECISION__ */ + +#endif /* __CODE_GENERATION__ */ +#endif /* USE_ACADOS_TYPES */ + +/** Summarises all possible logical values. */ +typedef enum +{ + BT_FALSE = 0, /**< Logical value for "false". */ + BT_TRUE /**< Logical value for "true". */ +} BooleanType; + + +/** Summarises all possible print levels. Print levels are used to describe + * the desired amount of output during runtime of qpOASES. */ +typedef enum +{ + PL_DEBUG_ITER = -2, /**< Full tabular debugging output. */ + PL_TABULAR, /**< Tabular output. */ + PL_NONE, /**< No output. */ + PL_LOW, /**< Print error messages only. */ + PL_MEDIUM, /**< Print error and warning messages as well as concise info messages. */ + PL_HIGH /**< Print all messages with full details. */ +} PrintLevel; + + +/** Defines visibility status of a message. */ +typedef enum +{ + VS_HIDDEN, /**< Message not visible. */ + VS_VISIBLE /**< Message visible. */ +} VisibilityStatus; + + +/** Summarises all possible states of the (S)QProblem(B) object during the +solution process of a QP sequence. */ +typedef enum +{ + QPS_NOTINITIALISED, /**< QProblem object is freshly instantiated or reset. */ + QPS_PREPARINGAUXILIARYQP, /**< An auxiliary problem is currently setup, either at the very beginning + * via an initial homotopy or after changing the QP matrices. */ + QPS_AUXILIARYQPSOLVED, /**< An auxilary problem was solved, either at the very beginning + * via an initial homotopy or after changing the QP matrices. */ + QPS_PERFORMINGHOMOTOPY, /**< A homotopy according to the main idea of the online active + * set strategy is performed. */ + QPS_HOMOTOPYQPSOLVED, /**< An intermediate QP along the homotopy path was solved. */ + QPS_SOLVED /**< The solution of the actual QP was found. */ +} QProblemStatus; + + +/** Summarises all possible types of the QP's Hessian matrix. */ +typedef enum +{ + HST_ZERO, /**< Hessian is zero matrix (i.e. LP formulation). */ + HST_IDENTITY, /**< Hessian is identity matrix. */ + HST_POSDEF, /**< Hessian is (strictly) positive definite. */ + HST_POSDEF_NULLSPACE, /**< Hessian is positive definite on null space of active bounds/constraints. */ + HST_SEMIDEF, /**< Hessian is positive semi-definite. */ + HST_INDEF, /**< Hessian is indefinite. */ + HST_UNKNOWN /**< Hessian type is unknown. */ +} HessianType; + + +/** Summarises all possible types of bounds and constraints. */ +typedef enum +{ + ST_UNBOUNDED, /**< Bound/constraint is unbounded. */ + ST_BOUNDED, /**< Bound/constraint is bounded but not fixed. */ + ST_EQUALITY, /**< Bound/constraint is fixed (implicit equality bound/constraint). */ + ST_DISABLED, /**< Bound/constraint is disabled (i.e. ignored when solving QP). */ + ST_UNKNOWN /**< Type of bound/constraint unknown. */ +} SubjectToType; + + +/** Summarises all possible states of bounds and constraints. */ +typedef enum +{ + ST_LOWER = -1, /**< Bound/constraint is at its lower bound. */ + ST_INACTIVE, /**< Bound/constraint is inactive. */ + ST_UPPER, /**< Bound/constraint is at its upper bound. */ + ST_INFEASIBLE_LOWER, /**< (to be documented) */ + ST_INFEASIBLE_UPPER, /**< (to be documented) */ + ST_UNDEFINED /**< Status of bound/constraint undefined. */ +} SubjectToStatus; + + +/** + * \brief Stores internal information for tabular (debugging) output. + * + * Struct storing internal information for tabular (debugging) output + * when using the (S)QProblem(B) objects. + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2013-2015 + */ +typedef struct +{ + int idxAddB; /**< Index of bound that has been added to working set. */ + int idxRemB; /**< Index of bound that has been removed from working set. */ + int idxAddC; /**< Index of constraint that has been added to working set. */ + int idxRemC; /**< Index of constraint that has been removed from working set. */ + int excAddB; /**< Flag indicating whether a bound has been added to working set to keep a regular projected Hessian. */ + int excRemB; /**< Flag indicating whether a bound has been removed from working set to keep a regular projected Hessian. */ + int excAddC; /**< Flag indicating whether a constraint has been added to working set to keep a regular projected Hessian. */ + int excRemC; /**< Flag indicating whether a constraint has been removed from working set to keep a regular projected Hessian. */ +} TabularOutput; + +/** + * \brief Struct containing the variable header for mat file. + * + * Struct storing the header of a variable to be stored in + * Matlab's binary format (using the outdated Level 4 variant + * for simplictiy). + * + * Note, this code snippet has been inspired from the document + * "Matlab(R) MAT-file Format, R2013b" by MathWorks + * + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2013-2015 + */ +typedef struct +{ + long numericFormat; /**< Flag indicating numerical format. */ + long nRows; /**< Number of rows. */ + long nCols; /**< Number of rows. */ + long imaginaryPart; /**< (to be documented) */ + long nCharName; /**< Number of character in name. */ +} MatMatrixHeader; + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_TYPES_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/UnitTesting.h b/phonelibs/acados/include/qpOASES_e/UnitTesting.h new file mode 100644 index 0000000000..3fb31129a5 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/UnitTesting.h @@ -0,0 +1,79 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/UnitTesting.h + * \author Hans Joachim Ferreau + * \version 3.1embedded + * \date 2014-2015 + * + * Definition of auxiliary functions/macros for unit testing. + */ + + +#ifndef QPOASES_UNIT_TESTING_H +#define QPOASES_UNIT_TESTING_H + + +#ifndef TEST_TOL_FACTOR +#define TEST_TOL_FACTOR 1 +#endif + + +/** Return value for tests that passed. */ +#define TEST_PASSED 0 + +/** Return value for tests that failed. */ +#define TEST_FAILED 1 + +/** Return value for tests that could not run due to missing external data. */ +#define TEST_DATA_NOT_FOUND 99 + + +/** Macro verifying that two numerical values are equal in order to pass unit test. */ +#define QPOASES_TEST_FOR_EQUAL( x,y ) if ( REFER_NAMESPACE_QPOASES isEqual( (x),(y) ) == BT_FALSE ) { return TEST_FAILED; } + +/** Macro verifying that two numerical values are close to each other in order to pass unit test. */ +#define QPOASES_TEST_FOR_NEAR( x,y ) if ( REFER_NAMESPACE_QPOASES getAbs((x)-(y)) / REFER_NAMESPACE_QPOASES getMax( 1.0,REFER_NAMESPACE_QPOASES getAbs(x) ) >= 1e-10 ) { return TEST_FAILED; } + +/** Macro verifying that first quantity is lower or equal than second one in order to pass unit test. */ +#define QPOASES_TEST_FOR_TOL( x,tol ) if ( (x) > (tol)*(TEST_TOL_FACTOR) ) { return TEST_FAILED; } + +/** Macro verifying that a logical expression holds in order to pass unit test. */ +#define QPOASES_TEST_FOR_TRUE( x ) if ( (x) == 0 ) { return TEST_FAILED; } + + + +BEGIN_NAMESPACE_QPOASES + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_UNIT_TESTING_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/Utils.h b/phonelibs/acados/include/qpOASES_e/Utils.h new file mode 100644 index 0000000000..75e45a56a0 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/Utils.h @@ -0,0 +1,500 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/Utils.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of some utilities for working with the different QProblem classes. + */ + + +#ifndef QPOASES_UTILS_H +#define QPOASES_UTILS_H + +#include + + +BEGIN_NAMESPACE_QPOASES + + +/** Prints a vector. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printV( const real_t* const v, /**< Vector to be printed. */ + int n /**< Length of vector. */ + ); + +/** Prints a permuted vector. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printPV( const real_t* const v, /**< Vector to be printed. */ + int n, /**< Length of vector. */ + const int* const V_idx /**< Pemutation vector. */ + ); + +/** Prints a named vector. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printNV( const real_t* const v, /**< Vector to be printed. */ + int n, /**< Length of vector. */ + const char* name /** Name of vector. */ + ); + +/** Prints a matrix. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printM( const real_t* const M, /**< Matrix to be printed. */ + int nrow, /**< Row number of matrix. */ + int ncol /**< Column number of matrix. */ + ); + +/** Prints a permuted matrix. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printPM( const real_t* const M, /**< Matrix to be printed. */ + int nrow, /**< Row number of matrix. */ + int ncol , /**< Column number of matrix. */ + const int* const ROW_idx, /**< Row pemutation vector. */ + const int* const COL_idx /**< Column pemutation vector. */ + ); + +/** Prints a named matrix. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printNM( const real_t* const M, /**< Matrix to be printed. */ + int nrow, /**< Row number of matrix. */ + int ncol, /**< Column number of matrix. */ + const char* name /** Name of matrix. */ + ); + +/** Prints an index array. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printI( const int* const _index, /**< Index array to be printed. */ + int n /**< Length of index array. */ + ); + +/** Prints a named index array. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printNI( const int* const _index, /**< Index array to be printed. */ + int n, /**< Length of index array. */ + const char* name /**< Name of index array. */ + ); + + +/** Prints a string to desired output target (useful also for MATLAB output!). + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_myPrintf( const char* s /**< String to be written. */ + ); + + +/** Prints qpOASES copyright notice. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_printCopyrightNotice( ); + + +/** Reads a real_t matrix from file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE */ +returnValue qpOASES_readFromFileM( real_t* data, /**< Matrix to be read from file. */ + int nrow, /**< Row number of matrix. */ + int ncol, /**< Column number of matrix. */ + const char* datafilename /**< Data file name. */ + ); + +/** Reads a real_t vector from file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE */ +returnValue qpOASES_readFromFileV( real_t* data, /**< Vector to be read from file. */ + int n, /**< Length of vector. */ + const char* datafilename /**< Data file name. */ + ); + +/** Reads an integer (column) vector from file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE \n + RET_UNABLE_TO_READ_FILE */ +returnValue qpOASES_readFromFileI( int* data, /**< Vector to be read from file. */ + int n, /**< Length of vector. */ + const char* datafilename /**< Data file name. */ + ); + + +/** Writes a real_t matrix into a file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE */ +returnValue qpOASES_writeIntoFileM( const real_t* const data, /**< Matrix to be written into file. */ + int nrow, /**< Row number of matrix. */ + int ncol, /**< Column number of matrix. */ + const char* datafilename, /**< Data file name. */ + BooleanType append /**< Indicates if data shall be appended if the file already exists (otherwise it is overwritten). */ + ); + +/** Writes a real_t vector into a file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE */ +returnValue qpOASES_writeIntoFileV( const real_t* const data, /**< Vector to be written into file. */ + int n, /**< Length of vector. */ + const char* datafilename, /**< Data file name. */ + BooleanType append /**< Indicates if data shall be appended if the file already exists (otherwise it is overwritten). */ + ); + +/** Writes an integer (column) vector into a file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_OPEN_FILE */ +returnValue qpOASES_writeIntoFileI( const int* const integer, /**< Integer vector to be written into file. */ + int n, /**< Length of vector. */ + const char* datafilename, /**< Data file name. */ + BooleanType append /**< Indicates if integer shall be appended if the file already exists (otherwise it is overwritten). */ + ); + +/** Writes a real_t matrix/vector into a Matlab binary file. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS + RET_UNABLE_TO_WRITE_FILE */ +returnValue qpOASES_writeIntoMatFile( FILE* const matFile, /**< Pointer to Matlab binary file. */ + const real_t* const data, /**< Data to be written into file. */ + int nRows, /**< Row number of matrix. */ + int nCols, /**< Column number of matrix. */ + const char* name /**< Matlab name of matrix/vector to be stored. */ + ); + +/** Writes in integer matrix/vector into a Matlab binary file. + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS + RET_UNABLE_TO_WRITE_FILE */ +returnValue qpOASES_writeIntoMatFileI( FILE* const matFile, /**< Pointer to Matlab binary file. */ + const int* const data, /**< Data to be written into file. */ + int nRows, /**< Row number of matrix. */ + int nCols, /**< Column number of matrix. */ + const char* name /**< Matlab name of matrix/vector to be stored. */ + ); + + +/** Returns the current system time. + * \return current system time */ +real_t qpOASES_getCPUtime( ); + + +/** Returns the N-norm of a vector. + * \return >= 0.0: successful */ +real_t qpOASES_getNorm( const real_t* const v, /**< Vector. */ + int n, /**< Vector's dimension. */ + int type /**< Norm type, 1: one-norm, 2: Euclidean norm. */ + ); + +/** Tests whether two real-valued arguments are (numerically) equal. + * \return BT_TRUE: arguments differ not more than TOL \n + BT_FALSE: arguments differ more than TOL */ +static inline BooleanType qpOASES_isEqual( real_t x, /**< First real number. */ + real_t y, /**< Second real number. */ + real_t TOL /**< Tolerance for comparison. */ + ); + + +/** Tests whether a real-valued argument is (numerically) zero. + * \return BT_TRUE: argument differs from 0.0 not more than TOL \n + BT_FALSE: argument differs from 0.0 more than TOL */ +static inline BooleanType qpOASES_isZero( real_t x, /**< Real number. */ + real_t TOL /**< Tolerance for comparison. */ + ); + + +/** Returns sign of a real-valued argument. + * \return 1.0: argument is non-negative \n + -1.0: argument is negative */ +static inline real_t qpOASES_getSign( real_t arg /**< real-valued argument whose sign is to be determined. */ + ); + + +/** Returns maximum of two integers. + * \return Maximum of two integers */ +static inline int qpOASES_getMaxI( int x, /**< First integer. */ + int y /**< Second integer. */ + ); + + +/** Returns minimum of two integers. + * \return Minimum of two integers */ +static inline int qpOASES_getMinI( int x, /**< First integer. */ + int y /**< Second integer. */ + ); + + +/** Returns maximum of two reals. + * \return Maximum of two reals */ +static inline real_t qpOASES_getMax( real_t x, /**< First real number. */ + real_t y /**< Second real number. */ + ); + + +/** Returns minimum of two reals. + * \return Minimum of two reals */ +static inline real_t qpOASES_getMin( real_t x, /**< First real number. */ + real_t y /**< Second real number. */ + ); + + +/** Returns the absolute value of a real_t-valued argument. + * \return Absolute value of a real_t-valued argument */ +static inline real_t qpOASES_getAbs( real_t x /**< real_t-valued argument. */ + ); + +/** Returns the square-root of a real number. + * \return Square-root of a real number */ +static inline real_t qpOASES_getSqrt( real_t x /**< Non-negative real number. */ + ); + + +/** Computes the maximum violation of the KKT optimality conditions + * of given iterate for given QP data. */ +returnValue qpOASES_getKktViolation( int nV, /**< Number of variables. */ + int nC, /**< Number of constraints. */ + const real_t* const H, /**< Hessian matrix (may be NULL if Hessian is zero or identity matrix). */ + const real_t* const g, /**< Gradient vector. */ + const real_t* const A, /**< Constraint matrix. */ + const real_t* const lb, /**< Lower bound vector (on variables). */ + const real_t* const ub, /**< Upper bound vector (on variables). */ + const real_t* const lbA, /**< Lower constraints' bound vector. */ + const real_t* const ubA, /**< Upper constraints' bound vector. */ + const real_t* const x, /**< Primal trial vector. */ + const real_t* const y, /**< Dual trial vector. */ + real_t* const _stat, /**< Output: maximum value of stationarity condition residual. */ + real_t* const feas, /**< Output: maximum value of primal feasibility violation. */ + real_t* const cmpl /**< Output: maximum value of complementarity residual. */ + ); + +/** Computes the maximum violation of the KKT optimality conditions + * of given iterate for given QP data. */ +returnValue qpOASES_getKktViolationSB( int nV, /**< Number of variables. */ + const real_t* const H, /**< Hessian matrix (may be NULL if Hessian is zero or identity matrix). */ + const real_t* const g, /**< Gradient vector. */ + const real_t* const lb, /**< Lower bound vector (on variables). */ + const real_t* const ub, /**< Upper bound vector (on variables). */ + const real_t* const x, /**< Primal trial vector. */ + const real_t* const y, /**< Dual trial vector. */ + real_t* const _stat, /**< Output: maximum value of stationarity condition residual. */ + real_t* const feas, /**< Output: maximum value of primal feasibility violation. */ + real_t* const cmpl /**< Output: maximum value of complementarity residual. */ + ); + + +/** Writes a value of BooleanType into a string. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_convertBooleanTypeToString( BooleanType value, /**< Value to be written. */ + char* const string /**< Input: String of sufficient size, \n + Output: String containing value. */ + ); + +/** Writes a value of SubjectToStatus into a string. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_convertSubjectToStatusToString( SubjectToStatus value, /**< Value to be written. */ + char* const string /**< Input: String of sufficient size, \n + Output: String containing value. */ + ); + +/** Writes a value of PrintLevel into a string. + * \return SUCCESSFUL_RETURN */ +returnValue qpOASES_convertPrintLevelToString( PrintLevel value, /**< Value to be written. */ + char* const string /**< Input: String of sufficient size, \n + Output: String containing value. */ + ); + + +/** Converts a returnValue from an QProblem(B) object into a more + * simple status flag. + * + * \return 0: QP problem solved + * 1: QP could not be solved within given number of iterations + * -1: QP could not be solved due to an internal error + * -2: QP is infeasible (and thus could not be solved) + * -3: QP is unbounded (and thus could not be solved) + */ +int qpOASES_getSimpleStatus( returnValue returnvalue, /**< ReturnValue to be analysed. */ + BooleanType doPrintStatus /**< Flag indicating whether simple status shall be printed to screen. */ + ); + +/** Normalises QP constraints. + * \return SUCCESSFUL_RETURN \n + * RET_INVALID_ARGUMENTS */ +returnValue qpOASES_normaliseConstraints( int nV, /**< Number of variables. */ + int nC, /**< Number of constraints. */ + real_t* A, /**< Input: Constraint matrix, \n + Output: Normalised constraint matrix. */ + real_t* lbA, /**< Input: Constraints' lower bound vector, \n + Output: Normalised constraints' lower bound vector. */ + real_t* ubA, /**< Input: Constraints' upper bound vector, \n + Output: Normalised constraints' upper bound vector. */ + int type /**< Norm type, 1: one-norm, 2: Euclidean norm. */ + ); + + +#ifdef __DEBUG__ +/** Writes matrix with given dimension into specified file. */ +void gdb_printmat( const char *fname, /**< File name. */ + real_t *M, /**< Matrix to be written. */ + int n, /**< Number of rows. */ + int m, /**< Number of columns. */ + int ldim /**< Leading dimension. */ + ); +#endif /* __DEBUG__ */ + + +#if defined(__DSPACE__) || defined(__XPCTARGET__) +void __cxa_pure_virtual( void ); +#endif /* __DSPACE__ || __XPCTARGET__*/ + + + +/* + * i s E q u a l + */ +static inline BooleanType qpOASES_isEqual( real_t x, + real_t y, + real_t TOL + ) +{ + if ( qpOASES_getAbs(x-y) <= TOL ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * i s Z e r o + */ +static inline BooleanType qpOASES_isZero( real_t x, + real_t TOL + ) +{ + if ( qpOASES_getAbs(x) <= TOL ) + return BT_TRUE; + else + return BT_FALSE; +} + + +/* + * g e t S i g n + */ +static inline real_t qpOASES_getSign( real_t arg + ) +{ + if ( arg >= 0.0 ) + return 1.0; + else + return -1.0; +} + + + +/* + * g e t M a x + */ +static inline int qpOASES_getMaxI( int x, + int y + ) +{ + return (yx) ? x : y; +} + + +/* + * g e t M a x + */ +static inline real_t qpOASES_getMax( real_t x, + real_t y + ) +{ + #ifdef __NO_FMATH__ + return (yx) ? x : y; + #else + return (y>x) ? x : y; + /*return fmin(x,y); seems to be slower */ + #endif +} + + +/* + * g e t A b s + */ +static inline real_t qpOASES_getAbs( real_t x + ) +{ + #ifdef __NO_FMATH__ + return (x>=0.0) ? x : -x; + #else + return fabs(x); + #endif +} + +/* + * g e t S q r t + */ +static inline real_t qpOASES_getSqrt( real_t x + ) +{ + #ifdef __NO_FMATH__ + return sqrt(x); /* put your custom sqrt-replacement here */ + #else + return sqrt(x); + #endif +} + + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_UTILS_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/include/qpOASES_e/extras/OQPinterface.h b/phonelibs/acados/include/qpOASES_e/extras/OQPinterface.h new file mode 100644 index 0000000000..da59ea9db6 --- /dev/null +++ b/phonelibs/acados/include/qpOASES_e/extras/OQPinterface.h @@ -0,0 +1,227 @@ +/* + * This file is part of qpOASES. + * + * qpOASES -- An Implementation of the Online Active Set Strategy. + * Copyright (C) 2007-2015 by Hans Joachim Ferreau, Andreas Potschka, + * Christian Kirches et al. All rights reserved. + * + * qpOASES is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * qpOASES is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with qpOASES; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +/** + * \file include/qpOASES_e/extras/OQPinterface.h + * \author Hans Joachim Ferreau, Andreas Potschka, Christian Kirches + * \version 3.1embedded + * \date 2007-2015 + * + * Declaration of an interface comprising several utility functions + * for solving test problems from the Online QP Benchmark Collection + * (This collection is no longer maintained, see + * http://www.qpOASES.org/onlineQP for a backup). + */ + + +#ifndef QPOASES_OQPINTERFACE_H +#define QPOASES_OQPINTERFACE_H + + +#include +#include + +#include +#include + + +BEGIN_NAMESPACE_QPOASES + +typedef struct { + QProblem *qp; + + DenseMatrix *H; + DenseMatrix *A; + + real_t *x; + real_t *y; +} OQPbenchmark_ws; + +int OQPbenchmark_ws_calculateMemorySize( unsigned int nV, unsigned int nC ); + +char *OQPbenchmark_ws_assignMemory( unsigned int nV, unsigned int nC, OQPbenchmark_ws **mem, void *raw_memory ); + +OQPbenchmark_ws *OQPbenchmark_ws_createMemory( unsigned int nV, unsigned int nC ); + +typedef struct { + QProblemB *qp; + + DenseMatrix *H; + + real_t *x; + real_t *y; +} OQPbenchmarkB_ws; + +int OQPbenchmarkB_ws_calculateMemorySize( unsigned int nV ); + +char *OQPbenchmarkB_ws_assignMemory( unsigned int nV, OQPbenchmarkB_ws **mem, void *raw_memory ); + +OQPbenchmarkB_ws *OQPbenchmarkB_ws_createMemory( unsigned int nV ); + +typedef struct { + OQPbenchmark_ws *qp_ws; + OQPbenchmarkB_ws *qpB_ws; + + real_t *H; + real_t *g; + real_t *A; + real_t *lb; + real_t *ub; + real_t *lbA; + real_t *ubA; +} OQPinterface_ws; + +int OQPinterface_ws_calculateMemorySize( unsigned int nV, unsigned int nC, unsigned int nQP ); + +char *OQPinterface_ws_assignMemory( unsigned int nV, unsigned int nC, unsigned int nQP, OQPinterface_ws **mem, void *raw_memory ); + +OQPinterface_ws *OQPinterface_ws_createMemory( unsigned int nV, unsigned int nC, unsigned int nQP ); + +/** Reads dimensions of an Online QP Benchmark problem from file. + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_READ_FILE \n + RET_FILEDATA_INCONSISTENT */ +returnValue readOQPdimensions( const char* path, /**< Full path of the data files (without trailing slash!). */ + int* nQP, /**< Output: Number of QPs. */ + int* nV, /**< Output: Number of variables. */ + int* nC, /**< Output: Number of constraints. */ + int* nEC /**< Output: Number of equality constraints. */ + ); + +/** Reads data of an Online QP Benchmark problem from file. + * This function allocates the required memory for all data; after successfully calling it, + * you have to free this memory yourself! + * \return SUCCESSFUL_RETURN \n + RET_INVALID_ARGUMENTS \n + RET_UNABLE_TO_READ_FILE \n + RET_FILEDATA_INCONSISTENT */ +returnValue readOQPdata( const char* path, /**< Full path of the data files (without trailing slash!). */ + int* nQP, /**< Output: Number of QPs. */ + int* nV, /**< Output: Number of variables. */ + int* nC, /**< Output: Number of constraints. */ + int* nEC, /**< Output: Number of equality constraints. */ + real_t* H, /**< Output: Hessian matrix. */ + real_t* g, /**< Output: Sequence of gradient vectors. */ + real_t* A, /**< Output: Constraint matrix. */ + real_t* lb, /**< Output: Sequence of lower bound vectors (on variables). */ + real_t* ub, /**< Output: Sequence of upper bound vectors (on variables). */ + real_t* lbA, /**< Output: Sequence of lower constraints' bound vectors. */ + real_t* ubA, /**< Output: Sequence of upper constraints' bound vectors. */ + real_t* xOpt, /**< Output: Sequence of primal solution vectors + * (not read if a null pointer is passed). */ + real_t* yOpt, /**< Output: Sequence of dual solution vectors + * (not read if a null pointer is passed). */ + real_t* objOpt /**< Output: Sequence of optimal objective function values + * (not read if a null pointer is passed). */ + ); + + +/** Solves an Online QP Benchmark problem as specified by the arguments. + * The maximum deviations from the given optimal solution as well as the + * maximum CPU time to solve each QP are determined. + * \return SUCCESSFUL_RETURN \n + RET_BENCHMARK_ABORTED */ +returnValue solveOQPbenchmark( int nQP, /**< Number of QPs. */ + int nV, /**< Number of variables. */ + int nC, /**< Number of constraints. */ + int nEC, /**< Number of equality constraints. */ + real_t* _H, /**< Hessian matrix. */ + const real_t* const g, /**< Sequence of gradient vectors. */ + real_t* _A, /**< Constraint matrix. */ + const real_t* const lb, /**< Sequence of lower bound vectors (on variables). */ + const real_t* const ub, /**< Sequence of upper bound vectors (on variables). */ + const real_t* const lbA, /**< Sequence of lower constraints' bound vectors. */ + const real_t* const ubA, /**< Sequence of upper constraints' bound vectors. */ + BooleanType isSparse, /**< Shall convert matrices to sparse format before solution? */ + BooleanType useHotstarts, /**< Shall QP solution be hotstarted? */ + const Options* options, /**< QP solver options to be used while solving benchmark problems. */ + int maxAllowedNWSR, /**< Maximum number of working set recalculations to be performed. */ + real_t* maxNWSR, /**< Output: Maximum number of performed working set recalculations. */ + real_t* avgNWSR, /**< Output: Average number of performed working set recalculations. */ + real_t* maxCPUtime, /**< Output: Maximum CPU time required for solving each QP. */ + real_t* avgCPUtime, /**< Output: Average CPU time required for solving each QP. */ + real_t* maxStationarity, /**< Output: Maximum residual of stationarity condition. */ + real_t* maxFeasibility, /**< Output: Maximum residual of primal feasibility condition. */ + real_t* maxComplementarity, /**< Output: Maximum residual of complementarity condition. */ + OQPbenchmark_ws *work /**< Workspace. */ + ); + +/** Solves an Online QP Benchmark problem (without constraints) as specified + * by the arguments. The maximum deviations from the given optimal solution + * as well as the maximum CPU time to solve each QP are determined. + * \return SUCCESSFUL_RETURN \n + RET_BENCHMARK_ABORTED */ +returnValue solveOQPbenchmarkB( int nQP, /**< Number of QPs. */ + int nV, /**< Number of variables. */ + real_t* _H, /**< Hessian matrix. */ + const real_t* const g, /**< Sequence of gradient vectors. */ + const real_t* const lb, /**< Sequence of lower bound vectors (on variables). */ + const real_t* const ub, /**< Sequence of upper bound vectors (on variables). */ + BooleanType isSparse, /**< Shall convert matrices to sparse format before solution? */ + BooleanType useHotstarts, /**< Shall QP solution be hotstarted? */ + const Options* options, /**< QP solver options to be used while solving benchmark problems. */ + int maxAllowedNWSR, /**< Maximum number of working set recalculations to be performed. */ + real_t* maxNWSR, /**< Output: Maximum number of performed working set recalculations. */ + real_t* avgNWSR, /**< Output: Average number of performed working set recalculations. */ + real_t* maxCPUtime, /**< Output: Maximum CPU time required for solving each QP. */ + real_t* avgCPUtime, /**< Output: Average CPU time required for solving each QP. */ + real_t* maxStationarity, /**< Output: Maximum residual of stationarity condition. */ + real_t* maxFeasibility, /**< Output: Maximum residual of primal feasibility condition. */ + real_t* maxComplementarity, /**< Output: Maximum residual of complementarity condition. */ + OQPbenchmarkB_ws *work /**< Workspace. */ + ); + + +/** Runs an Online QP Benchmark problem and determines the maximum + * violation of the KKT optimality conditions as well as the + * maximum and average number of iterations and CPU time to solve + * each QP. + * + * \return SUCCESSFUL_RETURN \n + RET_UNABLE_TO_READ_BENCHMARK \n + RET_BENCHMARK_ABORTED */ +returnValue runOQPbenchmark( const char* path, /**< Full path of the benchmark files (without trailing slash!). */ + BooleanType isSparse, /**< Shall convert matrices to sparse format before solution? */ + BooleanType useHotstarts, /**< Shall QP solution be hotstarted? */ + const Options* options, /**< QP solver options to be used while solving benchmark problems. */ + int maxAllowedNWSR, /**< Maximum number of working set recalculations to be performed. */ + real_t* maxNWSR, /**< Output: Maximum number of performed working set recalculations. */ + real_t* avgNWSR, /**< Output: Average number of performed working set recalculations. */ + real_t* maxCPUtime, /**< Output: Maximum CPU time required for solving each QP. */ + real_t* avgCPUtime, /**< Output: Average CPU time required for solving each QP. */ + real_t* maxStationarity, /**< Output: Maximum residual of stationarity condition. */ + real_t* maxFeasibility, /**< Output: Maximum residual of primal feasibility condition. */ + real_t* maxComplementarity, /**< Output: Maximum residual of complementarity condition. */ + OQPinterface_ws* work /**< Workspace. */ + ); + +END_NAMESPACE_QPOASES + + +#endif /* QPOASES_OQPINTERFACE_H */ + + +/* + * end of file + */ diff --git a/phonelibs/acados/x86_64/lib/libacados.so b/phonelibs/acados/x86_64/lib/libacados.so new file mode 100644 index 0000000000000000000000000000000000000000..30d3b71ef16f0defc3d6dfc03e8f9c4b46aae1b8 GIT binary patch literal 521704 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 za (la5GW+{2EGs$duOHC)uGT5K(#hSLN%Ad*6M* z+Hga0u=>X7wY9e!%c^g@VS4RN6Msby{7tW-KDY#=NAV$>AqOPYX(h|@Jiev;Nror3 zG-BQ5s9`buaZ`{oC;ZyeWDihdbm+#M{f=zQBok0qK}^yr{D&>RcIh#qK}aX&7E0 zf64km|JR*z!pZ@6QP82_X~?iO8C?~(H|+FzP4qII#RKkvQjadf-uN3*0P+;5Dtjdm zF2=*XhukBi;Rj7;wneH}@*OznyvD=JF%3}ZL7#qP+=mVy1YniXb&E|0QJC&gRnh7$ zz9Nw73RkKIj~#%Z$dniAzucVRcn7x+13Pc&BALvK6s?K+hjy{UKvNrtz0RGLCOqV7 z~2}6y^F0TAJlbFXQ-au;{hYN7$l5-j&HN->rle&$9wr7G!n{* zda;F)2QL|^YEA7gL8&C z9w1=WuCXm6QnVqq4pRkAe+@nBBjjCC{!>O;+2kM5VW)LBuPXy&r0AQvcerC+D$9Lo zz*#KF#@f+f+9Ac8Q=PKYOP|$kOk{DhiT2&!d=!mD;!ga>{d0fwL42j_$lS!KuSc@S z90rRX)3^63{iMMeyA4MC88orI^HZ$6=+I+r=;pC!CX`u+Lkd<19-ag_$2laVyYQili+> zWoUFZ*sC5)26RRgnB?M_L4LTl=L?w6-(Ww47p%%cXNbq|V2J0;K;=S07{F~n+#Y|^ z$*=_|2`k45XtXuuFvyCy|5^q$pwS(T%nUOdqduP>F+n2Jozc3ZBT_-29Yzr(6l+uZ zff~Xah-ajk-9=S}p&15uM@d(bio$9}l}3v41`p`q4r$w_^WHvXEHn;-(qQ>Ddq3W$ z?LH}`DJ%N2Zf(!emcRL86xSJX$&K78{U>`-gEkgQFLoAV;4EM<#crn_L(Sy)q!OiP zk@!yfH$g z4`x3L(cvcp4D$f?w_{DY+8E7Az_RC87Q_`N2&5 z7(gW!7c%jE@P|>$hqV+KcJ_PSEv-nkKvz+0T-tpCkE%pC$1wv z9^iYEo5x~^VFVN|6C+dKX2ghRl{v?aVKb7952Gn$vV`wY$Ou9@KS@>=_ zcY(9d?0&cvu~SWBOYd;x@S)%W5ZIuG+#AR(^^`f9T#ABTb)x}~W{u|79;WQ0&BG=D z&T#zK$oeru$5QlX5<~Fh*!7GmqnakKu0V&1b|+ewmKvD&DU4cAIOh?-BK1r2>2V&u z6w;S`(vFj$2xx^l* z3B^E)tmyfc?>xi%Zpg|$FU|KOOtm`qP_Dm=8ilcl0+A)Fmt+k=;4TVe6!lw!-S-G_ zImnwyOK>-n??y+*5N7ZV-7Pj!g$F{QVgybqiLVAj59EvHYgaq@NUK5^o{xIhIf&sy zfwF1J*-DRW#3mB{8H>B;K*m@=DvcrsIsWD|9GO>%20W=BN&9lgakQf{t{ny)+eC&B#6c=q{yy5k%*!a{!s zBVWuuC%!~sm3|u5T(T+L$OWg*mxLs*C@Jp6n_!}RB{*4HCJ0(|9sC{05dF05UeG{K z_ruh=C83~U90n23rN)tt;!?a60z&1epzD{yh)MT3Dnyxzs{7%!{F)O@E}~iREz#s6 z8WII^!qFf}8O=7l7Qza5BtF(lQ--DfrVKQD5&VV`@(6Xn3d3db%sUF!MR0{bYL_PB zhg!wUa4lFqR?2B+GJj}V@he7JMZi32Uv$4S07u4fe+H@^ea|CE!{fiJ6&@3Ff+#m$ zA?X?PD7{F>WiP_*gw>NrSEXTM#Q|#9)a-!1vCVy++M96Mv$dV4gvfyHl6&S9CdaX*oBpJ!%27a>rJJQ=h6L z@Y=so5j1W0Z54s;bGHB98Sns9TvRyDBw$75=Op}3lft)(=fU>!(~AFKSS0eO_prSx zu1Ot^WQ!y9Q^}YdRYjDD$*Rmp6t3WJ!#Yt~@jQ(7?Cqu_<^04}mU2D4Px2__9|nj& z3ATkUg@Hk#v$Yz_uh>1eBc1&l`405#mPhfkEHF1XJyaK6_VRnoN#R5dEl^M<68)h4 z=s!Vw&{HYihy7!u_ta_WZlP~zpQmC)>-m%ufzCy}+o1@H?Njc=?pz$wsd8SQnb)P> z59f8c5#UUs6Umt8(bTdd3Q&xQWVq#9JTz_0;&12*TPyw5^=Y0^h2w)pLxNXcjd(Gu zU7Y7`YeTm3;L{Kt-TI@llt=kNRPbXcQ?7kW14-^UZ`R<=2?ZPRuA31IPw*y662>c` z_!Tg_;V?VwGWIu7(-@)r4#VE9FE5P`JOILjgNNy^pvY0Mpekk{-U^u`74K*DoRwo` zOEYtLtA^jga^myYjkFrMv0ua+G`WwTe)uIDC@PK>&s~p8n?BVlByA~8E425(Qz!0V z^>F$-SdWFq-awNV)%(fHu&L}2ZHCWDoQ;}paVn}fRZqTTHKCqm32=CfB(scC%4MtX zPZ@)&z#VMsYR5Yf{nHvxRyM~pX4yBW?PXli7_*?(mwiL=hbmC%e7s;)Y2 z<$YVVFLSCr$F9HQpH(|8%Q_Wbu#tv)BOBD;uoGICXagQ{i^=~gw#%B||I zZl|i1o3niH$^`qg>vET`-FZ!NEvNp`%av5CH3EZh(LrV3_55f+De|SK)a+ zs)Hi<9C3u=Q@nPfjwVWQRs#d(i{O=FjkGsq=9NbEp2?(-oR0|q;7M ztomTo+iCv=@4LZVtGA4N%u_#s=d{-fVUVPKCxY>&mnV+t9)>ZswrBVK)Xs2!WekSZ zI4lia#`yGg!@jND|OD{7rkZk@va0u?Ek3%pa=p3jZDjyx**RuI6il z6AWlr1_-KXba|j|RNF7XtMeJ%cEj9KZhnNhEsWZnK2>U|2jdy)6QOvl&`})5nNUtW zC+qecI5M2}%KRzjg=jc)nUz_1spY#Eg~s6Zrx?8c3`UdTO!Y*Iit$4+csaJ1Hs+YS z%gr6-<_Ctk3eF;c{4~*?1Nx4va6aDRUC+rSCm$AVZDbS^XU;}tr_o8~2gSRchRtq% zZq7i40^gb;I-H-%(9_5ea>IF|3?U)pfwdgscq@`TFGJ~~()jRZGF|k_>d@Nwa2hA` zurtq9nS*q@AP6+sBnX-VIOfIvSu z1F^z0@i|w@b9W21D6}udhnVpki>rOECco;>J9jufX|LQJ2*Sr_Xd;-p3TdNI`M9^u zx;%q_DQXIYX&xT(z}E{qyKt~^41Q^6%*x6BE7v0H_!*u)>*$ky z=w*}o>8QSZp&I?#4u1Uz!Rg;?`~6pfsM;Js-?qHyXzl5k*5g$sDjf2-!rx3oJ@R*a z4;@a2Vai}HbsaH>sr(`2nF|U%Cs(Qq@DfQCrbnF$q={!#=F4A8^WZMq=8dzaa*^rU zzNqulD%hrmn^qA!Hfe-l4F-0HyY`2o7Jmn?zkx3U@gE`SNfl9ZR4c->G=ZjiTkTzf%pPLj7^t=EAjsI;-kSKON_8ECXD@NAxjnbtX0T zqO^JRyLf|x`10XyWPs*NlH)6P^9x2+p~e}d7|qjm2Q`L8^G>}y3_8)JtNk7=gVp3A zu7OZ6EE#H@bMk|Ji0Zb6CAc~$#+;R@ihKS8pOQ~#Uxq@m40r`bt2VBgN^ z^8Oj4=)KqmN)01OB)o@B%1z8E&=#0(wmUBce}IOJHm|wya@MVpv>_`QeU9B1(bJ(k z>_t&!p%FKAl`UG^YD$9%QK_heOMFy=vr67xlZKl$QIY~uhasN=qLTYP?R@rGgU_gy zNfl4O()J13`7&$F3qHC`>b^f9W;Da_^|ad=rH$hEXRXRS*erIjPs1GNm|pnZo1xvt zfVZ8WXQB(|>CVo(+VLf9zMV4XQY0Jjkv(Vjf)-;MzG|(b$=A?1Wwvnxdo1xYjP3MO zLybGUwf&Lf%r-P4^iyznf6RI)FLeFNmHYkIzzbwy`xJ(&MP2@;zau^P$;8D_dlb9h z*45ZT&i4p#;Zd|cFM)E4`a0=hnKf<+%K84R8=NxkGpx%3PB~F56xNHf4Z}L#<^ITk z*CIHOeRCzw4c%N(X1<5JNji2#_|9Z57Zqtaioq5eEozV9MtA)16pHbYxAqjW1oA}4 zdX^96p??1A+8?8n%D+b(q8r;Lw{WNk9e^FfKM&Rhw+DaqX6zI9jo2GeGl*6kt;v^k zf--wIx>TiHOk_O=D)drHg`A!yR1-R6q)pGUx76ag1zh*hPMfYVPlCH9x8s-&uA!!T z(9tFpQjZaJiyucT$3?PRZgs|yR~yLKphsKN=-MA_qQd{U+m7t~>I(i|5xOe$^Jq2l z0|@&`_8;*Y`U~KS;R>$ASQ?W67(s#n?Z1l-D4-ieCfK}gMmhDzXv};via@y|^GaGc z4(?cJiD?eP+ZiD%ryFk(Kbm>_F?4`o?f9)j*gG!5M0>EbNAbvtdlt|3qHI@Rg9xq< z8pxIHzw0u`UHXE&Sh);_gbBDIzgUsxvBNiFpz|a2(dkFMcH=HV_4{x5(W)rlzq-%g zbQ7&z^W~?h)@KdW6>54V94dYyZUaj0OTK^;ysqjtm*GuhrC)R@8AD*?Ov5x?{hxf z&;8u@{d}MM8G?k)LN@+@DECmdx*w=!&R$N1+XjoVGrt&XQ`r;UV#MWl>p@Snc5T~# zP7CXBPV*BvajKW`86Y~X*R~RcSSPZNHoVRHgDlV54CfEl->NkCwiw3YTQ9#hw~mHo zZ~Oq)R!0y zHf4V}Yu<|Y+7N1bT{)I3iT8X89I`eL9fOi5QnL662Lym{Nlz(aAg)wDH#=XRCPXQN zws{wEbXQWG%jWM|D6y`O62Dz2v97#C)V;n+y!TR>XC(jD&BsauR|rRt0`v}HSV_tvD0I z>{-qldNgMBSdZN5F{PO1>SEV*!6PJXBNKE&8B;JAZ*A3O76gU`u#bMK7Rou*Cbtc) z#^g%Nv{qh3>(qpBa?vE20@OsYZU=@J>DIYw{KwFAu*JbuE zE|gw89YMK%RiVTkr=z$;iVNC%ASF(m)H3|T`RBTU4tg9+h9*JbNO{L&*x8~935`2V znvrD1sm*PXEy0YWyi;vP+HkylO4+QOKGn^N7(MmHvftH`?+Rb#3H?qnhlgywA0GNV ztAPC|H=bmD+AmU5E@rV`QF?U(fjs4%%PPOHndkWCRNy%}vN^b4!n%*v<2h!2OAB7R zitm?PF+=xDF8e+gNTT)r*Z9(p@OsHt?7LS%`RgPPr&ma8@LP!En!&GGWYKz<)i9;? zxEB#VOP954Bb0i=9K}!s+h$eirIBK2r+-7bsK0qUEa&oeJ~B6tFfs6Y$$9=XEud>| z74agCqh2}?iZ>Z)9Tvm=95d!7z0t@CGm}Z&Zg$8%B{5wwb;Ka?GTZ!U)(^%}CrVBJ zi=3VbtKQLSr-x1cE$65=lnQ*6|LCw{Ieg!Ka6#Gjk2u;%lRAa85+S=-86 z>X}#NtK0^z!(3lLl2R}YpRWxaoUab)U1{$+6@6NvG^ytp4}b)#nJQHho9#dV;$Bt1 zJM;u8g*rSoWinb93p)NL1q(9rW(XN+pJ<~j#K<#(4^d{VKilYv2Uc+1q!DaRMkI7M zL-hDu8xSFA1fSfDx>@x*89A~BM_E=m(Sjx}yoZUH$waopG%P~T!LlvA za-`RJ7ypd7b5zBM|L_MPT|xeLLH=~aKk__rKg@ylbr4@+%HU>Jhd;mqFT0barW}=g z+Ao;q&tXgc+tY#)VJUaIG%pimzqpWQL%}gdRjdbQ2gbw*{hay+PNyC;Vv0NrBnVeo zoVR&L$18Z2{cAJr3W_W2#;`DrRh@X5eIx6rMtXe#nG;$u)Vn@XQIvhD2VGA&_~bO&Qlzr2q300 zx*j<|rTjN%IJXWCYUaCEFJ>ij>eSC5<3k`@0Yj;`BR3fl%O8la)wlhi$-B_lt(gw( zdRhtx#wi%BbsbJrZP(z$YZ|mJwXbGg^k0P|ihT6pphMY&767B~uOu#!Z4t%*-JDn5 z5(HQI{TSKsq8e9kwVz9%2884;tLgc-7m5RGDL{cMwewR=Gh^&er*Q7%T&t(A<%5u- zYYOk3puS;My+-H;)tSGecJCAown8`?Lxs~twVL!36l7>fF884FSQckzF?mc{Pk7I* zlMY-+Ay~?s4MUdeXU!<3^)t##{2I#$hiK1-_^*#zoR~-rkKr%@FK} zgM=rMbLXbKZ_8d7V8eFUf7p1MbVa_yUHi2A+(I;9g@AxHZnVereLTtJex7AZqxB4k=$ zmxV|&)Gb*Ob@<0pCWs97I+n6Fya$n1l)ur1Hu*2uhNoDZA}S^HZ7Ki#ESC~F*6o#7 zovHSz%UV`!ZdimVm^(Rqrnq*n{gkA|2jQ)mD5fg~vvVTFhS}H&*gNO2Zphc_U8xOJ zCN+kVMQdp#pkk2H(jZ}w#-hVgTnnHJAyXFq1hbMI>2SM#29h_H^wOR49Z9;BhyDF= zN8jv2$F{Q3NYSA>`G)*EHLx|AZGZcs1vD#}sZctJ@0QHA*5OLS5-8nX84sZoYSOoJ za>MzQZLc!0G2Q%QR<;?s8ue#wZ&)1yecc!s_S(O5&TxG8V9e}5?5rMx*lk~Qx3!~1 zHeJ%}U5MXCJ8UV2oX!T6c1Q_ny!>|6lcZkPU$gJKGUn6-6)^>p6@rRZl~g2T2VgWy zP*l^zU>4o@yP9ruKC@h3u?D^8ANdvWtHK?5)<84+H<|pO!^&vbQ1QHG?$~Eu@}Hdr zrSS>#)aP@oU46E@&p!m86W!+n!Dom2yo*m+of?&68e#Z-^luK*%yMZm_8B}^VpUum zgt$3YrC()#FqfY*WU_;<5P%-2SNsqi9Hn@k9dJ7?YxSylKY!5au7l0`c$PIKbqnHp z6EiIq-9Rv^L%zo{*=&6G1TZS79Y-Ta9Lvi7I&uI^4A|_-sDBOP)i(TtGyv(>vCFn) z-jSFBS&r3OM6sg@Qqm|-TrTMy4Hhh#isE8hmU65hdoKcy zogzpx;64km4Bffj5VGiY|7KQppaG_j`*StRRP)b^cx1E=}#ouLuV+s>c28CL_jHxlZ#nHSM?Zs|wI z5RQJJ&I#VvTESaf{%^=Hm%z`dnRPY}t)3$Ylw;Ra45zPE#w#d-arJwYgP|I-)`G&+ zI)G8JN^%t>gVNo2`MsK7i9%6#Uw)#*pwj)RAg^<(^8t3B*Rb4~JfdY{RCEK3cq4cD zjp^x)K{_I-tjXU7vE@c075XP3K$%A( zV^~iT&AbUs$(8Xs=vYvZs8d8RAyvzS6oCDVc_E7ikG3M=a54e>&)Iotuna}5-bq4y zq`ZOs@6@%Di#vv_h0pv&B_n9orMx|-BTnb0w#H^I)+8AOpIaNU-|1!3GvzN%QJE|_u zD@3uYzrdnK?H+~GzU9%>(Fkq4na6-K*wN(5Q4MM%>XpPfLGZe>d@D|;FxW8IF~4z79iq*@VzX z5aXSNM=fr+_{aHD(?1JNhiYkLqZ&zDLVBltH5zv|r-@X^^P4q+9Y@DbvvhXzz;tMN z3uX*fETEFF{gk5yOf?2+j7mZAW@D|3K41e#j28BYur)S*jg!yfeMTzQ=rv026U&cY zekBcRQiDuUaYA6XzEJ^Q$J79jdVs0mGyEd>UOUVf97N>gFgUAlwKWr5n0&k)PPCbw@~9iG>P zeTij%i5`*r5|N^3qmbc7A)on>@MrnQ=zL||$b`jWGr?aQfE;Fx?f!i%UboMdoKL1Q zj|%+RAjqxkvM*_X0?C9c=dGEyxp)dmp^^i)ULo$%nt8%(J@-xx6@jh`tW>7q`Br{si#@A#{jAlmH@(btp zqpLEb2lKOmMAbZiR9uABUsYN^&%x!-PZghBDNK+aK5%%L^86fbl%~&)k%OfL8`9lK4C!}3aWYBQoa1J1FhvilylJh1u2HWP`i(~|wAu37r8I>e zYD|Ddi+)P()ZxH%z*UTCf-~XC5LFf<_IH>Xz^|b#r z>x0lK$~ver;mX|{gudfiHpsO>mlKMKC!pUVf8x4tabg{BsaJV32YaKdal;i&R2+)> z!_WE9@+N%sYm@PBKmy6UX>RG4`&aWpguWun{kcID$2iOU^DXLKqE5`-_PJTK08EcR zj!-Sww#e&1Hb-Vh9Mu+iJF=;Q*Tntv##6H8J&Rwd$g3=4&PkprvfPxe-vC*MqwW){ zlT+r`l}HmJCNck&4knD@KnDY1DMwdKc?OYBXilIKBeaw;``uv%BrHhVw;vELgV^$ztW8$p;w1>KmPpxVe{ z#w2OsXp+SIzL^fRX5DRTVr%>piKYEVYo5_p=vFH8CjGka!_}fx$ zT#HOr8rL$Dm46jWw9oTiQ0d+4qpS}dK(EseT#(CgINY=!5XLr%sP-i9eG&Kvj$8iL zwRJ{%*n&$UXQvNaaLF$H(g!88$LsLT-hed|=PL9=%Nq z?&_P8*|9g_-hW$j?1q)C!=H-Xu(Y{q9r$siaC32c2S4!7JvAPi`TUUHB^x;UpY;a> zL?CF3^k6^~D4M+!V8E3mz!lttZtYtCLHym76|wFkw+jKlE3Uhup?va-=BW?oo z5FfrSwy>7yc(!^F5na8zkmSbXl+~7GVFFN2b}QmuoToo-ZkzIMOU2-BT*=>;dzCzEFwO9MQmZOLT`?H)!muut8(}7Wpc3yEwi~mXxshFnT*RfgZQxH>;JuMTskL( zQLD?NVm4&4``UOPV^pl?`-}k7!eo3?V=PcYc4Si`^F&MLS)Q0a+#U>B=Z|dD34HF8 zYGyh0zvsWrYRi52ff~JlQu?(-aCudj)0PM%ww;+B;yA?i*Wbd|?qPEy{v>5-zr^a$%t#xIQVI{!c=K9t0Q!e}1UzRb|8@{M~e{YGwYD=eKp z#0vX@7514?+357*p7W|Q!u2};Nm&`>T|V@Pam0`EGv;3Y+C7yyds^;(CD8+75c}u) zN-`^VlahSWB^gkjmNJunu zEN(AB}sZe(|6z?C94P6ne_Y|0&@YM|ON*~CB$ z7Mv_SZbab^V&BglV6nqi-I^aZ*BRG~*~oetw$fg7zpN1$RmTW?tB|kG2Ep=aU-pme zBX3=-`>U20KWxJ$7xJQnag9KE<@>D4|2tBY>O~qO+>KsC`b3?SM>$U8j@HIO#>c&j zup=#rBqHx+M(j$bqufNVy5c``$!&ctnUm?ee*jx~u7P-(@CsBEP)ku0{LA{Pp0{|I z4>H};ccw9YXIgIkJ(zq~6!JByztlLkzK;IZ_z3j&QDKu74p!S-96S4Z*m(!$r!do-eMRP#Ch0GhI3tkn(7$N_unlHCo3)(PR6Dx z98$f>k;q1C{UuO+?h%(h>V}^M&URNe{NGW!ACZnu&ATN(f<>M3-#w+T4s}m(BiPz^ zrX_u6+F*=zSZyH_OZxnw8oj8WC67i6zL@?BAjz2a1p&gfaluI9!ymrl_1rt}zp$<700w{3oX+m!7k5pmwRXc_**{5{*ItR3l9Uq3QaeUni12e)~4 z4@fw_c-O|rki7kO%Fs+aQ+-b|^O$Hag+sE}6OqkvUZ0G-Zhl0ru|8gegMn*hJu^Qm zNg&OsgH}7iTLOS>?lQI|GV6z|Ug9HoucwQ$!(CXtx86a+D6oVben}d+d%els%PTq$ z<&@SNu&^aFAY(%JA2E`cz#WDrhOfVT_vFo9_0=@;%IW>kaNgv+zllemz2vkZ_z&sz zzj6Z9RBC~px+s(fDd=j7zp~ZAJz9r9l74;TZ>BCnGBv$`x6?ACGb0vqAl^#<+lH@8 zzn*&p6{C;yC2j}415QFmOSQ54cEN#vXG18wf8z$NmZuAaT~(wJ$f+xTPba|*4gXc?f!ev5DSUd!p| zkcx?hZ8|hOucy`Lc79s@`4&H?rN3bf%_#9;^R{B&FX_8B7pIu8K{LOZA1K$LK}thw zXj9N2^Oh8La8h~tTJIA8$w?nsqxO$Gm@e2&cw9cR!Vk6dFkU}n_6N5I`m9J?JE0_@?=AEXXKMTnoqWCd9o3?Vfkb~%O`tD z6ddeUR%vr?Z<2+ILC1~8v`#xVWLgLCt8e<`oW-0%h4h&WhjG)Q{ICbFIbR+|JyRZ) zzKFY@I9b}*TCjI9$>()`R`Y!qKhIA8#OC*h-l%F4=1H^J(d#qb=$eYr11d(>R*W88 zF}f~;c<*RBPe0x$NUe|&1~N0frx8_~==ZZBxg-jWbL7(QjjC7M)QoAJLzM4He$Ljq zVhOE8h*$K&V4YatUEJR$FgFJ}w1Cy#sKE-rc$Fv2KYb%VLawGgy-^KH)T~5--QtgZ zB!U3@o9 zjTNJ;_FM8v$K;cKy(lTQH>vh?K3#o2-Pxt-hALfKK3ycA?x@mqBb4sIe7Zj`441|} zrRkcL?n494Dus-N042Y##By4ixg675Z~eLgFlWgVL|S^ zVM6f!QhY3zN3~nxAg1-o<*E(7glsH`VXdz>cM{$O)f~6B@!Y640pad&Hsm4P8Lp{Z zn~~(aDdD9xXw}K=`L+I=T1gnZ+VDt~$=S_=%6mxXOuBZE?u$NY^kCA+6{${%HW7pK z#Y$wf$}SZfdCN}yGXv`VJw>KvQbU2=1^tPS=Q!FRC9X4JFRpxj6@4n2`|kS11OWSB zQfvIjp(tncr3!7Q+$71^LlgC?WR^1V zw^M(W`d0qN4(hI7z~3YWrS8wt!elm8-qS618iO>n|tu$xr&QW^Te zS9p~-Ujy4%3k9@gw&25zA+&b-wPUcL0Q$+~h#=i8(j_t*IL#2fWe%=)$+ke+oGby> zI|10f{LhRbAqdQ7ZeAubuY|?@6xG3DUAQ5g$tAu04j>)&_=8F35-sz)Nu;dg%U*Vf zTKE3d75y+o9KsQXYh5&`_Y&%rixWpn1w_YS9H5HaijFIgUn3vpwzH1>QSc4Kp89OT z8ExYcA?JE`m_`2PIy}+7TqH3R&Rf9$vPgL{P49mH$t~PdQ<-JBqMfU;he|mY%cG{c z>!%S4Pk#M<8RkaAJ?DVRTOG%DRot6D7}(N2^e^rm$`p5ypt{AIALZbxsx5nOy=Was zYqOkXQSbnb6Q@sxXu^xyYVy=@@a1o3Bt++=oyLSgo&Yw6-QUBsu7=H_4RZXn+8hF{ zh>sUtw#Ua~0`II(_)iWodgpqHqxUHSaCxJeVSoeVg=KD=2v;Su3OZK0_fTn<5hZA| zfcU&oiqpxc{-@wY{G}xvINob?8rdp}y_--X@8A#V#RT-f%meig@P}KWoS@xU-^=eL z(V9j5Pe_$o9q)B`Cr3Hm>e(Ohf^O7>RF1=mSz2ndM3IhXDN0Q!T5YizD^jqsWcIpx za|alXFzvCC4pxa)OS8@8jrH1kRFaylg;vM6>UiHsUopMJuwYlZdQ9x02kk*VrPpAn zKIK=rbZje`Y%O=!%WIl)ZQs|;VC62|x^T%oKGw=y&6_P#t1q)DK4E&l>HXZlLe?bX zr+BV)Oy(K>)mma*%K>4ULnNjc2Cp-P_cNQ|RF$`LtCNI-tu%izO9AdV>}Epar^P3c zxDI^I(0bi2=xFKboj+PssdkBmLU!>LlI!HNW60`LGn=N{c&u`l4#8DZ{0M*kVGxxg z0nruHXMDIVd%0se)Cy$?q4#VeTMtB*MFrn^&HphSpdP~6lwB$~fpMJI5}EpX;}0&J zG0Moa3H%6{HSSZq+0A~7+qx~RCG)aNsTdMu6UT5YLywyM{exss*u_N?Fsyw9bDrSRmz@ntv<_FiC>g41#hK%E?OS#sllVckI@Vwa(c9@}4?hb!A z?Ag)G>~?r#c!!+fW5|+{f?o5-p-Ret#Tc>q<3*+N->)?C;)z6x)^_CgKgKvfJ&;#~ zfh_@4GTD|sR1Z^c)375?f}yoi z6B)f8?oUVW;H2zgm^|aFMSd&GDqOsbcjeubAZ*+;WwRChM7V-klh2S^|5MKO{AE$E zQ^E~QjoWe%ox@EvRX;0s5IeLIyiCeR)lhw~o!?l$#$|scc2EkZdz2?(QL)b{R$M7| z&^a(w7M4Px@9V63CN0Aq_8Bk44mzX0fz^xjgNbqs)7|NR{W!_;7t(9E-CSWDdaarZ zZC@tl90B7YMRmX`j9thgB?8Z@v4g(vcpCMKr->JdClOm2XBSRp@3n97KJl*DLAM3z z0$v6Wn-%0!*|CGJtMA}%Ds!9TodHv$MdELDMDc%^T3hz}flRY$b>_`HZB-7w!pP8g zO8iuf0t{mM=D4Wj50VJI~L-{Pfe~gc;bNI-iq;C*RGGBjHY5o6;kDOom z!mS4P`_?T20UAE?%RjP^Dc@I!!$+R$g8JQIL3IKezVJg!_2b(us8K;rxKuy-sRcDF zX#BrfstXmSt+MpL=GGweS1#?rs;+>KyvtQ}EH|kL?G4iIrUD05uCX@d@sU?*Oeoj- zbItl7?dOzeP~~klVApI8Lf_HQ45~chXZ-1Zjd)34_(=DV8C;Y@hC1Q%_6MOCBJ(yG zGVk6)4B2*mYi7BB2ftbQRj0!|qhj}G#?OeK>Fga^KY5^CfLEl{+GJ*xcPQr<9P7Ui zJ`s>y1GqnL@`%W~ipfnClbb6hCx9kSc|IBWgFnV9yX-(VJp6vWx(g6Wzv6J+hwG)v z{d=kM^fS3>tZL(vi+hyUpY#4ZkTB+e^&73e4RzfT<`V{AU2!Of-4*le{B;Mj_ljZR z1I+&}+>xD>Z~7XK{o=z!#*h8tVem@X2Uad(1pT>|g5+(H*8~Bbrf(K?bt3aFz8a4v zAGbzU`avy&0?crY|T6xpg#p!1kP8a6Cphr$WOpkq5K@) z@=#fL0<>Z>d*#iHl{56*gM!i*Ht|yj^?^;SB(TgB(-$`JSoFLi^$vN#=6^!I{iPtq zW^Zz_QU@DU2L^kG>ShbZiTT(n<}ix%49C~r_!0c7!lAV{#{dzUvrW4=2tKV$AhqX?fk$R&BaK(2)(0X`A_q*9iaC5d%^k}Vd);(u7g4(P*!v4}`- zEkhqZO(q8<9C?YF)hCc}^Pw&;q5uZ+p57$o)W&*N&=NF60}}onEkVvlli864AmMi? zJOmO7P~m`t+l2AW0|^W8UUmovBz$)nK*FCJRxPY|swq!<*UJb8!`#9d`U&b>D`q7=8E-5|xInwCmk3YS;{Y zxNjjJYKqPe7{e;<#Qyz#;DUHFv?zp@+Az+2-1a7v|0&(aIj+G>17z;z@nB%g;&LV=PMA z6zlH4i_zTyeP_D2?@T|pK@cSI^*Q-Lz{*tuNt_$xMZzhDBwno*@ZW(Xj{nDy#2%JO z?$Cb#J?u~-;S*{8;e9sWe-PY^Z8_iANu={M;z_rp@*Mxozfr_Lk~HjErT5j&sXCg!}`trwSea< zhaQe7f*$TgvqG*B^}Em@jN_?&v~OC8-B+ zQCV)w@(Qw8>rYmm-IQlkGgElV?EJvOJVWT={pbkBFKoZ~Z$b}$^tKVnmULMS{^^w7 zH}o)J_5IhOheuMC+e9*g9)6Kdv5(yFsc;`Tjb9kYyJ0^vRFvr#=*#8!L$HT)z#h(Q zMt@KIFaGdf1%LSRXCbMsk{jUv@9~GR^F`3b_``8RuZ8%-qku@w?fjz9Qo%2lg zm+~{6e!w76_bGWE=eNkh*6bX2`0vxp>g(K3B_=6R_jLrt8le(vH~!x6H~Jmo3-8L~ z3oiyWrI-m@n5ZldYtHL__)EjsRCsMvTI#=8iN#;U>tMkfcf+r&&#*nmDvXb zaaX0+IVQ64y7KgSG+{9W@hJ{!9LDJvmYyBJ-_`65ZZF7ZDuy8bAfM^nuc=YW^u*^1 z8;JFGXl`yHS-$L_l_wjKJ2{`MC7-OTJXurji}_@q%O^XdJXv$DBA+apPd2PPSt9oW zV3$x3liDE9NeR-gFKT-he!RhjKy$Aprb5WggR4 zDnkS!zH#GfW_n-hx+vY>+Dr+C?u!xUD0TDHkm%0%Oo8Z%Fo^$|r-AkPq(e%Rh8RS1 z@oX5sW%;DL7A2*21NdE^FY?xWx)-HV1u|2JRXi`B?(%%P`%2RpwlJPgcV<4_!qRjJ zjqoS)>5k5)n_85PUMDI>S&?rRk@TK3jHF-KCnV_~@ynezPb$=}W zFb69~P?(;tv5{Xzwnv9Q#Oo>;!yU}ff*&F<0y*Ti!o`CDAOgPOe)mTLD<&$wfN&q; z5W95niH20=?9!hC!YcLKp#8(p%#Laxke{Fjkq z04mlQ6u*?#^11Ryx{k4b|kUWs5`oJTG5IzABe*#JD9EjNZ70J=1K*X=wUSA4CJcx9~ zK*V1{ROl-^10ufPL9s0e%}}VP%jfX{Gn|ZMHCQ%5FP%o4X^>~1P9Bm zi&4Xr;3RjvKwX2qw!zjpFp215f)kvkhB;v3fFY~B3wSS$7YWphG<8hJZtwf+2%r1UrRR z|LO{OCw&?aQS>@Zv;gH6BN6fUG#A(uK?R^;83NI8iT@Pxuz_a&Bjn)#*9Z;HTH%|H z+5ap%y=UR1vU35qZc#s08qsg^zXNr0M$WSiM38vPWYL#{A3E$In9WW8MI@7R4+dyX zGmFBjTtz~r(V-88ICSUv2Wj1zudM)oSos4Imf#P2{V0h_@rUzB)F=LM+kXE={NXm1 zkH5@T|FA!@6PXkM=em2ZkXO#(7pl)$MD~e4+}ZlP^7V-tew%-Y6_*hhY)U9SNjz2k z<$VYZ@ll#3SYq$YTCc5+u>yzq^)uPqyd$9%fX3BA8H2=V_Rbt^a6{n5Q3~aJ(AGyk z;)Z%>4#j26RT_V$lpNDqBJTjz8Fm~+1vDQYYnWWkzzQ&~9RMf)0&3DbshL~u_)VJy zdfPu~ckz`}{W+q4Q`FiJB@O5daknRMr`DW&ND_nov)P22lL=V$#1O8d&bJG`BJ7Di zI>_IVh^$X!ZmX9o0DF07EDrJ@_>MNt9c|Ho$Wa&=#ix{p;C8_j#p(g#@T}H^X~kHX zf#*m72#|%@;4jUnQKMc-5+dfS;_NS|ngx6-5|!401y$VoAaI3nH@E5|He-1*^Yi)z z{7z=?TF)P)Q(tW8luivK-NVdK3OLTSL7GO=utKNtslRjWZ+HC*{MBfI#?e`-{-+9V z)nCHyPyqka@5SyXO!;2J6cs}+iPo9CN%kTdb-tMz)f*klC%r4CZQoZ8?;0EtoHQ2% zyE+dr(7=m^WIX2J(`}{4LFA}ITDhQo44*U3`+2>TdT(s~8JRczrJoc%E$6A@lcovQ zLvYfdGO&QZPO9j43LX-|BK&t)O#v_`^QPh92R=fYq&M(){2?3lfZqZyrYw4CYAM-; zpNvm%MCh+Yb%B~LlNY~V5F|f4|#*J2LHIc5z<8{_>jwm!G6heJkTXi z;;fALYplg!xx)TZFYv|8b6}Ei;s7iu1S3&Ts(m|D>tCjo?zPOao{GKgwFeEJt|prY z2+L@QGGP81%7!J=`?aea|7%niIio&B)@Jjh8tcLN9v}mFpB+b^qd4(c?L{NF`j^>8 zvEq^LNeCP=rm+*@`LFZx5&d@)(2GAJb0a4>23hH{w88WNCsto+%+D_jyh^fd$F zgCT>xdu#xw1<$w$V3*GE?ZwR_vxC2sd06j(Z_Um)q+jMa|HmX0=u){gGr&L_z+BoL z1OSTx=5lQPVE=oydeuf724);Qne?X5un(^}Dx+M+Xs;VT`z3hG3lx0ia&f~Q@Rq~w zo@jVWU0QC|B7!hFdCzkNYs7S7p!9?!iP?jrMI}`n%e8RT*c4L)8dI4M}0^$W`2_QQ@o@BEo zP{M~3kddftqR95~Jh?xODk3x-uQma%+)To!NJw$*BGRPyHLf~XV#-PQK0~O~b#cuu zsrS2`)QVC{`o>~$t&;Le{r9cp9aH9iugsrcW_}~N7pN3VpPAoO z_ARhnaXioSm3%jseV6$E6Bz5pE(1dS`4R!43{Uo!eh#YKbB;m+JehtEs%)NTL3PTP zy4Zpayvl+a6|~J|Jlkb#R?wR+Xo4yU@MK533g32RbtqMnOBKJ;QW>7?(JQR@r!P}b z9#8g?YsWOz-9L{f+x>D&`-+vA$CI6|mJO=BENyAEVW_G^Nl|RN0_{ z`Wv2Xna10Z9aM|RbOw?nr1#ZmNP1u5R}m(~>?v}pi37bV06P`O@-pD2Rx*2)O2TzpGK1&4OQ;Rt6n>6! zV1_;tv}mF~mTD#C;FW3v!t`Nm`WgGn8=<-wDa0}*K)3OtPi>C zGVWi6`h7`z|7gA6NqcE5O~&iZL>ilj{LR|CdQZ9ZJKw>W{G+WY=qw*s_Z-!oU%&3+ znXW;#=}Z)kC&*D;zvU9Gi65!V!)zQo(c2WK? zLO1S;ew@neawbR4dAwsXGab4K0g%m%)^uKjD11}t?p@m^rH@plGh*Gh(HMPyLEq_g z9e*%cf6@-BGV6=0T-u*1Z|K@OFMYH%I6agmH|4mIh^>Je%&fq<(F&e_A2|^nFtdSO zGqa7w_{E4JNyfW$UEAh?q^WX)a|`-JFg<2~X8R|C9k_*m`d_7S%%O#-d}~*%M@Bzu zTj0ws7>>`=w%prnTWZ;i&cW1@{;qzf(>GYo^mXKF%5es$B=nFM0gi9lxH#P5#v#VS zWtQ2#c5g?Q=K%obil<~@jubPFc#U4mcS8f*QKwUO<) z(;P7u_Orle0uMc~Z$gK|Cb-fU!glh%^p z5oxKqvf(wA=)2Nyka?v(O2V;U&HA-U?j`3StIt@149Jbrcsu$V8r4U!gRsuYR?_4j z3ztM91bE5a*bVXQh+W&V=|+fj;4UV?k^KGah$E1Z{TpaC*IDY4*%R7%O@s>-l%lIy zrg1!({5=in2e*-_2oN#G7AbZM2x#&4P=l6u7noh9ohB_!{x;Nd)ws|sqkDMV4jo%R zL+^fcssJ&1)09U+8wT6l!T2&m+OX2;+9#GRQ!XUKp3sQTI@=EP@TJovrKNx5-_C}3 zR(xXQ4PE+c8nU(}vo)1@8s$KjrFGWwRTin1Fm{wWE=r+9WoH2AbqPTh`)^law50c< zaC;7}^8M4ac(TPBUBnUr#WZYWhzji^Fu1-%#Emgw-=fP*)YidIQk2C?@m~bTc?Ke)n(fk z{qxx2U;x&c)7&gr-*U&mef;my^CAI6>o30^f=6g16v$P$TMv>!yb}Rd%r6!Xg4uQS zFSxRirF}NCa;imvYrI{V2a#X6BJxY2!9N^1$}I)9n+?1Z--t}n1g-1=??JghE#3fk z&5qdJzxMB13Zoiyg_Y53qCvrWXz>mu;0y(5fi>1HKoiMfvZ{L%n6+tIED_E&J9gEzW;;+4th&Zz%{Le9csWzs!&-VEWM6 z=Ud2ZQ#zbJ^b410)_F?g{ExqVfu*|OA`3EoC_dYQCe5@U(}zA%jzN_dPPd?TRd~89 ze(;4BWctwhX_ji+xfW#l(4X{sQ01)KIgHRSc7A3D|Si!SZ0^icIwxX|uS z6`J=y{-|sAzo`+0`!#2|HZ_|jSI~#@cSm3XQ>&4&xhUez#E~&pwafbHh)Iqzbfsga zeZ9uZaQ+SNe}tBB*#(Hh_zUMl3Xr?7GPoB0m%p%5Obt%IDLP|gOQ0%OCoaIyQK*)y zD1OvxvMVaf{r6xQnHRaPf-~}s{Khtx#DZKyt)tjZaYTM#yS6iKvwXlR$h_1R`9KLa z@sW!B#gZ@cU*;)g^N-X@0s#Q(L6@ zv3>gQA`ZBHt&jFQC*KVsi&AImcKxSDn-S-(nt=IH$pIWZpuujXBk>3i4IxJP_kF0Q zVXvqC*7R@Tg&B?=^zKc+hi`FvIG+4#6Y+{n`}EWQI%ALb7Q`B5-sDdTlM?7se?;gx zq>`%zr#LellIebriV)tekreVc+Xs6fmw23AUz*4Fy0(sw z^&CtgQlw(tKcN*Cgh5dZ2XeP?@E5Dmbiy6Mh2Fes3toKVfzV<@bOf{O$j~o z%ea~%cB|6U9GixB<@R|UzNty?-$h~s@zO?VpG{^xk*bXXL$J;b!HIB?oE&}U^(WV+ zlY%x`6JE3yh1IR|Td4!tba!Q2#DI5xnqi-ZOKOHZ-NK-YrgPVY?L}XnN~-j88jJK( z)`7=pj;4m;FCP2f!xnPC1QbGfo#R6-eqCFh9s(`?cCt!jHZ{I$v#Pt4TvqgrVbOVS z^ahnUM@5gfL^KEt-MsVHecQfzg*SBaoPGbUps(BaefpN7-|Xn^ zm-?>G%UcVMn%VhivE|KFnOd^lsr6v<876$_P)a(K9hwqIn zo#=F=p!L_gS{|fqC+>Ffi~L2hM1a>hEa6>Iy6u^}F!l`?1b7`bzl)=IzxjPLbmKOU zt98Htw8U*5m*9=xJX_%*OxxaR&^r*DM#5Y`k^m*hp4!0;SW+BmDSowx0@+B|@BLrX zI&uD_V?l)Km8l;PN{9%}(!J+rU5H3H$a$$kO4o*YK)y{f z9|ih0+of%t&OMlS3|I&pU`d_jpfJT@U6MTkrpSP?OMGO>Y>|?YHDl%k+jv(O4mdfU zT>Aks5w4q5_S#nOI`iU#W@cF_juU%h=b`))KPz_@S&H_)a6QE*@ZPt)Esf`OXWc3O z6VhC$@OHhP^jE?M;@%kE;b`k<@xIXFofU1({#w%)hjx;g>UxHx0i}}IivfE5ZUIpu zj@o3lN6GYRX`KS9aQYfsI7S;OgWHxJ$AFG&j7L7OUhg$?gds}kl3**Iv=S(W9Z2W0 zGR!oblF)H4M|d`baSo|(G=&gdkK;PIkD2IS%QP?m!D)83NAzRm4;)1K63x9ERCLPo zZsiXKp@v7)xFpUnY{&$e2oG`DRybQe1E)XzF-KBtLok`i?1LghS&C{WFjZvktZ&w| z;`v1R&tNiek1gWtNXd7~i@ZRtj`$b52``j%Dz&Ja)B6RKfj<*+qrv-M+Tw`AYE61w zz0`?fjbE?K2JK|ZCf?qo#@NBN{bO}E`*%4-qbbNeaNyB8{C2r$E)P%I5IC^FBMQ;L zV>}CL-|w}sc8ENfOb0j^vKkb~s19DN>9Edg0PETrSd*>7of zu^(cu!0=^n{z~^Jn~?+vvi@C4;_o9yA)?D{!`t!Lw(KpcksVbvxM4_vkFte)6JojM zu}KU%z2Mh=nL5^E>Il-1-#?FFio~2c z5hI(-6ceq5VN3I~iS;8|ANzWaX2yD6CC2H5K*Ee_%` z&p~a=oHk0~+;{O0q2cNiuBtYKEp@Fg(1gbJjV95FBWS)wi2MA-k%A@^zLzHkQ31rj zNc2excW!i~;Ih#^tGk36a&N(30{)Eb$O{x74O#2o!z!j~H#G6sX-44T7?q7gK_PYe zk#mjkj(FPGl-xEH40z&a?{TM%C#h>%Fcrt~*c9jWJ;)bt^WtD>^L%Nq8!ciWc||9- zFs;Kal5m^?%YurWjE6bAF_AsEzYRQ%9eCG~VleMBnDJAXa&Z}P7ybhe18^z?sOUrn zTQhva!DgoUPj0Yra!f0gy+t&ul`0>ArTi*>DmCqQ0yT+Pn<@Jb1;#}bI3CrWH!5*qDAzw=%4I7pe5=j(l*PC!ycHZt)lP+G^zFfb#|t@3a+Aww?ZZYuAg z?`E%ATthQI&c2B?bCpb*PsqN>f6tbiHcQvrVx+;+@=Ea^D|U&%s|x2$#UicR_pe~@ zy!TWyEDk(f20*iSeshY2EVB@2?_4+Dg4)kiB4_V>V4?+GuI&BCG|IxbrNe^0JI;d4 z-nsV~7PPMthxiYbJE-!;(=5pBop-x7tUk+v%-%W9m37ls6m-o3##uG2)90YdMV2sc z?_BLl{G!!+&8ogssnyO<`aFDAYKrQ5oZn_`T;{*c zjS}{rdUeU?H)~PW*<`kRzx8yhPwQ+!em336xiu=-6REn?9_Iig8Uajks5+rBF$88b z(wxxZhECL<9`hO66JPTCX$T7T#4BHRCb0#yFSI9KK$rwh6uM#~7|fx>2zER2S+PA) zcDj;2JT*>^aFTEzGaWKreHcb#{dw^As=KK^xpJg<3LDa!kvEbVOu6Ue@{I=qDco2;e(PP{nqwN|V`t|tV0-|>yk-Y4c)*46&Znm{uK zuZT~Ux$gn?iz1b$m>%g|tr_?ROC1SNEH(Nq6w7Y>2%Y8po!vh$^P%lLiuqC zobaZ}&NwO7J%X>;eJz;D__&x4g6GA$|HLo*@t^#-e-8~&_Lz;w=Ie3>q3(ATTjgTc z?GXEnV)u5jOLmBTL9s#zQt~f%hn*~ZP*nNzSny`I*Wu%} zF2Mjc@8ITmm46xui0k<#aoT31&dkrIdX;D(_nqSX4j4%i%LHP88Ok&f-*GZa(KN^| z$a`?9>_yXJJ@=8FA$d$cBU zNi1*Jr-J%1k?xhRUpFM(^N8ChAesHTWtdBbMXAhZbJI-RcKdNye<2C)zg6X0NWzbr z+z5V{0f}j=V^*-(o%cd$Q#vgCr4#Gyutpc(8ix8YeibdgkYDa#zdO=ptjTA5`uUJQ z{omN{s&{I?lkgMtUzRqr-#Pq^S>DWk=j?fsPfG1~OA+@Ng()=AS=J76jSb5yw%=9t zX}`bbCK3;9(t0f-Obef~VuC%`pSvAnq3&z?CSMaV&Cq#`uScidd>P|5WWs)M>XI0Nq^ z>p9rVJJ=jX*pXWj57RnX8UopZvcc?A8`=#4`Mk?xxVI83umKhiyvRtWn(H5lj$%G{ zSZ+EQ!VLD9zt4->09vq$Z4uCNskUs?6#xA{=M%qWd&%Se#dK4f9!9AJ6fgTSH8;qF zcm`31lQF;$s>b*0*OLT|>Q??38bw+Z&TpTId%Q+^QFRQ|PcLzCZ+f&vu4k|?E#Nzq zz0trf`D-b82$4d>iA+}!d?!iLUIJ(@7O+trHgvS8KKT$kiLP@Xe9>NYs76D2FCo$o zlS%yZay!I*0|>&Dr1q>Q9z=|mamhUsp)t&#wG^7+2SJ#2-wR6&98H@kcX#lgPMJ&m zdoW)~YuImhrNbV>mL;X#DQ&Hw)1{)5J`OM*$cUsL+SlRdpl`h6p;T_-uGh9?wrc_H zkfJ3mO=+6hxIy?CmqG~NJ})k7WWCvpl1%8k-9ede?ZArS!M!UYfC}I^2ftvs9>#8 z(9{0tze*UR1o8zMuH8$?L&kb2#TvuBR+2i`G_SIDu@Y7>l(z+>P1 zyuf3Z(UJ5#!Fa?b@=|K7=LSB3@`wu+nYb1ek2^yan2#9j!pOB3#{E$0D2)5v$ji_~ zv%$gw`;v@{H7FH_uvGk$U?mJj*jT_}TE=1XbArhFm(s-|7V7wCAS<_Ko_1Jh154`t zttOnKNa;vv=?g7cSB{2gFrSu3g11#@(-HmFf`)O2|)|88e!SX3IYj^`mI8 zg7d08p3dPdWQ%*4O-I*-WgrVTogxjxO@zah!n5J!iTnm6b^|Z9%cg5MA~QnesXuKv zfYos3-jP8>%y!*M+1hDrlWFkRB3)>c;mp{*W&>{KG5_bB^(cVR<*-UCB75foGA6TU zcKF{QGRP8cHPufs($RXh7<3(NFn@p2Z7@JJzolUdv7`>5+Ukv{C*&OQ7298q$GA9_ zLjnG2*6u=6``^Xjc`fX)MqFp2d+G+T_WafZOWi3JF% zCR_{rOQ01Df|q%g`2$+2oj^N+|5GTM11ds?gwD2EPedcdnT^dCadj;(x3T?Fv{_jPjV%XJKsQz1;CeMqEIq4{a+}Yz$TC=}ww=r~{{B=UHUpc+ zVs5F3@sA`}Z00QL6FSw(pCn4X&Q`2of`@o{F3aFVd-NwW9-G9#6QCOq zjiRsWq<6&jZ{4bDyu+oA5eDH66ls-*bB;IgQ-$!Z;7velzh*t@jp08~uw8& ztGzm3H-1Y0o=RpQvgX=UHD%D(58em^AIBdFQhBQb1+{(0O%U+tC5fEGHE`=O>_*Be?CD5NoQ?6$OXO9YC&~MwQ!83I(MW6H7e-nxCIRzZ9&Zn+S+14 zpV6Pd+Irw|7WCpV7Sy3szjbBZaJ&W0QqWnhtOg}6SX(p4THbrCxNEdPm3B`T`hZI5 zf6c8y=%cRC+pW!JZABWa8WpuH?MW->pyntvv|EwgbrT_UzFpP3dv~hgD{AbZ%C#vA zT_4m?k+9GYTnrpS+TYMc zSwl7D3#g!UekShq%Z!3}OGx|@NZeqStY@jS!IO5o)#+@gt?F;LvxM8clN-6kHMn1Q zFZY!^w5fCIcS~g+XX)Glzz%pwPj6;ab?jTLyZClIX0Y15!woeua1jaIqTAS$$}Axl zDRT1B<^P$js%ZHH{KR0LAJDl!S+8IV$YcY&`K2%y_^t4$+KTk%-257U(_`##@Ht^o zU*)%cNPD0u;~Wo!|1>AP0ho#Z`ui#}V(8wbISG66U)84RiBG{KS^m`yg1V<${&peE zKdiL1;rjVlz7?&nQ9a^(v&w6RAyV^seb-#6nwR;%Lxw3Dp>Y16($rfQf+5;r*6AW% zZV{fS;wCmRHzjmkdjYMPTh5qiUI|(76jA;P<1+tn+7a$&_$v=YXRPJPE0<^r9PhV% z3Sn>N06}g`2vMk{(Z)BCjA~$>Y^v-~Y%6L_veg?TL6Hcy3-=70wOEnO$cQGgJI6q) z)|x;9%oJ1#lAJDDKmmT^Bif!3?92f7S-#Yxe}E@F9r3w*e9bYxKs2_WS)VYY+u=uP zpG1PT85|)>Ofn4qo;I78i~5ZYFO_-+9jY^}MPxWlxM@Xj`nek&DyI!cm2I@H52`E! zMt>VcA=qDp1>(8dlaqzhj6x^0jIa;Q*=Z*-JYe|T!>ZCcAi1Z}AI{8MK!th%@n9E% zrq&uw<+Ipvwgrqx`7S1(H>$&vo7NO_}pLkM#d4m?gl zgOH~79=^V^1D5(5R=6Q+;wdZ=D5+_3fO^`i0*s6QFo~!H-$hiNd6T*%!BAvo6(u%g z9uE>y*~1K1ZBX{YlCs}a2|-p0ZwT^a9`~n)>71%%rhc{1YxF2D3}Z{|7ho0FC%9pZ;&uu!kj$o0+32BdQ{D}R@>k0X(-fvO!fvh& z#^CtufX)7OJ0x>zIpbZMIk?`RLA-2edYKAqkmz>!0NFDQ4$48|cE4521obrsve>u* zHrQ1Ckw_ZHBIn?X3zB2NG&wVQ%XqMwt-vY#_23)76s5l zts(~I>9!G;Q<2fRlr(H~Wa$l2# z^t;D*+@FN^6u&Eiz&;9ZLlXEqLxAZ)ZOUvY;dKr0x|g;YuY+#DY#OU;@g~6PPBK=f z6)h`PRKtsDa-CWD&ExaV*xh3^uGMR4G)K?@vtyCLAt*{b&)%GS+`rw@IWs6XAGw#Y z7S4AR2HxwS?-amXYF?QHr^_s#h$X;ad?{Wx#02;G?d?s+L;fZN-v{|X#Z3bUxC1(d-8f2wM)><=jL%~#=9XKr?`Gkvf%~3zql~WY0#f*=- z7FmH2s*4%aJa$pHihJ8KF9R)Q>?e{2{sk5BU}8<>OPq@P7p>{T0tiB2d9ej>k-E_8 z-Dq8+R@lo6r~&GvV=PC&R9b^eoG=tMH8+CQ;eX=Mf_&(t!{9!SVXZ&K4RT*9Y<{=asFQ;8wma3Vcb2lIa8OsPF(UViH_-;6D3cI8kd%8ALwazMlN< zUQhOzOTcWET+&JJMBb`+9eu;g;fb=M>T*P6z4o_9>MR6q%1JNKa1R zDjT29FNKZn9$MDuc-(6q5Z9q-*grep{gitDh;?MSb>tz^)hL~7?|KA(KBO;|wC4_{ zyTp9r+JnQ96h^Yv%v<;GE`rD&Gi!gb1oJy$qu2SV5W#R(SxkEWr1wn`M635@#xaF` z%^TIBsq7}OfP6T-EMMm;cfrHCr}#Xv@XO;K4DE-N{L0&_;$s&KAIl?tC_nhY)rGPC zqGS>4$SO14HM1~I!jiW%={2E_Y`=a1RU_@aVg^dKI!@d7aCJQYKv>5Q`DON_|8abu z1jiD4PAR@GxpEh{@XqZ!<^HFV|6=y6a(mR`+S2}(#}DZ((697qnOcT^*-R}T4w13O z*{{CVXhF{@CO`|Gc$5XLJk)~Bes$_$7IfL+7G(CT#wH8uKg@#KRs1nSEokrI7G(CT z>53jy`6VUpKgR4=uN`4Q9hT~v1@uCBKjZSwvv%a|SD!xE(!OrlZPhF7U(}32l{3|J zcRL#Kq8|k)a{S)M<|?eUvAHI1zq0$fd4EqR?nkn^1P@r|AHpxTB(}xwieAbt_4X9nABz1w zx!b=x@t+j>d%AJ~W$8>JgW8!{Dp=x(!vpid1{(10{m5MQEF6b%IX+AT%8TKBcyp>p zV+id(oc|!Y@3pv*N?>xT3z?mByTE-`0~K1Qx_W07(_)z4`0tk1%qp?bLE@eUAHg)l>W&oUl9!KIgVr%L>pEz9`#W5&N% zS;b3Tn&?I@F@mpI&nGJBqv7$&r!LbOaI;b3jN(sYlHEL zeB`r4mh?A|7kY7TYui8L&-?4;-M|77iD#Olg)4$vT*coF+v0cdOXDkZNA{Qz`{J<6 z_D}bGL)pgpd7=Y>#t9^QwXPK52ICs;uX~my>wGqU5%PXl|EKcxum82HpWTryJY8*D zV@SV&QF$`u74PKfz45qJ1aJ@CfQ>3k*ZKL-r`S-SN9B7$UcopYB@O zN!uLU>nMH|Bp|b=qE9_3eagMYS#X`?GaL!;WZ=G2OMS&WLj>l`UD1}XLdH1`=6tsW z*lEvDSYNBx+$>+b4V)Y|X-Kh!qK|sP(#uGjS6B+tQ-wuR*J@gNdc6y(O$lS5$vi4QCrKJWUo?W645DTghMtgi z9zQeF!>A6O&^KGf$=?^|;DKNc-0-^?jdM8sDweENP?#Da>olhdS);g)6ex&UnwGm2 zEVvz^yg*)S8)opvi(LMkbmEeL2DG2poi4vrV`t)>Hul6z;h zb9)W?RdvD7)m)ih-okxXRNCTCcR>;Q$Y08?HTkUEhh{PACr}GFL(i`ZR83JqaTrJLPZaO3dnj+ecKoYU zJ}ure6<_`x-idaXf!&$Wm9J}Mc&$-yP>C62u5fj63>Ns(;P}8+Zlh@SNs>#UOv;^- zS)3A@d}iq!PTIZm5lK4h$I5|HQqx=Goo-9H#E*Oe-@ha3Bz!VC`Y z!!p8bE+NKvgP`=ayyqiisxASwwD(&|$6QI)0_>XWO{4&3$E^?JWot%Qm zhoRuqx=i&|kr*+Az z73PLz+>kd;O=c&vp@_fZDDMpsBNXiT{Yv%-ZKArP2%W-sqASs&Pm2O}S~1nQe)rB7aXs@PYI~>u|rbk{du9 zuP<5z#k~p6mz{lz#ya;woS}2XOf?vtd=I&`=M9>}$NcfMZcTO^%VE>=PzyTEfxr83 z7dn%8ihfOaYU3xS#tvI@(Z1wGzU-}P9p2mdS@IHj*dw!F<=f5s>f6!YP`bVugvw4#h@2ytKVk2r1~}%m=h-g*+g|=8vhhgQw#wMT$$T(a|Cz`v z)ds+-+zOCx;8b$+_w^+kcHz%{NAj6m@=7JYk2%_+X1qH!Hg!WX@;*V!rY2$+E}t6v z=p{z2j(}APc$7PgtER@L_wq4y3IDu}FQc~~(T}B5TVhWw;lktW&2{|yl%;=oYV4`U z6x++NucwqhQUe8$TG=dFZ1xt)Cu{7f6|w%!o0G%Wa18{jdm_`@wf=*6*Lq%f{kl

m8J=u=WcVXj%z_=ZVwYR8zOps4IhBd7 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

&gB92&Y6tL@Ngt-Ps(%MLIV&cCg-5dn z6zj&P!)Az+E0={hJc?zwtssm#x=7lsGuRusn1}bLz<=bCVlc}Cej>C$Ho(lfh)J3p z8D*5;1uJm-$x!h~$>|t0lTYd4FB1ll<`ycjCm(J!^%pdWFB6i0jPGKO&{e2ul{{IT zMi^1$smCe__EMcJmy=${0!SUp9YFj^ZSob)sxd40k;30Q+@NRxIKr?2u97l(5f;Q# zc^S*P7$Z8w zbje!Xa9>ax_>d{971KT#2rTH*GUxbTv}bc(rSz10*a*1 zL&ywmh7w*t6HCh?aMK-22dtFWb?_=8wJAVzA?xQI;p10Tw^8&kijriMDa5UOoCt%Z z7o>+Djf|B(K5S`CvV(|vMbU#!g%=Dum_MWX5fy)A=}C$Z2*Oxfp!_5vO6p+PBc3OM zn9JDf;JVU&;`kU!tV6b*bI^ETB?mgkhb3)u{)Lu+2fY&ttRUFrgWCExiJ%Xb?tms+ z_(v>Eog$6l#ev0QyRrbtMtzxhAQDHz1;LBN3dJfIKAL-$w#8VnWs*m3Lt1t}M5gh+lMCEAnA;JqCfp7j}w2irD)*#)=K@F(;AV*=gPMNH8mAFENB8*2s`2 zWv*PqQl%i}ijCBuhu|o4LOEy=7FbF=f*6as&Isqa<;&TZP_T;bCEDi-1d{$0@lKlig1O=3w+fvex**A;&DhXBw9N9;X4Q&I?|!BN1X4I-021TbRl zj22#(*qSHnN6==`vK%195&%e~zO7;*_(4iz$&ZI^z+@W9D`$A4!7(Ql93iP?(Vny5 z!8CiF(XQUxRE;B^JQ$`9B{zl!n_WDRGdKXQ;^(+1K^vi}O;PKj4oLzwbi|78($<1l z;4OHcZwlZjGgaV$UrP?c1lBtO5w)U-f=zrk@lch8#44=dd&v4xN6qmWio)hAc;bZD zuf?iteO7csnRO>JA3kI@)2p_O)%FiOO}cBAh_^eJMhLPhtG4c3SzWx#YWpRhsK>~L zG2SI0>m?w1SR)D9FzkKe@=w;+pplrIyub*h-w-zpBlsXlccmUDZP1?&B|Syuk`u@S zhkQ}tW=9p3(;ehJu^esD`r0Q1+#gFMUi?&M0oEVQ!O@dqE6~Z41}zH(-eJ25f{K_Z zW}=SeYA0?&VqyZ8%Qeg^ZmxAU_Z^b0Xq~=IjRlyVL*-CUIKZ&Ooy;0UQ6QrOk+X=M zMxO{L$<9ob-`?UvNql%l_tV=Z>I)Yt0tKm|*6`pJ(l71rGfHcDvoTW7#Y&O-_hRV_ zf{@@n1c~AEwL%$CWl?M)@Wx>^uaf|H7jgA85re4Cn1c7J#F1x4(pQAhlpr%FwZR#1Yx#sv)v8uO3r%L6G zRdG@C-F$ei=J<}tS?H!}LIh1!Jai5CB-Hk{)mFyZQf=UwSXH;tsNx<)ZdDh5Vzqt5 zAT3E6_P(5TUMQAhzYQOTFX3xo<_!J~75O7opCn$(6TCChrIjRrBFFsMknDseMH{h* zbV#@fChRKOAj(9$iaPbHu~aH(7XlYCgpduc*+h8otrZK&K@G!EK6de7u6>kTHlp3Q zl}hLHwHb>jmi%XJaue|z=F+d)`L05|%t9o|VSOXhOAWktrW4-*_(L@(p?WO#UuraDwM>~Tr zodMi6+GvwVV2A8o>378~l}8CDjt1V4*j6TPQ@F}dIviFMNd(fkfPIRQ+NQ_`=e8M6 zuewGg16fj;kSwDubvb((QFZNA$Y91T9g3Tw(VWeC9B5m5oe%o=)#6C)z>I0;RZ({PQBh4E$KOs^N+Vr0ss|BsN?^YP+l`$}QR}t>aO;$23DmotEKJ2uU$+ zmeG*hY(^*$(NIm>`(?V|O#_kdqjEn@xR%!YY3q0k(B_ufWkt2&viv(WMb_C56B2_(!SHj87Ow-t) z7Kk5{Txk(p9Coe?KZ!ylwK{B_ooVO;>J1)6=7K0gmqW41q7 zpTK}7pg7D~hi(6jn=Zr!o@rB_nK&U3?liJC`2?nlj(fs(L3PB^mmzS^yb~t$>=YE7 z>=;R|D>TkeBn!u&Hv~=TPNsOoC^*g`Hpo?WYW1%YijH5VKWkKOx;71Ik1#SBaVi=K z*zD@zFY!6{Db57NBp@>kcecD`l_na9JWjFTseAv;z(X}7sfH~pV_}Ut19Hpd=F=p* zyJRtkut7c!W51zPKo)he$I}25wobR_y`0Qc?N~4dh_HpzHGx(-sy|cB$-jf%K%{nx zt92?t)lTcS!_Z1xbwvWVcWPCSC~- zWpyx7ZOWm7%b%NMI97ErA{3Vn+X(XKrDf(f-T&4Mgid4S%WAaa|Qy*{Q%hdJ30J zeizncid%_$agySU>y~TEio&miGT(aKy7_(tgrw6~ntAG>GbVyjtBz37D~to8R1}{y zoQQ|X+j$yD3t^cDH=MT+)36{hl$xFp(=+ofWv@-O^cq;yynb?rBR?w>-SUF67pXEH z6qq$MF?qSF<0)*_y#bRPr4Q40vR!7ib?ah0fYJIbSdf)Z>(44ES;@hCo;(yupz$MA zdK06avOv|(Rn#|W7oF98Idw0ov&;1D9R{Fpy>H#zj47we^riAcXt#No?2Spf&+iIz zDa+)o!r>OZzeJ(n@E24iQz~r*t=y>0o18{M*5qe-)r`b!8Gn+Ofw31TZs4?njmT@T z*nq~S0{!k)$+z|AV^zpMn>Ok`03T11I>=fr6po_)N2IkoQ1#q`BUJKP{aFPB zdoz1vhAviTwI}&66C4?MoQjMFo+Ls|s(@ryNHOCtne>VvE83~Q6|M+V+?5aObOz09 z4#DSEYPCPe9s-G%fPq2D^+Kjg+M4yT{hwV6rB;N+Q?P{#%g1jVOSq+{kUTQ z`-?w0)cSen@#~{id#$@WnORg5{XJmM@#`;0mmWePS=HLrT50{bGcfA5U3v!+;D3j6ry6y-`Xs!NX_QNX4(>W)>t#D|pwcDJp!+O9{Fa+1O+u>gB1 zpJUg7b$ciKz9XEtcV|cOYO8G$8Ke>#Q74>dB`XXY|4hsT5`&JdAbA=d#E$)%}xs0A>U-| z)1ee^Q4k|(V0BN7E3#h8%*D18TjCVRjC=za4dZ(Ka=V19!E}d4O3bxksJ{-UU;jpH zR3Af8Z(dBUV3EuvXQW>bww`r*)fuQ-w_sal4l6tcX>CdtT=E4txjQs8MLGL5lR0Gv z680Bky?XbS`SZDR8|9BM^?g|;G?aC&_4!OAnow`JcFb>%1Yq4N^8*@&X`KFhD+QS| zGfJ%_z{Zz?_8yWQE z++MAxADUe$^vm&Z^YBA6!{Fi9-sElI?<*Fnpd6$vDip71`qQQw{HUx5IN<9Zye3_JyHuYjt-ASfA=EHGR__j+;gr)!n8Sfr zE>yYSNbukmzww%dKIlMi44#|Ev!*i&zk`r@FXcey5`KhC2YG)nofa||k}l_bi!dpe zK!^-yJ+*mQ`?b?LNydi(HiQ!bDeO^SbZ}C-Q{ZQdf|L8GL^x6V;A9CuVm5;k0|eMu zOrq(8I;UPQR00*zj8N8Sy7SODlgl&;;>t7g%rrsv1nYfKU(|!Zq57w+z3+Yn@30MUx=TxAlcmGcms%9zgo?Mdg(ZC|x#z_c=?X{Gj&#;RUsp+$ZfT%uY{3hD5Q*C@lKE;?P+x@fcf&Rf_5qOBjE zd}&-bh(ALDG;K^1Ugr11`UqCw(x1%L9Fw2HjFJtCdcd(i`;b~=elq$)5>CYO&*TOO#T7`!h{0OYMB%} z9OGqD&+3MRO1@Z;L_cU1E5 zXmX<#7vBp#0)C0-oL|7sDg(_n8fd^e?4-@fu}!-qx8J7D6+sKrZkc|h{H0|;e&f49}w*KM|Oc6 zw9LpuEOk(%{wWO-;bK0%&Bs;RgLPP#_3>5y6X%`yg3Q5(Xm8a*NMiOcWj+{?_Z~_0 zW1k__vuZ^({roiuzR&r+TuiscrWn!WYT{|x!H zJB|DU?8<(MN2nQcp#08xV$tM#M2!5B6+H!ld^CZp&rFkLDAf{s`X1WU1xM`=QP*lb ze?uczp-zt_hg8JsUx}oTx&6D2BS2J+cD!2X*nzRskP0s3>;9rk{}9$(ZQ!5bs`uih z!!Xbl$cyjJp8mn!UXEtpW@gwhFQQpgQH)hxlK4BFEF(|OVU_uMu^Z4r)CE9aG~a}| zjP2R$Uchi9Pfq*7UuXhRE9nvMu_1%V1zl<;%*5(v))|+JT}Ey$*K0XX*DId7g={&0 zlCE0eCKTdW-*RGWQnf?Fsgj?DQ$=?rw-7l}@)LVAu;ZuEy%jnY<_U)=x`ES?7q|UI z`?TNaw0{@n)&93l``_==eofG9A23;I`7WSgPh^pP3!K-Xyp83x()}%Wf6Mes_|HfW zzU3@tI56JpgVDG9(v1h22)0TEtut)77^{q=4mMDv>KbFI@>r^oIZabAmTKWk_0UMN zsDj4UybmF{LvI}3BArJdC%4bO4W&l4;gYQm70z$h5nG{Dysbzp{J5bag%gVLO;7@_ z4vq@@xLyzoGn6?yu#9ob$eeft__V#oKtr1oY18$C6H^=_{HF-jW$I!3e55c@ZjVO= zsVZuAPiIp_Y$G2rg)J{$MTfZ+RdWx~a2L+Ffm0x+ZxDz<2gG9g`=2<_!KyfrmVNID zDm%hgS;u=0R0|~$2Ch+U!#0?XGf5LzX05*1eh76a*u08#?e}U%^>ORjsTyAa_Ygs;ca$D7Qmh3USjB9bcYwC_E5UwBUT zRb#)aj(PA5znSQz!G@;LRwNZ^g)b0QIHAk|ZM<}rua6uCJ>n^Xt31sA(5VxDK}khw z)b}|u?>OpncAHHLhi90_@6;+H*jk*s2OWCLs;G#U2m=Svyc#ERn!d35l?^)`3j?F1< z|5VSPsE7V#%YT{jV^XDm3G{M&_ybzjGdyAc)>q9sJ4VTgSGCpUwLTCP+4A6)5w+TUbhaj%P1UEJZ>=X9x?alm7KBSy7 z#$Rdlx%AVYsK(V#&YdoAWc5=xHMKEf?sy!Dl0rc>@u_hrYdOj36@%pR3ECC3i_W!f zxd0PVhg_yB+Ka%L$JdMMn_zy!@f``7(FzRc;XpwU;$<5`Te zC}3YkBU&d&jVT+G8gmQ&hxHe3r?n?gU2WAziOqmGEx&@JslW$M zYE1d3)N>4CTwC=z_2ly<%D|P6^oV0}_1pj0Wf&*uOZuIX@>$|3_NU}nB1wj9eGa{| z1CdOW)#TO)UZ!iuQ&aH#Upq9GnptN8)W61R1xC<~V%?0*XiWJnj2lU~Bd767Y(<8=Eh0DqID?1HwOCauP5VvI5^Wk(0 z_dTcP{|6@KOWBHo_H%`h(lHOBY|DO;iVAWS4SU|ci>f^QaS!p`3>LZaW$94`_K$N_ z$imnH{`#CJK&8K7uZag;4OJ?WPwsBza}LQBrcKBG4NXL8dqcf2+(K4^;c#l^z9_cs zw_Mpd*O48NXcVgK>3#9I1Iy9HV}!F7ixV#xYpmRZ*8YiGUE5Hqt4YtV=p9Dzlgm&0 z;Ir8NiA>Rae7^XdY}dhOgbH)zv-rgHadm7^IK7x^xP|z8qQ~;Q^2MK3-q7T5uRPPN znsWYJK9ZWIcr}qQVJLrUD@BKgq+5Ij>~G!_h+(6lkMxSbvT4$(HHZ-`14vyj{ze5QZW5E^@i0y7}!7(W75tw(0PBR)4-oseJv3mF3uX;Z%#OKas1c z=+DMV=}rQtBT06gW!`XQsLYVROXN=vG1^o9e(BAk3h5B^&z{kRqwrB19rQA0cC6~- z#0zw=(FgNSs814vB#qE7^%+fB#K{JnPc{Eh&Y{c-CaUo#s>cz`NL6Wtsqgq)eHv=N zNtI529lbbz1Q$w;DI7H%kakHWt}L=IHsHKxi|BMd=S1}TWN_oJ!PvL>mQOG6?9ryZFGA6 zsc>-fP?Tuva&8j(;Icq(4lloj$FurD{5%ej=Tk={xfj*K4Ni#NM7(&DC*nkz%qsZg zi9yp}oFOP6yOK7@(tq(N`R^u;DWiOw%BTAO$Rm(0ELmlO;q4DICi@s?fu4eg&LtOR zS}0?(P{}I(^*`5Cc*O-}_6ny=q1$Zm&{0m2MBgGM*&?^Q&3K>&4=r}e%<5Za^I3vh z&0OJ>p;PM5QqloS`32?U{F?SX81W{_L#2WzQZO~H+~ooaeMGpoZIKGb9SDq;KASo& z%Y0yG^GuR*kUP>kf;gZ>k<` zv3#&2z`H_Sv~qRX?ud2rYW-OXWc@!LDcI{e^87cNvfE#yW&ANwX`kMj{O=Q;BT z3NB~A2%bo4L3ySXE^+NeQ@)UqKU@BrzVchpOU|A$1!@~aCxK5 zKG+mz#w81KQ4-Z|P8lXlJ6yqTk2h;|q%n$w_St`-2#o0WS1cx*{XK@=X!5)sPC}9y z^)gccQ@8yW=IHFHT1)~RW13zZaf_S@e6hl27N=fGyeyA7(?tL1pOOB|%EK_XKKWC+ z*E{oh@KKy;HagAqcim8&sn5L|{EYjj4?iavkAZ{VqtOui!H=^KB?ZkJu0u;R(NlMLEgC|aWexDObK~>j%LRIIh!blCMS{^%VNJZjB_S!hO zx`n!<$>oOsNX+5Ol)DP)juqDTM9vyik=V9Bgf-G5&pz<*HJm!TH)Qcuo{|1^`6rUP zp*-`o+ok5$BMOZlCET*{Aa4n;^OHMn@B4@|E-sQEG(4Ge}XUnOk-Rwgo@65O-?s?F!%OO zbd;!Mc(;^)l9Qvi;*#k_RhVb6W?pglZIiv^_XT_YT4$@;(_W===Jifwv;20iiWS@- zB}88am-VkSBL~>d!Xw?j*4#)Hx+IqVPNnWj4NcIIVmLmZ@T%pw^+@ttEEm7sI}!IE zj!MBAWr9)0EmgS$B$?>ya+>5+v2E@!m8ZY5b-zp9AKL>M9J6S9gLx=*xon*5Fy%AX zz!8oeA^OBpM@3Q>VTWCW)9t*nXlnFW&V%gTmc<80(>|-MS?{Z*CmkQBJsO2+PIePR zPB6%3Q_V{IdT|?CiF2&Z1(Ix*GjdtFpe5+U>|!@Ji_x4_V~;^VaDs}D?}g>{Th6c` z7LlhjbVX=N#v;MTjUId=ko6!pJnV`jH?{Ue5=Spw5GY9CKk2e#@Q{gk61_P8tF>ol z;>+gq5&HZO6jAOo^4QuF@_)Y^Q2Kno(*ON?bk=II_25yhduGQgTlXXqV`%5f z#MuiM^eaewL#-W0zn$ERJ^3(MHkd3=D9a$wV6tScl1&Y~WBl#C2sxe4I=5Z$(%30# z?3>gyGf{17I#V@W$pGbKEjL-6`LoIOl*z?G_g(fVM9InJ#P783xio%O>z?c5*HW|9 z{wqX`BF$F&Ps~$4LW6JPNku!dMISS(W`_UCJUe~@wIm+lF_HL-Y3jd~KLj^8P3ewt zlTHLmm;I$|mv!-+so+~cV6|Tl+^WQCZ!%A6T<5u}&~VhHQ`u+=fq?jk2wS<*Bi68` z$#6d!>gU@uLC=(X&HYv-A7P+&N)|iKH78aWU>?)gQCY0*(7C0?K8~{Z1dFkb`fz{x z&dqb{X(X0h`Obe5+BvA8ZE3vD`||fbUlPZ{lf>iL^&agVM)c?(OLhYEg+8EnY!%QZ z7xZ;YKa*cNf4uxO{(s^Hx3~M}|3AFw|GY2%AL=2?gQvA8kN+=c5H-mBT>O8L$1&Xp zk~rh*%g5c(?foPq~c`abjH9o?i^Zy;m_RQjbi(E zkd0lwY}%j7jY6XaN0O1^3)A7#@v60yu~nxsmaHz1B%3OY`}QMz5V7Pdq1KNG2e4T6 zPwu!d9jhd+bZ-BljRgfAJJ=q>O%}S9L;e<4@6ECCIxYU4o&IhwXJlG8-O6)v3r%ku zrKVTR^f!&%=C`|rcHduC7!T3xn69E*6s_PkYb_c`RZi31Cjt`}?G=9NiT0b@zmdvA zT}6vkiWtP{v2=LxsN{}pS38~d2>o8lrsH*PKe+^v8PZst%f;0=6p@n7-b{Ly|aJ&IdRbppq-ymO18iu{H3UBb}`}yJ^?! z=a5MK?yjOg$qAO87pQvYhA(q!Ex2{lI8WO_?GKcrtGblGFoSmzE5;NDsq|Y=-*u26 zF&ic_x^S`57nz;w0-7R zl&0`%etag4;pfa(83KIvq&HujVAhoh^lnP-w14%Q&&qpn%U;erlJyls$!KOk;1Ll$ z@j8XEv<-x#SX*yDo?jVO&u%0W;ZTZ#V!~H#|L602dkLKzUu50D}h=xvE+6G;^B2+P~e5xdv2q!=7({^SZBQX24%rd_;-J5cok43l8SmKUs{{?B|u;;tW zU3#jiB`!Wp@6Mtr@G;!ONBl+8;F(TS?I*cS<&`e;G!qf&bqbI%3V3-vEP*=~el)1dZHp1sAL%?Fbve(6IL~+J`4gx9^Ypx1 zusHH~C*t9gM>EU1AIsk$OH%iBaO6#9&u7rh;Y0JQ%*6ci<{%VRV#cG@e9Tt#<|c8h zrtrq?knCjglPShP@aSdgJGf7M3rCKRpETA~;@Fe#(@ELC&v-;^;K>=E7PbVM6B|Pd zTbPt6U@XXyzl29|WCa%sIPoL`O9ud_E)GUg*WhQnSrS#7?5t&TQmS-yBsF|3bXyhU z7N=xa@7PFoxc1_mZZ;xc&ygNZnNgG*O+}^r9z=0|&STEIzIU7YLj#7r`{c*Tr~Kap z2vdN8nF2m#>CON8;1i=ii_g31_dfVs1%>kP`Pb+2=o_*g#NqZ1Cv~*;v?R_3SIj2A z&Y={s6oU^(1eitCGoq<+Lu*n+3nMxSd~Isfjn2Svh+HNYLkmYXCBC3~W?5}N z1(y$N9AX}E6HMybQ5_4PD``PSdQ*BowLhgHGY0zQXfw zEHeFcOAj&)Rs-_&@iBfD{MBT`f`d?1h8p&E6y1!)PB#eJ@Vb#1PE&d9gX{;%=JZY8 zfsU71vp&C=UZ9+BWYD|^+4ITc0q7-S)ju9G)@&rXHk>NGC7K#D)TLf()Ggdg!tsHb z!7z+ZA0`r~ILNDr)~}`j6knSw<5Wj6z}SUGqu+@yJNZkU{E=+_dBxpN8q%Ms46_nn zGssO05ftRjLYA6;3n5Cc^{3WI;XKyWGC!tfK!T1Qr%XmA<#9^EUxv;|Cb*y+Z+vOurD8-tz3+F&x;JCr|g<}S#WXW^gouK1O?paNL!O%hM%Z-Q6waXt2w(e<)AJ^KG zNEmt)B)Io#0xYtO3c5K|XCeJFax*Kj#i7(jGSI@ZRQSfC?w^@@#OG&uMCrOLizG*U z<0T@>Sq@Q#Q@YxzKCm=13E*Wn8)fn+pCZnuOm!arx9(|Xmkfh`YfrPIG77A=_w7j$wtsFD zgZYY^gmJgsd<(PO^%Ev++6GlD(@6U?`T8C6wM)A(i|rON=-UY@tc+wB?_{ti`wRDT z3g3RVX~ZdfmRtCIU*UF`OwO9){DlP?6_4__{0Rf4T7I4`do3U9w!EEhS+J$jD16wd zzLq;o;rQ`h!4HNwt^U?mu>GGDjIxoy8SuDbCRRaY^C)!n1Z+@tCZ~_M9x?8nSWd@F zGvjb7Wyj&!@nIr&`#>xW@db{qgPcdH;Jqfydcs(jO}=DGihueQ#9!}T>EqYg@xjJl zww)|)J04Rr>77C*Xr{ zq$&9h>J-bWEYuZ}cooQb*tu|vIVxro@1D>Np^U3}z0ZI3eLia6&#Qc&Kl;i(rGLw3 zR4D3iwxdyIT5Ti1y1d55DB-=qyyNiwy)wfZjaK_FoJaAZSzk-tzJwGFQii`-Q4t+e zH;aNOTUNP`*a>DXyBs4eYyU()N2xW(IHhFIeV+V3_vNP(R-5qS^l*&TzS;ph0N9W5 z7^%0lP;5?w1NLa@#wAKk4c`I0UpZEfMQn5K>RoL5!O(0@ZAcZ}8%lie^MKmng4##^mS^MwRDWGupM!ikRk!_+FJ|K6j5mA2@e_Q!_t7}Rdn+FIcrV3M zc3j!?sl6QzFnxPV1S4luxV_CBs<9|XJnEHw?r|)(Oh`xb#ZUGDbiV`8@GL-kR`szb z8LFRxO&rDX9A`krl20;pGsl6GiZ(-vEs$d5xd5lx#Ly_}Kq2SJnxeidbe_GCT>bDt zP|?hx(r4FwRh~Xiz=9sQIq&?@o_BsYIsH}*fz7FQM|Cr+(JX~yAI)T=T+k7!0A!Dx zBlZkU)`FPWjG_6`_)H7|tL;Ak0<285+LrUw+H;B3_GP5u;lssZ>9fClVt#X0`v476 zsS7YFIyvF4pRTAX95XT%EF2h04onpk2E=sT=j2Er{+J#A5=|!3dQK#6GJFz0G^6E_ z#pXrJ&|?@VR;P+?2_-pjV0qBBd<=ihP7H9$Jg4;}7FOijGL>2FS;k|aIKxdwa)$Bf zq9#_qJ(Bt&=rAJ0e0|*pxfIm_!vqtXv<|W=4JaRU9;JW&i$|{!7aW=bCLcIo^no)bV%U0gs%VK2 zn%Uu1m0(6>2=4W2pK6{UV8-wvy>fLywi@~D3?{Aii}pp(K2?3+UzH3lRD?_X*6&{zDTx)VuR zV(BIw?#p6$EJ<`=@@=-5nHBMD`5IQpef4RDJi!7vj^X9xx5fMNhsm$2{$!ptI@0i; z=`SH%(Ih7v9vn$sTikmslj`*>eiIU~f%R_!1<*ocrTJMe{=E68_Q@YgZs5En)5JwM zGA7a>r?6aG92y%M7rK~cXq0B^C|2p85vjcp&eucNoPuGQk5sCms%wk$=0`+g&`OkB z6I5T5ZUl>@zKd)KThUac(!OpjjDk+3oOg69*F3r^!u~GvGoQ*g?O#Is7tp>hu)vK+ zK`2fq9y%Wcrf32U7@X*$-U&@C8DQosKr7XZxqs+1YQsU+JE28iNF7H_=F3?3%a24J zd7glrBeyvOxlA$)Jr%Gd8@$YZY}sD-dKyjkikxF;52Y_=qGw2M{}<+6pZ-Kr=WxS# zRL-kQATpzloLc8KVW+t3N41J-s7)?sQyRdIJ=s74lo!*EZ!QY1>16N3CPsmx3T9~W zp`nGNDneHK4~%M?Um>eQJJ=`%H*>2}B-J$3p4o+?e4=ajlWBH*sHS(zLY!8DM*o|B zamU^7FE!Ghrc%de?R=AkF61g6X)nU?>_b}a@>?D>0=r_F8F!47#zrlsc7Bw z+0WB@b~Luyf((%!2CcS7$olXXgtPS7kC2gKzP#f#oLbJ;d-{HDzm%`hRJ$=PQWw({ z=O%+8+codr{yMc9n?gtmveZF@u?`gLk8{a*vBX`4w`d*2z8IH<$red1Tu8#oqDow& zuw9nbtoDBj5I?&y{)(PfTN_`XsWWmqbhX+~Gy|##XSFLt+Eh=IVvDgO?WzBO!2j;( zUHqfiA{XO4%IIPOj@fv*e`?h4!dPaJ!bJkmgV+8o65AJ0;|+K0NvAnQO!R4P7}AqD znRMlID&U$TWY_rD3=#xIYSg{mci7Xt$&D0Adc=>;H~e$qV;=vM^5pT0xT3YUfBf6m z9N*e|P~t4Yz3iuH3_a9jcRy(!S}x1{0&ZK}-kX@B?|<>WUuLw^clrK(hJn`J0f}>I z?>*Wx?28xsxktT5f6kLD52~ZdtK-?fdu^Pp+K6wvAXogS6e6jf!L9rEj=U3J$0_7e zHukM}B~3%P&+NwpD}IHe_%+LEQG%HMZ7GSZCaMmx^6P4^|Cwt6#5e!r)^5AvZ@zkG zs=lpi;c^abO4c=AmKVR3`9^+u?o~{hP%Z=ERS)@UIz|uz41v;UMIzY%Dbqp@zdt*> z-!``!wKAN#p)pUFp|m;S%l*8JQ*a+7QF zbKl@5Uy+}CwwruZe(niQves%(r2vN!m00Z?aUMy@RT>OTRD$^5w;oZ&tgxbf5To{uyt+$|&9I?gCNQrJrJu@8F?1N2ij1}N*_6`!oi=Lf@gN?)9_obvdmN1UGHpKSiIf6PnAXxkLZ z*>M~G;zDZQ#SPo@n?fAcTVSdGNY$y^xu>nXyW%%@%4#vQ}NMG#aHDjK7xw7e!0yUjrWw{>JJt9wImueg(?E5uja}=cc0Wv#sQH}1NdYwClNr4LV=nBH&$*TNGUMw zFLBy`rGK_#KO(z{z_GsoQ#eFOk62J`#=)LHdz5{Yr#ylh{_Yds;mu*CQ4Wb zhey(w-yY693qSk#A@kQh`db$hxuh$$cld{pKhW4d_4kL-XyMzak($czpWQXgz7tiHx{e ze;UfPQN8?M+t2d3#`nh0)~6y$@5IWSCjG+$9```}Agq-AM4*Tn2s77%*Bs7_z4I)k zeE8UBze_)Ny9-l9<=^M~=;FNnFtL;cU#Spd$5XxAobno~gwAD#L;h!f zzSA^i)|?r03l3?RH*fB|Gs~wp%xP|zLh9T^e9Fv*_?hK1W;M*7UVdV8`JB1&@@dyh zn>Bmdm9rbLLw*14VsSUFV*8>(K3suB$mG{pJHpK3I9*Io^BU-+kKs zPSf0J%?-^{8m^f(drC9dIny9w*40h38?J7c6K|MaeqBR6i;hFG2y_dcS>D_bkI$Mj zv;5j=^RAxK9G^B1q&6p-n&!?^VX93uG?!1C*KlU}(DGBu>*vm%KBRmYPs%@}{0yEN z=Vlu?AbfM1;?2NE-Pg{WOKXN~XF@jzv;&s=JQ$77rR#Cd-OxkBcg)YMIqpOK+^d@k zX2l`i)zexWd{^IAed?G&-P^x@^tsiyKG1T^ACFk>{)X+s0v-RC{jbmWY(6jloR)$) z3H~2eFz5P$In4!gW);l2lC&#HY}P}wo|+m-o+-9<_=x10{A=W&`P7{E?aG!0RluK_ z{A=VNnarE7hItJM1mW7b^S<5OG_9VmiL0l?8|O7lo8DZ|9B*ivGP_~U%y?tLwAoFK z)27U8n4YMQ&zd{OeZQJb%rj@ty>i;@S=Uc9=`&`vG)xDJ4NV1CH_V$ApK|52`ftbQ z@dw^0nB8zq!<;J{=FJ3&^S+aqHg9^oprK`2eSAt|LvwS1aGZ!YI4`;MDfM%k{rRR$ zziy7N7@y}f$LAH0I_>IN^{Phi(`LFy&vXd>Y3elBT)rOJ|EQqc2WrXVjQn~Il-uL@8HqXjRk((g=3IZyP^Y?r&sWcE z7gtjnr%-YEEN4(_nm6~FS<@S)pH^PeGE1uZv_lGJO>aTF%;sN$e|k&smf#I_lV45W z_bFFRtKVOmBfYa53lfd0P>O4|{v`CrB+#IA8VROJbl7bECHSY$jl5Bqe+HZe^Mfkm z4L!zRG?|CCY16NAI204PpzpsT(!78B$6M29@@WnS=*}r~r_2*Vr|6m9Pf4^)x!$}; zd-2Ul6GZ=W{x+(|rFyPD@$u(UTz#a)tIgGa)8g@ltDEBG@ww#~KjqZQh>u#A{WD|U z+^ZR$Jryd2*f4uYdAxC2+~cHpW5c`#bu-(~e^pK}yxf8O^TUi{|CyZhttZRi zKH_?P+@G}M`s=|X!XJP3P&16=MuAF9rNIYQ5>1Er;oB&5=wl9dm5m9G}wAf_9yQ`Ib|52cmOx`P>=6Jm=JgmRZflYV!!VkDl>Y{8?H( zhaX3;V>iybPI~&YhFR$AYX=xS`T3L%n!zQBufHGhJT+v0v zii#R7D%DZZ#TF}CT2rMh&s(0FTKYVwR8i?us<9ck@8@^!y)$?2Tp&^3caqEZ-tYOH z-}(Rl+;h!fW_rlkrSx3tveds8|EA|ol0gs78da}Vj^0ellB!0?SdL^Sn-Y`0o9aiM zWqcvsEeQHxJSh6a;Wi^ST;*w@Z9SV&5ALn2Gu!J;$)3F#c|`19!ApWfO zW3Kb^NBz>`zs(wKqr+rL0O>H@qWftYB_d7x>G8N)(Nxjz7@wm2_l;1~KQ|el;$$j9 z2~S4+nH93$Kp=j8t*(#gR6% z_r?57bF?{N>JL16BH9v2_H=X8GwRJWA*Lqs=Gmiq?oGLu5pN1&(*TsfW`HKu1$OL(27Z)Q>=J^gRhKgkx`h>*3zWlR4v&Byh^PIF75H@8L)yPDh2 zo@iwbVE3L~XC7{BXXss7aU`m4=%XA_H^I$1K^IvB+D|BGJ$y>xEyS;QfK2`fJ(XkU zSB&4a<0V~>3XLAM>aRJEW1^5Uf5TP|`OIK)9AS^J}EfBoylAKtrm ze49B#{CDbv%uZSZ`8(OS1n$=Cx1APWSS#A!#lFS)TTEyA+z&M~8IU1y>czZT|qUG1mOo;|_5cKUp5 zuB-hFb2H5KJ<{CjI?=}LfXwJMH!``@%G4cGfoNAQ$^7WKvw8y+B`&!dr3up+eP+_U zH_@D}G$+A3D9=V4M>kcMB6{^3nKxtRk=aBBCQRnZZR05&+GwH~vp1h|;gHgz>L$fX z*pn<;llRQ&+-D>u@0phHVksgA4CCOJ^u$jqoK?7HJb&K=Fk{|qZ~ zb6u|toQUR#_i}QYAI`}UeK|Rz%+h|CrCZ|9%+>cWGQ_15#-zBK;z_2EJ{QKSr2aGW z|IO;Lq@%8BQ=qMx>332$aNoVfY92ChN91?b?~pe#+$jz){dsq~Q)W2UuBRoYn3K5a z_9`Q0l;vAv35Yu_{5!z>-Qig#X-$^!)*%K2y@_> z8`#215=xk9QF@t2X(q*6TTeeq4TQCOR7i6JmT=q?pkSXkb?#Kkjk9>?@X?m$h!HOq zX=+kf5L9PNB=u=>W76A9vQ5S_!viy?WW*Icj~_Yd4gCsp6*14=^v|0QifJ5 zW4BT^O#L^PU&UoT&)&K{<>O?Azjgbmyen~PkB^(n8v)zzch4&s{$DkpSe>=|+&SOm zQt}#ihimSU0=HH{U7<`$JW1cqdI={#;p8X%Jo%X@eL(q{C>!&wm);nuZ0bthOUonm zK11`0U6FIL#(G*-fX(=l&?9<`brR|A%IYfBgJqcQoDq|yX$?I$JM_FOAboRD zY}2D>6+Iu{)F_^bg7(%EgeG#=*19`bXqD&2JI=l~+7WdH*@g6P7yMt?`uF!2+!+kt zczI5_fn%fZo_^*_|1eaxN1vbAV@yu4R4cO-d-VAW*w*9CM_4&M>e?LItGaa+!Bp># ziHW+3)FX2fGUKS40Xj?SmZ71`m=aOlMOhJ1OUy1x0c*RB7wD_XdhZ1(77)f%Nzz>@ zWc&!1xizEFwl^a+&2DK2R!(1~DY@2=e4@r(O|Qr`(oX^0=SbSb0v&)O=L1!{YmJsQquiSAsd+VAV~5I#Qw|F~=Ax&WkJ9X^Fx{HH z&K@4R6I0BPd8p@K685Y_^{c-XRFIs$-q~t7sKK`s-IMOE=r&0Odu)|diWfM z7nm_JCv8q0#x$UJDU#Nh!8zM<`W#z%jx-09Kkp0#E2`Aa6YEruo+8p2lT?v~>Era~ zPMSJbZOZ}QhY0bRd7UuYJBKY%OvUjlYd zpt*zkfmyq?W<&L2C*{s!?-A@#KbSe*X-Uk?h{XHL@0F=DtAqN3Xdyb%9v)3SNd=U& zq%V6IURLv_hR4z%wf`WEPChK$zgPXfW8nX^;dgBN7#8k7-gfp_V_V1FF8$_$c|*b* z)OiEXu@0)=vKXf(Ka~~ZLCtjZj?n~ZW@544#Kl5dqroVBDpnt zYg8mj{kK|?7-?#^zh;H9g1t2>a>HI)eIyZjD-{x>YX|$3bXj3fZ~g-J2BK%q>D!Q! z4T{*`y?f_wRT=7z6K!|i7l{ViC}bzv?z}I2mtJusTis0oYRaQ0F*GJ`)X+55&X^H+ zPaxG)Nv(tQBV06Br`uZ3TXr(3AtJ?=`J@OdfL2Lsx7~K1%XUHLnXth45KF4NA%kBD zC2?zdLmyPL)@9G-0g8dzVTSc)+3RpxD)Mve){MTK(Ae$se6Cd=g-geT=q!GVvrQvm z4Y2MNkvU$~jWVi9{ULH#)tPA~diiYZpzx*%W$=b33r|s3ZWwAEOvH{<7adA}BYS0} zYbWxVtX{IGMK7rO*)XOXeEA6r1g6&y(FiOj}f2N`bX^FWc8CA|CsJ7 zduvxe8TQtwepJ4(glo;}Bdz>m@m7kzLORm+sS!wZRv46A4wW|G_NVb*iua$f%{Ve`9T9GxAaRqx+CO6lOO|QpO z9=O%)z^-0Pr|zb@rMhF1i7dTS*YDdMb=Jzfk?Q9DzTME3wDbN-&6aRguu(#z8kUOS zww*OQ!ht@1m; zWv<%)!vNwArbzV%`fln;%24j!wv%nAAC=};-z1f4XAYk^&NdmQOO*!cwbZ@8g<$C5IMqHDMkC3%JI({{)wK_yM&!K6E(J95~!gzn$r1TFExv zdpLfkx!IN4h^z-s&1*?La~qc&Q29&Ouiv)m<4@lI*^3u`*j+w(|3B^OzWNU*etG}< zKe6FbWWrT`=<{=5JFxu4G6P3-H9N|6EHdzrj^!70?D{){zt7-5XYl``u>9xw6)t|) z-`b?mHSmniNAENEcAf8^Ry?^%u>4ueWd>_hP~F!@n<# zK5%cY>w^3_Ab(^_A#*X0$UPHB0xZ%SCPxI-dcNG8>yW?gC*&_n_5!&(=vztII@h7B znk#BzrZ1|Qcd`y9_e^;*;68JTm5a0YCYR2*(Jjc5g=yuud851`>LMni9IuwA3i)G9 z=>~Gp_$GX#x6`NFo7-eH<&)#X%PdF|_u1?a7E zAGqU?Jqa`gUQ!~hzK?eHnQpGaFmx7=+Gx$4Qg*a%!r2BZ+#wAEuDp1=xm-kal9oO zXrTgYZrc<%aV&87=;6rev%E2+Pp(N8Qj2Sv+d1C)$C{>xjdk+#O0DJ(0I5F zm-U7qa{8RCgC9O~=J0u=)1>N0+h|tpeHjr)2*-Nk4*cl$1|`xW4zX5X8X1DtPvhr zWY<-#5ignk(Id~azLq~S?MsI_6`gC@Un3mmRx_%_-z8mA*4MJXhCj$n=8H~73jI$0 z$yAf!FZR&TX|{|7)8>;hK3GM@U#0@>{-SUlvsMSmKB8$q1nDifPM(Z5O2aRUp2-J&gUtmU4$yw_YM`2^WR}!*j;-n~t_zK7PpnI( zSBzUz!gw?iACPre>*eOzdRfCm%aXRvX4Ju{8dpzgsdmXUE7!=$7K$wNJL}J^nXGE< zde5yZN@oRv8j^tLVw}ai;Y&`?r#9L*LZ2Qmz2kdak2fSugRtW#Gk}MN0xl zci+%PxevD9&}IeOZfFT`yz7SMe~HUA1r~v`U>WFr_YEx!7J}_y5R8F?-~d?io*UX# zuos*I{qIH3MwcrB7J=hn5G?7&KG+L(gWf0jo)=j7)D2C}|BZnSpugvaHVB5n5it17 z4K2iJ$gvOK(Bfb(H~|iV*Ff(_ZfMuV94vep{s+s!MKBCT`f(SW1&2WIpCS(yf*S(p z0}Da_#|ST22!_BCFak!w4lo9GgT3GYI1a|a;Ky%h3*g`&?!MgRigUz5&nwaQEbf85 z;5g{z9F=R}6u7C7@Nx7}hu~ilPB6%U5#BrSHwVQHfW4gmHV=-2u2;c>e$dZ%D~bg9 zWKk3x2d{uZKG3oVM*j(aaGLP!KjUAp41zH*4EBK$a0rZoV_*!N0(-$Za1cy@aWJ2a#^YciI0cr1vtS*#2sVM< zKTc>-&<}Qjg_(JV_@M~!Y9w5ylp=C29GnBk z!34Mn%B+v~Jn;|u!BVggtOH9xnGFhpQ7{a4ff2ADjDo{p3>*i0!D(<1oCn7*5Wmdg zlstxBa0=`MV;7MJgOB6>X6(I{dT7y*mGC|Cx@z!2CAM!-R^1B`>+;5aw{PJwZ75u5U4@I1BcJi{LQm{VM(e{opiM2+o5g zplc8D2KvD;SOiAEGB65;z!(?-d%+HH5bOry-~c!d#=$9Y0t}824lo9K!?^o(>TNI# zHi5x!B(#fQ$+M&7!oMLugYmzm z{sDtmseiyJFabusPde9Q{|D#=V}DONf&PCaoxtcc`oO6lCA3*^_Q&Lh1BCl$$UTVu zpA*mE>^$`*IQ|Rb2b=7y zFa{2SePA3M0>{BIa0;9PXTdpe5ln#IMZ$Fuy;Ox^ZZHTA zfMGBW#=r?M4qgMt!3A&%^d7=pumD^HH-X+?lYXEd41H&Tr{YfYBS&TVM>V27AE<;S=OTa2y;5{Y#V=un?RFOF&l> z?tp$U3>JYAundfXAut9;gkQ$L!h=1+gM(lk907x_CG9F024}$8tR-y`Tm;u2#$Vng zEewv|wxsoe{^u=e6GxE)XTZqwm$d6(6x@hH#W zI2Zxv!5HX0jeo#RVBtaB0ecTEX=B2lMo&BVF!=`bpCP}1kqb*&@L}A0+mhA``rnB^ zz{uq#t?&%bPcCV^KI!UxKkk9yXYkiq{M$#q0DC_~x`Tt@0_goP`6-G!U@15b)`5|a z;D2xt>;=R9GI6r2%raKod>4`LsTf@R<=SPw=%g}>za)8rq}`x){D7zOh?un!i3-YbL) z>;)UZ*k{Qn@(d1w;m_e;(En$o$9eRE0k9V=6C5HRfc`Ha4~~O#VDyWm+XeVx;t7m^ zF>n#=2gm=M{3iUDNI!5EbUlW?FOz0$7Jdc)3;$KjFOoiB7dQpR!IH0$ zZ@?H>^f)*|e1j!l$6sLKv*ZtX9wnaNO1QuVa1e}vCEvtf;MBL5v;?>a=D!X82TNKL z7y+Z;;y*2EQ=osEaDv_+k{>#G2AjY_FbaCFQI5fqeYpC!QY?6=n zRtCnw2sj0HfU{sXxHyD782vo%$}_mWAM-EbUobYjthLMYm(ULu&SL&?!uR84Z6oLh zi@_2w2nNA=FbuYW5wH{N1$#mNPnNY2a2%WkgFju?@;`w)U@15V)`4-bNuKAHwLvia z@Awmp{hWCHB<{}>E-(g$<@p!52M&TU;eUxc;JAx<$4|p&vCjgW1)D&xThpSTAM6ry zupcb(vajMZ_$!>mk$ey{{A1gpUkumKE$?cmg(pa&e^gdTam zQ`3q*kN@~YbQw4chCpwzruBg_a7do{64n?P<}-Srf#cwYVQ{mig+c!V$bA{}{n!_bYT5)C1+RhrN7-8wM=w|k2EjTo3O0c;Fbej9 zU0@vS2dBVca1k5_{T=Mb0ZYJnFbulBf*#Nh_JT!V94rH8!3gL-uW8-jA~+33`MOyC zSMkqd?DYZ1Ibm-MjK3X!ehv9cj2>y8&`2h^Rn|uIiJXi?Mf(@Ym3F1e1um_BS zL*O_#4SJu%-m~yv9T*2Yz$vg>_@_t@d43=Mljo<2uTh?Ri6=1n0pbY^_K}akFc=qe z){k7@#2=s^^s|;!3>JbxFd}P9U=-{G$H6|(`%%ILmVo184o-t{(DfI{fqpRhG4z2Y zAIIOI_tWSBr>>B1z{qFGSAU6rhA;;Q!45DEc7x;K05}E4!C7zu?EO6U#*ha$f>U5A z==}ou6%2w=a2&h>_I{Ca2gbn--+~|3v~qCqOXLG^@XN%vJcCo9H_qNLumtq}75cye zFb0;(^Vjen7zPJ~2S>p{a1xAzv*0Y~`Zjt&KNuV#|AM_>7#sv!z&LmjTm<7_uBnlW=_(JzxPi z4wisZU^O@kHh_y@I~e{;$^+O7UIhoi888kmf{WnBzs5gfq&MgXgJ1+~2cuvNjDdY% zFE|8FfwN%nTa=>-c(4u}1e?HdFbd9sU7+8k|FSOZc4c+sXT4;@ZR>lo$gj{FxrX0# zL!0~~ZhZM$dDYPS^b}zUMWsFMNgA6#GH0`dzG-xP{Sw!Z&c0?_pg->>Iy) zFv@l1)D11y!gX;!2)Ewg#C|{509=7^)xP{%-+Gta$GOD*%Y@(J%kOsAW8!j&?n$m8 z%x(weC-SpgBX9?WsP+ZiJ9B;OEAm`L@=RD0m^~@?N>|WVaLH5WD|WZ$`U--+{7NM& zdNv{3#JZi6o@&enF=L33(sNnq30w83@FRO2*=-{GkS||!cfpmMX6;Nk@#B`19|tk( z!0eQmnSRuD*IIRpo=Id!SI{$$nX7$mJspV2XP<+}zD&{-H=lA7FHd?ZeSs&umA<0x z?4Yms^17g}^wMoXU&vkKtFQ4jxm$cKb-wm$U&jt#XVyN&?ew*5^|kNxbyPy_NA&%Q zKI$v2@D*41iqKf;E2vcQj8l^DdU12^Vb&$NcUAfFFX47EX_jB%Tfa5$5rvZHMv)7g z;oNzVThWgd;>T^ia`ywi(wYn>B|W?zm#YOm_lX{Uy<7!wy>PSZcu=~xN%*%U!;kMT zuT#3W`i8UI&-g~Ns(fQv?gnluVQRsx5Y5K(7UTICSb@2x$eZgc-Rvu_NsoiNJomF0 zWJXSHUK3s!#$WRtH?&ev_ZP8eO&6=b+C4LF-4R1oQ@!|KnToaGo(6YIg zpKwKR<8ZerMlV-oaAR-}2q$^ACY5LFG4ozztyRoao~`i}T=s0GTx*%-3*$kvLi zN^f_S6ligG0JCn)ULa=tB#+18`rwWVA@1(g-7V#D60>>C9uqU%eZpPqE9mwFeSyo~ zps(ms_EvI2U9PVProzm3)CJXuZy>a$k3e7JV*UG64x?k$p9)%@QZVP-{u?3_WU->H<5kOHneR$D8|F<9v9nBi|tBmS7JMe?MiI##P&m&zSUw@X_!@FR%z2*iRKD4yEkLWeZyue zh(@us6I(SF|Fk1qkL(d#-*bz8cbALj@A6=MC)Qh(lVjPB8_!z}b*DtC(&LzGtMlCL zh}U5Kq+y-XJdwTGhzv8pRZ?@L(^Yw%vl4G&*K|tai$+2Bk9!+86~gSbS(WHrDKf$~ zmCE<8A=jl+NIWI+S(R4-D?zuqzu!=B92D18-LXKy+4w=)S;^b(r+oR3QFoBrqR||ey6Rjsm`O#F4}4- zIMhB=V_e*suoNR%sO}`XS>Byd+UnSfBRoa;*PtQulxDV&$te?`4-q60d zLHEm3;uq&KOjp5kT6AT$f0cQjA?LiiEzdK*qDfq0DN~z7y2QB)totl`f0Ff8s(w*% zPK$M!ih@4GRyr^L;;5`LPu16T=o%PepOEO~C-rL++$h|u6{B-exM?_P%j75i=z^Pt zTNc9B_N4k-M!i|?{u2JE%BVF-y<)6Sp{wunH?&V0y1Ly|(wC+FzT}mT?K^UP0di`E z*|!z<*ONB>FWk`HmY(-i+h=p1c<@v^=e_DY(G^Bl^%seUbX}>wW2%o5^fh_bt&~Ed zYXDuFhHq#s$@JN}Qr?kP>2}EnnaxS9^4C1NLVW6OThd>tv{qqE`D-hk+5dEm%Q~>6%1W>(}TX+>|c& zcOCOH`6xo%Z}ib0;!BkuqEBVdlhw)oP>o(TX58+l9F=X!b9b#^fZ95)81A%;-q1dp z?q_;-M!$=0yD`?->YH?XUg(_pYRLAgEKQ+rgl{r^G(Bxnd0C~i5uZ(-gQ708Fl|%u z8F()30^b#?PUgQ<-KFY>lrE{ee&OsjJ^!_$E1%Dd{VEyGlrCHTV@&1wYv**<^UMgk z;(VE^q$H|jfj_?Nuy zPG12-ilB@qq&`_dw(R@#snhecQ{6-z;JIpVS5U7e1TLbhOePuFANr5w~8`>`%@@7VU z>dbP#)z_6(k7dsbvbd{YJRSzyVJc8tjdK;gD{An%7c+Woy14tV4e8zBIg50aMS7QG z;Pg5v@;v5ieuzKu^P2G#a(~ZJ6%Dr36~pMNyG9=^nSQo<*E+r!&hq??JpuKZhAOkJ za6RAUy2zf~|G1fa*zWnNbNQ}NW1l*7760!W+PBj4gRS0CC4C_%N-?snn@q`S#D&y#r-304hd7ijU>33?1hl5AkW7fY>eEN8{6Ye71D{xPK6i-(#Tp!#U6r*!j;Kprs zN8!fcDB9{Lb|>LR;b`iU+$`J(oOLXG9WHLOyMZz0FdSXyq}>4Akc}&WyJF+2;RbD7 z1Kfa(YlrK%aWOavuhrc?xLzAK1lMEZ#^Ac)tl^!4>#}ika4{Q~fRpq%BI(O7$d&&> zm+RUuZfGp++)P>hpi>4e#%eu!!oQ@?Z|Jf1LCNy&577suSDNgDik)ukOk(G~E7&<_ zvm^ES7W21R!_HIgRr-fb?taG-N8KX2HoS=RN7s&(^1HQeY4^NprMjRMUG?aS zvv>cZBuuJJb#5CdsZDr{$v|8C#+c_TZi;FhMRtc#WZmyDh@A%E3gN^A?u{py_fGbC zR_$ASAj?<3&DXTssZ+4ohr)7nZe)PcvwTB)pXj6!y)1LUm((0yBXfA9otlu{k-TRT z;cW7uHcxakp<~{a(0-Pw{f<9#IOcK_g`{Dwe3FOSk-Q( zQR^ixrf~bpZ3*oiQZc6Tp6XAh`t7P6+U6Vf&@UMAH2TI!*a=USZ<0xZX|5U1E(|^I z$ilP^)9sit#gjB;?uPpXuIt$B^oEhHF=QJ-eD8S>tikS1>;|#B#n5~JvueZ4J<1I; za31IWQ815a*0r$AqpJ}QR^h=D*rL3Tpli(2VyJNsb8`^F{Zqu5%)@tjc*MtB4IgjD z#}62KcNl7!GDLocD?MZ(oRN_2KxvzUPBRv^*|TntXaYjw8|E^gz7;D&A77~Bw?HM~=BS8UuI z+@Ot1z)5E)WkRI8OEwrDfX(d=jR=(DECt4 ztn+PD>gIggL1mSy#a!@`HaCXddhEsxyQ|b=vewq?)aFWkcojX9==oAoPpU1<7{@iq z%%nr_(`X9|UP}FSdqS&7wuLEOw)r5Lck!>*7Dmuj{k(+s!2eHe;S}zbzBKU%Z3~N7 zY?gPQf=4fytKA3WuUi(t?1}{MMC?2rjDC#3y0CM;f~CPIn8EFh zuTE&+NRG)?2}_T(g?+Rp1D=rY3a!>K&CCd`)|h9XugBy0CX*Sog9Ee%S7;N5soh6B zEIWaEOCt?a1uck3S0lR%S(^1Lsu>VrQFRQ%W(;dE+=dxrL8YkDqNoZ*tUuvc4-V;G z+JbBivaBTINXk)>X)yG;zrhU?1Rj<(9A>?Pd5@cd2Q9v0;o8%IC049-ZyhnT=xvJZ z^yqtSdb?E$*9&KDd+OkNY;sL--8Q)>T$fF*3od4p>xb)vV>(a$qA3tiuV<~*_^$G2`@Jy9Sx?JQ6!S};w zdw5WN?^L^~+F&(?RC6ZVd<$8gyRv-O**MbaJ`B>4WY+IGvwe&RTDjQ*;jVBj%C&hN z2n3OA0xJ-7HR0qW?r(ZyLit$4D})=daiwr^8&?N63}=>ADT_^_uQZ|W zRk7t$`h~KWqL}6TqB=Dl!XTU3uj^(*BJgiZG(w5s&{>8z{C zQ$R^}2w6p5A6}7j6pG)%dxS<=ds6nPJVBGKuW?_>-sUU5=qs)Bm4{TGX6HiDW=nO% z(DJG>-svl?^c8Qht%X`UrqgRAAXZKv(y{y z@Ymogg{Rs$MtvgXq6^-Ce?q%g_$FVz#CtznIoxlBqY7JPTp;U}(~c#VIyZ{-IdmE0I1M3$)<+?AD_lshM7I)0zw3HLj>k?Hp{j!9OPSDA}I z2sO<1^x9C+!DbO;TOPKh$D_sSdkZU=GT=F7&N;%BGlX|5p zz1=B<8-cU#!z+c8vpm*gM}A_r4sIBZW<~vkYl6FKvm1q*u-WZ`lQTiA`ugF<;H+)n zFr4_$+GdTziT|wJG@SU)%E|dJ;y)|rdM)!OHqH-s#l{uE$r&bAyK;`qfQ<{m^~0HM zAXT(WzP)v2OG3L}Vud=ux)#vpD}bTV!${^)dXODOHe$#+w^vMyN<(FtN0GMnDs~sJ zd)Tn+GVv^BZ;P?UEnA1mMfy&q#RKzbARxOZ_2d_YA_nK{BVUf0AlXj5+2px9kI$aM!VW z)G!s@G*fLe;2d+^3(WP7F&REVd7pGw8+MqOMP`8>@0gno z^8}GF$v)a?=747~n8We{malW)DYLciEy(Ow3MMccb3ei}wUpca7&lBzyWQoE)kI|; zdlzw5*UmuPQM;s7`Q5bVp@jAd;zW-J)!$F`x6}5AFn8BcEwxyMucN}(&YJ-aeQjE7 z#z=~5Cu42P+Lvc6v0KIxb&LqwnHr&0mUxs#=Q_1#Co>x|uvp0IaCB7G z4&*0k?tdNQ!Ei!*u@J_47fhA9Ho@n^)1@@Zag!>y@+kJ|Fb`nPG^DZ@!nBav2z(Lz zn}t{9pFV&Zn>LxlG$q%KTsd-&i=0|-Qe#-zGk(fkZW4Fm$n_z|G@o*37w$+sa}|C7 z{w>0r?p&5J@Fj1vxz>o0$X-V_j_emiHhpdDgUR(LQbO{3;p@rods$}^S$@Km!Uf@^ z-jbhib#UcyIf~J_Cb%*i7d7-*<+|WXY;yf@#WrpjZj+50hbyvi({P10ZXPaR<6I@o zkHJ~%bw6AIoJxNqY(;RQ&nj02C;F`IQ^?TQF6DtA)r2bo*RhZJKOt1QsCNNsC0*#E zr!DJBK9qK#PuzPTp?%!2W7OrOrDY%gTlF40xg;2 zn9XCR@|f}qYnqptSHmyH%Ly5KBMaYvpXw9Z{}g@LG5X=DGR`zYa(`Nr`=b;vN+vb6 zt!^*JdONzVqU-C1E~9-zSGVk~zbxayOYApYZ9Eo7NA>=M_7l;e#s$P@MjK=F-9qjW z#{y5@?iTxRBtFpf<7EHbX@1n$v(lO6d6#S*3e$gbFL5J%*r=m=cKy{MAYD7Ee7)b? zfb=7f(m#~``3|~Y(hrx-F!I8X)Tcf8r|wX~zIRWJ4^!=mu^+eH^Xn|VLOr-jeOf71 zo8+l^boVqRv?sWibhM2ZFSC1G_Cz07FNY)>WxX61y@~ZV>^|Sf*HYfg;1&)iv`eCo zpPwrP*LozOeMAVF8IhAb*#f@^-zdDwcL&LoRq9=l7-pqM)BBfwa3yfo`eg{N7|z$!%W>?OW6aQK3t5P`epOveF6aQJcCb&&D zE(%v<VP?_s9iLsWf0RX>MmG#+wldL&)v&?9FkW=r_o z-}mJo@vW~Gon_P?rxMz?<$jmwS8WN?kxzJcCHHu5mVGbo3z?1Dx4(P!@4o*7td?-q zp=%Od1Kc~-uUqipX*B>#)~;eNhP~3Zgx0o-y~oq7sq`MjUOV=z@j40D3TKViS-2KB zDaY~?ybc$E>$2468<>2XIi1c0;2IuI=StwF&!ltJaN#I>Zj=kU8x3%ia8@_k;p!eq z=VEXZkEU~daB^0pX;;!}2yTpXAj6WGsYh0s50v*Ru6xOo^_gj=_O)L}*VNk*+TL}U zx>Ds<zxVBBNe9dSE>$3l^wNriuDS4SGJ3GpAFIU0b~lM z9`ZqhV<9tZGaSPh`UQGlp%s*#hFjxC=6=p0B$IyTB%ZumYn>ddLGC1Syora!lw1XJ zjQZdw=x|J`PRFCj%BE;>MD3Y9$jwd&SxSY`y(d$I;&O)}CG(!!L`eKA^I6i_k@+o| zlM_o@jrp-g{LS7jywQVs%G*?d$m!k|nLWtJCh_A+wliz{3iZhhQ57H-_^!OCLvJwkNrF;YQ{9Kd8Z1`<Y< z8N*7M6@D20D*ULFTTg$M7>L;feDr+@?f>Cke!^WVr+s=ly?$7LD}%Gv58evuAGqgY zM}A_r0Ime?RpJgm;WojA-k;FlA2PUdxbEImITg7u-1-luw>d3vaz?4y4uz0YU+saL z;eO5XTzm3yKdvE;TCON1Kfg*YloY+aFV7mxH&j8?VwbA_rnk4A8Y+S3^xR4 zO+PvR_KMBl({O`u);`=k+V9uZn!ZUHvl(ku}=+D5tLc+&S;=2G&&Pq1>AHr>d zYyVh!n9Jc>;jD6DxVeFpTs3xE;N-03J~t0*^l;S)AHXImN^MEKS!znl5&D z(YDWkb6g8t*SR_wi0J#bcOu+~tTcZ?WSP?DnxnIjlHG$0^UJCW5;bJl+6*_D6#=E; zr6)f@hhb9f%Mc~Ch9#!Fd5cH(YRn)w$F;zf@`OI`t2>~#!MZ07U_WR$!qmBPL_E3| zeG;geRRXmeyStDTw`(kclIi3s!|i>T^LAzygF6X5Az)@-F%fLY4inB_$!xPEtdAln zVLyc+bM5rLbnlD0Lxywv3{J*eAvm^wlO7XHMNQ(!>d~mEpY7HQ!abLBDn{a0j3Ixl(C{3`ng{)-VbmOCi^4h@Co>f$hhbYN;#3M9zOEL zR2dLX&M$AWah-4tHm(=0-o{;l3){F+xR8yTgsX!y>jH6i7Or5JIW_L(C*ipcHwgDa zA(-Q@G3Lm)=v#6K751Pdgqe8ASWXF*GvdCKJ>jUmjnDh1GegwJt zKTpj)NnCZnh2dtBWmt_FQsvLO{>*|6yHgx;4=EC!gGmrrGUF#KX{1wQmMy^k=gha>}By_tH)AxkMT})$o;n~ z_)d+NFQRJ(UFXF;bXnh9J+Ci9urOMa*WfGI>dRM0;wV4Fu~+m}&auu;^Mi`Jy5yK$ z`GI$Q(Bu$vM1zV4dC_n5*@Q-RaJf$CeH}mCbqm*oQ4w6pDErDpPR%*n_Hn3rh${X4 zm~wZEqoZ5%Sn6}|3@V4Yx^ZI?Hx_(6IK{1U!0U`0L6&)_9`p@eWj;RHPTIr{Y_sP}D!?w#T00ZiPB+(_q!-3`<9HD|I; zgVL9h{-uZ>a?B}}c`-@<-SnsD_zq684ol4^sWvTD@5&t5isyXD9xf*1nG5T~%^td_ z`m-EnAai67f->(X&sI6Spz>=s{>cCL^zq67+y*%7cqIt!E1t;~Y zS_j^yuLBqHcpbAKW)F#(YJ1f>Fe|xoid{kS+()T53->VJhU|+(mY;B?aJ_KPQw-xY zunukju1&aP|ADuFtYKg3h;HOMkvpDj`&2$uYrnShEL2=ala1@482&@8*odXc?iEWX$kE&YllaslHK2JvXVw2 z9AVHoMt4C!Z)AtT*TM5pg;WlCCmGfwmlfBexID>qxP|9w$%FjcJjTSQvZ_n zsbqVzV!oGZ)T7B_Or|pF(yMbj3sCN=jx=~Im9yV%1_#Wvi91|FQve8%Ztr3ys7vLI# zYlnNE5Xtt1O;ASrA~KW6%p>zIkx_22l$LzUc#xKZK2-gRv#>Fd^XQRJT&U(E>n{C)#ik?>V%%JD6kzUre5AjQ!*;(#+ zGnKln@hivf73}(7%{N83mvCA8T}eu=|6}Z4$>(bX31)Nk9MjlrP^8*98}Y`>yD9 ze^$*Sm2*1)U-m!v295A)j-^iaPVVpp>%y$?&ZV?-e*58y;f{*DxVu+(SMt&bW--hj6Eobs*>irSJz2!= z1a{9S>kV6drs{5$pJlqybFXthL)DKGXTgK?Ctu5Vs>Cn+B+l#Mym#>p9wCggenqw& zz5)Ib;rR&{gPVXe<2(S@2R9Cvnv0SMIGrr(#mI~zQ(U~1JXfn*_HG#GJW8d)jANqbggRlE~-$@7(qW%HxB zX-?Ox`X+*%^{-pfm~uDMyz0xw?GxzH>lVt`6S61NI4d<-rkU2VmuaJ3kZ-t6q2u#w z=xCP{a#qmMjgIm+@cp8s4&pZTKAWYEW6!074oTlhbo8Tx=}ni*s9R>?rs2ATWAR_) zB#$QGLvKv4bAVQR2|nT_qi)EX|?Af6tO-49B9{We=}IUh*C=(+s&1 zF8>kACbASMBizUuWqgaY)69RJOK+sr*tQOP<=Cq<>}Bk)RsKAnY$liVkZt1X!fqJ5 z|83ZPO5Wu(-V;piAu{IGnG?L|7-2Pe9Hdd2*)^`rlYBjo8wr5-ZdDGXJ@6jI z{kQO)tt=j>(~PuA&G+DMVF&krFQoZ z9lcDtN&Ps0u6c9?xYzr92UPu7$m1wx3z(_7d)4-;`jNiDLuPMMZ~u`ixNk{&x9Bi_ zG=Z)Jd=LD`WE{kd$5YH6nRzr7=W4FmGfJnvn2bxoB_o^FyQg0kSTktUNgX5Kdz=g| zCEp{ExR!4}UW2R^@`%NK@ZcaC`ndccs|T-55jP)tyVp^OGc8Q*d)|?^$oU zu{{~CRM}JEVm^y)j(uKUFd6VNeb>h({2s3-;XEQDO(a|;&BSMYDqKx))o_JyKXIFG zk`@{HSGlRia1YDsRAxr4$YajHfmiOzcQ1zzETygIkHHPWNl@h{Wqk_n3fw;lq1sK} zL6dXa%H>@Esh{=vsB$qsMmjyXq`ln8tJb>3+OH0|f8$s`i;f^VsvDNHCnO({rccWJ zkVBuiT&C9Sbv&b1@pBhCdXE@=nRGvY-|A;+hvd7S^+%VqdksGu?SXpFhd_EZQa-Jn zkaFzIVJ8w<(*8y|S;~*9-%c4%Z{tJmzs8TY@dM{!O1&uG4_!FPclpHKwEoB`ix=Jd zlMjSV>YWJox=t-=&v5Tz7p>$Wxh}%zx230N58MX07m6%DksE|7u*r?U`QglT7rCp3 zz7L5-e!|JOPWzGjXT|8;B3#Sqba&Uc;7>TKzKw8AaJO0P$~RLR;C^J`f^bFcd}mQO z_De~Tgn`x-Tc=j^4HClW($KU|9req>!|mXdR_D*SL8;Bv%1zl~graO>fQg<$R5%^P3rnO9>G zNiX@%Yaiwp#hmb%XMSvgcb#3*-^X|VoiY$Ng79y!qr{Naio^QwnztZWD6myt@iNTq#_R zxWTWQYZ|Tu?p_ZMDxOp4u%y~xqdso;tfzT%Y|^F8Dm+R4?pxB{lGK&z%Vp?dKJA4o zDKq>QMPI>3=#MAcidFQrdycKtREXck(ADtKCC#_a45w3HBh~Kw`-&}_K4+faN*Vbi z^>9WV=U(y5&u=FSZLR8Oh0!rFxTO6BUUKohr~HGy^-``{;oCp8q+Jl*o&#A@D5S24 z!B2mBN&mi;lttGPe-hkW^Gee0VsP2|%4 zVK%{cb-x!xP^G;sPSW~wxt@g_-&`(xnR4m>%;mBVDA)4}H+QvATYT4Z+26$nM!ND? zyUII_=}FA0G2=~RWyanwWp+etekTi4UTn@iYUttxIyFB*;TvvGic{uMla^j;}Lzfu`B3EI^aZX0w0d(@29p%&Q zpv2&6KKD%AhSYX6Tf@cf`8;-G5QZv4mzeUlbe@FTjC;>}B!2f9zUM@Ur0+Q?L;1b} z-!sdbr&PTYVZPs!3a#7e$xx*44x@s$a!+Ql>?}gupESKP~r=CTel|9;uSNu zS-m2`*zDjH$xD(4#U*ii4=5RYuMy;w4t%jwd{K|7Mqag%er5`Z9NTvCL@sQRGxHbg$SNgGeJ2TllsO5J_)#Lh5$+*4i5a#a za&_ia8SXq_@ZwLAHS@RFk%Tt=CPmLQdKmIlEIvL^6C(V zQb#nSQ0fTDnbmMoY9EA?d?FQ&lyk}SyA4j{gcHSwK$Y=pxd}l?sS)3q34H`!GL-mQ zd}d}#wlv^KDsjz{CY3-dJjbG`j9}HpBBx$#k7FpQCON{4o&$z5v%WETvBNyZ3a$qo2tqr8YSdr(PD&24CvXd+9+D0T%t^} z2d_RUk6Y!;N=>r1$T?+8$!;PiRreVjmq1HQi3g>+k?KdXpcEyx&*qw0cdcT7-moWb zntqcA;hPlrbAU^)9warzZxV54SVhhZt0~ul+tOT!Uk)NG#X%}-Yd*E+VKdD3ysaYm zD~6+zEhMi=ogi%%r{_6(!i;dqo8noiJFIP>Sr3>I&Z@oMVo1{48>F+Cd0x9#^=YcI zR-YW>q|(?LkEWcIOS77Fif=QG&G@Re1=Xqgd<30llP{U~2tJoKPTD-PVKFmq8)jzu zgpBmr4aW<&dYrA5VJ;vnb%pd?h`;n2$*id)H(678AC{!0l%CRB4oD|Vd?*!9h684` zCJtDuJE>H}0m*jkP1nl=qd2{gNa9E#arbc}JqAhZEyNUuZIM#a!s~n;ytVjCt{3@C ze)Ud}#Dns`w8_?Fw)VoL<&f}5F_!R1on|JJwHGFGd+N%-^%y=Tb^$^pZL%BJnQC;ePsEb9dbv3;(*XOzWlq8-vG--`+Ryf`4k?-9f#w6 z03~+}j!q_h{YlKGk)6pWx|LkBg`+Ei*+TvaWH<*XpO-qPS(~2BcR$Gur7geRkl1I~ z5-DB-Ks!R^-0Xy?M2Pnd>v(DSaYfd~T0_8toF{8e~o^EHi zg%@v{e3QivDU!e0Viva8VgEorDW%-EyCczKnsE#gx^+MAf~Ou*c6Q-T17=~&B`0ic zc1&G%cOo`BWbvfmcyA-==X8EDpDm3l3+~1pdiR8T8o$gCUvt=5Al!QVko#|^ICAyf z&L@*@m-H9ENXDVxLH1k#b-UtciCfbyuLa-E#;BxSUI9k8ZdcBuatxqTz{O+2)2B&O zZuJ=N)sPTI9+#MvP)RD&+}&P}ftmW&SQXw>E-7#NoIXfesI5MKE4t~?CUx&Jbeo}1 zRYBTT)u+8!4T3 zImuzxlE}wT9py~F?=t+j$B^4^*q1~ScTD>n=9lK5y@nipIN~uCf0AoBl~JWUyXkJ1 z@QQyJqOF=kOn0Sbk$5rFL-bj5lK5Nng^;tRn?1MgFyus^Y zH>^Ya58?NRe+R#+dB>u+G2fKCoc3N-8C*}^a@u-K2(BB>I)5L5>$Ayq!1cndr-_!I z%xiTU`d*|Mog0AbN6wssFUBlx==+7pF~4Q(`(RG>l6MCa|rJX!#;IqZZt zEU!5CEAzS=I$2*rX03VMQe;b!EqTsz@>>(eygZY|$!|oL%Sphttz7E8u_$&&u)Efr za6fXzzU4LNgvXJqM$S4XJPj9yv(5?6!-e2-lk>Q?c|o=B$CxW^@;ob}@XRH;EozRl z^zF=7-@cr&Mi7F(XyfZ#thd0=!v9+q52-oxw7mqaL>CM4$t?`QJWmKeq;V`osd>>6 z^u?aPtncYE))ypQCgC^um$gI2-0~CjkJWmKblEfeZxD5?-wp>ci*l1@hQum z1=~C~@5JVKPOtY(gr&5>?^9kx4PI$=SL(e%CFzyjJ+0WaRIrT79 z4>?pLIcE{$)7m*44vCU!u9+NOR?XqF%{jaQnnRrj&g47@cQ(oC4sww0L=IgJ6d69~ z67@8XIlLUI`yk2<{mUGBd`i!8^t8ZH4RIMl91NM^feQx5w-^ZJ3?7@~T0rY{u#-_h z7o&t8#@Ayx)yRrx+2B2mKuVaeSYs$2Oy?W0#8Q^-wL_ppj(CUWnBY$nX)`$w!Fej( z%5;)Y=~B(0xMkY%{KN|*(f1&zV*QA^Nrm|WiXg5ynTmB2LCWULu3yv6(4Dzp+fYvM83h1V002^Qw4#_*mldlFe1 zOd?tjAFVxhI~hfGk=Q*9mUUUlP3+K@u#i66#g7uPO<0#GlgukBC5_BX*9^xi-zt?P zD=>=Ud5qN7<>(qKjxIjB78Ebp#*A_^?;XL8!~yLvm$-`$&X_*fX#{}RnpL3_mze26 zaxA9-PU4=H9h}LLjHz<02ur5%yj|2tapt@6iV+X%NvJy{g-ltIG`-nOPE{gpTgzqY zt~IEJn2CvrV-*Wj$}=OEmiO>P8k7S7s!UN!WYZKdd&G4x6MCTqC?U=i** zc2`;l71@G!(S8KiZVN>=i0m}77r58kW3{(R?Td4tFdJgl5ixE@c2m{bZCW34LFBA$ z+7Mg_&TP}l(KiNH2lq4X#ck{U4RxL*4cMi1@>0=N_BCYg4J*c)w~P2d#}^D8*7KQV zQNGW%udf=pQtU*xENex{HOy7lSC^$Nb=oo|YkVrTy?@4tyNGsAkzXlX$mU z6BIpH;d|iwB^*YZDB+uhFW9!MuW76P(hk}CwoUp=625L*f9WD&DtI^jJ?xZA0$?ZO z>|NDYF!pc_XM29;^^Ig7^^IlAN9wYv4meD5n$3+f*$qC7+xUuJHb*#Q?*^Hz$@c7Y zbH|5wvMGq!w2;{?upGEI!dDB%vO|X0esBlR5s;;}Z06ubctvastvbP%_LKm%x^k6c zl))J+3}^HGIeameO(g?PX7A;hN;F&Sh_X{)i056P_(`7m(jV6ZIw#?DUDWs7FK}H) zPv^R0$np+FHs>@bouVtsv*@OgLbo1z@rUUL;i-PGr|aB@9XUq|LJzGd;X{Nfa0fvZ zTGKY$vve=AS;28(gcW1m?=(C3b29q~&!XClms-Q!{WxQ4tzlwMmy+m}7^nv&x=mZF z#pWKv?jcY-%NGJw3X2Z){JVLH>?ZYA-+L(+2h!`yA-K?k%lbEd47oA5i*VL4!xUT> zoV8w^6Mb-07wXr<-vpeiVOi^zKB;=Af5mx1&VX~j)v-IgEzdpXXi$+S%kNFy)OQD$ zwLHTO>)u^zqf6Otc5GU^yl&pTNIf%p(c|L^dKS^6_D~tzC|u{E<+Sf4PQnerJqH>2 zwQ$YCU4i?F5KiGt?FW#G@>44X$5&k=AHk@W6W5(a2Cb+MJwbRIm$jWY6Q?2f#ENd! z*ZP{a`RXftA@a7vFoc)9C2V83x1ou&Udg@muzk}}S2bTnsIK*eYF7@MxV7m?#)r7| zqP5-H%1LEEaCB=kZb{hqn23bUDQHc&)rVV=!^>JE>DFpvHMjF27kuF5TTTw3U-D)G zH`%sJQan<`sZuZ%y@?upU`x;Y^o8|0FSGDMRiuUN}vi7&xJgBxp`Bj|( zk=k#n+KMfFEAi*f`+0)u{kR@<4Ye$5pSd|*-&;wS)FIcQN5sX;bggHW zwQq_%;dJgl$*$5Dah#^T1GPn+?;1qM+#~7nTn{%3XN~7pxEUMQ33tuL^}EG<#YAJ6~BF1KgXTtAlEQlH{A1X!mVjN z!K{uY+mq|)iM+$;vm5Shct7z8XTF;y?gik=E-h=JwdagHpQ4j%51lwD?LrHBf|r-I z>73Tug_Nmr%#D_^dI9DGX=1FF97HRB@j$B;{02kyPwD z_pMh4En{t%_=}^b<^9Xr=I2AC+8f&*V&zZy6qVNtHH)n>t0t7VCox?oo{&#%nW=fxsr!=^ETv)!&EHzV zJ1L=5@b!e(pZR#Miq8Fbk1VR{zdrgIgUiV?pNx0I$g|yE-YL7B&2~4Lr@+;3>$MTn zC&?nf8_t9HMLvWkxkPG6$z;5CQA|h8Dk+B`)Zykd?!`Z~tf`leR2kWp{yt7>u3ygO z@Wh|9LUBfyv_(a{R1q0l*51gy{G@IygX@8N<|gX2N9m~9^S-nVqC1A3xxY&9KlH)P z!o5r!<0o=Ma0_s+RE*Az!EN|d^)|UKxQIor5WD?w?QpLWW$K*(jp5!|L zA~%j)#~O0dPoGDw6S;pDeYSo|>fAh4Z*F1y@=nJJiwstR_0o?ppEoWWAr+@4$xh$m4P#ZzdIC`G;>h2ro z_S@P3HRh1>+og$jX!R=d++V|hckVcM&Du&zF>*C=1(-}rOe||ZO}78MzhV8>OsX8o z*&ppP&j)3z<+tTMF0qW^o$ zktX%4ck^sxHWqPT9Sv?1)(jX49CZ%H@7#Cb~kH&0}WHzleMUZq7ko(xwx$1ZLmo z-d-kC@1WL6+EW=lVU-kYf>6(oF9@O|*F7GBkx4au}yz--9DOmu7*U_J@63b9ANvz|wK z#vC|fr0PFpXOVrgA)9&!Gx>e4;(e<6OTFn`iflXf=CRiz_CnGJrd`fBeQ)IXSsK(WY4G9uG^o!vwnCttvPeBM6+eO#O?lUmGmUu9qBuN zxa`-eG(HhVQ)INQnG>0|HTk`XWwa_C=}EG0GxG;DKc#!kzB<{uK#wTRn{p!kKOg^- zZX`BKe9!-S?K(=)&Af|eA(Iz^h5pz&_sbFDIqsRUWij9PiLdf(vMerw>-7-vP5$lL zbq^sAanE8L!E3BXPfJgjN(o5hMFsQC+C^{`mGiqWe^@~BwkGUS!oK`|=Bk+qoAVk} zduBh!$j_6k$6pBLvww~d_9$U~|KoP6(6#3sezwCkggr|b*H!$I;`iD74B_XeZNE_d zdMMxfIPtZdd%2G^344vOBl%vM2N$R(F#ay#GhW2nF|{W>K4$!;g9heBgr}LGJ}h?z zVM_^{Z4mK?eF?jWu=VMbC%!erw+Zew^(SmKVK-}#LaykbU7pPkZgnLay(`{M!EkiGx9 zR^CxV^I3P_kRHe2`)H96^MB%9{Oa0uudyCRce5^~e*=(TV3Z<6ZlRUd)oxvA5rNBF zLo$Nb*B_s_&7LLR8N|CWediBjuMw6a%-t6WuBCYF@oi1m_JnDh9YFWaBy4NKZf0Sm z|F?Nv@muunF7NsA4F8oRZ%)ExFQxm>bYyZ{uOh5W*esSwm8XRO4&f$-UrN_5in+$| z>`uD2ny&4G`%d7Q62+|a5!3k3xCXzX>yOg)QNC_$P0#=1?_lzS)AY(dUd9$%(_q5$ zrODS({obH;9^CV7eooho%M=^=JuTVsOQs!1Z(x(Km9TN_+oYEpziH`X{Jo-=ta^}~46#+|o>gzIUB~b27rRr;hF<3VF%)== zUK0XJwR~mfPMJZsy+ksWzC}43eV1H@qxT_GPN4q}o^poypZPYsp8T2k<}jbJ8;!&K zezp`jvkb$gZhI5o_HT#&!EL#Ku&oJWKb111PqQt$>Do+(pUYZK*bKt`z453SG??uF$C0^E5>P{IP-Wzl%=OGKHrH_fp3D2baj-&fRdsEKnzJH5? z*4H)~j?D?{bniFmUXEdoy(u?1d*Z2%z1gO(5pRunr!y~o7z=J9e|9jIvo&F_5cU*b z5A8&8z`SR#Jt042N|>Cx-w_MDN#62*hW^TJznriU!gl8}Ouy?f<2>th5nVgi;b&|$ zVP_JyDf82ZvHJ)sJ6M{q24SlRJ31!Da_pyw9d2QgejJM?t|9sFjvb4*yiX8co%lAP z@BCpbL)d)|#_|>ucK7>`w~F{0&b=(JLfGRD#y)rrVUH3P#^ull{n*eT-q)P_S>{WG zy+YW=^qoJ9y-V0j4j=pPW;D1r3H!gsi}`dP%Mbl`p&g^bcj&~=Y07l(j_<8MUa-z< zh&MyLuKnwT9YELz={kRy?^lE^AdGVb{xJ3mVT%dV@z`A0@ZSatY_Pxv3v95!1`BMk zz?c@uU(5*=<-?%zi{>Jy$!+@kQSUcDi_IUs{zvckYxq4)*n)rH_h$GV_!9@A3dS`>8!WKF0vjx_ z!2%mBu)zZVf3v{3SkLkGN@JZU1DXZRgBC%{pcT+6XbrRu+5l~Wwm^eUS#Rk8O@XFC zGoV?}JZKTL3|axLg4RImpbgL_XbUu0g7QIAplQ$yXcjaNS_CbFRzRzuHPAX}1GEX+ z0u9U`6Qw;h&=hDIGy|Fi&4U&}%b*p|DrgO~4%z^1g0?^d^FS<>51Il^gJwXppn1?D zXc@EuS_Q3v)o&4T7Zi=buD3TPFy23iMgfHpx}pg}Ln2Tg&dK{KFP&^%}nv!1zL zCTI&ZSc>vNQ=no&4T7Zi=buD3TPFy23iMgfHpx}pg}*%2Tg&dK{KFP&^%}nv!1zL zCTI&Z7(n@;DbO@%1~dzr2Q7k@K`WqD&>CnRv;o=#ZGi@ZC?7Nhng-2)Wra?2HSr2Tg&dK{KFP&^%}nv!1zLCTI&ZFn=_c#(&ThXc{yFngz{+7D3CP70@bZ4YUs00BwT4yP50|A3AT|{;8Rt zTQWL0GMd_N)?TyHyU+gAsQEej%X_A0rT5-VV1I%X^Ls)tE?BJ_#?JK#!T8|OSbT!r zFL&5!?|Y)%Cy(Nj?0$L_-zd<1=_tN&p!?ZToOZ(}O1nq#O#@8nnzG)OAtb>z@|Xz8j~{=7FA1aPOGFClDKY!XUgdA=n~VGgVI- zxcHVqc4i#DRiNh`LT=V)YkH0%j(?jVZoWJr*fv=GW*k4CO0XvpY!MUal!N-81?kI# zfKFjdjpNtT7a=!WYhv)$x)70Xe9mKhj6L~02;)u9&L0M@=TJmS5PXWhjC1tS);YxB z?e-af96i^fa`<@1xS-w6ml7VQKOL!##ebB5KVjs5AV|S(4f@TW=jor(f121+@mGP@ zH;>})0&jZ!8&SpM^xQ51pG`R1IXGB4{MImBE`guB3GS5`a+*~P_MoO{?oyqJ}S!pg9QHD67ZiT;D1WM-%h}% zO^mn0j0F4u!nwXpR)9X{Z~GEml)!&{0zR04uSmeZoq*q%fIm#Q@wd%oT;TKf{Q0Ni z9fE(8`U8Ka(N9yaEd-we{3F0yTMB-Oum?u~uWTjw0l>cuya`FosP_`;v(foHcDT$jz(0k3~R@VA6Lm`=k`oS*NOfbWrje<}gbB;a36 zz=sp?a}w~&67bs+a5ht1eeWm!alw|sf@kQ9_le_w(y+DX4}Tmee2Xmr1+thPv&g_-+&c=|gv#PY!tV zNx^x1<F(OCnbsF%FDPkYFD+sOtp1)aedThyj?f{;?T;!<#JgazB@Kfcs;2p*95d3M# z*^+LIYp)L*oc#835ikOU_6MFtyUc-{6M?6HD*QNh8?010{}%h`GV;a1TW?dqrw^Ie zd~OGx;=oIvJK!e|7~DXS|9+|RCrG(!w|}afyF|Wuev$5+LI;<*y{ex5_XS?QP3)r? z{K8X(zt|)42T^*j%Hc$XK2)6fd`s~~qRF))F1Q?c>k!x<_)iRun;r680{%(@&M$A_ zdZoKXpW`5F2kIbaNl*HvvDu;P~N?Bfwuziy^h# zF9Q$wW#;tJ{OV@lnY#u5vlJ6N1H6(FeO751n~R4d!#V%2^`<^c|#s0N&&mKhj6rchfDTUCLhbBA~ptj?;fd{vXJvE=aCPB{A;LmI+{2xQ}2XrIX zE4WsGouU6uz*|oWa4S0Ar+_!mkF;OpfoDD}a@21xRQ!j6zm7WJZE(i``1&~bi$_a+ zcLCl?;HM?XSpV6@;505EZ^5t;%m;23d`PzHKLkAQ^}CCK*Ach1UO!g;zl)!|3O)Y> zJafI&SL5MUG-YBtXE2VaUwsmIi;{EVr++&m%klka0qos7X0;xrM+h3+8GJ_ z-$}r)O~CJi{092PzL5U}@CwF#9beu7UWK1r0RE};KquF$cDYz!De#?ur>+p(ypN8q z9cXYcIpo*`ya+kz_oRPmzIQJ0>_Mx>tUIe@beKhZ?0Z&~gC6z+e2HFb)9! zAK=+b1phYhX*)@KrJfMK(s~^TJhNK(HLezbS1|ule7WKWh=eVn=NXF675)D##RQiF z&z1zZm)5iR^Dyv~H^2L%!G*{D|8L+=o0kZNdCSi9!VvbuiZ{PF#Ne(3`FSgh(&!Ha$)8qBuIsyNv!O0HLQ^%>J6Zm_< zU!64GD0RR%(d|6ol~)ANesLY}Rz>&^L{)wQyohy3wcDS8r#6uW6l>5r*e=q(c`xrh z0C@E&k^dm%A7ya-a7YjM)Bh>_U&6Jo1J7S0_*~#u1FwHo#vSuF>gn2Dz%zKif%^Z? zfHyF1J^?u`;MwUD3~~kVZD+){*GCOb^PqLY{{r|A175vB@DqS92VVRi!5>GvoSh*5 zV(@3)nP?;oL(VXtH7%dOABf~@UG%@!EZ&mGe08j zn?*cVe>leAU~)(o_{|0!;RsJbIf4IT@YlTY_bT93%zw3?J^{Q1JB*_2KdYRl#ZS(K zojYjYP@6bv1~Uv!;}P;D^^^U9XTBnGCW-liF5qQv9j}-m|3dKRXG*&q2sx`0`0oOL zc87KD9^AD3|#<$nD3HYuCpB8kW z-<<>bhb8dmmH$7bTwNC(N#H*V{F(2FJ$b*GKdXUP<_oTQ$Ya3sUf%nT%K2Zh^CcoC z7`L0WR~~U*?fePgb*#TB{yBr=W`}fxzXtm+$F(znx3JHm^N9aQkaIiuQ#VO_DgRG_ zSNx?Y4EQ~kbge-^WM7U zZNST)k_LPo{p-O5IZvn@*kKct`$yne?DtIvz7ZvgoZl9IEOs;RnWbx68XQax*%kbG zfuEir{~HPT_aUd^%@^vx%lnA_ z=I@hH87~8`dHrtd-9^t1Kkmuxjl^sk&Q{d8|!JP3H>U*h<0!v7Z=+?62T z`&AD10Z#_Y8Nh?xMZV?FFg2xi%KMVfiMpCcKV4x=OGUhMaquj}Rzz?q# z$=V-x0-ncusoMWAgSXe~qy+qA$SK|@_P-goo(sJEGci~hcDoXI_GiNXb?`q9Jbi^Q zegpVl6@MbC|382iSBo57xA|}y{RRB{LC)U5Q<&EmfFEdZ{BXz-D#!El<-qf|NVyN= zp39WKU+|r_kPEi}Z}7n|`sljH1HiM$w`p0%d>%JAemLZX1pH0NN%Mgy`XD?7DOyNdz=OHMZ~h)0-M1g`2I8~EpOb*6u@6G&llgqj;P~N??}ESS#m{?y2grZ2 zxaT(sa$W|1`G2Ips6LbSlzIio^EZZ^oq?yY?zAoNc?NeB#McwSpZO1w{}S+(gs0ls zgNwkQ_TtrDz_Z9#wSPSZyo&mYwS(XdgSQtjelMw49sYk1Sat&5Tp)h68Su{-yj{*0 z!QWUX^=g9UOyHRZM9x{jZ&CSZca{GF@T{l*L<)@TSHVxEM(PjS0B@Zqk^VId{1M=-sZw9H|GU8J$d~l7 zs+s$6z1lx>4NmpFP9&JW$w6$#0k1cuLThnr33v+eNB#4R1o@Q&yb3v4Z+>?Z@CL@I zT~O|CfCt|BrM192u#UPI{5yOS?TGoQ`t4lc9d}86Uz27FmH@9}-5ArP;1b|zZ++uF zgQGBqJOlpv8flmB;M!Zj1FSO~2mIf_vkwVB4d3Q7<5K}I4YhqTgtLFP&`-5qy}%pq zN`>x&AD#=m=H-*u0B?HZW5eKW#Rh*$z~6zK^o3IHIFvh$0y67a#|ESN=RUwI?+E|x z;?}{5z%$;tyR%ge@}GrJ?FNIl>v<3OtEkrjVEID=|2yE%BOg=!C)2=c^#75x%TxA&JQH~FCsMA?YwiYK#lgdF@V{vA zc75IefBjNv5Vae>e4OjmfS%fax7=Uw8uB;IPcp#Mh+|7p)bYSuw}^ZkUkH{1Z#*OI ztLrUS0S}Nj>Uj5K;92trm4o0yt(U=L>IMEf;>H}XtOZ`<2N38p2Y$E<<`*f9?^^D` z!0Y#mopoKYp!gYrXK}+>z-!*Pf0N=jNrBr#pND{_W{8}okl?q#s~G`afu1eJ-x2&V zG|c}R96}wkIXO1BV+ZP`{anJF<^El`BS>WdYZ#*jH>VCnsz^gbXtnwc* zxT6TZ{t^65tYawuTHr-*oSjY!%dCI#TxqW^$XNh9_=OneO}QML2)qLaSS|0d zxN)_?+x7er_{+~l{qrT2bF-AI@oF3;Dy(PgAwe|W?hHKe#_=P8S3fR+?bBk`phxAv zKHEaIl?HFu^IPCAB0p4n-U&RlRs^d*KL)&htJI6!#(e(U@<;2t)n~X~?Vsrer~Ocj zv(JDf3%vY8vBTGZj{t9Czw#vD72p}nySD*;Bk&Z?nI8%K9^ir3j?Wn!Ob&TX;A&urjn#?bbE2H6h&@KfNmt404c;C~T#$J2rj0)GQ|;KkKRhe*9z zn3vH{^V!be_~DR`gTH}(s_lME0)H>~(-?QOzn=rVhH*KEGOkzokBR=*0e=K|^KW7R zeU9c8;FTrPfXBf;?*gw~CV-yDn{p`Xd#2cP7s&qr@L-zA(KxvXc;;c@*LFNvACq#S|Iv2v3@=k_#VJBh*v4t zAqPD5X91RqT0zO+xY;4+B;XgT9IPj7g?nxSUibF(>Irgw1^xgVp_^;jG=Orx)Wkt% zq<6v2z&mih`V-(k*x-6|NWY+Ry!m4pcoFN0JK~n}fv1s=Z36sW;N^|Q4>z4C7k&-A z>W%Mf4Ib*D-!`YfZu0FL#r~(`!Y6>&?vw^80-p~&{jlKb|6RaSI3P0}{HFqMLLXfh zx&(OrEb%nWqwZ7r$P?6W8wOXcL;8mV{2jeIM;dM*vS@ol0}a zF9NS&|9u}6z0BaD9{TMY3HW6R_zjTXJV4rU3n9Vdz>8St*%$@94!n-_QjHrME#i8$ zf3`I^<-I=^2hnqnyCv{{3j7s}`|59p0&n(8yX$(+@d^At!)8gL5XT z|0%$0I3G0?_ne;~=Njer^!WwwdPVek75@36@?(BF8S>u)p251uGT>8>h;R3l!Rh=t z#(kCZyv{GaFXhtmg86(pLC%qoQyUb={4em20x{5)yS;OoG%SeM%r{5Jz{V*aA| zuYs50=en*ti6&6oPt)GK>0`h%SihYK`5E9voO@phyw~9P;gD6}FY}8R=<_vPyF7va z)&%^1$Z2}(w0{Af`L)Q`II}rzpqP5yDh7K9~175?tW^1VMDDVpM8C}nO5qRdCA|Klu!F#|v zynVx6Xd{OEZ~0*Hw<{q3aNxmbM9u|5fctY4K;_9Ea#*q_oi^Et)f_~DQ^=q8jg z@g&i|27c9lZ{Vq__=C=87Xxp?-)2Kj33&03V#qUK&odR@PaO8g;J+An2l6zn*JHqI zcuqw9{{`S#tb05KIW6F2@N55?_C?eS@#hBc&jub~e)J6RlYo~o-~R~kvkZ!O3OonAa;NAy4E}!@yj`Ekd8t?R9Pyts!16)h z!OukgVZaXrp7Q3=IpF!a=(!dA>dU}0$RCCv=LX|5;v{3+n6+oas(z+VMk_xyj` zPS(HuvzNgsul|o3q}KJX^i!_^PFfoHsPO2feGSbx|GMSTZ&`eCW>H-X;;yy30) z{?6d-dj1{!6)*q);1X$<#w%in-{98Sz|-KL3H%7)9q8{mo_4An^t&yz*d_H1jvsGIJr)7(AcH%K;AQa94xVx)aWoHNk&4@U%Bx zj{Z`(kiI^|A-{4?!$S=X4Mjp63uDt=gihf7a1oPRd zAnlUEJYp5_8NjnRho*Lzr~F>~4jUXdJLH@M{E`HGbprlFgVQ?pwqk(SQN~liYmZJc z$U(s0RQ_j$U)Q6y?UDLsFfM!q{Ih|lus*N;@EL=H$stF9KYzZId*V2`*af`mt(SfU zc;n;=hEe}a%VoeT$oIBG84noTDG*4*;6+cLTa+IM|9=4f=M?|F$T@GaT=;L`m4@g)3H>WLIbQ!Q67XFO zPW3ua{B0%5O#`n?5&2JVBp2o<$TqecpZ6&lpX{R0Iz!E(W}5~ zy9(zL=()uy;)ji$L_WX&ls_K@9$YDgc?tM|z&mafdwxyWgOh=0y?w3~1_zTvzM=BH zJmfmyd93qnihCNs(>T|mbKzGxKUet}$2AVW13V9W8_3z5 zCM?{J)u*Ig_MajbJ_x*y^A#Gm_XA#hOZZ=hoGkDT%Ysh&Vcp2k@_Pe)$*T#uH>L=S4y1#7Xu4vw?SDJw*NCM&*Zm)#um18(80?bjW=E2E2Z@ zNKpR|Xu*N?Z$2t^re-pqoehp34w(i18tkC@WPxYBID7{1j$_3>*^Nb56?i@^<*wRH z@COX;6o{`^tq?sM-7Z+-hD;3?Qm z`|l|5rgtvlN|l2J`yWa5f}a4dV`JxPsPGK%%q(Gi82G<|cfhZ-UNc6;4pls_v@7`M z1Fx+UIod9Lz*|^Ppk+PtIn&_y;gBm6@S7ng!22gx;hrA@&##vDMKcC3B*=Lq0bdI_ zbx;54lz6azrjHi+uOM$c5P0eksqaJ3rwe%1+gJT2@Wxxxzn%gAEe3ZCfUiFRe}^~j zzYM&N{;2ic@-)%2`IzXLf}8_@=e>DZ&fx9ko(BFF4xa1$_nW}WC|CXYF5s!FrN!ay z!Os)q|6b)_UULCdcrSr}6Pl2+{j)g7qURp>0-o{qZF9iWm^gkG^3OJSdwnkhe+%>L z7r^qc%K4bs^LaS*GYN8D27d|!_1FyX&ex?0l74TM<$kF~=HF&!|cO~EttDN(t+%>rM#RUGh!C%L|056LMyY&lCQ8A!iVHb&lY# z3JJ~!UdO(z+U-u@MV#|geSQhN{9Um_AN;Us@b>z?1^x;S&L0MrP05kCy=oVVoLeF1 zgTRa9rC!&8e-DGV%by4S=0oC#bHUOHyaVfs+X6oeco`4iPQZA5OM?75_$#lAf|H@= zuYl*%Qm)qPuL*Mg1^&#*Vt91p;Dh9N#&7E)hlWM-nFqXr^C6o9A2c|AIOMAd_~k0c z%Ud1;UVA0lfByu$j(m70lridHK&j4Gy6W*@!lh*v=U|D38|x1S#NEtUq+3eGdlS z_=&XFw;-!W`R^6|saehEH08(hn3`u^U~n)wWDWSUm>+4LGkFpG;SuQ<8sC1AAm@3= zsr*p%yhqFEDvb<}jP~@*>I%9Gr9yAt@JOMQ8yU!T^$!gehJ#$Ldnni2KeVK?Ki54n zR2t58jxGMG_2`<=obF!==ty=>+a#q8w7;eao?y2~{E$;?cr#YSqEc{z$>Z-EXXBt7MS%sz6+WksG^L z40aCmbq%){?pzrhaSD&%UV+wVmZ`gMxXZbjytKU}0SA{AmiP6n@R$esN~NKa2M!L= zEgn`Z4V_%*ausETHohk?M`@(bDHzBY_qpAG8{7V- zJ~T8qQW)%pIJ(d?+TTys8RQPsH>ee)3)*cUpx8-TP@j%tUCv-`c&L9_fvq;&N6}!Y zzkjH!le<@LbfnKTfB$H1cm;)v0bvCnx+~vQD4jw*d!*1?>ZHg4Nxg%^9BlY{U)=Tn zz8;Dz?bp8m!(Wr9h5ueK)ya3>W`9}w47z%QSE;@4VoJF}K z4>{zRgBRx(&zZZ3jIQqLtYLe-F*Wzcn%Nu^$uDYHpC4Kbr<^CPt7GVh1&;% zCHn&_lEo)2%;fghZ#bOTWfF!?eDdvdHVFyRf44YPW#sSgY>pJA`6yF_3?kx-~a~zXRYsPrD4d&0Ifq=RLbjnv=PU0_5>I0>t4PjVoGTw&kZRK zEH7pXIMj3m^JLZ*px!=qt+yM~G@$bUkO z!@enj(L|KdL2FeFGvcj-iDQT9zgkVQ6$ct8g0K%IG~(PjOQb)DwGc*Hu=>Kvv(&T zL^a?r+1?a3AZibD-INXEL@Lu{?dD-$VgFMf&Mh6HSvO_ip?9coWsPf*^%^F|uQ^0I z4MmYVcN(QBVT5KoNvdyyv(BxF^KAGe!X?uxqDArP}8nGKO&Qnb<&$_Lb`Ax%iG7i4!7-8h5lknN&e#kK(*ac({J<5C8V zA6aQS>+UHOx|dMWk)w%hkvyDRL?+mS-IkfaNUHB@*&b}8u4bGgp)aswhO(s-u&(WS zrIpERArUKuH5sk9E}A@Xis6~WSPp{+rQ!o_gWEK~8C5h5VfS!HZElSOA4XRmBgAQ}HKPZ)tEu~# zLxwX{Mq{K|hR0;qVIpBBhoKdnUfM38U{1*utp}m)Nkx+|1hi_^H!wPo8|tCy&nQig z!d~MlY1Z)lan7+4XFu9Kw1*c<1(w^UoAD|! zbT|Uo^M)xGq71Oln^xJhnQR#|^JAMyKD)k-Vwa`JU-|}nIGv!iuflL2cg*3?%J3|k zWO8rl=_~Y;y2fj$SJSQ*Ih$Dz?a6g5JtZ9f_L{Y~O`tiynNPBh8S1B1b6ULdx~GmT zF=?-}xNPT4wD#w&+bzrWkAx;y2X>o~i$njU^lhXxw8FMfVs9>yBU9%`sB!~F+~^yP zYkh<7C$e>~?54bp_SM3D= zM*n|c}mXg7^vXHS{{AOQKeH&2T$ryj&pj;P#gzN7}aG% zUPbQgUe-BCX`3q2y{up|SX+5&QzyHoB$F4?Ic%z;?5fdh!D}yOFrLAie{%mA!;y_= zA&)IeXMe_$&UGhHaSdt8lB2bF-ZM%vkq*w@>__Y5SDJovLL3%phhJKl=D~~@%*2G( z_&6TXN(Zk-##tIciq{T}udMge zg_z7$dF{yPbSa(YCD{?Kc3P>kSS*xm{4?%sLP*4?!#=3JT+=dYQR7s$(;B60@@8SS z$r7We@d0k__UV{OHcAOLkO4L7(@upsSEoT|nO$`tn@Iox6Et6sL9FHx??Ah8&Tm^Q z+B-x#5{Q8Icc@{b!LnzlSQv~#NnQ($jgc_p31=8FNfNbTX=pj^D$yPiRhuG#IZ{IY zWVMxMiN>(7cxOJq5h83gvstZUT|%F8hdif25bLxMm#cl=$nH_>Gi`^y!FeAyHmBfo zXt;DcDjG>ed!5kC)4fn;gOhrnaYf!VcZUxS`0zm;HQU`q7k5l6}8gg~(ubpmSP1lHa*JY{}3NDozsuZ5~N?A~82=DVA@?N5_}^ z*)+UUyRVkVZG>D095FI%*mG9OyqJgJG}0$WZMbuUW247wLpC*&*;BOu+l2OYSh=Lm z4b$3NxJg2baBFhvYdR)a$)ri6i-h+WCsOl}O(MO(c6jT7cHh9sQSAhZH=S(g;Ze&D zJ5j-rU0;9GKI-vf4uMp{{BV$RTFY@rGHfIanZAO~ps~cvGjp6dTZMVX5Vp)z+d1~VZgO}m99`x((ToH)wgqE z)Nu^gRV34SQ!IP*Bz6u-86>B$iL)(x1K{{l*zk80j2l4VPgc7zHaBy%10Z9lAE!d+St@CR$^hudK1OSuA(M{akFEWlV+}* zw};bYPWIXsqN3WN23|9&ROvnvQmr94u6V6QGP&pR&TnaHSU$m=lRJLv4-epQOs<)Q z?a4d@YrZl_E1a~{85#9dm)K<8s~7u{2X)hiUq5dOIz5&Xg*lGvxTCb~Ma<+JOre_j z8aSf^>_tlpyg3`53zOW?9wjstlSzQ14iD`sBMLHN6Pcv(R%ZGrnf+AE7LK&tB&KIi z-4Jb`=r4_gR`qThu+0!^{6B0ydMct&h|h;&M*~bgRW|cP*Ty1)7*{)N7l+vm-Q32` zAr@zuQcK}bNaw|+cD_4Mf6-&Uw1yw%etNU*uaS)2grM~YAh^3%rj2l>8dOQ^dQKJL zOpe!u9g@a&HVvQ8QsrGwGeOGAjMSdR*(I-pwK?sPY5_+n`by`6OPqG`lLUR3Tl&2` ze%w)cWR&(SICQDV2*FP3l` zsL{17u7~kj?x#me?#g-Q;-SN{^TPabq%=~(!VAe@-msv^zO+O_uMp*R?co?f&l*_Y zHzP(=PZeP;B>BG$yt)dee5RM1*v1{JeaX~SrF~iLedymc)V4Df(VqejRncsMvrgoJ z{^-I3tS&(lm|8YU)ktgvaugg=4cVfNo460_ z_Ij+qd%9{GLZ^3V{!!{5bao8s`43)?QeBM7R8$x@>?OCYLM3BQuZOj1YahQLN0PS# zi1yM(%GP$ld+3+KR^ z*9qFgt6qChgdyJwLW%{riyCUO_}pGt5Q@ zE9>XfER^jy2NkS~$3=G6@NG$SPM&X29u1d>Vlfrq>iTYBu<-HNa7CJXUxH|k5zZNY zTQ{t%Y6Q}|lg1Q#qR={yc171WS&HwwrV{q7Ok67(2{HZ6c1T#s>8BiKT%IM5SY#f;Spsox-h*W2msrwbo9v z(J^bIw;N2O;{+!A2=Xd|O_8|nW_>FXqj=6tYT4A)1`_r_r%dYug@Gmfh_pO8M<^oG z8v@Ovi+181QxPc2y~}FJ&M`-x$kt6&u5Yvj6Fo6p-#58xwnT?MXoZIk#FE3ABX-DI z=zI?PA=DTMegah)Iz&zm|ivZE?bR7$yubsEub$t&xm6a^z5l?4&-Ueafw}Zf&_v zVy`=!k?!?Gch>^A*OQxvGa(IuWUGF9GM|p}cG5ZR=u<~J>^pAhG-Tue9f^;m#HPZL zRLVv=&ur*qk-Jff#wTrZCpde%A9MppaWT5hGa`9kOwwXK*(L_ktFej3>N`_a$p_sv zsP%Ukj-6TyvoOeGj1ywjXLyIZC=W51b$%1Taicb&!SvXKy>WO24gURuxq)zk??#j) z$MX}`_(q9|$VMQn?}c}7a_Oq!xJ~%ofp0TjY}Fd%oX?3hQ`ewkN}!!pcN zR{E5gv0J+nXIwVWH#j?dgn--aSUV_0J!7dRfttE;Y0)4V<6Q`X;WD(<2d=A@4cjKn z)O)FiYMXMmw!w`m&NEco&8?mFnIf-W_%#U&volxi4y16H_t*zT^lG%eNj+V;bspc= zNKrA4Hj--2Hro=mQl7VXen(HjM20+ehW04i9$#%km<4%x*@|uYSOw!k0^e-11bZvT zAVg_xA0130q1jfC9n&3HjnzB}niujx8CD~5P$n9`mJE#!cGE+e(Kl_xRWi~%o{_Z) z-H2+MJAp51FlZkW234+>?X-RP5S}s_zv0-z+@_VC)G^8VNaDcIvV?73t0rkkPr>lh zn(Qw$0CX)ad9f?pYh{b`1Dx89Y|QnhKk$S00K`LloM@}^XVt^8k6YG85zPZpPLarxmBDlW$Nl4lA!E^$2Bw*T-uHh z-F=;@rjHZqvRxilrVrsmcs(_3bae6%^vjn>_{oZj-`OjFq6 zZEL#Gl|}1O>Ik0d^tP~|*x6l>!}A(@6MWw78&1BOX9Qy4&WJo$PUkV>+NbGa&{(JklZCVVTtu|5z;aHBTtYcu?F6}GlHnJJp zMgx0jBJ{-b)+}hL_PRBExA|RG?FNH?Pt*vdpFtjh{Fk-rlF}zND#zyj3p8&!dqQ>8adY zPiJ3$IE3yU%_8|IhnJMP;S;KQPQuM%7Mf+j-A|`XWYicPGjB{BqQeEb_U83ki=MA^ z5KXVSS+#$Rp@17#Z1i_MSrdxLs(}}XOgypO#c5M#@s~P-li=m_AR_vSpJEs<_e`x$ z;f}`e2Ck^7i7o_iwlv_qYE18slWmo^YaY(?0`4T*^$2G`b?R()Xtg{){yDs1J{ir6 z*nRcXm^LEzGTe3zr)=?M;ZR6iYf-vn4CdK*p!1XhO(JQrjo!M}M=y1zcO%%48s7uL zqA3lN{T9|yBY?&*N*b&n*N8?7zM;JdB;4wgvuqQCSTQy__;Hj&2@Xa&J%a_%6SCf7 zrnBr6*2EpK#@N5%rt#B#u1YA#T^EZDo=9C2>_?a&6tj^@7OALR~z3X4ke5RXXmq5NcgF420c8uhI@G%D4U9 zR6og*PV9;6$5tsL#E}e>V4SmTIt_|DZyp<)J!5er%e*Gp=2}(F9F5*FZR0p8o0Q(f zp?A6_0Y(I1a#;xaI_h zFmgmP1-q?8{uHS_sfM6zTvq*=-GqD7*nTC;em(S;RffH#U~6)!MPd2uuvXaC3;|wW zbTo>6<2;#^^yHprUo09k%?+#Kgg{Zzc3K@#!xbg0*vNZB-1L$iksi5Hr{)=@j3e!_ z%8gQJ5_jA*QE|Pq1AQTkmpqKhLtfk*#=(t$LZT10=dJ!APR1br@NXNcem zqOF12e&@z1t*PI(xYcV9n1xa!vtw>u%|v|O;?c(Ss?4pVmu$O-tD{~i-NUDK7P+CE zhUFf_^M){b#14+8N9%+k#?YvMGqQMPtCm~=_xQH>8E!{}^2VctzRQ}s=Urmc#spQl z5u^4Rvt8V3#XiaB$0hDAp0j!bj{1)FD83^conm!k7I%rz`W>51VykQA*dC-wRye8{ zRhQTa0fvObO?)Ls;Z^-pu8$a&YfDM0p`;wsKcnpN}m zS$F%0q19lYV1U)35_Y|CU(nsso$MwT&HiRVVE~{&6Y2Zo3S53sw=($21L_cjOV)3~cDI zgBV+YyM4$&MvCkFlt2BULuVJ?niMOh)KRGZ^2a8kbG;a!2v_>O&gGmEN6A{#7~RJR zw(3sK+LGg0V<$hR3&qHe&LPk}6YbYFMg} z(3odfqMgX*Ae6FpOY<$aC0o}DM^^5%c9b?RrnB5Jg1trZXiXxkOuSd&w~2Z_9oFMo znLlr5{vd%mM4LR@dDQjivhODuNw-wE5xhTy5Usi6H<3LR)`MJv!m_5_?L|2oXtGT% z?h=j2L-=E2iNiC8A>9^_uRRV}a{DNQT}-E(lwOyuaGTX4aTIkTCLU&as!U&F9xhC* zsh7k!d5>d8%pJp#ylqMCNW27qlo%})=20Kum+}mDtq9$ZPBhWl4)+`T7@hVTY85{L zwb{LOUrI7*e=kh#jgiA##L+zQDQ?F_ab`Zz%<-rFVjYbgl6G6o*-8xyXoZ-(&eq2) zi#SWKHaDEp(;iK2~IX>6+u5%kU%n4%md@)V^X+oRa2x`pO_GD8!uhOy_ zjJ@RMOjghq{2F-qleO#~^7lR?@m?zJJ(j>e%Kf4^bXs`mW6%ApqefNMM?ZMCfB4jr z_tJT-W}It~ zx{A)b&}Z*$Y(ZusW5(ueF}=<@Yf`Sz9?K(jj9jK_B6SN(ifL<%C3caI{s87_W?mZ_ z97|NLrES7N<&y{F@2$0ak~317wE-4NHnC<43vpX@Y0|6L!=%=FnzjVuk(b_)5u7{+ zn^^1;w!i4YtBDU@+|f7*+1^zNHB=jV>(>wknuIqwYdALrm_B2&dRT&d&5kyP0~K)G zQG=#BR3u=kCGB6}h^{zzDyzApSug$aw@1C=ofj6w#Zj)JS>-gAjYh|C$1~a%Z~~`$ zM(P#U9Ip26vPbCORJ8n2L)&d>4AApX8i-}JJR3fTsdA$VMp8*9(y~(!XH5E{a%}J; zHgv&+c0feZiM0xl^T+n(-F5=RMVqNi(mdHcvB_?ui@+*4s+lu{$HiG0o*bqzmPg&4 zQ!d!Dw6i{xI;hG$f*#Fgq`7QOkE{z45=Iz?*eQvEhKG*Xz=J2*Ovrkebm@QPrT zg_ex=^>^>y*Buy!rJch|gIQE02@^_xE!AA1hlfZk- z?pHd)}#F-ixZ>- zr4tBhFG?@)?r*J-@|A{$ksI|srI-2l zpN{)M>+321YVZEqcjbPiYxj%RSj!b{g5U=K{wKMmgP;aF)%*(oi%JWETj>&)XxqOb z_XnVB4h-+*Z+`iA5$4{%W~#KL(rb2z-tU+HQ}6!lOu1j_zi*D-ul}Rxlk{a8{o_8! zpXIO1{mXwZ)aTdVN`FW9Y)=35{xa?_zt8*sLich>djGk&|6JUkf?;7*>;G!U@6x?& zKfQnTo1*vX7lj_DjKsqq)mv$cF!z3mrUbU+{&zRyyTXs&r}RC#E^a literal 0 HcmV?d00001 diff --git a/phonelibs/acados/x86_64/lib/libblasfeo.so b/phonelibs/acados/x86_64/lib/libblasfeo.so new file mode 100644 index 0000000000000000000000000000000000000000..5eb0e988c961808119e0c2556f07ce4cb5a72fd9 GIT binary patch literal 1265064 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 z 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�VW{@Ci15Twd-bU`6XB`xo&o+ zIFZecbFK8l>+lH>Mv&b=-~>JpI5ApqqKwZQv1!yJjd*YZNn+xJIr|}3;RMCV{VtFQ z*GxGr0l9)2Dbb2k$!*tDLwK6luxThiel8B5Aecaed5j{=u4vO8JXOR(kf8=a*vTQ9 z$R8TQhah|rk32Ek+ue*nF49LA@u|Z&-0|X)p1mrJ< zq-n_kInuw!@p)Z2pEsiLf#~0$9sPTp^zU-%-}8J8<>}u@60?7s^C)`MzbPz)q}vab zZhyb(_U!QUFi8P0I68by0nQ3ih|pAnbA#C9rLRZ$hx)eWEM3a`Jkf-SWKyFip>wdC z!J4}u2W!G3W#pJJdW{P8Cv;{5ZFzmhsKTpEzIq!GoZj#0D-Ns|;wQ~f0M{R9OR zqHLj4$Yt5^p==*EO)_jTT2ma1O_HOF7q)j#_6P@K6N4iHRvikgP;Ki~BYOZVTvPZ* zX?&$^nVb1IwJkwH-_Ja7hN7>0A>P*-2);=N`fK#=02puNh6 zMpa{=9)Q}`f}@;&2!x|*#4~Ex;Swbl9wDBEM`HSG;SuD<#V#!fK5OEbVueLBd!PrA zK3vSsJ86v8gt)QhL5($6kI7JAQ=kfyGN+C~nf)yVfi@N{lolvTgCci|Uym-1?fvE!9D(L~|ln=iRp00Stm&o%LAJJNU3G zCK5f%&qDPG&``WXRaBwrx(7uC0rBJirud-9`C)d7SRc0XpA0u%)xFPi@nux7C_ryg z&|VUNMIQah@(W^M5p)H5NvKw$#KNM{4lEK&JsXQedqPPC%t<8*nr+gU*|D`K0y0{S z5({Lqnajj6A}mZAQ%2uZ<~blBldV8zv;vtj{-N+LK_Ef+2;iS zOn^?NpglJLU_82+ONPe)7-$4Ez!XT7SO7E50Wc?prOd)}k!WVP6u4Lv20>FHTRh28 z$T5z8lg5H4x$BURIMp!5SN@jQ4r=ne}?O6pJ6s33rhN*4GGY z+5iT?z?!`g#%c&lQB3uAD8-X_fj0MyH?AM4C>FlOQbDB#;oDISF;qxtfb+;UU=kbQ z+@K*U8K;cqhcJ(n1xD%&!wTGQV^9(c!!$YeYacnmQkI`67l`eQtpvI^0K_~>pl=V2 z0b*qqh}|VoVu2XqS<;qlxwJtnNC`A&cIQf!6FD8I+b}22s$Yx z5DEt@O+w*AX5P^c8ONU zo>Y3|uhE@jSDE*9AUWN~E+0_=*cGT>`Zs0xhnZ56$hh;ftLAw0OMe*>1G{Yf(q@Sg z3%kl4*d^Oli#pdY#R4UzUz(%zOLLTd3B(QzC@K9C3KvjP`lUHt>X%?6(}~FRZr3jj zLQo=@DczU=iw$PRtHDgXma0sHnJ}*h%%JMHt0rP_OsUGmD>!CcKawbMZG!8E{Z8D4 z)aM(W)}I~*S+|poPQQ}~!Jv@X=yWHyT_rX;=LI%0f#97JCdlss-MDJF~r<`6HIh}I=Ahpw=s zt33|!RgP=E)3c3E``!!&6R2MFg4l}^BNmuI|3DQD`qGTVMyCa`5vwScPKL1KR>^FS zv2sP!Fcvuw-V+<0V(cat#Y12R%vFQA{85)0OVSeEL|_i_g23#UFw$EP5dB6FZ7zfi z0D%CdM^1Z=W4EJ6+41~q!5{!Ng9d>;5+fD}V7_laAhFTOwc)vY{hQrBbOts$HR+WQ z*Z3^vDO$>gXdS~ujU&0(tJpDPTE8-`7_T_vN;ea$02;Ef%ZMoppY}xp3l+?eZ{xzuI<^Pl=@0w&$bU{Ns$P_9lHyC z1)2!57M^h($+E$@n!Lfe*_p#402|??>_X;@*g!nTtz0rSiAP4t)d;KS`%!^oS$6Z?A5UCGA!=39=!n6z!3T^B@$H zM_gZGBFneR7A4Xb^K-1|J^UOrkn3=tl~xmtO1EOU>H_J6f|r`OD5#>;`- z(=nB=kQl-Jv+Jomnxw14O1xdMv*SsY=}y>_<4NN0g>g;I=b8q)n0yy-1_}D1=k$?X zJbmPimssOnPRhGDc*NOLJ7HPp43m8|Hn%!wl2%`3_e5XNdof)MyJDNa^SNf!riaAr zeFZ_?Jy(?oH1_sB=ckyws3pKx?PkAcF$ zgTdX;p8W2Y7_quvSCe0G=5@0JY!W!i1)l3+b+Bx5b@$Ho=tc(v-CZ8u;pt#V;veWd zv)NtUlXlf6eSD|;*p4nv>~M9x*d59_E0gh-{m(F9&}bIap_~&i!^JT>)adSYsIs8w z@qLdW#rmFZbf``~DWF3IP9(p9BadMT4<5?)u9~mf7u=!jiR2F@Myw9i#Y8fw-K;K? zBVA@3pVyW1+4WHw-RLr)tjqH-JY5D!0=kTQ`MaB4h67hT6U5kuU~scj%wc7L>_bqZ zoH^(MLy(#l8Ua+#_GDGrbN9e>aMhBW6x1)ZASIZ zQJS6$ryzOnE}QqTFi zL>FUvW*K`p%eZ?t>#Va`M(_l~!?R<{6{Cvqtr%4%3SiWcQ-nvmWY2|)H3HewNY~^~ znGVD%t>^r?Sn937;!mr{Kn8s#wiG|t!Jr-~K36K+XOlC(%+g-}gT#o%pb%}1_S(IK zg?e#|@8rY)xm|N&w`BHUPV8DRcX6UqCdQZ{A>3G=C3BGhME^Sy<|5$Wi zO=gpvqE{oQ=;0vXU`gCU)Jw}1OU|Yw3zpPJQFnrsoid}AOv#L+nWJLHGCE!5^dU2v zB0FKmKneJpuzZUl0srusV6>cPG2{0oMl5E`v6<1el;RhCHP>}Z><#8Rl-Y%B>*6{@ zx8z!Kom-;qCfAV}-4d-ZBe zUh)euWE_I~XDXBtx%oni2d`e}a zHo**^vQ<(gS*fIWMBAgHNvU=!$sJyGLr#Sx!^WDZWYS9Y5X;K5ugVc`xvS{BaoVdg z$;;7Rxuj@qp6rrt8a@SwxuZ53tvuadohM&y5tcXFooBW?Py3!Np-0YoKoQ5Jh}BjR z?i$N;*I1s^nDy;>8SWz77n9~~W~Lif)vIeu^W>}>lz3Q5Tx6B#uI)T`ZRfdbJ5OpG z1n7!t)kUc5t@0YRSC_b2d(}0nd5h=;0dm0Uv{ga4xD~220W2WT_Eq2>87rW`{vs<1%yW#1x+nqkU+3Ay32W5w)gzva$ z(f14?+O7KfAv$pyweFHK9a&@Z2lKPpgFc%(=(CxFzE)k9YNAH%Q%$s5`xI4+v`;lr zggh&Eci)P+n!eKe)?Di*xl2eNdwbzegfuvQAXLHeT_FgLJB5~}n!-m~nK>N4ZPbqY zS8K;F7HP)~5k~l8h|kaF@;M|ftR1ZPReVovQN!ql8!p0k6ddJQ0Ix*DANax%j(C@7 z`RICdTNSkK;azk=wS*C_eevp)|ahiWkGC;OdH$avr?J-U@TSJkIw8Wnkd(O8BFLa*KyAHmuyG3Wa6eFCC{i=I0Hn&>8Xr=< z{iqkIaGM&TYVTk8!a!Bx<_m)Ob)t0Y^P4Yj-@>ZyOe*2Qmh<81W>MXbhl=#96UAS z(c@;V99VauINbV}A&HAN)ODk%sl-Ja)^=6r0nuU$r4MX%OhCXniHkO*5<-qirI@t1 zXHLnk*`%b11f~!HF-SGj&lhd{rw_7t(S{$hDj6CJwC;W+KG2%+2R~>P5VR&r1{P?^ z>V>65#)MX+ANk|vMpR?sHV&?0Kuw&IwbFasb%tZX+9}@w5Aa(8{?qUGuC+_t)bMS(a<2wu%?X-Tpw77j(p>>pW;$9Enp8M2Rur?$g z`%G8QiPK}sy{|)D3gqZ4Qjl!wai;d}?8yo~ae7RSJKyu&cR2*!>#^Pf7cpnzRjgPC z^hr3Q5EyGl_V(KNpykOmerO31S{{)M47BLe%wwyO^sEevSi}!hn%K-b4%hGHeXYnJ zXqveEW8rq)=8k1`xhtNCB?mZLw(e(GPKq1I7!hMEZdGfgVV;nUp0{3M7`%zgKLm(G z?@@)0&>4)O1A1P&U<@78^V$z%eS7)G4j-2B@((|lRWdXdnqB(q_|PovSAH}rCYl|V z7Z#d{Rc(<$vq1e4OhyFBW+rY3@#vSZr^((B;ngoCZU~XvA72*3r-x+twgJs}6E}p2 zOr@vXh}eX6`DH9i-Y!NnR^?O&M+3c?W6M1Mgw;6Xlu`c5*|= zmH?y+R7~Nvi%H7>O)LufFNX+=H9AMnoA}x&cm%uAB(wmgGd!=X~v# z-+#$PJsJI$-n|Q*+~YH zPhXD>Jlrp^*l#G6xN8Jka@0*^HB*%C{9QX$9BAZXd4v0|k$Cq558U~kYwRP+U|L~%R+NiewKN(K&KsiAh)h2XniyHTr<8;l3`E+kj0jnu(O(Q2dTkKtI9Oq^CMOX$j2uxhWVRX^nc%w&DbpP<* zpND)dx4W=koddS-Y!BC$e;i*YzH*hf6YD)({~#HdUL3fG3$+#Vu$>tFHTKavSu3nl z*D}}+*A~cDjbc7;go232L)fZr@1@cNiTKXrjpJmVtd*Wq*Zj&DLXZ2NRyhf@{fDeg zhuRp2qqOh~YOwRW24B!5yRo|1h-G$QAt%QzHe^(hPR-e9#bjyCsEiq#rNz3$#Psf~ z3fd+SbZ}vYqd4#02-23zNeIms@wrN`;O#_=Asfj${S=dToZn+bkHqSt{dyAU&5l0m zQ$(|HtA>ay}MG{l{cg@Q9dfkW>{$3|Lh*Bj3)9ZqE5CuW1)vtQ6 zb*LNtDo6U&IO$jA(y#niYkK+>k_7ZC_X$kh>{kGI&k0PTpa&SuorgocFh9mX_!yN5 z*F>VK6E^V}CLVw#qe$f4f2L?$_dS%<_WOCF58HjIn-j&im&MnUp8T%2C#i|z|4Dk& zkz6O5fKwn);F&md)N=-C;?U8gg`FHa`k>9t-JUaDQX3yXr+mlD&w9@Evb-=1?VdA{ zmt`_@oqx4kX*Doz#hxAw=95X^_!jG3cjMEo;T!!{Q>Gl~XE@5Mlljis)D5@p)ARBBf@n zcoePwJ&J2Ha4Ke9eKQi=y$E^GbC8}!dM4D>Z<0tE4By7bD-3)b)4o)BkrSC zrzJ3l{^wXSPb#d##uSuXl?U;#uKoqOt1CoQoFQ+U8Rei|RssEvvLwsV4D^;Wj)AJ2 z5S_Z5kfR*9@s0AR8F~FPQ+18#8D31C`aBp$}wBs`EF)VaX0|e|5@`& z7B`!=kgvPMDin=4LD!*f$(2{0?&y#mk1JM-ae>vOOysE6L=$RO8TckdL!6d&J5<9i zy5M$SrxfljeR|6o9aK(PP&u>ha^%x=Iev_g=5dtc#0UX2M-|Fv3Ho@eZ(eEgn^uXP z;=?~%_;63B_>k-Q)I{@P$m;**v$|;yu)*787B&c{J6OiT24T(T*wPA+fDKp_6Kp^~ zS9tKN484VZ#i(?4|BxRY2nb^Z5XKrnXsRg~%6yw?YLtH^od-G8r`}HFQ&I#^6E66? zrW(;IuN3zyj5qQ2Tt<9cSe}Ou7fjY)frwRCiu*VtPP!dM8c8F2}5cwm_9a%$sM>xW9I2&rIwwDCZQq5b?~Q(8?gykjUwpN1c=urK)jMsMqr`7 zHk7!y2q`@v!ypqH_Y^W;p zwXVeVjTjO-&;C*|xworVS>RjkORt#^SW^LJF<08e+s4>hg{-`7jMW5$dfRwz#3mw_ z!;wvdcqdpyh*t^?Y~#6sZ9F%yjpsVrIOHx=ymq1Dl|lpCcy3@DFksoJd;;F>c%z&7a zrAdx6CIJ;OpJq$~s>IX`f|v?XPctSFRnuAsMAcK1lEs@Afm1PH;G~u3{?^ga{xvAT zd{U$!P!ch^%dsJDfX1RSkX$}RBe}RWlFMhQ(FUtzh!|nx=!oLh5#_5ksa_<6Tc9VA zHRvIrFT7q>oR>)nm8g*dhzdp}+brSKS?sO?a_?kv{664iL#A_#!Pp}|D6V|G8 z&`HE=B|#S@fG25=(@Bh1ClRlmM14!HJBjh?B;vJ`sBa0mlNhf~B3>pD=|eb7eNV(n z238x&(Rh8H2{Sy8z=jG5ltRI#skd_?Q?g`}OlGoHt~}Mz;hxZF`Q-sfEy9Uv4wDLd zkF1Lpq0%^Lom3-a?-`=U~@#}EP2^K%)pO}RsK z?^JH#uIK2!H-8Z_x&stY*k>cw#C-h6dCM;&gHlDEa0L@D&%{F&POykp`6)s|h&txPsOUt_Jf zHNuKpZWFPpSzR?!SFfvCj-SCd=xTn|UCrtS6RVol^(MMu@6DGTLTChO+k5k8 zn`&0H2~bNFU!np8j&%(S;OZI{NYyp0?q0EKxL%W^Yxo&=4a*S*pk3Fny6?rRVRadd zRm0`(8W!V!(EfaB@?q%%D^t){1glp>3Dc;%1YZjSCHPuljlkCQe9hO=&ju|^w{JyP z0XIW+wSk=m&@8>su(VJ=I6;~d^pYh}u|t(FSovvOdcM&p@YP5C7F|?z)(^pD=HK`_ zjEPr_;`1uG^zxGEv{m)3eBc_PRrM#?t#4)4sSJCO49DW$mtGcBdU;^!ECHp<9}AMwBdT=ytFDYl z=@5fAE+X%tJZ}}W1rhOT5D_O!7Dr7kzM5!Nw8&p|Wvej6$~P2_gY)lZ>7XxOwg@7s zhb*C)IG19bV&XZ9iFK!Z@?7EHVOc9SW@!Fxswqh4EZ}pwe10j!=gkp5|63{mjt5us zi{N;;Q9J(BN=LY+;9DXQ!LdSQAvk_4QV<+J6Zr=Ytc%oKFbcvNZ)1bAl42UK#S?eP zx;m>eKSHbgj*A)n2*6Yi+&|H9;4*HeX1C zS4v!~#Wa0i*IF&6v0Tuq7Qe8j$D%d05Ya4ZA)@JomH80SENZ`_3DDI3T6;CMYA6#h zz>R-eMALV|t<@r$MXeIiENYdAW>Kp|G()X)xJpWB7PXMgENUT}S=2%{v#9-^o7&&F zsok!x!OfM5e+M~Ns%`}$ z2Qycy-VP!MDOW-?D5MNgC^AH$$Pk4hLlhbrqR_|?g%1l5AhMAm3XKd=Xk>^&BSRF5 z3_(#0pi{EAg?|uj&V^_*ENf->gqb-XgfLF|BuJ3yi2%|wL4c{cKOi$z_XkF#QbW@a zh*qQ2*aT-K<^eLGid&CP;?~0x)2$}RNuVAt#H|MmaqAI7+G;G-$0t@gKC#mAi6JYY;}a_#pIGVm#7f5}My!O6Ppou&Vx{8~EA=M^_ZvO+ zAityDIB8Tfj0>$9#oLsNG7R+9Is<(*%G$yV9kcWn#{}mXY$!H$#BU3JJMi0$-+ug#;CCE9Y`sdG(#XdaK6dc2TM3$_wWmvy zMc0TuBXa-N9haTRx@#%xGz^sjrcO(tz=gA#Q>UfS-ijN{zqB5ki5L#%Uy5@F;r?g3 z4$`$yBI7QoFNl@-A2VHrJg0Vsp*#_U2jVztKEv_}kHqBhp)P`P}u< z=*HpckHbAY{e_PPH~&rNA85X5_>tz)^G`IF4)0*HgOd2TBt9yMJFLVWUKxM>i`Ypv zyd6idpZ^9PD7{EZ#}lO&N$Gg%(#cI;<|0`xhWb^{7^)$E5fg6ET-A`jT6=}F8uVOz zDU?@WsRlh4UkdfOcm)K&MZ&)gdM>V6ts!4}zG1fpJr~z3hik`!ISs+T5R%}qF3Gm8 z%~ls_ht1X6a9EdZTi0*Ph1_vooqZR+HYuri$>C6O?f=U!uBE|mEe(F*%9&V8gWp;h z{35p=@t+f4o_^BBfA39vA+*lqr_aWH-WR{qT$4TaqDiz>pZ|9q(W11Rj^t=Kb83fT zj>Q5xL((o!y&=5h)R5|{84O@TcqL#6F911slTv*#-vF%erV7Y}H$b7P^BJnZs~SLs z(0D<~W3Sn7{Nco>-#_8vj|4u+fOp35i9;gB#pxJ)!KrVPQy(e|;M5Iimk){J0lvf& zJcuWF4o?MRa3~mqL%|pv3dSa+U4ADKG`!@U_f9FhJG0B=OgOKBYRPr*IB8A;VEhnTRKSd&=d>l|@u?)&uC`0~8Bg&|r zo!iSowz3ec;_KR+5mJT7Ulr*WaVe^K3iTqWe)ufO}FALejLSWbd z4ro6D4FXzdNY+YG4~9E!p%iH$JkdgUj!56nHcLOkHcLM)mj2ysk9f1}M(xL&WjE?N z-YlIp`@^m|eyG$Zo~Tbe700Kw?6){Rt>uWt@o6o`d&KcQFQglF-t$7b(F#2;#L+IC z(?vLdLDZU_KRb*W5aD^=#AWOA7Xd#fE^83GqvB*zA03b4C-Vn7p2Y72-#p0$ zOe9ENFcv{E66iQX7M$mb!{8++Qx~Mw18tM8E<72og?St7=b<~wyqx!l7Ut(s!b`k!OVbZZEVzqGRjlKTbtqaiBYhuZ?MAGT=`Tqv zxQnh)u{z*POMgj*6uL9h4~m}n8NRjrbIW&7xLpa&Q0Hf(6Q;;5PE(3krerujkD5s zAoSAoT?j2rf0Lnyq8n$V??&jz^cN6%P5LPSjvy*X?nowqt2EL>8o;$cbW3W1Nq zmC}CDkOhKT|1sJ$z9ow9y@~GdO>|cbPv4L5;ieTp;*0Q)ZP|)=*V7%oo^HfDAn^{X zb^y_P;lH%yDa0$KJ6uXP;vJND$5cz;GxothvSknAT|;;H8oCkhC5iVA!d3lewj6_7 zwM$ydJJJ@9;w#t|)9~+Ui&y?-SWVCL}PON=qVsdMA!w7Ksotc*#E|30V_~oe) z@_O|Yz677+t>yVB46grE!0`6fpLjBy_4&VEU3;>rHKioEnaCqMaLqp4 zC}hi7zy_+E@xV^aRcf2etX%prAgrSG$!P0Jyf_k&U*qBJ=}rdH36dRrN2> zBxA{;WM!ezoU!EXq=nxmX$yv8yI9GRBe`woY!+#mpj<{!#?+!RA1b%^gnkjj&{PgX z?>hbBlh7}g)^1}nmef8LUUC|2dE8>l!*u^J?Dt=~M`@%i62B=8yd%wCIWg0|2zD%$E#tC_$cRnhk2`d|Xz zl@O!w{rs&~CwZ^qJI6Rov9X$AsfU03$$(z6VeqMkLkW4kYk7T2gT?9&d7YDxnF;-N zlG519A9dybn9y%KH1*xk_EELmv*iGXwlAUIj%zI;q2Gokq_Y*Bo~xd_!SOvW#KG~@ zZ_nlk3{myo{6(B*i;8=#Xtk;3rdFF;ZklgX%e@-^(Xdm?-S#o1fm=OBjoJw<_tbZV zmU}eXlbY4dXkU+J^+yO&TJG<{b*8l3>onv2i=V(ZK5Dew?Qos_TJHV$3bsWx{ylAx zfgq*jK0W$u&~iI0g8I!K)tH_V%t~svmDaXU)GVoe`YhCUU+}*j9r2nlHOFgB8IIp@ z5|+FiAe8L>o>fCe289~3zw|TcFRV)O6rE1#hPc25Td?=U0+%wj zI8zH;{iV-&H;qTN_YaD$@m?Q~I3{B?7)D8!+VsmK&jz*W0Lg!e@yxm2aPRL1MW=l) zk7v?X#dwe%RyJ&W^}_)jR+hVAF^64Zw8A^18#WT-Vb|*$cl>ftbge#JTj4a%wQC?L zT@1frtMYTv|B2x@?83$PhL<2Lg}Il~io*GnIAPkgRPAiOZxIv02Sdr4g8No$-(m4J zY9GvC@vc?-aFH{7W>=gPb6+lODXE~AlI}Bm;-s+qBKqB)_Up01p|tjBY4QoRm*5$` zA!+iwlGgCF<*X@yl^UKnWK^#lj7UF*@DVN8b5S%r{Y|)sx4e=@vSr7>HpwI;X~b|} z=Ms?+SlO%fhuFD@ZdcBWSM`Cyf4_*IYf<#TYI+$ijxKZDPvVX+!F`P;r8w*yTTQq) zy3Fwyb37$EO5Kwdao9QL>iYb>I6BV}jfDeHmg1~Q(ik!+#bM_dG2!CqGRLQw<8$q^ zU|+2EB79G$ceXGLdOaP!)%cav6 zQ@P}t`V|N3*PTmOt)z1Cw~{Xx-7-%Gw8v0idtm)S^_5J$g@HXTlUo>Kw~flh**2Z# z(rG(MUIFdYX^x$?naa`MW}W8PY5S=h{p}Y!$FAB^<>+tAPIFZ4+81j@4LM5UiMd8J z0$WyLOn+J_YJUv$YF0ZO%m#=(@E^eM75q*#*A#9dV>R)CTPvMgBW^+jvxR~?Cpt-J zZIvjDD~;MhaW1w{;G8&X3k40SpBGHRxsEFz!y9g0Ou>g>6Gby#(0i@ek!LM`Nc*n~ z;eBe>^82*^#t8grS<6>xznnLLp!BTecj_QHvKc``vzFhcgMQbD(h!u9wS1}$`oC5c zbWYat5(Z63yL?F!`Xyq$FKhY7b# zO-dElV4nq?+-89-2r>k7?+3OSh?_e+u$4gE+{c0K0^;Uw4SS0QcRefwb8(U6gTiEK zdRs<&p?u?8Ll(}8?!(rL~Mn6ctwu*e%swJq6WfKLf2^Y}HMhVtav}u-XhG0cSn`X#{2O*;A zCabEVP1m;^)^!zaD&^(}sjO(zHQ4-MYD;a{P_?BtWvJRx8!uFCsm&GcjT7Go$*iqH z7wO!?fb9|35|IbK0Ot-v?J+>4b%Z%zdBtRIQ zFB$eDR|^c7m!8Fi<)G z1MkUWSBUF01p7j>guQT=`#C=G;~@L6_lUY18a%U~BN=Do-hiD>nLr$e4j@?{3R6&p zLt*e1o5b+;vuZOQ&ThbabzBT0U|N!yxnn3J*he@Z0uPcwn0jaLlLA7W|0Zl;xF4E{$fb9TvYzs`B~wbg0JHQsJWSs2`B>g7!rzZoRx_ku?puH62YF>%;yF96w39}8CwG| zo;SCh_->;8W&aFDN1Sg|TKjZq@(XhM8xFG3s`D|!{6NB^jmQ)7_$N+z(yOQOR3l*; zHya_^ZMlr{x!mec2QU@h-yY)oFNLc9LEgJr4DWG59Nym?@7~@;kWSCc__^|9l;P1$ zT({^_#|w=aZs>4chlN-;7e5(ri;e#HGxyx$WJ7mErY>Nh^V%$?4yn}gSFO%eoPi?{ z{i#QLQ!~pl20HKDVwMq=rTkS_pxO};2Ppcp#QtnIL5x}M&60uh80ff05PJSpeI!0M z8n^!hdrdJr!G!m5v>NXiJWJ~j-<1M=MMUkM&S-CvnLH+TEB_A1FyO=F9;E(e3{Wuy zRD={2@fOK;$_;AKkhew2B)L82T9#An&60sLJxoF#=xhFFIkSVx8F;3@4=@3akSngb z<_NBgQ4^-xG|oiQb$xBx56$J09u=A}CuWi^vK?$I81bN(DOv+WdnpQ5W9pmT8dFU^cFm{s?i2hO{uaZ1U)PB##eDwflt_V* z_siWHc}+g{=co3LT9aWP05|!%YmS@5wFCfi*JFzCk+-9CpX2PRdKOiEhKjys) z*koqe+unX3Ds<4EDBN%Rhn)K8W???fS2E{JhNeD`gPk0MdR0Yqg1LG6y{ zJK95%TJTg~_bYBGMP|~b7@4d%=iS6%-^`hT5y>**AV}OgOU$HAmNM1xW-0R$=VY8& zb~1C;S1Y%Dd|vPPvb6S53a2=v?2HJf_VQhS?YBwrmvpM0^4G4peaBx!JxqnL!^T3r zr;iUl)YJDr_nZFtjgs1@^lr(v+p9V{N^3V@{ec}2Cr*xC`X+0nrr?vX&3Z&mivDIU zopKY#H-S=1AKAtC-vqiWeMC-*zUo3c70_Ur5^gyty81$iEeA#OD0zg-LD5%X&7N5U zTdT3q3AdaRU5%AXxOb^=;1zP`6ik5<|E1ag49FOJElL&+vhJ>FBDsBdr~}W}%=>L=h(iw`5o(Yyk+1qV8*=twBZT6v}YNJvzS*kJR99ubHPp+!boX%`Af^TO51_b zU~e^i$_|v4T3yMHeYt}<{gn@%y9gHo55e>b*?ptPU3ItQSXu2xR){ow%I-rW@i+NM z`)b@Pqg**S#7?=Yjq6Y&+X_bi*gk4wIrwByt?4*6r1T%&JNwrMzvk^Ps22C@(amW4y^nVTCvO1kCRy1L?*iUU)3xg)c?C@W{znqU4y# zR)FNH4hk=8yBL|vh{YZyH$!niz2cQY*5T)Q2Gk!ON%Z)^+T$HYV^Gu&q2M)G2h;(P zS2z8e>c1}>>WBV&3o`fj->R1fTmPN7>>fF@$r`htXixOu!QX?;v0FST_B)W`14r)9 zw2|A~YPi8Xa@z`&f$9Gw1-x-a&0y!tq*P|G^JPvd zGuZhuB~>$6==jVG7RE4es~Ic~{)b!5VDlupn!!R3XlAgyT-3~9X#!(ruodgQG##CpnJ1&XVbl1xpU}#hQ;HSRbstRq9LBek5j;o z;(5L2-c=1RO2P{Z=x$eTiT;{$OZ>m7%yXG}lX4f(y+yf4)BS{UkE8opMSzSKLO?ll z|Et7+mKW~5%3VzN%gS9w_v^|n@!t|R)V$MHHEdAsV!9ty?lQV}D0eyCe^sT*=p(^U zBJW+*uur*X(tSv|XVd+La?hjtZE@caVKy6;dnVm~QSRAvKdIdF=-yqjc*7#HkyLap zDtf=XRn2%DzBm^XZkWoxawGU5L#6^XdHE-5U#%aZY-_E->Ldr1+SoN4jw`%ibTOP&Go%*n9&wjFXU$E}2fHZ38c zi7;?sLPD#&;Vh8QC=@_EvqxR&%CJO~8{?Pk2pD(84f3Qy`E3_?N9FiY|wmr{a?C= z-2*q@CPusG*$nC1B?-w<)NK8{B4$reV)681S#iLVidy_tL34^fn#LyUlYjL2K5~i= z!s0!+r+?2Yaq{oK6rYXjVcle_Kz0#@?=wp4Yc!We`M%e()FuCl2G+5J`d(Tu{in~I zjr>8?%xq;M{#_0+K`2D??dsd)mCQ1?tHl7Y!uCLn5d-XhmREhnI-<~)b5gAJ%@4K& z%$1sGDF$RYL&u5_XQ3n1+0`Pt9TE?Va&=f_!{ZgkdsG6k zYFCF-k-R`GKPviw4f(YmTaZJIgAXem3mOBm-uLZK zpN;%WzUn0|PKz4^eTes7!nc3D>TKi>Bfq6A5P_=~tU@7h!NtOFaWM?yCodQ-@x243 zn{5JQw;LPY^LV6h>uk#vSW-K|SyQq^%32-VhP`UePdeQakp2x202 zVd_xF-KBME>M$)rKc<#-++9i+Vg`+Y!jVv8)N!+D9SpRX<4pQt*jvZVqI5BFz!)rI zC1#~{+yz>vY^yvn067+hZ+h?2z^gGFgJIT1{daz%L!V*zZ}q1LdkOvo$ zaY}BEDf!6n2O($#O6=Vt(cJaA{m_#Qgh*(4K&-{QPK9S|)C0XsVeK#iqv#+20kr&;U#bn17(B!3#4nodPUdteC*{1VLWIyHerw%Nm&YM zs)BYGe>~cNv|Z19y|hW7mo^FX(k6i(+V=j=(1xwape?m?+HgoVXd9|%3n|({Lfid8 zv`L_sHVO37CV?K>K4Z~_qXa-(Mj&lcO>*jGHEeWQO&uh~(R$){G@SSyZ6->(`i?0g@%gL!SgN9ZT>;_{4bl?jH}m+wZL@<&(cgC-H2#htzt8*H8ym z|8enIK7J1L6wxYg;AVbyk%CzRF`-ja#y*~>_1E;v%8b|5I?IMVX_CIvR@oUF zED<2=y)sI&5p`mZbWKR!BUO^XqN$_qXo@7Ese>NlY4AG9>AJxu)SkKZy~c}dJT?a% z-*XRZXb>HAj2&?}h}Eo);4ZwKrOD5g*1l4jjJxn^3LfN!1|>Y<>enMfl?oF_9@G^4 zQC@+AUKKmSuqX>c&IgTUNdn(t;BIqYvR0a;(%I-*VMJRaJ??q;WF$Exdu1cZDpp^R?uB}w$rK20C@KK z)}w(v|IxC4_@IA0-}0MA@szdV zL7_gzK>z2Ep<8z+(tV}}deD82i*Emr|L0sow?_sMcB^M<*cg|Og$lRMMuP~;&^Y@%44cYyvp^SM*@^@~XM*jOG|3i|$ z^H^%+zfbZ%bU#1Mv5h*)SCG2ZNt6L29HL zST1X4yF!_Qi@7*K93bO&tGt9|!v@#Zfle&YIdGTWCBU{H1NUKj(3P_(^=%JOv|)b( zx<1Bq3~gdzjhhFDXC9CN`v~q6bd752z;AVVV07d4nTKVxh6y?ni#hgoCWhV+S-v6z z>6Y+VH569PJzB34hR}y@QZ^sMq=DCFlys>e%GCwxn1c*ni3kIKfG!W-M7s ziJH!YJkXEG!t{mE+QTddu8lza2fEuXC zY<1flEfVEVk-D4OBadY;&6a4uKiU4uyY6jT2zEqi=bUJa!{yi23S5E{q||= zya8j6bnd(a{t8DWw@3|yyxcbVH3Bv7`Zh2<^g+LmenW!K_Y+YXZ9cYw|xY&&t#i#KUA6L^zQ1MvJf*~!=&H30GE;TZ|Mi4SiA z#GyY-AAI7_R|Zrbn!MMGPcte8AwHR9qJ%ueONF2`t5#?G;b^%*EDyO(`UBAkHD!;M ziJttj>XGk>Wuo_(eE9SpFa935DS^KU`LIvq!vy}~1YhTAwzAqJVt2d;h)~47?uPdy z{szd)wNqFVgKL?nTk>*A?OUa_8%t`R3ODUfE2({xcGr27r$)*1ii+l%!dCna^F<3E z6}fy}nJ0f$@L}^|O>T6<(t2p2me%9WtfloWNbw?mFy*Z&cuZ1>kXD%|e^nJBNfq*> zlE7jHLU*;a{s^;r4B70(?-k~cJD2Re9Xo(Y*`SY-GU^jkxn;qhPk=NJax&r~RNtkd-Jlr$0s-D~S>bXe( zE(t9|${qOWtp`Yr6Dp9J=TRir6imcP>k<#Q*{rJPe!zNeJb+6=aq-QndhS9bNYN#s z1;}p;etL%l^5m_ToJAtX)EiW!2vc$IVLkUZz$HmT4B(E(dTykEOG0xQu$d3;&43G` zvVo%!rKn9DdhMlDjQ}5JNu#7Z+#i70)!45CD1~|ZQhvbFdbtYkEzz=5(VZZ1O| z*v`Ns>ZS%V)}}b0mD~RK|oQ}xY{D1ENRY4)v8shRjaYH zEU2t9hWg4H9avU&P+1Z6UGm3k0;CG(1(sDFSk}0}vT{6SC0fnNjAu)$IS_2|1~uZz zU!MvcjzVI?WC9fvsHl)i!0}yziVoIS{j1+QR-zwu-jD1S>C>i=iPc|9Hbq4StbMbj zc2~IRU|MPICJGmqkh8ClIn`AaY@wlelsA)tp(0OR8Ev|${2-i#`ByS->h00+`0`C~ z7Q%@kdqyskeIDXWUHvwZ>aG;`D~vtyc8GR$^^Z%$5F<{8G*nl=lM$<~6t~>QH0gFo zkahJ>OT-8xemTO3&oN^4mEu0l9H-t6L7}ex1rmQNik@4(jl`mSysW}Zyy^P#S4eIp z$UsmvX650r<&VQ%DAa&rNJ%6C58Nqb3Z1EnW41z;lvagFx|Ak4;R9xs?__cKil)-? z<1B|?5x%tiX%-+~fdXdRdD)a#7;_@UvELmh(><4h^r}C4#ZA>Ign8O-%}n`&O8R< zpuw7gHEeA9gag;-iFX279yQlcAzr(WI(nz0Uk<@}t7D~(os}t}A#dpt8nKIC?Ji!t zcJbnM^g3sEbM`aI)z@+oYWK7?jqb9Ks`mhwgPvNs<#- z=(xZ_%L5B_v?&cl7)JxDcqdqZD&BYib#7qm&J6_>KR3cqh3C2M;>BwhFWxu-b#7pz z&YfwO6>^s)Ub`&u#tEo%9jz8|=PF)1SMl})P}@GTF4^astb*s^5d0pltNgt6~&5QjT_{M7Kc7=n&93O1^JDayiiX_o*Dq9J!zy zS-D7k>{mHTeH=!P2ji~~l{>nim7~<3&Qa>`u*zNP?-+7_sc-6!xsDpef^}h1-MTQT z?qjN8scu{cTT>A2eSNDerG0c!(muKs%}fMP5XW-1bdoY`~>C42lVP+I$ZY3;t! z+QX%_Z#LJQkA>9X?ai~!f1`QU@VBEIN2Isp^117y(T&5?ABTH*`U@XzzUlk}%{L7{ z(p-A}iRRMb9gJ~M;vJWGMcsnL==f8mm@+^`(@kE|Qk|&-oe6;P_ zKLD!EAOR({W)9DqU0X4akR0ZjhvJ=0FY58H%3G%2?oi&D`t7sITdciLC~tv|yhV8x zx^uNeat`Up+m$z0dp9d@l1|$w-aMgd+A7Rdm^QDRZ{dAfdBeRDCAMJ#S$SMzctBPh*BBO%mBuxO z0~#e=t1jiF_!_nERq?IXzBchK(!N&uZjF!#DGW+1IHe@0$>5Y?E&-!7Lea=mb&A7F z-cGH#h1>t$Zm!85d(PMZ`yJVoUzB<6?pJZQ$F)1;xka9huSWM}UfUthdRqj3V$G#fspS?J-~6NR6$csp}I5#j^8%&;kYA&7o_v?IYue?5~CN~9O3i7bxs8ju2xwVJlv=qe`=*8 zTvPBZp$Lu@n#O`3hX@D=`B_9e@{n1%mK;U)(sH3lP*1T@wCpH1c(2W0#Jo39I>D>8 z`Kz@TYtHmGYA+V1>21|stX|VA*FVeJhTX~OsLYSK2lNo#YHCfy08Y0{*x!K+CtW@W*vNt>y?iZnFJta43` zNQ0Uj>Fy|B*OXuZyqcWtZgO6AlOqs=0yQ~;Kk#aD%G~74c98?_m{G1t5UjIF5FoTk zc*ae_NjC|CuQmw+zBUQvZW87>N%-X=<^;u_NU&>1t=1VUydTx5y=pWV)vCQ}tQckU z$Plp1D4R#bYx9VBT|Dy3)yxU{JYA06;1btvaPis=E?&FA#cMaXcwG(t%SI`uv|N9H z-EBQ@5x?i?R#@CeVs<2x?;@T}BUb_Xv>uv$5Md(x2cwFhO#Y%TA zR=R6Z;zARuM)pc~O;oyTLNdbQff5QT-CrZwVl`k75T=kQRwyV|C@5AaC{}1ttk9rX z;lrwSYYGjD6&e&PG$>YRP^?f;3_|z0GztO>mMm`JU)%GW`;1@*%j8^12X{a)AaBC#iYVETHZY zM%>1T55P5{=7L$kyCc+(ZKDXT9JRI&1Sz;HN0TDD7zkOTjwVrcES@d~79imk z{9q9^0%v_O2;x8;?Vsvs|3nu9AsE!r{Hc!SPjoR5B0?RlpX!LObRqD=$gB}R=p#l< zX~ghaQj60gwwBg58|_MdKCADFQG8xiiGc=?6}}1UV(2>fR;(E^LZL(`ln4dWp@Bi{ zDkv)OSIzk_)@ps8M&oe8jeH8Z2JVby! z!fif>0H2K>!hBYL2!klL7%h3B)DJ8Ml^jM@Ua{`zZSTMT-PRYZnhW!6?qqZVF$BEo zhJa4M_l5GlP~K~czA9z?SPhFlKevm~U!jUpC{bVt7!pQQ#(CW1=jULFf@F+|WWvBe z0nk^a+J_s9{R}8kkc=^rj7^BXURt}Ax>&U0fwZ<^jeX94);y#iIFJ*ZGV&nKQl~Nh z&bIIViahvMxM^>iyev(A()qqLc_(|j%etIkQUht-ZIa2ri~!5SnrsGo3?gfwW^~(W zq7aN~Xc5o0fr6kcM+apIb)r8@wm7nsKeMNxEGRX!g8tNOc%+s;vyA|{J6FV#+Sgfe z5!MRmC)xp)#y^)Bt(!useRmFyqr-Iq34ouj1oN!>NdN|T?m zqOfzQ;U3y`Fv}H{Z1Yb=r!D0$xRheU(t1%)%+H`7Bdy-GB3hLB+4N%=TuRH1rAogu zKZpK(d>myURIo;5kU|Pk2#VDr1J^cCaFr4n<`*!Tx0KNg^p-L%pcE}ls5}dqrxYaJ zr&7j$6eQBb{IZ}t%LDUd@u=p#1jaDG=59^>5 zXjCMK^0dT3UeHiUUI0EZ_hG)kJ2A?l8!!jqSnohmQ6b(NLRwWwF+)Tq=l#NP?^NMH zITWk+I;fyl21&`6sDL&GfE!uZt8_y^qx&S?r==cNzH_>x1Fn>;6$kVu#jFjLSXF{@ z54vxc&+GiN4^1SiEclG@20z1mucXaj)#&Q>e`(2*jkzUDqPb}8WahH)k>*q7(dIYH zw>2Lr-_?Ae{DqB%C;2*S#aFxT_`u0|!rGeigi|%= z&65(bHAlXG<iH1+8I6`6%R35`SBr~lRg+5L6QRxu}GRerL0DO3|n zq3>7fA8l`4#@Y~lO$^qDD^`Di$ELFE*}zJ^3IUz3!`v;7Wn>7 zO7;8c{$i*qo&WJxk|mS$u6`~%;iw;(qtado=l>i`i^(0IQU_v(!5vw5wMxf-Cut#! z0BQ1rhdIG9gpX(eeK03@6Yk+HucWC`QS>&+M0zvq;O$%@l1rrPTx8by zT>CldvxY33Cm9NDiYi<$<)I4q0E|e-l~kbT2<~s!b6h_qDy5SWUrJs2vVhtc>MIvl z2dLV}Y}s$sMrO;A&T2#D;-sO|TogU*(&g?MD_LbWS!LeDWexd@KyUG~yYjyybPu~2 z8~#%+K8>Z&k*7bXdI3~)a-KnE4lcegO3Ud$!(~oAhL)TPg<;wx2#Fm@3r07eDrmy* zQT(1H1R^wf!G$JNhYVSe!4UeEoJ?JiR-cMuuP!_puHD33vR2$CjR&de2pp2N z{00Qldo43f$y)wd?Y}ORb=Rl0`^HH77?n18;bdGZpWd3>T-VZ=7Oj|@{vw@Q;GC7d zm(Cq<&Pd-!=Wf(~TKY@EmmO*R?0>p|$Hiq|`sW;ee%d6cDbpq$Y5y?@#Q-s6;U}Z} zYSJ5%qxZnrCM}p9{xdn?n!*#;eB$YAvOfRT)wQoSwWgFL3l90s1_4ZZ0kcw>E9xavKob^u zmE?0YS&zVwSUQpxJ}d=LwalX`FBWZcG7bEGrroMCD7_j=b zVa^)KKly?F9Lak=eh;4hFz+=_`-rlO!$Tw?NyvcmPC}A^VDua%iJ9zTkR7Jy&!g^p z;xc8*GjZ8^ksl~eP#UB>Ayy+{r#$gs)_YQ(csg4R+PC4qhw{XFoy9~~7QL4zs&&5S zyYi)QFsS7TTt#B9z;h?>nUp8S9qx$RBw8;X4Q9E+F@x{f{TqRLTMyeaA=l`2`Gkf<>sf3KNw4IZUFq*hxu#>Nl574Q|2>pz z_UJ6CTv_y9uDMs|dzCBS0g-F^V*I4>!JyHXl<)J*5BpJH60Rxu7w=GuQI4g6PFgMoO7$*|l zI3x3UVeWX@+OClv%6GsUt}i~nXF%$MT&0W>EiK)TI?llm;yE=OR#KH`v(iU2 zE2%0}tn_!TN<>(^mDo#tE9>uPyB+nHSdU9cn`({Itm@g)W;fR3^itfMcRU>gOK)7< z=&Mz?W-P*RnWPFUNN*))-Ic0V(uQQ+RYqeu8i(9R8J&p+gvYT?n6*nzj526Pn6U ztnKBQ07bK1C9PQxeI)inv&JyR@BIM`Y!VeA`ud-&B>i-m1JOSa}~4a%3spt&huE{z3B!pY2)c%Ub>x z8cY_?@m-EJKUJ;I@ugbzYk$H-M6I|Kep$;OHm@X)Yn@ApSzez_?E45k6 zOIR$_If1Px&W? z>O)EKSt<2WAExD{2cL9|_x(02DyBr!pR*!ceC);iNaUX#15zIW^5dj8J;P%rEmQ1j z1D^NqtfWuu`)+>1QGW^fF(E(pf&A!Pf4%H=)l1^A=Ym=K%5MSMIpW@qWB~G{szMxo zk$D0(?H$Qg1>#1h>KIamBZ3f~3U^A?C)<_$!HCffnU|+5ISKo%gCZs%3<66gOp}(J zgb|mLKbVq%*M(pYmbJXW#Dsx*Cgr|02oRZc<$ew|y^zN~|Ara_Uy^EY%44fuW5v`I zh#{N$%wzmC{h7!3Y5Fsd(WZ!>`2kJnG5I9*nH@5AXJ#IgFVdg+vI&$g(x16g0@E^2 zNhQ$|D~*F=`yUaGuN{)Lk}_b|a?(%nEtHeS_`a#>RsT2fJIpunJLH>4gnU!D{htk~ zLcjW^OH7!MufOTZ1a7g*5q+;w(+iNvn&*hk{XehCNlLm7(;f|xu z&;1BFQkp8cYNQauPc)y)rcJ3Df9N_D%dly6k&3BUl%E9o0-RcXiTydwPp$rdgiBpn zpE*$ShfhD8weshzU~q8?MG>_%q>=t2F~--sl^XeSdVT@7SZtQJVsMWG2N|N@%5DzX z{I0hXZI_r4l{Uu_Q}#3HnB@$Yee|#nq5Bzb7|`bUu)b?wEM56@^1|8J9wUb#_Xnvc zGzIp55oPa}(LPL-iZ1mJ;98WN-MvaeenXI7Nag2GEcFSqM15QUch5^bbYAn4s@PFu zGo{A5(IS$A?5}yIPg-Xm*ax+faVm{GDyzc~qn< z((T(nF?jYrc=|WDi4{WGK}(mcmCpzCbkj`iS{OkP|8LY1pq^cwOlniLmOsU&E>ftr zR{ZTxUE7V+DyN9>yva#3SbG@x;5)IgfF@)?P+?No-hn*RfOi6S#5|96qXS#_pJySi$-k-D?$T2R!RO4n7@_Vea0U@l-9oq14A5IP{( z=v^Ki)P<_Kb8ojjWnI*Z{cj08OyFUxfvodxnZU!G1RmzJeZMhoJXDtLIIxnNAYd7W ztN)6ZU5z=7%bLBlRe@z3+LXRqr#thzJj`-hJQ;IO%cN&3Mc|@8W3DspKO(KD`mAIz#XLHltLW zN^ZLzXFoS=7VYtQ(W08P!$}O-F3v1EpNUr7k=Bk~-jekiU)Hx|Sb1+j-cVovm*kDT z#f}W0urqu%y0PYho!C~448Jim8g|loOSYBS4jw;Ujg7&ll3(s1A%CUw5LU04?n+O( z94p=KLm$TO`N>DxQ*kMV3)iz!A{+0C)S}ln$wbt#y$CN!Lv`VlkpMu>J%PfU=LQI zoN%J*5{gZq!tpha$T3gzq2OHlh@A2?A9I7HkH{fU^CA2%eMFt{q()BUB3EHBI|w); zBGH+xgd;k0lyC&+tr?K33+rDKSo$Bh3Y`mA#x^F3Gpq%A>YYqY_%JpV) zU>hj)*uGuHgVPVF{`=oMy6V4AA!d*IZ-iAjAa&|MPqj0qySrg{u4V7)^TD-!%a3x# zr&WP+)Rr8t>R?U9gmUuM7ldmatZH-Rt-^|km5Q}{Hy1k?Hi%8xFJPU`<4TP@bn>yz z@T0d%amRMCUdzw_utxEB%i~?~_p$qX%HOP;*=MkggzaUdRFs%j`r@|!Y^7e?i>mqW z|MXdZ5v~qKY`Yscz}wIEy~X4{&8qU!<3T>_)F9F${XEQ*Q zTzUkn{zrLdk8y$3Bx5DHt=z(d{g3u1xAoO{_x@k?Y5kV5vMO?L6^4CxM}yp{Cf9Q` z{DtpvhW_5V1`$vyvJq8X-@%(Y8syruX}ou%LGC=O$iRDbLkI8OXlP>kJcRLTuE8KL zJa%8-)}M?k9J?0Cs1#>aavWSBO2*DA6tR*Q43`$_tXc!cx17!W%F8x0_j-K$^u2x7 z9>S`#$#tDDuv{?XV_Uf-rlF~~JlY5NdD$=ftUe5WrYsOhlzVw%RSjWw!QV>_`ua8A zB`+8z?^K^gH3i`3z0Li~bA6*f!o?19*8F6!9W<#>xbwSuGws5zSBRI)MPSaZCRv4X z;Guw|j8uvTWv=Du{tP^n5=Ei^$=-pBRN+c2qDy_xkuW~Lo9&d_bDnG%JHb;?$#Sf@M%73&na z+VI1&bu3xdVUlo6XG4_Ec5!Z%g%5E)B?}+olzRs&u;PMjc@lMCw!mSv!w;EZeaFgI zoGM>&s(i(%^2Oo{@=cAIZ%A)-W4`y>`HJ)Zv-kD!QC3&}8A3v=q!TJ>1J!mc<8I<& z4O?kR)$I%>c488ZRZ3!|9YHKr*rr{mC<#PEETariE7o87o}Zf#jh>mxfndgul{#E~p>|K6*TrDx>H*BA8|{picc`M{96 z?wdTR_q2cyGq6^(?g|H!80kkBI$tPi$cX0 zg;pdTG<;E<_@cOqq(g=;3KCy=wq{u9iqiekqBcMW)E){z2ZOwIhJJ_S!XDd&pAQ=J z_3NLV9es(3i{=UYJf8o*NXU)m5Bt1F1R?BqxsZe>HkR*c`oH+dFOF=y;m^G)8+%5d zIQCSx>9sv=T7_VY;IUI2Sf!14SVfF@7;;4fTO>PZBe8NUCOe5?%}zXw?1WB~fKnNW zR8m=pC<186AAnoWRgU!hcs2H?-R>V5uV{jwIi^>-N=#6Vw*FsW*{g)eJZAy*uC>8S&ih zyT*9FVg6b5PvE70`2LB*aku=TH#zRy_DjrsLhSXFh+R( zC*Jd&MgNrk?jOE?$YwE{gd6^+H`xq_4Q4~(@7j3-xz82vK^%tRHIp}~X5zj&%#0`V zt-~XDuophKwnKbe!TJ{X2<#!eilMl?Lwvl!`bHyk5@C5q6>p^xUWbz<#$^`9ftcU@ zm;|GixTj;`IG@6g~gE`-k{NP*KlTO;bQ&l8qO=> z*)^t_pqVby5;NNpb5tazX2xPmXh|egGvjpe(GR8+^Ln|j0cD06+2)bw<#L$6=5f%(iVZkj>@ ze_53t2 zV|CBaLd|W0 zZsh{mNINrr`}U>pKX$va1*}aRnl>!8%@O0ISFhqO$E#;g>KdVJYp621=lRN$(~U-EkVADTCI2h zMa?sg2sTz;aYYak1icYtOkRyy9U77Gj)4w}n@v1p@T6@=7*6R9h+R;`2%w zqqXN1X~77BidRN4ZaZEnp|1tK>2z-{vK`^S=9Ax=eDW1EaJ#$TGhwB$t;mNOpa)c4EmL`Mmt-SGoYPE zlryZIZ3a{6s}|)b)&nk;dnU7cQ^6j>>lHkS@OvsFd4U_woysY<6~MVyIalanjw)x9 zc7A9XSoAH9s=8suyVoh)r>k4c!gO_o-E?*30e1xH>b|72*416Dow~aFwX;p-pTRt( zsZGCWSD*yuoXyihQA_UmxVG^q_IU3on8%=0ED1I=I>?*SD zW7IEM&IjMcHn{-?ooXej%Q*^GV?2U)IWMfO#&{&Km+*UpzeZ>$p;;1nuYeL4S6;za zqJGHzN$NctcU=SU-%w+DshPcd@z zK2rR`?E<)Aj%y4!$$AR6qK#~NGS1Qp8Xga*Bwgzq$_H9-hneam_)H#(S!G*d`Q`M2 zWgK2phq|Vw(s`HMD_=geYb_bZ~A4aDzBb)=u2h%AmIrS2PU1v(m3Tp50=Z4);{V)852e~Pk#{l&nT8hAl*l_3 zEV1`0SfU?Qu*CncIaw8hn^#jg&p=TyULupp$b@;JU~F4&wCOjBZn`4*ur27mVi~G+ zlX|-&ec9e5mL24$QF3so9OQ2*@aarbXpBwM}b<*cu$f1);cOi#P8lVd~sK%g{hf{F4pok+@8C56p z@$|wc{^YC#5fnbL#7Xmo=ZV=)&flW1N`3hW?vY5lCUve7c2GX#t+oyD?=}4P;M)mm z_VwCJJ^fLR$6K;gwh$LDpg`=6YvR`U9f_eu%L1T|amkGYcO)UCnLG2vxe_5`6xhO$ z54bOXCnK&wNRbLT%8=dMmA{K2TM&|`LQVrrO5P=6hj420PJx*s?W()c;B5HA=m5yi zJF#0J=}v6EOG-UzYyP~j@ri!3ejuneR3rxzgG&WVBG@o3c@^Aal3Nl5t%fPdc&K)1 zQSuSGcLy6LCch7NUUD1QC^cDnGWoQP#U|-u7qu@ov6*HqDR9ELe%1ds5J8#*$61GdiPMPhJ zy6mvoUP;SNNgGYq#F^F}S>w68RFH?fS~r12Ql1;suP&yNK;-qi2|_33S2Kh(l6oy@ zgdulMa81S7d*3H_WU#|L^oX)-aMz98xtW`F zGB;;w-r5=pp7leY`3%?-i2_ULy1=rDk5%vAmoOdG^~*|N&OOQ*&`z;C)#{n)!o{bQA#R_h7CvqQ5kCJ% zFv=MuvnRi5kE3bzE|X;RW;K-3sl9|{TP-E$d(K_Lc^daN_12u*qTb)v8~Xi6L!SI} zyGa}$L3u)*3#yjpAj44b>Z5U{jzUe;XL`|45IJJfCyzRoN3I5wdozRv-+X-zc+#o?)N)46bsAp%s_pqAM*WsUL zls;R^d9tOX%}@>h8)^9SW=r`$-QTyjFZSZIr3}}|1|Mo?Z@S>Nl(W6>80N$kgabY{u#Wif>Y=%1W{85-}dwYAb-9z&s z&=>ZDU@r&_UirTD^6KkH-9D4OzSl@l&97)8#G2y8yirCiq4cVWus7^IA3b^Z%hkO` zY-;{RyWvG|TvX&HD|Y5}Sy^QldNOaJ&^~$DvHd9F?Or3{kD$C1B9m=-{l+dK*%FMcJF@!(%U2T}1mzSxHq!u<6SzEYK)bMC0byb7I@SCLiYxN%E3zHK<``@OxDM;8X3M0iu&_L#recm!y%+J~|h#_+ax+XSs;|2KP|`+e%^!;4cE082zrs z@E4%d9Cxs7WHZtW0VAaVksQN@zW|-)c$hi1HQy*(D7dw#f1K9C^NcaA2isGe-9_zN zOr^B8Si6*GY$Zt_rkZXxwLPYcDd-*xHe833~1K|*u>MxCZzW5p>nad#~E`u zW7|kxaoXmLIi9hdRF3W2>5Mt5HseO6b2YIgf439U+7)->-L0fMv5x^SJ9|TFCldE5 zagWlw_;KG7_bf&BD}5C%?pJCT%iOn=5if??!<2$OOex?e>mH^T)n+3sg+GcAZuv~) z<`nx}KLY41&|8-(wEGSh!u#9jPSNgfdErj7&mE`TzpO+)@JhDNy+wQdPmS^#V4wRH z?e)I_c-d!XYUf|Wa5fKx3yblPc~x+7(^J@{Ha%gN(>8+G<%Dfb+X-S@6ZSDR5X3$v zYy#Rr5ZmV_U>g*{7Jx;`I|&{In1|g>0HZfLahK67bQ3o>eMY*=e2ID+PyjOhW{nAA zOH;tcjZ7=?oszr;--*dv@MWRF25fD@w}@Mp8uGApi94y(2But{^}F|x63ndSJd)H7 zh6LHR#OmS7bJ?cE`r+nt*_Ol#2J0ruHY6zw+nkIla$D0LT}N(X!j>edB)2VbtCHHz zWo%E{1Qb}yA4H)N#Ev5DTT8#T|1j0-KV!q|ZAt0Z78?83Dz!KFBw;hsFc)c{dSgp4 zqM+xJVPSZ7>1aR?pYMpAr}=2?PdHQ;Z+-9LHfBsR_dCh1u%W?*RP_n~_Om>K18hTO zL29Uj{w=(iiCbI{fD;QU#Kn-K0~mrm?g+ub1v;?R9geOZTd+(gKfe20{`b|7GX2Eg z-}3%<;>_=C*nb#BX_37y`SIut!Nv)htawz1@UuQHb9V11Vwex(XI)RgZ9vxZf3HYi z8k0g!dOtGf|H!`msG)ZMxp6Hs!1)vq_@gxFh8*a@^_}Sc_{{%(bo+yx1vza!UpD9N zIBX@|7Gb43;&h>fldh9smt27^f<1EXY={JTymYknRH+Z|`}(-V({9Kq4Mc88kEj6iJUOqF9R zP=?eL(LVPMBd|XMIay5N>6PZ|4S&fo>g<%m2$K9_PEaKpfs&tjj*9b9``n!afTk{B zGhw2f*seCO8bOjrOaag9Jj@#WL}DkUw%LrVXBp|S871c2*S$LRWMk%hM^tJv(;EzL zIgZT+xf(v~sIqTLhn@E`U!>~6DBE(h3+UWfcc-9p)uu7g^z)=iKPOH4`8MfiJgAR0 zYMdp*93CATIrUW43cJ#1BOJYZg`AJG9)GH8#VNVZ8&S|d?ax8sES`PtsU-Ha^&nJp zGrd?aIy_#%D~)i;mA&4s;1_kS=qB17aOg@3n=dI-Q;&fHqKf>Hk!AZ`NmAEu>ij?r z%&%|)4ich%UyI5wrm!y|Cn-q zfdCTXt~)d#p3{jwZ3??)6LO1?7LWejc;7b^Y0X5CRW!{vN$ZG7THhpT4G@&$_AvM# zw6zmUCqe>oyBKlM`1Oo~mW?!rk{!R5v_?yUBiUm(VAuQ;Bh&VstV%7@IYwGvl~fXy zV{Ca}H9}+i`AVIg(a)-oOO5D4gRw=m(2hzni0NrnwYQAW*slJw5g?gH7x=6ZCRxT7 zSZ;($ma%pFHbZsueFHGb>6zONxpqF!YepGCubB!QB1AZVM7ZrXMTEzTqlwUG65$6X z5$2ExAH_##tRr%`hY_M=iz0`7NoWvQY?J+q5gJPlpVHYy_s%IG6_4*#QSTX{amgWI zgvOG?uZ@5>nhbW|Dp8__0}^)mmm^7|X$&`!C=pB=F1J&q z-3PpcCwF(*7(5$jckTZ7b3X4ezmZJ@n?*+#ej?~5nI08lIKaLib^B+4_t|X|w_NVG zg6?MKgG)4_)EgJfrqSOLSuSeRk(pUB!&mcjKoWaZV!1L)-5+X6EH|%N63Yd9Ix#bo zOZGN0Gr0~|U$})+wav32htMNfqgjON^4RS~QDvO&OvECWR~`IKY2FB_L;AAMUjY6A znrS_SCQ9mul3c2!GiJ+{bg&UEE$xcwrY;i%GxJyy(>;l9)lKI{FwCqhCbM25pnp^S zk#UCp;89(!SgIyIQ%qqSYZG^i$6}lGoOtg1HDOcgZ^ZJ{rM-h($i`L}To~l;%@gHO z2Dh_%pEPu4dv?tF8!l9w_gyH&;BhQne}Q8pdanb?YtR#=6{g5YI-I(6#|dJ|s(l8# zsfu0sw|xeG?Op~Q zwU<$y$!ZjWc;D8!cyEu*HQmF;$lxsG5v9Tz`~7*U%3W{S*oNt--y>b;?(G7?XJ$UZ z&?=&1nxvx|7+w8Hx5o>`;-YSMyBy-1Y(dZcCX(~2C#Vj3NOAjpcTb7G@;%dV4H6Q33 zpP~g{C-l`fK0)0z7}-9rZ+ybMCO~aQO^nY$D%$7ujZbXIX5l~=K{e0+#~PnbJbO0d zlZ;Kqq967*Oy!>6a_4j&nXi=5`qHbhvaHtpuKM!zG!>>@GY|iLN9K7^-g!DQzg0K< zG->$rc4YoBUF_%D7klv?nLqwjMVvpuf6Nt)@eS4I@%q8u*iRV}d8VgMGFsU9=#h2I zfbtJ}s}V+{E8zB)Ry7}CWRZ>m!{};87OR`ZgjXC9qFSwCVy~qnfmKOjqO&9xE1QzQ zk~qGlRX@zt)bM22#Twojq@4$MGWO!lfxWiB`b_<48LAv=a@R=V%#J$)0i>`)RLi%| z;8)$tK*~ys^m}ZEGskuVo7l|6ox#jai@R~BrK4%vg0rIl4sEde;V8Q~?OK1`1)6sC z9RDA}uGCw z82nlNirnLU!%%{9jdwY=ivpHwyvwm?6tG<5jSb|4)e9AFxz-eGh+|LlRqbDX2~u}P zT2s*d&=>dFAG=yiA(!2bBW?N>_^Z_m^?c19za8$XlUSy{iqY^|e3QHAW!~<={n}P~J~mh0rM1v=-Uu>muhKIc7ls*4&hKRRk@I;Y=QxJ3>#prwLSGDf zs(a|Z1w31Ak>|&JJPV$A<85q&x{9&OaUgE&Tfw?1V>jRk+*rH@G;ZuJ9C{nO52xM6 z9>X!Wv3$qbINxAQdj2{yu6%YyqVE%`Q?I*GV(rRuZ%B0t1|Q}j=Y#A;~+JrkpC*wNtN~3R9Qj0 zp4z_59%jgdKxPj!WIiCXhZ!;*P_qZv8;scl%?rlt0o#WGtJ%Xai7vATb<%)^{tGnV z0Df|XJ+yY{&UzYv!28qa))N4_Gw9Y60JtAWK}yl@nbrN-4AuHP-J|H%`aEN&A-1UO z61#+~`8T@P=}6PR z{Bzyudg2c=I9}y3EeFpzJC_)}v|w9_R_powMESlWO+TI`^wQJUS<(!Pvu{VC;bOyB05$4O+4g)Iy~n zq7J1@2nPX99x9QW6fCj#D_EkpGQY`Akw22PmOac*iyg_t!3?*lgqeh!6)e#YC|Kej zR~d?kk_=0fc#>RPgtx2M9>O0exR~%k1xx%BDi;xD%G=8*4GNw_c(;Pf2p>}Lbiya| zAE~codrHL|XfxPo+LFtssW{Radla0;_=gpoLHLwPl!-*Jw6qN9^mx64g7G`QG1W#M5t6q}6)Uc!JxvpL3FUsAC?65gbiOlNd@OGRl|*%BV7FXdK2qucJzDy#J%` zKOukrdRnjPceO*#8&k&UcT^VonDK0xQK?;BR_eN-&ULKFDTAci#ELJjHo+ISnBe<7 zl?DRm@(6mbjE`1({C9t-#>ZDb)2;Eby*>VO$f&*janh%|$3r%}Vc)&QT2ntG)0lc0 z9cMoUm(-%*_l(6QGUgWfHWb;g$kE|5e*es{?>qQ?P>5+(*B<}u$@kc;^zrqw3vO@e zQeVdQmdxEYniDu<;%dy(?w47avXGr8n^@23x7Pmo^}k>6{!yzl*z-_6Iku~~HKF{Q z;wuyD)oON4=CpK}>@vufSZCdBGMT1bq|CHuy)h_`?jxLs;|9I?< zlxy%iDxn?*yvEO7al z|KjLh^3!yhqG$da8~E(Inii6oUz9w$+~G)LnZS=SIKC*dVe~S*-d4G#`(e91%&n90WfzZR}9W45~K_9MrFBbaX zPDyEg?mAnEnQ1wN2&KZv6CIe_Jg&f z<6@x-uv|IvJ{7z7S@0O|?I&vJg9o3B~ z-GgnkrH8SNw)7P1fqV8Gsl|yI!$R8v0K0w=O1m)JrOi zp&0eFhGOR1!vf+Q^^3DrACT_sv;#?Kv_Urp|MN7q_l7;s;GeXTKK?mt4Zn|nsMw3X ze=p`ORLciYpYN*j9L+!R=jz`-dVvP6Uf53*XTHAR&Itb*XA7S@!YX%tlODuZH}^7N z9~Tbl8Z-2=BHy|qn>@w(yD-)h)hLGhzgq^V+4Cr3^Q;7+cMs!O^%Yo5TRJXvCvHXX zV`U7VrR?}4M$nkly%N;Ipw}66)Cej{Jt{#*7_^o_$Bdx7)DI=7jX_)F)HY7iWd1Hd zq-55aEtrU#nEW<^3GQf*!o&yyCK6=9#0UZ=5@f-|2m&S&6otu*wBOa3m}Ckj;wC0z zBADPMf+$RkAYdXv7EFvFU?M>lOpG94B0(AxJ@;mRR1z1KjSe&V1Fs|)U19j5Sn*Z8 zVECd$@l_pQ_@XfJMcY?o-tTQk2pKGIztLS4smr9hLi9ag_)bfGQ+!v^_n_fBC3U^{ zuA}cE!*^opd*Tb)!{L4Q_5Ci@(P>hW(H$sBx<5?`aF z8EN;+asy366DpGS+jSeFBx!DxRFQPR@I^`Di;^mm4jR5FNqkXKMbaU|7bS`BR#wF6 z3|&#W9~U8{F0%nT5L}9^2pyDXst6sDSE+22g}7m(w{O<%(f6z3$g{w0?d91U`_HpH zljBB3zV$`6_jr=RK2PmIugLs;m^>rH_5~MjJQ!l1_qZUWiU0Qjx-Oug15Qa%IL%1= zeT|7zpU5L0_lZ0VF})!2;;VmUdoGmvcb@dWTiVOZ=XUqHyyR^|BT)?2X2PLMzulxR z)4?(BL09ACm^w=gVh`Fl3}d-vyLqB`LVD`9e#XT?yq4%DvFan`{gH#j80FqbWKyim zASqUPdDaa{w<9Th<)$LN<#oapsoVRDFUIQV3?pZamrhB2N2V6LB}2X65*ao~hPx$0 zeM%V_Hb{oMf6H>!-pe3;G1RLIg`$h$Nx#z^h2I@;o}`51&VS^CO6W1Be$AH0^adAI zai${+&Uj(Y)*}nfMy|?Qi}&M@ZBMKxoB4{RWmk_a7YlmbS`FstacqZh_YRC6dm>|i z946X9)4&cHIqIB+96|Y6q9JoZ=-d58=V>&MsB!rR^HMbRX?^zZ^?{ zFK_N``tw-Se!26PEnU*5_SbV??AfXP37bAnYDa`edr{{1%KoUPWB(eMOCK7vOk`s49vI0I-z@RXyw@pj zAWt1GP5po)IDX;cFh7Tb?rBUjMx{~lvKSBf-s^P9fH++G-~pE<6&mLO58d1NITCbF zVJ<~RE=-49Tq+l@%0)Wo0dGt$lPtL~*D|`d^MhJUWUhHeuFMCydZRimNE2q$C!xrP zS|9X0akN21)G=NW2j2gP>TK~w95y4aurKnx!Op~m!i`By zUx3owwZ4w5R^Hd8^M}S6!n5vin$odf1^Tp-c(*>qbHkBUhwJvLbh-HPIv$R#n6rRydup<6LM5}5Bh$hu-b9aeHw zL#%2l&l1(^A{DDqJ&#trsK{R^24sbYhJ*pG{E|si`M$N9qCi>im{CXwYE&3LoGpHR zaC|idezh7+W~2~==+-nfBaI-0ZuMfh{$oZ4!3KWzAuBzUAS-06hmy%^D4CJTNS2(k z>9*uFDmo`Uz%aGsrRYr!Ff)o{(w4-gW$diUQNzmQ;WDhum>wIK#EG~NAP1p?kQ70W zxVYC%5%T_su8I(I{Xsp7^;-Xg1Y&Ms*l7v8psai(-u^z(-#+hCcng~Q$*|{eagZYw z0pa9nDZ$az&`TsXxY|#~^ee8Oh6DW7&`~4|9PTHR`W1&y%V5|#6$WnigWG`u7#*PK z8q+YWS1;a5AwtHNOe7Ry#3($|QaGeSRN=Upz$_dRBC7CFmck(|jKW<6RpHPM%);e* z-tv(WYAM`fDcloNc(J8$NSvs`ODu&$`WS_KBZZ^)&BDE^<`QZt9Jl3|%|1yhdoeha zS#T(e!C|@uhv`~LG*Q9j^hPrzn3zD2TPR}w!M4++gkQWHjGC~L{MaVQC?-)zCRZd8_2$6Mo z<5-2aGCec{QmBNt3W*9r^S_Umz?NFJ+$c0tlH#EWWd24q8*w1vhC{;Lw}9Ek&@oCS zqcL_3s~f={UejEHyo3gHs71zk8M;nF!#cE`l#}Y4QjW#QZL36tWho(>8A0MVIwOmi z6@ptO#jI$L#H0ukO;Jd=R3ow)(#9GIR0&APKH6v`vLi^m)Ej2oj4C8ej62L~z;I9+&A6Tv`!YWaD0ZHT-lW`G`y< z;tH+IUvJ93OF=bE+($DXQ<_2FgLo+wf2qhL!9MRl0i8)I8pI@&Y|2Fk=_ta~f2(0G zWGPcJ3cp6A%ijVzh6tEx!bn;!vV^b>-DZ=#yC7Sl~ zk)c3WG;>2L2)d}_J7jI5TTQD_d-=#v3=-9_At{9raXOHXj0&|c3WZH45fV&vFt69xs40FIAHr z&4Aco3I?HcO+KPsgpMMV94j9gqlTi*B;#25$QVl2SOttB9~m0f=@n*Yl8;?k4+yo5 z4TRbB*-@7>Q$u>c+v_g65CogmGyM9FQru5{u0H-IvuQDLn zbxMW5W^Z(p3K4yYvyurxWjmRAiL*vK<*lnF&VY8x&07J^l_3;l+k6hzD_r<2mjs69j#k~LSn?2To@k2mtqWm6l3_K80(jG ztrBBAI9mQsbSq)TBd+D2rdx@$sh=R+4WZUc<5^sJR@2-Oz4Xbm_mlvu)FSFa$+I{l z{J|f)PQs?xizZk%himmhNi^kVGT@H^Rysn$m(lZ;u=eZ*E%(&<_Bi7+7NAW1Y!%nq zu|~TSPj)SLSDnTL|1}`1X3haXf5qxaLT=W{+?=&;`)!+ZeXVuj#C%(bT^zllLPHT{ zWsHhTFV2fN{by(m^5ZCnJra8E%(=9lICo36rSe?0+eQtUT5i?J1pEAnFai+ynFC<> z%CBm1G#%S#GOAkUrBK83=RK)-e#D0SNz_8iWW^l4_^-i+)YO2DgxZh2`a=gjRwmK| zQk_07`m7m1A5&2U+}VRYRDl1aG*;YFfFf)b4Hh9n3J5`22r)HUgak+=2tk<$IZF+< zNA@Fr?D^*}d;0ii$?z}<5&}zRaS{IE5QF!WYMqq~H#WV`u8lLwoIR(-a*@e-(F4;x zirs36iDD{5j5kn?QV|0>qT=fw8@Md%Q@x~*S;sHazK|9+$+XYDpmx8R;#}_Pi4y0y zhG4x2c!+Zfc(2^uBLZG_!*$69VM_3kxC$t~1iyyGSS2{pgoW;|N_|gcdpA^~F6{^F zsrg`FYacyX?NxD<=|#VeBjzA;3@xI&WcMf^nd-`%@tamjC&U-6iGSHD;DiJt3i(hK zW+6g^b%O{k;;r}SG0u7C>@D1jr`}624pQFmB0HeB?$={OEm7Lr=;~LLty8INa4K7; z($nBnwob*`a4K7;`>!cvbsA`S9&_FcH~}ytt%=6kefG!fi442!cHEZ6yC%CsB=ijlXTF%~AB!>qDZ>mVbJ?!w%m$A@or`xzoWnA(O41f}AJa5EQ5dgjs zoPaPR2Y9>_347PL2}W?M;KFwlZg?8;Cf*MN5}YtCd7F_H3fFhQn8+AcxQt0| zv>+3T6o?zx5N6~6WY$H(w&I?(A?s|qp-ylIQZzESj9(+OM(zthOMP&fZD8CHQ=qPzIyfOOd6%V_<&mu3k}yj#8zN!v z2i@13R4}4_DGpT%Zqh3a;Gvvk`&@~HqCpL#NewsM7$F3Btk!u0+!(y+p`>P9%^+2H zPsqBMtT$3ydBAL6TnDcJl#G=T*hzYxCS|<*ylbf zIZBrxugwhKElU*pel#JbBXQrpk$B7QBDp-U`M{~uh>e@nnyauALqW+O8qj~3&|4<- z1x$KXJiLXbpxrgmHvm~@ciolCk92KG;(V(EUE3diR{B63`TNqk_VRa0%|$JLWtMM> zUptWP6TdQ8j9<8=VD|NBmNIhSk7&LDqMk`*>^5J#=4OT>D%c`Mv)w^=x+v zo4u}L>~cKXH1;h#+B9|p9&H+n7pBLJ-G%pm#_q%WKVy&K{U2^CxDoN(BN5Mi4dS`4 z8vDMXgR@8FjXL&f39aa`DPy0*9F)VMH)10Kh9jSR_Tn8Wo^sBC!V;sbZ~M=_(LyVM zeWQgjW$PO)dULdZ9r-zDC0X$w(|mV+_4S0;l)TS)x_9OMGb`iC`{$p!*yTNm?HLf8 zIX~F<(>{scC-E09dB4v*V;Cgw?`88$l*H$YXA8iPTuoGrsjtxb@&d||8J{{GyeTQ; zG^~QDcb`5NbIwP?%-);(Q*k*#zV98iKXsLHzx3**?d?17&bxS}c)PV3ZmyAAYXh*& z;@C>X5ZJ7@o5pjx(=29lx-%?h^L|J@6jyk_U=DEdP#ia>#av(q}Fxa3`C zuP(e*)^IrQ&t`7g4o>$dy0sl#k~)}F&QebviZkdm!iYc4pVRy;FwUMYT(VVVAGtN1 z*_ALmZQqutyxU~SGL_6j_)P`lNczGh>lKXS=nI#;r?TVi)yi%y$$6>ClI1GeB*Jeg zxQy@y1>=M|1fua~EU=i2?c}set6&^%Wcx~XIAcglw7@v(M^m)K$6FIHH7kD{F=Pu# zoKb`~sW^BHC>?v+#lM99o0b14`frJ6$i*sK%!bP@8fhcDY|d_1jb-_(7xg*I{X_Y_ z^)j!{4#e{3;BBFb^ybi%!G}Uqux}e%zOio`U+mk)mT&Ca#`paZp{{NacXxd+IA`>M z$GayRuuS&~m3wS`dqp-xzEY!IvcLP4fOg6DZna?$G1czwb6JEl4;+{axYz|Z6_*Es zI+eH}WQYoaT1pVq0s;$x_m0G_MiCL)7cL2tMol>Zk0yGU!U7)q>0wF?c(l^PDmJ84 zEizb?mKIW!L$LzxQ(CxWo29HwdNfkOEw)09@d-aLIN{ z0UmmMU@4%O9tSN2NG>NV1&CDPQt7=Ty((8P(==FeokWk_mR!r|ambSEbb6eWT%(0e zh@u=qAfU1qfsT!n7O8`TIA>@PJ@CNEpNRZH7Fb=i$RI?(?N$~+1P`3@j7TG-02Nv! z5+dOCtcX}b1gn4M`B93a~7Wn5g< z(d?tW4$|eBMNc03uxKQ`w%;(0K2r0SSbo2-`bhSp-IF!$i%cJ(l2`b|5@#j#AX5bO z=_aP4b@A#ZI5vV!I?uD?pdxbW2!-KnwG?DFpuE(OZOEvLx(d>zlwWB`I;880#u8uT zg=ThL(4_lJH3rj;?-f(cg;%lKnR#ohv|t<>x$WVuOPvd& zX!YKH=Q#S6>-S2&l(1ROUA~l1)MJOn4lpAwrKJf3yEm|3W8?4I%a;qQ$=x@-d$R3< zXmV!~ayTXc_ z+*g#ghN??6UfQ)%o!4G2XSR8So3)&^VyRi*t8gSh)s*|v`uM&*m>p|=34|I;R)$uU>{D{TmZ2{d z`Bq_ltm!b$i8Q^c>Pe%nIshALE3XS}EpH69l&=k~DnAx%n3BA)U$CJlc@Mr*k^`3! z-fM~EN_m9St?q6Fav zMlBF;a!HB-;+;lskrfakA_nQP-+XOE#GXh-l|QnzN?uhoxn2rooH+7VVS!j3N@2zj zIj&`~pf6HQ3t(c^+8(Q>*N;PK?ts84%BFr1U)!{F()0CNGN;XO09v{GGVOUc#kF|e zy%O&>cDJ9c?>8K>f9}K>cU`z&(S*RQ{=sz~U!!}h`Qq0s{b!-v*-iG@&P#E5uD}0ckH3E%BD(B9U0#&48Sz~y z%k8t15$g7>4dE&~kMFHQ-wJo#?tVqS)%kS|1M+RJo9joR<7r>>RPC$uR0S|vHB|;1 z@-l8)V1xIA0eoiFfPpf`uC~#C)=T0arq_e}ZJ{HB-wz#0e=F27_`_gBn{CU+P)quH zLZ>(KyGFh%H-;K zhL>d9B+Zf1H{h2oNz!ZpJubqYVqEU=P}=lLe#-)Q3^>EHz=lVLGm3#bLuw=>%#i6X zNr);krV0vuR)lENwbD|Vqo_>!`$U8xsD`eBSMVuglflRu57 zB)=+XEA;JRYl4$<`~w$ZkU5=iJK>qIve34z$Uku9ZhM5|J-!tq*7$Edp7WaBKN?49 zbN&C`iZsY5q0sl1J8K=1x$9aI`M{LF|H?w&+j+iyV7mVKRm*Kfz7O;3cBb6wcV-*! z{e+HZrs3yxc#;y(fu2X|_y?akX+Q)2&t&twW5c+N8ZQ9w;gpO8E(J}@c&1E2(=z51 z69Oyl0qwpMYQq(iZMa&pEo-a;M7b7x^}6!Rx)%Q{8Q{X_HZO|!NdnU1{}&GR@6$#9 zr&07T%%Xo{7X6=Q(Y}s~{#gxLC8qR0RMJmj@UwuxS@XmEKb~1!S!jcln37Q(fS}3r z*09>%Fk#OEd`ez2>KAO7m{9_Wv97c!*f1@_vl=xoT@5*&1@f!NDvs0{-k~F)g>aA1vNxjH!QRLO(i`n zr7Z}+gCW8~el0z;RCNoBS*kiq71J%E@Yvq>m>D2P07aZcy7=3>cG;U&W9~l3z3Ot7BRf5BaqYDt0iuIGfzE8=SWkewmUajpL2W zMc8GIpBNuq7&+CD2}c`A87*$2~NEN(-PZ^seUKNDf7-a!vxwRs~b`1Fq zMLz3O82~_^%GLC-jGr6bRbh;ufm~a?WeD}yjziez<}9_(zLt`AOR3HOFvm}V?i2tn z2ca}VExmMQD;_&aty{j*&SdSN_IuF+t@D!^K3EO>pI`pXp^T91&ecT7-xq5i5tJ=zS}*{{cZPm#$l3M3IBu< zYnt!EK)N8`pL;w7b`KPVTkPsmJXLkjGwU6}x*3Dqtwf+nzzl->1sp}NRX|L^(0E~i zrwc5xACb-D43ZR&EJlG6DybgX2q(CE+7r#=AUBHqcRL|BAyQX>kuek}Md`oLfjm{q z9ngPM@}UExVxCrN3r&PF2<;~{icl*d-~C+xxUGvvYo<6PQx0*lr4vsj=nL}jWP<7hovOl#9$uWJ%8?hzVww^PHOL0{p zWl{io2qzHLLH8I$QmBp|W=dyoEV$L#EZ6lZXIBpF5@AKKoQMq}xeIw?hEb z?4Su5VG6`yCNedE-3@|j6nK#g<-dftIVndXgnkg9=enaw;Pj!9eP5+2zNOc;jV03 z2x&3GucGG{BvBe<)bv5Oxahd07!qKkz4{sug4Uq(e47h>t8eoUUacEQwCAVLN1@lL zJWoLK8G}9n2i4jcG=Bh06VOF4LqKnC&i0x2&@Ec9_@&U9DPShSYymw4M+rEID(!H( zUE)q7=n*iRV6lM30LwsTGY0*z%u}`02E;v3`_-Iqw11$V8Y=Wmk8Kll%oWf^-8rw@ z{a=RoqB=U-f4LvEfj(N7m$g-Nh?c}c|KP3kgf7uP&$l=N*-8=wQo&YuZ@z6abfIbU zpa*RQ;=hCvw9%b&$nHOces}wh_`j{~24pc)Vkh-g?Xu!sX{=J9o#L!5_@(=EEqdxmba#n5dy zsctEKN7!8Cz+IBzCPTSV5d#|;;Ts|czL9N{sd!syke)+DHYhnTTI9fJ%7L(w1E{OJ zYR9QO-}XFPL$oN!%h_X}Euvs;X@VgN`a$%&L==p65EM}`+C@-A!Dz2L=WxY6bep08 z$^e`q3PxuV6j3nRLr_G)Xo!OMAPV3XQ7}4v1CXPps|133+KT36m7k!l z{$g8IAjvafjVg8EY7^^$0TbceKuRpeh5m0r>OuQkVW!7U(K`&z#psQ3 zJKt9iO)22Y$Ahf5`tKanT9mU|#&EF!%HVw^CWuuh6YY;>0MJvD_M%p#i4i_gB(bTH4F4o3Z0n(W)c+gzQx0s1$4V$x)SENC5=vx#LOmG zEMPH5>MQB?iaUegBmqYeEEBMV!*fStGu}Xz(YcL-^NW~Ia%j0(56;xNvfeG?1fR0D z0TcX~p+(Q{QFC@Bi<8>Vk%GZOrJQo3rY?dt0!pQSBcN3JB>_E*5D-x6xuLdu z86hm7RI^DyspftGONwOdu~+{)`U&QXt)n^qz^0M&o_*dX5V3DROc$rKHoCL6bb*rh zbHEZ(-U$en4GujfDVsx&$PI&@+v}lINWLWH8WMFFx3Q7HN!A8Eiv^c7ZCIbJnrH9P zGlm(`GwP*>nOt+2$pvL=Jq~T2!p7~8q<7Bz5B7@#X*wb4oip3s`OP)8#RhLfpYguAm@FU(-gWEc+$+KmS@N6%n;>wRhvjMdFC+acc}`mm zrGJqRwdQ>RckYbOot@jK#8uxMeC9o8YKD~l;(Gc{&fcji0Oka zrN}NwgUDWOubyxgBAe3pPV`6za+<*-8600YQzCzw!Cnzc(tDEx92F&o>qs6lMS8|# z+XxvP@Za2)Q*ZZw1=8kb|6rLM<@;TuiYBa~#T5;n=+0W}$*J@B-IK7shUK(?{b!gm z*V$(erX8^AW=RK1Ung`=8o&A3MYgwdbHWe4 znD48D%3q(42}iySGlChVmlWEd=5T>$2AGj-;t&vnf-J?r7vf)*VgO=%khK$l7zt$U z1RxkyGg|&6f}>~wK=s7D3cj=z+W)`_GAB7jw$&i^dGocP|K{ne)$Xh{#0x45D194L zmcmOw=_{bJ)S(^4-hjsYZxQs^JoDH>5%g;#q+VySIB?w@Ek(U$lGa=5Kj7jI9aZSIZcunn!!WFf1WztILj7__0={p9U-!621yQ1?;Nay*ab65^Y z=Xt)j-BsJ5n!KKCd#%v7@izbE8*}})#ESAuNt`0o3mmyQZ_fM%-H_k6i2N=7eolXU=%buif1h2Zije&8!~AcOLa@BFjR`j zg`76u2ZtT*Lu#moy&CRptU;LW0{_(2)YCm$Pe*1VmOW4%Nf)i~&{Elxvus+p`C+sN z{%F1;(Nm!i6NA&R>^Mzvs4VZZ9fI}w5NfH zyJ|ZO;H&feQc3)i3{LPADiLK#>WFND41O^EimvQ5wh z%^ug{?K4`u-74bk?aupokVv=V=;r|<-CpaYpEKEymVO?i#&K*>kba)dex9WJd5r4d zJk{MwT6M9XDXDUOZ$VOFLy1y0A*lPe%Bn^wcydrm&asN&XtI2V|6%s9Cne{@nVjk8 zQyS3&yH%un-Rxdhh9OqK_mCggTIC1auUg%=_y?~_K{t;GX!<@`I`9!P@W2FWdmd2Y zo=A4CGjzA&PhchT1bB3@znd=wKA`In5E6q!#6`vz;PHQg2b6{7DTO&3rtN9Yqna0y zm5fJuP<1*H8zM1H&-LGSs&0Efk8eMT5%z{W-)kOd3zWjLmnMuMd4-;*WTwCtSc3j- zu9SsU7Z?;aJ}-DU6U#5Ka&p&723Oi>=PaX+*5I*Wd74Y}S@%@plV1bF*=w+K)zcBc z#~m)jO2#js0j%-Z){lnv0#!31XM=s-<)qivtkoWW;WBSNR!K4Ra=FtMfS|?*Ozq`E z#Pxq21YOk%tG_JR98dc)Ma->flr2AU5&rjL!ZRUy@-lAD7P+!1SES2c`g`erUzzx? z6aSXIp~hvK2ZoL;TQ!jJR*P?7FX24`@7Nn^TNWG`YFV~UVl;^F2lCw_@z(4OZC%zl zkh+coxvi0C8;y+Mi*-v3!&tXuu5U}g%DwdR0`-<@|i2?Ou^O;@o~_VDImtGw3|Hw zLOiD3JW0S&Nb)qDX?h&T5+Pj13X zw~}^+(8)o!;*%zV)J5p#8v5arCL+~E=;i?Z@JW+}FBhSk!}R0F753!3Iqii)TfJeR zL#y0_C|E{K`-C4|adHslPQRREGjD;H+kww54u2Vi4^@Lfqa6skmO(BCxvBybL!!El zb|JID4DvDvpFyI$j`phkw7vr)0=ws%^^@`3*Xs5`wfCSg^lrRd|D@yYJM(>qWmBH# zkv%owo_jq{+;CMU+Rfuz;(P`kBToBo;24!qIN^PG&4t%3{>x?EB(J)~KJWV@Fd=Kp zOj`c+6s)eiA9{N%cGzU5mUvQ-#X*dI{YM;y$2#!sY-a%Zz`@sKlYVq_ir&v2Y8&&f zRm)OBtLnFZ5^|tmy$e3q4fWVgK%*_#?ZQsgO*)WH9R;4bIeW|N{l%?yA0)bc>vR1# ztfOe-Iu2yRheiJDZtzdXD#q!GZxN*sFwFIRSm?vfV_12sU%rZBFqasy{Qe%>=D4Ng z9GkT@*Y{>_)`xX_`}q#$`OE*(AF80uWBXF{c^7_x>Ua%-r$ zej8#}3~8}R{tZ7USc8c76tv*`P|{||frpYBZJ}-TuOZs)1$)wh4c{wR3;w-kUFi51 zvGXI%{&?u0QP1jE@dZvdp1uhkvmEHqnYF~}C1!Pbr30PZ`HHu4(QKEGI2x?47*Z>N zXfKA~qFeseIxCTqP@&U-3~HS&WWZjh->;lCI`lcIJN=$=hUr&dG2}U?gN5N9`hiZ_ zsykZ}Gw4sw1i_;s3wSh4OI?N4lFYY*_0_d^I$Z#_p{VC14uC(YeCl7Wy%#v(Z823s z9oD=gGSc~x4g#zq3o+2zsaQH|8?%ER@K;$ctbNK^NzCA>AmzUZ{xzmQA?5!9`~#*x zA?5!&_=in@LO`iLSbwBeR|=4>IiROhl~JY&MIL|Bt+1+gt~5BjM*e3m_U$8*PADi! zL$mfZx-=bqYyB#8{+NOt==?DS*R&yNny}{A(4@#L^$qyQcpobL6zCk+7}W>ISJ#eq zA#ii3E$dUk?Jw8f#g>GZ1o~@7dl3M?wlQ1i?^XT_Yagvt71aOCX;JN?#0b&!KRsWl zeKepw)jvIdSNmvKd#eA;*{UnYx70pL0$?8MA0a{!3~}fX^^XuH4l=~0L)1S)2n4tP zt@cSTL)x;`e`EbSp9FWadG?YV3K|?Z2XFx51N)Dm8Trdvg|+?Hy-ZEQVD3VkQOG%9 zpLaF$3YC5p8f56eSnLzbRMeeyEOhAVw;+72{M}gp_9qz&3PQnG(SBK9C3GB@poMC& zY~GeRS5w#)4+?r(qmtuIiDt;E-}+)Xi$?uCRUDimLoMi4>}R*+qg zI_1tu{QHw!a!n|51p8Oc6|6G+o^v)4s%N}RW(2=Nr-y$}IaBBXS%sVvQ8&${2%7Dj z#2}=4&gqfjv5-zN1F?l;0pxoJyn&t;U|xVqfQdf^R)XX}0p=o`Yl#NP6i+YwSdEUTAXPHuSitlrr2Hwz!lpkV#XrVw2iR5w#s(-Txr^{U_ zNCTveY^;I^sh?An5)ny{BY_f0|Ed}$H3vSXegfY5hQlhD*kBY)SjeViA`u%B$gd?3 zgp*4PCyOY8e5zrEqC^qoQ$TyFe=VQF+Ee{&`NS4ra`kUWl(-$4az}NTeDKJRi`&vtbQ9DGg+mBhnleIfBhbBBemUW8OxSK1?wrS>s>cCj?Rv z;iB5i5H2kc2Z$!+;$Mozzm!S=zP%TPPCqSlQtXd^;d2x^m*WeelZL7%p!KXm#}y@X zmdMo)6d!+b$}T}bwa(RSGC;p_aS*Aov89hYqxCWqwB68;` zXEuZCC~Y7QAZ>71h|-3g3B2CMFCu2BJ820bijr5!8O9}Kl^_%~HAD^mI4v)t21Og9 zhC$3eSWi(C&=D1+{3&X}ravL&Pf;WNDg_c!{*79g1nVhu92%tRcZw5th>C1R1pu+ zh9Fe}3`G+4Pgwz}64svTUrQCKWki4|t%p@)a`kVODlQ$O{t?0`-IE=ums=F#=m5Yf0Zxf z!ZrvwDtYjeBJeLI0QDYMP7<}87-)aIehB5n5_};iGEhfSV?<8e>NO@vOPt&WTjamt zbBg$;!NK)mol5;{u1{d` zQ4Ef`4r@VA^{;ga)`6bt-{AUg%}%Zq-ac&1PN?@rW+x|Pc5;A7g15ch=WQZJety_1 zyzPzf_T!bvRhYR(1Ah!?;J0B7JfFbi;PYo48c-r&dxV))HdF&NKZAv9klf&B$rMc_ zdE#HP#J|chnxB;*=mkFRulX3JlKOPvV|>BK4j@Vjj_~okO8RJ9$DadmXx{yO7_iB@ zFG|CJOMfm617Kba!pXOP_p+$~Xa>OKUxF~+OXe`jR%8hjv4Q}FZM0}8P9-Bt5^_lZ z*(x5haH$H8GI|IHtBBoU+-k+RCc_GHtU;&)KE0;$I*ieZP4_9>Qg4NqUiS!?Hq3qRJ- z$uTvcfzH{m+8k3Km()>gs5nm#t8v*6O`-VN%7(qbeo31B2HPJ`{1n-*ZIG~ECMp`k zeyoPUe!u6`U9;bdoU5Dc_w!7+$Yb+XAPRv$%Y>a=$N4(wO;26Pb<8tS2%C_a#FJC- z1J@aBMXn1loLmW=?J2NXWhOiO= z3o8Za1S@sULH-B_NunqYQcR;cC~Dv`I0w>2lupW!Z4RxN_MmkvS#wT?{c+zX$vIW{ zV&KX~1!Fje=o_5#A}61kbAHY#rO7$-_9Ge@=JzrQCBqaO3`26@v~(V`8XycqNX7=$w((TBg%Qe^xKWg^h+R{@sA}hn;saLvG$>edqZ;%x_I|MbZ?(S` zqCt@OFmAB^h1&UE34XcuXD$K=mUzajN`g(@NyRbk8<9CqihbVysQRQ1`j?ah+B2SH zbrk(>4?$_6Q_=75YM&RJFs(3Vyy5{8-5%WjfbwsMf_spcq7(%K9BBZLA6QrsP1pvfk$eL-{dLx%o4W{LiD)bh% zh%v{|CdT-pm`fCTu_Ah*0{gYr0TI3GExn1wS7Yho7XJ-f8ERd^;DWi78-FPSJ7Hug z!(o5?Zcw7V`kVNIbY09x=*}x7Q&c)Xf!K4FQC~6SRc5J|V1;~4GXAlP(b_Wgnkz)J zB$OG1K;{erhr~#pq1yjuW;6w8I{jOOPE}CD#MF%{1FvL0W(#Rp2sPBGml?N+>GhhY zGnKdnu|BP8ka{n?WSwzUsTgWWub{I8W>t~720!Q_Mo&WC%HmvbJP21+MXlDM#4(7g zJ8??=S}NrdL(}G-8f+pe*j$4vytZ)%zyg2S8R_!Bh~BEwKN61!eJcB9T$*gb5;o#0RHRD*=yNbFxP4XaD-Jg0_*JzsT_Etdmp;(pxw4-n;oy6cqeKBYN==F>!`&ch? z+8_T*5{c~p;R_=3Xd;VDW#<$B&`R8%1^M@5s>KG8QCAis1J%?jvo2|i@kLJ-h_v-4 zmZPX6Lm0Xc7@;eWjPj8+D^idWe6T1(_`M0di)697s9W z^plDEnzTItB2HTvFjlj*5u&XEpcupwKThL>Qi=+W7}K|PCC#Li7dxSpC(uk03Q>!^ z#?+1Vk4r6$vY`W0H^wl#-Xg(VY#a(MK~IxIaKeoRfwZ8zVoU%OqJsXWS5(93Qy|a% zagmi&Y?aq5sYlJdAW9q^MJ9gcd)U_Q%I1k}}5P3(6>N zM;X#}CS`~sxO7~q_=?(RcI{3qKcMefEB3JhOnol4r(blM^1_+QuFFil4ql+T2(cv~ zE3L1w(X{*^*xn-EHU^vjHp`@4S}Ufj|7vu#F-qn8Z}_x-@XH*y+&OR0dY7WYA#uX>3m`CkI*0{_>A+GJ&WT!GQ6} z(d=H&m-=6oNwlg9pw(fkq9^(mjmUa&C;PQNEZ^E(u$f!(XR|HP4e{!io6}t0*!)@O zHB~1rwO8ME325O7wR8=}Q;ShJ5P2=mBL*Lt@u~jrh7Npt1w!gV$1lq-LEj|@(3WW3 z`OjnVb}^f_nVU)OTTpf46UYx9W2id)b2*;USG3X}w zbV74Ek0l`eCHCs?4v0=qTJ?{;DsFO*PCg9hZ^1uRCsORyKTfu!_W%J0GGNJ1-H-rC zmwteyV`M2xKAO-x3pa6a{H{7Nz+U|aj2!LoAN%>ZKd}?2QdffE5x<}G)Xwm8W+pF!?^iuE3D@hBMR(I%RSAJmBJJDW! zZ9hx7On0Yqx;0UTPUPIlM9dQH)z4o2bvCL&H!5ARHC>uc7u;q^XPY)5CQYU_O^QzQ z*{xD9oVuvaCRtz`->A#EK@4={1k0*nJyd5B)FnFMu#Kdr>NWl!Ci*ty`U;(1d;Y84eQNtz)`?fi{JB20*oOj<8XoA2 zx}Depe$-<-nD3wJOv(3ytXm&!df0IE?B6d}$hv?mA_=Cf957`$9buXVOm+WT z+Sg&=%i>hyvZV}Unc>!P-Ly&b9K7n!sQxu|`xLuom5gVgroy^2@ zpfiG|OVC7LnK&XV3ywrNh5!s49VU*2zM}?)-~TL|b0EkPt?3|wE)Pk8*db?P$1Ker zFM-I5ZO5+SM~bxIM|yQ3@AmlNMAy3}b*>tEMhuA=GbN_Xt@%ed8+CT=*Dd@^g1n}i z_&qIBKbNPnOg`^A9U5G%`&c-|g%&jXmThO!-%o}pW;p}>nLYb=+)5kWZgZV;(4Z08 zb7|Y2sFh%OkbSIhH}pR0*KH{!WsfgQVvolL*}*N27O} z!>#S<4!igw$aTWM#HA*Jq9H2{+KcdS6z$zESfZSc`Ii_nV}p;IkIJwd-M=maPsLS+ zs6p!*Y|JclYZBEkoVqn*QH{t&qH2=ii=RYLvu2Awk(r{s*s2eW9(r^|m z41uDtA&y?elmV~?sjcW0`gR(mIQi3x6e}ezMnJ~pxDsX>&?t+8lO-8ahFCN2=Fdmy z5;Rh)hDqPCS46)YeTd^Z{F6#PjXhAX5#{@i=8ZTFpY{hV($iBCnoquoLp2zZS71ms z^kIFz6Q@CVG=`@^e5ZNMTE6dy`_5edcaIwfLWmF!gebM#gYKfibVR`4e<3OY)7VCM z6~ZfWAY_Mq-pqxp|5Gg8e&SUA05n{Tbb!!1@@l0o&C|0OQeSId>_A6 zA}=s)%Er|*IE=x=57P?X1PPA0F2iHnPm7712jNK#2CDNQ5~Z?)e z^exZzUnfW1aJG$SHIC)`%SR@-{SUU~<{X__N;Li#DCXjj3eLvJr0A!YaZ&^fSw7B# z;7aB=rR1#Vd63s~vkv0mDo>71eH!OMaM;uShov6d@;n^d$CgL;fBSnWWuYcQWxb$$&>xjql_Hs^^eX&u@cIv~eIBIiNA zSFjQhjq@Oz4C~M~Y(kgwAW(6CQ1CjA6I~O+bnz{>FTwu!qQRVF{S05=bmP83(dR)( zl-y8VB;++>4edkIT>MV)@>}F6@=v`Yf#3_3qyirv{jC_Xs0J{Bzl8xHY(-riD^=B8_n&8SF_f*G_bBb|`LyEUX>lr5nDqF4)jca*|_= z7x59ksCHo`b4C;al+qdwrbZ_oxQ;E`zXX z4@NHO74T7X8Y)czoxrYH$NdW~;GdO(7^kt0z=gFq&(@Zu{JhFGUA{M!B|76^g0>1hO{v;8V7#N#xYlA(+zDV}W zH19~-zhL{!dQWS?-3A`;ivvc8J;XrTmB3QNLYk8|k~S}{0QF;maJ&Fx*DM#G(@XeV zQVDnrys$(MEDkV$jxB3I07HO65nD{NT!#66I)*DtYQV2lfL~SKx~+OLOI2kxnVrk! z0}TYNOlB@zKDZ4>66_0Z!4n1+a$+Kav7`pAWRSsu5)`>SDpwB*loRJ{> zy(Wk^(QHK!Z>f%?%|wcRzsLjstvW&IIPb|oBNausFms>-p$G%}K@?0r6AAdwl?3Rm z`%EOToc+@8=$^^1m1-s@OC*_aD_Tq z;J^q)p}s2u^N{#_7$)!+ZQC@2}YidVTr`%@ImdDmf>BlKk<6Z?X8b|j+dEh%yTmS7w}Sao6UOQ z)pk{tIyM$mXRmSCWeMK{YkyhE0MaEzq2x!qd38wdwmYnAwoR4yd5!C$2SIxG33`04qQ=Tf`Q|4#F+a6_650k z0s}M9wnz+=v%kjf+oC0S4a6g={k#ET69WYqFsq)-+`*k8$r|&j8nrn zq!I(`2uE0_s(_#jBvg>1wr&44MK<$@djBeUGt(YMlHrd=^UA2J{W}Q~0~MYD`Ievv z4{&rrxG#?Z0bV0-B`3Yd(?;J|9mEddyy6Chi`k0b7wq|t0Nqaqcji> z5N-*6BN0yFn6SPmL_>Enu&@gY?srVVZEW)zg8K+~njyHm-R89%ru}6S5p4;cK(tJ> ziHP=!7#%{vsb0Y?*>i}wF;I#$@z`^B&h$KtPakzwUfb3M+ zfydFoV1rz?CaUWqqMb>I%wi|@|JQ_1dH%A2 zSxx;5a*H;I*;G5n&dxgmCxB!Z%dQY`X(JQEA{8IukZIKUMS%6}vP>~6=0I`3pUd6Q z*#3D(A^v5Jn^c4kReV_fXLD+e4S$b+v;!OdDJU!({@m!NNrp%9H+p{;Kzi0DSZ3Zq z?@i0hbEaj+@YGS5Cs{eK65#yp#k_7@`bGn)K(hSu3_=?+71mid{Mk@Ug>3jgm31&Q zHr9s!Sj&cg?Ndp{hX1rHd1KaRqh+`fRHx56BbL_0W#%A%;o z`5wOV)%+FZR1XLQ1&4YN^jZ+1T%%B=vE>znMGkK;FXSeR0jt>HGcI z@BRq%*tNW|-yJT$ljX7DJvF&8uKCN@`r&y2?00_@Zz<26;U5pKoJr@p|gZ&z^t_1yP< z0ja^`@wPVZL@YrBTMp~08f3TIg-&I)<@#r*(AFQoOV0~{%gtQ4JcDm_TYav7l15Z* zdgjzo+2nSinX+!92kaSk?)P1PIc z>e{&Gld5qU(m0p7Ps}RWGSej4`W9MJIxV$+1$O+hmfZkxPQTJ zV>5?{VK!Dy2P8U!AF9S?sPhc6qfM&AOuyK{!BoI7o{3Irat52c7U&t?Xqz~rSsmwtM=b)g2a}Y^=><0mJ1_;sYhZh{vY`>z^@V+EV2ns2+ z*W8$FKn;2q^rl;P{@6@xkn7F-_Hl(QOHhV*%sSbc0OD*-1Ad#Uur(gWhcMM;gC!9G z6xSlRk|yn!Q#t{>5Z+*no6Z}E$r*qGJkT+`E#J4_^Fmjyu>P288$ke(!yA*Tk;;*X zF;r|Ut8JcboG*a;PbK%3%FZgQNUA^9Pd17?zN0zf=xvQOBTm|*9KHG5`^7cSZ&dq` zE?ax2>_2AvQ)L%1+kYQ?o~{hF%+@K;Seu}+ zR^)}oQ=4lU55IFzIMvQ0b^;sZWgRr`!n{vhd>Osps9bA?_Zvt56SPunH*T6crK(z* z%@ujtXu!|*t;4pXeSzajwD)jfCTs-UZ`>$Q3Q#{Aq6rkW-?$|^Yn{H|IO$)k`;A&) za%aw{_vVh~o}=tH*2{h)w;Ag(Fri)CW_(lMW?Y*AXxwI0y{Q2lLW6X!r=H3WKUQ3O z7+W9QXmpRDAFKZ_FMwRRy3aY>55O{1e9qNlK;UyO0||MxVr#=!@rDIulW{TnWnb_^ z_6`QLA0Ebt>rUq?GB<^SH+iqYDlzlkjF9__legyhzkRsCKk#}iev(__=-YDNl2GlV z{jcFCEHy4CuhI741I4>5`qzqu;&#BS=pVG9F9E!kb<>c%Kt^cVki5YiNBhNPh+B}) z_2>TNJJ^3dnK|TkB;yMjoOtVG{0_4 zYrTLJa*x_L&xeicL)^Ho^N-f|t;LVw$82%FZIgRc$K1!zk+hRX`o%##_qCb&u4-=? z+M5_|?*dDEiCWTFX(q-luU6m^frH!@$vUp~2FrhDL9IIXs1tLiqC?lfb!=99Osv|1 zzTA{|&2a6vUYtP4d<+AJ$=X^71a!vA%Giy1Mjq_ zvbHL$!y;)bkWA2S?6+j0`&1Ip4r`;`HmrhycGJF?w++=2?YK6otpeUE)Z=blD;5*> z1s~#x#Svvaad1EJ?QR#xhgf&*ujNE zk0!5+3ysA$U=D|Z4|;2iSM*X|kp-Wr#Ki;y7dN1Tg9R4{=M7m5#8qBD)=k2c7hJa@kdWoIey@Sje7`IeWjFLcOqN7wGMFlOS0)El>xyNK> z@}Q26bBKyO19N&H|cl2Z{zCRd~8V@D!P1;VIh{iKn|wJZ-e#X(P%Tc#1tZg{Rlb zuA70UA?&Ofc#5sHL1Ll1Mp@__f~OUkJBX*ZmxO{ty!Xo9T4-8Da=k)7peY$A(Nyh} zA;(GSLVsKq0GjTVeEWc=Y2MqzD4f7|y9N-2iRur6H|f}2BPJbyBMSE;3d4M{5g1%Z z494@O{Ra&U#$g#?FwX=GxebZnr6Tat9z}$HUUB^CI(07Mx88K3G4>!*v>{&X<&zhs zVPh4zjDrEd;}1ga!QN96B1V_!7(EA{qiY9Bwh5D{ZD0#p8-%U?)_Wc5dfcdM4(ob6 zMqN{+F8Gest6>F#*;0+Td@PQ@Y~zrSVD@o%NXM7U?2G0qu93F*V_-K=2$4R`e$^y) z->?4Oj>vu~3}=_3+p?>CsbIJ|NECIBh~KeW^nEsCi1$7hup9$_UkSMJ=k)_cY2I%` z=vt+G$o*ikF6!Mk74IX9z0ZYdyeV%hSl$qCBgX7LS7>*d#M6o!L(_oV9La}E7~GkG z&s`O+hLMe;y&^A&vicRZ6^KJS0KbM~S<|H>4Uy`~igM?uuB^zmGOw&CcaHj%6>KYm zUy6Rfow$#nA=(%0#1l+9oyadD`vel;-9C}nwa&2rtns=^$P!QT_`n)(c6tedc;?$y zfK`=$fI&P2AHX=6cs=#MjXzJ1LDYD)~sTZ^4 z9<^a!3oKwM?yPr}5e(mr%>R=i5jW#&gYHpp&m9&D7I+^oacAMvBx7$pfhnjq6|GJv zTAgr*@%Kbu;i&tX^K?5BrX*q2@ZTyL&v!)o4#)O@VTb(AXi#TjQGF#oA;s?Mo9SPp0HK zmf6HSxIH08wgfp*nT_+P0sDgSXa)%B*7m*1u5^uFYduQXLSt`&q;H-oV;i=IV75#< zQZbAS%8pctRMS-RduI8aTAGM0={a%qMRU!Q_>_pnmIQ+e%pxbVN}aGY@SZE}9W*Nq ztfQ@Aca!g5KjaU}{xtc=yDFW)PzE_J#% z;3XJvJu8Nma5(l|hS|JV5pHF+52eG4cWI((Ehh-AHQH#y5?w8~fh}ks%#&vOvheN* z7w<0^b~Dw;G}TCCl|~W`jJ640M6_BVgaXn0Ik^EsydUBM-sk%^jp%G%%Cp2uUPCAK znCUlQac`z4wKeyXYr?X==Q^tY|1}|nt7fd8n+L(XyO{N6O8NIj9BFf*o@wWc#+5$B{#y)8*8Ig>kxlAXe?l zK2VdLwMDlN6#vK&S?WCU4*T=p!mwG^TE{YX@=72FoN^uB){oJHygQ4R-m&m<@Gj~a zJfBKxkoA7E4g1nm3ci!K21>zGc_FU%gOukrzJB%}u4Gde)Z$5T*MNF~js1VL2ZQ() zB8+CO47tbNSXba5^o1P%V~4e;2h10TsF+54aqHs5WB$cN2%;WRj+p$)`8D!uVS%QE7k!FdaX;W3KYM0ps|t+ZKf91lo;lM zHJEX=M7xjYJ7tMBjRoe0Ae z`|fgAodS031!4hxbtTk5T_7#Dn4yBg@KbRuPQSx0K(`7{C9SaGs66mhYz9nL=7Jy; z+_1Tz#&-v-1&gpFZC~&#o?+C@TIqQU9;)#V*cor1|1u0oCc+Aunh1J_iwm>{S$H)r z2?YmxKW|x+37Qg*frzp`!`@^w*I`(r?Jn%!lt2Z$Nk&KPT+rx^l^I@P+Fjy^f|%?Q zOqNuI$ylkW?`6?!KuZ2LbX%#8pmT; zX_Ua12w-Ivw;0Nd1Jg6dW<1b7-oEHg2*{Xd&x0U9d!od(YsCh^?}zAUPs{F8NqZ%E zBa`!jE->pF-S_~kPnnZ!pa0+BW8BG|PX?`T{Xr&BS{j?C&^n=PaZ{oasiv|UKt$hr z;8w&6M5$a1rp$@!J2~8#HMdTm!1T3$ko>^2#s~gRT?o z^IyXyd7;h{9TU+jkBHVSAQZEpJmKTVx2HpB1 z2V^+&4ahN--)6_^8}8)1(j)w6Kc9VH0&ua%VAN}x#~+BP!*$0n5MdX177wi)bv z{JcR?JhwcCtVKk(&HQb6efg>v9+PzY3}}Zom33D8%%jEc>Gm0ffU4t50G+~2*=HWr zc|Z+#0Jv8Jj#q`WSG`w*Jj6c3x=i~_bX^mqF4||L8mU0~EY(=-GX^J!w$FTk<34u3 zZl4*__JPGdWA+Pc`kwbo2|uv4E^QI}3^+oY5`Lx6ScD(UL~T$(^20-OU3 zgSN*Env>UqPs2!qY1o1jbX3Hk&tyo8Gr%kGZMGkud*b}xKW4b!Q(vJcwX6HRoQbK}! zq6GX3c0lqLE=?>Joe3D1Xm=&%em~X*r`6wS&-tq5d<}4y`kHCaQTm$yLhF^yzcB3{ z_5}?X8?>Q+{rk#JVmBEom@DxNOf^nTce7L4?ux@Wg9!dHJ8Ltxin@x&Mqq7W-fma4 zz>~#N^SxM(>h?a^bIzkwlA}DmmP)_ca-OA9W-Nk%iHZc%{8Nm4_&fj@LJWJ_mw5I? z*6YB<%*if84c&YdmO(*^3SD>%C$8S1G{HY`bHSiOE4_%y2YPVTJ6TplhV-q8A?&GW~7_$h3K~R9MOSSo-rK;FWMU#|v!F5Vrk+~L zU5cPrWv|ZzG*!-I^|0As9h}awZT<&~UxH(k12kCW`#N(*;7~;7t*bVQ)ok#~8{~Hd z(l#wPVqdfuZ9#a*xJ@_$^hJ1Qf=C(iCNje@08Ef~9nd@II#UPn1yfAf6E0$P1zHk& zy`mr6QWjSeX}LzRAT4pYiP%z+rB)TPb|FzKU;Hr;_}@41+?1*vJy6BaFTH?{vdT!T zU~y$aAkFUrJyB$E6GldL`pYnxVw>Yj*gV0DqyzHe0~k)75CV8n2ZYDZkFc}RboI8Q z4F4Bde^@@yo=T?;@4;zUcuw zH*gAXT!L?t?*n`8Djd-Ts|g;9w;cp)c^9WOv*1zcb#<5$zG!yh@a;;Ef6@RP2OPMe z>2vFkc6$71a{<0|x^e_gQYAWbeSr~2Ms!ZhUO3a~~k}s6@$$@VTAqYj#h{84<|# zH7nC~p}VRz1->`#hRwgT)H&Q;*wi0}^ZbNz%t%$pmHpKRoC9aSxp0zmD01X4MB{RO z`*VB;Jm}}hs$K0>HK|{n;F4{d|9p<97d0X*7=W7fjpdsS0M~ z`Co5kW17B#-2{wAdR04y=4GT*k9J=#e;Nh%$=uyA12aU6Ni#)L2_xpc_F!gM)B z|CFATGW+U<1%s-N;h_G1Bgx=PKw*p0{ZBwDjM4Oarn-yWQ{DHv@5ASB!M{(BR~W`{ z<4lxVR)4g!`4+WL(i6;l79$p;n(wfhIH?BvhdsdX#j)$jLOmRs1pD)=!Izt_Qp%sw zj|}o_ZWkTLfOGmODQ!&&w^yH&+IP{~Ip%(AkEXV?)zPJ%r&&)I=8*ZUJkVCO5V4Too2)3MVf_MlMH2-Y-E-~W@trb0?6_a4|t>p)7GO&h?rfFHw~_o#6qYZ&Ct+Su~@ zrax_x%_%jW20x_}9N#A04^DwEROmZ7@z2;e5spf3ni+$erh;#-U#+;Q!AG~gOFjx3 z1S>tEvr;TW%AKiAzu!exiXE*tV0v;d9Ox|YzsY?tveIHuFeW|KW?wWMNp29mVyFxC z7ckcGOJ{tc*y?iq1x#6|&`!P}bG0vM)?Xyr=kL*9kbLb6(i0VoB>Vi;`U?_c>7Csp z>pQ}Z!;=o0fe#qmkO52lBkCX>fLU)$LDTYmHBFDM5!_8pE`5{@B4MA8>K%IZSLxOF zn|l?jA0*x?{~!7*5&^tbNrY^tBi9!ckx)qIM);e|zoz}LHbl&MnkF|< z3e0qtC@BD5Q2s^^1;_znvB&~6D9869Wx~kpQ5)^g4@KiC0Wu*0(usY(gJb+drCpdW zC(avC1h_C)``DDGTN*_Kq)N}D|;5T#0(t%M$C%?TKti zEEb?JyryXeswF3bkpDMS-u$gzjo+E!usdWm@oavg3_~_~s7QYO-dB`TX$)!Uz9>0J zY+Swq^?YB%cF*gHl7p=0an|!qvmRXzD(Z-mgLRQ|kYr-WL0m9v%4AJ9peCPY;YB(2 z-)$F*kRHT z{RX589f#;pxTJi5V5ifZtfq<`Qu&pn!Z7i^D${De)*0TD0XfY<) zk{59qJ4$>}iTY+yC z4Tv}MEaOpz=HLiJj<=I9$G|WB*Sv2rI1XpGZ}q;*;DihwM}LpODVaP?`av$m(c4gz z+`-1Ae267aJ|ta9QwPvC*S9VoN2$hBIu&Ur%Y)j!^7Y#G@;y9tzapN`z~8|9b}ep_ z94-pLnL{IMzD--;4d8DzBVPpyIBs$BIn$YePv9+k3*O+Cy&G~T(OY^_|CI^(5EIj3 z8W_NH)dR-S0ODDQkwIfp2-?ABrL$QX`CJ!KnAWP99||eqwx<`!!xeIuXMQPUE6=>; z20_|I4+4F4doVTGkPsN_thtmESo z$u^Y1Eqt8F{a&rKkafD4qVb^`+aQ^ABb*4e=1a67*w zRW>p)+{{~UkklroT3Xr4xN4;gvUpBM2-{b5VJ~IWI5T6{@nz~?#kg|rtV7E9X$G@p z8c}-gRN@VgxB^JrWh_}mA_q`SJkZW2c~Y3BW<9qGTZ+tQK~M5cRzB|+wWhwsxP4J7 z=%)+Sd=t~S*o0k8)T@r3$7;5qy30IPvk}!{`lu%ARY%fCHSw-Gnm($Dc-39#qnemk z9j1?JVqSF@`mi=KOdk=A4AY0Tk)#j3kFC24OM^c2QH-DueF!7y!El<}dW)nFtS^6ob<5(h_VoN))#Qmlu(D~DEIi%9rb{rVVOf$Q6$+t; zdGzUJNj596aOnpaS6o^u6Aob!A|;ZV$z0afsbmk6Ev=i*xDp~#Ojt{}Wxp}S%1~wu zOOV%ZFyG|!jir4v8Mn00!MLT$C2_ha86I7w`DT1nm9wI%92iw)z^L+;=qk-OlcK6D zjjA#|s>%kV%8}7kns17tsw@jv2_eq~fDGxN$eIy7RX|pO5L+hq;inp%KK#^`&>r9?xvUj68IMNF@(831^X~G@8>pZU72T!+ zIGMC8#K#`uXa%q_Y1wK%_DS5u;08VpOT2`^ZG4<0ZznH@Zo@O(qT7U*usvK~bzptf zf%R1f)>j=^Uv*%8)q#sg9k|%kk=ZV5qxHGI)m$4D__U@U;Cu@fUj@Fmxi0b07Cqh_ z+>^w{U8lW*RnIHlmH1nSzpeOd#NR&r9l;-0W$9lIxf6K)W{g>g+WqIZ|82X5zd6HS`m?wFUN~&bVWtU0L zBDrSb4(>+#-z%_v5DJ!OVVPB)H7mT^|wj_U7lINB5XeFla>-1zj z+uQN}{K3n2yon*xxpJKqa>w7;p_MysTJd^6XcKXDoj8=5lnxhg?t#TP9Kad*e$p`K zb=b>{FaJj-V0Mr)^E#;QClksCF$q(Jq|)nLrTrPjoFQ6%T@UA|XZD8P&d+z-BIbv^ zg|VR2S>z6W*mI(Qi69lqZ1hB-F}~haa)+dcuyNGwmB=V*bE{CGSp)yO+=h4;3i?4$y#jk~u1zCaokuZM)nZDjsuOYZ{*w)u z+Yh>0Zq>ewrXMMp5;n9E!%8k~sRD8+COKR{snR3+Xz%#d<$(`XI9dc9<1)nu@=37A zrOrywmnrz({q3hF_{{yq^RJ&=9{BWSoX_iuPh^eLKSNS_7U>0u_lh8%64XmYHd9tp zHd9u=fL|VrT!P<7S>69PN>&fSZ=b9t7029l39qou8-M;J*SCT5mFrv6^l)=$XMwK< z>nn?FfiS*Jn_m>5RK|^@`9-*cjF1bVbrLE;Xe&eCK%kLP1|XeM$Q(C7!}yJs*c8p32~6J~|kG zBoTG2#2*fVcO;ZgkUW`uy_OGI!Oj@UAikuMR((>+q#*w~{2f7=#PaK+4;R73& zs5cewx8hG4?-9XZ86H_as3n71C8)&#o@cwDll|WdGQU6g=;F<|Ay}7fA>Y7f9ejhhQHicMi>Lw1)3wMe=Bo@Zg~3U%byzK z=#~E6Yy81?xG#df;Qq6y3N+nna~!w2{}ZEGTXC%h176v%1NLX`bszETzM}Z za|YZP8v9h@KA8E^((&ysQ@uC=_2O=3=FN1t@~cQ}DWsg&l?=vz&A6^)Fy6_yu4Dk_ z8k^J%)0jZskJ*ld46P*+w62kyNU%S*mAR+QPJUm37ErowVuI3j6M)*NK>b<=$gj)VWYh749qR#hse9Xw;^KloSb4oCAsUbTq1gEa~0^BsDZu$mX zF{XYnPnlu6>QUb-&vITD!V|_={=fjspB@`tFD(A=xG4Bfm2-N?K>sm6??Zpm_>4;$ z{i#sFbUl+3r%Qp{7h_2QArnOu!N{G6iKLkO;hgq3a-IcgSUb{#{8@>^_LvgLV z0s#RARFsCw6sZ-lvfh?;3If0?ZIe-N*?5*=tA$dasD=*l9@QF_91ofK@`ELdZNy zx8BKy*G<`Qy&(~rpE5$RWCIWz=siQk!s9?yAUWJqKbpERfoQt}K6PX7l!E!6i$J)b z^57pfUlja~lm|Dz(1+f-nip1pK2`LN!TSq-8jJUzhT+>z&+y>|*t#WcW%xoly=C|p zhTp%p)}V(d`@*RW7j1nRB@7aO(x*LLs@hX{B~8(A2`nDM=JpF?e|zJdZs=nN2uF?& zDO34{w!hJ6VXiNB{c|6MT>2DZY_-wWq^3__W$b=FmZZ4^R~eS;So8s#J`ihc2^B47LYnOty7Ab2!rNxMa1TGBzujmf{K^PYa|GB>tAz!|~u#W;R zEehyXmi){YOO{+Js}>9OT%hyjpVuPJ*IriV&BX-xY1vqMXg}=T`9Yljuf2TdgDYSN z9qW1YkBGUk2`uX4pLcm?Dk)> zH(1sxF3q!+XS@W-MIWw+74wSN1-w0SLGo+!FSSoem4qmyljQ8s*MPs(!h5zjnFuLO|yoTSt`*j?r4lPgR8@Q0q*nA`A0JN-oh8EyYK}o zENOr<7M#pjaL5;QI4%nu7ih(eAefCFOzyd@UENwENy#sU~H0UDv#r%8wa(_;Y~DF8tuGO=(J zXrM`v6(~xV@zthSKpRY;M(Fiv5+cw+v4D;ipoYgzOrHgAu@$a?p5;++xv4^joX z^WiaLK0IK|hlh*#@Rv9r{u1ZIU*dfDOLQlC1AM^WN1_+5J`%lKNoW=yiuU>HR%-7& zC<47lgD&Yp5yA4I84Mp#VX(CR%Z{orI9dN?oeYnj85%?XjLbeWgW-2o7#xQF86_QD z0R9=}R9lqO#e83TzHM`N7m8pTjOD4L93QGI&+=s8G2VqoQAv-ysh!XL_%qJ$WaX2kh{wkp_3Jfw!wbiF zGii#ebRbi4vj(ZSf6Ub4DLuoA^RpjzMtrzK`I<8Dfv1hI*=O+wd47=M_h8=zz5@le zqe5BYEzqw3geMpLjNk$kDH;S1=lmcr<8<;d zYCoB~W4{6pQ#r6N`Z+>9@lR>HPpl?)Z63c_-#+6G$O^!YIPC>R$zZvfEwVah6QE6jc=K_H`fs-En6Ad+mPNw~NB> z2+yA92gyG>`WCh*&&f^bhj%Nj>-x!kTG#p3x-8de&eQponEIF1|KM968u!z98WGuV zO)BEV!pR_)2=YOOIpHwgjx(5LUJXtkw`#wuC9N zk`SB06t$L6VYQl2VYQyH+>q<)tQ0P>yaDC0w3aI$SVkf}meQ>l6C>P~(zh5BBYc_K3e%q{t5_#aO*18Tgi}+xH)U?qvng|L zWp_Qo&BA9xFQ#f`LBI>RUbf=j9gWfWx1jKn#6Rj0vp8j@Gu6*J`o*y9WZiWMs!hGJ z_nqPX`WgAAB+PTU)xp z&mB8r;b;2zOBFw3I-~upV`#VTYk!}22{K*_cAqbQg=gfypWkNSx1p~V7F=Q|T%`Ps z&`<84^3gWkPpNXdN z^^$1yor}cHY)sgJZn#O;b!k#;FS=|K)MC2Qjh8mfPClmEWObzrNNdC34PXCMkRzPx zhQYro8yD6QiS0L$;Gt5Vz}ML2N*Y}39#`P44io`?3n7L5>>7}s%(Z%F?9SOPp=>og z?oP$QBjsoJLo;-b=~Z!gq{hy5(*<>!g|$&u;uJoyPsSf?it(q%yI^4x(|Pfybw+x9 zIKm$z*O#y6UTXBp1=ny>O;AGz+5PjmbgkSkUAC=bDRWD`51KTs%SZSfHNy{K($ zco^>DCm4C6wzS7cE?0!RVC1rFZ)ZpjxMH<<4MvV$ZguPD-+FYuhuDw^_GkQEL|;tT^%iMdX-H%7-Fq(5Sa(nyZp@Wk8goWDe2}se zF71!0*7Qsc>rRWqfd9R>61cpA?u(Brpk1V|?!(xq|19M06Q;C1Vul_(o!mYD4#du* zu)S#d@zMHddc1YarACjYy!|(KA2~uS459RuJ@2EbCHa}hY znOz@;DY{#)q+h(1XnJ|Ck}r<^>nrfD7@PjV7-G=>KV53{AC~8>nDoBI^Jhg{4^z2r zyMhJle0~b(oEOg*hdzJyW;gWt-yqzs^x1UtrAD7t`8*D->!Z-m9))%*hyV4aSaLXc z>94ALtbd>D{mVE0)V=@Q^K&%#lDwZ$WZu8zvID$coL({?LaXCVuGr z&G4oXuJHD5D};NJ2+wU&VP;f?lIUTg#bulo zO7kAmpS-FWKJ{_eTykA3`o8D0M^d(TQ&ef+(TzxPy) z>ErKkycVKkwLNt0cCMZYNrtEQaPr1~pHKcC<6zLH^jM37m?q4N=Pb(pg>1tMO$C)t zdwQx9k9?;~#J{#}a--If*^0kI`0JqK9s8mbC_`LlQWiQzZPu9ki3}z@;7k z0hm~404CN|>sWUc&IZT8y34fhGOonB?z(E~*ORTGo!n*2>kywXe?;h(OT1<4b zFUlqV6aTrNfWJi}o$xy#7aX})qz`q5YSJK`5S&S-m9}iP3WEow!F#QwmoU7I2&tUl z&Lm?@Th^%FN)4?loXzw@D*OP$9a2hI;7lh_*MybHunr5jWx|@yu!2>h>H(6Qu!b_M z!vYqXux2tWu$1#zX*N8~n&9AI)&vK?vL-mVlhwifVhPAlL4?Ox6CxbNnh@b1)`SSR zuz&~_k-{GSB48GV_g51toW7b+;pf$aTE_4;!!IkxRVZ$za}ZzX;|h;)Cgfs<^>Hm{ zxTCqxlcfK=HQTPBM8OF%DRk$Xeztx4LZC@oSQaU#GgkFZgRt9ZILo zLq+98!QX22q< z8L)_I1}vhQf)#~o1~{Uc0gk9Z07@6B@RVY>6pawpoX`^ojtpJ_ZF z<1+Z1;Nr8VE=|K{)6aH)L;Cx6u5UxrRcGw4)-kYI>qy(FwGKR?wWfWdZ5;TPwlVEp zZTrAY+V->^q1_eNhHQM|hB2O-gA| zMBvv;^uTKYJ+Qn1fgU=`tVGtc+8+qx+akkJ^o7MYaW2GVSW(D*<&B@M&Ve#~>R8HSc!^S~5?MNuUis1>a(q#Vj6iW^L^=#~GVf=jT z;F;+0vc=EnIVp1tsk|b3PSQCiSLkzcH-q||jAM9$OB_voo#81Z2q%@_%&_1$qRHhj zcV96~upH5TvMgpn|`ibQx^D$?>wL4q1(Rk?%;<>WUMi{ z?N>wY)X7Ic;=_)X-idC+%^OCZ+f#SPEkl*dWauJ2@RJ)p{3l0f-1>LE;6JaF4kZ;w z;-|iz1?`iV4u{$=nUWUa9 zF*PQn+U8zor0Oa0b5oeYSWKtF{{XYlumlv1FNbE5G%SzRB$PuQC&7PkDKvs4I1O$Y zs=O2TY!EIA{}cJMNV#(CF+Q9NwOjB=`Og7=?B=l50B^e}v6vnFBQ|2ZuOF^5mv)bK0e9#FCj)y}vHboNl=V(M> zR3{7QvNn(fNF=cwYPb!gfpAhebPn4v(Z&%?F29S>-z-5{MEjMGVe}6HL`%QI;GY{1 zydWfXcK%@|EGh8(6RYE|&~UFs~kw6rQ5 zM=s{4QsJ1}En3x5XC>HC{=(Nqm!fX97;)-WFW@&)w|eMQGE!u<^4FkSS&q1#_4-Nl z?O}V)w0=52_6!J+v-HH#BVF^njmPzHJe%t~sPxSXCV9X}4cGfz9h~6m;FS}^VC&^c zE(V~StM&HRDT6VDwX;)K+nB7c^Zs7amG__orehs`wWKM3LH(H4!!^0=Z}teE=pU#9 z-?!?(_pLhg?_1G-Z_{IfPpjzhXZ=ZbsK*|o3w*1DM{&@_nJxn}619QehETAySd48m zQdD$ivDk8Fq^jrx#oL%!xoMVSnRu}?6g2V;RZ%0K_+plP`FOD_6f|Cl+hyF#JQu~_ ztj7=Zc&KTZ;2&0eXJ=I7xZl&kaWA8%Zhgr@Pu;pDjh?!7LmEAG>vpu|WVDQl8EyFz zJtkJP-9Fek3Bs`<)&D@3g^H0CKx+hUyLLV*6Z2hhtlg7C6EfWEa6gm3Ku z^r(%XgF);_AUKf0CJCl9*e1b247M|fXg26`|DAgzim${7p`o|nGyv0TXVj|8E!&U=gFY50) zq~t)9+$Xk~Y;)Ahl;6=)CWG9G<=38$)mSBg zZh8H;!|dpVv%H?g{y?nx5q>rI12sSAz^{ID^rG@B9UuC*6*$I>OP^as(+7-Ka}Gw+ z$0cJ0@9BCn7Cnjn^z-+Ho|OLClmWNKG&p-G*Q*ZoivNWT)h6>3KJ+PQRBX!lbJ3|R zBBf{hx808K*Wq{;@Qs8pfUX`p>7Mg_1%s99>lLF? zMDEl70p znUM%C$)Rk1i5brko!;#MIw}9_Z!%{u5o@j$Er6+A5R1Om%O5&_==F|?UL#EOS`bDr zu>+7_Veob9$TNFM{GYOQq!MF$_UAc-pZ(ujtNd}nO|mI}e$m=1{>F0pHw8=-EkPB1 zD0*Hv;OS6QgzAOnfxX&a>Q>*m^Ia>x)dTxXeEYJHRFCI83a9sAg<$H^5|+GonV_)y=4@55;tkAkkpwemB<4 zM1!RGjk8muS`}7eD=cP{3>roX#-LB`|#u!zZD;fPKJ0L551jITU&wkQ_KsPq$AJ&SY4JG?ZZ-67D&gkZ@7K zgfx<29TF}w0FvR{fWUABg-yUo7ZX@E!#XhBf-r&QGpqx{MM@LcB!=~F6)~&>D`ptL zLY4Zmd?Ol@fE#Jx%Ru%8H$A17y;46@$A(-bLpep20{bmmhn>OuOcC?h=kY_0kK3u z#0;93zK2@+m~ZLhOiLf1(<{*-E{%k^Iuc^56=Kw-V`C~ojOM_CR?LB&WdX_qx{T^T z#Rh_?KCtzXK-(gKE@4c@7Cgo5DX88A3ZgWDmNKl*rW~e1R0qndDtxO0-JnNx{5hmY zySwac^cF0}1Pl&f0xq)vmUCNSz;ZMz40v-S;C2IWcX#qbs`ub$CSb4|6L7f&upAK! z1C~=@VZd_i3sJqt9RQ50{49=6^FIGw9g3OQ7d^v9fuUSX!ZjAO#{GZ$^K-BXWcoAd z(SC8%{cNoxvjKli_-n&oyVB=*pYT+>=n>)byw_>3z#ZKya7XtF+|j)PcXY479o;K% zNB0Wc(Y*q9bihID%@W*8?$!NmW>>cWaT}HgDj8iOlizF;!BSyv~o)j4Z* zvYO9``?N`s_i2;D_i2+Z)_q#7TYA`Uz0cqEer=DaBjTPUIS7ut#Lat%xcneEFFlbl zn8Dy`3Az~EAi)v_#rwvqm4fz3;M>Ul>JmNHDzShb!$m~AYtUmI7w3W`PUBy*x_8Tk zV$E__7vXLA+k?OT_%km@&iyCEg7}jgz4$eA=@%E{=ML0!tT*0vdxr{IyD^-1=q z^=|t&w?tfDyx0=X^q&_>tU3{Af4&SdP84|aqT?S939BUg!F>?@qGOK;@Y57e-CdeI zZ{n#BU%J7UD&^|pa>Ez4H)`YhPj#QZcs1=KefZW5zWt4I#SzB0t`|pp#H}tXbpiIn zv%iG8v$juZn3vdcb&I3rD#*Iz@bI^w&Yg@c?PTs=;qtaA{T(rK{GpuslN zQx$U2#FTw@=j)&>HQ+DS`fu-Lw;oaJKX-ZN<8s^c9UBwi2aG9l&vTRUVQZ$yEzcds zhpdtES|yaAQ0>&z5A9)@%=9IaUYNT73rts@t(YE=^n%nQ-(b3mGQC04b5c*uWBMhs zz8uzk+07?8im?-_uP>MVrCR}rk&nFP%XzHzgJRZ-nZ$udzqqa1DN+lJC^|A zB`FTk;_VWd&BD~yFYs;H>auDVGV$$+DhIr34Y56-6J6rzvr5? ze80StF+fgul0r`DH)5x{+;fe%o*deh#(S=~sf>HBX}sr(Anv)Q@t!M!xaXS2d#(uL zo@*NKxgwa(Anv&$hlm824Ot$B+6wSKalae$SPT zE?IIQN}BgvQBsVls8{wvek~SG#7;`ybmTSfxgxI^Jn--HCs91%@xdXxMI{j~kXQh1; zlsPbv$US0xCA0rEB2BD&c@!KMMdn!c8qVu}rn;|(8{+zUBY`A#QPvx7*~l=jS^epfFNN^)hH!FqLFnZeDHZGa@e4GkA4%vQ)?lLUXwU|YCE zd&K4D2fDNXgsV4UrSb>=<8X;%gsb0wwm)+ON7o{Ij#ZZq zH{$X$;ck)Zsup0o!oc>1fgKD3J5FFN@OmmYxPR0YSgHYRYiQbo$y+UT?+(}960ZA` zaNQ?DwH4Ryftz2c+ugNphfz0{PbZS?&y8i4X;YK8DJU{Gt>LNR~;00;j>`)X}tp-iYrZ#y=c{OyL{Uo8G6YZsHf{pNRl z>`k~rAA2)}RUdoPnctb&8;MCjKVUdZ@czGUl`^ow&xU#V{O{iiKYXWNt*3taD-2<; z?G1}j+O#*U|Mf-Tr($o2(in>X+TsVIPh;GN%OLmSAb*drXF^=X`a8mC%h_jo}q z?2+UYb*696&q>WO4S}B-om0*yePX}nf8s_EM0M>@3=j)GsI_6!7a zmV|;&56E|>YoB;07HKDn;p6$~0ohK6cEUT+fy8^#5gI!jp}dh=OVL)8DO{|bCy@0f9SUDVtNZVb!U90s@cVGxMAW$+Kk235GJzX4-cW-;Ar^~yyy(J^Hx4pAC;&0{K>hX3T zUNj+Ha{G4V`tQkb`)}{8YPZ>+S;0L1+fRmqPdT2*DDWK*)fS}Z z4YyS_oMO_K8GXWGL*4CuPy7gA(o>GxJpL&eZeJ+3ss^W-a?&T%+dRHW&bF#RJf_N} z?Zfq}^~F2z+pMijJH<~$9G6jAvcO%crv?ECUNW#Y)j|W1#3(_C2 zw`pr|rAy`BC%>QstvT&DORlqVme`Ov4G1xP9e<@MZ8q%~(+_)Bq0aZUkJFl2W+kC* z*Hc#Fw;2#tq2`^)@xGp8Yw>P9WhH+@yK~aVp}u|IwK|wBCiq$_;OwtH;B2=q*a`b3 zw^Z6j1l0LQHgR-3n^hX`uvGl8*{JRe=mNW)u4Mj$UM|{B8wZHK&Ce7ewoKTl#gPxE3kQ!*L4Y4f`jw=pOZs z{RfkP+zgH*SB9Y9=BR zGnka;GHM+4mVMF3V13N}P0#>xq`0fJccA-I=tu9NHzIG00f zp1cm+E8p)G&IKxUd?(#AZHor3HQW>2odA|_JLqoU^$0u(b_-6^QcUb=0F&J>d~7i2 zC)@up7}*wgur)r%wHrPUL` z1Ej<)_+tv$a0BTfJASyr@59~|=<`|&_EMy^NXkn5HhV?|XaCgqe$~zn-wN9*g{U>a z)lu);=PzVm6r$o_?bF^X{bSIKr@dn`(1M+)w`tT{bH7T=982!(9hXv76E7Ju%?$6& zy-qR!6OlolV}=`V$=y9DdQVZh&aIJaKyC^#CS@u*vy$;E4F28z-Ig`W1zCRyj5Ot` zLc<+w>bMQ~=kXo1FL)R^^mA^gka87j4;YH7avZzhX)!y z1m|hbw{2m9XVi+>{|8~>+w31Gq7b#%^6kIdERx$+gI2YC-Z=kVmy+*?s6*eDQ&bVh z-odY1M>03DEZc%+PtF;2yyD4h-}@#`i9Z5hk`qdXV`wr?Lklkq?Ji88l&tea@_KgT zq0Ca6*o6^N4lScB^)G;QhRRNsjYeSq}~ykc}PB)`;RgAocMx;H+Gq7{~ZZ3aduBTCbi~A zvWnq?=06r=eot_&F}GaD_&$)8j37gJAv6BBM>;W~AL#3tsf$CwX1zYWlZtDj& zdq;=cSL+4~tZM&TkFP$*p9ej9B;MrUH2dbsjk&%ye6w_eJGeIizx=jpE7n5&a&7Aj z{@d;tNu|z3{?$~Oi$AU1QMQC>p5NOTh8~=gKC52B4O)5K%4fBXy5M%EmbGF3{nq4Q z0(f2WiiD86%u$9@A4%!c@xpyeU41s6f&HPeH>IP3^15I)%C6iW+Wob3sKNPlZ#^5T z{aSJ$K^tAykgZiS>$Jh?4K{6c-CKZAk=zF1iApO`wvJ_wAj8+v>ueO%uEmh3c)5g; zS-)4203&4rYzG@!w>>m=Nb=j#LR98<)_sgdInfAIxg(ojAxBH3Vt01kVYH`zavgFH z!N>CzW%o|tIkaxWcGP!(P3+un)RBfyFr$tp zy^gU-$wyIhT6zG3lUTPO?MP&Y;Ax*9LcgVM>vo(3^6n1>rxa={OuY+@na7UrEuI*WzV69uRokw9X8%Wl|g;2*ny^c9WdsO4X&B81k zeKs^LExjFs%D!Xlk8TgYR}Iz@y-lIujG~Qfmu9qUZ&5wlrRnWbQNBjbO_E(Rv#3@u zT6W$e-V_u;dMcPA6~b% zQ`=Rv8S%8B1g2G7yTYcG)vW;Z`-`@ymnfl?rLEA~>jGE>FeYV6>jD6RCor)f`5kuo zST?pV&;kUBpE-gR-k`Rwh^3nf@o4MR4i>G^+S8i&OYyGRw0d+G(Rbw4L1eB^(~yV( zXXIefzWh=3_60P+4FJgIKt(A=MFS;i zpivPR5ptv(6%CT;AhV)ky`l_B&p<_~Mnz6ZavBw7N;K1`XsASonic(xUeRz#AC8I~ zMn$(s(k(_sBPBZ0sA#lAN1GL`qJD%fyCmI(iUt}LWlK`FQISWY9;2dsiRPOXeNV4w zyrhqJR|V3IiY7_YB%`7ti53|Z6-%^ORa857G+R?5X(dJl4@mR@qk`!Yoo-YxQ=&6j z0lYBS+`*mk(5@4qt3s>I9`&w$(HM*t7CUa=@$9S>Ia#Z!+LP_iaN(N+TV22%Y)Z(5 zN^Pt+xvU4ea#ap)Jvy0@Y(pJAgeA-t806aOb{69Cv9JNHEp4Z82Vn4Oo3^=bbs?g4 zOu}DK+nlx0k*rU0oWVJu?2y+@z7d5wwfGZ_xyiWgBhQsed1hnbqf)D)y9Na|a2?_z2yQ&W*Dh|l#AQyoloAT>$P zJA|nNnL5y26-;I-#%=CZNWMKC$v|k*&0guw+d`Pf2V4O)5bGqfauoSS_u z^1IE0nU+uk*7C^`Y6Rt9C!r>Jw#u_jo`>YQOrF*949FAngIZU}bG1Cd50J89F*crD z4(&GY;|`j=unuw|#S_qO_Ra}t@kQm%Z5%W*ld=Z9nb3yEbO3YA3gK%QMW?hm-lK?t zWgT}+-+;$>vX+uYJg^j-<0v_VM^QWT;wIaT-jmq+E;`EULsC5|9Z(7&XaPL3u);Ck z#~UTPcC~j79IoiuS4j3olpMEOySXUeiP}-Jfh9MaC7aBW0C{B*KDRTV+}VgcW438E z-Z`zPa|V31-CPvVZo@ajgSQo()W&(oFqZA)NS34Cb>2Dkd<~T7(3ThB?8}YbP$)R2 zsGWI3l9%IMjuObbLGo_#&S_+3?Kn$PbvmF5g^J!ta-<^B5j>fNxSxU-YFfRq_wOq6r2GgKMN8MS@5`H z3hMBBP@Q8+JwoHRf!y+)5)UD`3V$n*?vfO=7t@0gcpM;5j5s-ky`Cf`CIH*OarSyW ztr9?v4(1>nH10TBVbsJ{K3?=O>t_E|bC8bm zz;pD7wLGrCt}==$_(kBA10Ha0&L6c-$#IYEeShL_X-c7eF_UWeNrf@ z&%#Phz4S^x#$@S~_V8tM*iXZo1o+rhjCU}*y` z-LzesRoC#W)>QW`$FsgIFs(J!-GvhEj@%4Sa91L-c!xiWXD&)@uB&|(>by3cdimzM z-P@7*ebx!iKgU73pW~QGB6AcwTRpb5=w~^wC%)(yfP4&1iqthL?PEOv5fKW;L!Dt+ zunX+}V1ZMHm1&PkUo^-!#!o2h0Hx*O@L z39PD7`Q1W27D}&o3CN*h)2fasCb|VlHb+8Zub@gccaMJkS0rS}7HZ!jjg%>Ej zgbtOlmw}1Pf<FFH1SGDJs5ezxjt!tGVAGA-3=vrCV0)PZpxY1!RiPZ? zEHVS4p$!EYXa42z2si`M7t&KO_aNYa!IL)8OmJP)SB)@B&Cx{wa}W!wQ7=~zlfb8I z5LymAV)+odnN8*ki0CrfffVe^f{%M^@opxDe3gVsF|0KREr*@yI9jnmyI*H`%tKtn zumkD_z{b$hBmG=QHyw}f3<|ktK~ z!st2~1`dD>4U=$G?(w3PsD$0;@W`Mracg)?7zMHD9cEXTAs+{bom_^}wah~8lw2bXBVHD_v=t zOU3N6w8b`S%B7k4mBlrfm{5wFFU{RvvHx-C>Y5wC!hyh7N)%jLYLit)Q$03W<4LM5 zM(YNNEY#^5FCxwW6S+{%YJpo|RNOTN)Z5Zo#k6r_?D)m~RjEwW6Q!&GG2oUN*Bda=fasXL|ix)K)8 z4L>DbO+xTSBO{kw3SoRcmtd6T60o&h0(Cc(A|feQrfPXyRy%V%EI`9L>>9L1bUTC zm`vA(5++}P&9&ayRl6{&L^GlXu}?ia?b&6|ZhIcJr}p~-(}_j13bUnox*Adz7L7^5 z=&7xV9O(J)Z(^$1`Bv7SrZxT6>#e!N2szVKw``R^&GA^v&RF8n7^el7$6I#ATiWA^ z=D5xdZa&(Hes>Zw9ZNLNATixPu4)w?Gvl(qn&-RN<|X{!+^p(Gj9B$=o2@EBf7QKi zHLf)S0au{&%lqfdP4|DM-G9p-xW{ITrL_?V~Xp5y*M zSf~HTkuU>6%Sl*M<No?UfED1X-?D_6IN@S}?r?*EL8QdjRbj*tg}E)Vr7 z-G57~^-Aq}y?PbSh;N8^wvXU3s!HgHB|T8fS{Wb6R^ggBUiYNBu7jNPE)GME)E?oF zF4U#d$8n8e@HA^DFKS+O`ENPPnpK%xv;Ae{6a3c*e=eZP*|$NHDayF^k5KnEtGCTf z31@At?yB4Tw`|m$X1k%erMlRyHr=kqLQ>tl4Fu~8V=bddwdo!RRu;rsDiLtO>k)Oq zYsz>`AJ60R$tRqz_?v%4BOv#~y_f9Vt^d;y`OFUgEqc9;b}qg6M!d5atdUULmAiU^ z?(nQhqh;;0`Sq@w@}H*sZCAGSR5K%-zgc>>|CS$m=|>u^J~zp2SEhS*omAh!^sbrj z_-}d0<^SxjT>d9)fZMKoz30!#-`%xJlo`BhGL3Y_gw5sK5X@V$E(-TOu*GZ{S4F53$-X?PpL}vmw{aepRkg%y^==6v|$A7 zN_q?D)iw~Y0zGF?gXVwud6ez{>XtWhEWpkqZQIKe!|?-HLF6(#Ctw7_xWE=Bujy`#!uy4%N2t+wIpw=m7nnk%8;dkJQ0M1I;$e1NeSW ztDE`Y1CBK`pUfJLd3`kY5QRMq)E*vI>_HWyGb8;?cc?SD{Z{+1>wdtU$sWQXYM>VO zAMlt0jKVPu6bQnH>cljdjFXv0!^uoT6XtaWhT(V%9T?RxO_gUGwGkdctsg++8VFR~ z#aj6 zN(wyIBCbMET#jIw_3Sj)1DMXq%q7U2o$^>v{9s3V-^@P$Ede*Lm+t$9@qlyrbPodQqrT+eG zeM`OiPIL9myN$}W_k4@Ww6Fb5dI{P3)$EbxU(mNJd_74umxE}v652NEH-D87W+2Dg zi+b*~>eTMArM&5?+%g8Q7w^A3bizW`+d}rtq3rrxPA5Q4k{xQyTVmByS=RHZZ2y}# z68iAcqLH5CtOhu<*Z(W%fAI2xo&&^om)~eS+ws=FY%y+PKhkiMo)YS3Q$oqXM~#OD zT@JUC$wB9AH}l^z^!%Di$^C%kKD^fIvmZSTswynp@ASx%KM*O*X5#rE_(cHohs{DNr*ay|mV7 zTffAZmexLK1ko5sn7+2O_5s_V$7>VD6&kbj5qLs`rDC&v={qp0m?v#&#Vj<-Vp(DO zB0NCuBkdymeJ+M&{1f5hXhIjmUEAv3R^B=^mTf2~cYxrw)egXFj=R2usPQi8iU7kX ze09&Je-7)@e~7WcTDlC~?P5VMwYyR0`EedOkHexa1Ii|sh|48RA=fqb~$Sh?0J_$TV6<1$Kqn;uy= z$W(M%hrfkO^98jgwW$!dTZ-J4{#G4XRsA8H^4LY{4`cfaQf+)RD~&AhsM?-G1+q_X zES;-#5hK6qeLSuZ^PgTwXnFwC7%#uZpy_irGW$U}ie6>*?~P^rtdwSVeL0V7Ci1AC z#N&z+dGWH3!-sK;#zkF@G@hIK55I{M%=`TJKS19w`o{(8*;vAqz!UcZ?oez5=^OY$ zg=$bZ*bw+b1zMUlu9LJ6X(}INftY=gGd}*UJ4h$B()n{(=tm{DZnY{o^06Jk+|?#* zD>T+VU|Ru17KU7#vu{+!+?d`()~Kpk67+$Di5dvY_S1b({?Boh`6p1bZAjMRkUn{g zNgrpoWVz0+IzWP?gyXKQX|+dM~S-b550_)Z`XO@p)J%6XGm!;2_tXT9!W(m zMgh1ANb^W7m;^txq41DzQ@1axJwjaVfXI}`83GA|P77xbsyx4+uwLZ0Nbo^zYkal* zP0t|SR9&>xK!{oFv&O)(CPgh~%QiYyJQX>+7F7GRy5lU4Y1I0$i({|8G|HSP0?eY0 zqqvJ~2Ry31uZq^XLQCNrDgcgD`#a^_qr}@(z3p(252~NW)h^bC#>zfk1#)E8n%96; zFj5sv;Y%hL*QKl`b@dyC>2KFQXbgw>NfTeot(X@JD+|I7Nb@!>23upWYL%kbnhx-d zR5Dh?abS7T_H-|Q1A;puu`{=#8?^FErk2~?>Hag5>E*$X5_x?o5^dQkREEj`_1!q0 z(DmgfSz?3C>?xI{tlyfy>2qXpI*qP=q~gEi@A3dt)$ntgH3OcQ$`ZNNZ6L;aURnO? zo$CAFUF~mb%GGc+A=S{cjH#DD-Kd-Hvtii)hsKtSoq23%Z2f9-*!XNjHb4!lXB>4I zKN+ft>ZpKnp0echv)HY7dHduqnUH#4ti^`mZ+gYl3llHIX&}&?#qmy1OJ#Xah9Bnt zH|nNF>m~C()S4ze+ahzVX78c^`i_%Wnli7w5(3l3X7Q0{&ZOu}7jwO)%o@GYYgDti zRxK;jq@FJjjb7t#_H+wZ-KnbTDYmR`ycBjhd-90|Ps;wbl0CWKV@T4Tjq5tB2zMpkCOHq$`Ew62y2#UR zE*P@x=#+fJ#ezaGN{$UEa|f;L!!P}x_^toIP$m+N)ZS$x;Sley{Ft<#qiQG&XE#;{ znxm>@6n(q)EtD7mh^9R>+JarwF8q;7!(c&vt6!EwmX#zq^hHFZA)R*$OTnfS!0ZevYtb#F-*+#(pB;z3vZh!DrXT@vOT2}wurBz$AgOlLZUh<^>{Dnhze>vjU! zv&xSe<=t2NEa8Ohk$t>D^kaS>LeIN{|Jx8($WGNTJCy!8!oN`{;$;x}6bK!8tzuF; zUqt$EocAp_T^VSk!^`#O@%INn==Iu<{DJAoe zy>9ZgQBQwUfb4`IgI`1S^j%WAG9b!mc#uvHiD_&14x+MhdQF!9E1PK9>YeLv8lS84 z3_{d5s^`lj=kSg{YRu|^zv+{d>v_jyQ#cF!4B-Swx`LiT$taWKSo$Z)X*Upm(_ZqB zDng`;O`*T3-6B0n{R?a3B>BfWUCySEA_OCHLf7@{mZ1tY`sH55$_vgU;$7&~t_d3^ zlJyl(50PLTJ%7c|Q{Wq_r=aIN61)H9>Jxnb{~+G_A8G&7HQSkm>8oq6WtT`8O(Jac z?b$!UqPO8*t#4&c=(KLI_xYRy4+%Dcdl|L7Y;-tPREup$Pe7C2-Ib7)M+Fn9J zXM@eQPs#5o2Ff_i%P0nD07axx_J@hgt7#cFWz6B**!5`r@T0l467zNPY;gbv9Eddd ziFj)*R?Wq{X#x+GP9p7n7l4G$&^?1gb{^-U7a>ij)g~37^aZs~Q{)M;ek-qX@)mM% zd}AI`&k9s_SanxC2B0(}hd*N1_bOMHy&Q#*xfc789V^a2>YsG}b81-NJDfoddwcy4?hkh!@yE!2;Q}Yg ze_;d&|By-cAtlwg-;5!RKxlz+BL0i#bPs&h(*c06zvkKXU&X(>w#IhNwUY?WZqmvs zJ3mZdJfZy}8d``V6nIojhAaMpa}(GE;2m|I(O8}3A1Bgp0C_TJistI+Lq zD43>G{9vz09bixO1H-3L?p|Y0u^BMTk;K1~LMs3&l}3xzVq=G1#OF27sOyG~166Fv zxd=L{FjSK=p!ENzt1!e2LkS^L*NGuL~lr$w%z9X#_ms1tSfy7!#mqy91BYT@=}oZAgO(% z9%WqYf+Ka$p{~3jajiSKl(&fHqC!?{8V=Tc8=Q6@d3h*p=M*_vHI<>rN=HniYv zFrr;T-C#b)dGq!NX1-2RE~?w1T%6sbUZ6)9WWGVHUevO#MARX5ljv*|7gQyPJ7VD9yA_*lWL@etSHWk;3_YA ziT%gycVhC>F51&6dOpcd*_)U9cwE8TaH{b%B6G6cbpK|Q2S`RhhYXA07?L*qbGOt2n%6k^^qkD8cXnuAJ4+L!Y(p+}m zx*d7))Moc?c4`j~{sDadKg6D%c9k7Wp!r55KVilJV^0rihQ$>yC<8U<+<^zgqxgT9 zaVNI<3gw_ww6v5wPZ(u02_{^LZYV8x2K3dEa#v$g8pByijm~VYLN9NWe46VF=LMw` z;Vwr}=8wX(-jUhUtpIFClHVg}u*2ll?DB1Ocut>34g#baUz9C^E7ApQd01Ji{iMea z8y~y_&Ox^pqa#Gl!_z)F7wet~y-|pKlFoOdLe~gwsmGUyR-_;FOHv7yxuVpITgq72 zXkv3@z1%0(^R$|!QVU&H+wX-MEvp@nVh>S|I+bFic$B zTxvjxVF*{%;dZQig!a;$NmyiZ@bWUV4CWA=J|54PlcBq1r!bx4^G^_=@Av0<|9So& zBR~HS+0VcJn<3eGfhRi`iX!2M7_E|;57@>(1nWm)$ka-BRLNO0BqGvewgM|njAkiu zb~$NIa09eH#3gVFK1GYx7r_;xr}v#~oJFq^6>##6TeG;2hw%K?ZIr zj+=Gd5`owf7_`6d!SjK=s;((^Sky845EVk2t0 z&!nHM=n<|Zq}FBRq6a6-!ArbotB#H9$-z<=ot1-Q`I)nqsl}}*wR!T}Ehe<>*o{ZW z2p>=S+5+~>KrA7r8JmhRHlLGnlHVULP=!O2!*%kM0#zOdrx2)hL~GQppZEf}7ORF@ zq#K@GEgx!xNN*zk#H*K>@}7_Jqx<12Oc=Q-$ZmE|q^K#|p4qMJZFk~r3SbCs%fj!i zON~wuCEl>so_{@A-f~Ap&UZ=Ifc-MS+Qt)!05ceGd75*>GM4xxyWox3Ppb5u_|Lu5 zGT@oO#p(Vpx3ViP(o2GKd0}vA?bSLNzp8-8+(I6gX&X%|N}o2Tm%x;ey|{6__OjyC zp_QSn>9gl_rn^uo<$|TRFfQPQRK5Ff@f`Gd!hLhLrw7Mb$GEv2io3yU-Tb4MmM{lb zm-4u(43qLL0*ydhje)tP=(2BM|Ka(48S%R~GT%lI8iS8&j7oS7E0F@hM+=BR<2}s# z3Elc#uHT1hmixJW++f)D$=Qe6OYD)sP0D@E_yYTqXJ)f2t?9}Kw?503Ck2%>9* z4F?@ocBuBMN#3g%kA{_1t-_OwT`>c&)xBLR1}_gj997CbT51g5RpN3z7~{WkZB?#G zu%u~|Nls{kDY_po+Fe9EQRedBRp|X6um8NRg-0!uJrI0Az>@x(tXusyn|qWeelFvs zzqa{YgEc?0`qfcrB6B-6v(rqK;HI+E=Da}*Hl0}DdNuDf<}^EHSJz&RCUVA{E@Ir2 zT+Lf-hy^_Jv!lJbc5W{UiS^Wt?CRRfILW(!VR?h0=4t)3eVN`FJ8=}y5K7ENbFfn8 z++}M>ne2FN4aw1^M6DfTw1Y~f>M~p-=G>*_QRF23Cz5yRcm;q!FR``?M4+?;-71mL zN5pH&!R*#f8)S;z?ER{CS||D3Ivrue-gJ@yTQO*B#eXOD$lT>6nCL^8P2)8=%jwiA zBJ>)EiaI|MB6_nC^VM4-`mzxVyoi3i%BVi^S_fN)vWwgR4Fl&@RqnyF&O~6s+K1`4#SB=%OGsVJa^h*HU%)7s#`BQ6~E~k;x`}t59c?s z%YMS^c%u91Vl^MgZ?5<+e)GS>Z)DcK;d2vwxyNtX;WtNz_|0_VHbk$l(``L1G*~&mXDS)Ls`C;p!ewLR5D)~mh?PA+OYme z$e!3A+*-{c+4WSaue!F9U&@HC#tuLdNT@UCM>5ShKX z+~{(g&`b49U348=<8lmjO#LBwNzY>Ijyr#I^0giL+6C$LJc){)J>S#1(@!^vlPE(^ ztV3o$XhA>aBDJ1W>`~IkT;viLIlt#!svJ5GJ>~da6VH-S6R$RGQHZ9IU%O~WHKGs|KS!V8(P7A}k6hSwdj zmU!u2ofog0k4Q@R&WP0?kJq;*mgnVl#F8(MjMs1BHo#c@dt8<>GFH!RzOQh-=3jH+ z&Xon#$rp7?jD%bHF~z4)N}&RU&=>8#lb~Hi z;JZk19jWg(27G5IxZ8qfD~R47@LA}9MUGUY>i1o&P?trXr(m}QFHrEP1$nU`7)C)3 z_$<_Ck=9hd1+9X?*eLGHja@?L{mnweZ;h8S2FA@*nl0O+315QwO=4HHej}MqaZGBxT$!q;Zo+0layc{|EFpdBRq6fCLtby zsX9oE6iR-bcyY4vhxbQ2`St%Hdgx$C75ni>20CqDqBbb?w@IS0+`+}w| ztZDm#rY&3;%aiT)g~Ezw{Ok1ch6+$v^!k!JYmQp?B@6U>&6hzbxyJRU63@a}N1G*-%m+O24HSJQ7TF8I=|#W5-mPrtROq~_N0>O8pb zoQ`LDo?{NBSIpxTfh)Oubrpje(esxx z?Ec2=@9h65a>A&=C_(rs_bwPDv5%0)K#=*VgiU027>UCag#L@m4d*bqHuxP{7-eG)X$e1?Sd zv+$V~-eTd8S$Kyg50Vc$2^jsn#qK6H>M~0Nda(zTw_kZXt%lE8UK4`RPg~feVD!%{ zd{nsym7AXdFM~E$^iM6_#Gn@*jB)?Cy%?b}G00Ana}sgA2*GBNa});GRi%3$-qbG$ z3(2uJUe9~#$USyQv({YnH7zmuL4~I!ipIqAdgF=Wit36zHHo52!v61akAJc{G5%uz zciYD7iLke#%Y5w%)WE?R3?hJz;s`AqWIoe-uCK> z7yRFChq@#FyVKDzuZEjmXjm4_JH$d(CyH**m5)_)`|oZoXls}wL3uVZa3QDs0j@$> z9sr*ydWIWl+Hndvz-1~sI2bw1JuO^qx?C5`_809Wm9v%Q!9TO85tzdy?OP*dxq{;3mGJ!x_v$%a zGy<*m$MQBSp*&k)B7u)6W|Ecs9|}(H2~fh5*Dc2+0-S{sb^g4@|M_B(xIX;<`!0lL z@=3Pd3CgPz2y-AI@zpmF2<$|*A-A=^m$&PSp87crRZBF(2K2F#Ura0>Ggy--gr3-x zdB^y6MSi?u>-0qNs0cJvQ*n^hPgadN7Eeqh{mppAF~wI~{N}1L?^d0AjFXQ1i2v_1 zRZYkIO$Bt&abWT#;}yU4e-}oKO7;3^vER4gyc2dsl0956_~Pi?d!B*PGgb#; z*wo>9p}I36p!V6J_{XJQ1txx6-${t_LvSPWHlJK8&)O$`8f!`&zTp$GJM-+{54fn1 zUyo?n!fTajt0jc*asM`2diXKj)ym!M$~=F)`?tZ8=5JEb8Ldj0(P1ewIxS^JuTo+u zYhbsf#JXH_eb(F-rG&Y{RsXtOL;a)T`kuyFEOJXfcN?~}p{?G{Y$F|ZvdNnsWx=3} zeD-8k12jS4Ga9)AMTbTs1-duq+b-)=HvX)9UzG1tC`m+Zfg)ETmf$TS;zM@N<6nDa z3gUTflu$oSkXVV*TnSNdq|8c`dL;^737=9#!d7Bpt^~~nBa^JeaY&I2Q&9xe#%QI4 zvn8Ur670BOBxWUAy%I%~Slw#UZAP}htXzSR3d|8FT<_%%Rp)gma=!LFZ3ZI?Qa7`E z*DJUPJ$U#xMb>K{Ok^~%`pxjaY6Nq_w?_KJ7ZcIEzIa6j5^a^PAQDAaI7}<62R`aw zvyT80oqOb^D0p{aqH+w;>)BHCr#W(TG;24*4g}{wm^XZ4jw7i#j-=)oNrjE1!j7cE zj->RzkG4!Hcvtn9s~|zl!El z7qz3T!8)ReFTDphF=@m#lTL3lsZ^KOUz6~`E_(GToOnf2^Bk|(4=YK*8>+^1x=D|j zgc-9p(!}KdHyVtre(NLtH5V#moC0n*#45rgQr}-5;O2Zmx|#S< zsny&`s&9y}M{N;eZV_T`5n`K0Rj+O>(YD{IJYi!SVaGPYj%|b=@z{duu;q%m<%+rG ziWvt$$Iu3M93bpCK)7#IO~n>}b8U8?M_O8^v6`Z`cpi7_`z0 z6)ex?usu6bIi@H_fE8Sv>$=MgKY1{5@lP{pz zo-@g!dZ9|SOFQd6xE)tHDsE=k3q1z_e_{|TTw%n7*6e-;yb$I4ZbqZ@#n_OqwM*}C z`fpbIHLbVht*jDksL%y$$7oAy%QNx%Z6H8s!G`+WlW5GV;fC3G{XVwy$h>`~`5xvo zDcqozhzvRvSvU_$F^~~RP=T~%smjuhYZBL?1t&d4Sii49OGaQz5k@O6h$XJ}RVU^; zh1jTy`PJ$g2{ETZExo72N-c56wP+Y%bc5FN;7uoLw^7^n zPQOijS)9pKTLmhq#fdqfBtoO2D8XGyD34S;bJJr?rWD*+dA`IqB@HUI=G>XwrZ79% zX~j7q&xc8fpBSll!@uqZPfbP1ZCkxrjKs9(Y+*LME9`XKJD3C0aVJG8-uADX>=lOm z+Q67pm{eDwm;gkGIxJ$aJ&TSjPgc$^T8>S4ujfJ}h}++%#$RWZ4k8E8b6yU0o5>WcmTyW7Te@LiJfK&)aXgkDqej5GRz2u}C$~&)DT{61f4l4-!J|5+aIe0khg09z$-PD_Nt0cYHL|8BW{nPfNniD2KE*O> z`0fsKyr()bJ2#nFRyLX8rak_qdo`P1e$SccYi{)>^IAk)aGYjy0)guSCr#psKYLcj zC3WE^n%7Uz$0>p!g$ous$PJ|qMxGIN-sW$*oX%G#BL3Lw{yzn6FuV2d zTboF!Af<}CSdxcF2@nS_+j)cyJGwgWxQAnN>)+v(p_ugQ))Iz1GWE|Z&ni4EFP67Y zFxv~1j)0*H*lKBGH30c#NFApGRe-fk8Wig(W~l$}Ktx>89f(L0P7xymdF}ruiv380 zw}dX=bJ{|UtgYKgR_mU4-d3qx5g=I#<~vg?PulUPUDQ;c0~Ubs_)^$eUSxp-4Y1iz z!+l<)DQAT}zjMk>ljfBD0(a-mdGUJSl1A&d*>j#6Pva>HC@GA*kCuaE%+%oQcz_WG zBSy2n4b=&Ql_RC<-TG`9xO*=Q&uY`yt%Omwm9RcJqiGmO%^6#v76NHib-dlus&Fu4o`gkh(@&SDNyEO2)J`rN$;A(RoOFD9kbRX z7Uj-WZ_J&m_9GwlV^YG|$xh`adrNMzci3cW+NBif>@*PGtq@0}f$(0{(apMgzYi#dL)U=8 z%t0N>2JEOc=(FW?SjZ(ZuexGaq4m=Mf}>8R~_Xz%@9VTxXDqKT1-$6K$Q(_Un*U zsFEW20*lEvD}@E)nX#B7UMb8Agtu2pubQh=i6`T%RB5hMS+3OhY^f-E*t$=9#mb4b zA`^2(CgqAuwjwPhsx7wzI zUpYbqQVgpPS-sV0QTy7_qT}NzUP3SSX7LqOD1Q(;pG~SlV~a-Fy;w`j{xpZ57 zHp@RjfVw{cpWtWG?|gmi`ci$7uU8sHbxQ~Qo_+Ju<*}CSu|#1tavur!hS#Tc5q*lLmrmp~eeq{yF|IIh-@x@Z@7FC_c zw>S1Kdlc=G6I`^*q|+|JTyzx6`tb2ghr7mvL&KHDAy2KOBLl#0ryktWlDuY z`5KuM(^EyEmKv>i?k3&TG*y&n1x1OhJUF)Qv+nsx#VOBqNq_YP)=?c5cX11!J>NC^ zXBZvP4Xym4laF`Z{k2(HZKu0PL<9w_tJef|A%eQ4zh!&8K8XlT6pj&@B_^L`R`qhw zt)1?a_o!!6>#!FA;Y{jI!@TfAfFW>ryCmlil{@B@^w-I|>^Tm1?X*{xK$bqk3@*Tg z*tzP|l;m1;M6c#+i-iLg-eKVa3wJ0tyC*Ppx5ZAf*uBI?T?TQasEh4XUiMdDYM13L zx4b5Eqh%I0p&Ko+Fpr>gDR+kKWzg=7hAiErF5wtJQSN-#6E+UbIvNG2U_wX082N-6 zOz&SHcWD&{W})$*6V)WH48-bt^R~1+A475KjgH%CO42>?7KE>PoF%>XfU4mY5A;M0{4 zB%`OrOOIBaOQw`E#4FzQKR{~qG#*6PG`~i4I(7*y3&5`$ldd|qCw}YV($1RZE|OC2 zbdZEp)wwU$ApOlX#}a7$ZkMnlDy^p~5qYm_T!+pmYZAWCu=A5u=kDO_Lp4`dJk38& z8MjoOn~IZMlh^Mh#VVfH0c0%ix1`4NIBrbEDvryfj$2A=bWA4F)Ys4u%j=GF;#$$> z(vH!5xFFL|2Va;g6ymCK9wYhVC87SzGi!OYALP>>2lS1b9r~cn;k7)T;q}tx!9OSU zMP*6yL7d>H6!mdE^51tZ4SD0lCC-3$)eSvh8aT6)?T*!%N;dMIpR7)W@;R1%T)(j7 zIhNL!y)c7yKb;wLQ^Yq!{Zm3nxNKMkSkQNm&Zi@ZX(ItG*XcGmWh)_@$5{-%nGx=G=ph0d~9%9qVNo8a&x@m#V;lb zgV0l|Y79VP|3{#;%~j_PR^u+@{~rDf=*7xdHRkQAa}UQ>uPE&YBK{r_Fdolh@d%^wwZf_6Jt1L5bxnduyb{1{SZGZ*fSp*`dX2mLRsO zLUn6q=tH6}am+L9$)?#3om1i~Zkw540e~!5#&(wx-d`fml;-Eqz#{IWfI1RM_`W4}$7wn2&Lp!(wWeg6*prUwyAPia zgKB;Pu|dmI100$Mz)$SZ+$p2lZdM{>C1Qyg1^P7N4PJ?oc-}7ZOKv-rRlCdbOSAOd zNB%E)lV{Ht5p$-yuq3`|{+&A%v{zu3+c*Lnq(=o?>TE>`A- zRI<_aez`)v``&NuXb(pjj46v>;w@3xIP$p3=d`VjqnU5P)usD=w^be>eKg}vc#jaO z($&mxzn$LsI^PQ(cD@di4lGBX>t4m7A-e))PN%tik9U*y#-dgp^E)BHEl}OE{GB4m zHW8$#X{D$Mvb4k7d7&A-c_eHb%D69^aiQBXZAQ88r${I_(kaS~^ybBJKyI#w4@O(I zu70sU(y}Ex+`NoNip9gMX>CRb7upJI(<}y@Jrg#7@DVSvBr($wm9U|I^did>Gku!; zLPdVtiwq}bI`UBDGB0vQVy2@3Mb7sk=dVt>g%$-&_ac|5!eSN+u7oHshPySwOO~s4iXwflZ;$o_)$hoQM3E#&7 z$K4voE4ITVaTV*1G^PAaPhp`zQ1OaAl*9Pg2N^^J5X;{H%H#{@wsZp=fuH4qntCSBQ24XsKmEcYp)y4jmXG}%Ro$w&*N+o z+|iIvxqj^BDk0Y}US^xRg;GXvT6tAfP9{f&`lS!?1bxaBz*JeFs4y9ZajaYhaL$`5 zm0<=MhOwwzh7cLfQ-=BCN$^g%OqMUXOz@(=Yj{7G8S*F2+P^)cip?$p-+?<||Hw)^ zHe~!EqIs>Hk?C-Zmr}3Qy--jrJvX{a-wg^TmEUW{GV040oP1%i(vv7&UpJid4hz6r$EL>*c zb1f`n$ID)B(Rmgv$?MfVNkLc1WCbVm%JBgBai_oe68O2HeKZU=yw1*M1Bjb)i zw#oia3Ux_#|7j#I8Cm_}sK~fvq~fip|4UC-HE;E=yB^An)xU{S_H)|Pd6pRv_EB)h ziNYm{H1#PgKP2Rv&hs0tB!5_*yH53i9u#D7fpDn>>3bk-{3l#YiBZc~Xk~m(8AB{(gRf~ zII~r1%*+mCrfD;#*+g-_`0A9NUk;k^sbf}3t? z=_gk#5&u?sHKzmUcBp%Ccu+-~r{Z~E6(^TD1^guwhs>T^!eht)#~lA)r|=m{%{hfb zJ>0*w#(O_SIP9f}%a#|UgK>euxTli8Wv!U>al?(I2!bduNOluuhI+@(;lLVXHBe(v46NU1`56$kP0Zk$5mNQNs# zSUUISU1Xv{2b4NVp`!{-=2bNPkf884&tw^TzR8$8`M@!1L`TBTAeSGI+fP%5_R|z^ zKk1bDMM_fX6#E9cWfqZ48QN7-hIZ8yZ&#_@kGyiclbIZ4<+Qh^4DGEcLwjqAx3|p4 zWQkYMV2v;%4NTGZ27_US8knN{R)S&1DhNTr$#&tZ`O_JHiHzgc39*+CWGw|wh0T|I zOZaY2J7~h4Bxr1QOcPdb?U*LatGKY0zt(S(vucn+MT)a^ELF(XaiXylW1+U11xg{= zQ%GzzE!9?Yl4B>+U?-eeN&xPWyGo9qY(u=~_{o*1q92MA{e+mRbcr$W}rUWK69z|@pNHWj-SvSD^9#8|yoT^K81 zsp4#+_9|qP(5;XSuwNlY8JN05p>l+c^r56MlE|gFbC3J9+(d)FPHpS9*Ds!J`A3N zbDish}gkVxoJ1C$j4w}3CO?DZPCK@LT zOPZ*lb`Ro9L6>2&f|F5s!@L)`)st4=`6sY9+%9_bR-&D^Lu&6dzsa2eXCSq8tRx7n z@LPy&?|o+OQqenh_V#`KpDO?Ych$#7Q0xAx=3V}E6S(LpmiYMy=(aj>kNv9vZkvYx z@ZSsyO^umdhvoM&*DZZgmS42#Wq;G>D^#xOTlyqMbw#UkR#!aFGK0JNn@UKHw(Q3# zB;JxqzB)S6a)9^i#?<8P7}Dp{{ecP@a}myUN2}r3%}Mf3Mr+h8C zI7{aLFT-L#U|m>kEAvk_+_xd+szNu<$Y!2TKry$AX<6}AuJ|y!ALGp-Q3eTQ(E>bY z`Z67|2U{`ut_|sZ5$ai=t!IN*bIYb|Osi@>n%POmuFc;!N)}?2hr`0QyW$<$%$;8G zdwa7nBP5NFLjSqqhqJ}!31#ccY$wUXJBAsY`&`X>mB1(TsbiS3 zM?F(^hbyi(?X9@(Sy1us>&<$_+^~wgyRBDN_Gq^3>=2-0UuMTpuMTCwm3g5KBWSiQ-@oz&*0=!klP$$m6vvI~tgMWrhAx4km4pS5N!nit#;GRagUdF!*6JmiUuwD+I)@&k7jx`K9O2YsF&=^@)623@={)Ud0?&4#^AU| zN;j%-pE5hTtO<2%R@aUY?JW;1S|vsBo`7g7RX;Ygi1{BJ_fY9t6(d`Tvh@d+Y#{ar zrCY@L$iJ%G@*nj-FhVABQ_rJeON)Q&2fZXihc9BWN$1`716F4OImOg3;#r1IU_A}hu%DDBqZ0UN2EMX@H5#hI6>+pVKl_u|rx z&`n)w>>xFEW;4#p9HPc^LY+f3t|%=_uPA%mP5;r%tkN>klGg_y`ybf!Q2p%T{m`f2 z`ZDp-f`jyYR_R(A+3Olf`!aXhxM=A75bvk8UnqK8F^+FKrzd6}u~Du~Kcr4QWb`*r z-Dy8PKQk&_C=_DNOfUHUaE(?{-EVr-Q)FsD?qE^}Nfp&AJvI+&_FJ@mW0bUbrV zJDggXlY=(-?BHQrc47WuYOPV;t_%#X%b2AYo|v=oBWiGrYa#qd<6!3>$h<^zI-F6& zeytpg+8xnIxM*A3`3am|=O^4#B(E^Gx$*Y}rCVS$#!U{0F*^3KECiO>m3f|;7v^sl z4|-7@+N#oZr5jaxt1{G;t~EAwSXoZ!Gi39SkqxsxJ3no+xYuTim2F=LRnXL{)IU4_ z;HdQO487ZJ>~CJ^FiTf715S!TE(lg={3OU08XW}1wenB_}hv8m)_*lQ{> zS^Sk&p3kG!Tr0E0WUZCtip=Yf)}^r8RAfcwJ*rnfQthMjc{yR0gsAeRo3sm>j6{w0 zjgV7^KdZSLpV{J(FS~IIU8`zQ{)cCJnTK(vX-IY>zC_1gSxMlfuAw-v@~tBCZK zKbksxzUcL+=6412yC`+|LWzb*U8aN6vkUWg87Xxhq|E`9|DW#+B-Alm(gR)z+KYjdFVEP5QAg|BX@9sCE`)UeNMs z6P+J=fz7fa^LuDOB2eMY2UC&7nRnQ(i!;w2WXrG)CKQ=rDKmE5w?edH$9>t=dVMWx z1Six&v2L%knvM}l%!$`QQH$M@h#QX<18yG+QKs+II^1b%B$5;r5W8C}Xt!eFzuolj zTCpHYQ_PlwB(lJk12%w`maNMhL9~x+GLqMplX!m;~Pl`BJUiE$KspQ0>u2f_qx$0NKx#+CN@YO+y_*CV*5Q43F(K5cBCItkCs_)m*wx6klxK*#kjwwH#`5; z3F#jc?Mwe)?5u_%9FMU1HkJ%Cqh+H|?f= z8Ye7~5Xe8jBT;Ki)jWHF2RHpS7g&!rAuYCP^zb&;N}@^an1-g5N^@9tC#2;<$o ziV^wm7WwyKod3GjOZ#<)rLl(**Q~*t6ViP}&!+pvKB-(%a#h2xO$pUb7ja&z5%mXu zAw5*7zi|3zfAFW$KNa&+Q#}=0=Ty#q#;-I8VLOkphqbEOg3D|>@lnXZvHPj>uhEuQ zp7q!3Nm+T;S2rnxgb>--!~MapZs0MsRf$uXGyYwPQPLm0vNK-M>2JRH*n96K3JGA#fNEg6U8y z4-bnpm}jrzcsM-K0W_dPfrvpwT?+B=u*hzOz;K4$PZH@>T%Yps@Q})Wg$5%W(SFCn zg9U8!cEr>~@dG*!u%jTmtfZ|vFIk<}8m;(^f88FoC_q6NOs6_ACmbEO7tFm7XxJV2 zzJcRbWX!heMD=6`7aKg#KIZvI(>8z8zgz$o!Ic(ken=pq7m>EcTiOMZ0leqe0iNY}k|`R6u(cuP5Lh$o*$j%0aQ^ zmZT>bpM~`V<+HG!!ub*ld$~$sp7R?pnCKRfL1M~Z@!J1jd|Yd^aXI z+mRDmCv07v$;-aSHJL+mJehDM;v|73SuwIi5z&$5OwRIP;RyNp#7AhVmgCh~Vs1Yp z$_=iscl*;wg!V-o>y@T0fiY z{qWjOQcn%9$$z##aBrswtUoZ}1Yk{K^Jm7JF^p$!@Srti7vJUoOt|5~(D#k`@-xz^ zG{Pw!^`ut`AVbI zy-Tt&s}w_?pRHGnG=*x+^%Yq#Wd*v|7B^w$@Dsw}ht%85l#%rUzQq&IHLM!^N2FMG zlON3m#S)_j{qzxOl308Ibi{5&&y}G}bW&-8m+VMyyk|n72q&YNw+~`HWX%>-_vcV< zrFNoaKVm(?#+I!vix^aDyv!e&DY9_)td*$WCiVCsQy8)A446ocr%jX!v$?F%B5BP| z!!A-^?=-E~`d81-OfY4eD24@X29b@yKWFSS>V9`*6 zg_&L&^zZ}5cbghK{8{u*6n}@Y(EHN`&_xO}Z_(RA6-Eboi-|9D6K~LjrrELz5GceV z))S*09p*_aGGFE`Qb*@MO{y=`L#odz8f*62WNct9oWOvwvZD;D&?=>gH-Ms2W@{=^ zEUO)>X>b(w(q`F9@{7DwUS>^y47KA=g@_=B8SED>~-j+vMD{Uk! zE3f8fmCn4d3!fGF{J;owD1BJVj2Elv_*x8HhpirB@)|O`jMkv1qs$r)8J{)zn3wFb zqpA{QcZd1c3E71Vfm9#TWMi-!n5!xGjN=tLmU&3Nsb(Cr^U!UvemZC=svQ9B_oL_M zJU~4v|EvzM9}ADV{Ql(gdo_0eUGTjvd0{J*35BQ8o(_ z!P85V%j~?sY*rZ@W%Evj*?GTE&;=Q=4#E7y336-ml9vYynANRcn7bnG?0YhHDDO+2sa#8M{4vV4QPGi*) z2CAjgYB@hN$C&g(j!9F)+TLspl4h!5eO8dIk_@Ag`yJaRX;oI3JvQH(tMYJYxKFZHI$4$l7swpmpvL5xsz?)X?Uyp?{N&D$^ki}E$C-Fo#%4IgrTP7g zU#yxQtpF6>ommX(M;2(;Ib^RC?HbpH!wRe`g78xjo~-IhS@{t_Caa6JO&YL~Yev+_ z$=L9APFc0Fc@Z!mk?MEM9s+exTx%AT5dytYSx%G(4LQltmvMl-Nsf1f;xLdqjf2`i zR+PC#0@lijh1+s)F_Z)~u7K;=0vG1w1M=ft;mYqxerQLJh8@$5M{E(ZLLphm7Wafg zySDdGRldx7Ov7j}W)Se#g-4008^R2UN;JfWiH6zvuQ8Rg3~ioeB1Y_ZNHl;B=LT}W zPIm4$eruWF!dwAYm)RpOEZ@-!g|xvfK<${|Lo~I(P`i18DA*YelD2~;&@;o-;3t9w zuFba-w!Rz-Ho)2A1Jv()Q?I3dn$j7=y!;(%daIg-8`pnUQ1ERmi6Cz_bvNVSrOLI#l$4e?i#%J~% zL`m!Pv^Z7*zKL`OKkgEpx^i|i?;9p?`t0hSxG-!Bv~-As5||Lz(AX!`my=l!jjs- zlD3N>KTl=C^Zp&M<`G~`fW+CRuFTFqV)z~(n=7T2%4@}U+ph&()yg_}QT;%f8T%G| zLhxGg*q5Y0A-q((bVw^ zOjB6re-dCbi&WO2mkgch$3ZDwt{N?K(0&EWRSDRY!0r;kKCrg2e03r-{_$EI+eA5t2 z;hWCc3~19sIt|}6q!TZ+z*IvpI~6ilv@Yf2r-yIy)5A9n!35g$kWRxl4e2DoLpo25 zZx($JzR8;tZyv>AII(mH-|PVGEU2#FHNo$;;}yjXahb@NH>wlYO(HVq0M$O`g-FvI z{-%$FJcRSzPv97rso_QHmUcO+nv?2ND2{nJ0(!_W_$Kg6ClF>ez-4!)7ErSnFoEFA zUcHnWxI&2$43Fge%r&I;O93Q&g-gD?HI-bHsh4fFD04Zm=U&W5N^>Ngy?eQz4>R|V zZA*6+?M`=&JpciKc=p0idO4Wk6U)O8&uT7-Yo41$Jfr&BgB%mMv!KBErrbl9iEJ012}taq8N^9q)& zmZnQ9B`PQ%IeZy;1|lUEF0-&=1bO1yI>{^OAkA`>atNqHJskq-P)~<|PVN~^5%>Q2 z$i&p4(S0b?M^m@`gTak@Nn{n4-U!3}zQpPazw3ifS0^?(zq(d|eh`rl0;&d9#3!=d z-}Id`mGxU*79x2;MYJ`B%bVW*WSFa!!%e-sT1vJL!u+gVDCmX~9{0A=iIaf8s?d+h zJ@e>iBM4;O>OcfI{48sU-D@@%F}G)Ju6v!$1@ve(myFK$ zyY>BYiTW#)!gI_UI(5XyL+1g8FiM}>4h7*(g6$4)xXFs4v($D7C-599G&1yO>A%uT z!CsaJs%3xP@7bB34OREb&~um@`s#k^R<8*^!?341De>k+A$3ad70FKP(m6GnnDcm; zEHjJ7I48_zUx4+kV_*Rg=Jb$C7H8gF>k6RPoBv?`Z{)dS3RI@aVT9<~z(Xl1cn@VB zH{FP;XNO*;>MEDN0l+{gr;rHrb9$ld-dX%0CzMhr&^#SJi`8_Yu#1N>Z%e}!>{KOq zLKzEO#;g&Bk%kphn0ZD<8W|%Edf5(b{~1gtv|wyPGB1LKSc!TWHXM^`LL(}ur&=H;B+cW zo4f)oUkZ@DoYi)^nRZdxVaaG_z}gflQI-FkwFy>8YlGG%Kv;yFgrf$3)nJyYWz49! z%*q`@W+hllH#D}wjK~#3MkGCdON41PbJj$qjL31^Dn8^z7Wl(WVM!TS9 z8OykKrZR6bTQ=K7h?33+eH&IUl=Y?of7D(mH1;;x-cSbAfLSF>who$r%-)EKB)Ucs z6fxI`rmQsO@PM&$Y~2^~FzT_q?-wi(Sz-9AIgt^@s-p_r&kQyh@~Q!lz;3*g&SRBo zOP?d#>1gITL&xQqcCEpa=6e$V z1n7FDc@z=Tj@OTz^q3nE(%VGE?B{3AddGitvK;5Ia@l?I&PpW>=L2`wBKow5w7?>| z#XWq!?Xvh#(5I*h898gRNgNWIYhbl0|8 zcb0`Vb!4mQ^kO`qc1LC_t$WB8YwwUP<9mad%uvVP$_}}T(;@3vR%0-qXq}j?4bU@< z$6W_NhZ?jitwZ(ltyPCiy&Qyocu_3ZX7)Y)!}sf;?tbIg z%iIuk&HOUq*VG;Z16~%2Z4$#pkioW|602gXGE6qmSR7fD4U3B+FmA$~R;1S}!hS&l zv72@nGlT=RFT(*03ImEO5^sPjJSnK|6&*h4sOnA$(L0SCYUKJuN5KM%)$0Aks;G!l zEXLrW6#@($BV>lD#TW>*(W_FN`WWLQ7Mdro=3U2I=Y(F)V?vKw5Aess-~S-Zvf_8z zbU0i~gf9^ZLe-a>e8?0i%~+;{*C4FLSM(x}v2RhB33W9%S383jqXM=}FfwOAcF{y7M$YwTvc?C^Rr+y^f1dDThAr5`WqPd_f`=|7cEod&JB|1Y09nI2i> zNumGpsmuA){l5)8eNH}g6Nh~2Ue2#h_{w?3@*=)gmc5@g;xpVyK6UaA8|GJMK6ELp z{Rob94tJVr2$cNlB8Dt=C}aqf;Z6We?k(P_BzW)Xb}1y>DPjl|KLZg%p!gY>Y6uiR zJ!Gj@Aw!@HcN$b#;Z9RY_#nT!55t{q+II@v$#*K;Y2vAHr%!UxlzG;jN#Kvcoy=EG z=#yZk>stYhzGC*41@`tliZYvW$MY^&HG`1NrwTGw&O;( z&yc3Qyy(4m?BSDdoE*yJ5U0sK|40d=V{Tx^>oL}{hu7H6@o}|X#EkP}Tb9nJ7K*sD4x}O{XxW2Q zQws6;zruMm&g;I-(^q+J99&_f!L622lMcD zu~peJ&rz#DfsVk^#sBEhF6s9ET*FwLcoGL zv()FnPX~B9s&vGPzo-8@g%s`i91rh4fhmE+)M2=*!&i$KtM{&dNI{3j;$_MmxgL00 zvKj;3T2=` zN;Tp!LNVfj zM%=Xzj@65JrgkaLh^Jp6BOW7^KBe;0WBUArc!u`F?XS2w;r7NULzwa(K+IYF<-1+E zkZzW>H3-nqAB4-4d(YVA@LW=T%B2|g{x(|$=XUX@ zlh%U;aO?4ZjS#NpjuOY8Zs>RO%3T@5)}z2?*ryqGOQVQDR<1CqJ-b}7q7#=xa^yn< z3+uSaD_DBsYPbzlss`P{C?dPJC0V}n!?#$BrIgmCo7cSL{_p?2_O!b%!ri~gJv_Jm zncb|%H*;DD&DNa&Ctd(BlFQ$?B;fD-Xa?`*`J0v!u*(8;``fqeN`OytH-PTY@HaL8 zHw$r1!1T}QD_PF{`(s0A6p4GhcmCz>FMn`5vUr2b-`w?ZZ++ce642>y8aGMBn!fFM zF>_~(J~zX$N7CPPI%!ocJK`wg_^ zq1mDDl`Eub<`a+)%H0klkSkY#K=r$qSYBK{zPE%LmDk>&F_37ZRlPFw!+@8xro^?% zoxd2Lp*7`-mznwN8P3Uz@_&E6{_Fg}jwY77!yEc)M?@qzKv!+M(-_+L*#=kUHs^2x zx_W!L>Q~RcZv!IJyb2y4#;6(he9!qG0hqZqbgwOlPyl?^^?sW>Q6l|zTOl2ODKi%RwRoGmRkD*W zEnCF!n%l!ft1|C=N(V!NB6S|r7+f`i4y2ctx!3OCf95So>9ZIQ$trBgWp3f*yb0%E z)hHr3oFbc6QHP2ES(3&ok+#vLZL}#O4Oi9Wz=(_3)0D*(MuCCg28~-~!EPFZYklgC zirX~x<1zY}6^kjiTyhp!Vf)$7Y;UyGvTSNhbwu5W@FcUgXtY29Qkt?U7SWF<>OG21 zWuq4<-$3vlpVueg_WBz>v9^G8-JiBT>cw+@pdrLuc74g!zSpX}F)uG-U0KS=Vs)&w zctxpgi`wEv#Y-P^%}~24j+bt<-+HIX{k})PV{7Fj80U3)W?YnLM-z**ECa!f9Yomo z9r3~+EPKegbJ8u&XezyLd~OgisjGd;;%J}Y8=$MAv3t~jn|l4eCuAL9jb?ZSkHQ0P z_{91-iWmHl)~w85?-piQ&EDG2?@;!Qb8MVY)(q>T+E-y%^a8@2o%{~m-Ow@{Z&5Vu1xv%uhkWj`q0s1}4< zS|8|bIepL?!&Gtys-mMosR8mT38YG#Bce^q$!^orYvmxn z$5gSK40UHizn5cEgO_9da!sjfqK}YeAMu(;@jb=j%t-yft;8+y;yAM*PN&tZ?j*0q z@Frz<+H$LX&e{7=lz-xEoBd*l@tv*dr?6r;_3R zJg$KEHy=ao;p<#4F-6?Jr&sX!Za^`&;7=S8&yyNoS(qraSH&pG^`rKwtEoN$VIIM$ z?Darpu|iU!{}*j<10Use-T7P!eoI zgH!Ss&9bf2l18d=V(?Ib$}BvXY!ZidlN7gZm+pFN+B#X6v}em>IN*XO>Td+s^so_ns$X2oSn(OGzsb+BN;6^g4@96#}e z3HkVmFKkfiJo%aw$4^|QodelAi!bC!#QkWg{Hm}=upplaKW7!Dl!2f4!i;?U#22zc zg9R()YgHUSaoKPVQvAdhE?4UH@~u)FKkM#5aE(%Ll5efz_=(F>X^`S4zHnW0 z^4Zw-mw&|?(YcL5q8;=(-c`U)wt{oxU7{Qnc5b}Owuj?E=eI0A-nGN0J2&3-bsu(a zylbZmci!Ri@AB!+jd$Jd!_JL&ISdwb-fi*mu01~8x$&;MeAv11uDyh)H&<5%OI8}I7%VduuX+zJXhpRo9N*N{(lZoKOuA9ilMYuJT5U-tP&e7bYvUC;Zl zbK_m3F5G#-+<4cxPj_y->kS`vZoF%Puyy8BSGbv}1sBknK%J=)UZ6A1jdw+T*tzj8 z!%WP~EQ^n04B6#(ZoF%z4?8#BRp-K;dPPO$*ZXwm#=8(D=Wp`q&W(4? z@nPr2yXF$M&TRgu>r9>qIx|z1rF>@R#=9~;?A&;l!+k;LN{f$Uz1elfx$&-LKJ46h z*K!x`yx!+u<O~ih z#lyX8uZ7rK;(c6xycXgDr?Y4F{CE1ivnmo{Wwyt4{axz?P9A2~^MBvxzM>)rw%kt1 z&}424yv$AQnCEjld6vXp!2&1zlH4m;;G|!3$7YgmhwM^27W)W6e6ZLLU07kUKSX3M zEWFJqh^gjYbN092!VG4t>lYd<`(?4NOYt@H2$G3axug%z{1p16GcZYsJ?e=-x0*j$ zf12(eF~yWNS`^_#f}tW&_s;BjY!N_~Z#VwzN!yKbkPt`OrSClFxlwla-VvbOpB#yvXn)n|Z>geTB+h)m-$6WLRn z6lvU|U(Qa=wUuL|%%tqlH-$l; zo<)zDL96kV6l%!M%RiNIB&#?=7d7bTC$+_e-Eh$L!D*93^gbh4nL8~Tk^Y65YPk98 ztX9$6YkX0Amn%xtvLA|_7FK_U%PG1YR-TbvVh^#G0P!q_(7x1N+RNlW0lNjypgi(9#OGm$*L7h zhIk|M89$^fP7_F_?50tk-7}xDd(*@_Y4s}2csolop3Z#6(bEW6+ES@!I~W2Nl1vD+mM!!qv_kD*d_kNYgy z3CmKLOW1sS*U3kgT}|qOYS>#&+dWbC?GmG7`FF09kNmBB_}RIZ5N?*Yzz%b=$pZjv zTy~u885c1v7}BlqE5@scXGul*&M_^b-HmB+tO^99XH}rKoGn^oS{$oVuegN1;3r5m z$j6UoRhs0Rqd0z?iP?p7GgveUw;=^qR4T)>+RBhpC?g*~K`JXBKb}=-m2a8i_z6<* zD~@S#tja3ItyUaAL28YB{CHMnt$gi@<0nY1lMkllvDo(WT_XwwXl2?_LrRzYWUDYK zn|yeSxfQ0Ru)~L)8x+3o!_Ex~j#;TNErne^-MK;Gb{}?bP}uFm&J7BCeAu}`;VvI` zZcq@jqV~$Q&U_pxxfjB6juAN zbA!SfA9ij~SnI>i4GQf(?A)NR&WGh%XC8!TRKp8IoWZng@_C&b6t?)VbA!STA9gNF z#)qBDlJQ~Za%JC#oy(H(Vdt`BeAu}x86S2oOU8$t%aZY7xyH1d@B7m26Pz0qdVScr zEEgYkF3ZJ-oy&6ZVdt`3eAu}x7aw*m%f*MC%X0By=dxUU*tskhA9gOw#fQ!1v6%T6 zK&iX?KhXI#*zC4*v%jn{qW&nkD@6UPM%4Fzs7%z~jqhEE`m#Nxq%;j>C)-)buAFw2 z?PNQfH(VO1(DQb`hJ%taor{5-wd`FYj&IwAM`u}{cK7Evy=~W(Pgy+iecHhagm#4t zpCq)+IuK<}-uTg`sao8b4o#{U=DDqkB-QH}0`hoT{<`u#p|S-cPtL;^cp|^s=y*@l zPZI5S`=g1#k@t>1mv$$#9~6Z@lVJ=*adINyJtFco&!RmXkq67jF^*SPQfK?3yKG6RjX3HnJg z-qGqR*Kq}on%6PM8c%3DqMc?zkGC1|%dZTMLf9G=uR*qx3{T}39ND@+#HuHU`C)?NIdvdy=S43^Im(GbeC@@GuCz8lR-O-7h%Ts*C5gr_GsAK=ld{{w{71q zLIJwurxA!dJPqUAvkAgoKHa%z6M%bsx^qt__^dJiBs{{-+fBxwM&J$k!kl|HK{w*l zoqIL`H}2D&dpf~pjrm32m?~EDPb1I*x8f^OCQc!V6w*4T88&TAnovFIrnUWXqQiS?%4#;9-r>q z(+M7190i+3K*ncu?%4#+kWY8+*#ymqPj~Lw1kAWkckbx~ix;Ub^gQQjqE&2pa?N@z zU+2Q?Z(++b%i}ye^VE6l4`W>sqD_}rTtTs+DGph3W}$OHhxP`U?Aeta%)}WIj44?iqF-W}iP|QRAHT9q#xiuLwLi zdF31-FN@BU;I#VYJK=6Lc`O$0b)$=ZTTcMex1h0ZN=!mU*jm-xPVB3L7Ht4S%QLIY&eZPGt>Y_lAuMo^xQ7;55`4 zB<8-F{Z~=v7$;)AUgy}y$R&N#@;&&mpTGM}BC13WE1n0_Oq*=O=Zq9C4mq&Ikf>7+ z&V{v2USG8;XKIDrDI3;q>P`)rcSv58&Kc|xdB@E=j)B>Z!EEHn)H4E6xn!9ZuGQ`w zF{`02im_R1ulxkOc^*pKLp~b_GN$X*<2E1Jq?o2L(MLO+?EY8jrIX#yeB;z9uO4^( zyQs%uEEq2G7;P=`W!-gLxIbidp*Qi-77A54U2By^pLK~`QHxB~O`rem4*&W0Y_e`@%*fb2ZQoOl z@5ZB;?+jUtju}LKtB?_&^u2MPYtZ!Z^Wy$M&T-HDKw z?%r32d_lJxk3os(#aWvh)Mk}aBM*)Dlgw=o8sPNei0-OVeG`1vU_ErR`44VZ8&{+I zNbbLLphF=krArG>(k1JO0|}b)N$|b&%|`iMCf%S$cqtDHl?R2sSRhsUT1@SY%*;ga z#a+Z?q_Ndfw}Lq#hBKU&x-Sk%;Osa#IHeTL6pP~+q=FB*!;5Yo;?h6LB>^pW5RXLFJZ|a@`gc!tBwy8Vz2V zu~JEU94n~2qvTyXsJy1tXU8FyH*0f8UdIM1@1P3PIfrG?)LCKfL`WI-DuWI>EQ1?- zN+@%B?db8TIhq|c$Bp}qA6RRAU%P4P+2MzZUE3?gvAdjpCcR|d_%=}C40J25Shv*I zfjM}dhX|W*$4pAsn3{b?Q(@YzhLXwD&!l{GM_Jj)EHDyLR%V6q`nnZF{S^i-#re(p zOVKJby+kz)29XL1b=C%HN~!cMWhSz)+{cf)x-?E?H#?Kmb$eNzvas{?d>Re9$oY^= zh;31bN``$VE(xnj4>c4G)Ci~*t8*FLbvyZ4J4_kw&i0q%Wu>+|+_BIXvKY?WEf2a-5K_zpP_6>EsK5z^ z)DR&&^)97mb)K{SQaxB=M+`YSfZaHGtS4{yg3txF&aJ=H9@)}5(^rEUClJ9N%WnYY zS!aE{<1SYfgEdFvpy~{)?I1{_Fm6M+yIrZ2vu<3UtuviZn&gLb?OjSMtWzC)Ghv~< zzD5_C@M#881sxkHrvo<~9VrCxGtH>4npz;hu$Ba(*6vaKgz3Qj+=Mv$IbH|ub(xXU zt;ig=BlD4`pkUH9-dsQ5(DNXz^M_OvpFPM8(!!FwqC}>;2Tfu2{ezh#1nCB)*2{N= zd>ZjZ1g&D{#YsSx^bXCpMTWnAMxRrefW77?sDl{l6B@H-x;kOIuocfRklQfjgDWB7oslgb>rB`e0rU^Y)(Er%ZI)6 z!b>&4-afs-r;9SyU|iwD2^aRdadCgF6l|R9)0=$$c|JVH=XddOJ65vJq|b8&XURZk zBr`L~hbqhak2C(`ln*Pf(wF;is}HaA;bkuDb>rA{eEJ%nzRHKM_u>q=g#? zea3E=apMntxYvcdTs*XtPapQ_Lq7b34?pC>T`r#G;?qZc`iKv|?8DEyaF>f`x%l)6 zpFZxxCw%w~7w&TLEEk2(EeAML2d*0%jlk2pTH_z z;CaQKu-I|MI>EskT%hmxWJ@Ue$|bw^b1)J)A=r_#?5Wa=hM4R-{O2b|*H*E55v@?ETO+2AgjhDX>Jvm;D9868#9aj6YoiC@d z#aW4L@%ltfzPLIOGreyhI6SC`+a@3-=OwzD_2Z?evRUw!B$sXjUX@9XRA`HxigA6fMOr` zdvsXU1z@Ulu`k1=m`%4+csk5Q(#3nzC0-6Mz4T$8%c7)Ah?q+gp>FL~U%_^Mu0ax9JIj0$1{m~VZ`V_@wIF*lL*qCC%L_R zWQGboiE8y<;2RzOuWz2}dpjU+`umUusA#0Bwo+c_==ppDfxjjTyfkTdJ*oaTtBm+5)uIDW;*N_-0G-o#x^LWqfA!Hsy z(r9(Dr#Za74+{uAk!b~}? zzBB#N(dj#VXR_5R6AjJDcW!#5+?Bbu6jd-?iB9RtH0#P{j0f~Y$~~x~Pmg6ID_vKt zAFap-=sM4J#WwvLz@m`}vKnx!zH@?E{_g-8>pc6J>(VRJ*QGz3{@twgat0n` zvODwPoPGx)SJsq4qz0cxhY38KDfR$Joh61`pyj|NUwmiuVu}+^3V;zCQU_1jop9a* zn)y0EK4-$!_ZZblxUyC=z0K6Ja|%2a-!ynC>xk}j%L;hPQeKcN_D}BLHvZ>;rv+Pn zGCVCy)VskCvlE%dvC>#|aS%|tPsTI^N<-PmGR7cwr)rH*s|O}q9sfoz-UBez8Za6B z0KNz&CV@&wViK4p@v&>unecRthFgo{XAV!d`~qCt>9E34RSFyv+bn+IT=PfID|ms` z;qU^>sJwiSux`CC?*KU+k% zGk+j~bm@7B>{lJMw>{p{_Pj6xcC{u~e~>F-B301ep!HwR`p*GxI8={+3?^_A6M?gDQh zg5Bl~IP4ao**0wDL8zcUhVE7?uvBBn&LO(dzv*NKwRO85mJuKL>TJAb5T*UHye=}~ zJX`2fmPSh(J1#)fG~ZA-Mht`(b-e)juibMZ&b#Wz?rJKUNrMrUoUgl{8kE#s`eZtRcYpGM#LFO}3kc#G7|QZN-NWB=_msMH3`hb%r< zjF5x2>;7Quar!n|KUw#OD9zT7hwt-9ngZWrzj9h3R>X^-oNV!k6ktrGLvfT~-TK*@#o?Sj#pSntt|lMWR-+Cvr}I>GY`bJc*)UUGpG}s&bWtwM%&E^MOR+zd zh&IE60=>_2fWQN+Z(%@7vXnGbmG+z7(v3kP(swMlB7%m6UZu-N0uD8>vDY!AC6jF%ZwY5MVJ_&`TEa|IbV)et@ugw3 zHybYdwP;InACid&mxePR;%SWTT=HP-Ydz_jq1dK>ritOTry6@Yk57wr{ZB$_)UT_> z-SU0BcVA20Rn{2Pf|~0V<2|{&q{mvP={eThZv&BISQ#BW#OqX8a zrQjFN%SG;+q_cyqxI@X-|Ne{QuMa&6}`P;$*6*(3i7jJ$+&{!3i7jJ$%KLv z3Qn{nf86oiu@i!baOUCO(pOg09wzky5-$i_Bgx~j@IM2;%%i0pUNm~BuoO#?C!CRG^0ZQos+-2BER+S zM1g@7l~teFdWRisip#qbut^u+5}POS>e0tC#XcCjH zy7-U?ptI?+>yT8mQ+K!IGQQGHTzU<<3JTvkN)V^ zH%|#O)-C^?Jd9D$aSzL_ffh9h#n*tp;g-V8c#W`ly~vC}9k9aciUVqTxbP&ea%}%T z#f1w`lwQ!-W)d}x6WO*`N~7qky->|3bs~OqMWf)Ga-|=g&*pTc>VWxy8jyTS`%z7< zIGisYv;FN^*V4#qqmj+Dvh$@v_DbsSIAl!}Cmg#|v`+-;HOy>r98Zm!SL`Rt70L1T z4>gV(Iindf-1;N)RL3^`J)YC|`m!Dsb+;5J?6Vp42?C+Pcj*UwQ`P>TM*nJu$E7uD ziO^VmnBRRO+jb9xI6^@tB&;=xh^Uru-UDNo0$6!6Lt$yQKaQ`bwnc)~;?bF)Rbp$sFmHqWrsX`x)gpdLkx_6Yk==Zn?p zm!|fNE1l-p>**Ptq@T#=`fQxS?njc{ksIiS)m39!nZ*psQL`TtSjKO z)5SLo_5$$zlgeRCw%`j2Y&=(ds>mEWID>r{&lkreDPVaV{{d~P>gV&Ak3IS^J>yJfOJv46-i;7rdiDu3jPJ<#xki=7W@=?mZhuBEohHv*?icUv2_4aE>HZ?j z&e5lui~Z^1$>O~%-sa+w@Q+Tm6bF+>i{tEZ>7$RO+2u0qdYMEv6I+6q(^l~*Hnq*K z#I|39OE3QhBJvb$a;E4C4_m8Qzijd#P@~k?;w3EEsVERfgYA4iO|X|zA+)NBETqpZ z?~HKR(I*RvCsU1RM}#9g6_F{9epGD=*Pcj+%_m?Q$dPX@zLP6Hjj&c-9Fs~xW&cl` z?Z)rXWhQ9F(p{RSY>oMMo1fME4l8eFIY)OcTsi_inSC%FwkIMUUm7@M^}Ax}pncb5 z{vq?X=Bq&(d74r;`tutgXtf9&hRJnygSJL5NLR+qzuf#2=3l3- zAb+I;nc|DHPmNx+-tQ9}6cFhZma9N5nwR`P3pSgJMieQPp*dSJn`)3Z)i8E}4ShzK z_khl2o|%0rQ#`0fy7{gdeH-rHK-j^Q+7ke<^={eS$`rAk23Pjc{ zrt6DW6<^g9&PIR$bf%3su#xC9u0weZ?%m#Nq|V}$#nOxwi?il!lDG9=JXFLT-2ZUX zIfkp6UTIz8U>F!SQqVs33e#!Bqteb8kgN?zwt*_LR&iDpXRYF_Do(ZPt5oaQu;fR@ zeO5-f4$`mUoJ|Oa4yot`l!TVx*#986$)=|OMi~WA#EK;#fO(}{xFiMlqB3(e7H!RO z0P$l)k1gRMN;OU^T(T_eyZr#ZZ4+U!$oCNL_L19|%bZux(_iQ`K9& z)VZnx=q5q%^;ohSWUJi`GV6Oq)&@B{*EQsDSzHB00MxjZ&|u;_!}AateBSuX& zoqX%^5iYIWf71iuq7$$?$okoesu~A2pW5)7u{#IZrRll&ggM_62OL4h66Lh?FtV!s z4aRRR*U6WaKb%eWbiB%T2%ebT{nFiM(VtgCKysR1e6aJmY8|O3-;OPPdh_EO{s1_d z-go%gd-{(3>R{j7pB#$)*^PiM?TdH59clj!k}=>?NqZDnBJGn6oo`on01wi-tt3X> zho5YnA7(mFG(H_hcdGfdoVO!bo6~Pn!08|dRHm}U*Hvq_cps9!g<)c|hO#)sc@7lk zc{;(#hu2T9_vcO3la+`cqXk#<6D*k0vVY`z#WNhzVX8Fh5ucxN4;Iu-#$P~uwsT*- zzF{U_$R(fb_=Z=VvN>qY#P00Qwhb{4)>3*h>yYL-(yiNtk)g%m6{UQ*x*sjC0+p*l zm3H3nCzM@d?aX%eHRO}eb&RkR_~YHmJnhV|p5b6bi0i0|4ygP2dgBt+0)4YkVV#x~ERU1NX6&wJ_YAGr#CYG;qJ zalDuEE_;vVJ?hJ29`X{MA*Od{NlZkOAIAqdpSDglsejCYazeq?F%IQPcp8kacAZ_0 zX5y=q@b2Gszw5V?ZCLsq+VCGKX9@y12mVWeYA?{{qGOfzpC{zD^(^7)jtdobcs@82 z1)2aoI&Mq(C6v4(smC*e6%2|(c?OAX}Bz?pJa$B{UQ@=g9Aqh zED@YKadxYGa!SoL z_r^9oO=+EPM`K-w@T7~!>`+1%Jn0NV`om7nm2EZY{CT^`)OYxj)?vV6i(N1|d`XrI zRULKv-ND2}dX# zCyyJjr&vCDaP%q9^+cY{h0BI&T$r!G7X9VDWCBj{yxjHZb(HXI^*p5pKzW7ecv*GMO8O}KN`iMG} zJ#QCN@NTfiLyC2u8jtk7vf@EI3p@6H2aU(D5-;$>&fc;2XZQ3G2J!FjxV-|0ypy4+ z4Qr=m!sU?#;mj?JZ|`j!_uL=pv3Z$!fB`jPDV=eBTsYD|$uiYlMJsB1 zsK~HErMkVJ+2{Lt?)p~!-}cYu#;=_z-P}{D*W-b5|E^{{Kh1znj%V*}Us_&!!1`J| z$)J`_URHYL!whLAob@q=l%76^{^d&yX{QaTa8+&hl*<0;GuQ1q*LdnJ7&An5Ei@)~ z(bL6ygp{#Kz}eTnToLN~x${|d9~{jmpVE>O6`74-ys(Fp+_avj?*pTB9=kBsbvK^o zVo8{N6oom{+&BDwzQ^y!f+WjsMSZq&tWdsi{(E&_+{5v5v%MmJEV!{-{uM(ymc9`T z-(2@OmfxE*5B0ry@%_Eeos1kD*6%&TeJ{*>qwj@_pHS#6i+g;y@0FP^_PuiPA;mp! zaR-O{o?P()%lBc{>8>h(m!-O^fUDKZg-OLirV+cUKsC=%&ApSKQ_ZpW?-0yeF~>fz zquYPVt>HIU+-Iro^VV*aRqt!qHMNErDvkH%AU%%-Lp8Xy-(>x#B28}I{y?l1+rq2@)y** zh`<6V7pSwqs%BZGuckn-5&{gR)rv0Ic0s`vw<3SPko~p`c2&YEXWIqcm9UE1cEJeY zG|*rNUA9%$_FICEYe6yjepgO?Ip~T>m4mL7RSHhrfK)+flRFk0K9_GB^oNqJb?|EW zFlybU{M!9&=IrGkCz@Jn@0Wp(+rARdPpc<3L98xUD8a?1z?>JkoxEQ$nQ5!ac#V2r z>SAAj5;@<&?UK-qLuLP)kwvI2(GN9e3ly6hm2a1P*Z{fB~>Dd{%$Pwf7js;CDr*&n! z&#XQzEcbl0?$GZDfH$o;XYA@&uqS1Z+FZ9&_jK9*W*$(!Ev*V^d9bd?RUoLZcFBEn zCJTozbe-19a?svfXO>j$D=nvBudTOoi93AmJ>7VG$yQQJo!uWVfA_-7C;4`_NI@y6 z*!@>aW7S*!laa%$czPur39G-}^3tNFa!d+OrJb!lb{&J^5ehFJpNvcvIA#TAcc`>= zyHxn{J?8H&7qZ;&)H=gc>vSL37xeUGL5$r~Wt?=6$5TpW1RtJxtncBA?@{QKr5ILw z4XpMwUmaHbE8sMjgJwOv;$_7-Y^S^{p6I)8#Ty`^A1eN2#=hV&=aZ2K&HwnTeSQ7M zYx^GVf4o+)uUhN@`{nuPhx>nQ(TD7lH~r^*uk_zz(T`;C9;!wE=aUT4`b#mjr$Qci zmFR~p$5^=}vM@em5h|ZjyE68@KzS;p+j1~s6LAFltP`pD-La{J_RZ@P#jA+|3P1x`9WKhyedo!o#j7Rm*y0C`=yl2S1Yz% zRN(vU&dyS8KAdTVk*l`HrS0-%(>xc^U5N->#7HHAa_^08FS#(~3X4igH5K!@ZyZ7e z;rT)xMkVaaG?YqHr`4T-SUpw+zm{=l8L!H6&~4gDF#hv#)smlHZ+Q&eP9rwqVpC=O zs=NamE>dh}`YNO4qCLk8A-%1xlt244MjmhB{7{S`tKwu#}YGWyVXoUa*=gNb}yT{$e&+AN$p0#4mDtw7F7 z{gZed`nN!ZW#mp!Eh%Gof@)3~%@Z6^5M1BgvA1kC%M4}xcjgPGW@e}saSJAcObbdK z#kl@vuIQo9w`@t6FxdNa@oo0+k7OF}Z3&nC#YuqrU`z5r!}pP4CcGbgJ|nVvVw;vz zY#L@S*0ls*bFs$|{%rRyETRBV)D;`~Y2R2c{FvVuuo7G9mThbj#=2=^irCH*;-*FTv~^5tTNmR+z4z==YLIL$f70tqaU(Wx?wCX8of3CD;A344Dv4X&K2R2{F zhQW1H;T3-77X>1-NmqHtw?_XB!Ki17vc-vP=KvCp?v7{o8T~;1Un%D&t9@emr!pB+ zK6`x1{Wi~E@cmNkQn~tiw$AIK`L(6ekEUhY>@2Q!B8%3W4hfPEbe#Epar|m8pb!z` zg#N#ckcK(pOwJ3v%+?quMrENF#>SMK@1oskHaO$>CHyW$mydDeC?<`45WQOKY--B+-M-KIFTo;el^meYl zG*c5G0or%NLwpYJWa_J##-}CwdX)7FD|K(K4MoyxPf0(cC9K8hU8V{g_$UID$Fkwd z$55?(RBTYPpQS1GM|yc2kRsW-UB2S+$wle$6iaXBbeWyXOlK{XXbZ}=w8iVwvIllN zQIQ+8FAL{HOdb_Pk08oI&Ezc&UUVDZ5MVZJ`hzcZ}Bk{Jc1kwF23xIoZ zq?7@BkhGI&@d@1mc|m`HCUTBZ#(x1g22`TrIf>VdAM6+_=cO-?Ndl=)CLf`uQi&cV zf;91?lc!Agaz~7=*|`P>fZ%W)V&DUZ2Mg<*+%e_kj$=1L?lsSTeC$tgr>=+M(An^H zCErh{pol+I(!t5w@w7n~@)K7}n(J34_Bw~m>ve;!g@UduIM|@;<}!5siGwas_GZ`9 z{pWzLf|7tO4U&MW_qH2@+KYPsx}z?0Q6P#2OaG>goR1uQ7x;Rd5|DW9n*v|Z9hhjz zOs~(if$wJmV=Eu6z?b;z_Xb}=-7^6x08Dz@6Y;3ECNs_HYo7sM*QCRl02PE(5Iuw+ z>Ox94QIwQXUS9uO>}DU#aK+QZmt;&qRIFk20N_=ZMk*KSMpOqDwxp-{1$@d-1AZ;T z7jP8-SD1z5W7K2>Uk6TyFG(RawD2t1;(jToPk}EJK|1)V^6-UTYaqyq&j$ps?k68G z7No>_%?HNzORRnW*m}SUeEU=2E4>T^_GykSKbZ}$iDeu2XFHF7IA-q-g{`Ng&(dY~ z8Qy;1VBcZnu!q?KChTP2iCYETxt{=ce+?ay4Ht3&)&t$Mz?#Wk%dj?x>Q#}UJgDt_ zSV&FSOju2HQ}F<(qe1(5tEN(yw;kvu06nzz1$vWO`ZWTmgF-KA-!c0Pj76~U)f?oLtj>0L2=gFv+$hYa1L}I z8q(1Ulxib9ge>@;DcL60&jM7zhJdQ){OO*=q^3<@CIc$;BV|mLwZ}Gn1SlfX$=ii< za>6;W%@_E1les$}7<U7oS`EFKIibAQ;439=gXZJ zovwBAB+d8q!BvPTz|Hp{&y8+(yUXV8a2@j8o>*=oT{_N9?H_-*bc*pGV#DO%g$vt5 zWl65od@Ve=3nPrJCUzT|5G^5ypu-t=Tl1+jHLJmT(ouMUirbb3)Dtxt{olCvw6$n-xI_V&brb zKPLD&U>u5*?LpTV4g^X;1OSf|9)t$;+VMeFBe2qER_dtE5M9BYMUCy?MNn~McdDjgyOIvY{d1b zG%~wJD&-?REs+Bd*)5QejU~NMX6G!ZIbD`-ya$`WpNMsx=SJ`2TKn@w@Z1A ze1WhO*LXw$QL7tM*=e|L;vO%DXNW~k2BNp^Fg4b}{%70xUG2uNm?X0Tak3Dz-)E?^5`+;J%NtMEjonF-!v|%6 zDB}aFtm1!0MTf=>qCh%TMmcWWB;(L4s1EN0IaQD*sHcS-^66F#NJ#K<^WTo$3;NiB zobwsna<%IJ|Eu~wLp`QRmkocTFMl2+$LJ^8-gHr;%YI*XN-F0?A2EIjeka=ouLIT7 zL))WVGxAK%K*x)d_yhi`@$~#08D+zH$H1SQv&LgnK6`p9b^~67e~QP$MbnFiJA0~Q zxt{Qv>73DYmhc}a9iNswx_(x8!-qQuVzJy&&OSR|950PTIZLrS{rh%=8$R6GAB$zr zk!Jjj%BPIFaFHuxpmU`Bt)+(x?fYE+Wc-v9t9aPFAisBVtsN<~amD+nsc6z3=MZQV#MN;Kq`DxIMxbfdU9 z0CNq@ZvY)u73W`3<~J(+BHK%kOzJDBKQyLufbpR(($(dAApy64`Sjw3fGle0{#bUx zH*h*M!;eldy2TAMIgu(ZtaJRa@PGME047eJAIWZfd5fBJccLJtJz8XzG} zw(UXgj-OPWx$vgns8To7g;qj82YrKD`2295^}D6`1|R7v?cA0k$|4WvBM-=8PbwQ3 zK(A+cOL)cc4C+k@Zv5*SQYO)>J%&j?KOf=D!fx#}k#lwD$UDUWXh5qr9nOE?8)e4L--7g@H!Ej9 zu6tm2GOJrRB?=nI;;~HFdLrFu*KwPR59Zje^X#}lGP;Xp_}9)Eh^=Gi&Os~pmex1g zcDk}@q<7!%)n%H-39fHUlX7FQx`xQ>>M}Jx5-rmkgX;q#udd4kK5|t{TBJ8s9m z!c+6Zm0yc;_i{}>^0eeD$!}TfV%y)(mYA2i8Jc-q>HVhKtjl)mkYaL^b5^hfp8<`9vFu-7QJfG;S@qq@X_Y5#rr&6tGi9A$Pe_Rw_$sch17 zMQt!nQpH^d0y|%1#vgbw*9j@RmgTThQ3W2uS^GS|`f^y)m*IsyE4zx4~0lGl)Of4Xuff%xshA860ThhC7d?;=PG=e z;?vxSpWR)0Z5ranGW_X1LR+8DNW47fA_0%Fd zJG#LY99tgBMS7Y!I%sC^oepvZX1@}jmO+^C>%=T%`KCqg!O|=4GR0wQqf~8iK{Kbh z=*=NOvT$9lvoDgbzTFK6dX<^v^^EQ<$3+m?dkMp)yCOqB*X_#=WD`==s2MJErg%wP;iy8_63zUtS}L6&vL# z3FTGf#AMd-$=K1!*g=M1HOqRga-)GeuO4@$E=p$6IyWRy>;_4U9U@qkYeL^~+SLSE zbiN9mR$iqC{3C46SyR{PAnYt8VdbT^4J0(KEpEZ>NhT{PFe zG4FEw#=PssV&n&4)&MVDES?y98JMw=OYjvEns*}YA6A$FCt?9C$UD^?wJt{0F5u=g zz{kx{>^7jy%BV6{)njdxt6VlMt@z^61oZ-pTn|&w?y$Hp^RBQ4P#ei=7hENFaML0H zo^Y^;8v-j6dt8#{K8QZ8nB6X>VKQcyi%})yae*}^&v+Y@rPxK*=AACdfz*K7wZp|U zDGw&Ch}q&|T3w6_tR4F&%E{;+ANMTA_4*590H?ZudS^Fru9Ph7n_u{5R z0z`QcSS{NID*d+_rbIW6hJ~PtXe6N=(K%MPxjP$Ma(@)mNK^;%n%gBMPDyp zAq8rpk09yF$VSA+^kLgU1YFAVAZeKx$PVie7b|6_V)t2W3cNQ~O{7RGzk^7*o*hKz zj)_Bx8(}x;_q&O9kBldX**y4cb!7wLYTstP_<7p?;pl=K!iQ|*cx4OG*|*z5V))M7 zLNX#5rtTkz%h`wjpY9)NBok-v9}jFebN^_GyrCNke(R7akFoA!<>jq~#<*7Nu;D|3 z@A3+ls_oee+=#xZOT2DOsx0ri3>X)t?%%r(9;e7`8&EpCTZH}Iv46yNj70#kf_Ln@ z2%a$d4pIyv+Jm{!BBfpA;v>m{o5#jR%r*^618`VQIa)!2A}m~(a9+DLxufx9|II%d z`$uw5;U3HU^tt$}UBCU=F)oR@&`sG5}&4R^+HRfd- zgYYsh`;y zg6lHL)rnQH?f;EVX13mLGlezRmSU;7@r`upWeCdW^5JJV5P6RK0k4CHMJmO`41 z!dx%j$7HlCBaR=6I4);CDeyxx96vO}N76p{p}6CR;yx07=y*%=(T>}}2a!c#;%{@s zN1$6<|>tR^Y>oSakR-HC!K(wA$E<64!)py^tfDp`|83YobQUM99#`d?e4 z36{Vt!~&o$zey$!#cufkgMKz$@8B9R3ed36W5Wsx6zHn1v>3t;%oiYVnI&S()1cq7 zkx+P5Mp{;Xh#Xe5|2mbFs%gep!8Go!0P|3ziZgPxLB42&VYZ?K_Z5mXlYB6?{g)`l zN(fYfBT6k(m3`pIrZnGM!A3J>kjzubTKoP-o|N;nsr%IOY7GU^w6bcwQmsX=8`I+? zfL>kWAtZ=~blZ+(cXDwjcIPtC4FV{zzu^?H5h%)A`GLO6V`M^CRzXcQ8d%U%Ak;`# zgR1sFX3r+3PWuO!MufH^2b9c({%}IZUA0c@Fxl$G;29gg!!cq!FH2V%jUk2~yw(}IjqZC+ks3jpZ4Dzun|_2u`Zf(*b^kuO%Hr}v zzhUC?SiUSS{|}#*xZLQqBO)oxBu!xS#H^o0Ppq-3fhUG>mZK*$^Y21We4O0xk)GHB z(g3FCh>#dzr{q>(M<7A%dF#XCN$v3*m)zPS|?BxpbW%_D-(leTjP(Q9MVT0)mIss(k>mp zZQH27V&EFat~kz$=nj{kNd82V?#v@^f2vblcG0#vD5N;GU(YTqB^EpV|im@HeJ z7%V*_(m{$C@G@lC3w2>s0LnMhP>O>FlJRp)s^!`aOLU1%&2L-bWZq*$5L6#lHTCf- z#G4o^Gx267vD;FKUOq*_DXNL6R*7Pq)N@xkTUN3d7q_Dvmm*HZ`Oi#1FoFuBnw!TO zPJyPtWy}>pMzQP4!zw{l69Y@r*QlQy7BR$w2!Z!WBGrz(NMha+x1mhT49|$fF)fuC z(3heJ^GGGclkbJ_JXY4wsyiZM!$&I-A4KBCCBcF9j78G)z`2lUcBbCF5&3 zXY~iH2*2w4PC}L3c4M%zYNYqCkh)kL{RY^CZE}gA}wYkO9tqRXJ@2W}?^b<*IDq-zS@$^~?n|GbI^R=;U z|4Vz*%EX*-!IazgQkGr-(gnYF>oA*@%&<2JH;!8x9J2?-!zr(>H=zWa zEj}i-l~E4JC8g|l^34}bzB$|Eo3p{0XQ*>^-|(<>h(RXv!<%lIBH#paCEz@ejqJ-L zpT1ec774Ev(SSjz$8Odzr)$D%CEZ$paPzU7e^oI-c&%uJ1%YC;6Qt`p2cn9pQz_DD z(+tKJ)C<&FB5&q8d*M|X(RUMbBByF+G^*17tY1{Ah!~RCb8IzYfy#a+QfEyrY+36i znxYuJ@XY#f2=PZdibI!c#b+bK zEc$I1YG*5!#%{(Xd(aHAEIFO*zO>UuVyr%@C91mP=PW8vCRvD4sjy(2l5&+Dl+Rs* z`ms;4uhR$`yUE7Vl}L<9{?@o}nRXYpB|9nkYj?>`@|Skn1F)y8pgk@{dwuj?MeKGF zw%RRXmy579ey{U8soycyDW5q(GvYv56DVT3$@>mfq*C z&*r+!yWvkeAOe5><-6g}2hPBs`_ICk`_ICk`_ICk51fuaOOyCBFZ`*u7RUb8i}HUK z%X+UZ0E~m4y#W5b@IMaxKSXiawv~w{$@x|y=ldaIxZ+Wf<;*s+JDY-^M3p~-AX7O9 z1!|5e9}(!CTgvxO+(dI;Fdhk z1G38lE8O`S0(pEz7#UYbu&By>EH?(o+t=TVp3DTfU zaVPI*(?xRM*bhH&mrl|D23Q4SM^WIrfnG+XPp}7}4L8tlJhcevoNa+GoiDl!y9VH? zAo`H!8ill?vnAJULa`n>o)qhY=o{e|h2`VX!;vf&>`I!n=P@_5JJ zQSBY7RcdjWEeO-3KEY|NW2*Pk<(4q<5GeGnPTpj}=&IU%KF7S$!Pp}#wpT>)u7PNR z&d|zxMd5~XjHVBxKQ9Sq+mLPm&3e-^KzoaM0I7gj?hqmFGb^Xh#B4{EWw`UzD0mo< z;UG6#d>ek@d~7YBC%NE|G7N^Zz6<+Og?GZAGYNYQZ*F_O5tu)H_;*vBF@@OD%rO4l} z{zBaoQc0Zt&}?YlYrByU)g2&W{q&L|ez&05UHmK>;r+GP=C82hsZEd_^De`kpz)(v zx6LH!b!_`L!A@c+qM45p&xlOtEtiW7U#(1Vm*|R1*x&m!lxWAC$2F^qauKc}a5#j8 zl76U{o^HOm+iOP?sGmrc~A4lnZA?#y_WBw zr9Ef)xE}gK|3Ql$&Or2DTd!*M_4-S%ZL&60?utIy|3YowEBz0u#%`{2LWhki-+wZ4 z`IqdIR!yN7^<9f}0x?@>G!e~LuLg2wC1ewH7<)-@y-S&mae+kdHQs*&{0B87%5?WToN`rO&=|vrq?w~IP^6(4pJ6YPSZt7W-fv*^rz{U zn72u>)6F~AzWa>-u6&xFvd^d_zDN{f<`VO^+Gplno=!d$+n(^BnK*KlePiA=_KkVh z+BaTa`34id1FmG08AN#gqIxzuHV@)P>sB%m2|=@MZ)d`*G;w<&#qGI;y(YbkqK(mu ziNO=BRe$q}BitU!g1h4q{9b%|DiRCX&$^>q%vm31q5ZOWYm?vUnrROJ?$2%46p-Jo z*){dtmh)ycMq-H*Z|$pY|7@|}Z)!H{Z%D!>>^_x#)5V13gByO`(QcOSA;vCqQGIgX zh7XxZF($5y&`YfSTqN3$R?@UAE^=KGPO3CUjE;(QiYBNJnrFQ~Q@SNbt z@8~cr1G_M(CUF1b8We<pt+Oo((n{9hpJ0n?fJkD^%u}V5G)_{UXhiRXnyQ=iu)$njFyIbD=dR6cHrjQi}|4AyeZbCgn)Vs1Kk3YH0nXI-WchEKlj~N~%@DxhcAxt5!*#np$hw ztCgLDNkDWw+lk&W64KWSXlKsjzk&ZI{!ONo%3jHmh;9C5@G80YP7)u<;IAL%A|ruVxkBBoO1?&mY!9 zm&gLZQGoOA&Q~LihqR5DO89vgy8J*rpC-f;q`<=IA`i=Ch7fVnXc|{9P2plMYrClh zKIbh7KX1t~mPlLYt^Wc|Y|6Iv=Tu-%B`*+U=e$Dn60@3b0#DbleBM9|_yP6Xgz2i6d*CzSwY`cDV?arhd&? z{EzmB3sG4bS)Wltx`ta}lL@GKj;#NvB?KgN_H&XzC?4(4Bwt+rK1<-%D&x`bqpPnD z5PNcdS}!NhkcDz}%JKTj(GcE)UW(?&FJ#$cgr5 zzgkyE@{Gfo%g11!hNJ}AfB9jIA^u`6d9<8`Q>m}@WoEp3`C*u;50hXOBxljHuk|bb zey%^V!uI8o2V&nrhWaQulgCrB?@+_DV2yp58Bbk)e+%l8-(jhtORbnk_1Q67mm{%n zmuBq0{74IGZOSnm`*!z?l8*jwfYE44_JRhoh{V3#lg7l5#IV0hyf}@IiJF-Z@5+0) z*Xt-a{;P_=p+38HP6D8Mh!~ClZVYZV>|!vPx8sHXRJ1 zf}O+K4~Xx>d@G2Zb!i3Mj~zX7r6wadQ;I7lRmnn(gLHHe>FA0kH;%Ule&uxTU*^IIdYmvbn6E%!ZK`q;G; zbUMOs30lI>19NYI>-U#&eM|ANDd?WFNY0BezYM|$zU^vEy7*-0k0XZg!%t(dhE0lr zN`UR>AvU_@ur?E{K75}3vzdVAIcz!ynxFR&O1HG}{|>+Rq?2zt{LUqy=Fi!T-Y-s12(f`C+PQ#~N4{xVRr-;$gx z2Eu2IVR0ED&e+{}i_K|Nv_Mj)*5&BxjtWHT;MzqC)S3i)NO9`MzVBGBUw9XN#gXrqGLmA)35m46<84Rl{C#qOpJTJ%~9`Pb{+p*|v9hAPR1VjO4fjfhUXs z{KJ40{E%@DCZ8%EjxD_xVN?(2(kC_dyCC}~nD7zxdp7dHN$vp2##c@@&wgI2g+{1v zT_3;+jDx&6Yj@88>XNf=nN@{p5e(BJoq8_x5$$=I$OiQmn{ zlDdb3TAaZh4Ecn34^F~gn*F$Z&!Y}}OEa`a2D9ej^VCjBEXN<}GS(;pT?r}&v3d)+ zFDtVjg+oywsz78AmK?>3XY-K}_TmG13>SxUN>YOxn$0DrDyaQm=KsqnNOz2(eX72% zZ|3i;!Xz3vqVM9xkoZSL3_gcg=?l{zWHVY9PfQbyw|ZJ&_lK8N;SxKu3NBz$f;bVM zX(ZyZ^GSXV&SE+wB)r0O?#ySB$8P#FI?9;|J!pIkixb5m-GdU(gyvsAhe;-*3O_Fz z5WWXA^t?b15LlKNhu;sg)r^9yO`l){!a3)cQ8>51bp{wL5E?L0JUqV~#opti!Z|gT ziM^*B^Ci~TIPW+zzHoJx%Kme9xs?rQvfS)sDZbn_lQCanc~b7Trj#o=&$)`O>_d}9 zw@w!1iv|JuCa06b8?xoL)~TWv!#kd%bYbneayd0Nvgj3vUw($HeT)w@HN-gcIo;gke3YBh2E2NI>qBv52sROXRRy5YH=3;`;Fn3u)Bw z(=BsYg0d?R{#UmcSk-r8WqOFQ09@hv1o`Da!Uwbpc!hl&x{_6Jyd|7fa`u-3E(b?7 z2$}P$`q*Iv860iPD~dcSU2a)5apQ`NVIq?(-6W0u=NpeW&~3l947xqsK|>&ZV#61; zNa4RIEI1!92TYM3Qu*A zn@!aG8EDiM`e6h>nXy~+B;T2I`wq3IS%yo~Zu_K$Iq9ZfJD+4c z@3MFq-rqz2GLdJ+==t$-V&di|#g^kNd#e>$@^!DRY>XxCKzo^j3C;@|#(v<>8_%zF z9VNs_`>*=uEn-U@FZV<3xtOmq=_l*Oy=0Nlf2}7~a|pdqvQQ5Ou(skW@&J$&0npOyLq3Q|kV_wk z1ciX(hK=K8`QVY$<%0+*1MI5<_J=ubOCFVc@GUPNbkS4fgG0)9;3vxmXCJKaz0o1g z0PPdW2RZdA%LlQ@e*VCs1{#iLVYslG^_=T39lNYFrc=wL&FJ3n>F}CU=`h9nri^5`ti)|=X+iBr17!1C8$K|}pEKTqt9oFi<8=gal4^5p z_&~?QP&(dD%7E~8Z7EYWj3P|~%Eua)#Lbwd>CR!+gqpmKq(N0?%9NOTvI;*md44hx zFPk+fSNk%0UOYp802JDs+;{VRJdk2&lWX+ffUHsTBC>CSAqz|ka+GLr5ZitWE&z}H z_)KGWGnP(t)woNM*PN2^BV?-l9a6)5??um?qkhh>IZi0{V2Fr*QpsTZZz71D{EmXk z>vwXzdOBXG;gV_En9;PG9}%oG9AnGa-(V_U`e(D};MY55E51m3Y384F@PaNvAZumi z8k^HoJhOd+9WPIsUP@8{XJ`LEb?*Wnb$#ahGh_&nMrUF}ceJ!dH+D%&TX5+qX|bCL z6P&>Tu|g$QT5NRJ?buofR})m zf}jRPn~2behyhW~`}2H%zqupsqaoYR_eOh?Fz+Y&W6lM18#zy}-B z6YxMGx8-}-RoDw;W_r%9XE$w+zApb_N@iDb5rY%Vb|bgrah8vPYX^4j9lhl-Dvf){{q# z_^rT2%QB$E{FAZ0zpL%%J-hJ%S_J5rv`2Bh#M>IVM-g}i0at66rYrY}`3r2mG#%W< zTL`?uv8y*Jfe6A-|A~@cxprMe%saV=lSqn+Q{vM9R={o69^KY`u6Q(Hj>{w zbrm=yUn`tl_{@PWIA-(P<|ZT!kW~w~w6Tr}(jt^^6**{!XF0CO-dHhb20rBq)7`rU zT8i=>9?rz=+(%$I&rX-Gv2(C7*eosBevi4SkuuhG=bbUmc~oa!wzZFlE6SF{)yFgjPxGX zXOLEOGX`n@K*W{bko&zrJF3M;5`i6fGJRxX!G57V_BlJ-=C<%%te=XG(0pPi)R4Zs zq#?E4n@>E*?65Z=ws;Ox0DXMIp2IxjWCjN__s_+EpIhKH_+-%yj}5qX`>vKb%DGE- zK>YA+(G)nGl>k^XEEo$C<&?%O*B#R{t7Cy}7}9gZF+Z`IZCV#`8#_X9>Iha)Ph2r&j~*mHP@X$qFnDihEB{WkcK4EV zmDILk0xWH+@5`g6;Cbs%-yK!2om_AgmUOtaetPK+o~0^tFKV9VeF?99JP9nT%Qqcw zdY1J)ZDZ%T#?Tb4-gCQL{LQb^I;~T_G3LYV zuisaDD@#XeFVz!k`+|=f=sF~ocB><)xGWaz_Q@*Y5vG<=N*80~l!$5YLM-(psA98+ zD!7z_qtD8Yek`{N2k7+s4P2_w-SB9&H;J5Yc3=*kN1I%ADDkey?%@tY2@}3q^O|gu zgF!ngchgkHngVJ>T4_$XpWM@b@df|?wzp#J2<@@|4ByW5=hO#dj>K`A%7&V4#rReS zK@Q)#kr~zeIUWDt+u{e4aGl{%0cQ-M0?c=#UEubpfI`omxx>Fq^yeifklUE9UsO~N z3{4-{I@Y$rR%i9H>Z_rHa^>UxMxNV|W`@i)Rzhv%oB0F$Qr zt`KYh5&qGMNvJldhw0Nx#*Bh*s=+yycMGclixr`OC^WV6v&9B(mnx@fc2h5lti24R zu$Ot+$=}4Q4<_4Ym8ItcT~Cl6Z#slh4iVc{7OnmB93$~SKj@FieT)->L4xxWlmN(f z0BilL&-CuM{SNp(+7|py?XD#g9J-H8*@=&xxuAR*8y)I_{jKxb4}CCjSZMJuHl3}% zM3UapK039HPSQiJukI_U*`YE?QdVb{EMUKtW4&q5V5V76rR_zFM8&8VgtVl$ramKf--m-?C#zc}N zTWX%QOPw#s9mGjYNA0%GJU#!uPvh8pG&m}C=sz`rQ0o`iVfmLu-D5ZzHxm z#`=JR`agU>{hZ!W((NZLsBI>@BQj<*^=ok>rU26R@t0tOx1~HYuZgjg+eEo_7;zsR zuRQHOlIdbqIzCu~znl^TA`)33Oo<1WQa)szfyGTY9eboq+iHYObT5PPHqp%2euque zOnE`;srJ#IA3ZXBAL$CbzmK}TeYEPG?V~#vj2XHs^5-S&+G@RPsCDyD0P2de(7J!5 zlOv*l5kTLdV>r-jogAd|K)eSmKuTHPC1r(6B=P&?uRUoy-?j!YZqR;8KzHz+gLvC> z>FeL*Z<%zvUt@LXy4$A$aanJnPyxQtW+h;q?SbX{{7&WOenc2{{V9fFMk0*U3h$27R?s(h?AT&8uNyQsfO~;v!Pn?l3jkQs zk0AiO?bRa%{0qY=$Dz`?3048uHp64u4-4tJ$Dc>!Am8) z5#)rx19s9C;jJC-0B@}u_&wmQR|4<$`N!Tgpz#2x=2_0oGBaM(>&OOFT9C|d1VK|y z_Md@%sc;|Jei7AwsDMWL2Dyzu+TxK=T0R~`aOaw(;0_*InbT~^7i^a;xu!2Rkf)jY zA2lPSW;RLweC{FuL@&9y=!8xkQU}jdlK{??lTNl`pz|E8X5Yc%M^5%PQ(fN`V_rMi z|JQukh#6?4|NTHp-=`J-xuZZS?iju8(pL(pk>At(pES!J9bm#UnhLf0zYnR^j&T1v zCabSSEuBR@N?J#fgL=b!e@2>B*MHFF)vw7)e_LwN)G-yil42_0b+J9%_uZg60)X_F zn5Qo0%**=`%ZoN3LjLsJvcA#Qh_dd&510>sMf5+9OF{on^O-T||B1leVqLZ9gR)8W zB-{S9tnI7S-_k`s6KTBkL5vl|usw-8{<&~lJ#bs_Zpo%YQY0Z?I6fL)AQ|Wn-Ty?6 z(}MI5h~`gEc)X^)fU85%ZLr2XINMw`$+|> zJt7}-SKPR?&hu#eUpb5X0Cx#WYL4S1rFG}XG(KepIe&Ey&m{-=XrfTjmMDgh>1}MP zSbC}v(|J04dbJi~+si%~E~}SGK1MQ{sj4CMVl=hacD8>mr``}IGh=~BUPMJrP+abm zj-`$^K#%vE*A+Xc=bYsZ>aY+Kue?38{84ofFIO>+)JeigphnOtM|G^G*NF0l^tGKv zmHULaN19Knc4NcMKyPT>zc4b6>15l~YIUCu|C$3@<2rmGD4yd-WI&5MJx7fr5y05+G_)iBvY?Kq74~O#OU+qF!0L_W(()~%YFaMb`^<^ht}7Z>$)R0nKW+1J`}nQ z0TP`dv>I*YlNe?0Y@c95u>LRLlp#Cvno#Rz)?6m)0*f-?^cOXb4>Jy%N$KHQ{}9?5 z6fms-0Yt%<>*4K|FpCp8i|^b8i63vn8MI^Q9EBcjIYAT8P@FF+`X`S_N0M|2jZLv5 z>6b5gZABmCm;|(s_dSo;Gaa;9Or|DPo5;jSbkygu{66cleI~LY$FohUX7}j8L$0lX zTtnry#k8Y5S{zow7KV!|j!LicQ&^F{*{L@ZsV&yd+w8A1PP@M}p?FgnjMeNjVx4t) zh^>{U*tIS~IRQLz8Q{3hrRS|i-dum%|J!e_{5VdzuYUHDBiaAU`|8d+9IalkPn@1! z_~8LZryG9279A-1DFgNKLz10ipyVTpVnTfcjF86%$<%>p>NSsE=eqWAOmNQ$Z-j;k z8fstrn)q8E6F{&3)|em$#okc+LTKGOw_1$D!Mw1P1`=@;yD)=KT(wQ8cEmd;HYfn) z2K0M8^%9V7pw@RLlw=goBuPAw1ok%6{$}|zK9qAf0{EwZa--mfL-|dj-m~#G9QO#e zV>K_dya_ZuOOFK^m;73g@uGwBHv!HsibySn^7r%^-R;A;==K5_=iLb`Yz@qgzo;{r z_uAm34fs+)FoByA$#b??GeYS&@DNUTo~j&9ILKGu_kMlkqyzu{U*+i_YMLm1VK4Yf zUB1U!*-ArGjzUQ-`6H{f|NBPwGei*{8n5zRPhNj`Lq_!ph*gj7U$WdETb)%hwGe+z z?Q(t1*svk9b0dOYR~nNx!_q*Xslz@#JM=;^K7;Y-`w0w8GBwpqkjrxVEv@c5J1@UY zoem@nnf+eQ%XKe`h8Mq}^D@RWBftOivSWWCN}D?5yaX&I#rhr3%MW^@i2pKPTQ||8 z<;xs60ZHt%GC|YopFnf=ueB{(Crf%@g6;WBV9a*xbI*HwfeRv{bBn=i0#v2&*2pmR zead<;*trVQ` zZ@%HdiE-DGNW}Amf;+oM`{VQ2>jKo@|3T=LzO9F#+=Jo~{!?F1y~fvth;aJ{xMtG# zetmTjXBFo`=%1eRrj2UDeNU8#PWOPPe~U-uBT_2o%rJ80Z^&=-{sC4>(Env3+)_+A z+CS(;&M%0xvG#q0KB?yZn+hwnnjX-aW7#9rIpwHjSH%_963oM*K^2`;-T#+z3=AYg zoEo3;n|+8Ij*541t0wkg^Fc3jJ(=_CQ^Y?E*B@}r<~!BROnZU;;4+R_=?^a9?|&El zLAi7RdOv++{lSYP^A?m@P6{;U^)QChq(69wu;~wAY|EuRfIZI9o18NCo(VdGX-;QQ zu6r;r2ssJ!BT^uIa-T87@i8L`6Rq~i#gvK_*Ex3@ zJwmQMHWYHyk@N}QfV?i!Crmee!ZdYms6N5fGF+=r?lS;GwF=XmR$+3XR>4;!n5@VK zHt`fX1sfbgfDX5oCyuC32ml^AeZpkZCs^lA7U{~3s8z^yx=5=q4eF;{T7|is(mAcd zD5f7gYk<*oPzs2bjmcyTDZ*j2C^jH~Rh+u9_b+f@ZRG&Y| zYZv?&h}a=|g%S7D_n?4Jv!8_Js@y($$J&K*r(G!8O!v}plV3Zn)H~lyts3&__EW_( zBkU*CibM9(Fzv$W_tTxTK%>RG1dhez8QqRwVR%w4PDig$9$NPwbn;z61qJZqt=AAO z!@a*LTrg=FrhlzR`$rf+3`pu$MB->g%tMm4RI&~rhA(D(g{uA3L8bFSMP@iOsAX_ zni$fN@je|TJ;M)Z&7Ef}C!=TJ1f#~>FqRD0FTBo9D3lB5^`H)KNxyK7aUsd-h>wd5 ziGjHcu{qVkK6BjYPgo3K*YhlD(s>oD|rll)2O zGr+jja-ML7w1(*wfP>+Bh2y)mr3&;4(@n21O_~~Cui)XWcdA_&32%LiA^rAv%h>~r zeE!|rKLWl2bl(Xym?^+1&OcG5AnV9*t->KAVUtdT-v1G}jrE1+oUcxZZ!qG?ICT)) zsdNWlwH@R(iMKnKEa`jWS3_y1{#~X{aQHiSG9P6=fzD8?%=_~oBDhE$`aC;u;gpjY z5G^!a!Z+FAPVBFvweRnqJ0cRE&;I4&8(x1o7rvoL!=Rh1eY^9ipMm#bgZp|0Z-Z}m zefS3N|3mR&n#P>Y!D#;5n8JddoSE;*bMPoW+VF{hzJpMb1+dfqu*XEWTCK*#PXFMR zOv;Crb|B5hc^*AC`oSCQvmPHI9favA;2>O9(?B#^V?dpkeG}P>_=nTzAD)C3`tP8B zh|y2+2*dRcPmRnYpnvG)oR)m?dUAlT3gjsn_TEtW;-q5vqMKhIV>u}!Y8=6ZuX)JJ z8S$TUT7APauL)QA@{`l+8(dAloz_7&!-flneQkpcC2~5QgVQ$T^th*#2hU_?`3G4=KEB+J@hLQ#CUwjxIdqzM8Z1UENoAe$&zJ1^dOf z%M?BkUhwgO(>HX}!`}fPoB|s-twRAFFFoXl+J`5f_r{&mKAc*v`rF`zoDSmq3=G6D zOb3C^%xcSi>Jq%rLy2qfAzd7Bbrnj7~!)Jps-6*&yDov*%f znsT_pjj{iqOwcP&Ri>ACPz-xdRq=JXN%kcMElA3@w2eksIn)2#4_ zdWav|Fh|xye3~>vtWJnJ4A}_-_Dzz6KQ&eQ5mrpZN3lND>Fs|mc6g`T6~)-&H)fF5=PrBWNFbpg8-#&8BqlTdaSuJrHXB zx@g^E5V{XQ9$D{D1T$8FtVEpF;RgVx0}iP-`hK&02ym>F?!or+Fug+$1#-u}I+N;$ z3wUv%wUA>3(jZDJZ`}2d3(M#bx6IndLq_wn14mBwUGt>JC4~D+#Wh@>55Fj#!;gD& z=`Tw2aBn^={lmzhBW|MaN~#|MH2vE>O5fK(7_EQOqytVn*T6y7JB z19}^NpDfunNB$DHLF+F6?qZp_dwIfL{K-r?>OU4DIn%N51RTm-heHl-{B-56UkI4^ZiN0$Tef>ama{?3Qu8=|_{Q-PAc z!lf44i_K^7nlJDD1l`%5cSXGI3s{w3!?iKzHk*r9c+;C&Zr7u~4<&=EloGD>cm35_ zT;V9MO5lF(wnuE{zL=Xi-IeABAb0*c0uB!4L5>OB>`h?1Ki=|E-7!w$rAHHp*EGGi za$q#VBfBKcrSt3U&BY1ha5KcUo_XVfm!&Ho=Y9`bdYt>v37%xT zyQA!}P^&C=x!79M(Q=&I`!bDC5B^ZEq+g0>AnsjdnDLA}2#uCop?Z0#(ADCF&Q3&^ zz`5)dPrc#|CBaa1^?P<;o8w#Dy>Iq-71NyPWxnn5ccM>U1B&CvUH(qsalB{gL;V-n z0yNVZ`Ca3#G&i)!SpJ9}eO)1Wo85bEEZ2?ua{LQ(J35p$LtniQQDTK$njV9_R3+qv zg*(#}2zK??665BZ<2|4lK+OYayG(B5a5FgfdPd2^8KH}M}*#Z2W z-mlBJm<-}jVs_h%veg+eBe;N_(2e&bTug-G?d>mcQ4&A82kmm-!|}FDahG_1r(GC| z^Hdc37ZHcZ-HA?V(w{`!Qvku{|6u5NIElxc=nRhf?=-I93-0_g`HAk=X%L^(Acowt z^Nl%WvZ-5H|Bqa-d;GBx`hWB0|2F-<2ZIH{vz!0+j}RsA0bE5 z!S(4Nk46Bu73EmI4&Wgkuho4^f2ytS^Ns4yd~VCm224C(8mY(Z%H9RA&S(!jI4b9&CjhkcZ7c>|Y5q6db#(8%>W9G?r7R23hyk!;yl-Zys_pu2Vv z7??mD*BMJA+jPop=VI`zUY&BY23Y59OFQ$IcK_Vn+TF&VyUlL~qb+~Zb_U2YP{+W-5S-1cm;y+_+|2htoqVt3kBmvE(<0Fa3?5bqjRf3ai021E4>Yh?y zUlMC8|0Qld&KLqC4fquv8Abr8weD~$-yE0A&x&%4+IiOmNco9DO2Kj9?$6Kvzvh2t z9uqGAGk>+fyto+{)hJF*X#NWK99j_Piso#K#uW|G4yaD+kY*A*c#%VzjQ13wfz(ik zG<6<4@qoaq3yfG~U1#+Xh#Oq-(>QBDK?5#+8fOhExQzLYBI)2vWLmByYzBp^Lbp^ycbv;?#-q!Cm?xIG|++X!*9xO)hej79Rf_A@!p2D0&-+`Lfa4@Z7(;=t)UInSaQYSFf-Ddwd7Q2|nUcRKh%53^+L^nLTZz@cdSL3j9yCUu8zpB=uNyu%l~(hdEN zOSs;p_d7b1<2O$)bE5@<4F4K&UXQ@BO)QmrW){kKhog#x)Hveh*+A}WYP!(}WAM*` zY}aHUa`8!kXRV$@+g$jUa!%iRn5)WN{rw$g6p(MvI9B-4w#DYQ<>bnbnd80mbX=y! zn<`gnyu5168CLnEzc1t5Qr8u>+K#1r3w;h~{fE`}NBR1!E>%i>Y^8E8;pZxPezU`; zN&Lr&RshvNhOj0RRi3B2x~)HJ@U6Y& zJThwdY8rNZQg@QporRHk>cI;g{iCz5bv#E8mR`Dju70=G;RAR4#(xWr${xI0l7Wwx znVa0CT%Q$9 zZn?q}ne53$lbrxf)6I)-Q8(Y5_=&q7z^XKp-ErkXLQXU!I7U< z<{Lv+CbaGz(OyNX1ER zkClgX;LOp1(xB7sl9SnES0eG6AXn<~^bI@EIyAfO5`Itim3e1QrCV`bBR;kW_0<=%;)tChMt7II)DFP_%;bDlVcU}4YWjiQYNq2Nig*m$74@+_m%tc z_LDs}u~WW&a3q@RKT|PL%Yr13-ChOXuoYsVJ^RY|9RdZM@V&7BtYa~jBKJSra+J)H zFESnRCVYyDdkB{gq!$OWuev($rus1)u1Hqsr{y!9J!~Ib1y*B(bR<5b- zSn@tLIx?_uIWjMMUO@Dj2!y%t23wXsIYNDv-SV+^5w}ru^Br#r{<3yZl?9pBaQSh5 zM4E|c4j$*3!@%m3w>&j;eI**boIdJI(@I|qYGkEb21`S0KEpRYLt!W%<0+8aroj02 z8@p@#mT+N|pR1!)!idSJP~3HqSadQEA6>PTe;J{=Qhsn*t}P&5cdHh7uam&g|NMO9Z*uHctKDmkUepgoOG2(?QCHIh|r|R zv?QE5EQLSsxOL9WyTEsZdB63l@-~}y*zm(6&AZFMy!#o3&b!MXp{~_XJnt^i{DZ6d zl|}RJ5}9|eLxR5f6t?oSy;n3>Y zW!NxWRSj1zmT8n+3+u6>4GD<%hV)+^<7I*lh>2+1<<)ousgy0+M44D}<8JyY#Koa? z2rRKxd0M4sBUPWWCy_c%GB=T{V(FQ@dQkmctC>=IM*%Mp`}KTVjAzc=`_75>{Ut5W zV@HbV%4j7_PRV@{A#7{9JUouf{?hdH^3_MV)ENXI4Ky#1p-$=;&tzPdo_>}OkCOq% z>lekx=yme=st)gw@(oo?Nqkkif$o9#Y2d0MQ)nY+o^+Zd`RxX=-qGmpAv%oUOL0L@xPs$UpM*`#$o|Ix@n4fS5luHt|0&(*zdG8aa^ z5=C+OdNp@T zrn8Cfo`CYwOMW%pg^^nZz?fgD?A4su6fyhJPgG{T(SNk^*V&Jb|CRDHdVWdfm(fO- zvJea~)7ieT@_nXGXL$KM{E{(Ao5f z(yl4l{gp?u^O&yvUtO{= zwL6xcZbIhSO(zpPA%Tp!7XQqjN(Mar8=LPU{+*{M2$2$NU_J2Xo$e5}JTBqv^{Ahk4$bX!rPe zxo}H5_%IlIRJ3M`-i%v!7Ggxv>3Ya1v`#M(>qV1F=Vo`Ou;t6(J6-0O*Z_ea$43gU zYESrAVPxErg3_l&(_ef8)hv%izeaE(0^TQ-ny?3^ua+OR&0uGe5bf8E5@WTHH~g-P zG=u}`31{aEvl%QS{n_e991gTlVF8<~Q_@KBCEW9}x*(d;C7Dw)95bC^Tur8$!| zSzDRB@-nZ^SQ+$EJBsM-OSe*ywD6%;`SRoy$f@IE=>*R?9&JchpM$GG4wcUKzql%R zG<{>B^Ij;Bf3ycZL|X;$yL`lBo`-(3{13S`7-Xo!p@<7$CqKl7Yk5>f$t)aGe<)5JO zxPT02jFf>9Po|}n=LVikC*L!U3LwIAji`=06D`@QmJS?oWU6=tb6RNi7ZDJOq$z$} z50{@G!axT<{?E%6ePVj*YfK`n=JW`%>LQ z_+yKfkqCMBlXps{uOXNF^U9qw?<$m!V8QUW3_!l79PQ%i)10M4qa5VE2dbUd9083b zfW{mqvI^sd5@>MVd%W@l3Zi!R^l(%Uc`sr+c@Zjt5rNOp(xc946fYC{Nib+V~@PP%#=))&l}GsCzWR_tyf{~j}pOFc7; zyUh#(V+=FH_y_u$H^a~sjO<`n2JEn`F-RwuEr3480wX63C;8?Bt7Wf}dIITeBJ~PV z+|k^;4<-T+JG^ph;~9xmXM)=cZ0KWyz-q-oD;ysIb2+ zgJYIls7N_KA)Ebe863MbtVqc)6lt=ICEbHHThdc`>j>3%1V-OuUp>V1#7(?d4CtI z^*ZDIU9k3q>zw`NRft&aBg^khUor?Y5K9NMI`?AutL4}W&_TrV&hhft(3)|C+0$8a zz$3pRxb2htV<7cb`=JtK0k4>NQx>n(p^d4PPrCXySO zBVVexL}Uq)AM&bt#mDcljPVC7SA1^?VxZ9KD@jeg)$*bzDNrW`!0F)RSb9zX?TKCz z(&(6MTaqYmh}x5Gc;i<(&-am2uNf3%iixY$4LdTGQ5x&1Ea{&$p9SRL)*b7X--Z_?z<1gnJN5UTx z0>^ca$__N1Me}E9*%H;ZY>BunTjBI*ov7vQQYyXRbXl=7~WR>zQwarMirkxF`-B_)S23=ykwr@-# z@Cqm&cgt7^--Y($e z$sL_u-=<4XJ8=A!!0~&4AEp`|^LgXMh{t5pqdajU`$nC~ysOOv6q}>-cm;*i?OQx{ z0$Tq56?4*8f2$YU4)4(u+=KJp=sFs3OTeoq@1x?DH=riZ6u_HKIkL-_zwwv__oW7h zymw+iqNDfSg2W^S6W`LC_Z}3E6K#+#KRcGbD!|U|X35+EV^$i+E6P`)Qx2t{XSyyy zuUwQR7T85G;E~>yuZr=Cuif`1-?<~?10?hLbdUNJ`G874-^#x{MIHb%m*-ODY3V2( z+N76-7rAPHxwE6`={)AZ`Ulqpj@waL5xfidD$+|23mx*7gAt;4b|Cm^_i{>0ckB+&|_o>TCsCnIIUU{pg%Ovfb;+1sS^O&CCX+QrE z?@1lUr7I`cDu@(4`iO$1BZu$*pFhWYymLG+d7kLb@pNS=#|y8nSN52V7^Az3wJ;*p z4svP3Nu8n`JcUDMdkJA^wk>4CD&?$vHru5`vuzw!8~ihSCf_J${>l}_HNiI*Wuady zt}4y~zdS6t6SaxQHl1UmYKYw?pM*_RdHn;Gp8lXm0F%pZAC0? z8@&+VkAf1OvXAtKfo-H>8?rLW|o;H#Q}Wa>?zN5pX*BZ{UTN45_yi!&PGEW|wf zAs&w8=lgiLB;V#5?bPEH^EDGQ3g+sl7Y~Nx`R087Z+r2ey6}vGxj9Ztst24Go>4F> z@&Q5o-#QW`JfmPfegra5lH(b|GYV#6j+gYf)f%2LA>V;t(E-%FRVwKSmV?RK=R>P6 zgpy8IK4G{Al4a#Q@J@%Pf6y$&o3?2uPFN>SCy6Ps24bZ$o&i_6WHk$1QCZ!$?MLWP zkB14zWFcbCr*z*JIUf1KmDT+nZ2r^NvF2^-_#bauNB!H@alzZx@zxLDZY*7P{xf<` z?9KekvV}AYovvghL8d|zG=FcRc5i5P3yt=FFV`|_d&-$?_Uq8pX$oyFsmk99RA_VT zraQDcL}&V6;OF$SJMnEt_0_knWB)(B-OQ*nr|-wcx24^=ZpqaN4qc|DQMZ5!)R*QdxGyPbf7~KL@#ccj|sF zKW$rYa4+xFW$R8`cJyi64W72_eYETNvytRKKLAf);{gwP6n^yf-r2R?IMUwa_)%UY z^!QQ75BmJ*9~9=5@B;S`iP6 zFFjvT!mSonDaI78w$bE578QwML_WwJfK3IQY31#9!d&TbrrcIA84D#&zyM&@}-9?g&8ir+^dMAoo+iq4+0N z?1UK8Zw+Bgf6hq(;l6DMW6BF%BQd7mhB3v&W1|?;Z#%~H8D@VojOk_R^0kGG>4j8m zjOhfm;26`sJYy6ghxhT%1oi>uZt^&tI+EE z#H8x^1qbhD?>%oLB!s^oB%kT>`PDfm;%HBb-!m1zdI_D#@vB4SR}!&sKj-b_S2Ylf zdHL1*39$zghA%ERe1Qn-#`6<}_@b}zec1t27N-|)9bPbuTm3k*h?t8M4_I?7>v*L^ zl;T0|+ljr-wr&@Sy$+)>7#X=B_R8_ICI_gZA+VicyjlAR!M^HyH14<18s4hRCmbkQ{|pH$iz0!Af$Z@Ou%L{VyLY zeKU`hdh=LmhofT)i`)pkP9cmd%x*F{;l)$P z(VUok6{~IA%(8Dfb#^B(FV4Q|)Y+Z*yfFL9&z(|OSJ-W2b{`!{X6Lc@rkKj3M(u;) zc=0R-9?8TQWUSQ=!d8@ZV-u0hZh!0I} zSAY+7|Ha`$0CF!Soe*!>{TJcIEEN@AhcJCG{M>>oxFCZld3}A`^lzFt_8Y5DrY{*A zO}!CHY_YeB+Yfx8eXsQ zu~DN68V3_rT{p|2U^;Z_$5se1)5(V3?b7?Rt3B zAK!fD^9m+3KcAiVqJX~3#+rJQNo{}gy!L~d(pY-J$C*+_`ux~v%>kx#vrVba%JSxd z#{T8=3rBpzTK8k{fBLc$*b$hg0Za}OsZ1i(jskM*oOEpT{$yP&*;w|Wu7ih^9_xJmV%fzV>DqHBdv({AwVj(UF6}1k?8u%#_Q9@KV*G49 zr0^?&?B1?NscUKJzLM;`uCBG&$GZ*@o?rS}2^E}(@w1<-4Oon+n!ysvKB@{%61z0= zXdruSS0_D!)OX4IyY^E4y3*&-MpdjG1;j{IwYHfL(^jn=1##iDk=gm~jvm%9ZK{bXzln{xCaQ8xY%Fe~%5P$$Yhq)r2^nyWX7G>1_&I80dqhSQ z)=1qe)#Fc_`P!BEN~=Toa3OP4pHw zvB+G2+v z+iCV6-$u7w8ssAm!_auC&X{{WoRSzIlw9pyz-D_{yVTAZjrotH96fkgzYg5P-6PNo8N zY;CibZK?try}&dDGG1W10=<3(|Lj#zM+MUHtQ`fxqG84qsPY0jUDoQ+45cI$SR}fp zcKga36*Ct%DK_M0FUve-+2{opD3I|2S1Zs zyub|#EFzFd&kE>TVf((Itz_U4ln_W&%~Z!3C47z1#hbRIFFOgs0^hCy;fyK5Sb0>n zki0F&P;AcAl%xiQs&*BjDx|Ulc20*YWEh#y{gHNVrH`V3nHH)4Y&HW_!Hxq|+u|0d zoyhL=EDhq#2G|=dS3Kj)40=o0sY}08U=;r!+9RoUnOpER$|*LHiE<&y#KlBZl;0M^ zQ%zK!O61p=RLi!8SkW2DakzUKT(P7uUYIC3u1@h+7shM*CdVzpt<<22-njrtl?Tt;6f^o0pQ20t)*SEk_h zx=$|@pKqVwg#b)3qbcml`gmnI&iTfD)NQEoIp30JalV<%$F>@e>y7-R;pb@Wlh~ zVbctr7g}>aAMJf(v}yY2JPStu@%KU%~Un0w6t`GjHmM$+i0jw;HTJ4Tx|(q{yN*4lj)A~m&LMCs z)Z>abL^cli#j98ivOR7cvSLU4VzXs_P0gc}DF*_~B>pPQA% z?=zIgkuK(0l!geD=UvA(xteKXy5@vVocOGcWRrZ(M9KkV5&Hgp_hKDN;&pHLK>+&*X;ne$5-bn%8df zYIZaEhGr6SY=52{qvnj4c|(t1yw{6)V8AQ>taXgy*LH33i(ic0KOK71pUER#Z}^kg zAP}Nvp?oHDkCoo8#`meTh{=VKtu~n(!#WLYGWihLZkTK_d%K>e*uv5iCDib2j31GZ z*Kv73nRRj~^TxS;ah)KFc|aloE#a^I;?T5yaqIK`E;k%V&ix`;HP6(!V55qkpjX#L z78psrh+}ABWYK!nMrWE9mUePc>gujtvCin#T^SWx#I@b)xQ1XEG3IZ&Fj5EgO{QMI z09m`e0-ZBH5y_BxZPzZF5_$kR?0O(7d~YCogNW)*Z^}r2x$DWN^W7BDyo+;E{e-{; zmL4pCB@Yp@VI3kg0{%$X>$Eky^hp8VqfbNo?-cD1I&1o9M5sYXPUGoVn_bei&BG{; z1>mj*cx4C+pzb^Dw#{baxu+Gg`|0dMjc?L0?83`#g-ai&r?Xk-?B1@!S^=TZ*^$E{ zViCezSNfP1035d8QWkP|C%eB<^}LonQu%!L$oTEt?s$^h9Y;k}yhUHXQu?HBWQY;z ztb9h>W9t;sU(}i%(aM$XosxaIa$ol4@v_dpkp*0F&W&P|R-6Nyq@p|Yt73x{jT`y7 zJflS2&f!mDfZb&$WOr!wpP~ox?uxEKQ`29<&z79uthp-`oTGF~WRpT>m&_oL@@1C2e6mGs^zu!$P{s>Q zvrw-Wnr{7&Lc^j*L9C`5MH|QKETYOt*@}3~LUmq9Lh8oUA}^G*en?zz(alzeNMw1W z(R_IMaBD*-WvkyJp7 zJO?bzkIV&c4u*9pkqhPEQ@;~038_7-+75ybmPQ`7Y(ODJdhEMb-@1MHa@P~!uCJ6@ zk=Ohpvz@f!VUj!o?IjOuQEaEM%}y&docJGmZsw`hc`Ry1xDP?Ugr7oXRf}zq)F8`u@Y>?Qi z6?;c%Go|tgV@R0c5;hhkl#=jamymG@*etP$%WsFds8B*{X|EDC5Q%NHaTiX0#PHae z?7Ud1-HT#u;xiB@bp**g3%$+|J|mvvGxi~dD$rZNXY@6*-309TmV~F+sIMdX%IFJb zqftuJii9aSAL@S+61PBJXBS-d078Jp2@`hpk7mCMmXboTu&jmcsh{vQea%Q}rm`iTxGI20R@Hd;xvPL1&jlj%IS7 zUUCu8`AS{r?k*>-t1IIs*YO;D4iv#B61zw8@ag;GW8Y8X-(rsg)>P}{b5N<%dwtjg zxlIB&vKm2?1Cil7*9sY&sv-|Q2gA#n)yzf;7BCLQkm{3+BLOMbf^{gucp0oN?xnV6 znOrx9L#pM~Q90Y-b1=NJ_gx24wedLtKc2L7Bk5fd9RTUN@#R7KRFfD0>4KZ`lxJ=V zxL=gNHY34-`OtlhZ39=&#ifg?EdMG=50ueeUb0L*`Al`WOb|##ne=3ZSJR?gCdh@N zOl$m1cU;uWXD%OC?F;g){E}64$3+_p^OP3n`HYt*Lmse&t16!b#4$mZWcOUuO9mjv zj2nIZ%VzA=Z#MZv9)FDX@dwT`a68!29?TwIFiW5&VMWK%7o0pJ^-BM@0DDh9{~>v7 z!VlkiS}6bbw%{-APCjMISK(Ao{`4e~@ccEQulT5+pR88IyTecKJWWDH+$^ZEe%nP^^3v7i96h!FgU${y?1Pf`o8tA6+`Myy2%;nU3N3NoWMwQfZ;R z1jJXkG~RLuoCuCOUNMKCu;Iiyea+mcIDW#09qaTpGov_u!iFd7^fj|rar&t&hC@~DEon}4G*Eto}Aw2GfuRq4fAHlsi5t4Kn}9gfy1sm?BiRTw>5 zUq@xoSmEd*CGk^{&`Yb)x^fam=Ri?FSzGwYrAC>*hHByAVFyjxvZ7Rido40j~0 zgHKAN|}LaVnM?K3znv?gk387FBwbOrLI5;=X=eTy^V zDLf4=jo@iRD=rj|eZ2Y@oGh2ivB_bigB>?N9!u@c-DKtkHajq7T&H?W0XDBh%(6>r55y zK9}w-kZ0ab-Kln_CV0E1Cb$-|0~6-IHsYWlax{_G42=|-ksKNsBCS9hy~rL%9?*N=!X?i}iGND4+D=#P3wpVXwkf zi9ht>xY0?RvqK4aarwQ$%Or?x`j7Ino+}nCr5?+|-*wSa=Ja6a*m_>~_UqaI5t{P) zpK5o0+H7>HfDQ$LSoQWtMpgl{AZi{@{ zhxToGXh;7bRR)U+o*vVA@3Js%-T!{6`QD+Az@a%<2aY{>e+o2_>QMn7-r?IYvo^bH z;2md!p>@yDFQ!27?q&ga|6BTD@O}p$spG&qH$J%OVep>w_d}aO6Dq{GU#(pe$Nl=# z1>oL7)%Yv~;=ak`=u&T>_V|j$Oip1O-q%-LJtVFK3yS!VxFGqa4v8B>TorL1q`PJS z^l}I2Yt-zjVmJpSR0Isn=Q>d3)&VQ$p!{MlN}E0Efc(Q=ly-g8!T29|QMnBfT75tu zev%&t$8Hf1`fy&bh%SA|I!<66@w#Al)`zWQ{~yzw59c^wk{(0zw}vyGK0(zRnE%T3 zD4yLyLkiY<8{D;#?%s}py`~%+%;1~TG%U!jsAv3PL3Tww!ww6wE9x0-Sdd*&&mhBs z?239ed{~fOQO{lv*Gbh<4~%aUZS(#9G=o1?v$^w@a=)+k&!l~`KN$kwUn_#|zXGld zzVR6MXME@HkPcvNGpOrg!6l!BCga{HmNQ!6gn|4YDo!SK3Bi5B;69`{8Pg>M{E5>5 z|M%Yk@Ym5Dw{N-}@OM6ET1YO7<<9#Aejo}&Bai?-D!}c*-+OKk@}3ULgc9&|mki*P zeCN$Lfj)rOr(HuZ0%Vw79~B8;D4{`UD1Es@35P+9pak9NHk6>t-l^l(w;W!`ukT=Z zD)<4HoX-EkbYs3w>wiH=qq_iUbTd_GzU#9{?6FFA` z#-!N6jVpl_UoK8G=EO_<)ymn$iN=xS5-0nZ#;aL;hL{jg_aTf_8poBv}KlkFzo=aFL;jqxXUcA|J2^A#_6|Md(@%^p*6yhCO6hL2`a{uI1 zkp=sQW&3+?*-pKG`u~HLgnv>wJk(fjcu4#Qy@KB{WORJ#3mW@+LkQSg93Fy)hS-}8 zw-eQr$ehBn6V*(}IKs0N)da}Y!P$wL&4PZMg=ky=svl{g6n`9NNM~uFT>uh zBa|47a=jJ|@`3h~C9yycwqe|T92=|Y@h$CwSlS_;l85G9OMkJspatKa@3TYdZ!CYu z^%q;Z<(l~!lsELK_%C^~yrQiE8QA__`Tkjb>zGlaHAS znXV5-(>I-*T|;GqkZcm*x4Z>D|2ICsju7(8!qR>q=~z|~z=ce3$s7(IW7ool(q|cU z9u1Q=X30fL6YCZ>)IQ6&xM(G5W0$^9X=3BThT>-#M;DDi)ZvX5G!r@Yp`;X$^M zq~p$W2iIy9L5PbZKW6UmbB^U(rtydx{7XK> z7A9-D-MR1xs26&XSEkGjNHn!a2pvaOfgC&yhQmz3Igs|^%IaXsP#j^c!gTnDfzAY& zMLc>C$)6q3+E;Jxq9V}@?cUjtfnsEXn0G5Ub-X1(Rn11~P}6RPGBd1iv(8J_Y;`Q+ zMQ-vxCE-@R@q*!6IsA2Pa~Fn(8HnC&ZQH_tAmVKc2e+~leEusezTYkcXxZSz?sk-x zabjV^le3H|iv~&OTIgSS>B7R{gs{XcL&#B{(7|(1>lI$QIJ9s=Xk(T!YteGjc}hgL z&H6u}~p`+uBrx(m;{D(1V!=6j)=@A>a&zWdu~bv@fld}sI!7~$N|C1`)v>$pbC zz!ATz@7~yz;g?7Cq9)LJ$y$>ULo9i4e!vt$Wn0J;wE20zn-_n`2-rvf({R@4_oSc#K zY2y~1xe3<=DXt^It+P1OaGifHxV06J8m{vX2M6lnR>O6o<8(qe5EtJXuJexx2ioFb z!*%{y5l*PH;$_2i{(+HE3L&-)KDjA7N0= z{a5-E=PxqXU$d6_j=~l#KjO3&lrB%bh>z*e7w5wgZg(3sCx~Z($|c zkdBtVeo_ZzJMRs|yLod9??)@}!x6jaVndmS)UKZ#y7Yf^IaNVWsN%I|lL$D6;@<$(6FuXcaSpGfs1 zbz|gpv_G7}>B~z)_iu@%9&OoD5{e&BMXOqVeIj*BHIs~v3Xy2DX87Z|8K$ot0gk$8 z7=Ln_-7V3H>Irk4ist&2n`dP|@Tk!%Oil})oDR8+SaO#fgN+oQD`V@NK2C+n6% z>ZjF56jjp>e`aoc@baS>keA5WO}lxQ@b%p=B+0-oWlzdmBz7AODWpHgVxh}^l}Jy3 zH2+Sud^E=BnWK|%zV~K?1&s!H@rV~(n~uddT#}x5e2{wsxHUYChvC&~B^@0NVImhL z@eN7(QpHe3qHxX4OGhH~YfHSNIzl3&d6C?uNv3{ST}Gs~dz=i5 z=@@`Ug6I8!c}_HK7d-5vooZJSgHTG|SKUMIe55seU-c+!!|QQ9w}I5vLCfiNv)+1h zNA-Y3dR?ry-n3Tt5!sNQf%^qNcPRuJE*a=yGCkevz*zOr^(LC0;k7I?mx5Sp`o3zJ zkN7i4UTgcO)w_!GTI2UsPb-eKd2l14ByIZBByO&))%J%|6M54&nX#v59AhG7wHzml zRvL$)VNGtc;#mC$RQfx%`*`e8D=UOG!G@;vxaHfd;>+|I;JKFXRBzB{dIBy^VcuG+ z*Q%0M+mA|JuT&~kd%O&f-K=645aPo6bmIVJULV+%#JZm1b)>}F+z#~m!lP&)B+`DM zx;B)2$9;H1>s2?KHj~$qy;xoHOYPEgu(`RBPBOL2{FZo`BtB3kjFJ5b%vco8p7K#L z%2BRJK5COidEJAFrEg*Do2sFI7Ep)D=lW(bUb7pCWroz)M*YFDnGqv=x6pF{hw9>uB=v*LurEwtBQ92{|d zOJ*E3<#xc;)r+W01=B%2Qjo6nw#K~b1uU5cl&(KJ-}cOejUqWRo7d2RoA z{f<@FX-f%f=!ymVqDo=Gs~3*od(8Zc6cpNLk2VG9SV6%<@~$@qG5xWx+( zC_Io)=kbG9gE%j}S7Adtt{=M;?#t)zQFvND+^z89e3+v$I8Fn2fR789E1`CUHxd5u z>=aj}V%0z3`%|;gx$?b{?~C-YA)2l~$?}Y%K%%K5zNNK#6QBAoE>kIz2JO4vAhwV1 z^o&6a$4Qv0M$(tyU8A*nqJ7V_?<)I_+INI+?|i1q8A3_^stH=^$C2h@$lwHGZeA&%TYdse9SJjf<%p zuy12n>WX|p=$n~_GuqX;Dssfu-eIXK zU)!O}kjYQqW3`g8-ODIoja}d}s%mwlxPZnP`ev(*GZy-p8*sxJJq_o#C)0H}Nw%uv z{=D7o24gu@nR2#bXn9VlrmxkeAodZ52%;^02vqcZ${S-$Miv$%YjmEJNb{;`5Z~ab z5hX=CSGM0sbBM0Tzzg-_WWJqgCbA`!Qm>UauGWz% zp>^{ZQoP9{Jlw9}HP! zPd6eQLmT}2xg3zecrlBo9t*852ZM(0--RomX>o67`6^#*JK7#JlBxYg>~tOz0kupO zOG-H5PB!D+Z41L5jUU&`ed-0i>7nWmMRpd zWCe8w#?sdhMpIqM)Q;Jyy$GM>0sqbx_Ll%qGr95>I#E3VEw>Y2PAC(I_9au#P02LW z;6tJh*<}XVWgWANy^S+oVFQ(80*_jIindU0KgI*cqqUivpNOSiiq&j0&2~dNxkJ~& znENhV@^3No#=t9yKxaekD=VhQQUkG?ZnnPd{3=?Hr=v(Ld+81utq>73Rx61m@=(bn zgVkoe00Eerde-8E?izL^ zKYfR#f8q|K{dmTxuaT3vaW-j!Ia<1i?JWDv@CUiI}Ztj|Tc zNISD#`rKTk5d$u=&LWvOErHGuENaxwuhJv~%N($)Gp3;73f0~a(E%iZVp(7KO2NVEjhW=as77Sv}o39-rc z8PjK(eO_zjUgzS8%JHUAz2w$rco3Ur?M<|D_8GTw_IbUP`%kZ2IpwT633p@DB=N1E zWaaELY31zm1}pb{=h4Te6C}xGE0ygn!({8`@Gu9*yr;8`^g`Y9u1=|h@bwz>s~mXo*|@?NY{sf@;LX@@6MC~ce_zR zflY+PF=*UQ2xtwTogJ)rZ|u_#8dbhzt|1IIMrS)4)y(38B}yG2ZskQv)j=^dRVN0o zkCCd=qA*o)I%gYF#}GTR=^g*Ziw2SY9%f1#av5cWVtm9SgdjvDyk>Qs%{B)M8E6G4b*~H=C^R=;s#^`*ri;?8o5$E3Qx}WrCVX+5?n2dsbvhe zm~*vMsxKMkGN#Ja@_@oq^R;YJc%BREa|yMK4XwT@S4$)bBKv`oB9`lHg*PfZEg#;X zu)}d$*Scl>Q>Y-8Is|RNwf@N~`Lxy#(+``&gF{Y2XI>x`keX*j%ca(4QoNZ)e2{S#^>f z-|B5MXU94NmFY=#@K^mRA zsdpnV_r;6&_y%XR_@po|jmqFG(2+=CMqT-LU<^^hkijB>J<>OsUS3iKb!}%OWV;xf zXzCde%22^MI@y!l373sX4fnZ((G=w*6;jJn%0hPO87HUg8Hpt3EvTcRJH=9b`a4GV zthm_-VQjk{A>2Y;4XFc02uEuQl=z5SOaYJi_n6LA%feI3I$L;cxokKgyjk|#FV#2; z&-B%g85yk7kijZFJJ1ao9IbtJC1mhm%`Tq`zMZBM>AE}>d2j6My97Nfvj6k9?Y9bl}8NucDPG zRZy%O;`*}wKSw2vmUj0mGWqFe2{<~Vc%ITLqTcHb<|DI}<+D29eSDL^%g6WoWojze zVjp?=@~x0v4jPFLOBWtR|0%PRe$0qgwr$f_=bcJ z`3aTj89u(T5@-4ek(SLqzF`lA)*V4cqt4dl5YBVNFymS%SkSjXH)|+NjAu?u9LVM- zFHYyAY%;h;(fS21PV|}VFx~>u%<|%N>dGwVEzpd7B&$BrRWiqU3nU|-$BK(6F4F&A zg2VNrSK?;O>FIctc-70|9HMOts(qcCv~(i-W2tQzMj->}B!GDoC@G_?c;M8-m_uJ7 zz`>=x6U6%iq5B^Ly_B*0(&Z)%u$Kt7#skNMr+ANGAkh}*Jpuq>!d=`Xp$)MiFz9l0 zt_KR_vD$2c_?XZYpNZ6NpDO`n+QVS~PS#OVj?PF=fB&DZ*BS(WBF{9!5bc~& z7_2h)5=2DP<$uhj2g`DMYKNt!T6nSOZ{kglx>&sC_|YXxuC2%UTD)nuC4HNBFf8d? zGJs9Q0;m>qDYo5$q>Nee@!}NQZ%P@vbiAc_&8KI%-lbYPz)W2MZa~H0`&Lf}Nr1C8 z)Og!ZZ5hy{o494dH;K?vXu7mMIs!Cp>DYkuxanYvWEqM9o;8szePfZb-=O(?Z5;WV zV26lKqsX8nV$3u1>>Ho5udX&(#tQFIK{FhnzBsz88g#U-&D4w zuOH!ZGYVPse@-pZQ1cegs30u#3UQh&sb0^SW#MT05|our%o3~K2&5G2lSa#4R5+*U zy~Il78nyHXtDBj61aCH(YF9!x@z`AOQUfj_-h^sJDeILxr|n`>*sftqNbq1lZ7<_` zIhN@d(C(3HW+Jsmoa!>w;Ykg#??jNq4fp5pDfMflgAW?-FLbeyV(dLh7Pdfb@du7; zdG(^%sn?x-sGfAJ(rfe+d2caNl1$+w75-Sy`AX~}8G@dZT%@+wt0T>-g1;JFl8tX; zA#sDN4;(jI)or9x7)JIt3*R*(Q3%H_5W5gRowbzM_x2pi0tEIQA(kw3pBN z6N>D$nQ*cD)zdP+JjA#%(5T;^4GO<(1>8(im(mn{#G>8zpnbb3<*rk*dVOg1r#YEa zJ&Qe$ouSQw^j8}wj%0Boe~H?VI!5kDXkF82Dek9V5MvufVjltFsKi`${D)(5n7F(; z&iA2b0^XW-mUyyyt_n4r0loKD z>;mNp97+V9Ox6zg8RFKCtN)C*s7fHgTMJv}q=SE4P}irE)eD^dWFVT#<(nONrnow8 zr_;&^)m?^iwV*n>HreZ1QFV_LS2wM=y4HbEYXISgINtSOF=`yhRf-bl74$aIvhGmp zUKU34GgmnmbVip!@C@?g>|pqh6b);?ENtBOWTM6@3i-pbFmG^#Kdz82$)A_)u6#4w zJ^r=qF5U^=HGW@quyT8LaQx=Z_UkKm@iV?BTUXhh&EV0cyK<16nQTwxtJ$9MdnoY| zC3a>XsC*F_O;z@R@%p<6pYphm86ZQ3XQ~I~lrS^iKIT$6-sZ=5C|eaj3+NdRL-?s% ztR5|H0fF(RJ#X7GqzQ^zrqIMsbCibe_+$8R(2_e|%aG7xrwkJFkKEdx(7K;Nki^hW zL{jZFJB*0Aj~EIG<;K8e8U8k;uQ-{$70JvG4d*5~%Y~iOSCyo{9K=>Dmj2QhET7)u z-!oi^<L8&E907~7?zZcxeeO#%E(R6%yNWXh5V zyjQ;@gtV%&hwgcgqI`j8Q9x*;9pZ5kuu(*e71{yE)dju4xe5qRayNXs1t8rmV8R>F zrUi6KM@F?)0Y>#mfD~XT8`76aXk0fTiqc`-UpvmBKA65tGGkyn zw$F+BJl8%aSq}F-*}l)V_^I|i&c3H9`$hIS-I~z@9>mp`VQ{Fb>s&L81G%0xW1n$r z#y*q(hrPFfkFvb-{xit{!KTinjT$Z0*iLtomd6d2wq#|u8JK|?oq>oTi3Lj=Y~9wj zlqM@GZ<++TJH3&v*3w}C22%-pTXAEc%gz*0V{;qrO z$s`1@UEAmX|9_s(2Qv44UDtiRp6i@*opY{poMy~(jnj;Ip6)bbo@-4r<`LICeF$}s za;O=dW~%2nZJ6h|rVaCWt7*eL-sZGnp65AjnCAwk4fDLfv|%0>rX$qfZ*K|rAzmU6 znaZkkE3(ruThgDAoF(JWUiO%gM3flECPFBdC?4$S8^-CP=QwsLoVI-RV8@4(p5@+^ zw8Y*1Ry>j@YJ+a-b~pCq_#4o7$7v5I(Vj9Q%yOGJVoxa(#-*t7gb0?@mGP!yXqR;u z;(GX&%=K{m6QIny*eLTZHZlzKJ7|$Ze%%*Enm{bST{&LFSLpN2^vSz|-f%noS`T-= zIlgd>xIYa?G>bn!D6N3B8wRDx0nH}|rOEfqM@S>wUVoL`HNAw1QG&TZO0RFv*zxK~ z`bpOgmdp2B^lr++EUKGa>V|Q|(ZXvT#{LxllGl&GAB|%!8pnM~)?&12->og{Lo!|N zy8?1tgZU>&?8#8#+5ney3ZUePJF#Ziian>|qC5nyd#(cVzA&(MLdFMZq<(W`)sg2y zRxGf_SU4eVrFg$>=r6ET)hTonCj3)hR5U7mzgp&@I)1OX_Pr)JtGR^&l5X02B5s|C ztb$?Z@Q|_AoP3nx&(niK?&Sb;wJb~?uhZeB`8h;b7W+(T7?h9kUpXr2e3YQ*W$3h6!4|I494W2-&bO z{&u8(dSJS)%tSm#xE~j_$N!GZm}g^*{j{6l)ZgaT;TG=fd^2i~{bj^z*Z#eZqt*gZ=Rk^eGPgt^)1ko zsc&J-vqvwuPFnA4(H#}Uik=Dfteu5%-=j}sFv@z?`0RcC>j^kF1g6LAN0tJFF_y48 zX59u)Z7(b?)QYme8;vP4w#qSk@@@n$F>n+YHHdVIlV@<9?1}kiT-+Z0iI{y;nO-V+ zxg=iI5wov1@961A`(oDUK+yNSWTfht45~|mzVCK~NA5yduBS-VDc^TP0bl5XSpi@8 z%3T7q8VPWldXRw~9}W}UL2_J{I^P399@R%N&*|{U?JIlLtBD9+`Bo+ZPUIRm{c7KX zUBQtD!dLztAHp=-hG~HFNna)z5ck8-%U*!HI}f&v+z@Pj#@GDoVL0sZ9E(+LiX}<{ zCihu#6>;}}k_7@s(M8{d#1RiGN)<|_1;qujl$a=_?ytc#h`VN$vew}8@1jd5ndDkb zf^I3Tt}!BI_vRPH=gh%BVQKX|lb)|?urOjy4n>-73(Q#>#np@ll*9k&{EzUz+H84c z_brXUr>1-@2gRp?QsUMrgb-dPqzXjpTk$>^cGA~qWnf)(4KHJW)6_FUc(Hyx>pI7U z3Ukly5t%)LZQ)dYA*mac$%m0_aMsFrAFFFz>qvF68nYjG&uyT%0#3>iPv2B~)V;EK zv_~~j#gmBHD|&Sc(9_FAm4n-c1+BM?EsnjI(8aId5R9?K?ZsswD+OLwk^B(gSgnk4 z5)1K5kmWFXhqrhv!+($+y2mSg8&R1`+NrS@V0yETr0H>M`!a9-dV#B0*Z4z8*C*HwuNGpvw;uIG z5Fb8u>K%Qw7qFc}{z6Sg)Sf?apWz+BajZ}DQ@39&w-9V&=v0`(2Y;V>}go& zvOE$!fr-8X^ST^YltI*ihF_NWmfH&UAG zP9T=3U5kPqU($#jRJM{L7qCOGJb(acdww;?@hcAy{pxR`RfpaB%1r6%3y55JOy?RY3DKzqhD>s6r$cJ$ zL`^HTdk7L)T(0zgEK^3A@WvRuFXn_33zUgt@7kR>LsdQMJK zm}XWy%zm+sDa&gex1I{+{1|*q!Lm{q=d8B?@{~JzEp6!3T6rw9Mr>_Au62bRyfR5`2I|VB|}t*Pn$s%P9LJe z2DifDrotyPmO`aNR0x_HRAKE96)tcq9APTF%QZb3qQdcKREWQxOcPq)h0>Y1P?{cN zjFOr~MX@?nglHksi^1y`qa5BEv2XATSK;>qZlk(zo3Ht+vPyy$Vv_VZ9+Q3kYlIpc zWI~MgD#&BQwZM8cttq(j#XOg~AogIe>aE42)ememO0EW9WZ|5wlZ%U$g_VZ)Z!=w7 zriv@EHko33OVjJ=u8@_%LE{WsQqWzjjZ85=C-@!+3GC!9PeoqysSy%_=1Y|h~s zWHr%iu$A`XWRF2t?(AT8tFHptcbU`qCx+a2u8f$3xo(1yPvtv8hKYqnV`CQXUVD-( zU?!Hk1&sxse`3UarzTV2RyVTWtY=VjXlq>>ToX^K0N?ajpM|P=8YVhi zkY+rWK{`tE2GU`BuW;d0^_3gSQIeOIOUN;Y(@mHXX$R!n-cPvY zz!qk6mT>-5-UP1)8wLl)K^7*jkIzkBVQ!v-D>QzSSCpIQpb8VsLUsCc^WZZ;2LI}Q zSCX6OAd2N~Ua6-q2T!ojz7@B*d80h1`q9LVnN+~880YOm6UQ@2m?z2B!5NW*B}_~W z`o)CFHX)k@l1RE=<&d3adRFR zD6-Xxqu94ayC&e)2Z+womHGO^T<(u}PWc|~aPDM#FrgTIkJWu-yAe*w!BW-spl@mi zXkeAK4m@^}r%kd|Sy6-@%qvlO9#UJlkJCt4A3|;+`@;Gy6Ya5|jF7Ti1%zlO+1|gW zUx#oW`M61=qDQzQesr=us*2~`cy64G#S3UBpA&Y`Nfna#_=G`I_3I@oHuxxI3{>p# zMu|*;Uci)}KG~aC-T>C8dIiZFNvx2zC&Vlig%A#xNPoPY>{4@IhkdXd^t%edYKaL9zpZmRLaE(8J1%hyQyT%dEst4q5vW)vHAAhT#BG7y$hYy8(h%s@UdL0V zX9GvTIXEtO5YL%@y9LcU%-|0?3v3G=?Uo{_DQo@buN>eCVCahx89o5%5bbP8ANv4CFtCxpy* z&U3^(=bQAHdG?s+8uOfIo~N6-%_9d$>Nby(_J*9ob4=Yus@ojjLIvj8oV-H$=Gh#Y zLLT#M&O4zz^W0$SHjfKT-R3bP`K3H$BUH8D_l?%SZhiz2IY}~eB&j+lNj7pM&B-PO?b|`;Z=$-SS%IZfz&m8+9Er2- zWhxKlBxNd}n@yr}=mC{qI-v5xftCB6COIBwD<9`1Au-4%30CR(t=S|hhoDgTe?c1i zdtNkTHDxNt-*Pr7Q~7P#Br1m@QTe9_RPGNy;>^ zAe$7fI<`30U-2kU2QgFX54uOz|Lv^Pcz%k#4{{CJd7p*14Gzq!oMb0$p?O&rX#(MT zqfPsliUnED+e2O$r%k^5zRGEZq?Zi|b4=Q>yF(zCS2aEGGNzG$m8AA%cF{AU3~ZSP zIKA`9sEA#(!oKTh1p2UiqFwY!RE#1=_E?a>QPBNEk@{og9fGJhlxG!hz2u3o@Yn}d zUnTTyUw_&qJX9@pi|WMpacreR31j~Rfxx$cQd2x{r}b6Z@ylst_n5wIybLmVBT=@urPM| zR0@-tn_YEa@q698QF7*DCePU!Ws0%Drs(!mEd=QAdkEu?NKPbrTN1W@brr2^moMSH8}y#ss1$J+$m4TfmK!K zeb5wx0Bmz!jfCe>V4lT3Wbz8BoIuWa0?5j@AGC}?Q@EVk`x8xR2taPdApdN|u--R1 z6`wFgixOi4L)Sa{%zDB7x%Gngv-QHapJMgturq0XI$St(#g%7P4BpSJ7@VK27_MI^ znToY{{NWNR_77e0*fT2z=jT=ozRy-XqOpcbwe|erQr5pTQ|YSguCO+k%yEOKx0d8$ z?=s?7>)QqW=PDuMl(6n9ZZejqFL=v2vW1Otj`;|yF^)_L24ueVVj=NK7i?Me2FY~& z;X-MZR6DI8qieX?q#{o7qOi=22Z>w5BnHrH^86qxcQYeFYZy(Avu8>MbNW*^UaK?VfX(q>hXrD%LTVct;GmZhJ2S?5Tf}U1Dse z!ed&A5UiGvG0Md@fmHSklWRO0#E~-Xy}!S`ez&`Bv-^Tgt%dm0jd`{Z{(8yp?U{Xs zpksMp0y9uf;Tkq!zhc5JeVcCUe%*civVnru(NG2MGuq5{S*FArPdnX^&Dl0}EHirX zDMPgH1c{<}D*{bmyzR@gN9ncz0H^iV!dwg$KZZ;H}1>v3BQPMrMj>02Du0j zSJ)Am20}!+b>*hWFmT#{6qyBbvdbwWqrj^Mq{t+26e)y|aI3=Av3#U?HmRv)!ebgK z1HFrZ=Ij8+AE&h`l(;z%PgG-d3B+CjY)`el%YpBF7rwXH6Xd*<%dW=YL2xZBnYa;S zziq&_D@5uhMh`07d(yucb_jIr?60uZ5v$)4N(2Mzf@TlNgFiN&PE)A73fM&z?O0;V zZy>%XJ9MiyR=>k^ry%Zmjuu+Y&Orh$(2gV~6<%wPU75Dub?D6T;LtLR1tT6W*G)3* z;X#xTjmNKWFVJB@_x_2t_fnZGvL`Xkt}(+8RdCrCYrM!RL{+<3GOqXT!SZ=M8$}Ta zQWX2H$uXM)&Xe&uveUtv#Cc0Wd}j&P#rA&d(eE>l)w(J1Ty zt<)Ft#^6)?_0%^mI&`}BCcGzjqE)*ZH^V{}IxJ+y9&{-DmTAA~4bOt={-z`KF9J8` zZ0riBF^tDrcHZ%f+UXdo{-^_4EnB7lRZJZ72rT`nb9cc=<}T~6Soj?T3hrdRIiYTm z6IuC-&w!e78!w_041{qV&+-Thv!vmQO{|Zsq*j@$#;uV=u#gUNJndhn?+pYm=Ht)X z-V5xSob8!u&)J_DSa!CkHmgH|^0ig}RaIMZCw_{n{)h+hXkNAJsd8bx_eBk_}nai z(&rDLBO{PYKK!mzeUVX`DZybOdK;h1A&viwS%H%pG(;0=$!A++${c6F@a-B!T>9z| z=>??M4Uw+MOTi)16@96~vq6+|U=vk+i;6vpF^dpIOJw*iLqpf6_aOC!ltv(>oI5@? zP-P?hARPMU4C@&%y_LE_v=vI{f*SX@*|;txvKxlw!3S4x4Y%7Nhhc6!2t@_qk#Nim zUn%FzGKngS*Y5@hg~K4YqDr2ZT%y>rKbE+*(4GO2eun%={p+Yb%IH4;a`&eoa}iG( z^vq&zHCMfL$vesy(CTkOveS>{iCky+?LU$p1ry$fs!BXm@T+ctAQlJ2|$pp zxips`ZOS1?roEx)MGifR45mltZfs+zY0M!=PaA@i@oVPZe=zHlae{5sXr+kcB=Ew= z2_WCxE~0O}1;5(-OCAklA4<%3xqp@-s5$2Z0O`y4(4 z!qm(Q4xNs9-U?M6U-$=AW7jwwcZSpMjd{ifGR!w3)xsq=G0UY6*$?WH6m;Y_g^b3u z6SPiOV3EGQ>O~fR1&#Qo;J*6}>$zaXi>Y1jJA@$Au^*G!pFXou{y{vaiyyM{ZynaH60-#3;JM~;pkJT`_%DQ1 za>vp9a~`;BV%LGGzhE?-+&jU|1vV1ai6G1*3lMavynS2XC>$QX;vd}=Y}*T6-x>F8 zZ^8Uv{5|#!?24_LY%?GhtFO38UGk}0_iw2t7Z2x-h^w8;}zEtSp_ zGO|g|6H=|)oF~LkcRNqRea&UqRi}RLW0e3NV+UNwLz&rsa*ombH%v9glGbMDkPbqG zAKwtFI_7I0FS>F={!m%PbkM$`V5qE(Cd)fiRst$$-!N>btXt`4Q^#;K-~E#f)ccwn zhXD-xeJeU~{A-UgXY5J#cr$s@%$9PqFjCKIkJTX!sgC0)oSmg? zZee@(Gtqg$s!rd1_cAti!nmBRr%rb>u5)#I{Lr0NH(!qf{Fvla!jGHZd3b;*mFb-)k?g7)^-VZ{Pya>#l~H`i&??4KhVPeohK zSpEsL^|_Uq>^6FkS}H^~V2h-8VXk;fr-`2Rke8k#wKAZQVb6^c`FG<){GO)_{Ktto5(^KZ~}#vseP>o@IoTWN6{WriZ1Xdot;^5 zoRolq=NTS;v{=X;z^lW;Z!$cj{nacB>5Co^2N_O$Mi!O}a#+Z=Q{v^{GOn!;$2?sv zV&{Km*T6e&ionm?V~s!%nL2D593+LG9>hY9by-L#j$t87U?HU+*zz4-!`8EyH}4)% zVQlE0X71=giNivoCWFx8v*|(Z2Iy=6KLIPb1r-4yFfapSkk}M;aHuQc8X(b;ecyOZ z{9yvOMh^2BgCQTy*A7pF*pZuY`mEH~yo!=xJnO->bJ;E=r*Vg^H;5kxH@VPoiHbz` zPtFtE;6X;5%`=5CYKG(|6{>;r z`%vs(RA{(nNPhA{QJ$J1`N^#&Ye;^wRs3Y@a7}(@=4FiDydLsArd2%rzcJA+0~2g} znH!Q2cr%eOG4cABneK8t5E%9>##D=IQPhtgYK6jMUN&4z$lA;ObFPEOt*+0lj9a}J z=$Sb_s&#jzt($*X9 z;c_%_qf#4TuhQ?zJwIxkQ(4zw+bc*ySTcb<2AuJF7|)kko1bWHq#}mV%vr5iB4Cye zi+TAX!^N6|FJl=OiY!*Dy6+%{@?C|Q(Q!pPRCRF4E|y32m72B6U8q#dGkWNSS}{`` zDA0sEVdtzoS|j7^%r;Ne(vFcEF~7=>;WJM=GWAI^2JEWzPjdKASXplX^@|y@`wfT9 zm8qE=1!$}^eJ{l`^o@B61L%K#c=+3Qp98*!0&R{?APilL9st}cJ_yo37cjg8LzO;d zCImj4@!iI5`16nNwPWXi_djNQ6Y2W7#`n|L0gKCX#&i zuQB|KDWrMQ_`Uy8oyb&-FR33(jz^y^PZSahip9Q{dGBi?hmnqEC+dmj_E3*zRx`8Pbs!ocyc zX^+KrpNQ?piZ4V`D`Wfq7lfV0=G{plN8)c}k=_d7{I@o zQDZ4uwA4_&sG<6bF9=!fO?ytDRju08@VBtAQYKKHQBfKF%_6?sBDb448E956lu+4w zH${zF1@UyXx}Ls+d1P|$bbhWD**EqWG0Y68D1w|8hMiBprH-1A)0imWU3<8h=DV3u z9986FrZp9h4Qnj+&r@}3(O9?sOjy&JI5aut@Nyd?_lVAm(zc#ChG=JIq zc?w%kXLb_~LRivH4^BdYo6}L8O=kNu~9(@Uqc7#dZ$>PcVo zPkGR+))n3D`p;z!ik6WQyW5NVv*Or=`l*YJEOg%V!&{N_=B)5*TH$YHR``ZkeK)q3 z7fdBo$+hI}ybHaWOly zRchrrZ$QfB{7(gE&F+sEtM&f6S?{MYiHcerfA_K8ovioEtoM_2URKy#W^D6gYFQiw z;_4!@;w_G{>~#^DWfn(S_qvEoaEqfSqE(%Z-Dz!4>eUE~EOxubShNC_fqfS_EH_^i zfewm`9yIz|pIz^y8%TAtbm#awrE6Y=5-?XXdL|csfTyP=1#sVmU!`btgFG*MwE&oN z)v2l3P!p4t`WoB@{mbf`vdSjT#Q#@bfbW;TsJ!5o{e$SLyPp47UNBf*@atF4!Ds$| zP+qX(z&kdc^U4dR{6*yj{+IqT@`77mhGCYS3HWS)ykIIB1G&|=Coi~!@BWX;3;r1) z$2)S4I-k7Yt2#&h<>dvxe*PUA-}&SP-+zwr{pIBa*S_%%jqiN&g3laed~ZiyQ1}N5 zy7Gc+GWv-A^WFa;dBL7@;qgD_JbgZSK~(efmzNi`pLmDh;C%9eU+rUjS$V-1&m%9m zi)kN-69>u*zPN30{<-A^J9`J0&y^RvpS}%j?N5*w4A5tlgP6_j?^~tHN^%Oa4$2(r zH#}jDCRNt_05)7Xnk?4}iUSr}tPoBLaQpe_;jAhvmY7i3(uzBQHTJv_am<{3(O%Z6s_%hoP3&&qDIO?p=4Yh?V&b|X5){OKW zDOl*7u1H2!RrTn4OvrlEH>D?Nr7C(-E1o@g8iN}OauVZU*n))=s!Y)`V}ysbX0uWX zn~sn0t=K`0_SgEfK7N^>V6ciM&?qD=g;6VMk3MPYv70ON=t87^f5h?zh`M4l2oY%z zL~o@Zc%44br_}GZA3PnsJE`I^&nvNOMh6;Sr1DYR`cdbrp75CW|wNNO%zmWDuk zg-py(oQdJ6`HC5&1TOdV&&-Xi%b=MNXIXgO>4~(xf`uaTk(l*T|GeCr%FavFvoBVa zofoe&FC9xtH3$91H~r^RdHH0r=ym4mR7HoT&X0j&X4-I9y8BbJnV$QYDs((k?4ln8 zXKAWtGi_$RKF=P_&X@1L6l-7r;8#H1pkbbOgaZHo`=H$Q_q~uEr`G?>akBm#X8XtF z4#oihXC%@3Co#mG4G4TaGi0;u8n81)jq20spCi`<{@(7+cp7Hl?eC25l-JpL!%5z$5aicT;7yKW`VB0?+(0 z^YuCyE<0a;3N&T=Hvpa<9Z27Xny;}&d1Y|+e-EBM`t-rKz16>w_rSPXGfzXbZE< z&wo%UKhqs;(;`RP^jAg&)${c+>(dEoy83PHrAHYoAN|JW0rJtmgFANQqc=G4dY~69qu-s z2kHpoYDxc5^c(r=P)~kldXlpOj=t)A^K;8PIzRBdOOtQb8YN!NEd8hHo6(Exea-@tk1@*?}xEqVyx~%72Vg_ zHovxLx$V6?Gm}ZPnkTRDydG@Z?<{62yO=4nn7xh%2hC$Q*RXLJu)Fb5hbd$^o7Iub z{mS)RpyK|o)KgsK4Z#p)I?7$B%q0aaA|RuiQo6od(Jkwm$SN*B8LK?`Z^mI7xAdZk z8Z@3o_e41b;r3xw`X8WVS^OvX*Zou)KUhq*XV)!w+b!_{X_D^!~x0q6U2ea}^u z<=myKFImRjEHo{3MgPRNs^ea_f<2k|EY1?a(q|Q+CRR%%F{3nAKcg&oW2}C*U#qT5 z{WruCpDm2G?ahzj79*DU2X^Tlg*av}(LJPgPHI!_xg}-zb-yB3_0m*())i&;r%y|5 zit=e#9{0zF$Esf7y<@5!8deq_xg#4VVn?iMk1}xmzz@OIwuhV1v8JgrUiA{E5Tkx2 zj6|1F*r6cZNEVu9rshq50IlP&o+{{O@|icD=xlEs9q=UgwRh$x-`d%YGVc;X zYBNjy-}pNCE=4$oOE*1q9p5-t9k@|LA69q1I%>~VM}g_b zMdzzSK3vZ_7UF~BigPpD@tkAnrNCM1!0|72*ypRmd)9tnxrrWos$_|R zNhXkkl3(X186KGqUwM?EF8UnAg6kOX(oflC#U{p3zzSx>msWHTjSA z{%W1~w~Ux8%~Qd!++qDZjmNB~w1jK5gum9}&F`jc(XaSZ^4p%LRge2V>bE_enRj=G zr1#Ul!Mk|g{+sUkV=3z_`tMk^^l%!$e?rdq<_#L(Pn>s;00ju(n=Pc|FpunG2VaAk zZ;aH34Ucw1JONGy=SC8B4*s=^yu!C10$8EHAXu+V+GJBRC1Y)dt7gOc!JB+^irw&0 zu+ARP^he;c930pc8rh4Tn*w@ug%m6{L}j4N>+H}+WE0ovT2XcSnk&cxy}*>EW^Ta* z4W{g+Tz?LuFps*WG=zCn@m6VuzyFpHDv)W#f+Y6&~Xf?UtU@T=r% z&cQ$JynDe_-ur!DpFxhVWh#HUYNO@)xd`A$*|;6?t(Z(YHf?;sM{q-ZvCx-^Co~1+eDWq zn5}#M!F7z3z4tS-MS=GL+12E;+T^dx6$xX3y@N{#}x(&*^S%FVEaB@&WNAm%T8n z<**fIA8=e!H;^xU(@fHB-^7XEQZ>T7yeru|{oUTS0_+$&ys7v79>oZ*kY4?iAj}(Z zWCzU4OfEJ%Nk|h0oCjl^@3aA>=b|OrGu}_W%qurXY=Vw%pZPK(^JHK7^D^6oCCz@% zVWhls?+p1@(IEp5DC?7ZYTJ^%UC+(n&t?!{Z3lsmpOZ=NdJYo29jk3u)(jrnoR^C> zldxW8IRMr^CvSxRD`#Ix!o`$f9*VO(&A%7T9(C2V($;o?=s!~eh|9@_xVhPjmki9p zax|MYcVLz`Co4KIi|8fUuW(3)1uE{xGQG)Wty}zF!Lgh``n^ltWZ+_Na?kZo-2OiR zn~{$FZ|}_YnU|&XUBm0yu6Y)~l|P^M11~tnbSF&6_w5}$`UKSoA_1@oB^{F!1 z;)*O`U`s_3*LZ&&p?u5fJ9Z3&KQpY1j}konGv4F4C+QPNO10&j;h`0dNp0eDJT>~E z5Jx%OUb>xQdou4j+xs-U`$1gD_*O*N@?#D=<4Yd(2YUH4`pr%`>FL#pc}8jIO_2Lh ze?xB>e;2-4*16~8swZ@mp8jT~Ia-H$`Q5XBRR=h~=zuXtKfot_#i0YlPQ;<*flA=L zt1wt~V97)F?S1yAPqIPmv2Pi@=)uJ`vHFb;pY;7Z{3YF;{0(gAeE3VHp>BsWj&0y~ z&s(cHV491)>pLH&&h^A##amyf>WL-Yvgr<19DoNtK@7eN4IZAU$*3nJ-KYzvDs8Gg z{@_%5>;;NXm*OCD0j9H~b}B?lFLt$0lYJ;wwUrWuh%ySjB7?DOOlByFpQHQRspYM_ z@W?$e9U92QBQc>YA8ZYe?39EiSkW84a!;_SFCVhnimapIs|Yg$m3?H%TQ;qFJ52OU zS)K2tlGboxd&9MzZFl(pMyrF%>CTQBrIQ&fNgq$1tZP%~zbVY+Nj869ae-#jM5P?e zW;*NF`Te{6-tOe_IshVMPr~f>u3GN~{xWHtxED`?V=!e}oiZlB-Q>4Xo+v--7Jq;; zO6>0RSJYI=Fb6fq3kr@9>WY(veCD z!g(9p-T5%x0`8M<)jfF@m=6Q&&gQgdP5z!R`L9Gb_p`w5ExEG z;fqu-X8lTbLdiz{jM|_OxS@@}D9pZKRkII5@^%~i4!Fv5YOrekk|zO6E~?-m$y`(k z5_Hqn(H_qzggUtp$&gSnP=>H*7YDS_?~2&7-X&c8Jn@Y8MXLy9J+@Hr7Oy%9T)v42 zIXLopBu1f%Cz_7u6N6ZFK7?%O1G56BgU!!3)WwCcL9T$0P%}SA3@n0N|4vYJ3j(w| z{8x2$%q|Tx1W>F&sbJT0f(qw3P}fF)!@EHf-5s=}a?8psoAqbr$UW<2L*^7zJ+bAicb^~ zg+DZWV!SjaJ~4wA3>O%J3xx0UCimBJ4X}4fH}@RdUI#aLAM?V+8y!HK0hL}fNgs{d zvpy;|ZO2r*h`318KZbtkwvUlA`gJJPws=(<9~d8HgLlQNjuDyqc^;u6ugfWJxFV@A zU9etBsY!$$LLayd(Q)(ZzUGh723Y2!2FpZ^&>8Giys!CUvhf=CTYmDAFU{O3 zcWiRN9e;JqVf;6=^$=lwFEWp616AX%?(7JchOTy)wDBL+TkNBmk?*bTP9D8zx4_Cf zMsO7xBb>SDQn{AyDoY-$eUkqw)5`DmNYx9C{}HlYZF==Y(9;{NI@EAulv{lnhAG1; z1$BDctCQQ}aj;X|M$FGCL53j$V%;uviIteVI&ZtaGNi0cUD(7KaPbq#bnT{Odf^tD z-;z99-aZuY!5__R1@_Er7&#a{f&kTtkAr!WuHEU;l2>~EHB+wVU%;0@h&*PC{nK|k z{|Np@X#pI)C|$H~$4_<%ZrFpm)YmHS-|wY>rk?ZM$@2d54j?wVU2w(#;KSNAm%v#Y zv&~7QT|C;{HqIma*HVDgNjQlI`~76NKm9eFF7{+~*VNp)0c~GV-js(OS5pkrEE}lv5y%~MTm?~Z`TWJ->z_Zc}PE{7NT zVBFII3gnJu(GE)cnms@$>~z5td-Ml`R>u^3?g#Uup2JhPf6DJ$IOi;gdJaXgw&m_* zLA>e^A<8?E4@nhlbh=fCew8%k}uUg&+jwI^|Ow!feEj@p!jOouZs=N@=fjKB6h=Qu);K^ z(_iO@lYNc#+sl78dfm?C&RMSj23wiy&uGG@`R#ewR~ZMD*p-@dc*HlQmnpI_Aj0Mt z3ezRw8NO%U-4Bo9F@WRDlVO5L+cE|ucAs>(AlD#H7F#0~n@|tpLmN+&EvvNWoBAq! z%MLR}-Om-{-m5?9F|A>1GmLHp9y`-q#%=RtP0TKPlmtT&f(p+>?`WH9lV0k(Eq>pExTFB>8Sr|;ht%r zAV$!tKkG>xqr^JXxiqwIFP+>(=hoA?SLob&`g5iF_8HT)3-l<~W02gHMZ1ju#jI5i zYqp2~J9*xcvu;#dXey zp9N?6>q;rSkHTB|eS+W4WY4T;K$D=`;_HRgCX}Wx^qLHz%0TxsbZU4BJ`fl$vagrt}fP$U9tt56&4v1myf;yuU-M5N6S zkWHd0_Vrkr6@WjutKn%?6o~afYl8_ZaqJ3VQcMUgZ#yQ^vl*>?tbPNo*ovF>@W~eR z2L=so;0&?N_%`?h8hHtKa1bu+7y^J~+t@}x9J{E}oC8@tE%#-Srqt3%(`!pJ`$hX^ zli4@@Q2g1YT1Ni(YgY5;rq$;vONnArtfp0EThCRN62+!i&2E)d+uE~3_JI<`HM=7y ze_8}bQBN<<^o@MLsGLhdL}1*{<5?tD(Hg4Q7Wuo4P!-=YFB!%k%5hL|&^jQjo6?_` zFcQ?{Fh}SkM?^is#p6UdYIWqu%*s*!g4If^StClapf&kQ$qw_r$-M8@^SoYY$4Sr5 z?UcB4X>xzbHob1y?v|alQf2Qn^~^W*+{t(PKwsb43t1}JMv3{Yq6G6dncDdL_a#SE z?C^Gz|4Q9knQHe#&0m3EM9)%v6wVez>>p-}Pl)o{yK>ytV9WKI_I zI0&@eU|k1+&Y?tcaE=&b#Ul`VT%E^9#hjV_3YhLWTVpzvm+^5iPGlrTsVmbbH3To8 zpfT;C(H{P9;W^6?=^J-Y>lWY7SJzkTt6D;z&p+$1%vaSkrE&RI)UgbF`I$N9OAT&F z>Y%S`oYA@bcEoB66D8mXC^akJN&nkpx+}1hWijWc^t1my@cox<1K~qN(ALM7Y$LlX4n-SJ3w_9kIlm!muy=B65h5 zaPv-IbA#c)t|Gq1zjTCsq2u9ZZa;pSL`2yoSe0RVirtuT2!jY!_NhpuTlr}XkJMJ- z3DdF7MzZu=_{#1ef<#0GZQ*9r(a#F|VET-BnqWouw;^1*oa&s<J zOgTsK(`D+DY`WI#h`DtJ9i+{H8F$XJW@gPCGatFr+v=F5b63nckdvWid3> zsJ~o4J(f{VI=FqnL@yO|W#;)|tavrU%q+)*hdCB7&v4w&n0!5L)hd|7f^aN{1I>4d zneXX2^Zn?6`Cdi~&V1LEFyCLSD`CFBRyS(Bj!c^K5_irQ-`qdnXDus9%*SCF%uHwJ z`)hSY@Q1Z#4N)VUG3O3lKO@;W7rFR?y9yclUsr;c%06gr$uaQFsH2(FT}hvAAH=J+ zH$LC&CXQ zP{IW1tIA^bZGlM04(z82Hq6U0n~vjT>J`1)Q~GqdwNe4wsL8l-FUR%&c*6V+C!QAW z&Y5+Dc zso$#j8WFRx^je>ls{(S7Bm#7NiBTzpyGcGZXwTl zO`;cS?jQsVoJ0Y(F)*e`>9#pd;ZsR+nQ?HSBd&LzGL>`x0@Djs{*|8|JY9FQ2H~?m z0ZB|x;*SGYR<~ILxn`m`F;Hl&x-E6NJ<8Lxx1gc}Lp9pw%J4Kb&RjCQ7(x3g+NWu8 zBDx?|Ef1;XaqFdMMVlI&lltwiBr;kUsLuFxHByUpTC3`{UZ%EUdqpTvE7x<(6v5S_ zo^3I%zf*HVpoU2(OK;>Zk*<{tYXHzAv6K~>iAbpU zBLo*?P#RE)2e#e``H1+5YeU3Mtlto{wqa7qR)`X22ty?f0`8W;FWy)}34OzC39}n$ z4UjGB&h7#-S2G-EH!7av7s+_f zdOl_^WJ0YDYlDIECKFGQxSDTjrGa$hY!ce}H7{B)?sA8%6n8y=^;6NiJEhEv0N~i3 zbX1>i;MUN>z-U2xS?ce8!-6oc0`k~3Tmx+UtstG%9H(29MaV?$DJL^d)vKOcQfeUn z3unTkppjc}Pr%a(65$neD}L044wMpvqY`NEQtzCu(X-$UA?pv<$E?ljlGB|Ff7ag} zwX}#3+8q@cUD@BkH}XZ!J8z92zSR5Unq|zZW?EW*8~$2{L%XP z)z+d_@x*5%l&+yjywwV?CQFR-?6nH5)ACz-WWo^rUI)>{j7P$RJ6q9eUZ$B)I}vNf z%Bay5b%(4OKaT5yKKS(aj0CJcV|g@j1NtO%kJh(p852oMqlpX2gl1`7}1Uyx1(IkHxCA$=7$NqyD6|JfC6Ci4O^ zCBz*akP?!`H)6vrbc`tF3yR+(-4L=wZduZtue*L*YWbDVPP5{n**SS?CydSjTi2#Yq~ zil?BBG5brcG5Z$K#cbS5JQ1=Mt&G@<)`qPk5Vx51rKNENDEpz>z`8PGEm{M|g1-YH z@I?5>+$CEYB_`C3nSOvHf=%m&pKe^0prWRZ%4kzpWpD=AqE!prl&n16xaM@@>eT!G z*=(B1&^3#eFZ?O4wj+tzt3rH4N6~YH@Kw4Z%_r3Bj6T!Ngc%Vt^_XTVmiVrj2s$5G z6|x>vfL6_26!E?FrRD5m16VQZ7JB)6CMO@*301s_lCdCCV5TExZ3PnI*2bvSnFYrO zqxQnBWsKHLhDM#C^Eu;Q9>+`KbZ0rz5HrRcZQ8$-0hi%8u}oO8EHl^_9(D#ByQXnv z<9~$`rk{xxvv3SMY&R^!e68`3#h;HP%wi|lz4aTZRHvhuJ%h-?%(XqE)g5@Kf=I|W z#t4IJ7vZtr8HuMGa^IpAzkx8MH4!_!RtcIRrabCtOFhPk5+qRxo4@H(!JD%8p^Vj| z_K)_Ku3cI?G8-%719BKXAkF^jsRQQ|SGcKNh+=|-Jk=>ZrS#ORr(Qkv>4}5XZv1|J z)0}FTYQf0zZYImS75hj3&RWuu zZq2D5_4lu-5!K^pQq3J^C|1P&*oVKR8_ucvAGtT1?*MJXlr*bbgYc_$JO{-ZWs*Uz z`>JU#+#O9^2A)Jm{sqwJouwgb_8J-LtoX7y9U?7U9N=&eNqh=9;jt^?i7)Z{+U1k% zvF`~sy;=~i*oH*Wo>oAZvMp$#!q!1Tx?vnZ;ry8A0HMjA6|g=xT?+3L<9ql z)(B`F{3?x09?9!NiTD~I;H2KkTg@eacOyU>TfpUzlLlLV6!ZhJDJa7or$MCeF4INJ zd#)2-99|<4+U7BYPm7$8DrRF4tvtT%CF$z7=SM38x@tGNY_HK}dtF4j@%CqQ6b39E zZ?xexix&~S7-Z#>F$X6-+2h21kH*Xk2ffe-B5h-HosaRBQ$>vJ1j% z5lK8^#eV^Efa?!hCk@MAjRPWl@$Vt9UC26$Q~sD0ZVfi=N1xPDma6_wSp7t}HCAxT zTAk}d*L>+0jT;l;hhm9aR-$_X%Zt=kF;{=j=X6e-X=3&xgwPa&8nGL@!}wKR7q?4F zW^%b2+htJ%oN|O$i;Ew#C3V|B8UB6fZ5$G{Zd{XLD88i1LKVa~i$gr(kA$op!74&& z;ZPEXMMnr7QWKocY^6%6)As%gDE*NMc4#ejv@g`%EVSCVCT!n$5@g!Wj64GC4<;Ef*`rhF-Ll}DC$BcLJCnfFQL9t6m<{WLAB8s)he`N}wAaLRh2j{ZFESK2Qq zV-<--UHYJ?GySA#L;g5!(?DE&!28F@$-bYB|8kLobsWl2U3Cy#5U>BRZ_2@V;?BZw zVm=}-gE1zQf-1_a#vcn&+zm*|*5M9*t&O3?XALi99A#ZjH0?1&gDD7Ggb6$-PhOlJ z6pRLhAoT<4>o~5oeUIXV%V?=RUGe&^7RXiK~0XuV1o^Qo85Jc3TF8*q(K@h_o1aYVKjf)^aSPp`CD%kc02x6Vb)ng67 z6NcZykfhiBC+C+V1Hh* z(Q5pK3r}$?4NYac5pRw&ii0m+_regBrTmC>g)brnvorXj5qtqJ^aw~{r6wHo`nZEK ze!O_HiCDL2btp0W$4uVJzHDd{fy(Nab#9l7r{I>K}qHZ<7uGOCl}7IC0S zEXgA4!mH_Z9>0=X=vQ(JI{w2^Dsr--Ma;ghWwu6>_=$$i0YFs~wm$K=S`N4D58Jm8 z!8Q5{USUv*?jgPpl0^a*gAXOCdNx{*5#5t)gj2!8NjCU>>Jo1An@)1FlxGFI=(}oG z6gz0o3Y*iLUEfsxIIWz3r7@(Ta`HiYFsSw#Tf;s!7K3 z=L5@Q_G5EQk(L^M&{KRkX8%OvWlAFa#3d`_1ohZB^Mb7QS@F?OA3tY74h&`1i!t<> zZJ6o3pwoqN5Nm)iI@`Wh7khy)LyxQKm~(n#I#d(!Fp=J3wyeT6PDyBun=;Qy;plHt zio>N&gPGdPo#rwP^nVr^SW$7qz>4x!5j|&22SnD6bu| zuh7w|j6?t{Sn+$D+-!@~Z-&3#lx{S89iC=i;beQ%)syX#vB4XX2WGCnK6u0RA#3xE zA?tAJ#??ZG!+v>>E*W-KcD{7<%dP1bIhSzMFZ!WC5KuK(dh7LY*##Y6xvDbvE+UCEWJnoOL=Ff~r z_%nMAxNMSNeT-R05ngd-`WynYj6A^rt^=j0>u;`!*=D06G!VQYo2lgo;OOLk2Xh@> zs_F`<K6P@fhX@eQdL!UG4dC&5%LI`GAR!FROT0d|60w=70{G z-7aX!H}*`Q%B1RCtkem{VyPwMB!`!pG=F-md9SN3q=pEf{I$BNSjN=0(!4oRldSz! zHzLs@1ft}5{Awnj5z2ND3dzGwt8JvIFKU&V@l`wlea}f$ed@b_7AmK9OLGUYjfi&Fo zZK0ugC)!TuTYb21pdTT7tj|z4$VEHyGH#!A1rK*ZD?Vr%I02!Mt$3DD?1NC;d?H$Z zirp%CBQ!#)2UIUeq+v^vLg_;6YzIJF6h4F3 zhvPMhVfj;E|F&@ZZGp1dn{*8feU>-iYZ=c_70_-xCdv&Tmh>Ad4SzV#*DU``dIya^ z#mq{|PIrp)V39bl>UBX50@o2Ka(AadG)D#256{vhm<9fwPJ4z2^Fw>&Zz`B`CZm^`L~5iD5^udg#aB zi`aF--;Mwh4PuP~JkFNO!6F(J-~>0sSRE);H;Gj$DexXTL&R( zuR6!epj|Y!>7{~-12=!Vg&k57@U6JeK`JQjwBBL7En`}G)WNw_ViF7Grn%*1H*tF1L|AzC77LSgz%x}$j8 zAOzLNs6oC=RXl^^Ys1b&Xv9Lfb*i4`oOWqvaFgGs!U0XLxX&H2)1*PIce!cQTRg#^ z{vz+G2b+zE5cqWPC$<^pz;Do}P$o+7+DPKR0);>{sB|nSbZmw(|3zRe1-Jyus4oUc zEo8WNN_0du)dIFozL)e(ENQKjo}8%a!dwC8NxalV$docPxw4X=dmcA zui=$Ar#K`bp%$%H`sxC%&1?rb(HZAy8OI=-TGUE`cn(JcjGY0yfl9=qm5#Rg>Q z5lpjyJbW)>WblTNU=7E-VS0!}JSpoL2QS*qS>=DZ)M2+Oxh!HYTT1nM_J_nfzs2cV z{>hGuf5S^9AjkN>z zxpnn`4=l~zy82S{aJ<+tF(7NA;Ox3`b1!Fa-NbR`;drs5bmfbQwG_Ic#$2N~Ui^SD zJ|Ov13s|&=j0P4>2Pb;egX8o+^&neMaq26<9%Z^iUg&F4KqOJ9hA~o%H&q>{8V2{9()%SZJO-(bUWG zKLz$PCIzXtzK-|Qogl!qe4gi8_Q`URLHx*wZCC`mH z9dR6hMC|#6R$EgFG8Vc(4vX@d`U>a2r)hWJm@U@krgmBze_>Vo{DVgQ&icdx3lKN5 zK*#+L1eQ`>vI~9`#Td0i1DNVy-RHHEBf!$EB|8|@&l;^G?I8maW?g#fKJTv7=LfCS z!Me%iti;qx{>-eQbQ`NMb)0$7a;~O1QYP2(=hF)y0dtz(@K(H-SvO~;STbh;Bfz0d zOlr`VGzXUINh?zxvm<4xXYPlKZ3v*{aqvpBqi1I|D_)=mOj11!Fd+U62puo3F4P5# z3X`;&x+qlb$8fdbVw3a;Nvey!7FU--?<0^K;fWm5_ji)m-`y$nl0*73=Nxx7sm*A@ z9Z$@3!`J)|EHTM({?xV63t!7Rs(|Fy3cCOxB61DGRK)C@YLn`$mf6jQb~PpaEKp71 z6#KS91U#=sp(zLJBIR^?4nc731*e$CRzA(_3U~WN>@%h@^=$;od^cUtkAve6nJHLl zreM060#Sg53r(_?fLbRYXb3|^nIc#uc0>9mxk+A0HMPztusC4;>p2 z@xjl(S1IUpW~ff(5A4)?aysR#3)0YVndt$*O%H@BwR_T=@V4vn(~){Q^jqyCv-h}J zp<6TdBrM~dtv9BXsE4gg^umikX5)(yP0ru1#mHY0UJG->cO?fZdNbQhK5*|8jyAjS zJR5GT&)IPFjn1>-qy_IY`)FQvUdVYMuw0XRu&&muCPrRKOqX6Y%jwJ&JEns@RI81q z6J7?4QQv86)zoOw?FA<@crb(T+@&9Ca(}pz6`!72@hyv;t>LUVlS|H6TFE1#_~I*b zSDjsREPt^B2&>g+?HZP0j#&n*P%_J)`A$dicDz#7EroQiGQEpc`P|AepfEr@eI?M6 zdU4?ZU#yb1{P*bh>ohF-J%14WeqsKfO24}X&~L{S&e+pvsF{P%+35F5dNU;b{)$V# zxi<&>HVmqv->}^>ygZYB_YJ1s$8zYmrhNeARsul!_UJa6GL&PY-mbiBrXQfHhpHbL zgXsg587Dpa3L)MCN~o*O8~&-KqT(46UORbrgheicdW(nyrJX?Ukc3+Re6a3QAl3{d z8#p>Bnk|4`W1tE*Ndj_V;X#yopTyRKD7C~@YFW(BR+T|&(Y3Gy5wF0zfXEQeC- z9`lftIt3SaR!V(c(_flW&l08X9Z0DS$n;Zc(O{QK`yInH(N$nT)VdbBomrB>6uPHw za%JwkYi&M##@bA-?WfJhbJj*6c2<%+;Tz{C$-zO$1(JM~Nism;lH~W9q`@S)8Q@@@ z&ZNs9W80mJE}OZNby^NhzQv)*nG?`FLzUa8-l588p~tLA7L9|m9eRANJu6F(T?`Bq z&WTvh_0#0DQ@x_ZSxIrskYdjnq*&&-69Jx-~M z&>GWAEOX9S*VNbGP$}f{2t$x#dNCw9_TI3!Cwr*(?IZ?6%6G$EdL?HIX94?mk&f*`en1gNXgkL4ji#3VifT3Vieo z3e3z8NrCT$0=saKrNgI1hqLlVmmXgudOWr9Ec7^gnr_-Rj-zy`RT7XcOEG=1G;H~d zgg`h8z)B{R_(x+@gGRHyA+UhxJKZwAoG1V2lB>aJMd5PBcQa3=oPCj(-4^gS^i zA2T(<9`#lkzjA+6#&OjgdB*P|1?CL%%kcqwK_5Qi$sT3oa8U~_50)LT8WWE<(G(Rz zm)H#*)v1+VmgFlC2v+THe3sL}s7PWIZtlQ^(hF%Klc*_l2*=I28wrc9mv702jhu;P@V$fKRUf|}Wt=6F3y z)VRz&SeM7`(IYrdV1R*YC}LsXaYTeP;^~nwn&@e26>L$w>W#*o3A#m1WptUo%fQz6 z=q}SiQ8JxG{q)@GQa5pra69wGV`0Txz_gL9BP2S>iR0V!mZV6T#NCd32G1J}pHf=2 zN%O0E5STeJ3@5$1T$vFleVt^Id?wzr4e&RD#%jr+>WqYE5_*fyaRW|3$YCNDnruii zAE@ccp))FR@O#`kX`M>H4+?Kg#IO&W@D*bpHW4~(l2UbGCWKo~ zdGU_|P5B{r<>)hRK;z%EuT-EaJAe$R%J!>%y3EZn&-yYk*iHbC7>!i}pJRbqF;#+E zd(ENK49C20Q6tiqEutt3lakBejeqo3&vr3kD_J^X z5x_ZNN^l@#y~bKEji5}dcu~$Q7k`zAqgBr@d5+PS=1gJ#{9yFB8^eBD<@?C0phc+o z>4BoS?bVfnB;wEihq-qTjO(iJeKnDiC`4i8;($xtqP7AgV$PMRC{m-+nPi5H!U*Jt zYy}b!C{Q&7YQO@wM3HhNfj8qV>GZ@!rOr7zw*d?#*rhQTYJ=pL6en@yKy1f(*?Ewe zIEfuQ@gooWe!jo8_h=+Ll=g7%J%30uv-jF-uk~BM_xk;Q>zTASw=(?@9Y_9Bb zi+I-g;PKwBDlF#mON#!o7{B$KU_zND3v{LU#;K|?_R)xfVwbD~NDzsuy`{8wTd;l9 zv@+ZFx5wC}ev&SlR%U3Kv3qG3*~u=Lo{J9r55^G(LT7Y3H#>$02>}_RxW}cR_%udE z?mU>r`6RVcmcZgJPnEw*l~1lv;2j|k&wY%{A;K?}7kb4JDoyU}d^joB7xiP<$5?)) z;)xsE2@fSF_1MQqdz^J%oUBknpKpGrb~Af-;HR6qm#lyB<|uK91P2|@w;xYd0gs}b ze24+R7hJNRDi%ak66a~f{{oFB%J%}AUjvj6{>^_BDABJTM&kl70!o^9k_QsPCGtWz zo>S$2q;T(F2NyyFATk3>Twoy$0|j;$(`1=vMf|IQ$~`x~1A6V<0#K&z2v|`UQ!oWY zfFcx1)F0sCCUOJQ+q~pTv8D5$24RTE*w_SD@B0yr#4&!rCvP35t^{FL@2TMXNMrqO zzW+&u#gXr&PFX2s=y!u?y(deeU!C9!(JpupUM3un6hru)BAMn!RQyhX?G}l7p+#a| z=pr#w^^@@R&Xt5FDS2>5@$!0)fK4{-XGT$!wG)Sa75MNY@*6;0jF@ zPB5!vTtOnQ5uPK@wf6rAJti1xh#(E&IOFe0`12fzwLjPBy%+{f!c&?3uq5X(gc!TP zQ~ZW-1|lH{=P{9b0@(@%x^T&{FLG)UUQ@y)hZC1~j@G##+<=IR+3Hig7S5qF9Gp^X(OQa= z)g;7NYuSN3rX@#wo*>KMzF68jgwL69a7`se1T~*SpB`ES!Z%UG(|d;c!h*%q+LMzu z@I!d*$J5219Z_D_b;F$xWNa8Ny4$~YYiTCA($c!3gX+%&aun_} zE*N{Ksvl#(OX)nmz_zeUn;cd)fvy|0-O;t1Ya(wu#u9l>!;{3E$xc4fqO6zr)jtxQ*s#xgZj z4LrVpK9!b3U$e&r})S zFyXj2;q6X&N4?Y8`#yoO-@A?SQh9yV{6l2Up){%vTKr5WYn`2{lt!5}x~w9pV11ko zj7)i|U1wND`&7`1kfdn;e-YIlJkNWD@NYXd$xiK2e4E!bf#BJtbAT+IpV)DK5v0#27Xdj=$aK6EsrXG^9PfP@ zelf&1@6q|Agl?3m&waisHMZy&tk_*U|a->)8az}rg{h%~F@1ck=x zk3|RG!FBfVgF0iuJqT~$65SZq1Hpo_ewmhgz_R}PyWf@jG2_o9@97`y{sX~z>JWrR z|5Hce*OAYkX}YKXuA6TFr(rD{Ny!ZO$&)aXr+AtzZ}Je5;7+Ko9Wd1vnR6HgH{QFQ z*vV}b*~OohF5WpxjP*S??f0*I)%Js78tiKXan{ifpCoQOQGX~p@Ymp_f(|n+8xz%s z$UuVbNC{uZA+q>h72UiMGGm3&hV+_7aApp4wDDZC$Yt3b496$@eZ^?Tjd%X2!+!%& z%G)u8_9Q%a=5qJZ)=(Km3k2^(ypf!+V?~JI4|h*P@ag(g+`D_aLdE=o{I6$Y@yuHl zDX1_(?>dDB_Fc(hDM6wC>hnP>@0appdMDVRZ$vpSr2vV+UU~WnFS!W`(S&x%F)xj1 zR-}D;Efdjxp?MsyMHOoQQNA;;OAlPfppU~96H-B2)~FB|$q*aUUek#3gkG7|vNpR2 z_GodIgd`~fov_u7{e%d#tjQ!dxr2ZFrKSUYkdU_5sxJ8?y{3uNU>_0TmyWy~;*WT@ioaQ6pw>K1ig*V9iLiiEeI8IX; z73Fa}tETmcs0W?qgZyQE&&h4`>km98Nlq(($Ho92Bfw*=*R@ITSf9Oenc%^X@)kmj ziN?=dH)8PE7#)zK95umePklo!X9LIrx}5=#SP}GHA-(7S8+eEy1&@K3KwBv_I>9B? z-ZI+QepF@6Bw^z9uK~yT@#u%h6exR>%pMZ<`o7JhM4Hd9NcoH3RFSHGL0NMw7fMg8 zbWgPJPhgt_W}Y7%(D@R)#HfOh;@8+!|I4}|Z189u4{31C9J{-LO^Q$QNqf7#f6cgoun_wG+sQ@E$> zfdjA@f62*Y^?s@(m--(d+iZVR$A1Efa znncROMCe*hE1|h0nk>Q@!QhC%slZZ%U2RKHA`*)}h+wKk?>^Pod$=O|ryy7BM^K_h zJAaT%Gr+w4%1BL)!&2#=5wk`QoMf!77||YT4wd4yi=2u#lXt=f)Of^S{AMY86JcC! z?H&@Tn0)i{^Q2JFGQ1d`pNHO0-i%ZSzf0B_21t)-z^5p z$XQCt{JXMemY=1cK>Y z(%y=O5-Z&i?W^Hky=PER-pLojThUOQSu1@H{e#Cl#I8bGqiuLxbv@ejm=srPg+uW{?qBUSmTZ$LO08#4?I6Rc= z#d{C}#fM8?sf-#|GK|EkMk;8ABs(5~zr1V&OQD->*=ujaucEGR+KgN&JSNKZ8|tyV z7oNzYS~wK`^wuMBKUp0AnSR11wl{hlf;r*%f-JZ5Pfxw%>AyU7CU-e#Z}pR==_gbD zGDqw8r0Z8d&)M&pseb<_YD(@2(PCbIp6X9zqH$(jfBJb=e;k1@cdEPCm+0w6m8uQpx&drV$L@)h(X2=+M)^r;)gO*mHCDoc& zNuC0)b0e{&dpV@jUGlmx?;75Z-SnPl|6kI7?}{qo^pb0%eSgjpg3*`$!(zEp?wNnw ze9q5cMxihaOL<6pEh7M+Whi?|hj~xZqL-E-6p8h6C4ItH(KOp!WC=2+R4`_z zZBNm&`J3=C;cvpjWd0_+PiS0tpDh2Ib85*MI$#`{xamF>dJa5Jd(GI{jnd8FqXV-v zuqn4wa5rAREjn-<6(#UJjoU+GxhI@Ips{_M#13PRoMbIw@7E-X`6e%>d8er7x+Chp zWJ=f`TE}~zEul^;(tW#BFou?na1?DrM1@YgSEy&;TvCDG5R=96ZDsLR;mwH;``bo#<19@i@uriF6qitB;x%Mw*(=EYkD#6k0Jgi5eo1^G!nQ^z8~iU2!@QnZ(ziA+gyY=iN0f)|f38YkvWk%G2BF?u*wDVdt({AM`lu zg4~Z;6XOTcliX)FH1E#?(d_%zKAMDm|= z8;m*5z7s#tAvOwe{wTyaWbmn_#^C?uuR;btjzSo$-!%SyF6K^I5Yfq7F72X9Sd`&g5yJYUiIdK9KxAZE|%ISx;X9o|8$5IzFiXg~&x z;?X&g%6yTu)!OrL<*bGCuaEX8#o@mqBl+EsR?2%BQ?HS#jEX)jzjWP)WFW$XhG0Gc zhgY`Ftd^D8%TTImEE5?)tc-tmw#T^X?Z zm!kc(zycc|-zSPVzU09pT!3%qngEUX_qmhT-S}sMZh2Dxx?vNFCLEha1-hZ^kv2fr zDbS5dKmxj9Wmn%W&@FMo(fIcw-YbJr?g&74Dh=pfaG-mcgFHj|`0w&@-sK&YtqIhw z5Y%Ai1~qmLruaFhLcG0TOCh*%ZkRc4{!+B>9*BmnwDSAKHZhA>eK^K^>No7-^E^|s z8_|9L`&g`WSF~@DFiu`T)n3!C!Tk;$0y+^Nr>KrG@=F6o{$^w3Zw^i8ot^txCZZJ| ze|2r~@;~>qVNjm9SFC7O$u4$B>L;{iq|K3Ocj_l^#tUkZT!{x<>pg|*J@6>gVg|T< zKD(YR0TQMgz2hvr`IWJQtcC)WQ(j&~_84jpSuZ{8$WfDVnw3Y3# ziI3cb!)_CsO$4Ijygp=HZ$#TKFbC0d#lLdzL#r#`XSw&m=?Z^8&4>zr ze*;yw@b^++Q~3KWAD;eu>9sR{<5Vwf|Bcq^zu$Mw^xxl^(cd?&o__x=OQ-+7@P_HX z--lvV_`anx-ZwsD{1?vXe|*Wb=YxHB`8r#?m&DvxDEC6))2WP08G|8^NWlMeWt%yy zI-v?Vu0G3Wrmu38wiowR?&BkI5pGSA+1N>ru`Nc~;H9JEAF#bWcG%OIt19_eocVx0 z*sv!sIn#`)UxGfMP*xWSZc2fV}iH>h^1z^%aLjJpv_ z>9y$BALMSd{}=q%#@jrgpM9VxG*lKKT{?+GhuxJ!6%n{_I2 zLWF^LSg!VTvG#Dc80SlRVHZQ6|Yp7}*v-&vLR$DQvUUE}%JO0W{snMAdrGThe zv8)yT>;Mvot1^?;alhqN+tT}$v|P&5kqmMV!49ng6^SPRhxI9UcDFbOl?90-N4;07 z8?zT5ayf5*3W96?bCi2mqa)F(v|FZnj`+(s}E2ODI7@|j#P)GSlc$9SuHNYPs^|Sx-@sN#a2xC=H_h zKjpth`~^`D$SWkNQ`DW}3UQ7?*NQ!>@fWaRX4@tzMY0tHCpq97GI894^BLh&lPfC7!WxrM9qh=S^AjxW{lMYL?$LL%8U;#}&SK~{(^$g!o0 zSv^s3o&U=9H1k5gojI?8Dlk+OR22kdiSCA-L$qPHD;KGpEq$vKh^yEmn}hnl3V7)2 zdCQY6`9UftOheSBH2RFD0A03f{>!p&1}Cg;S(Zv)W#P#MiKlP=vhd3Ye3hT zzk#$l`-dG6HaPL^#WNqRMD+z#@a>JpacIaUOps!!vw_g@S=G6e7YTKPYb$5N46%`(#_=9GeM|L)5K8V&(M&to(n47YX0@I11z(q(szRAM_h0 zgote%-njA+T6#N45oNn0x^8i034^Gb+T@UM=>RzW4~n-ySQkSeUSH)JEwjt=V7C{* zk^cvLmI;o3S7uGi)Bg93wMhiGvS(VyGi+1d2qP+qIew?Ys`Mszx|EhSal~f6l+0|= z6SP1R5r=`hPH$oPfkEmWElNfIZg`jRVnU4}-e%JjxH~V7Dg#4=Jse7V_XhZtnT?8r zt?{(jL!$Z_1rRJGpzH?CyP?@WDq$P^YBkQsmsIm2{&yH_5ykT3#%3kAuYe)kB(&3p zZFW8a6`E;F)X%Hz{)RHw%5=|7dsC%Nj*wb1JQ);E*GX?!L00Fo=-_)Hte_v=!KF2D zh(6&8CE`)ll=mcMjAfdK_@~rUbe%+x)Bvk)T+@5HBs#d4v>yjxlF(U>m?4#0p83zE zg%EV!fsxfj-Cj66k*6uY0G>p;p#QP?8TY7JKpBD0CS!K&uYy@MI2BFVq-ssvd$>_CVuu7S#%uV6 zzWlt@#Y59U)gZv6^bZUZbigp6V;2qMIt=5^g<`iFjb%^&MTla6?A9rSsbRtVM9oPl zh^>&C@R4rWTOm0VBmsR`0#TKaw|v@VBepg!vyB|KreR0!7@oMH-t5KvXcooaQEHu* zWNIs+^D16}RB-U(_dNZU7SamM^1DXA{W1$F(L23WMhV;u!g4h}82PPgHiJ|2a81Q) zjd6(RX=|fTrP`^)lLoVcTgwciHL*Ij*l`6Y*^-lSOy8Mh{RmBhR}$(BaBf%c!_}vO z%NPR~)=w_4)u->M!Mn3^#4f{RoIN09i1jv97K3Ruj9^}n43D;e?v$bA#lRCVX5uqI zibfSGA)8>k<*Sxhj^$CSvS@6_u@|RUVZ9#eVa2PMVuLXi%`crzyx&&3CRI;mjn1F2 z)EXz=llt1q=-?8G0tY+zOjQ{iWnP}u7>(UjxeQ)YL$6gzHb|w4*HrdsN;It_%fC6g zs(KpFsS?lmGG13+UsAmT5~+aCh@iMo9mrl)Mc~u zo)IO@c6_MD_>j~7fUdNyv_V%j4XzboXMgC}b;B`%&_h{7G&IoAEI1uc|JZiA>!bCBVwhN zAYw7d?BcHzu^Z_;`^IEACX*LV0P?8hg)J6kd0!Phw@nioY;c*-VBCSca97Z5gQdzX zl7qUW(kX=H5)T)mJ5JHQsJkRwBU{z!44~0_HtoHX0EeVxo<;c6{0Wxd@_g!+?LA_9 z0LcYdlr7Xuh|X-Lh}Blzi!Y|WxW2GVHPRgPC(WrgJKIq^WyZ-vy6L+NT}6UQ5^#DA z06(%SjSueO;e=T1z>Q!XE@JaRY5XxPyDI+XIoKqsYbynfm~)uOugqvlLwa9T*`sN} z>S7WzaDY4WSJHwl6e?tLqm&7_Ohe7Sd$BE8P42nlpwDl@NvE;VLO9}s397E((8;E} zm&~;%PAa|bx5r0+y1<4U^3hri+;==2c{zmx0IM`QaIZL=`IvrNAp0hs37Aq%?x!!E zIfLR+2jGix>t3`UrlsWG3@!d9Ynl5bB5|6*8q^Cg?~;<0NcB2K#k76ALX?~cOiy$S zG?6>a;*)+W<4#OJ#;2L16Fgw%=qO)gj(!ErQxV=@x9|TVq-kc5wx-B7Q<9X{NQ3a0^~kB*T;xGoDY& zFinK@vB>C>9oj+b#`c}07J)b@vDadjtuZ?@sCULMImpC#Ie*ss$# zKaP9Op4dP$$YajMn5CjOkxI00Gm3_IPhX`REMh-jdo%5>txVfFFDZnBKu)om$`t|x zpDBHc33XHxMR|Q?4G)V+Ug?)yulHiI)}_Xs^j1^o+>}fw^?7YsUTE>;FfB4Am4%li z1p)v|4{o!^tGA|DqqXW!x^b#{Yp4rwc|Dg4Ok`sgZf`=Y#m-w{yv7~o-#`Dk%a{b$ zs|1$0;7xR$tL!1+*z6n#lcE0M&%T4sSvk4CZLgp;RZL29Xa%;h;*%~U!mJpCr`daA zukc%>V-UrLas09gqB>{jRZt{c@00Kpw@a9D+-Aa3rgs}=R*&8cIjrO}`%UqO;_sVH z0-ZJ|>LHx&Z(xGVXGMncNhB46={1BIry1KZw+2-+%*QmEfkd6zk3yF= zF(HHg8d;2??r+e)9_arA*0y-qG%szBW+1l)r}C75XLI5w_ML-py{lv-lvHv&wjyCd zMWM~VLJw7lX!|~>CJ-v|$y}?VR8({zRtmmfh#y!UqsAU?{0htiA4%5;ziBQ$pT z)7aFxQClPwYHYYo;}hhXo7zd@3JyCM79*=mQD8`@l(+VW*d9)eJ;A9N4Rch{ zqm4LH^gvS*x8YPyc)yUo55C*ZuzW(*`$=MQZjCzOw%O_+ywolb-l<&)QwKqK)Imdd z!CH(&7;l>3+7D@WDDeCMbDYbi-G7e)m;haBlHRj%tCNPrxVEx`Fq#Wq1W3z5J|u`z z;a|*x%N<&eJeLXX1eSaO`_Th|LW^iPFkGQ)5 z@VHRG?D@4;x1CuwjRjW)2Za$UMypMKNtPZLSOP7^2OvB11_+%U7hk4_WAO8&&-{|-KqDS|5A=@W0 z$uML02dz}VVaHhJnqMluRJ#&&!h5%O62onuD#qi%+E&=!NxB}pzw_Uu#_P=t3w`|V zI42TN=DkMzphmL_f7A(1Jc6g-VXe3;9flZ)l|`J?05P>p+|l~rt5_lD1%1R||01sF zsxQd$kiJYEBQR@VKA2UlWcWD>WgPG?G;hQl-m&;1$IsHFIFa;JW(#KvsMRbL@8%@f zlRTP|5ZPYNi_<9VjItx-MaFMz-5T-fDMxIZ@w{_?=L!1V-<+I;`SG z{~JAAZoFk&W|RUBTtDhWz1F7u)oKtJV@nsh9-VhI>{Mujs4aOLZ_oZCUS3`x^JYcw zjg@U>l15tN-qEti*k!8>ad>+SM|dhas1wf!(Bz3yJNju6s(Yrv47oB+h&EA*qN&A? zu5&I$?=;5(MAscdHGTzIP=P#VnI1NYLeMfz8O&V!(nh%Un~~2~`n7L?BJ3KXf`Go- zV{WDWlDmaQgIg{UA>C>~6631?0L^6N9!4M9A77vom(uestAHX5!?r=2~7#0Xnm+`Q^lrMm?#8v#VH%oz8;WY7Gx;Kwg z7ah?QyWMBg^=9wZN`>a89rGw-^LV!ReSw+uRhV$W{;xQc@hU}O1)M{a0eHr=3Xvnt zOJHq141#VhLlA~~prpt?E~nZ9=PAQ6lBhlj&l+>h5GCB!dTX?rN~&72MlA%=8&Js| zug-q{Dia>c*nso)s3|f;JXc~QH;iP%%~%DEHwWTExs1D@_q_6kDnXIUvsDQJ9{GO` zeWFm6kRsvOvFLUTi!o3+%-@NgmhcWG*;L0YV9_Fw>V;|Vfxs^r>JbOgEP`#NKy3z! z|4BvS2-pDyj>?!=k~sy>HU(PD z5|DbDw**9f*}wY}X9*aIGWUc8arZZ5NyQAK96DhoX^O2zGD0lvhzqg470e`vJTk>J zv;fR8*qSM+3@_NOWLxe^GX6B2^!(#VSz0=1kk^z7*3NZUArbyIw%C9ul2Q2t6ROa9hw zVZcPX#cj7n;#yq7Zn7lfDZfC_S{%xKfxW=yh0~S_{E!vRErCqbQGmqU1fEFYAWlBV zA+w*0nJ{b>0ycwy709C+8N?Oc-_}B;64ciRJ_5=$B1M2wy-#@4!3m`v9P>a39#bzs zn;lHT5Q__ueVTEtq$hfY&8Jn@$VVM#2)6Qc34pff;@t7xQ;526ii4U8z!$z4&pc&u8$xpa0H$-F+G}DnAZ+NO=!*AS~=s9 zMikRF8hhT|PP1Ip)Hv+!fd3=FL+ss*H2u&jDb^&r?*BS=#>^KT*h)=j#He}Kq4r!q zVqXnMxud}rj#JZ|lVB~&16m=hG&LOA&*=s3_+{ZC6`Po7)e?z)aZ)C8A;Ve-oLU!X zIhLe~og@cw*lQChhocQF9uAOUKfP zWq{<-MnSJmG&98Eg^Y3$@2?XZSYm^$V32|N?a#hoAve)sZmLW$u%#NL)DVWQ#g{=F zj5-`-?hgVeo@0>TXICKChnfe^phsKw5RwETp1t1VyrVQ&?<%*T6=G0=qS0fp-dbUI zJ7Kq}GHQ21rRgknNszHc|Fdwo)7Cq$HqAN)8-W9xAT<&UVH%D*veTf;$&G-eh$9Ap z^Cm7d-|uFGUcFcNCj24_Qh=JetNhNpWi-xs@)A1~^-z0LW9g90EGK^3MYR zU*_<$08QDME{*yhdrs#AVf7ex*F2Q^Z(Y#1^L$vRcQ;Wy+tsSPrC*wH+W zXD^w1?2LJ3F1yuOgXcbk>6gPHqy7^NSN3+5waUGT-JFnXNI;#PSb(2`=N$;0NE5$=`+o)|v18ba-@OBPW2OF^C zkZiN}V5sp!?u80uH1vfVet&l3y8~gngeeF}NGdczNoSMF@dcqmA%r@>FtYujeEwq| zVMcXv?|AOVh^NB*xX~HKVwCxuX1YZ~tobd^7UBO$^_Epqg6QGS!(51=t#Nc^U6VWK zHkrAhBANMQmA85QfQ}_*wP#}Ndi786olk$1 z&+9pCyEnscTJ<30qd4T4wi7phD_UbKSJ9HE(kVH{fn8Y-8qRFcqZBLDbjJIe`On%FN!A97O!cIaw0FlcH3v$bU#f3j ze~~^oUQHk4{V6`|!QN~SH>>!`i=BNPDDeHQT+DmDoKoN4rfbt9yBhsfztf{Jcz%cO zx)?KJyQ+6Z*PZhGlgyrUuIReEsYA2H+mO|7uYXb>e*Y+sq}oq#VWWM!E9GrCU@iQ8 z`rxcGuBSO;kdG$s0Oow=jl_n0i;GaEF=%<3NgbK04fs5yHB+=a9F zTKU0=f^E8mA(F1&(81Tvb(zhAUz7K2y$vinxbqDljV*jx#y(QF*z(CinCdlX)zlkC zMF)R|Zq!d1#1lvfVLiFQfv8edsxP0LMN5O4gwq&J8WsreixMy z1hIkiZmUTb+b9zCcUa#U>!^D$_kUyEeR?QktVOoG;k%g1_ue?gR6;&7-e1A%Z3Tvb zL@PgnX*KKXxZ#g(i<>w$QluMRzfp6hznotMT*PsZjLjs!JLdI^)ZHo+TolAY!go}U z9SkU_; zgI^BV#d;dSE;N4Fg$6Hn5eJ%L7e*?c;aSKoWJ!;?`CuHU@$A?v?E5~)QIScvwb2tx z3xn1$GQL(kIvB^_k=fA0ckeOEiDds~HuDQPu050XzNMy2u8t4rSdT-3dd9K}OsbLX zweLd|mm|pvKPIGTbZF}%RnxqdhaDOu9My>YPzW>rko9h3kS2;6xLPkf$099uaTbM( zWFf=NZgsNrem`6sQ^TTzUtw|ZQ-|>cPIPM|#XWlJ61h*lmE{79 zT!QA3Yr>2U{N@4wKq;xBf%wPoA#g&rp7F);11>UdSkGwiZwJZ;?Au`JSmZL=!b@8D zP%8>7!Ar0-3>QJr8r^r=4+~!IS~mx+xsl@QSv;l4E{^cOfTxt|XMASB`I~z*Hm3e#$ec$}3FOF!xTssT)oq@qAGcw>Q$S$#oc$MgyA?(iwkp_=`D|@Se|ZgGsv! z{4(}bK|6SmhLHXCS$F5SyW&vUmE3jmlG82UByYFFesDyf3-|}}P4I#Rg$#*;M%egA zl9D@)AxZ8G)5s2bc$^Foe}(H{nV$3v&*3Hlo{Pl}Sc~H`A%kg+3PBuEAaELcb3N&L z6Rw>j8B^z|+EQ*H8!2%cvL31iPq^u!jB4uU*)jbFePtJrjBycb z&}kC#g#_FwI&dd!C`9wTK7gLGodpD}Z9Mp4zl;p1e&|y>I^ei_7(c@7NObToP^ZBE z9vV7K2-KI=dQ@`BDKgoA0K6fpm?SgM!qwES$wR>0pY5a#({-yCm?XxY7@5|Rnbh$S zs38Gr05`GxfLkiM*)N%3Y#auE$B-TpW853ic_6peb3Li}vZph?Mo!0iX@D!vp`Pc5 zV9PK!&xJuQZ^HE3=Iqneg$NWA0wwB=A9jC@Y^K}^3ySFxU_o_Z1hY3v%=nYcAY(QH zDpji3sIrktIHr~r8z`J-MNM&oP`;xHpyGN#2aX9ud2yws3ks&j)09cc^#xud!OO1Oz^(A=0L zZJ(=aX%>#<5_S|BODy*j3&n{64->C}ndeRBN@Cy3|nCXOu0-l4FLJ+wY8ci>hCf%zNa-8tm2w%lh)L_3$wVUx$aKv!92Gd67wzR|(2?4FAS;32+-7Kknb3}KJ6KxnFk&i?aR zO>3;FgKoxpzuLcbP7lBV%6jHQeo1-ui+avD^?%NQ3ASmx=?08BVtxYy=C^6T-1U2` z!M|_6JdSJgZ1xEl1yf_6w;7N*1ziC2e6h=HngyQDOm!I?RwKtuX)3JD{vk~v+H$^| z(Wrh|GTq}S2=(ncO8K{5pw-}=ANEUrH=Dai&joCEIxi0NQ74$4k{YmG9#}z-d6sNc zG{uME%J+#E4>?};AEXEHewfY!^q(flr}5!+e^`k9)A?}ji!kB93YDzz{SY!JdZP%qIcU$YhcTWNvZk5_pE1scBq1ZutVIy*l?yH#gv|Bl_iD?<*ajc z!%U6`ijK2C(w-4Grhmq~rzAg@$@b7o&XGi8(lxtZ0TY3eBkk;#QK?h712m!T!NA!= zytwl_Qeg!rsRoJ8uZ~Hr$=KYm=?1r;y#;3Ez$XA^SfwR!cmyhX`~B5sY9dYQ=mA$N z=p9p+Y-9sAEh!truzKe#4F2i}5f#6c`$P9Lcbw+SRnNYh|8 z<$PdI__MzRi_jzk$B=h7nw|n>YE2JhvMacm_BLn`0d*Tcm3J)M8+R6m;z+2Lk8a-N zJ&>1whKT}-MU$L%9-=^T0cF?HZIC?`P`M0LTR$vQQmj_@&?2P##e~}ghUY(>FD`qB zh9!jphAU{Gb_oPfpE~ZoB|tyV6h#~w91W5&NQ4PjI&px9*3&vV@L_@r9EAkzEI@!n zwKc${y94M%q+_CjkMK>5zMB%lgyrAmKsMe<@iD! z7jZC{?rf;Ql(>IpQ{^nIH7v!I=S-#md4;us`;x64zjk+sxtH$nFpq z49)l&#!~OG0Bg^}*ze$9vcdcTg9!m|5oqOT5 z5+Zi~0rf!&&tKn%fFL%cOw~cOBOWAgCo^{yRx|FR?w*Io|$S%^N68xgVOzjK!8G8>>>t;Ezv3FYfR&?D?BBvFOO0Y=e zg~rSmOW8)`ypZL}8C_*z3+y|p4oLM_P*}vT-NmKmX_p%AQ(*c^)y++KyU7|WraXwY zLTecC#MFZ+AhnXA67;r`8p=i9gS}6vX>?t>vXWiTy0B@(eW{@2iUO%y#oZE>H5U_^ z$=3bt^bmwlMEh<7UUCMz%kt)Y4sUR{bDd_W+FvI+FqQUiw;fah6*{<7#L2}e9_!(2 zxJ|4q8lnO3(cZB_s^{b#FmjpGcrA^&UNtQWtd%Q49GW%L&beO@%H`x_I&)*?vK0ST z%zr3NrDbqIuMQ^SJxXEz%NQA_05y82g$UG!08@6kP|vA*%`tAKPI`Q(uth6nLlGbK0|B2IwFvp-|)4Q^dr& z6=ijsSrM&XYeEPAKAakEicrt`l{C{DqO}AVQJ$D-T3Ur0Okc3rv;~VQ|H85CQ*m#f zoTwUvgL!3`ALTt`ASV?{a9$>mcPJRtK}TRG9O+J%T@V)YAksQ}NG2%BI>IH5Z$#rW za9=N$LWn?j@7dW>AW0Iw+-Yt(v)2VXup;6}_j(V)!v6rMDQy@*-lOD>$+5uIRwys!T$Jt#DK4Y`UBo z#oMaN0r7EeJ;WEaMp`R_KC432+yP?+Mevf| zfiT3DSY}YY`k7t|=Wr0jIS^g3Z3T!fs#iqC41~|ldW#UU0;?-ww`C#h#^E0756%-ZFU#!%zezW9+t;*dRU0X8lwA~SV*X*BuzYlC zim^Om?L<0i6AL!B$E;+@LG-yz7x$ zL5RIyz~>Mc>*9nwzoK?<7S!&}7aYj*| zwRw1l>eEDw4mPn+aI$8yLO6z6jH^|Qi|(2U1?z~eN;{6l;v9x&xs}%D&|g6;)hGSh z&CIQcfmc9mt(E|E;rVV^!+Zlag+wxJ4Ws0YMF)2%*ckqiYA3~xbqBpprUQs5hG$v0 z_L3^(4|3~v-q8dS4mU;~5yl!784r85(YsemSx1l4d05Rf9yY}<96c#H-*(y-SI*9p z(jxX?VHtE46Bw5%JPZy>_0jC5FCK+UT@MdaCL;i?38jHWmQ{6sC$|*r#=ZyMfJsXZ z$p`@ihmCwjPD1~}naBz7tBt!O{J2a7sfNk@Si*{83%aX15Oi4(pyaNw)}Y-Et65+J z)MB^sw&v_L@^dgEJ8y-1Z4gu$SaN6$w_#h3`!EfdRLFxu7#mX)BVya0(J0W6lqlGd zF(w!iY)N^tK^{SC&@9jEP?W^Y!u!oby#N0#eihf&+3WKw$GHU?<5z*YR=}@>*nXn! z#dGj0yA>8eGHWGZS-0Y(qDx6!0T0IUIuf@vkhpEYOmbBcw@}=sIjoK)kXW3Kr?90$ zrm+u=Cn=#F*4r_7o-+PV{%fe`*a0W7eYDlDo>mDMNv}Xz^I=Kdb0*NPaY-oX!&U&D2 zTkwYycuY`_H8-i!EtqBi+Z*!7z*1(u%4iqpH7imr7FSm02{qI-lvEpP8ZCvI#_Nf^ zn@gl@oUPlL$md49ZLf5 z+i-HUY+(^RPpl7n0DnqQTPgsa7#-9nZmD4PDny^_J&i}G3!jf8&wH>Fr3 z)>W{s|DFhQ#R1U)3iLt>DIv#H2%=Dq#akA=o*a8O1P~|&;U^<5fQ?&#eEsTeCL@tT1gqmR6vt>b1S-(<(^ARWf?SB^t-V& z1Sae37>ICG3{dHiA{f?o91IZl3IX;oz_{+9a; z+R@qdYSKQT-0t+!FPwCTfGGghDkSSL3}SY7rfzw5l-DFPZ%t;dnB#TzQG~Rd2;Wl9 z&Q5syh}^BfA(EU(Tr@f!ucBqqMU!6tY~Dxp2FyV3qyl$2j|p5p3mf5}UD_Y{=mwe)BxZD@E``m?V`4#SI5ncLLAqVC3% zYu)2VId+m?S_gF74KeQP_(Fd+X$%{*y{Xn5@RVwQnU221*U|Y8*|p6n?#iSc@9*H7 z>uxGbiS%i3mKipyOQIU};jM19kFJfGu3>-atCSz7YU`Fra4bx!cPujYI3^vTfCn$E zKH+U>P;(XKDVg*(*V(iEbM)aSCsJP15It7d)#QkLiu%ft&%Is4gpqC#AV`BEieVIJ zk{4iEtCUeD6)Fb5POcRSpXuC)!lzS47O8sj)5I{>WRh#>XZ@MZ(Rj-O0!o7_05Z6< zv*D-WpXvI}q0XP8jWZQOq3SnltkG2u$VkB3X+zmyFzVe_Mo5N$#0>*Sq;nu7+p6IG7E@zT`?A*hH5zoqoI}M~&wupCKo+Lw)5=yD|BV2~6`_&W8d7oJiDi2Kmf+z5ssKhKV?jnoMC zWw}IV?E4rkDp-R!qB>oc>z=hXVL%y5;qKFk>H{pa;SakczS@}A_`6d*bLrP0$khy}6aD=a%*y`=PxX()LppuYlyvjg^8j^2V z08(KCu^sK-TEewgB(AN@4oZ-|R~W}$QtHk(_#B5{jV3eKAg(`;l|^cFpZV+>IRh)f zR)E=zfos#$TC!XguqlD5LQCzs1Qtly7XM1bF{(Ag=wTQ#Jl??wC~t%uWhZ0)bBz?n zLMJQdt{#hf>>ab^pk}r_V1^ zvKLvZIV&!;NEQmUb`JGqFL=hJZ%yj*8!M}*x8uEpqS*|H>NnZ2Z|ZQmD&trIG9!3Z z_gof=t|M9*pRScU9}<;;%M+Jxs_bE=(N*3lrryUwx?^V2P#VhYCZj8A zy%F?$t=5kG@?B{i;*1+`d-wImZy;4vC-y1W1Ol&3|tGsH~hhocMVSV^hv!DKrJc6C*TUN(=wJpGQT> z-ip9;hfaJCW<4dp#ZT?JWHgOe(HOq_seJRVsQK%+zty#WZ@&Ep&u*W$x5xme`qi1x z=yDcU(%a^YuE6B-I@csK?}~Hg138oBUe~Y;usHUXxbjn&R^phC@X3}j)>q=9ebN2} zR4YXEU-G3#&vO&!Xqd6+H%Ft%@jiBB-^1;ACXOrk%2Iq^GE}fyNsBc5>vNS?_zm9z zWJR2*Mgae~%b>(`I@y@{8<=>13NnKx@3ZqJyyQy0b52DPV!yIH(4+0F#A~(xmmIzv z3vF<-c2APn0AyA^jo??MGzfA&hks$Dea+J&N*5T0y4|a4*dbt_(4=w6_%;oP-{9j`>mKxSj_S$$ud` z7ok1>4BqUPAyJ>zrnH@k&W&d}*Jn?jacGyUKi&Ov2C_Dh=|U~KZaD5=dD1hxF0L|e zC5Xt-2-RYE4-#u%J&E4nZ7~>c@fW-pU3U}^HQVUfYBH6M{0hIe-ylA&T9jMZLgs{V z+uWxF1*!efbuXBc($qudqnUe~Th$9kg|y$7Y9}1S9e@QKdSGGIN4yo)@`fSnF=a4^?Vz16@wF*zdmqHFriQ$}Zlnb0yg#}1ajK|Nn8tkl=6G^uHLRSY-=UgM+Y+C0DxUd1cxbPIs!A()q@5*#ER_XvbrOvEYNLFbE0C3 zq$F=^%D;104?uw#2Lc*I@w(8+FwA5i165Trk&VsS`eU}72*>NzcK$rY;h4mp`-?i?mHjPLvy#VC+QX1{-1V zZO#f^ZL^7V_N010yRj{Cxmm8!Rj)F6(40bh-i8hyP1K)`4qOUiNdBlurAOs)lC3hfVi6d~k=j1*fNi z55F>eI6e&@*n)8Qpel8EB*2F`-~*I910Ak@EkK8Q&|#EF2@nEQAe4}C5Ft_D1x{FL zn!qjLToS!oOL13dl?Y{P(~u+1l@bBDL5bMqw`u*Ot6p{(f!R^TZ@>8!7P|NGlS%)k zx0He@Ql19>z##~ML)s}ldSX9%fGw-gL#ysDA=`T=x#&>SZ_RyTw)CdCyW{4GHsfIuPT!$BCm>FIy-g7`n z_CXWRVVQ~pmVX! z#X%c80z3IkC#*qtj*@qW?M5{RSEGqkUlgswJ>ntR_dk1-Q_S#TZS36TRv#asA;e-c zhiq+}&7O+1fW+nuZ zW3Ej-h`ihzd5_KiTnTr`q2T}sGVn+7vrzJh1FFfIIFp@)5rB`yZ2DB@{2TV*nF%~A z+YX-k+(dNX%{Z^T7x1fG`gU#-1sfdto{k(0_qS&HSDyRW==PJdt~jvmiLT$h;s{5Y z?(BSc%D-|Cqn+VjVV4R2%CY=$<@LJ?jP2Vsc_*cf2`&Zy!*<4{@ za@;t8J-|vukR0H_smx}ROvv)3uz(v%!59>jTDGIhc4k^85EdNINL;Y@m@xuEe1E05 zlmNYZxo)9fSJP&*6h3vPU zo`!3ue=!5sZvIH2>*&%SaX-K{=f=y!d#3;BT=+(z?0D~!c8v0;r?8C^$wdy7u>spS z_gyExFRqmHh(n0)kN0kq^C*8WQUCPKQ4Tx3+Mj)Z_J(nAW4Vu(uvLab=6!)9X1V)3 zbTIJ7P{LvVM(hgolJ`DJS=wylbD=5maiT5?Yy;74;Ggk!I^`Z04X4jyrOcBfW|UrV zaa2i@uY+_HL0ZEnmdko>UEF7uUE-q2Xn!4~Dvkd3`_C(~w@agsMZdWtnmiKgqrkhx zkpC^Jdx(W3uZZxf#1GS0D#s0DkSj*^x*RrCq_O9?G|45Y%+y{C zFX|WzDbX0B?8NRynZ_ehNAzd3zliZV-6l9lYYR?=d2)I*SMT?)Mr@(bpy*=QVa}Dq zfY*&lBS8{{sx87D7fC4ZZ8M<>w zwo4QGMCqN{F?1CUgt~Kg55Q`y-iF#>&Z@?-Cy7)f-}PxLxDhKdSFiUtnVIjTY&bS9 z?lF#tC6-(Uh6$N}IPQeZ^_8Walf0)51HP*J{d#4yUd}PHq$Mk|l3L2K6wVk%LJ$i@ z<>o~PF9!%XfeR01b@I{z7**bu>CDk|%BL8~a-cx>bsF6gMsowlZa6*yS5*H@ycq;Q zn&h(Iecbp|MeYW@$~tmg(mBJcI&>$M#7=p0E0godXU$tOTc!e&{gQhyhRl3RRI19I ztTPab#hak6B8Wv^%AWjwKAzV`nNK<1RRepT&jfLFmhy(k!;93NHP zKg-OmbddQpgqu4E=aMHtE|P$N?>G7h_D$sv;1t7bUoIr#Zyte&^YUhUKz#Mq4Slh3^*LFF84Ov#Iie|!xe6iSM2JQ634EN@rlB1T60?q3eA?CzTkZ*qiUqJGr{UN7vCJr_W5%bY?C{CjHqheq1?mg(jRg5KJ zN6tGeGM^ou<@iPDeEq>s_emE72#1oGY79(ZIE!f=4Gf*uv|dOBdc|r)`=I=s9B-rzb{Xx$cg8`^W`x z6?IzOV6ZFvT27$Q36dL1be<#)t zd%el~mEzBnBj#zttKvN-`~4Yn3T~?~dFirf(_RybI$FKgG@AX#y#(s8Wr(6O3g4?q z6*sL+7k3URb@uY>hS{>&c+>YtujHlmtF=ziK_wudtq8`w$sU~DsOOsv#7?7fW`r~5 zWH>}@$dT1r+4_F1e00!YMy~Y=UBg@d3YbLwJf|H7(FBvxbuA;{<#uni9tylXs3g=F z#nQV*VuyEV0=J(AdT4mc#MMO;VZWPB=~dz z*mph@w~RM&btvTuacMDKjDS^`Az#{Pp=-pvx~Cj~$VnpATHqr&Ea4^BF63l8ZDjIq z<07C3TP$@;a=m6vB`cd)q-A5G zzH_DC9UT-<*%HK3*U*Vp!0Eu<;ppHQm;qU54xPTtqMdVdod?wdlo#ije_UEJ8D4y6 zm?8!{IOI%r#lxm7|AcXXm*?-EFlG5ZZFvWV*(JQf+bP3r6*o~XbOQa@w8E&Lvs-d#%H%nC( z>+T(O+pK{W9d5Ih?4W5WdUfe|V3|EahHhrFC?jKBkXZ`<*zhjmBSMt4Z+wEBX1$f% z{yDns&xMWzeIkrqpod)T!;n5iK1vRoXza2k{Qur=jHZ$Jf_O`O5oV7CMws;kx5(cA zfEa81jqJ z8;l4TYuIl5)RAA4|KlEhvV**q0AoghEOTz$VAdWEP48~)@G!xYHxckfd$(8|T8*k$ zIKN@06P*-)wdpi$hg`Q21EoYkGzyYc#`JgvoBku3d9%Qhed3pz?sXgGwGbx5^VX({ zlkh!urt81u5HC5}J&&v2Llqx7l0Ew1G%Wn!ju}{Z^CE|VZ{WrZ3L zU&Xd3u6up_o9uiz?%iW{nlSJJ*)NK`Fz7b6Z*$yBLL0Qgw3$^z@o>zw9XG#&Vti(; zP2Eb=|0=N7N*)if(6LGxY%M2+fr5ZB&b4BkUzap@uB(835}pO`WTC4JKa58v{K_v8 zDWC;c^+;*F!K!kc3)Ug6h0$004NnOb;I#xUNu3By z?M@_E)5b*g3tCLEG}RAfAFr`@dz&hsX8-#(ht9~hjZUs1%!<24g8b>mTgLQS*wNbV ze{_?@bG<%Hi(O~;9D^G8I1vSTK<~(VEj1IeI|V0!ofLU}bDIK~jT13W3R;mlEfAkE2X^+< zU4cFEWB3PN1-*lyI}LB#-lKt#>(6g}%k5%$=a1(8;wi1|?@sV@QiYm)C4c7S_ zvx0`3yfKs)@eQbyDTecdQVXXTZ;^m&&VGDn;NiI!4-eR%+ro_X8G}S;t`IdI1OkTV z02{7QxMZ)hP)&Z>E5CN3bcBLi9Xw#Tgkiph0%E$;7Rm8OH;?88-dMI`(uQA?dn3bl z93<&4EpNm+cVzql-)#;BV+L&7D_F5$Cg9Spq<_^B(&rt{HEc;%zYzCJN@e}YFSrZz zAtXvGorsJk0xTQWDTAae(Tz~!!+dp7ib@j3B5fI6;wW1{UJ{q({it!x7IDnxY>sFE z!pKai2sI^PA^kbXHcc?JEsib%=Y7UBwnXGin)48GBepZ3mp{xAO6XcPc<^2!rCXEL zlW`T*eKf8}1k<-5m6LqcSUGeN;+SN6JNu^b0Lypdzw9-|prrfV08V2-7Ql;g_r5xH z>z#qZoU$Twxs`??CX`%bc0_YmV9W+Ru;d~sR54Qs4Z`uCmjnIyCapo->M^Ih-MLX7 z3M^l8i1V(if4v*4cVK^LX5P-$`jo$ljcJSiEsQ8?uu!5feh6nu8m8?cU1Sh8%6|pp zjXVF9w%jl7vkiCxfrEU~{@+0l$=pn(ULtikkuos*2qjX&v^-PDPKAV=extkmn>u}1PzagMoPc7ae$RKk4=nDHH;8~uj6%~_TLGDAG5 z^G@U@tFt&}gn$mLBr4{5oiy^u{O1&wQHoZ<4hSb#P>C%i6=i%%Ud0!4{4-ml-r@KX zeAT_%iO4wIV>z)Y3oW%E47s7Ivg;oLrO78tDJm$2kg6cOsQji)3Suf1bX9hZc$}L) zLW*8(84jl9awI9gYZ(qJB@EQ+aPgQ$^b=#0B4_W~=&CvpnZ{{v3%lrpis2@KR*uPn zK%&rJM<|E@MpvnP7Do_O6gh%VXO*5tccV&zvh0Y;$92v_0+voeTAUibjFFmdDJotX zS1v*;rh`j%({#x-FWW)@ZuY(d0Rz;Ig45>&2^9iwAZlPfbbR#CcdN%?C**X1mZFMq zSQ3?NI?Np>A#g-P=HA#-xILe<;GvIdvHW?@ai@qNqUhjv`C_Xl&)kY8|64t}K-NvU zXwLn>MabAr=oIwZxvSN!pnyt>W71L4*i~-w?&I9Cl!|}$(SWxuuWYat)_H~Ael2dI z`d4w3%`lQ^k(~nj_&e~|mQaUq?ElOc0k zBP`927Vl^{IMQP^);u^EYq^c>5Q9W#z>T2u?!)m(IYy|SJwOBu+XFxpYlt^#UVdN(V)h1+Ug*UXN`5$qT*~_+ zDU^&qk~+<-dxF}DB+=uK6m?h#lM7!`SRUTu>__FFD8>`1VbzF8@J2Rt|2S8rQTdLJ z6#|M^7OmznuD))h(qE!_XP-gwH~yV1!fu@Dk6c9){E^%9jUIwXXZZi@X;Vr4_ulH- zugMi3*BeGQc&LB#u(Vu&l z=Qi*>Q(iK+X*yVb{%2tMJL4xFiNgb$+Mi=jul+8H7o(7wiE*W0*3I_3=if+saem8S z^_SVNU3f~-s5^%;og4kyFR^*zf=lEcRd5K^^J&Kzj_}d@3~wS~fwTEIqz=~)p@T*R z%#@#OC+;NNijlItzgH50&>zn1p)Sl}Hk7^!2#=s7_<2Z5+hEC;N7X2&f{y5C%d% zz)QPj{j`jxi$Aqiq|7NI_1A6c{uhnUZKd}nZg5-LO#I7OxG&OBP7aKMfT^~KUZB23-cgsOrxHE7gNc!teSuPvX`5;nj@V$6anR&dOm9FB+z zSo6B%MvuWo*F6tm^meVn5m*N*^9?cZ$HL>55#)LQ52P(6HxX$z(%4R3lD*&ClJp)< zu(jrba9C|&O9WNA_C&_AYZCrnOTED32fDH70mh-za-O!s1VXIBT%+sO!X=3x>$)A+ zg~BNE)8P1}$;Nh!3+LvjemU*mIC;RLlV5{{2mX!Zs;TwqbBWQDcx?f>*g^JsK&Gx{>I>Ixqop| z!Q!RvbAU$e1R73HxVdjyK*%8%DS|I@X2MN;JXytfi*lFB@0c7xukU}GsOA4y5)ps- z-=UAeU%VcDyc=wgBOwc&;0M=Pc`s`%yNp}lO)(MnauNd{R#CEAa5Gjh(b!H%(gfUM zYsN%v!f%6HC_XiQ;$hb5Od&Srn$t_I1NR}ZdZ?_p$db1!E1q!qFanFMxAt+x zkp|wT>PbqRr~T{44}^Jcz`O&RoaLyGAlyw`1muPk8${<6IR# z`Zafd_WO|sSNLWBMclF-Zrx6(r2zn&>J>3>@sh|9G&4LCC9rUr_ktn}9RRp^W5zA} ze*XR065CnO&|V=rQ2hOya*}8iY+E8rkW*rX9R9k5t&D(MlOGygw^|I$uZ;*b+K=ls z8n5yWt(3pUp%LP~mUGPwVj)UQ^?Z$-Qkh1sqh+aJogMZ95^NpvJL;9O{U4d0`qq2?kscytKU|rzb}*k#0zHVyVLTa*0TD6M*-WvLbjlM6If~~^IyokzGwnh#R0wqq3%IaPqe=Sh8TCb zP!Tjq9q7b?_3UsSgRRkPmS+?bpSc?E#llh?1aZrW`VxEJEa!L_jiaMvXLE_SE(4#G zEmcGhsmv0La}46ZeKC6r5#?ke zN3Q1`I4s5!m6bHZ){)(WyUtxR?x}0de!L_RMx6Im-bz!X4o}U~Q&Stsm?#{0tph!8 zL@dnkU+e@*Jv5J3-_L+M52&6P}55 zphx1TsNVvSGa396!lwei1nAVmm2hS{vre-n_ot7K$Pu02MU5Z`S2IWri+XqKy_a!} zSa>Y6E2yFKl*?2AAp14;0>t-njqn1eXUXu5X)cG>Rc`^_8uskyI<}m*(pxGutfM&x zE(NR<0kc=Co}pG2ZZ_84EhSKwS1=MBKv*ESgsu+!4+J#rHu$Gx%=4VQCHRY^&+|$L zd-m*Q1M6fl3#hI>tO%yw?Io&eV#l)0YtC-|egJzB=Y)VkaNREwZ_m{; zM0o*D3UzKZR%A@i@|FJK$9Msrq(8?NU%nfLH!VI1ekabe1iv}D_2L%qUJ6An;%G07 zbRlDsk_wrRRMP(_zr?8E8f^e!f}fb;eLzAJ!J+{VQ{eQ@NANah>v);cC2_uv2 zJn<}(Oshsu{F!IenYzw3YqsTKPvFT&A#M66Y{=qLHDgt?S+=U31Vgl6pMFc{sOub6 zbi-AJQC;h*qnfm|33Nb8p$#x-VS{}ipis)71qway&vo73{3&>T$DMP2>hHW>&^+IN z_n+&&{@>Si-8zxFh6=cB_UA~=!(&6HP&+xZ_&X>4Ke+ll3ph{CW0_O42A{69l0KG^ zr>sQwYVkID^~P4Yu#n6YbV>fpcE^GqSvQ3RD@extnvkhmT#kgmr=Z9l#xIv&tHwzZ zPL=p=50c|1=BFZY<|+fe+}b&LiydZMbvp2{imrQfTJp8pAhekk7hj1g z8%V8$X{?AlPex;8cfG17gGWv>`@Y;R!ifY+po7Dk*H#93ak<`7KjMuoh=*?7Ai#ex z$v_-+AdKKwZw#p^&@e+KYSFpkIttt&K@C;+68k;|nY(qhk(wBkcwU9fv53 z$9U}t@lT}7QnCyv8ymAI%RQHAl4{f!b8%GTVM!+I59!k4dAEq#~}~Q^jcTZuexb zYpG;z*W51BnkET+{=Vn1LZZ8p*R-cVKLTEprUNDL(*O%x`*^&mUeMk@Bo{sEPA050)!MHK$muRYZm4M%aU+BduN|ul5IdJNFJGZp+!(^%2i}4YV3m z9B^7;P^9A$%+@i!6UrjD;w%m4CMsm+Be9$ zN}o^>*(PqR7){sY;1%vyIP3P1)=I z0WDLrm+$h6at&v1eKZ=Q;T|I#Z8QY#g|Yz8!*>U0TifE#o`P`Mn!S&Z2BT)_8J1QF zo3J={4`HB~5037!e7umoLxu%(^0|A2WOZ0<UBiniy}B*aywzF zAXb{(`nOtbbSFvsLaDmJf%DDsBPjY#5BU7Zb~*7N3!nf|G`}H9o2o}&RANlh*;6!Abf91-m=#r;HXim zGze5(^>^iOW8BQWqr8tlDebdRP303Ts-~pSaUWBVAHhO+ami4+mz|7o}ig@ zab=)W(;Q9Rcsw+rN!B5Og$~TKVMHsOkJ}{V4E<|-Jv7gN{LusXrITxG%yqt@zx`CD zG=Wz-9xhsbC3)%Ax|benj#0<%)Kxc^pOr%fB|`ZNI(7NqWf@&w{DABDGk^K7tYaOhQ%%PtjJEM z@H-tUJXL!qA_<3z`cXzJe^d&@bVby;U2NT;5ym_8n8TdgNj%5NIj+F#wZBcp?*a*z z)-M5L6(g^mRfuj95Qsqzx}ywAtG2J=2fKx7KP zeDub#?0n0(vYlIzpNyMEgV*z-90KhlPn^wDPI}P9*_jf|qoGqe7cPCbkz3*k7RejN ztQCTd41qxy{p3G63qhJP(Y5*@+Ah$ecdvQ}wzeJeYw9nZI3(=Aw0!hHa$442X}f`E zr>0ceZu5(dbL8+buK7Y!^uuWgmBYeX&M7G;xhK=T_o36) zL+x?xBSd44se#RE9VgvZ{TaL+*1vt2pC5{^tr=h<1@D+hBTbK9HG?j6n`_^r$23oO z>~q^8IdHb*CPpr4K4=FnP^q!y?b1m;Vkhx#Rxi=vLWtHd_e*J`2I{Mrx=wJ5c5<`e z$3+qr1qJnSW7I%RUI7PK&EDPKyNeCRChoUOFdG(`z;-y zqryVX_Y$L_r6!0fq3Be=o zu8FJX19SCXVaHv;qwZzSTe%_c?xsRy%m7AnH_NWYFKyNYa7*o4L=^y>s9g&!OzU`O zaxpp#UTl2}UZo?g;+n5Ce7;99Gx6@}tKy6ZyvyE9Tzfr)?p)UP*;zCc_Q>+aqIX4vaa@co%vN$JWD)^+kq z&ixx18-Dr^tIN4bYxyFO616t{ASovyops?KS__7-LX`uv`fCx;j4lQlvkKI=le3woggkSh6ttDF&!H|s^Kff#x8d$DbLB>b; z!&N_j^2-R%sAO z6MyhjX|o{R$uCfGi+(z$b<)mW)aet2%mhr{%$>g9PeF} zs2{?&BV&uiGTU2r{Mpd(syQTTgFZLfP0TwnZWG-m&)dl2{_`6pd^fwgBeZdroeFao z+$B>uXbT$=Z0HPZG0zfSi3Bp(vYUWsMi37I-sSU@oX;;#j>~vV49jk`VYCf5y2L)Q9AsHpXko;lUHnNnn$OZ?(@QHnecrmUlJ7x| zFCIeB6}|>XbYiTIBl_`}Jx5Ub7#|4X^2(haiylDTU35_SF|IIX&)gxUkN zo$xa^L8ThznD63-Fs&`~9SSycAv`No7#0^Y<87G-rG?-`kp`lfOllzcPV$9NLk<}M zJ&7BjCPVWwb0OO72O)13k+2FuBxZQo%q0-?VGWpXH>j6*HU$renjE^3 ztGMufe43j#@zYtm-i(IUab12Gx6CwttGQH|MidQwpWh5u5Qf8S(s09p+uXGg#w``4 zA+FNtc$`#B$%jV>qS;hje##kbLdSjL#Su<12>t zeZZ{OEItW?MN(*dlGG(fphwSgehuq4^?q{=&tc1Vm+X8_)Q%=@_$0fWCto>B@!nO+ z9U!QO5`w(YiONmx63ED=C?gFF7{DIksldq3K7R6&bQirB6c}+@K zzKx?BBMWTG)vn+SLKjkjd|nj2>rpV6nrI?y=J?9SY)Qp@RIBTOd zArA}e9Lenz(oKpvylmTzk8$vL4$I3~+jcRXX*^9*NZ32Y)8Y*pCrOJ%tee2oE;@xv zRV>X-l=5b69tCEk83b>j^NDGU%lbFQr5U)(#_bNsP>V2)acN*;T+T1Vy?((8V@&8Z zCt?mj9yJauCN^rE(*#z@&a8I66gVOypU;Oo#L=J>t(kX>Zse>hG8WU8+b>q~9JI5E z_h+yv(Usd4qOL1vC4c?rkdK{7>`ZE7J#VDk$$ZANRN5{`XKyv)$S)4soins6XQ8Eu z98m4tr$aiB@n7*J;Sl?f#msIZ1<4h7EZDh>fg2NnpNMf{{=}8fGpOW8G$i+=nF(V` z66&0p!6jqms>SE$<^sm8*7+SM?OXWO-uoPk=v!`fx6Qrdd{e7f6s)6Ro}&zEdEGFA zz4maC@+Q)oxmuKut1EqB%_9b171&f-Gjo!W!l_8Htlo!SCvJuYEJl@NZxSOi?&FNe zm<%c<>jdLzrHOpzc7`p{S2EIxY(yw^!kdgckTZ75GOC?MxxkdpZ2V=Rjaiy0AJSCN z(?uzMBDUM&Y-n8ji|B#V*^r8Q0;VZz)!5Jgn>K_F+@#yl10OEW;w!M~MztFqC+Xsh zkCIFOMDw%f?zN^03on;%e{xc9oAP+Su1|5k&!Sz19?NSu{&_#4r{{Z_-jH9)<|y{RtZs8A%EvWHr4&WsLoUbo<403)afA!Z zM~#LnO+OqAk1u%xSIJ-ep*aNfTXV*jT*XfuU>XJjTwi8~+&x>=c_qoHP*`jf-Y8SCk(3NM*E9-^QIa(MGkNkGra( zjk1xZYgRZp043aE3$FwNlGPsL3P^9|4|5ylPxjWGVmMdMa*W_^VwY^l@mqYi5Ldpb zV*5#c**@Kim*Ab2Imznh(Qs&05Toz~8UR03|6&J!VKJ|PB;ZQY{|DkSCWV9}ra__h zs9(MoxNw|jD$Kti1&FSYbgpsXlHM(AFT&eqlj$IJnGmQPsd@9LxNTBR1B+W;W@NZ_ zCulGCKx7VWSI5c&RLTqX&eWcV>(`P1<*%0~pF7bzn}!{`^)N*s^J$zJ4Cv1kmCRta zfjVILtA|7zg0k?W*lVgx$z9CTh~tY82aBSnnWHF!R4Xp%9!e3ya~4ZSW*o-)qw#X` zT#OcQCU6{jbST+J_0+l2Y`6c!G2vrO3C*yN$27AJ+F#W~qnf&1#vo)8|)x84k!_SKUbalwZVV z6tDV&N{ZeIu^oo2ds9AMTt)OQc&(3cY?%whIbQt18n^xeT?nfIO2BFppaGo&9x84B=YYv^ZC!=w z=3YW+i}m=B@YZz+mU4&g<;Z|)ISx-EpUuTO-4p6*lL6azF(w8x?1)&%aXOWd0ohBJ z<=-Sp-}F~kRPjN?7oB{a_u-!G`gNR1?ph-B*hvH0x6iT1F#>7HZ9a4JgRSH~>qQIC zOH_)|H-oRS)A4mv*SfiuT9tcqsw=ye+-p`e03YTBA~+2GY1C9|%@CJ3Ud(}_rr@I< zs(guhP*u&`gP^n{>bwKe?uUL@eM}ckW`BEI{4!(H6lb$XnUC6Y{tD|8P8*p9 zsg|K`^@CEE1dD(X`DGZC=p~x7*?;}~2+GwX#S% z&J}~w0E2*$`(p!_k`_S+G~3I~5Rc_KlDfhK@63Z*Lld-t7ZrOSbmU~;*PWkqs4gY-?A@vvKUVgrW)hqED#KLkdkf*$X2qS?@)&$}ubV}!Y3DJ8q=SLTcTztHShIly_G7ydV|;yv>ggUY6T8Xw zb=riuX+XmQeud_p>JB2-psxBO2c;XZ3nMHZQ-^T*2oCJ9qFHL@QyDQi+csglmGmwl zN@jP)h$S%Q-d!x7$ZH4BirS2w&<|$C0&n5x6l}u;C+A|!6bN?f17SeG4(E_)Kh_UV z_I6q^0p?(4lKXVn@q>#vu-y7eK7`!@>ggqTu0g=b-h7jDaOAjd#g6;6e?efGk9Be* zwEUP^SvDT~;?G3X#_s4^^HU1`iv(3Mw4w&>EVm=NNoyh*Z!?)keG_98I^9WyTMQ&r zQaQzaqqRtZOOzp0K5merK3YT6pm#dq36lAE8Qbj7xYz-TYGbjZQ3y~;Nyq57Q)J&2 zWCW+e8lz8MT`f`{*9Zr1pYM%}Ha{;#&(TlqvIs}?&NaIgbDtl4rrK>+ARxRjj?x}q z-(X)=*b0TJg(^~L6zm<_E20w|$t~@x`YW7-Q!&49Ye* zlW+DNy6gIhY;v1KU}of%wFFO-89*_Q#WLL>7e?TI&9Axx{hVTnRB#RtG6Q8ptBW z7u&&OL)ox95T=o(W5JZ35prb|mRg0=vRL#&QF;ez&{Age9*mMyMg2av})nAalt*n${yNMsIH zm}cU;zfuoWZ~^S|HP3gYbd7yF@oCoIzSq=KvW|^|htRLM_D#+f!otfGM}G+;wy|_X z_jcs&M5iaVdzby1chD7QHn6Ghaj!zyoEe~kM;kTS^1|PPai&qKx) z=4t_~^@RmDX;#?A`hLw96hUme$N?mg3HMOe5XX=@1I0KMD2UwL@tMqVUkFliai!B! z+~FdjD+Wq5p=-F%!~)x$&0hDz77#Q*)6K>=Is4n(oa5XPk7|!bknHKK0=DFrC4Z$& zY9>*B-=miaah(XZQ}>`mX`F0lXPytZt8iP_BhWA(i9huU^3md*aCL&8xmVzW7WncI zr(+rO+wgx#zg+tJreE5K-%!82k-4~7MZXN{WY8}yPCYk;K`(RrTMA1xfX2K6&6H4BMlDIB>{+bzOMl$qXpVR58b@<@o{MZUN2D{v&SBGBNF z?+6DJFqMWC-^zPX1QwMoerNKDYgm1=FegnUj1<$CgI!r2bh{j9rHN$AbvVv<)HADZ z%GuzR3VjF3alXh)nzZ43xl4#{P7B{#;z1hf5Oxn?R2mlhk($M*k(1tvY}ea8le%`# zB;^N))VN=K(ro3hA317TnqCX7PVGmQJDy<(8$~FJwEhaD7`_b?o&K%Iy?dx!xz^b6 z|715)11)blW^7yw=Hf?cjwfaWH7CG7==7iNpHy*zasx~fzAm4p0t8F%=+@62Z)uEQ zZ`jfe=q`LKt!%0#yD=zgNL9WtCSe`D;ZM{{nH}0&2$B0QF3dKsYw?0U)y)gykxK;67c0Y%AR}KbO96d zqh^~pTNLE$dC8T8f3)|j*ntVoGUvJOY%6~WH+A4qlMEyc;|9Wl^UAH=)dt z&e_pXx`BEdVx`wpfCII3TSFgztSC#u`>M#<<2Tk>6#>hMMZMxov+Vi}{3t}D=ib&Z zpLeK+s=5N%p+X(-VQamh<433`J9K!K8xcO?pl0Byj$!@n5j57QwGhGq=Jn$s5D?Umbxq+37OSt~xE z+qBa|jVRsU#5MC-ITi52!+OSLU^_9vD-*mDba%VHk857Ig+H_G#yCGn?=r+t+f_73 z#Ri@BtND^KJ>PO@$+Es=jAu=hNtOZh77U;`4{4HeuAJL$ym_iV(1Q<>gnWwrpWvVc z&RaT8>xTx^3-_na@206rJHbgOaz}?brkXo7R(vMET)D~|UHt5Tos^Zd{+?`L`c}fM zGKA|pCRT)?sL8+~p!Zrgo6B_19^>cBKRTnF6HZW(x z!PLAga8knnELhiYqkgLv1~+>6(WH~2s64UFkm<8OtU^R2HXkeBM|QgEJu{S!f|7hb zfHS|4J4XEo=BrT`v+LL5{^KF12JWSrKt~l%TC>+(06PM{je3VvC#ACQ%3dPyH40mk zT480P(QIHef^@gK?STYvOa!MGA9osm$14f|OO&VxX+$kb->=jaN4!crwCu8;$i#4x zi1w&v#7epK%|G}U{y}gVL=zDywY+J{{^Uu0yp+^+d8*2bY3XuSjWNweHF$#h1x#eI zoiz?RPzk8Ouy3XrtzdWZ%nDyYCQv3|o@MN38RxghUl`z>_G?CEnW|jC<%377CtFo7 zjqUND_&a93b}r^vt=6=0>&bh#IYLlD8iIJYI#^8yt#bJZpVz$)WtC}bVg+F-qg%&s zw31U`_N5~W0_V8yr+%14p^N5i&PvvOtj1QU*nGX;h_7I%;AdrkQy%w1e4EjaGWS)u z$d)U<*;q()Pd>LR%@buYUTcf?Wp4%SY^6Q|cdX*?3j!`jqvsb>e!;#zH<~CJjaw6E3j8U9;29s}v zsm@^8oGO%(b%>vUsR+|M3gx>?bvX#N0cL zx$`5`_vx`B31ao#V5{}y=$st+7vzdYDElhNlGYe18bUATu9z%wgM(T&fP!zem5jfOwVUcEuf;q{_ge?frnPwi@3aucoh3@BA`Je8z{7)<1Lls8qQ<#nMRY{#A zG@JZ0cCreVh{>|)2Zmk!XI6^IG;&G>Xmbz!lKm5@*GM(ka4{SIw06Zh8k$mfJi{E| zIY3}LnE7Y+SD%l7BWgvQiE(+^2-;~Lk&*C zCyCQQ14>TF)W6NhuZ>+f(C!#Rhsl{GU*VEQ`oHlJ(yj^lI0SoMp649N!|lCyE>yzp zwI+yodlMgP58uB-GZT<2$5pt$ZZUVi&vU%x;Z1Bu@dM}83{ZzeHl=DPGah5y$xX6y zkSewVWO|U`ba_J`a56@N$h$kP#aB!@BB0gNmZMt%Y##zc(4uAW^CmEmN;PC1@~46A zt{Yqn#xcHF-*yT37|Vf=$}XvlFXnqV5`v)fJkLXV<@7E+R}#rpz<#TYsh|uH0dC2yTmQvWmtRh4|8S zS4$$+;nU{OYBrmzOfFrH;(flP5)^PFFADEnXP82LS5xS^(RG!3lR4T@MOP{R@$+I5 zsQjwT&bXKOf>5WF^^k6S=9+veC}QPqzNEe6xDnu6M3!+eB3$c93-K=rz;7QVJ z|B{EP#K-SD# zt`HqfnHIA^NilN{+amsLeEoNs*5a&)V0ZQ0D2TF39MZ;*rZgM{9vgyD_@({FQDj>E z1}lNJM4XQOQ_js$n6r?_mVk0$&k)FI%dV;zj_Z7%v|Z_j1AF*za7W{n4fR)T<>OZ9 zsxBWLb%h`;!z$e!IA+)X11=p^#adZHYk?gxt0w4sZ;lG^tMHVJ(Bga#X571!J4+;X zHbsY*@w=AIo$HNXGFY(aPKK?;t)JAaVLv+5*O<@z`#^~xN9}Up^xkFcpfF`T8NaAfn2{Sb%*7{`N2h}*HCD}>4YpsP7pvv1s8hK+fDa|e<@0!KAzN7a zUpoUK*058X;_B<@3^Wj-UHF3$TALwqAC@ZUG8M>spwBmAYYxer-A!k@ID53fb2jh4}i8bK*`VTSXikKkIc z4e_?pC-w8&^i?Q*MnAQ`n~&}$HjT}}30>ehLyDxSV$I6n|Ml;fMCPbCjNN9;iNLb z8CrUVcS#;{-kI-T2wq&>Xb*>u`PH~T+n_&Z&+_-{x!~;3XlOP+uv0>?f`>TyzWP?4 zlngnNCt!mi3WR6=l0V|RPPH3v7+~7UDI~HX`G^0}`+^AXYar?Sf57`b!?x&HDHaYA zF(Bx(v8{2lJr0xHsK!rQ#8Eq^b$4A7SD(jtPFvXcX-nh7+9lx5(m<1bv~O=!5^Azp zVFT^a6W`LRL(3a?_~@p9-Zgb8c!eJ5wT51jM=upnENM{fN5`V=Yj}&jg~+1q7ub?# z;}-!8+q!FchD)J|E4n5%4ixgqk;yy8LxfWm@AdIhx=gXc@Cyxie`%vuiDz0~e0TUt zP&El)YUkjljjR0IKhXi^#rU@K#CD3Fl%n{40kbR3ma~3*svcO{g@YD1o=>flXu%`A zUWklPlS`Q)ks7+C84_8T@UwleJka66%2(aU@vYn{ZPy@3nd1~Y0vy##T-aLEY-E2J2}9M4=iYirAkv#}^kI%_0~ETw}yyc%HHPM7+M1_nL<;=ilsg zd3jYWNyy1siMxqdfrtL*j?r}uZ+d!7@akakJyP71z4dwEYc5oZW1Yu~f-vd2!a2(Q zfJvGQo7#0`zuf~=6E2IrI^?)dzeprC-}(}@v$CPPb@o6Pg2 z(|fO{Sb?=8x-+G|Dj_oB^R!h0BbrGlbK1g3DMRLwDa>Zf?vC?spv2_TUlD<=)*W0P z9v()>p#}gbxAMn`>`}RgQbJRBq=4K`VVMd`2LbYr&j zoa=IIn_}U~HUQ2sgbYKRC$L#?OB0=JH@@Ix+O+jAz&dU{pP~*GJ|@(U9%)&GWW#DT zqe2oa$b_BeUgA>T*(arvvbho@h^=dzjC7qK|gpa0>9XI_t@O;3M* z5!?LJufMQ#=Jj$$GX42^m=n{lf3`UD`o!fkug^PU{C~J)=JQdYJ-xkipz-O~U;W6; z>+coLxL!2#`p_@Wy#7Mx%$Guk@`^(cHlWs5}n^3#rw z)xk6bplu-GCY&BiAWpP%iLOgO+QHex+gtc8qd=Ah)m8HxV6&n)qu9BMl2_zM^JYz$ zQhq@Lv_61~rt~3-jpHaDi6+{K`{JQ|5NzKUGKn74j zIg(mgf}I-7XMYto zs8N!7VBJ>OL}0r3Vvn>%r`UGifNHUpSJ=`G?MKg&GBm&~;xww0_kqbs0bL{gHEQxa z)c6|OIN26`4~>#!O@TYR{*M4rX}f+C(L~`Jez555ob1#n;$>u&u;uhJ5dcUAA53mP z!x=>tj#ue3yb1~hD1Zj&7agi;LN$@&c{uX3$}F86t&J%8P3Rzu?lOkgM-x{t8Z~v* zKbLkI^aqCosGiJD2f^khuFx2@q)i+)%b#WgT6W#%v?Cu7;+zGx)Y@bx;ZwCvctm9P$6;{-EXKuXll5QPH(THfqZl*EGa^Vcu#Fw4GCQN+~ES1D{~Jg zmn}aIt`xq0>Z5r!tZhZ(%1yb=>KRYC6Rqi!)PCV!A#xO;@7 zXCWD!k{+ueid#O76=E&=U?|O? zt2{?CEYtzK9(xINR&y8f-D;?*!DyGu=;Zqy(JR#3;WP-vKa{pxYdUD`2&G{RF-NPD z71D_-HmeoLPh(hfA4?-tB*q1Hp|9GaC!m)wcok;Tdz|Suq}P~4yaftkTjm8Zn?N>U zz&2P9PJ%?lOJN%7ZF0QYr1bF?qkWV0f!=t$RY@(Yb>?^(C?hP{pu>o#4Yw+Hl31d+ za86ZZ<4vbg;VTy5t+8qB%C7&MPs6zzIRy+8w~% z-@ob!!o1Z+`5`3NBq10K#Kcqfy|IpW+M<`H(Zbz??inp?7Z5rh6PSP&EsH)kB0j1Kg$&fv}ehBHNC0WAPMiv4JVlXbTHX;j1x~P_;&Gy-W z*HeUM0YyAF9U3E!VW3ygFTgK-&oWKY!{F}fWGU)AtE46t7*VVtVeFXG9;l^HUPlaC zm}u0gwfST={#(bQGl)T-{y@ZVEzOClY`k_*;1vBT)(t}AuFioAj;Y=JlGw6Y>zBizU>#)XV(t~=Qqz3~( z=%|iF57004a1Qif;|)rZev2M{Z>iIQp~V~1!idwth-krL_FAD&=pd!f55QEM4o*zc z!JrA!%JMhJ8ky3c%Ae=bK~R309E;@p7f5BLy?99jd%oWtMi%6w=cTgld1?9Z(qi=I{;rG$bD(;kiyJ2DJ9c`$sxU*@Toeqpjbk}*oF@Dt; zXPB3+YKV58RDK7?!q)!L;P=pE+f0+iAI@zZz5YpbknhRwtT?&SqMD$gZa>EJ0_7ud zI=7dW;%6C3xHC+c^^*Rj_BZEoROD* za^%UMV4vqaG7vb=Acs*09iwVMs%pNySE_<0gvi~`n0*VZ6l*kM9KFX&8#N(V2Q{$9 z_{EBHYCJ+52jJ}eSnEKiBcepmOYtXPjpF;%7Kzx~tGN$X89bTEUVDvV%Q(m9+P_5g zNp5Jxa*g#|(fUeXCZW^%*g32}LEELkJbiHhR<)5U8#w62v(zf?K;APRq7en&KDl2# zgTEi1KZ(!!7XGe|!DkyO%v4>*`BOw{gowZ#d6=3-bobDUzjryiYv-VqsKfXYz_3%F zlY*%M3k($-H3ZVM#)~@MxiAhQpD&|4l9>=wXTS;9?Y&fbD~ufpxD=fv_CJA_ zXbKK;iW3Dt5?mj}k>lj<1rm8He@cps0ip;*wriQe&G?9qMNs4Ne&c@>GUci5FxZ6+QVG$>Fr4FQJKU1_8Kg@=(4 zr}>n2>N>M4UHgC&mj*2G0kjAUIMZ#MGv{XZf1b&6t^aFRJy9IIMeo;?whPg?((LP5 zHQgPw5mhP=o~w*Qe!-9+o*mkON_vJ-ppvkJrHfa6B>&<3<@sOAUy=XlFSW-XJGtBR zin+t(pJ&yQ+7|i4v;@koy3B0V30eiI)%fe^Zr&8#ZFjnJxJ#$f(4afF`Zz@R9z%JT zhem&9Sh9v6#Z86TC;iIdev<|O{n^w5{izn)h0{q8JJ(3?IdO>lrBh9j;b0k^>VJw1 zDLLF8eOt8(ru05)FA~)GY;w`NogUkp4@<6Cdkmx8M`y`=WrrG~8hf1{^ zL2CMVQhm?l?*9^;ZUM0W+u?NG`y5Uk&9BUWQ!AbM8iHO2zy@ciTznE-o4jZNUz1NP zn6aNdDUb927x<5|zX<;SU+O>VMbJa4GN=DH`j1|H|D=cJKcN5U=Vhh1ckBPh{-ZmZ z|5NynewtzaANL>q5)$q|r2nV^)_tZw26B|?hMM#r&wq5^2VXzlU@!m2^&fo{ZZy+g zmhn4jFaNvwkIrH5@c(=N(dXd!|55!%Z+q{5GXK%-0CS3d|C9KSE=AXyNe}9?AibF|D462WmQy}7&=|LqM@zS(@^B4t-Pe&5px4|RuKU_TcP}& zX>>9H+w*V`wbq27;UdWwt$IW8#nwY%S3YWOj4ybB)&^)z!?>bKvB4jq`qoX{QC)~0 z{hh1^u~}E7Gs=LYt!{j@D+=+)Ye*Yd*n5;1Xm(ziuO%yK&v8w|3e0YuC#>Az==t_u z3VepGN9`$kB4d&Biz&6MA@&!kVr^#>dlWQ&t;em;Ya6cTuV&0}wO8RgmZV6qk#bb8 zBaN*=sf{I3vxj`=N4VF74#~=QW6BI?}Ttiqn8r# zj8h~ZjS#cV;|t@$`)1{r5VVRddnXwsyE3@B2(PB#c|jG;6%XA{T=pxNjEtd0Q)x;T z;7bVED2n=5;E@feP;r|vAgiQ~aWLs!c)jUd5&QEPz{-aHOXH5aW+gA=Cwq-AtY$Q=>X_u;0VbPW ze~^C_-d&}XrxisIr*ZDl0-0}uuTm0(Sqe?>TNrojnMD!5Li8Oh-e27i_C=ZDgZbry zdn$2L)l=EY(xM7h$6iw)U9f|5;pumN^77A1$<<}W3UUOQOOHZil-*LL&^!`7>aX6y zRZl+pHgVR5H-NH{k`>*9!{_p6MX}=YhSfM-3h^9E#O3Wiu;NBKsN#1$e|yS5jQO}B zWO49)JxMN&&5vZ)=d=|u!E9&e9$_JGXIZLLF_Cv#!6TtD2z!0Q06%aG?Y3gEDwepxSe1iHZGfoOtA$nZ7B0@g}w~LIFt4t)kgN<;n{UCEi(F;BZ3%Z3T^VzNA zaEkWmKKYq_6ZUlUWSB87QFhWR7crYUeA+5_IJl?2izo^1ENLHndVu{-&_ICg6JR@& zM`1YvY@b1=qax%-5p*TkQiG&CP*_0D+M3VvVULimn2(+omhu7vjxRhHkg8_!)Zwrp zS-tm_Lflsq-C(mTj(o3zNxhe?;rQzM?5+35pCIo`$qd9?lbO4Z1cCzsFF=j2sA0Q` z3li?|67O9`3`OrWn6UdSO<9A)-ViYwN4-lSkB5l z#14G+*1PaC7cx&q6SjcM83v?Mqp%KThEabKu2;Q}SE1+UNgE@bxg&FWIUD#(7E?4E zRyaVzOo%j@$CAtc8*-B#Qhp)Eu!H9-itnm4Z+&*?Wu(gb+B65(`9gJnip@cW5l?^G5!5z$*LNqr=czq4bpidnz9D zes?;)z{#nTQHZ`r;jluqLOF{sQZQ4QRyCbVp~Ws07hw^WET+mM2-A~1E_E4j_Y~u$ z^~DM=<-@#N1@aZNQykpWlaI;pTNq!bF@;(tZMpB@kgLvjG25DD3VGaSi7CWDzLQ>c zqX2L5XliAMPIdYFKN-4IO4sV*HK44)g3gV^)%k)I$|i5b2}HS0H3fC~#Nh?S-bdm& z+ZhYN-ry`4A&$)wPejK=$GH=6$Jq*O7Zz3u3wP53S&3C_zpwizMX_Gq(9=wkN_Jg` z9GH&I%w+a*p-eP^H+(tyIr$}-J^4;>j2KAAj$$t-qLZKMI5PjI97n94wyk@Ss3HoR z+ESBUJ;1m^>%xV4FrV!j@(erTf6|<_u;B3b;GpLEEHDj1A5?>u#eRuS1(8q@n&T6g z229|1=Tcjh<@u%7si?j+`Q4uo)$>bWg=Ib@!Z%q67PIx)e$BADTmS=I^QVgD5IXkm zX4WdW!$#DpQYa1u3|fu7heL0~6zT)bN6$Ki=MDp1@{|q%;d|mss+#v@*Bzqy0`2#x zMoZ0_kEU?#P{JETv7F*`(P2xI!|BUVYk*~Eve@nGFX1yCe3|1e-m86{m#ebZEu}s9 zV^wshJ$ljD6h%GTqhoWAmfKN3Ajd_4mk|A z+y+c9rCctnu{*gQIv1`tse?YAO2SbqxQ4*$?*Jd>t@m?>wdesL&__MnT=ffkzYCWE z0*5Y+Yrj{nmd`Q}yG04Ts8Q5qlCI*W#pnu4tn^65%?;aVu-JRQz^B~*CB}S&!*u^` z{sjDz;5f!*(%OK~_OXxaQzpKazk|DBE*hY|iRYO5l{?I0ej8eV!`|M?VMQVLO>*8Nvi+qwXXL-VnfA+$9n%mR>gv%8Ye)@;H{a6dsjpg6FyN7iJr1J|F*1 zO+3f;G?m5~y&4zZ0=IFbBhieJpd~(Ch?l$#rUR=$u2ZI&=0`i?!Xx$Vz4r?>JJAz= zPN{u|2KS)Y4WmYq@oBmtKMy45!b~(06X#QictI_yh+&{|IHe?VCc+2f!cDWLGdW+| zci~QVg0UqP)o6i_;ZN_^jzlAPWTCW$2#NUaFE&~=iK+ zxET8D!RSt4l;|3|nqjqwVPUnfH}qc-k1`v6h`-VJxn9pF=vCRF_k(6wFt5JdEtot; zLh1J~V1C&Q7%dYU?QXy2*St#rP6X@8Ai%A|h509PZPE>V30OW1ye zc+bG38Fw*ySbNu|v>1{w?>aFEL5hMpriMS=9vy{u5N&mTs*e|<6VZc9WlJM6>hsH8 zA*VDE2TKW4$;nnJ3Oa2{J`Q>aapp@Q?^gL6D$htEuWWFg5lmn6Ri#|lND7O$QRg6& zA31tfK3+Jjg-)SY7MPWy&-an;Bf=IrE+uB%vqU@&k}y{ zaK_@M`5WMp1JypBXRME`Cx^&z-Uht?=uC{1T+jUi#~V z6Qh>QdtOXydGgB;pc*aT!Sv~*huvtanehvtfYrzNl+ISTyrG4>K`Cn_vcE#DJ+b;x ziCUo7Dz$pv2=T+$j^Z0&t`N*V=S9&ADVnPM5`u1JLuZOQ6w^WY%U;nGrKHpW)jj;i zxO+r8Vq+0|K|eGbuu2AfKN*UEEVI)(% z7?lMxGdh^$w+tAFojtjy;Gs{(Ef~3XC`G)N3c%HODucP@Wl1Yd4UXNVp?6YB_A%iG z-6J0`s-URkQsO~)x>DqU-cIGQZ2aFCfsb}2Z&E;NQB=cT(8mI`cY+j(U&AbLJW(y9S9vh-Z(|3d6aJP7X1iyQv4nxy{e2&V{Dc zb4lD$Q=j|>wmwn97N_bN^V968cY@v6QCon|G&?Hg0}P|ZN%gh1+%uF@L0F%U<3%fglo%QCYI$hPx-n@sAXR`ACHD;JHJRsh@YhT z9kOa!=SFgQoNX|4!zHa#^7}tKg-lw0(r{Q2mGPk5gqYReqkth*METvI;R^UtRoW1~ zr~;>X@I{gFVPbCrAxONd9zmRaSvZX**t?ejk;;GJN%@2N(t1F)J{@sKV|{Y>&jC0# zn9CQ^S61<@yx3~s;s-sn<;ac@dO{P0^Ldur^xqB%by>Xx>93ItfLpFq(glyCL< zG3b`|s2q%QH_H1cf70bL*`WoGal^crbVw+$;>GrO8HPC)x+{mUNUU<4(QcJ@#Ig0} zhhVhj>~xI048^`qH^O@`$>%R_k2mVQa9BoHx;)c5d@36{l;DRSt57VR$rP{i`l8~) zD`bajn)1oPec7iu)jW~CbT>s+bUS<9s!Bti7FH4&H9C2}LU#8J_L8&g;|l&@89REP zGP!Ad5(qJAys}Rea!GVgIL4rT^^%d(zPw~L_YI0>>PLayNEKRiwq}cfNu0j8qop$E~38Nr?vPH z41xYgD%wv2(8z&F?g;gDzT^1m%dRLt3T{vOr={u@g~y$9?`@lVbnek&^iuh)lm-{b zXd1awRfvD9lA1P!>?Owvq%14U=rB`3OAxM0@Xg29SudP6<{{*7LwPqpKL~{tl{QVPEY|wcWZYrxlj_u_-+OyypB_V5XAk^-amnw+Q>t0;LVK7SX4Q60QZ-p#JJFsX&XcOBmRww*WxP z;BXaH>e5O-j(nH75Plu<{m7ZfcNL5$nuuG>Xr>Sxjj9sTw{`R;YR^8(p^y?ycUn&A zR8m9Qh~}q`#vV1ji^V_TH&A?#t}TjH1Js z0;7S#@9#y+BxGM_SMJI$f)uqtP)sH&~4(5p01Wp5T9WMJ5<!=BeF8?DewoJiu6} z;cyT|&;0ldubr^}dNdPDtuh>3t%mDSG$xY2t~q{s@Y;s20Hi9qtS|q%glUS8Fbw%x zL+e^J5yS~qL^gEf;uHWo%!$G2*;eyj?C51Xtd>#3WII(m15a_A=`0EM&*Y99FzZFJ`{IxtY9v%xJij8joOxJoCfmUE9Xrk3&*)L~1m z09^s_5nfJngU_uqZ^n0F<6Z#gWw`28jUD+jhE89d+1;6b@YDXm*C#)?Lm$laf3!ur ziS5S+!r{G3^U(|WB}=>5xt*Zjz>-uvvj(oiZsh=X<)2qUCKR7~{zwIHrZ3NwncmLZ$8@g=EvLK%tqVIp+fJSKcWUN zgTc2~q1V8&!Pr8nw*d{UbS0IOJc&-4>YPI9(+wzwlG?IYfi!X57gr$`8awbu408_^ zXMek$av+kF_Q;O1ki9?kUBKQ*?kQb}w}oZYqt$p@PUCHfE^K5dXWrZ4?|o#{ zIhb0|bN7wGfXyQlHh+6}LB`wlif+?}bp0al-pF5UFT-ap*ZJj5{LywnaO%M0ccs5u zDH<^KWPT`F$fw`pldS2fo+f9!@D0_={GL-iny_s0*!2tJ&7c zcOfHn14F@kq8%OcIpMAyV94_9tQNZwK~~9cQnx0SN2iWFw}sSlPyIQAdor8^Zd>5Z zH`u3F|B4y}am95(Yb^-4FV|#di8f@Zb8@|HI8c&$6L&k<$i9)kMIP64Pm7Rok%3aU zm8)$0CXk%@bmIH@y7_)s{O9r`k5>wEFpTf8We|3j3|Q1TVRQRPK3;+gHm$NqenRab zeSkn=Z>IJHdk~%3b?>NTfZ(|;f(UScE~95?BA*J+e^ns-R?9Cm{b5-1fTk*me~$^x9yS*MApg|Bm zy3i00LFzS6)ay|Xoc7I5S3XP)Q4j-?3iMGRb@w?!l9aR5oI zDEXT=8EN#C8?f*cA2PsQ0W+G&J+h2zeRBS)hVEsZv_p5pT&H_)RxLupQd(HIDX3d` zT&VCpRAVodNvHFW*Ma`(a|2cWwVQkeBN>TNLkuRgeirna)8?KD*H@s}1b#N?b`Xw` zOSDcvK2R-3XT@D7oA+lg*)M;DvWPzbPcvdR<$t8S+dSn7pm|Nl?nuq7Zi>1$& zmwZlug(#4Km~s3eXg3Cb(?X5cB6ILk@phw=BEA%zsrKpi-q+Acj7-ftB0|>g4fF*D zm>;Ex>ZBD=3+Sruq%4v)CeMnU;w*Lpx#20su*C9xm|2Rbhv>wZPGNL*Z)m{<^&=eh^LQ5shhoF_BDpX~ZyguF~aU5Pr% zb`)kx$`#t91Dp*J*dN9PRYd$Nt#U`m48&pkn3PC)ZWCz%{rTLz;8ab4+Ao|MhNX*R zj&h?CtLhocj@tVqQwF?L0x}NouB}#ONs(8lggjG#11awALn&(Q*-0eSe@z-2gt=a2;SJ5 zit`T+S7JT#$yz?)#IEi90W%3hD}b}I7sPtS0J68@J|->e`nJp?`3(NDT9w9b?|mgd zSSAv1FT**=cXNba!)hd2XLbjl3nKY=)pqoy7vU1vyU&}RK|J7F>-DRrgO_Ej5Nh}? zikV?NN^TQhcqxhm&V+qZH1v{YiRt!A5jG;5r@S8)GnsFXJd{xEleg+2)7IMNeiyPX z|2nbkZ@`-&PF`OVWNh+H_X&5FH>#SkBUgO!NruuHcU7jGtzMjMxgaAoMaluwmX6VF zJPiv*Dn(tYp}4|f`&NoXo3k*wD`i)xjN+65`f=@Iu`Re28?|~M_wlF0tuyrxHK9Ns z?JRc04U~KSg8ai)h2h%_T_9H*?5jSQ+LO+=CITbLJpi9r6!0^I^PrI68VjcxFAs56 z;sbm$p%w$H6339D(qsJ7)so++6Y;}NgIW^Y?yxIE_KF~cQ?&#KY<>2+k4q~7a*{c9 zDdE#b0RG^vDvoz=zB7C2{z<~WTJ$}Q@KIMZ+3dOoipl^=T}%@G`{b?MX2=~;&)9ZH z`w;hW%SK)Ur=J&E&q3H9G+q8hoG{N%iug^2r5)H}Q^G!{Bj17@EwbQB)-@*yJ7aYJnOY9CMt#eNi z^=Y0wyk2Wwg(obzOQ&j~3f#rcx9N9A8T$iunetD7sOSrSSX9{{a_CC&pV%m&xfChe zhUZAw0-8ym*aHe%GW;6AcSohhNfkzhns2@@8$XCMNUo|zUafeVeI+QP?La86ER!@?xgG|`j%!H=CGav*H6fF^)B-}iRwvhg+@9N zV|O>DvxU94g94%{wlm_&4WgVr=d7DbEe*`Pjt*5MUZ)+@;6S1jav}5V;E3icEnI{M zGBCPVP{vb|9-CRkruuUJOKP4l)yHm4ia={tPGe?MX@4B+F-{-a6h5K2^a8 zrH7PPilf}@1_kTfrzO^s-{6P6@KUtM|9o zgA{L(cNYCx$ULVldLGQH??77)Q=Aa@=@A4{Cj?@N4v%!ON6GB;8pVaFi#-w?--NE2 zhkk#1?*R~-qLFhBqBR#!j9Ps3?w0s~$fjeWX*u5?nVzGytWAV0r|hRE{~aQl95popEJcSx^CxmpS? z$y-|B?@8{`fUqE$d(zl}9?`noS^Z+4S`$-1J{YgrJ}WL(;+&@AKh?7Vo@tml+hE9d zy`J6b%f^f2Rd20d7SCHCu>cEv5fndx0HMlF0!uTrAS1B`lM?*X2e zuwiQf0G?A?7yT-amrBXT;jF|`PbT_R+hc67av zJ%U2?kTyO#ML`g#@Msf*%Mc}n4zIHr$JoMqAo&UAM~HDAks|e1^R#IqL0*JL2{=5+ zA6G_buMdAGSlqAel+|H5K-2Q`Y>1gVKChkeN2%6ESmHlX_nFG~KUSSO^5^30xB8wc zv=hz?&PU%ePrQ1@oE+wlJU^xztTU_;_cR~-4en#V)_m+@d)-2uo9)bC*&BUaDqitp zs4#f~!QT8#oBpG~!!|n(Av^Td8nu39gMt)Qrk+0#zr5`qrwHXo8ZL<&cEzf7cHhd{>E*FEV*AD{ub+vPha3Yh#BKUB~h<*ut0 z(Ho$+8+lVhuEU~d)H}K48r|QIP&VT}w;RX)YJW1JCqfpQA}GQ=_R6y2r+86%dkepr z@C*8iN4N7QJqc*i1>Y$bJP#8dYGmf~p5cmbm0uQr79eHon@~~hx9!mDvK$FssK>vw zhiheUB_?FVu;$~`Ho=apPasr{5Awd4AS#mM&X-)4J6*2T(bTi!6wX>GPJTEu;c6vA zK)l=d!)ZYssN8U0PyYHGnoar=RPRoWw&GBnj?)hA3X!NwU~h zKFO}13*l%u=n{O_5gi5ZG~^!hEy)fe;^kRU+?aLk5?A4rT6F0cb>u_6Nw%uw3eeou zo}sAW3gtw2LPYB<&vzo-N#S_tbWaU1b{(e$Ga%kuAyHEGWd#C*H3oO{9VHK*cn;l= zWzd+4+KtdU$rWO~0g(Y+vl)^{b1XE&@2>uw7?f%a2C6{h-N;MqUJUSuq%$3pTqj<} znV4sBTTRM-YC%!=3^Z5x!0@dAtcvy;1-NntF->-Upu1l91i=oiHouH#4>LMCXTSdz&7~oZC!j}~@oHV8MI6i_9T>so_85Gyx3>_qcV*Ovxu*7!4 zD2r?zYn?3n(3S~+Kg|X}6&kS` z7I-fwl6^L775~S!Nm`8&0U$=+`zhKyS&m=KuzQ z{e!ooIz1pgM*25rEK8KBse;m%@Zuaag+X1)rJe|zga*XF>fXG))I&doqCmg)OM1uv z&q3Uu)>(8-3ird=rT(wCC+O#RyBS$@a=DOd3vXM1^%0+!#>;LLox?`88MTWzXGNZ3 zYIY_q&pxM(s2;5AlYBU3S~FBI#p zQAM92=12UI0{3QPqWOi@yykZKtn!P&y5s+^L;TwJSv@eQ$S3Jg(=isWaV1gBiJxn% zTH@lM1G}d^V_uzOx)=6=!GwM`pIfr)zJk-4VYU%lLEu&I!_fn_e8vT*NbqiPdO+%4 zl@2u3G%6+ckWL%1tzg*{xRCDT)iroyT&=xB5^8lGbG4uxwfDdp!Qs`!l@+t=mJ+65 z7F2W^r@hW)l`Fz+7P(%lH6=O~s>2BOXU28hv8Z@A?`eCzG>k)$f_VP@SP>4r>1{UkS5$g{`}<>kCi-y$Phoj4DPiV<1>x zr1`##_4I2XkQAQTCImVcu~y>k=FZN@iD+}RXjZKR3o@a{!I`8%%lFJzmDU%$Ao;{O zD02lQIb^JF;_jN<4Zfaa3Ho+CP|^oLACVzW9#@~Gomx`;f!xj3g?B9x17V0oLd#8B zJCXoF(%t1vP_$8#1`k0&C9th=y$J#g@Yhmuim~afmrGZE5-#ObhTbYEmC~3gye|11 zPa&kfWUB+$aFE&T-~+J+aYnoga1fK;5|?bqLAQ5nLV;m8x9BjBr4xWbjj@Pj(8^YI zk1pX;GMjf_`4D?pS|VF9mZ)8ZkgXuZ;mMM>)#$@fq)t6QG}=B(s4Txt6=gw0QK;6G z-E#KJwbz8}2e4}8&<%RWrq)#a278!gHDIL6N@)c)v!?;#dsVikkf$3zDQk}VYuP>z zCSgAJ0!E&o0T|eFKc+~sAVo0r_fD;`(u?F%U^@v)3TsM^$L{j&xZ_Ovb-~dZmK&F} zWj(@3Q^!zP)>;Ty+i1kHNcI!z>4RfwPwH){1|w7Kb5b6P;T9OBoIt{>r2Pr=^JsbF zVofSFksKNsbn~rvj@jJMBy8dd6Zr7v+P&b!FdyOC%-y|=0nf%V`tRJme|%Kco&TR9 z1B{wFLpRpg(wcU&ZL;;#CjHQDqV6`M6PVxxq7q50Sljr6viK==Nu@5obTZ2Acq9Em zrQN!VwYKzAyJ9O_YQ-jy5Wru61@Sljo)HyLgn(#1&(}HkP9{H4x@*6Ge0@mf-gD1A z_nh~6|2psUe!maGn3d1trHy2)|2w)21MrXxwkO(B7LS1vO^WZrsV5A{b{rjWJ-aLr zn0nEeDQ1Z$f#R>dIDYnSA>&>(d9|j2;fScB6>rin(NF`b5y?Gratq~=Ew*BvyN$B; zI}xbHZ|KV49|mZAMzZH&EiqVUSStSD^_+t4^Np%)*;7GM-Eo@eB)z#IKGz7u?6X=N-a!a1mGnvL$XWC)5Yy0gejPi&oCsx~=WPq52I*yj>n z3#Xbu{`3?)5*LQ*FpAWZpx7zmr#_;!Saa1ToC1H?6?N*%#qyMobFu_6X#upK_fK3S z`>Qfya%$#iP0MHoUILFz0E+&2g}x^D0C* zM-3%h3rQR3*{C93xs>SHEA>69RhzQ*vc>!+J1coQKTXsPCpCS;a zke^ZwVUHZJ;LZ?<`qJR+t9n8~SGDoh40X`Nam}@3nG<{>)^eGSeLbA}h} zN4QBdS{CrIT3q?G4NIFysOmgJx@mb-+h9v3Pk zB~NPhPl&&dx3bw-kNZ5HW703=$-&QOiIh1{C_XdZN>~+pAD}VosvLWfIXMD~;C5{y z=uxJqVb&yq9g7#wf@79njhO(5PuFYB6uC3hrB+Cnu2<6;uV<#0`f4-2ForZySr9hF zK4B+6h;dg)3GbTlCVhX{?4<>J2|#;2Y7V6>L0~YYnqPK8&=a4_9JkASx72?3W9XVj ztJQaDZpf!;P}XDnWotq&;&d5WXFQh}#$+r~BYfxCz4_nyTXdiI_|AX#BM%hdEp~Pg zjH(5er3~WI8Z1#^SDcbjmkKSxRsp}25&BucwGVjOhB&DL-SIofYfLTAAj68Pgp>9N zGX?4sIMHCMd%=;4!??sb@?paKPC#MtciE8VG{E+VrdHUF)}-Z~M}QH3e;s3xv#-I) z0#`4QqFY=DDwzUq zg}O%j(fD$xyS_2A`zedH$w5ry#y`U3=_TT?mmr6luJBFkB{PwZ%V{7ls&e^=QI$D< zrFp1%Gwe3go*9-@o0%0)51?hedzwNbs&!tx@cK-Pn-%Yx-WT1EVSrKkyB0I=-9}f0n%V~nuCv|Qs{!Pg#E^dUJ?Hh~aVDVx)ohpalG^lyGL+U{ zc&{wZa+oT&5(j=XA&!R*CF1gkY8>>uI6KPViN<9z4#YPk53%IwP>!WlEK#Dl-4y zc_E^;ATsc+p#k7V*1GR%vlxS_P`DYP;RS%#nmJ{Ip0wXWk~rh2c-qX%ncL|11SE=7 z4QvMMu%|w90U*Fsm_ci8GLD-~)A@EeLjyW<5rjKmlp)eO#|nR9BA{#aacI;CkeFL$ zu!mMK&j-z(cM;KId-ds^jVw%>EY&#g7~G6DNl(VtC^+6{@r(mehDK?=$yB0Fzd|1} zk0Zvc+j(WQ=Am#$h!5-Sa>>yNYF6$1QSx3JeXbCK@ICzyysWsY)<<+s#zE3`U8)Kb zS%bV)v9sbq*6!}aw|#V|*y(#?b1a8Y46R^ygY#lV7io$JRbaMCE?=riEo~xG5t^p+ zIQ>h$7F5I~#WNda=PI|UCg8lJnh+8&+v38RZPrRsOPYw1wl&$P7@Bo{5f0W08>{`k z_(<5`?CpAUX__WXgtbWVHi~+jYeot@ityDs92rDLqFer>KNr{3;5l5pNa1*=$x+>BVd)bL8qhoITIHY))VSiZhE zxtmEUF6G%wnJbB0)1U=_^33KPh8vaQ92RNo%>GKjii{WSv|SE&hBX;3VfckVLmp4@ zCyHnm-)Sz7RfsT4#5771&L-0*#aQO?k9~%JWb)?8=O1hwXejY~ArAj|WeERh&U_3l zXi#`d$x!~0n>23^&@zuMmgn!yJdi~1&pdWd5u=6Y?(=zWz4KVO;|tswOk=SzL37L+ zh0Ag?-QXH|n2>U!_mHs`7#7BKOuImHWSLRpxluR~?o|K@laE2xt(iYQ$?AB37l{>V z>s^lAm}XG9dAR~ow|OmTZGz97jFw&B`f4n&OZbJZEjbQ07JZ&PR$stv+G9$^<6&D` zak6b~SM}6X76WN#rXOY(>Xpo?821qu?Tm_$WU#z(MC^G0!2d29_A8msU^#cs3F(8* z>opICJN7F}e2ynpJebE78z44yqVy@md^OuJ)1~qbM132M(V2tntV^sER83wp%u5aCe>|^|f)yRQC5}frF@nt-w zN_nhepRxhD#bfja3cJbaBAwLZNh$~uRa0#8pvaIs2mT>$F-LEdGtVn&H1p3`X8?R@ zR!4D2UTd1^&Q6Qr2HB(c!4C_}<|M~7`vchhi_E`22Ewp(ta?JrMhriTsmEm_m%<`0 ztF!C(L7CRj;KyuE!3uyWqd)7LGEQPU%)sB;GgwVJn?0k3vt6N@eY7U+*w}U1AO;Te zXjE3;gma_aO=V@jm}*!$f}W+rnm?*KPp~c}9GMc6Z$~szwJo)&o&%i1KU^0j1hYQ< zosbL?0yB`BA9c0GC`Y7?%jvkExmDRbzNeM+hVuFU7#^J;pXb>S(Gx|82x zTzAu;%q-t1lNstQH~x+gfCnq|;6V7MkJG1Q2Hm8rgc6y&AK*tu`x3YKE6z^C6x^a^ zdMLZC<~(X5(!1lWEaG3HtyPrK&D%{KPFV7GfV8}c^H>WK%$jhyIgI&ue%TN_le6hO z`<6T5LA_bu`heh|JdWyuFf|mbcv<2?<<-1vlupk^Z5l^0M0k3-C=y$+SE#!6Ix+j~ zl#m-C+;NxM6r&N7i6q=REU=NK&6*%%gh+5#ULWW~>OP8Wb(B@9#GTsjtOs+}&;V3J zIZ6;>H~H)l=PA-6RA^+San~j`Lp#p#frw0{?BsBa9`u0ypRfl{H!5vxLQy*R+^S6w zRAXj`5n8jgK?l#o9iZ(`*0MuivGG8l9KeM`y-ci&=?7UFQO<7Atx%5E;o7~zfLO)B z%$lA1NPlC}`j*nS5(9_AN$&hF;^SI`)Fk;;CfxolNppILW>B?>_Gba}*Xbfrasfh5 zgPA#KXmmj7yW%{jo|e;SLFsdjI9-`@dE9(qH2F?Z+pTnDh3VJJ%G3~c&!BByMLYm; zaAS2PPjf(rw}4s5f`p%0^$8vNy^C`$g;Ee;{@;0SuplLcl5b5<1 zk{`_r-*hvbWF*38Z2<^z@$-P+Szi&ZweejV53q2G!<&#e%ui8rc3E|i5|~DW`876? z2>IrCoq+M|ceKFG*(W^U3T)xj^W_|CU0_Cdmx%31{(19L0v4f~^@+@$vT(5KA`bzX=*%nx^*Tq>>$`qz8&R-4*NPopaN_R>Og}EhNzUIWv*DoS6J~zy*u|crfeHI>4l`QbT#8tN+xlJ)h`$cU0goVJRBNVWkz?*7XvD zIAD$`B>5mQUzz^=4rMV^dO+G*Nn3u;HJT~z{PJL4 z_c`m(c7N1SyKiRVFX;XkKfvy1d-J*v&zR!QE)PZ14d^=IsMA!CJX9EN{}Hp|ezVZS zAy~i=_!s)9G!%H4Quho-qvA(1-{zCibQ28eSkWlg$N(16&gW={$&v3M^a5rZN@0$m zwytLjaa&~5dpl^1;tUU~59kcU?FFJs^cNZAId}PjSwTbp?8qXNM0%O)1rn6vSk0x% z9o2lg6u+$*80n1tG%$Li-Vb(#H%iYMOQr#!H_kAlT69$uc20&6!BRY5&6U5 zMACO^m5V{Ta*V8a$sX~gch5if{Bxbh)T#3`|B7)@DWDBsh9@c6LuCG4CXpfIi#e}} zKjB3rp7eRklNz9Iny!Wc!8QDDOIP5#uq2ammI`yA9ZWi{o6a-b$SuxbFWY97n#xR< zXIGCZ6E=h@;~hIK``Dy?l}r3wnLNo>D>8yTrHmrs?R*IobEN(S$Y6))Ff=QxOsRp) zge{!ty$jPgvsV&zr$#WJtG!Zbi|+pkp_@WU^P+_qC4o`YiNxG*^+_f%C(ni3rK^%~ zTj*%nLA0xX6kPXh9W!iEvHw(#U2xuI#PBt+62=yI!>~3oM)VoB21^!0GDQ4*H*ls0 zwWg=hbi}f@N5$IuPgOeyhoZLlLaEn~aBfq5;2_PZdDG-ln)LaCsmp$R<$k_Vk{%~~ ztaay%js>1`#?`zLZvO=%!YzJ)L*$Q$k#G>hQF$C=6p8svF4uvu=>Q)xV@i+vpATs* zi@AXzNtbdH)qR78G$uDBO%lA=*<#1#Pmdjp$MO13W%HBwXHpT?4c87FHhf$2rU5ZW zV%)hgond-l#-z{dRexYRtQe{f^begW^R~yp4>OZ_^S*ms{)mh=l&Cy1XhdUkBRYmj zGK_E}PJXmWnq@lhK~7SMX;Hc*G*pRU7Y@-(vWK{!NfubM15muhV~G#o8tObdDjVtMY6@1l2@d$fFp z6_xsv<3T1isZrzspNu-M!5QI?YC+5qJG8H*i8q$N4{!V`3`D$fvA&h#jlUIdEU$C+ zh%sV{G@P;f3r1UOsB;d}aNKalT~gEHu655AcT`GqJf)~W*z~pm&Nzkc=5xkhK5fJU zdYQ4NSi6A&*rq9R7vU)OXaEkumnO)aVS0$R%JzFH67kUr&R5~M_RLdS>ZOaz0-Ff@HW&&>{0+3?5Z{IfF{LGXsM(PzFXX1`TXS z=1lsbPaI`nLkAUg7mY9~7z*la@s7;v_YI8d_;C9#wW9V^P{XhpRb^;kRLB3fMm6gF zM|C;<_~1rWk~gXo!tGl{9hxJ5Xqu}c3d~$3yG|GwmH68+&Q;<2k80^!jjE^&vZ!{y zRhV2?n%cxM^Y!}>sG3-#sT==9IpehMb%QlJnw-@!PFl%5MeK4V0F>A#cJ$l)UD+n9%xNZ{!-RAe@Rso z83G7CB$I{y!<0vT+0Udu`R`a&mD(5?rK0AnWE-hCuulZPM?=H9hj+a-&?p4VYzoU! zInb?`oD#)_VP&Y*6SmyC)xo8M+QA;fHk#2=2^c)cVj1~aYRwO?H}MeMiD%X^5gNfv zTUVwT&N`vKD*M(7Tix1)Gr|YGamo9?!Fo6IHLCkNL6vOsG>31z7)yt2nhZmR4r6Q3 z$^=7+c$3*#BYYOgCCFe)(-@7#qJj)wGQd76)*N@XV-<`#Y}Q9BdYz4IkXCGvy~FN@ zVitN8;}$F#^?4WivNQ&}Ct8lvNi?~~xBS&+o@Dzbj>$j}G$6o!*4Xw3Ek;W8;fVN1 zx%0SkXn?XysHP*47V8&3Y?-KNx=+1ARhRYe9+Wm?{nNfBR;-m&Vb)8$eR@z(Thh_= zSlI&WoN%mW7yMXAO|P*eR&640vnJP~8em@zF;-H+>jU+jAjdUK zl|itUDlHdBlShi$mSaifNo@w#g@?Z5&4RTZ**vx;TdYgz85J9_$I-7jS(1g-yC=g zrYlVlt_#-Zrcy<<8naA+Aw$GmRQnf|x&R!V1{^8=$~f3=Tu*9oT(__m^unnNumNWO zz@MdbM6~U>M6#oeI_4H(Kk=++vLqBeY(kJgVCV6QUIK;=&MLCn80$c(VOwZH`Dg#* z4+a3W*^i43`pii+zPa+IP-egihzZbrXE*-a&O@=!vcfVcDK{w)9YiwUmY=JT(dj~i z(_jkRnWW3^LmL3S!cyF%B=d{1h6fwO!*!)`Z=UdIJw9FBHfxcsi#hdpgt1=J0*wiu zU@iLiJujx=3Xb!`(YHQ;g&0Xdb+RJ9%NFa>zXOKuB;{hzF1p9s$k=qr5h~zezbUA3 z6Zhh?=>{@?Rdvd)M+SMb^c}KT z(m<9hIu8fFwGBtT95Ki5M_0=@!OI-6N{v09bc1ZP6;nep&s_MQn#j0OhPT0g^m6&02)lV?3 z7lu>6ImPyCCf%uZ9y59-<1Ck#+BRT=+aiR-%B;9gUgpre1}kc=jtocx?+pd$Lr=TXFaCc8_b9I>FW1@@_J()|i0uvFveyir>Y@MMB#Ou9W2H6{D8 z=(?Ws9i2Crq|5>t?I{%v7zeL9=UKtu_@H8F`l!h^X}Kb;HHi3kOm-8GV{O1lFOQUG zGHWn&%UVQtO9HRN+^>aXmO@!h?Kv3?O1?Wf+`iwiCm)8)rJx5vPGw{Lp{JSa5@_Zy=In;?yT zp0_%;CdhByZPAj1P>V{9Bqf~$y{&f>|A=Lok3^j<7&W6^eWymdGBRs&3W~LB5|_zj zYk?d%oU?2ac~HECu9v#=`Z9k*>xPO_kJ0G?^zuL>$Fn@pjQxv;Sa>W$?D*XWGemF~ zyOHbw$vq)3(2O)wN$U(_o+Mz$ZqiK|Jm=sDIeeBx$*d>@2TpYrFS;}L%lv!r>56x> zQiZzkQ*At0@9eKQT;KIVVT1FoR&Jr>b2#B#swl5Z%PK3jBjidsa+I%>NAZ$UQb8C9 zyQX`TQPDHumHLiSoC08Cnlt}p?T587vlrNzokHit$eQyQne*7JAk1t;R(7w)${^Y4 zd5r9v4-I2v&H0S%%gp%*jI6|CNALklAUSrl-{UXM`P^#)TgrY-$qE9T;d{_Ub>Fb+ zaIdK(3e`u4w|-<;b-353J~jgPQXI%o?)AgU#USoT9Bd8cndk3f@|j*^FYq~-JHfc^ z<+#}0hVc+Xc?okqPwXt&T^iU2or;CUIeG=oFjEM{=HoQ-iHN10A3cx%HC;1}30C}T zC!ee*X@})%H$QCW0mq(aW$^6espRxV^J}@CWQ{nTVdinefv}Z*q*UlbPL*de&tbwB zM>8Cnb3$d>6l-&*mKw9CjS)ne#gngN58VXolBg|bB-P@51Bk$V}rhs;~lA0Mv%?M{()qIR3w^czgpS@ zL%qaU#2aTp!8d-W0LE+m3-e7**i*o`=D6qKkhxM8Oc{!Z7=9TDOHwc$Qk&5(q!%e( z`d-03*9mqysnH)P+?P(G)JTB*qyb^=RgAye$pE6MLaMvq(Zp#_H4y z03KG1+2qh5Y2!iEGn*JGRgLL{w>>d63wZJ(mWj?mFS4RwDtya?|Kjnv7u)Zkvwyjq z8g8M?NTOsm{>GwvHG&GLiDEu5LA5_Ki`E$=hKeT)sPj09@U19^phkwD@RtvAu%tTL zAfLa(4pP$ie=kMQbej6RpJp+=elIr=ADq8WHJ9F;{lIqRBlB7xi zeX%NEQvLjOvx=DK^wm5ZzVQo26foheqbo&N<;EZVluw8z>}nP}M>F$Qz%OVn+A+IA z9$%ug)Gw{hKK=}6gW}$U3BbAqj57^k6eJ4^^fccse#+EVREYNWtuKIMm)GVTMAmc8 z!}T0CKHM{am3$8dU6%D6I8*;N%MOzFur|RPXovHVJGHN(EBO-cC3q&jSg|ke-fg%# zo(Px{sD~>(>Hv*^Bfk5<`9?|~u)YI?UF{AH_8M^5$AY}tq|M<}N>Oif%{DAD?^33d)*`uW zSQ2XDm(T8~w*}j}12xH=@Mp6P>u4Gk}vn+xxr>b#UUT7q7|=Fr`m4U z5_dOj8-k5kGv{_Ef(VG@YoYv|+3&&e2kp$#+3L1GGVzU?ct&kDBJ7kG0iXF5JLIzW z)=4b8*dKbhA2QL$hv95gBx@r=rsgxYy9>dRj|s$;OxulvOZIjXs|hNt0E72o2syEeUI}LhgcbxuO05UjwG{zimnRw1mNdy;mir|u6PLM zUF&o90ZuNS+2Sl$QiHN1cz5<~a3b0!1~$I4@0o*-HFUDUypBp%?>}q|;Vo z;9?J=tU}h>bP*|hici!=psYykD|m(>IFRRaGju!fs2gX)cLWQ6oGrkJoA)(>wv zFvulfC}q0yjtnQv+?L>7w^`s9-qt;}-|`r#E3ZMlmQ{qcl;zYt*->$B^=UqmD99mkir9 zGkzO`c5c5y2e5U$_Y^s#DPC_j%{IEhe4^Z&2)qPoYUdWB?9F_L*0Nh(PzNXHJI@_r zUs;wb$UL6x$~l%uL|ZLmU!VnX?qnG}CbQUiiGT5d*ft6gG{GtOWW)wvLByh%@-%m=dH- zb&<@0IowZ2D42K56c8V_DSqb)I$FXGfRiPjZVxLi!<+~AFfqJ`BW~G$oyOinYO1Tl z>2n0ZnWN8f>fNh~CF(K;%c!%C9-4U?$!uxoKHqinXcNEYKLksUAKHmef8+z0_e*Fu z`&}dm$ZBk?Tj*~-p5}ap)Ys~xIu1RW?>LlOf{mhe>6fhP^D%X+T2Z;p^Bd2|TS58=iT5)ht zuQeo(&XoK0WkY2mvA1ra(^F3P6UX19Ey5|&{P8-b2_1yB_oWIync<(cVy6>C5zew9cj1QdmKKXBS z`rnO2voz>=TaQSHsEL1xz*tP%f0a&uMxDL~DHn6E$P>fSt|RgZeO^btHnhG_pX@%W zmoglAxG3K86N?_2HtiK!Ns;q1>>j}r%Wq_mm+}(Y$v(MP-TPYJ-qG;x^NBL5^ZEYZ#u|kG7N2K|Aux>L>Un-nPnCUT0uY4knjKcV>r z^1C$bMGX9pJNZxQ=Ge35S@Sine~ zKB_#k4G*2BkwhBnnCg6)w&P}o5xY?Z0BzKc=pqB(Cf19$>oB zuD3^#@O)&w$AeS8>N7-Y_-yySA4$6TqSznB`)R!fx>g7cQhV)p^QGLyw4eP9izzgq zV-y+3tV@id?f2Iu2FcC=Tlt`VOR30H*MGH)PFINgI?ukJY_2{^`*tmmCq6f1&ZAjR zI2(IBip+mcIK%0*b1Vkvi~ff)`+rEMuK>~>hFdY0Poa5&d1OL53Xc4Qmj9YKa=Lo{ zBfu@^WpRGrj3O(C2|6Qy!<~B)td4jy<37)1B}~ZKguNHw?ESiSzd*`(=4m!jwW*bD z!>}|0BnFf#&#@u!Uh{`Iu^UD=|G=W!UuVOphSsw$0)W4~!-J{t0r0(2D53|PAM;=#5Y0+P;kPS@*uPgRZGtd0WlDOH^ zLk;IBAQJ~ew@UKFyL04!CgDTh6GgK2kGfZ*0v#~xeoi3MCp@2c3CY3E@cEXnmza@y zckWx{J@&#NS1+aKCNS|iL4d|ttrQK4VT9sBl(T_Tsoj4vsS%11G4K2Vsg0MLRkI9m zG(hRnN}QXguO403>XjnnyA&DC#>Qukr(@#sB0ZTmO6^vyS=9`zXx?izlL(;1ieWP1 zcM328+J@KP-(r9&M;q^ysfL)SVpz zLk))8uV;pVq}jVoZMBx%M!bx$*W-V}Jjw_IyyQ~Ea=MX}vU}NtI=It$kP6y{>j78= z6`P|K58z%Dt@v{jCqeup$srFxR2Se!BzHZV%ueIMqT;#hW!_a!lHH}znpfL4XG474 zi(j#UGWqc<1FmQ&2 z*Cx{ic$M&z5>*Xl?|;rVy;v=|iIAbKbd@0*NXr?Qe3vlQY53eSBUX3Hq1#}h$_e(N znlUQ)$VAn9qmK|C0~s7iHP?E1V{RKLmTi#spk=uZBggnK2~rOro6#iQvN z1r6!TBF=)l6V8=Oqv-`9r)`yT3%m}6M4dO--0XOs%ewS;$~b_>d8#4Z5=y>=Gw+Lq z(cWmekU*L6+zI!^WJ~3szzJ_s2cG<9!f9K9;r2~VAdWk2%Q^R^NVSvC7baiYOI4n+ zNz}H{d58wXcV0!ZtqIr10#7H>7l!QPH|uK-%smnB=^ak*`I(H~tCayI8BfCem%oow}7u zpVqo9=6*vtJGL)Yw<>zlb*tjejLvv^#)_D8<-Ke{5fs9^QRiV2iqx&~^F?s3r^8?yKd&C>(;1BwW-2|@y!5M)!kk5#s$mz+cuIIpXk1x zzN<5f(-LAJXW@*_`eaY3I8E$~1^0$i6O~b-ZY4+DD-WzE*OHw9HtvEo4R)YQGwlGF7M>%p0PCU zoDfeptneB`V0J}kRBbX%X4lg>FAp4>0R3&tmHjMz?>Fz8BmQcj}fmUzqKO0?mG9 zRcF?av^9xW(uhv=cIbA2xVhwfAT3nQrm3yZS-XX)+5g7tduccl87VdFz7dCbcn ze%4yx^BW{kvk-J$Gd4PHNVx86{6Oc`~*`12o`OiA0ZfUJ`r)4AS#L@pNq@?zpSygR_sO;S~>aKESoHT&)BOyyLDkDfu2^8aMu7 zDX@<_sl^mkbw+ERcr*U%Xn4vKv8tDnFBLkge@;2>-gKJ;PN`4xn>J%i_7i_FkKI>y z(pzcMTRvonKHi&J!%Lb|Cl#XG?j%lV+nPwjv#`JMI$QIH`V;)cV) zwBhF$by7sek9XA1T;Uc|@2Np$JJnFmSt_NxtnqZpfY3=T*9(*SoYYJ%3?0PNYXwUP zOb-4Bs48eA(x&P?+OCEPPD-!U1W*5y?xWLY6`Y^>q0~c8{(yzR@(A#PN;7*{`6N@C zgJAj={hV=&AR2amqIS}&O)pwg8Y@Y08kpQd^SOBIGQk8tEqYEam({DdBUFvhh8=#M z*OYo7%7USytB}yatSj6;suHo*7;{nu)X40{(ISyH#Vv`#+Xc!*p)2s&&cxED-o^8O z)8M??;Jj1k^qz5xCOPc>kzU4-h3U2GC_Rj)m#HW0?>V>J$VI}rU7rK~UcZ&Z$#ksV zd4O8V;aRkqEJIOW&ryn$qS(;o6=2+J1Q*zL3N; zrW0v1d2|~TRZ|tnx<2=!!q6GF?BzD>uJngt;hkQqG1vjn$C$I$cHnk_D}N{(h7Kjz z+u-zi@TcFZuk;dvzwLc6vv!H;^+vuxr!)N6Ud(?uom#=2?Q$$_znM1QL~5$fN9c1f z?W$JAW6F89!1VjD2Y%c=JVaob>vcMOry*Uqe7tbk8eRll9%YSYvWq4j4S}j8sC3oQ zdgqzAv;Oj>beLE(I_w3R0dhj*LYbiUG;3~A)wUts|B zgyNR3L@&(A*n0Qted{gr!Vle&1XStkx)HXfChgQYPc}v?p6oXy zd7krl|7ta9?b)<%I_UkkSNygr@tQMSnq0ljmf@8iweehX~k#$O+FC&@H9#3bE>o7{^ zH$iGd-SPhs3q0Zm*MQ?Dr~UZHbXFzMa&9rm>W=RsO>ug1h-SlgS}y6_B3z8Sw@3`f zooASs#mX8gAU>C-neLxo5qT zNdLz4vvr4K|PJY~| zBruR}4_(Td3&0zSgndy1h2kCMZHF~P^aU|dsCehnQN1_-mnEE_Ide&c?nf0 z)1ec<*VbEVS{*E_Jp$QooAxRSp_!bXL4~U*WLj)ZE3={8nJRTs&1yP|1TW{p#M8QQ zuF?jdSkY^(kv3yM2&(dQJ2x0BqG{Kl6!JU0S|dC64@RCh%_@jax3WF+aY-R^C1EWf zKOcNi^^6l8%bEjP)4a`=9KQ-PHV@BOD;^qlle7gXn{KwIF5xvmzn32+J1jIk^7t!K zG($OCdXkp$)|8ox^s+e=CY;~w;s+6m)j`i1O(9u3f*b0cp39e7O!DNZQCM6x#+UV8 zG}&F0a8i9-Hl|Z%v`uN#3vQ)AoZpx^tbLKfwQKZN;Vnz_gOM^(_?x9%#?CMz3SOf& z=6?^))~#aO2-zdSO??m4)%Vh?^)zh|x@JSz4QWkjX)x{1)QgT0ih_Y1AS`j7-gk^< z0;kZiJ<=x@tJE#(vfVbNLNvkR#5^tM5{hHzUz;-T!jutO`6XE6MdT$nwFV~3go-FQ4Bd{2k#>aOz#bw=E3`t)X7~X`AQ*B%tY3P5+B9ht zpl734OAbi=Azi4CAU-%+jfaUr2LhlC`d?U|>rgu7;}1U zrLJAWdIQUvxd~WaYWYxLt?1hWalmafHwMD!Ox4_lQ(pocX~Usm(B2WqHLC@_MW+u@ zmCibvs^saZj)8EtRa)3Gkq+n}$Y98bFRw6da#pvX=@z3_xd!i?WvY^1X3;C}X`JG? zV>pv?%IOtnnF`Zp3cZ#B7#l3(kbnd479m`|bT&D$@2v zo_zt6!#6N^lbreM>?Bx|!#uXs^Yy3fL8L;Sy(4?Y;=%Vf&mVmMC{5+pKN;g^{{8f8 z2H&5I@h<=Q6{*4Zzf2CkznR|W*T0$_!uck?f~%skq#nPoBME zX#d$j%6q>FbkV`#e)1QYQpi}DlGL_ItMNm62b<{&yJW=eOSHB@A{U1e>2pIeiB7@s zTXpf~C_Kk4I{h4utgcJF5bk)C^cYj!`KOmob!UIJG*QFaYIfP)m^&3y$PaoGHCcXo z>W+ISN=n9P_~zca_IJYV_do&ijqM{e=L}xTs8XDB4EE}!x)4=vrb?ajVE1ziN=WPd zLbU6(QA!d_imd3?J@p)@I~G#o>A0fO?%OasmST29!*8g0xvfXdC0~AbqFZLvy1E*^{2{)KD^B;V*R&Mg`1?E#|6pB14JS7Ls55rly7ouH?HZXoy+2m* zT=MX!@b&-6ZBO5Z&H}8fEo1o8TqrwhomOun+ehheb}|brpPonxZR{=@UD1CU$oxuU zD_UvB`=1svzp{{7X(98U7BauGkXdOV^Pd(nzp{{7S%g71SXcAvqF-d6fWUM1OY@x$ zaweBCxlDs&KDLD8tKB%=0<(J#w-o)T=Z)r~@AVuWSF}`BN9l3JQ6Co)de(7#?s4!( zJ%^ivdONr@`z9Zk9Tl|8Ud*+%SB5(-0@a&z-h1ogG0bV6Ig)I?mWrCdm0JKV)}?$p zx-635XgW(r^`Xh`c!N)zcEE`TMPI0QgQwJKKVmGQ7m(2BNws#-DIy6Sf3nxTT4dA{ zuX&sqj~%z04I_xCC)U0@+fXo)=o4?0L2M z5z$a_foN!2foP}`8tQ4|XuYS{ErEuDcA-?m=W7fNb!z6uU&ojYgqXaR;%j?eQA^j1 zD_*Rp7hNZ8X_JbbhOlnp_aCOZ6TaKvCjK|nR&-OM z=6)azRlN%7bk!YqKP;@jj$}*8L!$tBSG1-t8vbc--6s##wckH?w&7m9{3X5oUHdY0 zx8J-R+C2B1p1m73`E)j?dpz&yu0A{Y$J{QbQDj2eAK6fIwRw1gZ+BHNUaBd3juYC0#;=&;?V$qt$G4f ziNQ7K!cX+MAN&1SuIczmkg{n%&Q0KQGl4%b6ZYfW1TMGtW&(ebo51Bo%K9Fy*}3Rv z*$Ia1_zt+5J;U6zi=51UhD;SYp|eRsY+xUN>@JT>W;LY00f}86V$#@UX$eVuvHkW_ z$Siq|8(UpaC-t=uqNv)mw5Kmp+`*sm-|c?>%92Cw*5aTC-`<1BBvsWjTqx=t0aR7)%a@UGdfW)=j9eCpN&dLrB5Zknpr+L`R)= zLfpOR1Wp_o|5LE-j{2I%**6S)CX zbx~P=wEfAs-)X=_fkuPLhMIL2H4lob*M-*4odG&UE)xfje6D-_jFLKru)e8eiXDO2 z`6%tMoyC3M)kBc&@}+rrHuRY{&yiqwX?(1^ntc(mXQ4{c!|@`}1EF;lk}V(*AoO-5jCYl?LhlLD*)H zu0cqr2dA51t*Ug+61{RwpYLMbG^erfdiPx*R0gz_orBTXeStIu!jc@Co4 zyWz1W{%q|ZuGs^m^*wt}dQ41wQ^4FEZeFL#P26@D)u9s z$H=aWt^PApJ?&Q?$pf{xx;AO&oIM%%j3=T6G6oh2onDF%- zJc)7&jyvJ|!dG_M5#V~QzUx^~Ev8jaq-+d-PDB2KWn-iVfMsaE7}-l%IS#=z@Z>MX zt3=Jlwwv?tYA=%`D77lyH53nQHGupbf59s^ZZQf$)@3G9nd^>E1iCzEt5VvHTRftY z65zqUMyzD9=i!C_dbA1_Umjj~;n6Bs40(8A4HdwU5x!zjsCQurhouQAwip^onz3A` zXJ)dQU*>Ay&~H?d+`0$vj*S4+&cLFhIxbUKl6=2_rjG2 zf*wEQ2wZJ-(x{%k z&#hxFIn%7Jgmw7n0{G|&(eS@vX1^p<7p{8-&RJa7{zSOF7_LX{&3afDj=f#i-XCu7 z2Tf5Li?N@?MzikQHpNYxKvu1<_Dfq#)O7OGS$Etn5Q(d<1Y!D89UrUvOHum4rGr>W%~s68d^8KN4-&|-T|QJ*HcR^rq%rR3QfI-^6HKJ-Jq%Wi}$TJ+SFTU z>qQ}fqxCVyvFaTcn0i|BMuOLy{$0o4=HRvb9T!~evBo^Mm|K6FnKwAVeC1DnC+Hw7 zIELx(1C7k|=gm9FB2-|-!1OF6;sAPgH0*}Z_ zaFKA~JuCwo+wL5M5C1?1EIzapGcU7RikZc$TE;tCObSND12}O!b?scgS6z!2Pt*1H zuWNDRx5rf1;>Y#JR985%@M6C2;m1+?3yv)O2V|4Mj2wPk)lzgVVnx_Lu4n`t@#NI7 zHQXTmxk{$87`)|hOYwc=!|i#crT8A!8Slo*68t6m zAKZ3H)F2!_cj?LGIyx_uI^a$miwkd5Ze~hd)!NyG59^&>+HYQ-K!C`Rf-}pldz?H) z)6o!nzDwnQy`0(5!xj2X*WxA!1$~~m8y7s_^vEkHd^n|P?Wn&Sm zT;kDKtAN5vVgMBWF(9oMRzq?ka=?(qHQfIFnv|K>(rtLcw zulCcrG9P!xk6|py=f_s8cZ*NJBZhCB2OLI`?sGP7^3E&qigg^taj9J9Nu!9ve|<%l zJ~cD5{A!a-?h?fb7{>wmaa=Fh#n))KoCPhQwr=6XJpz zcKlV_vQOI^E5C%F;3GNI4%)2@4=IV=8HAL$de<}m&c?X2qYj^3yj>Wa238zMJ~y^v zU22mX1x34}UC)urjifj`2ulD-$rK$=Pr-b1XQzj(Tj>3@@zxuMB5To-0c5oYUV8Yr zM3sGHtsa7`OAH1l`$5o4=wRIGu~TevCW3wfDOna}fpRuNJ2B4*1pRTZUM&R?!DL|; zh!}U?V%JoD6-*$96tdsOw2>#YKYneG34Q_k}5I%Dk+mJ0qe)#DOQc8rrwj#XK_;xR0yzvtkpikHa7R5m;A-f?`8R@`sn zX}b$=8|s&9y;xVmp9Xr4KkDoC$t>vcYDhgG_kJm&@0?(Q47#Pn1R>uF86e_~g@2@> zSr0@0-8|i4k~Ys=$ru%&>f3@fUO};6u+uBx>$XOpS1?`$EPTdAxr)^BLZo4WK5hEq z)oz$J=;Lz-ef)<*KYsVnkIVe0SIo!Fw*-VbEle6JhkdJY(6{Cc`j#ZD3UUJ#V^V<` zsA*awr~@*QhSP>Mofy>g>_JV-hL~&Gw50yk0r5yf`LLGagIb!IYstK*uh#|jV%e}4 zW4RYiRW;A<3RO!7RW%RQMBNdU>o!1kW{Aa;!{7R*mBW?I7sk#&2T&{E>R57EvoBWF#DJ|HK%hRn!tND0n7PM-Mp_KE0I?Vltx4GFu!r?@Pr z*jsT>hn5##Z(M0i;(SgD=_X^_4cSw1B^lsvBt0`c1acwf_Tz?C#G47***kp`;Yk-X zUceF0jdV(`V2zoEuNhWanf(>6DB`K3 z{zTEllrx!u0Mk~_d>G=Ha*^(QTAVUpcRno|(}oIs6zz}^Oa%r-X|Ll+ufV6pjw4D zd`{Fq@HvnA2ZrlWzdu}BHm}qQ@D17kD zh{6XN6NUSY@ou~Ku83}mXnOFwqG|tKYA^O`iwdWd4XMqFVLs(!R171BoilGlgh8Wd zG@N-eO@xZ_zygD1>p#nF^CixH?Rx#=oO(7P`Rv$=eHVWV7r9R<7TcIOVGZqT2cu;Y z05KvXGG&@7Of`g{N>k>VdqWDk?+|jBB0~(ONa$gTJaX`+Mb9QiOe2;2ZDC7+mV>!3 zHJbZ@FR`V=^MNl3QB0BflA)o+rif;XOtYtr)NEo%v$F@9HS;-NuTBgxOB1};8D+Vt z6ioJy;iRfu590$(SyV8uYCi_-@m}_aWGVoxYa(OJ6LUXDYS{n9{Y)#3W+6o*4u(3H zYT6e-4`qO>;q>JnmT6$v$>KFT@R$j0g`wjFZepxX;sPAF)ZoCUbyjqH3k~+34?SmkOy%%(zQS67w3>pG}Q55ezC`aCYEGx zqe#;_C8DuUtkl@^_)g;x6!`zcbinVCyvUF_a`^b!0ZoYxXFx8%2Vmpd>(tA z;0;;SA$49Bpi%^@+b zPAwX?6fJK9}tu^~*dkA7-U6#xL{0tT?1M-W*Kv=p2U; zGf()%13JfHD8Ab-9?&`NqR3#S!Lo@)V&9kz@yqfCJKC1z(>y@m<(K7s z`xd_}@7sPbG6XVc0 zUC-tk!%>Xv`#S0a4{8;y5z+~{fJWxB*@ zSx=h3^jRSkWSqR0%8-sdn|QlaDKCdpAEh(BDK*3(h-gu#lbNwKrYP**rn3>&sxWQ7 zMtIj-s+e%r%BF*nIeoht;`~H+dLIrFDcg*IQ9+&vx0Vae66kuR5QBN#y(Unx*I8Dg zSDm#H{ouc?vklz2oU%?Fp^DtKb8LmUy%PxH&gy1!w|b_1R&DP7U{g4)ZM0P?`O&&m zh%IF|Qm7?q)wZ6iTFy3fFkSJoG6spOOxmS-UwyUC*(-yQZm!s?zu(-izgPF^@1i3l zm*E1=J*(wxRpRRFEoY0nadlzK*%5nnUd!3V_G)s=*(LVstd_IK+bd>LgX(#s#M1P+mUCEgaq)P|IhE#OTgy2x5K1<;oKtNs*0r2-rn#VrxoYB-mb2r$d4585 znt8O2*UxV>7u#CSpJp!91aDsCzd28DKG$;gY~txOC%p2NIRxqmVyZZwxhtXa`krkq zUxAFc*xd3JXom~6#mDEX>b91o^g&*~&8#IKZ{9`4q!iR1U9W3N$``?0A0BtvVm*|q z%8S3&Ql)2Gb4HcSB3{>r$6eX30<(zU-10jqu|1oc_wt}^2@n3L#eB3$89oU9pbP!0 zHT67RaAW4ldKqnQF|&_lI8pN(AxSuO1cMC&jc^kuj8xAPX^XMx)dt4`tA)1d)g}C` z4-kDN5^!UC3HQ=f`j&G35)9k13Cl+;{TqYO>q(uGd^;GvsR(@_ntUf1y*Ehj>cUfA ztV_KX?zl7}lXAlfj`J+z`{onMXw3PUs+VcFfV^U<7vfIW8d@!gg{SPImBK}5t)abw zSOs@SpxGDv7uM)4oCt1>ho^3->UXvz4~N3Z&zyi(@*FANXP-o32=c%j#6$#^MaK4E z_WHTSc(MK~f)q572W78MwmpYi>Q+!V;||z{Ye)&p%nfnZAZOa}FjA^~cCBE*ymxnZ z&1$(qO9QUH-g1RjB;CS=IXu@VCLvd5wM<1w&uW<-7pAD*REyLe!m4Caf(^(m=4y5$ z>X%__jZBedfd-ceKND^|qQ-@2nNKE|+H^|uDWgA~wge>M`gUOM7gb=$!HCwkN6r0P zm8cn-K!15V*arWMSge)yV(e9eBh?obYraN^RQK)wKHLm+#c6kqGAYpJ%i?DeiAh6L z>Za|o>lv`2BfSFGH0L_riiwY4Hao~j81Rdd8dfCV9TQG9jMFPZtbpuob4SzNsc@;Q zTBb8`;~IX>#8qw6{nWAscmCK+I;F~*UqaW^CiY2Jzur`0G;!y>>rG}3$^gC-<}l9n zpk=_A`PoEv;S9GIbW@!RKPh0@X}yLJ5Y)@LC#pzqG5m3LK`SOO@fW~DYaz%`_5n;F zf-6`YGMjf~FX>6N?lRQy_4lEMFP}e%8tTJSyJ=_aqJO+cD`fc5wf71CE7m3Z#zGOx zbORym$PvPQTBeH-rf`U#2;q0wScOmWs;Qx-O6o@m$riNTQ%u1hhzG!`0VY7FF7-3tJXR8c=p#%c5#~HMwPxAJ<7h43EjZbBD*Uq5(Cd9N-6hh?EbT$g{bS|UU-XM?Hrf#ql3RY4G>kKbbO*PTc; zvSjp%40PCgxX)a;Q=$83`{ET32M%*A6IHMJ8-A;ZJ?|q5cCs@eQ1j?j>~A3i;51Du z1|)%&`jIvffX6DhopA?$E)&LIdADWGAgO{hESpwf23MeS#9o2T#rDdweNX_*9k)z8 z!=i%E_znIDgDuJkgSp}<`03*;-}1x-IO<_5IO<_5Wph+l>|Ae7nw@KHu77otkjvnS zFh{i|Hw-EX|HLtgK$IC71%n=NQMcG*oF$f(dhC;sTAyj67fZ*;NvO;Zfx zVg5Da>PV|Odf^GW2gA89wwzW-R)1}8r+$(>CE2g;7qeZ=E&eYS4}~Fv@Nn#V;NdK- zJHW%a!SsinlPw;OS@ijP^c{3{_e{%S-Qv5*h0zb<9)RQG0}bNpY_3%Q+`6x#j?;3O z-SAuVsD%^}QCwJhch<$NBp!T8T> zF-+3KH{s|nEz;(+YoWqQ zSs_XCFy_{Escj4Y&wJwRW7M^8nq(-OxgCzVpB1M1n0t7%#bgh2lZVGxOkQFzxx`|! zdH^Qx(oeEye3snUU~)|C?^_{G+wPS%EKV1&oQ{JI;}!ed;PJSSCi~!spXp;&x~s2h zo3k~!4o{XdPpnyY)tlJ}#ikt+Z8S4ntHo!(453*6pkrf4qS+z%6kby^9gV{F(F^qjv*QgG^6#G@< z1KeAfygv_<-yVX=J{Ifq5T(|Q9#v%youLEta@b7b!G)9R-I(~UVbw@>`0l#;njX(+ zdn4GEG$sqoXp4qna31nn?zLnaRvhZdc9I~g_Fm7(s!~aeK-5kH+_I(t8+dxIL^XDp;NZ~qur4|-((M!)vecOfWVJ#e`*msj_4 zcZ{FEl*Y+dr**}BT7%|cIND6|a3M(k#z;!dJ0l2_QDC^BhU$J-mCQe$(r#9*SNIqn zHp2kJaWn8_Nj`>ubtr~=c#RjDS?hoz>xI`}6<%YT7>>hwIz29wdo>T`-tkdxD6W~M zr`nkd5HjDb5xV6TM)-OxJe_VDLb1h$v1xr|mrQ!g6v_I&&@7oO-Xq9E7Q+NsmQ0s` zUPaj-zk1A*a}OG)({B;6H+H)8-Gc0VW}Rp$nHjP@oF(FS&R1F$66P)27*wsGIj}0$ zjbxRczztyIx{4&*s~a{i)8b{353B+pVpx+J2(&HTIb(A5oyHHQK<9Ko2v ze9q9AY5yV`l#wCEI?`|9s=Ai?EnQ1%kvSO%2eVz7W2WnOj=aZY6rgjAFd139MdHa+ z@fK}FwQuu^hfKyFZE^l&-1Jk*vOnVw{=m3l=5($qPriK%`DXO4z-GI;3}3gLjtcIZ zqRvLb-zPg~j;Yyt)f?Hxl*c6BhKe_&+n#*s_PBMC8401ukm(`PAPyRCzuYUH=R@0$ zbgEZ8WTe%$*dJ-Oocm)O-9kDy(&#Ry+*xPg(;FLS+3xMHjZk<4ROM{5>-65($sU7- zB@QdBWRI9aqvz(@tbxC0PJssud-6P^5PcZ`<0faLyz3&$ZV>Hn%Vj_2L~-pwDeNL% zpKc?yaRX_vu3X_PxEIft_9ysQ)Y*>4koua0b$Vf0ee2c+yw_7?${)W>(!|F7ZguK5 zIt$jgXZEO!BNuSJCB^Z=NJBb~vOQxZnMh4_(oe4=jSwDPxI?nBJ5}#v^`Ov?8K2YFnCK6zDoVGdD)2@Ge`XZ%@)WkGn12ix0 z#9C9k%b08S2I4ha+MZ0e-Rmfa8=Gh0_FFYDQhO6t!1vX?uhBAshHNhv9}goqo*3Sm ztqbQ9HafU;6s4CBD=nmyq@+XY1}Qy#Sm|g=%P2LGlLH^D=~{TAtnk6>=>jr)TtT#V zVmXi~Xs9_7PA%aoU3V{gam~b2SUi|-OKhyXRIGO2lGGY&jIkC z(a9Wa%#?rDpM#Ce0rSwbTyrp1a}c%0u)jy%=1sy!XeECV2K+~vhZ1GXc+YugxEJoh zJUpA5hXykbZDt-G`(I`rzL!4_JN$XbD;-s{!=Hz|(n3nTdB`gbQtHh^Ug>B`2j(I7 zpk{+N57*|;gD64s&_0j&63|$_WY=>^T<0mJgc;Y@JjzP?qou~=^JmX*tOpnxYvR3J zbnPju>LmFjxXWU+T@7NcninY!cCRPbNRYTXj6uP%O3x7+YY|z9`-G3UCS-vuRm?3je1v^0cA9V3up68sWUPloulDGdcp#I8lO1Am;O{{=lhKQ<*$t(c?3efT zD*6p;Co^h{2hLn1?!3~}q zFKk*BtJvK@3<6X(;o3O4_4$y~9C8$|GK_e#UuLE0^-s$%@n1V;_UpJ97rem)+th4r z%a}BFBF2A3ND;_f!G5F3Hb(-wc(qAD_bi4(n}F_MJlC%H`G7xx^LD;V0kILrrNG&* z1ir~vFR5=GRZs8-E(B+K@g)m3R*^FHyoj>dw`)t0&BSo~d@h{(q2bhzSYwILlMUWX zL7mgnke(R94cl4pQy4npp>8FqNN-a9`UH}|FlTlrHAyh{HB8vaG6Z_}t6=ms+RI6u z%{L6M7wi1=-vi=R@@QAAD4n{+G_7Sr-HOcbKW+ATU^Ik8 zX~hv?BFJM%#TY^$+%b-pl&Gs;+`pN`{G5)kQr!QRih$um1cW4*|4XR zExZkU(`_r9H_)3bdrAW_XDdJ@Q<7-pCQj zgA`T|EgVhZ-%^-dhlhCXTd=_s0jElqny=rio%cf=V4^%~S0KgGbw7nCt&BOBfqQLB z{lHFOUGMA%+fAM_^5%oF9JG>fIKgFuVN>mlON@(?O)?xfV)LyVKD7Zod3|Xn3b#P< zFHIC)wkmc;+nV{0#L}0MA&HEMuqt=F;Z)I@C&M>=Q_4}>3U}fm{0Usq7QJ#W=!>`B z@61?SK|UyQj=4;$7j=rSOu}vJB;<;2LPcQ8*2(UfrmNDxK;)&0?mX=V>|i&S()E=I z2R`@U}JU@Slej!z&(GIBi%ltm1)%|2V7|PVvCPPg4xO=Ql3~ z61I#br?^UM6s~5sv7GSa7n7!Gb4p5c~9cQ!NlZq>Sk$8Q~*~LUMrJP{E;SHI8 zoUpI|tI_7{6q73;ERRYp9nJKYzLI}wz=el$cr-k&cYt#JIa6^^y_>En@vAuV*~ zfp8Lpsm-HVkAw0^KvD`3i7%0dAKg;|Pks902#k|C#l95$J*OwSDvl!wLA#ChiX?a1 zSQ!;nqAmCma7{dL5cbNGRrSu{aO$&s%Ukn&1NMUO+XzV_07-tV!JqKALTGaQqti6t zWrz4%bE4m_5_-(!$_yJc>U z9_T^O{3%mSs zPQ8t{W(9{TkI3nYj-J;-l&n?fsGIjHndVN>~+EoM8Riz|4v*Fp5HnD&Jh{i!1~C; zkL5Z1*j<{3A1lPVGs{2j;|J<<1Da72DpAnG4*ZG?1Ln z1(2-T<`I}#kBu|yv2mWl^I@a#e0V_Nc_QWv0IrQMJ2q33W0>K0jW7US=c-Sw#@(O9 zHd2LnBzT28{$Zp?f?2rZV2>asSrK^^q9I#{-W6 zbb`URTkWDNIt^!Iju|1bd$cj|hRKkCA!Nc*PEUhOGt+C8ioU5+rqk>sK081UzC5wr z<1{bZLn}}=vaULT>d1{t7`s_KL7d2pD?e*wMhD`_E~$QQmTJ~Mmbac7DleRONNzwD zeY2jbt#AD>XQ1GmeO5$bgQ3eEzlsri>n23NjEy-PS%5?gm{kJzvsEd%!FhB>Xv{B@rR+PN)TlbZSw5VV zCl0-1yqE%_$*IFs*07vI-ZIynf|X_g+yE((u3IWOkwRKD-4X7PrcV}UiH&Law;(mz zXn=DrJfuzp4*C4i@^xy9dZwmiONoGRPvUv*=p3HNQHw zYu%AlHyYn=O0@I949vnVOtLvF7gDOrFgm0aAq$-)hB{nut)N z!mqnIkOUAyIEBOz&ODPq2(?@B4e-Ka!p2bRVw!y1&=qzV7Qj zHcg>yA|7D~O((u6(N{3+*gEFXF;gD7WG*zC;Pf$J~x{`_o6>K9<|gK3e;Z zkzZuEkI2|c4p%iNLI$!%K#6hQltV-v9wH?`PKVb>LRb7PcE}rhGAj~fMm@6mS#Qb% ziQwDTkB6H(@77_&h>z5Eg>FfF1he*$skkaQeJF!^9=k7-+Lq-2Bb-<%&hL9X>%Ypp z!BXfap5G&v4LGz3kYnWAN;uuz0{1_->^5h{wEw(Zuq%2RSU_W!p zW9xv4lAz^y+0$ita&cLn%pW69m~XZ3HT(I!3K~2yjxmjQ4`W*H3YpJ4M@p0Psn;3L z2to4emSss+?Qynp`GJ!p6OVl0;WfF zJ^>2Euf}bww)H0#I|;(kcnAm%GR&@F$4Vx})(+&ygj#e@#K!~j{7oGbAkkii{ ztnc9vtwuKcWS~N}cq-P&nN|LRrLD!U)-`v2wZ=bxb!+EE$=2fM8+c=7bMd^U*5auI z%$%6Lr?qpsB3xQG@!$FDnVG$N5eXz9Qf4gFTY4KI139q#Y>#&AoE=n~^^d~>`{6T4 zq4UulZ7ndz<6I{W^&q~wnOuZ?a#OP;RVz6MQL*3613U=TH;DiFMr)X)AzV%p>Jzx^ z;?gS|<^b8hrWItIdpB9<$vW6N<=$*CTN&pE;adLpsx~JUUu=SmEo+x6zi}2ti1cxH zu{_8j23}#1BN^`T?ti{{@M*GY_NYSUL;XADu*o}iESLAYO~rb*CdD9oHlA3p zO&=DXeha_l5A<1f$`gsj{md;*g59JG1encQGj*YlTbV24q?kN0^KNp+UvzHfomK9N z!vu6y?XKMU3cHV?SPO0RK#VypotrsR`h;_&@{nc!U1V+7c?$Lad?snS8 z;0PVyq(`TM$sj=y;QTIjONru<_tRg8zk~Rtds_Xy_>^LdT|bxW+0bPSfHLk+C8gCZP^cv2cJ-h(tQ0toT zJxM8LKpdwcaS%>T%ajXOE?8r}l?AIg{R2nfxG5Jez4XtIDuBxJhdEjL7i{|Sb*9+5 zl=<01ZXmou_XM?vGY>nOD65HZU^JeAM zZ^dxS6-1li_?qxB90zJ|NuODqU)Fyx=ilGzzvK-*e+;VxF?cObj<9lj;0!TUje00v z@%C}Y({8R4y;t>9Te?p4lo!uCXg?2%|2CB*0hs?{AN2OG#Kb`HzRZ-j62XV*FKQp3 zkmZ)IfxVEqKIJf#qH~tO&vMLmOvgS`> z;|!g^q+3&EOuDNPOd6Gcs-oZeTa;W7P$E?sXW`ByU0H58|2@kt$9U7F#J7>_B{=oC z*3Rc)lXUM^`9px9nnd&3!6(M!Kx{6uIEF|7jZjTmV%Wr&Dy7**2Rt!`=jK)#1j zLZivz?&D-wa_G%OMO-k@?B6-eU*N}pJH4C4SUwVaf4KQXiQEMP!M6^!2CX$D5tSLV zzp#hpx%CK#YZcuomvVT{Bp_w{f;Fd{e=B+6vYX_&7_Q(-{5bQQ$2Cc9+}Q41cevk-t=&?s?M&K)vGhw z=nf;oi+mV#A8ZaztpVV!blVbIA?K6f=czb|@6fxc^H-l5WQK;yHnS~PlK)aiLy{_O z!A*>Qb8G_}c@^CuK3Fych)mj1T47L(pO&*fbN(hq+2aTlkRM!p7F@g+)*i>C01r19 z9%lD(CvA9`eN{gV53@u1X?U0g5BGtGwNwBeazFkP7~LkyEdeKuJeCN)z%v1&o|PXQ zqX(_-L193-&dSd?kp^Dn3r)T1Mf#xnveE}#`7E8@imrxGlS9v)?=g&bn8_ z<2LXNmyyFggF^D2o04Q8ND|VP;^_jx%WxYA@1%!;eKp`5Y)#5h1aukuaBqTphrqqI zVcfeQ>2Pm>!@Vb&|LZeVk0%yuUCMCpvxa-GR&eiml&JM6QO}f*<pjNzh?H+M?*g51>tIjb1OI@L(m4$Y09KtB5kYxPy~M~{Z?G}cSd^C zk$CrL_3w&zpCfq5;oWNC9dmqUzwj>Zvb1Jz{_S{|^$!)#znKoPN0Q@mLx6T=gnDrH z22Fv~#?-n=TiXhI;->}ikJ97SIUGU*AlKyrJwYA_D0E-2rUHGpzS~@GEDbJL*>+lx z`Di^r2>49eYPhG5z3KW=NLX|qhP!Y)8}^+p>@)o;Q~c7+WdK~kfy9{<2-Uh$4W>$O z%Fe9MZh0Xz5crfqU6kPyMSCZAh0=+Kb- zdejrP(H4Wl)|DCmOZeO(JWSzOQ!t1?pDe2pB+{bRn>ho-Ao0Q#;*@0%gDAks0w8f% zzM)m%WYj^LC>4MpxcCgX_)r;dfTRc?*Bd@&>W2X`Q$LK5Eb8*AgUC!h7#S{DUwYhZ z(H;#N597BK&)-yrL&M3BIXJXuVJe8HRAi;!CYbAS+q0GuHxSzN!8S58=5ugz7SfA{g5620#e z^kQ@?1$Cx)QT>LY*#k(3XeqV$WflUlrgnDU_F}(VtkaY;=j+3~87+p-T?CL(i1vmeSEfakw_ zWc;3<9NK^)!}HVL#D?KFpuxR&M<_qzqNa9NR>}P5J;zrkgCGGow%WDn%jBIs& zCSTX*3NQ5Jz1eEM@@FTN6R;tC`cH*_Tna3d=?`UffE_~Q(_ZjX?q^7Dn?AB*eL7oB z$%Q%O{UQ2@zX8BBwJVr<6{ixASeyTv^k1WBnGeG5(WZP=}-iEC70jQCI zWyO(2m$Mpdo-|-nmbrTEdt&3loFS+oHwxH24hGj$cQN8-J@7>h43@2pS&{9 zj(^vkrs&Q>UcS`5G}&&sl%G+qSUO~hm(9{}*wh$hwqF}5HsH%Ue3x`I=P$4Cp^yqb zPujsOXhcuj&s|NVu3RVWY6)((@n{LYZv&L;6i%_K)^23zB>A_}>2|1#Ae|*-o6y(7 z;(Rc%KR4c00w(lp^qTz_a{jOMDwF;3R{vGBxw8D`(<%Z zpXGN`csvu2*BZp{yq62ERu9T%t-i@ETBmxZ^HyzhaE%@}yY%RJbf$7SgU3nxyMW~C z6x1?hF3Hdr!C1=0DyW{aE77O(RIUH&YFA{uX5d<>I!Z#?fuV3Y)ifC`PykXCKC|^4b66DooTEH8Gubl3{ojP3zLXaZR4HmmdiTmml9pb-{r;s|4btXNnpCx^E zMt!@)OHKor4-dr*M;lv$)6iny@Fbxq(pboXQX)1gmm?OdZw?%PH!oT*w##!&f)z;7 zuGAl@)bXT$Wat_*a9UHMS0^Fzac+dY0i8)igty|z?9i3*P3t#O%$ClTGe z=*rcdZVs~m27YCCb4(HAenxW7wr2FM<4CM?%6_KQGVgs=T zQw)+0uRTTs7@by;nQ}pWgPog@&WPF?!OFQFjfuWbYhsBQ?gRaj<4l~$cA>JHaU|trLm1hCDMH;JGVI!IA+~5Us>xWVg z5l&Gk&s3C3_V1Yps7zS&$Bh3J7Jsw9VeEJ@SWhrsZFIp6mz{Ni2lJ?-wGHz|z-|%( zjWNF9=cONEY(;2?D0OlZ#4Ej<5X=7`;MQ%(27$}(7q>pAQhztx>dQe$<9b9VEN`9< zp=}|KL;{Izu)U*5}^TIGtLJNkD?W)z+84Kl4532uE zXWJxMW;;EoJ(+-`T<1fK1N5rBPV&92g~#(a=DSE*de}lguaX&>79~+2umHm)?=gi_ zdSV^#R9oSv1~U$g%hTFSHfZ^k>`YkW`}EI?JsOoBoQ{8ab82uJ-NS~ZA}@%qk8RUY z4smfW+P!FpzN5M*tC4Y764p_l=vR7`P1WLA$ngLAoO)`=n4_`uIT}^AAODf4Ru(%q z;vceG;ZBxKMp%&U{716~{YNS;pGD7Wa79`v6I=BAb~||hGjJPME!++HEAvn?*vvwy zZT)bsx9kFj--d&!?C35pP_}tFi;vCVC${KC%Pv=0~RZHx)au zcWd!mXvQ)Iv&A-C3#ZphhDFo;aSGN+J~f)a9W2ATRac#=RLh)Y?P1h9>sgT@$zR^k zOdFyA2LvN5Mj3}Pd50hHcDujD9a?2ezZSi@p7$183ofXyE|}5SB_Tem%8bS|!Ysq% zF-GHDOvLFF&C@15rR;e)k-rqZRu+HrVZPsB9QN>)%#QBJpHZ<3-%X>%uV`Xa%^^N! zFqu8r!c5JHYLVnGYv5vZR?8+7IrRu*kV12jC207`g#m*u>fB*#Wt%W9?x|5DBre1+&1$lbiCom33&={P42m`OLNw%W6txRlxZ{9G4KIUhug@JQd zih@lNmW}3_ovH!Mhx^}zD$+fniu#faPB**p&nUJZc$w?$MZlUbCkv)xoZ-da`TWgb zEOsT99+J-JI8lJ4R$U1rq_%0y{B^35@R-7zRf)a^EJ2LIx|+nYKc)sTYYTRwJ~O^C z`MN>do$5t_8r?M64owuGKo_`O8%g_iQvwQnjk^wED_+V4XAOA>-{7Vm$Q7LSCJ!=L zg1Nw1@6I9=PM9?0X)@<;%lUgU{%huHV8+O>OSS}O^*}yZh4om4C5Aq&(G(x>yMupu z2$r5MR%mHqk@p6Ng6Ux9L9B@BjBQd|6&!0xMX!e?HLMzxel<^g);P<0PN})zD&3?m z@!6Qd(<*!s#<>|D{OGbDh4pDn#&p7xBT;x}tjRbVieoYgR7V^rgvm%hl^@VeQT7F0 zRKw@>#ygF9md>4(AcjdB!?bA9*ja7ie5X*ROFJUV0o&0-CJmRuSBNmUC1PPD8n+tl zUUvrdy9<_PNDK0w8c}%UX8PH|(ngVC&z0hiPtyGVlej}I`>*oHr<~^b-@_k|<7WLY z@JF8ll^lUlr=&1RmX>w&=FUxQEKIC=NGd2=kzh5oaW+Gl)Obc}_rg&MIWtBheH4W} z7|BxQ;dZkJ%?9=g->*)<(xo$LRd&*5ali|-r@{dljKhr7BIYdZ0x6tGi981lJgHI5 zWdNyd^`99A3oz8c0#pdvhWwpO{`3fF@cidfb~1#}p+#`;7#P{qf>9utaQiJS%$t3d zhs{5Lnrq%fa}Lam#0TlCo$@Q*&VV0H{F^FtZ4aAW4{`KdP~WuR$Fvte0excHX4uA! z;AX!&{tgziWR!x78Fr*X_sZDFNpX7Qq^4^Ok%&-x1w}PwQkz8m2K_hDG*R9tn?x8( zZfqigRIDhp&0w)DtT72e7;AD1W|Nv7e)084xBGXDICKWn4@%V-PyffTP5mp@#B}Ms zX0VrzaeuVirmT#fdq6J8@GtSzIAt`?vZmHk-3!o^6_17PrT0}{I+>S7Xso~C{bz8c zxkBH>`~2JAzpe8A-EaT?V%MJD%KP7a`}dc+_h(k#|J%2Jf4O`ALzVYWdHeUTckjP8 zuH4@f-v0gXyZ1L#-rs@XcpLiM?B2ho^8UZR{rk7N_b;rx|E;%w|91EOCoAut_4e~sy=fyQR}Q}MHz^QAK^a8!u_B|&+UZc-YWg<+!$Sr<0`V3h>5w$vN(JfW0N?Y9+DO;$xdQT`v)Qe%^JX! z!-Lg|Y8v);db5bYChqTXJTQkB4A7v}V7kA(?eKy%;f=y+3lg8Hu<5}a#%c?e zh{s^HGN0LUt2x-q>#b#e3-PcY!Efj6IXb@)8#0RFdVdqYnVUZ1w_~iZ1@PO2WB6^s zD1I|iu+uWJjErOBigrRs;ornVH8FQ$jAJ%;d<|zYi2q`@i51*tgItWwGqF2Fg((IP ziQb2ynLC-HICFX7@qGc&mwtA6@fzd91RkN=U4F6DHly z$?X<004qi7><*D~E9Aoks4Bdufpkn|yclhekQ@7&v>q|r{Vg1slU>nVVMNEjgSlb^ zuDF?93Q5PYa1|f)YTyfxALnL3IfS9}2u@t-peswP^l4>oba~*wy~L{fIFi{*z3JP@ z77hWms|7^A1+Z2eZ!`7-?5;6{V`?}oWP%ujcCzSd(?z_gcoTq30$`_1(W3Hu z7|c!D0qg;NZ0v>$QPgqlIJ;CTuH&~gZ2n;9tof*)^mm1SRN8m=vpfpE6=#~PX$`Ir zcahDa+4*ru(p66od!Mi_QodeU(F85~PoYVrh?aNW*)r*Gv z9xKK1B`yB_ZQeAAn((~CPWksNwfzK?elNHdWt5Iqf>zO369(unc&no zk=q(;qU>0;B#OGg5#noV@PtYx%jwaKHcZ@G4q z{(%Jnm}D{ItFanMC&PE>qt3^+vHi%HvFMm|MT?^kL=aUd_;S~D$YgxIt$2eYqnl=+ z5IcMQ=M-DAV2wHCvMX4;_qT>$f7;rTBm|s9sQ1WzHh|G7>T!LhT>_ET;mr(M>f%9d zBf~R~ddC~N=qKwrTCA?UJU9(bKEKCm!D^jQfUOwBqN74~31nN%c7kHB2t>2l0Jc4R zZ?vO+v#k zD_|4G7nkCT(;_f3o@q0tSwwron_nKuEuR&)z$W4tV5di4M`?Y$SrL7TrYC5n<@_bV zR?4Z); z$ZP9wLmiCRAhBwYeeY<4el)-E^ZfW>wE;2AQtU9Hf)uC5KLeLHcx@9pthuqqObY5s|(Zzp2_m{+iU1CIT|Ssi>gjAaWtV=I);8} z4vm~R0Flj|(;wcaef^p{nlYL`Un7K>eAJK(+LzNHkf@3@2%0~dJdM0k&bwr(!`%EC zm3Qf=^QX1nSH|Sm2bn$Vse#$!vr>OC6eHnP^)k3M##&^I-Th>-Q!0efcaPz&QX!1K ztK4`>g)sWAlH(~A!sxroju)ju82Nyi6y?*oZq$sSjpIsxz7OY}v#Lw~z=4;7s#Xph zJ6t-Kai9--9?)*ze*nrz5BOH;WQNOpT6iV!jJ;eMH_i^RQ@C&V>fU{k!|R>ciNo?| ztbeR_w>jN5R9@fl8n6H6_b0gbuc^HMuW$c;y?g({%KP7X`}Zfh_di*A|E#xvf3kc3 z*vk9wKk|KVwnwNydBZ{!%{&~zybt2%Sp1j&$Cj$cMlkoiU<=O6Z6(+&n@_$Ew!PgJ zfN{Sp`!ii$WLp;$iM-J>2Tyb7fJd})rZ|fgFLtdJr&ZwvlAk)wItmV>aja6$1JE*l zZ9TG^KqtMI8?jYEn8pjHbn#$XmA-D|Yei2FKj3BqH|3Dg60=+9lRAH9HU6CVXNdL! zm+N>;T>1AcG$Y#Eq;+O0hDSH(sg{`iTWvKr^0h=tfWOo&6R*&catlik7NY4QI?Q|` z+=UN&D8n4_O4h0i1aKS*pfZ#lMR>jDk*OSqT4L2UB0$2#s(q{y5KVwWaWC*gXF5m< zZ7WV}Q`dS1$!XEU`n{m`{Z2d)`*C>sX+(r@sfGL+lngv+EMC|4E5ti#VZ!aGOjn`~ zDtd;~rNcZZq;Qf3HHed5El5KSI5Ko56+be=stP8*H^{{^vE67ls!oYgCf|Zzhc{{8 zh^nGm_->x6QaxM4zbw~6Pl@_)K0ke&Bod3TrR7FmhD-E;i6lgb7)qZWdD1emErZX& z6W7~#W@9_ylI|F;EiEu~Zeeuwp>@IBKCJl!Q~xl-p7Bl$*UgTQuMc;pIhgtjEi#X$ zQ}q%^%VIX|{)yU+o+A?7fv_7Uk;{7F z2ZIB7;N7bj_ba^EYt&9;&UZ-CNr#Lh(k#Q@6rS{w#T7IZ2I|CJ=bd|sn<9zeQ_Ld; z?xeNw-wmH>Ymv2pka$jDjbE;(4O%B{5@RC;nSjKUU8{znc`PZcJ;`Do+5Gbgc|xIL zyW;b6F(l1m3zye-Y@vzlAp%E9U}_|{*!UWajSFOM(AJs8u(ZuuOK|l1WF-iqHOPwj zqO~ld9|+qX6aAoqEOfb16mJN>M>&e+N~<6RN0DOU0$O6?p4bO59=&OA_3t52pqnn{ zLBS>5jrJMDoJzxds^ftc9f?3+SV|#)D*AT>JNj5b1?$jNEvSGa0?_29x&ZSk!U}%L z6MWZIgq=Sq-!(KLH-PTcXiO4)pRWQCw-MS)S!oNVQrc+$36}f}|12KW2BA)a1(mfI zn;*q^J<6rM6O9&kYp5V@w;80%2}BY&2DA1ncH%Cu=bZb4vtJFqd6>!7Rd{YFwI9;w zf|+r*cBruM8if@ohW`no$cYmWd08!N5^-}HseHKX0cT7_dlVGBslfD_$gM$S!7P=* zxTzS6Hn8x5CW&&3`v1+|#@d7V5qE5K=U z#zUC8;P2VX_Bh#EYLDkKr%D>KgvWJmB`(fzp4dsUQ(wm$sX|E_C3NX-4Qjvb?^cYn z*%H^5Gkcy9RG+Mfymqg!E}aSfwuVy5&vd^l_*yl8<}-W?H2@60wvk+4-^18xM!QiV zxP+(~xFo5W)5bPhCS5w5{)`(82@C&q7*->GGI8X*rR^9s3c0B@QqNNmlcGW>Lxyb> zg*qN)5_&NpTEFhnsHB@?n^^V!(01nJ;`*B@(%((lEBOI^rCjROd5Kk9G!BizvdM(V z`fr@Thgz|HNO=WuVLzLa&h+}UY9wyFw z38p$Dj~ArlwrwT10>{$zrX{F59Y%)Jg6cnVTJDuRwVL&?Nj3AgedKc^9yN16Pa}H5 zpWO@fZjY$N%PRgRD8i1;{h8EW$N(*SxKX&X29h{gM@%j&*@_^@xRA@*%ec{4xq%1f z3Wj7QpNTQMT&w|l!WC{c-ZpELZ%(*^lPAJjLDbTB@_}4HdF0JBd?I*^$R@3LfpG6m zBNJ;W>(#yy(Ky5((d%?e=Uh?yaav^%MXZC1a3W@|94^PM)PLh+Q-pE`PU{k@q};8b z$(LI`XLJyTtxh5IawkQq+np!D{du-bH2c5K`FE4sf{kBzjv1i}-m2x=H03`^lil_f z8eWcS0CDn8mZT#?RI#v);kEY*S)9B@6i85mDRm2e!cW26yjFj``qNaTKiHm(k13NQ zm)tz9<=o|^U|<nU`|NWzPPRHeCrsCtS^=cutdBpEqzn+B(7$tOp)6=s}|% zY#El9j@)eG03He=J<0&y4!6Zhcv=Tp9rG*9D;-HA6hqD4j&SFFZbFoHBlHW-Wg_IiGH-}2RG_aKE{>4 zkM~3zW&NCM*u^}Kz;IZ9ut3G+C>i>5S-EiqM#-T1FGgHy zP`ac~Zf-#r7^BYLCArP7c&1og z4?ZtUteT1=ATnzGE!w9>BhiG`v605ZG0IF@B<*Fs#w;0}n)L6~;86}*5AWS;Gjx$O zPlz|d5FnvhfRo)>0}z$jfC%of`RMNpzIqaVQdnkfOX@DbryKd|rCwnUUn4He(T*BZ zO3{N=eXb?77EHvZ^GN7We#^gG@8m@mM6cM%c9v=@4n-Qi!K;4;$(+Fb&|O9s(De!4 zcP~fuL`Ii4nyRfD#8EqO;0^SuuoH%<%uzGoBB`BA^e)iUl*xv;Fv%@?(2$0~J>%)m z!f0ekU|3GrHTaZfP_&nR z!5X2eLxJAw>f~y^QCdrAIQ3ERD5FaP+;h2#A}6Do)le4Wr41uuJ#Tx^+|Fm?qHtlE z7qEJuI0X5p*>5yt?pm^^&9M9vYt)$A%_I@3CZ!tAub-2{sMyCNZ8WNQZ)Ky|Mk8tP za)O?W6GF_U?KrO+eJ`pj^IhzXc{S|P&$s!@v-*MWMk^f z^jL1<72Fx^(>>`AD(NOnE#gb4-@$w1@C`2E&+z7KoZW`C9a<=0!s5r;KPjwCv#;ef{h$#ANg}F!EBDn0|ng_A7|=;xmm!4o_NF zu9LaXPGg)n(m@e?O_BgPG}|S?sfRH+SPuM|KS;In5aqbfUdjcFmze{Ed)OIai_Zx` z8~B|4L@eInvpis1is}&J&wA2^(keFmmks7Vn=G-!Yn4qz*_vVJ8MYUB6V-|4MR%$a zl`&mKP^y)30i|fiIFOI?Yzo-AU@>(g>0by)J`)`b6gb}s8ztu?`o4mcElW7{Q2jt| z?I7y0g`%#R-hZcQbzQ<(7|z0#D=E zY@trOoluFJ*-C~?D6R)G*nXWu8t)FdD)N@{s<^kEQCV)fPbRiKijU|D&zC=o z^VhBmX1!{oKtub!iNT6%+<%ttiLmv|K}j{y*PK)h9vL3WbpD^4B^5!{;9J#v!~E9F zRHsGNmHhUCX=#c|4SB(t@l%7T_jd<(<|K!O!hCq+oWC!EWu`6Ir?J;HOZI zpZT)Sf2j_6^|j$!*eX6rYI{c=ajeS@NKsz2ehm*s_0 zdFmF2z%gqyH^W+TLh4ydA;V!k$4+HlJsaGrw3(nWkQynZaw;s8Hj zRC|_`iaV1KmuNt5CR~iA;7wN<4!T!ymN`9VJRF_9nc_bQ6X}i^&Zm)rvHA~av~xkx z8k~_cH&&;o0x<+C1~5(R3tyGHBb{J z54;pzgVY!rB4T68fHvW&l3Q$^SDB12%}5JY$tQ#xIALW{jd_4K4;Suwf0V*S3_+OH z5cV>h)?GTPet5yxJI1>{Q(v8E#BjKLE2n9GqsB0D6Jo+oav@w_-@{i9ePmcN^gehCrBzd5pHD(eJq4|+woKQIVFU~u+Gz}M0KNZc9QkLN&(ivDW$V!vkiW<{E zEqH;T-=rPGip12&yUP7^S*<8Nhwd!ojabX^y_;ggT-mxlV&eyy^kppZ3`j)z@Q`Ek zWa(C(!)rZMPaqN+F~=L7eScvH_f`sscN=2+9zvCzv_m>i8%hIX*j`b21ke&bWc|g8 zxzkNKJvB>JYxXj>QNVaowpHm50^coN+47dT>l(Pt_Sxt*C77;=MSPfcbp>s&;z`N` zod>lM&iT>Nj$k3o4^q;<+3asDdzfGzB5<^r#a=+nDpsLh(h?SI)_}(I7QY!S`S~`i zadeW4iLdZ7;Y5;`X?Ra;6Gc^AOe#PCf}nq^vM2T>lR6z?w+h3(Bu0~asom5#e&p?k zq#b^ye4=-T>{$AQ`p4ooTBJxiM_S7_w08~fNZ=h5lXaJOV@nt0cpQW{$Hc5fp}gGR z6nSe>xTo*Vb>5Gzku+%omR&XF&BW4Ih~A(qG%mWX**H%#p-Knv5r*)9R5T(Bs`;)_ z-!bb@2bioF=2rnX6C$u7C1XQ^6A9>3;56J^aI48>v7R!SX#uAmN%f+HzbZl^i{$2B z2yek9AQei;jy6LRl3u`vyq~&P>T9yF76ogon73a4nIo>>LtLc z+XQWoO*y;}=sq(nv>8gockZ6#r^3+S_7#w|BvWIMPu?_YEXOwib8`( zw>kp_z?ofAz`WE(u_h~o8z@?)K%-XY7W11W8i95dN{^Fd8je0Y+vGjw-?(g!lI)|W$U484HIj*>V213 zr!sVVRT=IJMx`zRc~ zH6Q-RS|_hdoA9VOeN5OmiP!y2xXd(QX2%#JGHV2qN^jVe!^q4#<%5^-sWA~Eh?Hw6 z+IH4z5iL0L?%+EJSG|mvYt1$_d8i2wnNXPTWQ0Hid79@0=N)FaJ9P4*e-CNKQsi@q z)*G2V29kHX-mr%D5`A*pgvA>pb_#o})H53@X~gCe9dpjVE22W6SwKkv!(1A%4k?EQ zVlMHt8Zl*%brE@g7{v_1i((#9uBHYvQEnx-JkE0Eb&y}8!N1QK->ndvVvLo@`B{BC zQA&B+G;J*;R=KQU4Bgd)=~F=yH>T_>qmj*;>Z=pMWe6)L^0t~-q=R{QG+6ACEM0b6 zG_Wwa~_Jv4${k@2@KU87=d*bXSf}!C6v;~u8=N>G} zvtd~Vj19{&y_YkAX3|W&9q*GYn`AqjGzlm!EgXgUawDM|>v8;F&ckH+tsFW!2O3!{QeC3&Uu9D|@Km&c6280`_G0G=L`r=xVO|jw!mK02 zP1yiZueI3AkTSCsS8~+JeFlv1Hk129cgh%$ynrABwMC=32&@;K?9~m$_@$a_Hb{cMoM@wkjNAB4vU!W z#&$On<4kkrkc|XhK*L<0w1g!urXFRjP$?^uFz$!5u?NMpE84hGPgFi2Dv!IKBl_>L zI~{h%;0iv4M+)KJMb(#@Ts5xO{(*AG%ZL{B`7ZkW$f!Pl(0OlJlffwd-8vp#`ENFq zjZsf&sx(D)IiCnty=YuHvcD$#_d>YTcDg*>b-DI~+1Rfc{4rgwy;{%d@`L3rbH}=j zj%Qu2j%qJuRg?2+^lttIL!))?cKyYULA=(PEpsi`g&|V={9hmv=aqb>Vip}$Mm)AE zV$$PSRd#;jsV0CSyany|LkP zhNtRw0LP~4Q!fnXs`w<{0&-RK(E_)A@qHGX%Ig<)NBt%$zs0?HVdced@#5RW*GKPv zvhw~}ztj82R^EUAk?#}#J{JU~-B8DQsSW-=&D`$-`4f*JIx7i)2+zm9ai?8jWazKb6G> zMO3R3xEgndzi>)-6FayM^4aVt_*9=4UaWEjv<8>=(5Q0KY*!r4$uYZ>ER$4Pwao}S zDOS4eJa|2wnSyXm^nT8qj!;0hPYwCE=y|O~i+>V{@~eF4(x0=R{z zb5t(3x>(0b^V7CbRJ&y?8wzYxwqN^;>w9=AKZ8@zH7E_RPo{n@Tr$swBO!nCq!+{n z$B6+FeZNwPopRuc7qpORLk_)AC@^Ieyr<_ZP0U5z2_`B!LltS8x12cc+z0Y;a`XdW zMvv*9Ai z+2T49OW(kSOOLDFu&>pyBhcVjUac>L35@}(e|OYT z6S}e+CqQ!I!rnihKU_`3+v}HA2dWA zF=^Y}oWdX^mc6Q>VpMpl&buKigf+usmfBKIn4odywG^vV`b3lMZv=Ksw8HMV=?AOd z#hN*`U{?2W!EPOgA*z!Rf|T%*J?%th(=buW!zb!$_+rG==v@V;L%oKgO2WYS#8q|Zb$ z2}6&LX8czwz@$<5FUuoT1=>&#@Ap26!4`p>rbx2Jft(XZCWAn*jl;5MuER9$bTGQV zSsIz+D(8;OI#vVHC{ft$#M2VKgnN|a$Zf?V8j5%|qLlDg%Cm>Oi=>#zDU(w8hLD{m zfgcWeFLzGYl6SGAG)v0Fjbn7^9I?iy-oTL8LG*f=AO}&isPm?^&!&@p=UkKaZzG zkeG+m&{d)mvN}Q=GD2s9S!|w0kciwBB#wqcvh@=_FdS1j0t@9)X<(w-2Y9m$5ZfIf zw)hWe#?I=27wXi^MjQV&U=eMQS!Oe=0*n5xk)Sw3@Rf2iG|l;;yiVFs!^B8!B=FJ| z5P@JcLPA;c6IQTL1BI6Kgdr^h)fhg&+!QEkpwh&)Fiw_n1<5oD6KjQu?s$b1RE4c$ z+r-Pm*x2_pi#_rfASMXEf|`)Iii}Uv50qMcb8L&kS@Ea@P6Kml@??O~a=l-Kl#uIW zt!BS$_seKL2D4l0#BIE!^cU4p{E|tzG*=n_K+ydL&8QxHG9KlUhq_GWC%I9X-V_+* z_o3r3>hMc>lBd>2N(&r7$QK`8APd04r|`RLHXSk3=gBS^iYB=`S`cbPI*ps&Vr^f^ zf`kia45ha4qrEcUF-{I?^U4vLj!ka%xtTJG+YDb4%BaO!&Eq0jnc)6576`Nca~9pY zL3mFhOvSVAxAC6fgsZF$P@aL91wqN1`?@92ou>r4zy5hy!lIcvPO3n@@IFF%SKKIO zYQox}Na~seGpSSnA3e}mib1LidsKFL|C?DMbLqX?^5#I0QRvS42O}+;bL{?(?#+RA zW!+pSvJxbES5ONlAtpJSVCzNWu;8xdYC;>$;->jhx>)`e5SpE0sc$Xwa*Ii!ejcb@ z^Ta%wL(e$RQku`_7UtGh*H~}~%)^Z?e$O#&tap~6CK6&WhC~$WTp)b}H5ougS{Q!? zHG7mF9z`L@9~8no;}$BN(p82S34iPhrjB7uWMo$R`2*+Z52;YI{v+jekO_T3 zb)#fpB4EaveTrKJsPTwn zo(*+vXs0?(nH<@tQo@+DGs6FItFcF9oIVO`EUoXMYQ7t`PAf+`*>O9gw6Do9i3&5k z;g|G)hHxXRWD3SJtdVUB*mODt3^l;$6KP<-0d45Dw6MN!vQ9M$X$bC3ta?g&6RH)x z0m$@~K}M2KKtqC6JV%d0?)waAOoi3LC0uot{M_m3rH>;coE>5K@)rAE#^Q^p08F4z zM)=~aSpmmrd@+-DB)-%bzI2qq#jSq@XT0gJRUm*w2iq}B!3!ooRHbSTq+aH>S+>FK zA@h>tB7J(&fN7W_NTM&~<)hiRT3XN>Wk@l*3Tt=b6)pb0tp64v2Ic+C9BVK9c*9M2 zaRnhPiY=H4;hLsFo*eVX@WZ?p*Sl3GH1tbG;74>m(_UQEMG?xCcBDv($`+4MHZzH` ziCY0h!qZgnu&&7ZiSu93k9oX+>3fMHoY12;P=RourVJV;b7sfxzldcv9qA)3;MEB* zZEVZ5+zMlY$>tdE;wUtMbTs_x8eMGCaww?cLLSr}*5Qc*U*%?lP@}7i8nWGl8W(^X zQcyaOn+`R4qGmb87CJV78m8S!ODRJK3n@w$xLPYp4mu_c2t1l)G0&#AFx3vM%Oi^Vd8kXpIZP$b%6Q%50GBqQ!VGve3{ zIJpmHC3QbuE1&sL?KmR)ia4EH@ERcyT6+N`@DaQf{0AlL$0on-n#I_|TF~ zt(}Hr-k}IKX4Fre&RLC6>5-$shCmKPt?-NjbQSGT2qS-Uy7ViSZu;rToM6HWu!a<} z>QO8!go-^nTc;LxZoUY1HnwdbEygrrVtV*QY|2=5XwSB7+JvP6vnNc*=>_p>4qol2 zc7#ACyk0&IexTJg77n9vEM#3FI4cRq&MCaea^Le+LFQe;^Pu(w(==uq39Nbv*@iZz zfr^fvg22+&GA5U2ie4F>DIcO*Ratr$)ro&-!<^{6J{_@FrfdvJv^SXXDV)uK zlv18i_C*-?EThXSfD2m%2h|_b3mvZ4%j8O(OgmU^bk9|B2i?8`vW|H6-Iz+T3P({BPcLg7c_jTmaMSztNe4;=?RMz6r6XmlKt;^Ta+6p$PVX8Q7 z*zk#{*3ze`aM<25i29wzyKdBYD_g~Xdaz$)X#>&>5BAf0%7fkhwu7y@qroOe$A4<1 zzvY%w8cW^O^@V=ihi}LpFg48ihJ#b9P?D-jZ6W+`R_vq!ma5*xT>L^m%}q|)%3{%B zq84mwLQ&RAO|#q{O;aY_gNzoTM4ek>n`mogcEYRx1$*?5oSR*aORt)4wu6ImjZ{`{ zeNqZ+4Ig(+PnSNyIuVvYjsYE4`WQMz`GU68mwv{)E#D}$VB3^0Bn(SG->tQ-Nn1-@ zD1ZEUL){u4f4Bc-#Cpw5)PYJl#2<-zCh2@_ciG2vC0Wt&n@(74OE=IB)o70_R?zFu zStHhrROYZh;de7jKHAW-@$?41R;i`{u&|RYPGfW4*KsDhe%pX3z)FkVEO9Ktg>Par z6UCJ=Tp+!s&}9^3VSS`6(n_$70n}hl#`lkPSUAjCq<=+&DB&U1T8ZZ3sj{)K@_#B5 zJ0;~p$@P-c+79l2MQMk(e;=@Mnd9W7xf=-cL(uc65_j?$$z1VJmfF>AUFBk!cV5DMi~16*9_c1x~8O z)&iqOxjT8hP}V0_9L&iKg0AyKkB(LnisU*UCeRo?Z^kv_raX4Z*My?J)V||B>q(5@3zLaq1;V%rS6bBV8#t_X3OO@6sXC0 zHRX8cdgqm%L7N($Pf?ZB2PMKU^U~+M_R^r4*ONal8&R8PS8lNnBwN{E` zWd>*KsJMw76UUaKG&js9&XK{0vzAx}$qhbp45_T8nn5w8MuCECf8w}qbnx`Bo~8Xz zZT>D+1jS~(|E%a7u@^V+f~%_L;JP0EKu(-K6(!0}$&7YEg=h1bQU)c@{VY6IwcvY< z@5IPs;oOoOe9@5Tdw{KcOeyPsiaWMvxz4$1$lB_Qj_j3 zJ{`vs+|r>(OHI4>H;QEG@IQXAkHgSQ7g8G&GD+i^kasnsRz^rsTE;+XE1%$z*qq1t zW2^6z23ULVxPf4VJvWw@=wl3izwov6;i4AndwALQ8NbyVVwiF8t>h25C0Qlk%NLI|x9+k;tu}#fPAq!|Ka_iqcw~t} zW^=aK!Q#I%_vNd^w@o9~u_tghjd+^F^yX1|(tvmT{>1VD&rdhNkE+bAX)jb|58&C+ zqBW6aUw1on^SmIRKEZO&=qENlJ{XgHk;qXJa%~id+0iz{0Ig@r=ib<+I&(ZZwt4Wm zcc|{J;0&@9?TXG{1R70nN?=B*q%`FazptqtKAy*QM_HaVcW&U+R@sQa6ljKQ+?m6z zo7241Do`-BSKqOv4l!+Z2(N6&b>5AQcqJv(1T&5`&_YaaJrfuHG#x}t5*qEft7a1p z!UOe~&Ji{YR$hm15g&+@gQjrrRe(SEf$Cp}!Y%A0>U{>%Gq{+Hf$((Rh0sJciK%Rw=gqJRY~ul|^ptrv14j8_+hWBD%8xoTtrvrftDwPn=|knr7gQ<> z+0__K{v!*&v!s8UNN{|LcJ_Rmy&}})&7h~r5mnKVTZ~?+s)}ByppIZ>2USt2b;vqG zGC`Hqs7%Sh#npuu*bx+4OMXF7$mJGvoq4+oe#U%MspIJmq*5Zm4w1CUXul#xstvRA zx-@c7imfx%H_A98B=ced#plZAn82~Z@vF}XT0X@l*X>~&7zpX}fhR4j8zo?2BKWeR z3K3a|aE2}C@1i?eMT8TcCBHJONo1j9%q>9?0WTZ+y_xLjcPSCrO;SUsvp36AfeQf;4D&qTpAyB)P*~IyO{&7qk}Lw_~N!6STFJ-d$Wjhph%Iu zF`S`tX=GkLpInYbanU@fH|pde>`pBEYv&dL~(UJ*i|N2TgpddZ;u($Gxfh?<`GZ>|R zMDVqrhgvBN9=YyaInSFBPzh7kUGiv3=^xgM&`D~lB7F?tv&=F=e*-a(-%@!5+qF8& zPx1_Pvlv2i@=ObMoRY&cDlGMYUZ|mvF6&RZxgCjRUjp@k0SCE^ml#Te*P!k(N)W=} zjt(*EsM<7%yQUv1JX$i(E#Tuo#cnflNQe!e>!+hjr5nO;WQa4yo-7&H`-~} z!bs!+=`E4yRP?)YJ@n9YF?n5nHY)ZEqw14O4cQvBHq#x=WHqEgOqHi0kUo>zN@HKM ziJXg-m>M>75r!A@=ti&?TMKgZ{-1*yw0nZ`beW?Qu>s;`-?|W6ClDh(miNaCq3U7jB_2`F`|0d&f6D0|?B$cb89BIGcp5jVyGN6U7spiU4K2<6~ z6A&C=9(8jq_0D15Mt53ZPxKNyUU%V3SHbBV&u-#=I(o)dTkd?Hs}9}a_X_?t@{dv% zrlWiP{7u?#c&H}2xrDzx(Qo~Azky@DQ2JZ>pBzzefKYX}gIXq4U2w*Jc5R%qj$Y-; zlo3^2R)1w_L(UlZG7_M>Eky;ELvMZUt?Sfu5XWzBH5DieH$9X5BX z+eQ##uLHiqH8L^KAaI2GPF-io+X$w%=VB}wN(B?)-2t7_50hx5!Q^A)FDL0WD?QD@ z{M%qJ;*Wwe)BIV850v4!OL}`g*SSyGJ=jpFmt<*3hnK=HFplgO72Bi|>TL1!Ud~QG z9+m!`8s%2z+<-H7mKwKtL30{Cx!1lHSLK2;5J?*?O>7S9O$;0SqKE%|cT79?R_i@# z>({f>s^za-!%x#$U-EbHgy4HaB0;hLq`aHDqe+yfb#)TG9d;UZci48B+gJ3oeX_oR zQYxS>{Qyw!&ZM?a&&Bpr6cScivNR7%xEY<_92=4rfiG(p?0KuTMlZ@o)F?^37sFqB zh(apSK9IuthA5zk>O@{P;smvmsH+Y{pGF(IsmblyaS7%)xrYKNooc|YcWA`e*o8E~ z$f72-GpD*m6sql~vEg4_?)vs%)Hf9YyU;C|Y&|!rU2+QZ^aGx9pTn^txUqv)wqBv_WHVIGcKru09lXD?^;1 z4^gf+=ZzG|d4<86mho;c3EN2bd%@KAN>sB!3kMsnM84*%?aUpJ@QqAtmq|g1Jl@Ey zV8`k&TY@>gJRB;xjIR?=8D}B(TKvbfts|_v7?F3r_t{dXHfTnPU;J%Acq65Ay1RLyrh<;x9<6K~U6B)M z2=((fHFIX}9wnBQ?|@0(A=0j-6t=!#bsz@J;U;&Xm)c=j5$m)eENy&@2O#`8dWUpO z1(B1;&)ABO`9Ntq+7OTpw1bXr5SZC#G^@F=9)al$@W!$~@!j1ODSmULYpzc^Ez?$F zQIs;pNDHM*#QClDJ+!qjcoMuKX$~)Acv)uao2?rpwhku#ig(6YMr(4??etQ3ORHC? zq4@hxG+tr5V{JeDzgkL8>|UW77s@as;DgtBc_FEtX?g4=OK^nUY*UGIwwS8YCeiOf z3dP*jg-?GIAwl^z@iP8I{6j8}i`hImF+V%NZErqJwuu<2SWT_|V6#6E9`g;6_2&8k znhs?#&1Vix*`4Tnj0GT^6Ux0rTa(gNqQpx?&|`}I2}sMVXi~QjgB;kxdJNtmrlmZs zD=7~DcWTJ#r>Yxtn6K;`Ei01Y1O`(c?`~>|-iFVK@P*Y;FiL;P{gHFyd7$EaZ%!%4 z{iCUi_wd}@>>uU@LIbF-E~khP?YYKF#*Bbw-Y=8tB{g(#S>xSN#ijR)IREonw}_Y~ z6)a@3{B%kuy`qK z`g=jm)OsRQxQ}*Wp07#lca!XC>6HLTPI`g7R>rXSlfw%am@=O%eHBa7?cWMM*>#0& z^7;{=?KXM+NB>d%k(GRP_#1KaFM56n?PW4|1ET}_sj!S&ZP&G&fuBGv(vsZC`itZ7 zJRFY-5ueUqR_8FISOJ+{#qlB9rv=;E(IL>SK?F@l-4QQL=80D$ZJ|XT2|aF_G;sVI z!I^SJ!oeM$?={fdZs&`J}O&C#0yZpL{h66d6~Btj(MOFCze zX|7(tL%%}V>(}$fR-feHoU9NRmL{}6NBaTxnZM~pgg3M1Nm2wFzsvQL3#Tq}1KAvW zqlSm(Z>o|vQ9h?=(L=tK2%(w_8-J6#X@RCOf>nhuhHXq%X9!Ef=?1#kLn7Xk@KWuynuonTv5rxj&+1$|o-0k6iTuu6 zJT0EM3ldA$!-u@v?XM$~z%r#U#ET-n8sSB;^&jcUEuAl*5K722${L+WQvH9$tBWhw zWo>6$a}gyG3$f?{IZJPZ^K6mQ1_8u5lp#%rW!vbt_U22xwyYny>96(3l?4Z#OdHuH z;-&PcFUund648H}@?I$m-|OBCTW4!$gL9lY2-Vf54qUjj=-+IDgRv+*c-jt$A_(gu z>4rXKtwF6MsmK1*e)Ew4Vo*eR0AY$c#jSAi+^}Pr!}svc7~al9rfcxqTO)O634=v0 zxFKq4q_K&|1|44vFa zd)0-e@H;3KQpjjtwaX9FAgU1xHIwrvnZ;RTJHBAJD1750O>Pzip0paF#Usk{Y5dks z|5#Qbf?qR-kzSJVA7F~H@Nhy6Z?X>?D9ZZxV>D0^NfY30@s4VgRp^K}qrM{=Liylw zYI#F=Bdn|9H8rfebVod{dBW^bW18TQlVaOjgO*Qdu23NLQKagkMpa^5(lIPE%B(Vt z!4acjS51D3eOVoBbI{0lzyqp>tbAlwam?c_fV4=a^Uz$hoP?;

?*8~tiUoVQ5vnS2G+R0MHVVLzU#86Z?)s{Df+ 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 Jm9U5i$DKP+|IcOc)_2a= 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]BQg5B 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 I@$>e70cl~b2mk;8 literal 0 HcmV?d00001 diff --git a/phonelibs/acados/x86_64/lib/libhpipm.so b/phonelibs/acados/x86_64/lib/libhpipm.so new file mode 100644 index 0000000000000000000000000000000000000000..9a79bfbd31312482ec7d7e6799c0caa738bb4443 GIT binary patch literal 1572648 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%0A9FrJ)aWYIKess(K4z&@H1ZI*W zl0TZ*2Co)~!Xm}9fmS1eyT_wAa_2FIGj+3k7Y@~S_(9f?Q( zH)~e!Y0rdb?ID+~+4PW0JeywBo>u<FwfxaHIt>}Wp)2Ri z%4D*MhHjF&oK6sugi`YIPTq!vdIuv%-=GIja`X*8Rg$wp3F)q0ryi_NEm{ToSVQj8 z|JRxtrlvZlyGsX0L_Z*(kzGxG#dosaVOs7^G}t(JT$uBdjHB!Z{%cCi?J3DR2Xjts zMqtP}(=(+xXL2T)a|(7{zU@=1G6 z>iNY-?YUgfuWYvG3Oygc+n&>Ue*U)u18&#qI-Yahz-5(8OL zg$#_yt#Ssq$U=K9}*9Wqh~7Z>%IoEg4#Swxw>qmKy-w zwpC3vFE!Qdh7G#G$R4p6m%6C@9XcQJkFkE|r%GIZQuzfz*;P$7z2g!MpKkUqDusVe zaf6EQdWUN^grDJNS@Klp{fnxaywJ)h$SLhfEHhH0PrEW0!nv$$P|4_{a6)jIR{KQ@HLXvK~iI!5jU7nqs?^=@tw*I_B z;k`SU%Cev2{co1%y#0ui-~U&tB3O`9+?WVrAPSu6~xk&?r-80io-d?3K z*6Nzg?uwU>aT9Z@!tNIb2D1K?gNcftxLs?U>}5@fd55yj6G)1=nR2&&Zl*HTGYuSY zVdtfcuHYTiC|Ve6v$uzd$a`y;|7P#4yx&~skD;0TiXF$)Y%X{k-Nd}Q0@GI-ew=3f zWX2y;@OgFpd3EE1BuJ3UG^$uGk;~K;oQ-4siU*Wf=e#{OQSkszty!lxeEtPWAK$ZK z!D!c?(n^8W34X)d`L-=?PkAG?0HDsr>VMz?EK zvRHAIs?B$;a~NW-zB5yu3dS-xx>mD|vDn0cO63f#;Qgrp3>uwY(RfS2zY3hpWu`~; zp~_41UY}o^(Qvc=z}r;6HMdtPD?K9D-||l~%fy6HJh* zlQXSB#k54le?Ta8IqI)eg}Fo$HW7iR$=MV>&g93%YySg$iBPBE9Lt-Y#e*@2%B5+R*a;g=vWNGREW2FVT>)iSyhrNp)0e%kipZ{u2oKb<@G-+fRAExf69BSv;If2iB!I8Z4wknx?L1- zoJUky+U-hak}!3kP!ALeu+~a20E9QR7@ZL=ROLLVThKt~ngzXkuLwSM;J+$qRG+hc z=Uq{k{n}-St?$A5ms*FI6*AS14a({+uGug}B`O(1Ww@LRUO|MrO0tQH?>SF4B^s7p zs_tJ>4}+ZlL#VCR^+#tSrl+2j0&_Lo=xp;wW(wXrd2f%VtnLU)rt<0VvtO1JyaVI> ziu+yvf;tgBWQSsy%d`lu@`-QOK@j)yF4PmL&3%4gV0>cMFH3-ulY{)L=iiU0Xg?w( z&{A{J{epw~=p+RMhk9q?-Dp z^m)6F8t_-%&JQu5)TX=p|>Sa*}-ADpB7Rd*xf(=RZzKpcRbv$$)gQ9x)QTw2@SL{g| zy<{=R5U#Qr*8Xs-6Y7+YSosVN+uCZ@}AM=MtU2EAdDFHk%mz zThuXR&FBXbqkqlgvw}raqV~a{!>NC_^V`vEwGDLj4l5Q0x%XMWq1$ow|v zT$S0|@Osd98M}$!PS4bI<*a? zd7fVxJpJ26CP{F7Hb{9jHs3Y|^1N)ne!hnG6(X9A)p9jtx(&pBUgWx;uX`b^=z$mGe9MEixQ*>o=Kwwocu|8tHlgl6r5}ejzdS8==S&x|)t|!`gYC-`QY!KWULE zqlz^O_=Amt-#=d(5<5~_)u}WVt(E5Y&zD|3wDg9!^biRC+0wL|O)Yq&@xYoys{5b8 zs6yN3^Zk)`Hr4;JDOKp_aY?TJ>1?X-Fpsy5_bcwm)j!4Ss$Bh6e%{Un6XEJlF8JH# z|5>@NmBT1C+HKp6eM4(c(%McQY3(uUeTc`~tg%hJt*qZlQ*PUn=+sU1qwj1=H7D;x zQ~!r-ec6(1s+m9a$=i5H-X^`MqQ2pdRCB`}?ur+ayqc0xiHxoCTarDSQ$1NIC+Dni zgJmDv%ZQ~_32Aqc4AMqAH|0i4l#~|nGwYvseq!|f&Ww^PVy(OdWn>5>>wE4H_`{HqNj8)wo3M{mL3FaKh11;avS2^Iw8*ROEs=4!>#H zxo8FzIq%J|lswThdhu=)n(zQuHy51OXmy<5@H0${d(c*#@Vt%IlU)7o`LFcsUQop_ znuFQNd@$=K2C$Gm$QnQduStd{MtSq~yXW=8^)d+n6CQqqmT<*Tak!snGv~gnjdvDRCgCSUbi9)6VonlT5y<1me!DV5{Id5&>KT@izGc!7oy74t0y`3m!Fo0f2 zUnE~}UJ$>UjgDjvlGP$WF@*I-fKNvR_yJNwSUR`HZQGBR>HgsBPW`L%Kk2p|h~9?P zzp>yX-nv%G33Wf%H5}~XaS7URMbPOu-p24hx%!zg_HCuNf_4*77sTgI%Q1WbJ?9+` z7n6%$d{C~pCj5?-S>rnWeSe@>JpLNkStovZCx4n4FrQGU?s{*IO5E zp^mH%g#Btw~xnE8xS}^5i zHoDB!WZ3p1P4PP$_TEogq^hWDm3}U6ir+t5S|t}eO(Fg_2bff#t6}EbsO5Qn#o+rN zE@qJ6c~1sazd(RBkx&|yI7%cSFZz)~A1X5<&ijXm^L`&PX>NPwe1G)!n(BAK3;)F9 z_jC0-;eJo@_)p{gk>AVJ_wo8juKsy`-f0Rhh4cP#!QVBvZTp{;%ffoau|F4a>^qw3 zpQXJQ#q@La&rtK@M>xGP{+QDj+MdjL+Y6$a^XoHrjDy?WF|NMiP9FGEUv?W0Ww*gH ztH;8B$BF@iy=YdTFbLhy1V_9eXqcE^@xn1~aP4qu)o$0z2VL(hHz+%|N!;@LsNjX5 zP>&(BjZ68?{cYRq@+jqnt}f+=iR5| zbhG!m*;rVOa&(lVW8$nb>F_2Zhb+~&8tGLzkG0Vwn$#rqFLIEmix&k~T~nf_aURuT zgzn3)cqs{6Q5RsS;e$Qu#ia5HZG}(JgN->X@Y4AkXq?naq)0XG!{acJchG~<(2ok( zC(8q?S6#k`n|@3=Z(NIuTcj#qzb19l9ZU=|u=OY_)#M%sSL$}Hs7_3lyY2#T@-a8~ z7Wjxc+_mER7!4bbpy93GLOviEk&AahkugjnU#R~$JmsQO4>E!87IH`Htf^ZIUoXUJOc@lpNn`#XP7 zzp?kvz}kkc7fx;VRy2ESfvR;yp0m97dOX<2@o@jh25QaH);@zN{);`~3Eu;zQ|S>- zu$So+?#~Na0kXFTa5CU(fwYyS26~w;Qr)T}?x$${#q~-p2Dn-bKuM}%HNb`3m@E`| z`1;76Z5_vT)jRWem{&=CZn3`Xl}GH~f?rkeTT5#;H2b)XXVZu4OY?qfRRISWZbJOx zNvX{8Faf}Q*~FNcyu3ZLBpiE-+<7o^z9;c~`^RkmajK-LW&=!FZgIb3AV)U&pX_?eN!{`Z@txqhba3%u$&OOk9pjqI zLooMt{#?$Qgxu&|z3F`L)nvgNooV*oMG`G`ef*?TU|PGtvVB+?)j9t%*u)-m)D1o? z@$7TxdcafIq#Y56#u$I;oA`gw!l6CfR(J- zfVioiS;A+n2Gj*YG6YvH@#RB;#Fw`GC{X||6JgSxHOD}Q9u!xcBsf7Lu{%AAk(te3FhL6KKv=TVd zoY4aC22I;cpjgqE52p9L5KI`O7{#h;};qMn|Zl;v@W0)Fd@wB)rji5{` zy}OKFTuybTA1j%tik<_Y;yKQ|Q+p1~JBfPhjz-0t z78b$78OZrw*B>P$j0Pb56Q)$*e#)>cV5kT3t${+bt=UTeSw)jyo9Q|T*4zP0>ibykhqyml>mh2{3n&TiBm_7mGnm0eCK82~Aq2>{BbxT&|)W3#hMhx`J&_WG|XVNpAa?7rer};`3S99sS?`W*GEB4etqM&L-|1w z{*^KO%YlE{e-D4x$G3caTA0Jy#I;rMf>Qt6P}bps-ww4+&%jkt1z|Md-RsS2ECc8Z zz8$!Sn}aVyM{BBu<@;0RCp`6U_~yD+l{N>nf}-6m*h4-LWEvVUaDjYRr>a(9_^|}2 z@}k9=KwI_j3kVL00Q1JfkNJ+(y1Kf#ewU_W{k(gOf+GB{5=pdOl_@JiRqJHifh0Qi zTxYv-A_EGU%CUaK@}t4n_cXHeDE>dhzjwo~yCrA+;V@Eh*BNlxu5BfNqfyJIhp{e! z9C|EkR<8@!z?t*Q^#ET!ze*3)sctw9i&!Ok>!2r%Sle9(6Zi*7_x6j4wwT@%`vttG z3eGcw@@BT!97g2fl}G#odn5g4`=PFv6Yp1n@MOW@7eE?wuMj_IaoaYbw)nMoWS!S? z^u2-wna0ks)-d0+MApax*6plXRRk^Ocgfao?)*y}2au3`*&;H|*01a=)9Qb+%UP?0 zoS#oN39IKFFF>4+9RFsz&<1q!eh0FI^*u4__PpPMh0f}kmUt!v4blKh^3J=3paHJa znVAEfvsRIEz3$9nK0ykX;d5}kC7GqXW}SU5>svYR(Pjyd&~3SSbMLRx&j^E5uYg3SL zS>aD+sHQ|ZomdQoP(9%B;Y)nfpiv2{NllE2bJC)?&{X75cJj=UsnH)ahC-%;_CT<# ze@b{YC>wF2dC*A;Nd)VssA+|XgoDPhqu}jtu@dFskI8JL)l;A}J)^9$9lZZ~kO-C^$Bp^0l zMarVV!wtOk;U)|*@)+}&2~5ce50Ou4{O$U3UXy?RA%N-ni!#gA1Dtbp$YWF~kwGia zNM@Q6O@}NvBD{8ifVhhVwCgF=%Phi|sL+&v0qi4V#*BTAfw7vV^WFxSaR-Z{RH_8Z ziBnBGWRTA$rX2G2O%x)6>N{iTkCKKA%ng&Rtqm?_6D*c!j3|%V7JGqO9s`NcAfK48 z5qZr=0zbr7!|!<3OBcK&{$Wzqdm7XenjXk^ z?Pj5NPcpm`2n<2O0*HY4SYneSEVOQF$vvAmM?LdTK`YF*7;a9=5~|P}qo<9cC46+X zHAxqQmZwZog+yrVAfvP2Amlwp(#UM$qZ6ZnGvDEHYgB4hrahfaT#n$tFOb&Xs%nBl z(0JygdY;3)%6mJ#KU$!N+z8;~{RYuys=N*z^&&(^KA0Ne3)oTsoK@jnm{@#{QN{*X zvjHels^D!7KXMzNzn!$cq@}qn%G@i&i7}=f$mje-Q(}0S>r^Lql~S2MhE;Q`v1Ws` zs#)+4s1=1lZ}6f5)$~%0Ra;Fv>PuaiU2f2+rV5)x|3oa37;jE7w3(c#EP`coxL=AN zGu%q-hj1FAj6T9j66b*>;l1Cc_h=5X7+K zDzP$b0vpT}<4;~+%$r=s%JliiGj&sf*=+(uca>!gB+BX?hUIYA>XIZ ziLTKejeQopWtoL!f{6+emFi5GFsVM2mwsybzr2r^u7;Oh1utFm_kV_$E(H!l(D;AI zOHW&AkN{~@ssvsNhSGBepE%LkXA?98TQ4Jf_Oe#EA(5UKM&z!P*!Z4tQpp`=UouP! z)Rj%%p@Lr={Z^ON7JMtNfCHhyP|dUL2Dz%`T63LaP$Rw3g+zmv!}(EQD}tK`?SR zU5zl6(7)uFl)`;=st~VEtWq4jB^>`P>YRIK(r-9Zd_nAL5Zk0aix&~`BI8MuD^^xF z(c5oWH%)cLvMX$kfi3G$6$0&wGY5HRHYlr(xToX*%s)c-@Ri=kN^GHhI%u?@THTt> zQ4V#e;5;Xy_?Hg4T6?BH*&rbHIoMOW3F!|KMZ3BOafrP`7Dj7E8*X<*8{(Wz-VV7u zP!?p^61XGg8Gagg48b_YkRZTUp__X)5Qp+<00C4kmQbzEPRdTcSYULslO)iQ<%=?+ z8S8pW_|N77S>587ooRe?a*Tfl6=9%o!JvXszCVV@lU8U}rmngv@n!fGJQMX99$Kxa zBiV^?%}sF4X7BAdng;z|s-Sq-w}l+8e~(GYW^Ys0pDGLruBjGV#0@9bITk<)yQ`w? z!lf9Jlqh&x!W~jVsI#M3UnF&-9@cC&#(8XbJi*q+G$X`0cW^d}c!OpRxKLIi%z-lO zNlnklP86`V9|6`0U;=EbrO6+kG`{Yi7-P@LLy@N_djI3UG=^Cp%tOzGO$uYa@(p@8 z$VR^~*ue;0jIBq^ec=AgQ&CrrLYGs;$zoP|Zg!dg)#SZpE~Zz*LsXviPxg<`PJ^M0 z4U*@;P09)RN_)M^uBa0?aCuolve5wQ2R!7~k?T~FE(KHK*X_i6nMrMsO0 zRtP&^@XrFsy*QFtcon~>%43)~uj(<)V`HQ~7?TMf|98zEEGUQOi)nV4c0POh9dbj= z%Ct0luSla)Rz%W>(V4zQSk&lgB85ni@*bmubT|A~)Op;&yGj#d#`?AQnFgi#7GDsf zlpL^G<&UJJR3TG4ZE%g@f6g=zHZ~iAC-H2+;;4R{Klzt<`y1}lYDq!9X2tK}+1Zjy zBqFIg1^`<&veORu7KnpMG2T~LO@V}pv%sC=o@hXX2n@aGy%?w7=5O(mFebq*;~T918} zT67y?z&t@1+G`yf^Qb4A2?^|e#lT6=E($W=h`{wyvXHnc<1!oflB1^yuY=~t*-HDM zrqpNYf-i?3LuVTTKTRK)SgF|?6r}MqLZ;CqPK38BSg??3H~6%KPyQwXVBa}3B)#?0$agd)|SZ$vRNO?b3t;Lvz2_u%@s*Dmk695N_)k`6RNn-9>nn$1I~v-?*O9EATZehG9` z5rsY7w3pV7ICS6RzwusUfF1HToa5lY>3XgN>MUmlZ+>yONTyn@2BYW5&p;Tl-l|gF z*ZqaQTLiS)WVnryli?ruKs176QCjR_MZCHqFT-<+KN#X)*c+{{kiEFTlI`05!5)Tn zbDPu-*8WK8iPEYNx2Rd0(+KHq2n-K`vb;aB!gUV1&bwO4mm6UQ z`WxO>F@~1B_mCiC{c)Rv%<)-gZ+6t1n!73{jR@~`&Df&ac~e?&dV3%SIMs#0$Pp^u zo(mS3%GH?a9>Myl^s8sQ!?5sJ@eju#*J4p2?2C;1JV1q}hF=RcSVMR<%ujI4>cuH3 zmPS{gPGUq{7UnAcvURF-1Je(ntH{E3y%l;~$Z-Ag@H!9*rOAY!EccD@9ko#>62Th6 zk6=1j-g!uNuZ>ZgY|1s9YGrE2MV|H6nenTVsG;=sHCg&I2&!TEcm(D+HM&9JoI|qY zvEXIAljauJN6bvudcX^L?`ag#QZv(cp?vH{h42nbJ&LULPnhJJ&l@>osJQ}SxD=y| zaa4*%A>(ijv_m82A*a8Z2VYbpT0QxJe>CE}~Q{ra?=13Ly)a z!rw$CrnIX`f;AwC_ZHdT^x1%Q9_``zV5-FWq*IIiQ9HWaxh#2T3)jUkE%pgayIA@o z97~FcoER|!e1gTY9D{MY?y(sIAorLVU=Pa3!|E1kW^StIKVdm2a}u@XdGEO#vH#xV zn!`#%c`#AVQblgI`pp=(*8@eFx6CA_Eg)QDJWR>vnWC$b#ryd(CelV2#d-J-k_KPC z7^oNmCPQqfF0y_`Fb;o+AsoS%Qq!VRLqOZA=Cf1!&VUqPmH6x5PY|D{OCLrM2d<%d z=(q|{#{_X-071lphR!AGjv|QvmK?6S|7db}%*es~7ihVL?!xgIuCv9w7!iTMMbK$1 zm+W)}c)vLznEX-j9uaHw1M>gHA@MFFo@Vcjthcty9J51cfqfBXh83k77$bnZ%tD_( zCf^m2gWJ`g#2$Nl*3ob=<=a*SUk$&9Qenz5Kw+k05C9J0pTrJ??Cpq-Cp(S&2w1#6a0$SI)4(X>a=6$aW$epNm1@#hq>V;; zZnS|BAlf)l^A&dv%J>3ap`2HnF*66_COh@w$igw&kO>@j@_}E7Hs%>^#FQ{d8-pbA z6G$S*UQn4#&DbC!jUyBR#nJ;K2oZ;A0aIeaU`;TC;S%2VRmt`1ljOxxBfG{CgEXvV zYnJE%vJh;C0$U)1b?uJ`BmN5{?$0bA90ShX@HP}~!OAG>!)Rn`H0C0E!o&d1>%%vs z0LnQKm8W?`h5FZ$^;Sq^Jb?ymrd!wQjw5Qss>Rap#S;W~xF>z@waCnuOt_hnlVz8i z$tqk5*+y;T(6`2L-(8XM<4T8=W9j4PeOK!JRmfBbfkoBb)dep^0*z$#R2xP5hC@XW zeH$WBhHZz^^Ih+`@U}UOK`cso{`@EiCt5Qz0E)g_j0+9b^cNOE*cd)`g~6|pZ4>tt z>!IrL+TuffRFZSv_6|?P=0k;Ok;=|0anT;k3bLjnMts-M_j~XhK#Jbpa1Weq8W5tI z>@@Shnn{Cj4e!3UsJd69wcjnR{daV0oImoOY%s-PpI{{#c6n;i_28faiL`92US>ya zZB}&0^W;{isxJtpEBdJOr+BwtWIqr>1BLl+DKHOHR**3bo5_tdY{Bi<4@kqdF^h~W zc!}`}4e4x*pdeVJWw#tru_r~i6n6Y98fBk?OXGDR;%Gkzh}mgWiAjNNyDx?##t(p` z)~RuQhLD+Jod>MWNUuhzW|`GQq~^a;2r3P?E)~0ul3>jK~=a* z+xt34@vhdRDQ{#cgLa3UTCAnf)j7uDAgR=G#}v z1-OVM2>Bye!u#R%LSN<)o@G0nOrm8cv&HW|`9F~wSd5ZLMtZmZU|=9hWqQ-f1Wl(^ zg^4n-T(&NPtZD^hjpA(RSl%*G9ZG|avHef$bHX3B(K=PPQ zG{?4Flwa}UF|L1c`Gr9Pu^f!%!eI326N22?_8A4I4+5s}41Bt0_;nbpGNcSAO2Zv@ z3+NPz*0Ib$6r}0i2`6=fJ%o-ZlxM5pit5mx*7z)56{D%zb%+Ae1V}TCc$e8CbNsrC zg0j=(^fxz;tSJLPOL+a?5*y*CwB%X`Y~8`xPAlrCdjUwQwx~vnkwC6h!>`K6U9ir| ztzM4hDiyD>00(ArSUZy;9LzmulOl#jK^lw_-Ode?F|ZIL`Cx3XfYd1eg!QN@Lac_f z;L3^4v(e!qnut6j4)Pu>Jd0UBTLw?esiod&W=#>lm;kV@-RYUs2hz5RJX}F3d}4<2 zY{bLdv+=d4jzhF@RuuY>okAdslre)~QX&>z(nZ z(3IoUMeKtPiTSpauo?TKYYo5-@eqf9!cFC0ko4y#35$vWlbsxaq~Jf8SqO&agY!k_ z5w^?gC*99F_^T@;;P~^Xp+qhg_yfa~^VA?RSWK&ts>_Nkq!{_-0<6PxmM}+5b-V&~ zl=tP>9~I?EJj3^in-XhcixK5b+G`EQu$*gytqh+;w`sl9x?0+|k;u!Uz3cjAcq5@< zIdu>ma_^&^tHz!6#wB5JwrxaW4OA!}QyWT$Sv%Q?vy6LXBHjbORhC;@NbN(2U zetlct`m_F^cibCx$#G;+Rf{sM_P8ihg7EV@+7%#i5b*fNxn88M7Fb*FAX5SF{$Bk; zYcr(F9^}P)FjH+6EXkBof!OOk1a5TgapEtjn}2M99M`qDij{M#tXeBV;3S!< z>28!>r}Vva!u31KmEIDi!^Ik{Or73IUmB&iYWcsCX9dLEpSd|obzT25T3eJ!$GKQi?ck8Q9bQi7?dmKyQ?#!I%nJ#s+iC0;=t!sWcfa8?8#V7w9lq|{cH1{=Of!H zY^UAfO~n_CI(uQ$MYIQx?IDNPJ8$yclJuQw7Hq^BhFSaap}P}v=eTXpu?~0_!!5~; zDf_3?Ef0VLSptTFE1deL7PO2XQ#du5y5$#xsl!M;N2#T$TZBJR-UO)`rIw{``B9w8 zj_~@u3qGRM^3<)%NyP=Ba|H7BY(U!t+lkD)Oa>fUD7Z3lf~zDzDgRe?y-RfXttT<< z(R>-);3Yyh+kotX+rlQ7nMw1@P}#IHdu6=IipJPmsW>^d74xT-y_L2txgp*Qc~~HN z)U97RkIgW%pP?3^1@Mawgi%EL>}PCa&U;UV={77RryUHVMp3KYf9OjCi8>$V{ekMe zPPZXtFxFvbU-zGcaI~+XDMrUe)wW5TQv(yqscTuk?9QxT`wxU=-R3N@J`l6zEV8O7 zE;L8aX#g&w5+qVXwtiD;(T!BAonZdxJDolJwF&^c<=^7;1ueti6%CN2iczFc4a_hy5JGsL*=rOL?kZVkUdY_!HzrQ&U`75tHz7=Kv`=+1;>&9|-7c~>i5PKX74 zg+r`@2?Jekf$jgS1FigJh6lDwcu%jQb}?F9=NV8KrNH6=*9mEDzhGSVpG7pa>22Z! zp{^Gab7yL7#0L89bb{oC_D6CsCviIa(# zu3xv{Bkoc^c@q;JRhIXgO7jW!g)hqNqHZ=aXl#pZnpiu|5n={&hY3a4t&T0{Bc@C_ z*-Z&$($?7XitWQlFO8nrKpE72ab0&tYcG}*Og?dwD!S1{{oB{r}b5hD@m1zba(3hS@J6{yf zaoMRZEA`M^yHz^(xl($!k$F!nvkvCl))(6LG&#M%>OtBEHMJ#&R@585vK)emkx>6# zRpD$b1WCiw3I6nV2%0QQusCt1JU5j>w}lkbB1ya2U9khKeb}Y$o0Uu*E!1(L-}z4I z1;NPS%`B1dZ86{S!AR&NQq5ULxTP77E`!k2Pzz_*EQ*9EEtF72a1oxRbvm0S7#Uy^ zp{PCI?Dgk8Dn!Mu+1B?3a_O87x&k<`!vwjz$(c(;Q-ivc+_*oBE9pv8r1btMYEu&B z{z0RfBRMY1`=7V(`m|h?c*$G++S9cF_G?~ktwKun94Ubf-soH1w!?|J0HN&>YvPdh z@;D(r91LwzMkF^_`s@40@k}(RLQ&M0!$UqE(7Y|A`Y4n7#R< zRI&g%nA|d$EVCz0?$lyAV6!kao&y%s8YD6{8#4clbO`5KSwMI zu>}9znOY=XVsQugw!JcXOwrjy6Rw|UC45XnT49%OJaHQGE+;{>Gd@mroqEjQ+YG5>_zjE8& zVgZXOeZxA$lx{eBO7~g%A3k_WqmN8ayuM-cu7SQKbKa&DIvrfQiIQzfEx92P^5vw3sEUqtW_QgGs+PeE&AJwumQ!>O`%w{6AcFboKalai( zoCAU^GG4%^L4~}EQU&%N;mDS#RL_)csU@?yi^*Niv#q28s3@7J#!0w~Bd)0I5fi)s zbiBtnsnXi5STofvF)l`SX>&O~-OaDm@=AM$cyaB#&UQKs|DMnDkaHf@w6yY>Fbcwl zoONgfxVz3-Dss544%>?a;ghOR-VS~(7&1{uzK>eINph4UhQTk5OMFDbWg*`@lY@9X zuOz(T5_uy|(KeE%p3Mu+WXmK%{eENnMgHUZr8T{h8-B$NUd2sFUZ?)p>sh#a(0V~z zk&nf{j%9)2K|_g`%!D`JDQD?xok#2r$9vR=hPb;v(CaZ3Hl>v#9{6Yqt7-IoKLMf+9n6W;UQ z@}BppL*Av!_g!iU3xS`}uK>s%^=9w2LG49Y9!*1c46h~DP5)zliT}|UV|%tO_`EU- zQ{1))$qRlPwlXFfRQJ= zZA*+MB!m!v&xjwXyJA3+d3iGELI@N((wmiGXJ-!&zOj zl<&2QPD4!`)H+y2%sWr!SxYoW6PYC8Oay9yQ7zaz+{|@WD#mk$w3;fd*-Xxq_LmLD zm$=?0F{5?r3Tw0#Hzk&sJ|Kp>R*B{MoU&JAbG=F|<#9xAjaYbj(q58ly^0&S8>C%cAAUCF+yr^)^oMlkJ9U@*8##nzMMZKb$GM`A2FIaXZ6p+Y*WOk+*51 zk%O8Wq0(kdMdvBD$VpE-f{iPM5Jg0P0vs8{Q=&KI8Fw~G?32S5D7CboHcVQpyvg-S zbuD2_lwi*|&RLrj(!y61zbG<#+}?F!4QH9<>JO)S8kr0E%>-b6mh%$}6E<%EfE;u$ zI4Pp0GKcEIdLU;ac&`eZSUV3Iz5`}g5~~|2OZa%v7AnKaN%%RKcFbE={8+_a7P0yz z=A)8O3(6WntR(_6p-e=aNM~EFCcWAW3n%xR0yk>&zZ#XZ>)zPMO9qfm8kkA@cUy+{Px=(0|qnifWJ z`ee>~NRIpR@ZYUuj2+fS2d&XOxDYXe@J7o*X39m~P?q9nY6`C+hkUbwiEZlCN3m|e zL4b|)k{P^B^7mVC0FNDUt1NPK0&pvuBzFr zA$6NF3MV?|z)W@)pu|2H6KB*E@twp|gil|pef7N1S+?SyMs3B;@L4k;Ag^Qh_Rq-4 z!E24Jm&R;F`go=INY4W(XGJ1y_FioEp2&L7;1Mk?(po`*dvqz{F3XI&xNYS=YDc6K zNa9s8I*=>vww3rFgHo#yp=TV$RwBL2^(P{_{zme0rMSpG)=yPW9p!Vutj}_M)jNpB z(&o0^crDu%`Q1TdLGq*2F7{*Su#__?E(nsJlQg8T(SFPcU;*9=I)Ki|UM;ItL@&9* z@uP&;1!ap2tNLg&$84Zy}Iyhzz#RhlC-s&ugB-kfu=C5Gvs{UiQPq}l>J6UIU)_W?)e(VVpJF4afWA!AW z2vCyfmM}vu3K)X!#1dFdddy?muer0zbge_4{UT^%PE-&=ST| zUlGTR-@&yz2iySU-3Qg)LeW7`lG2c`$Y8 z`oSLtQ-`h}tc_ETUO(t1^?mCH2F#IKKe$IT5>JOXRcQ`*S9QIEOLZSH&`_@CELuQ7 zH5ptzSV*eOdbR?*Z|Oks(b2lWvBhn%fh`zB2ZDa+nnA^%b>#|4 z<28c?$H~~ipCUwd^5n~BFZia;rpO1}%lyOrF*uu=ZL0379$Hdwu^b-|IG~5pG>i5q- zgTdR&u=*_vJ{m!S_xio_k8zh0@*@*Q1tixcZj0Pj_S}qSri)mcmoPFLaTKrNJE|W& zF8Z*=oh+(@gV^okC?ZbokC+*5a5H>~Uu`DF0hC`#7VTJkt_CTxN~5RK?Xq_`&i{0^ z*x{Gi?;O0TaM6~Y0}D=&mLtvwUwW>T0>lya!<;7LsJrD-_Fxj(^9jo|Et=6#4d__+v_??yENw|e~yE~e0?1a^6L>LG5Y6j+ddq6EheI|?Kwe9 zPT3L3H|bY}r9KoC&Mv`eTm159mZP*Hcj?GXs{6m_WZpkse$uA;HP`!EsBL7sIx zmD0!S#QWXsR5xNAYi5CI}E>$-61`NAFaeh!(@s*#T(HaUukw@{PDsKa@{akGJg zenS#+CI4@GFRMb%UV7g%R%V5_t;u_>+1tW?yuA0kO^-Z;@(vOQLWe*KH@M)?nVqK} za_b}ie5(67c>$Rb$;oypbguVs-zexBz2Pq5Y5kX)U5b)Eg0FCZEjFEd(W2Gt)X6Cfkp|zO@ z3eKa2pzuJFnkvjshWD!DtU}vcCpDB`E1{|IbVh5sVQG- zxUfozs*6?YLKNh?ZjoKo&DK~w7rDHcy`IMiMQ~#O%~jF!)mD*Lwuoe zoFqDzK-06kgG&OSWv)sftReD{Hdf=)L@$V!8vGhlDKrt_5|V#hi_XmJ5P_|y4iZot z;*}TD1PIbxIXipxOq$5aKb8k^v{cW}-^3&LHXhEqd6)^Q*5X|F~df*q3?h7p7-kaynkWHyZIN-u>`dY z&e{ePpp9w;{DrN%g{=?9tm#T~I&n z=>iVf4)oRjW3@h|U9J^3IQ#K}@vyLGb4N{~ZGX<$>5aa@5cD-zc!4zsEf{E(1P?(r zs2FWrX|v(zW>}1 zfmU_>r$u&C*DhE@e=^&32+i>ehtT=O7M5fdlF#T4%Em8;RQHowAv#S|49i{;!)4Yp zoEkWiBTEh}xKF9eDF-xkLs1K`tl+d&khjvWpc1Sam?4? zDBaCm=W$zeF!?ruCDelwe*_TvLZNNrSQgm|Sai-xK?$#+f$0$g)1v}Y?#Ys%qG|}3 zw&Kc5%HD#Mb=5?Rj-26?5W9DvNZL8lVT=GD6k`YX9)blOb ziP=fwn+y3)v>DxOh2*3|`NvdVmMWRZb8B?$(_1&tFp5)){3hx_OW(Or1eVn>%gxE zyKZP$m~Az1IE6R5AL7H~!rcF1To^%MjO6IRFk*M&xUg&7wpY>me#&4J7xwR|TdFkq z>zv?gPJOuG!dw&<_Q_)6FcRyPNL1MA#l!@O86^@Fc50Lu*>ou-5))QIqGZBgP}t*; z7TBQ!K?|naMC*N7v*X5-8z#1$Eo4n{~(l;iFl0;NVhw z{(zt`@(CU5UrTi_BafV)_wtJpEdi&I$+p|hFV|xIBIiW9qI{zoeIofQo>VzUn%);& z>txwyKp@uvq7Z+<$0&7J&UqLtw`_d>BHz1KB!*-e`h9)pDO!T}YrmUF-b#7jc`Yu> zxGU)@gxA222&hWGZSnikf|l#rmf2*wh_|5ZL{aC>^o|`%LG2?vurl#9^PRJT4_cR4 zd}t0Tz9?O5u`Kc(c_(-HF+W_8H(PpVKa zsrj}i-L~4(S#VRzK~m5PEwX5f+xD2-w%1`XAv$$K?X6b^XG>7Q6uc!d?QKwe*@b@Y zVGCdjMm|XnLVy}7fiJPSoyatfBCK;cP3Y*AzpW z(jW`ccK9mR)}{Nla6JfMXsE7VF~RjJ6zFEq(fQUOi1@B5MX*L;B1BY^kF|lm7t~+k zhAQR3&}w6GopGf-n|7g?ok&TQo0v6A^s;EJ=TWVC3Y7|NZ%f5#XDz4>-O!Y|!wg@3 z->8l)G`*CZoMdT+W%P}G4FG@zr}89%xig9lc~noWM-nYE#1gA4@g1vGnPwcxRBee} z%)b+HIQWgG;F|6MoP)jyKDwZzt&a6IKk^+91e!d@B69u-j1 z8n=e0K%P9o5hydF22;G=Azy2mfvGH7Pg_B6M&#a&;bWls{~&zQL*Uc19ZpX~jF82F zIt*!eU(UO&&GtNiEm0)}Mr4#LZ+ZU-NAxkyiWW)}L%;_Z z@M1%KaJM}95qo5rQY-7=vOQU3z$Tr!c(ZJxd|O`*#8B6p+YYIcMxjvh)n=!{|XMqyI&pz5+!{8x{>M-& zF9Z`1rCP*?!KQjXX~;bb3*?9zwId;dUxgxt>bjlY=$|wu6C3gRKHGQ1Y7J6Mg9HP> zlYrsJ#7zXs{*&cza08C>a*;&VASXQNns+z+b{IYf- z8CcaQKAI*_IdUHb6+bTo({I3HL*DrEyU>gE&Oy9!h65XMtuT|X*~?mL)ckXqpRqvu z#1dN){{EKbSV`vG8__zPB1Z;aIE8|BE>%UttRq=&ZJgm9r%k^8@p(5u6EiFbK+(j~ z-QaTwH{==dQ8H`+Q7-YI&7Xd^(-rp9gqMF^Mjg&U&~qVa-t%Ddb{?#37S#YxJ==7S2}e<;SbbV`FEYWe>OP`R_^hl-eVPlr;_q3nmnwjMW%!ED=ZF`dW7E+nTzCGl z_KX=SOpxS?m!hRuO!c1AF1JNyWJoI$b4CtSU&z53+YA0lI|`g=z|ypqqF08upQ$$3 zz2zOW9gAY;s?p-bAe&?Umh9g1I9S8h2J1D!V%o0ff63(wW%xv&y=1)n{CrqVKGeOv z0uN}8>X5XT(h+~smeUNUk z%-W73%nOuIGtx1@9UOHQUT`8OwFvn+xiwN137Pp0{>Se?1l{zIKC&DwI@koWh`8cv zG3OZ(j(8#% zMIMhr{f2ors9)ky%z~&uOTOFbmDZOvOcr{DL1K3xkOqj$UAU&HnoZ zd%A~5+w~Lo#0qv)G)FjhT&Br+J?HGsc`szWFf6B}&`j8Bi^-gJvn-0fjT&RMSR2wq z{0QW?wc{5^pcz(>TPbzKf7-W-OxR0QrvdZ~SiI@cc?hfd`%>4RSzhpG;!>6Q|M{YI zF9cG=C{ddV5Ec5!K$qSqYo5avTJ#kZ^~Xt2O6(Zm z_aJ3;WDD5@P*Mcx7^PD^hxVXla5{L;Ydknxi7l0@W^)v6s)@j1y7d|n&~@}Gjxmim z3Oo5Myp$Zjv>b7Vfr$-|8$<}bj3Ye>=yV49?l)H{{8%<^-{!r7;X*nXy>ETb`?K$P z-*Mz!ahoBpc!%clvktbUYA`_hcCjiSlMP~2*rtEbWdo0VFer3KTsR^EzAYsS-|EQH zH)+3BAo2hU1+8DJlK155+K1z4$bAEOjzZBD>AE=LahCK=X8Xha5wg>)`Z@9x{eYUZ zl>Zu56P}`G|I4hyUR#Rf>oi`(L!-{I;&%oK=wox`nT*!-^+HNc+pfo9rO6Uw|46}~ z-y&-(q;B5J<(9VuV}*W`Id~REA1jqnI6=l%)i8%}2kPuL+~e6YX#RLkEB#)}JkIiL zkDke+AUJ|o|Q}zd}Xd{V59T5d0js$p$Z~n(5b{HGc=5W~MRwL&@`(MDb zggoM7CrU&I`xvLAc~QTu$F!Yj@Y43?Dsw2+pjZ-mgsW*OZjVB6#V~FJ2EaE{q0a1M zL-D~38ib- z;RqZ|Gvf3kQ3E6JFkMROVAR0lsh)=*QSs#nj*()g&CkdbfSR=pho5E(780j3axyWI zv4;ul)WS*K2l|#4YD7`I0GEo@;_3O2jfUIgN&-EozUW<&%jm;)87`;VKkr&{SUWMl z5>4XH)4hq&1@*jebv(R7;R27Py0=i3jO|8g{TMx)Y&3GEcPdZ-hth2OlZ z1Gjf2Su1s!I?FQ=Is>N;&WBGex`|AfP~08QCDDESSZP$Y%B=Hv&UsaF1LT#TUG?cq zd0bJPDUYk^LyAQCpE*)wWoO1+O2=->nKD?(_QxC8Q@GIC$QCmyhu^Dit|sk3vKj#b z4QB;R=~>cQA4zq8F=6Zga}S_%Jf;W~5X{vgF{-T_Bm=UdRLwf}5X#K4u8#>w1?!cl zT~XL0+$H6XS_8f5@Uwk6j#br4-pdO8WM_Rky#4R+<+E45z=eX(M0JBHELJF^aRfGI z=m6w55**I*a@a-a=nr?y?b40e+@O%=lH?3WhA-gAa8^wxYRfxE;&7ZzatiCw>_=Ix!`TKeSeTnR zgc@JSREK{`o3>ibb%?ZEmYMfsQ?rm5%jsAS6|9o0hjz=R5>Rpp7Kqicx~5?6F~H~* z>@{7`agemm{!@?Gx2y~0yTT+_+%8g3@cDk(aK%c8vLt4V_cII{C~>BSJ#flLnLV@< zGu_L584G@|?{jj&B?DRKb&OX+jvL*;O|lp6pZbp_{s7HOS3q>sW`nsWfsF^Gm%Ww^ zYPsfQJ+JZl@_tfIwgC4igA5$``qt;`AD;J{BfSg=@sxCph7E%Tk~vP9NV|SRMix$; zA_qGOnosnz;B9=%leIiBV-W|Q6@a_%j|)>q;Rs4QqFa} z_JD+&d$1H;^b=gKF#l&Z-YlzwP0qfYvjJ4>aJ^(^LXceC?7UF$Ga0TxTQaEc=KOLt zuXKE|DX2ZMnZe7O_1gJCEq6UX#5ZLY-0FD`i2oMH5!3;L zSZ|Rru#{o3rAQ%eC&28wTHXO06q??CDgokSdn|PQTj9$ZAiL*^^zADaM{2UA* z>5w59iKrLnp$ZebD_+D+GWv{yt;-`vi7a*A;AT%`YcEwK9CCN^QbK8krQC@`bK6!B znIeivH`Q#zjohQwB#w<-nvN^8chz02Z{EGa z^i~cTI#j5SBxS0*B#Fx8t?7FP`FVuC2k~PLm&~NPmkU3z-J}J|(%EjmI0tad5Fa1S z6esBvoOcSr+)>+=$Ixqxe;o7M^W9RFyWey*W3c-ao0`mowHzHIwu}f;-Cg~T{Bkuk zM-A3m+cs_9kw&Py4M4)nG>CRZ=7#D8N9E{nm=%9Y0+=to)R2;|^n}r0BHDY@ro8iN z-uYwR*@Z0VAv$5e#pXIlv1o~pOD)g2Kk4rFBt5EnH9S%Z#DD1yn zP7>qqk-%7&%uo{m?bN(1F%co3gV;2Z5jLZixuI_^rgV1=wKL<=n3{J>p#K zjtym^na(g2#r!&Ho*g-uvw)dn%|cryPN5tXn~b(Pe-In>po*^}^}{zVi+akd>ef}< zFNfPnR5QX!rffMsBARF+wF$Qxr@RGacaf~ftxCq&*{rKQQEJ>#s5(je`o!w}@E$qmWu?DiaSOA%y_j96Gtp668v|T3XJO-EIdR8aUcL_=!FpsD9 zo9agJNPE!Yp*Ckq173w&Q_fBvqhz#WQcW-Z!@Pf$+ciBnq@K?jk74*j$D3umWsG+} zAxC-b7PRqJfVG=7-lEzBn?GJ}+=iP(hnmikG;fG$gb^?GYZlxuWSN7{NDyYgS!d@2 zzacMQJ_9bhiG`0saMewyzB%Sge%_&D-V}{_#Q%QGKr&zdhj~8~!KeGQe&u@DfsCh7 zN`!EJ5?ONruSJaLc)A~$vP&x`%%l}Dz3uc# zjD<6@&vE@SO#fKEmxseoiE65{tAuoTK2-#gOA!<1Xg6+AMx#h8MJf&TD_M zXeL@Ye=s?!W`oJ(O`{}6nqi7|NS%yB>FF0w;7S8QY(1s9`$YGruv0PXf1X2ir|Uor zM0B+VtK3u_I##A<2ww&^vJIYf&xjNB}!MU^835 z(qa(5rxXP`k*__k;WTyew`x%O*S8(F?A41OUrH$e(HCq&V1MD>R6kmlvck}C`7hI#o0&kCT;76t!gvEH}>3~-v<~pooQ9yeu zv)=0P2V^vs3yUpwGOk+%)zurwqLK_n%M-=qnQ8)ulbpo=CD0EA>NP|{4vAJQ`k(Ve z?@rB5!T+o)Zy}%I{&5BWYo*Ox8L9(d{g!rtkLODLmdlj*JY(qE>Bu0P%_B#uVyHe8 ze4U6(<^cJ{I^Qn=fe}q5pVLUu$Pa|eCm)P16E#5*&@{G6-65>ky8I}@#Ek!NZy`&P zkr(JyMe=e2emV@MAW{gppvhN`xhUvtNLyTeLVSf|67yZF)7(px;38T1cv?7rQPiWe899`mDpfU3 z9&XiK%ej>PyhCAWN)vHRmO3F-F3@~MfvxN3{e-unH?gW6&B}(u=)pRdqn8I$Q>9XJ z{C7#>*OJR12h5!+ylKtT|bA~bqm%3RoG#Cd!nEc*K>zjJpM{7)#{6o@qY zBMSaEn6;@~W1Kflixc)a!3Z;ids{O}ZhmXlnNw&^Y(c`!2dhZb>D+3Z_By;c{K`q< zL!Fr&Mr3Q9ype0*?k$s1=oBe#&0`qyw{4rYQD>EZ_WZ?1RYmgso>F1 z&{NStt1PHG)HX15mH>mQj*~zhf37s1B`muZYh^8+m#q*?UtB6Yt>o_dLeS3nUvdF< ztw@7VT;*^$?>&m$VgMG`6nelgb#!&kznm~d+iR{FINSZ};*G~(iQz|lzlw^+S7&qW>xUS(DXosE2XUOW|8$-Q-=ZQymV$$OI! z-6Q(4a+WhKpdlA;;aCNpxWt#~U~It@lv@fOErY}`4O#W?ADt>WKPaoqUTo|Bx*Z~W zF+;S67FPeWlO*Wd!(V_G)(^>YUG8wwy%ZIW3D|xG)pjmR3P(z#3(l^jf0nbS5X>s&YGU|L_{EP#{gbZ! zD)h4T%xG>~F(;*`bBVOGlfGVVgfnPZZek<0W~{HBe%X!9eV>Nod;tF+Wd0n6N`2X` zI{bgpUM1K6h@WlmdP*8W3oPU2U$G?+mXWukI~!Bo8+a$^$R@o11U6hbcJRk_+BrD- zrk>q%cLnWiJfpO-v<`ZyqPuh;#w=uL3z6f^RG;JeO$k`@)f(Ehk z!v?Vs$ZLHIw6bxmI&vRzXsb_*XI@xYqI+_SA-#=zDO+d{c>{wL^O^fs)1 z=lqIjX-En6;rwGcE+A3S1+Zb{7nf^Oh%~&se_S(H1W7qnfB4J{v2G`Hkj1VO7$Z}T zVE;6{;2T2PIf^}4s{G6(BAMwFe=rUF!XR_n1wqBB6ACge3*p%o>r3O`ahc=v}t#yB5D8=Wrw zBA+ZW#V&1wBck0i$#s%p!3CStH598TxHoYQB>x@Sl4aBwJ1lTE!-43>)QIumazjP3 zyJ@(*GaPqO#KoJ?mCk~QslTdt-zdXmE#pho)W{xy#IT(1pm>3nTU6D$@SBv)Udj>0 z*~^G9YDwKPU+3ln$3}UiZkf%Sx{hp#fR5wSm4`$crDmDa3Uj9}mvbKSPTOLbZPyX+ zuGVfKTx1Bd0)7Eais&QfJr1xevNp@n%E9l&-Blvs4I#wH*S+-<`>dP`1uPXfT$UU>KE>!e!G4TumGJ* zVoJ$GYB-G=9MvA>?z=%m?laV4^mIe*6d2pvy_Q%t~b*wuVHubThjDho{zy z!+XSox>mbQ-ahFV5Q(n8(@lQ5flGEs=&7N3Npo@u!4=(&i>+#5 z1Sfrl#wQv@p%~KoN{$mUM#42g;oH%fIxSSmiSN212wc`_Ina>l3SSD@1LW+>CQh6v zl0nygA-dJF`U0^y;T}%IMo}*sFxeYvy5ss zVBo?mOaR1Nv90e%Y{M7Nm2HS7_FhGc!-C~sS#*HTMHeX!S()Kzn!dN*)GoI%UPcdtDk$DVt z8)3JtDfnNv=?q{(;W1%yaV-1TN*+ohu2}&ZkuO zHN=SJ+gA3UGQ?gf>a(|#o3poj4$Ng$W*Cu~07Yl^7k4}U-|W46fLv9T_T81Lgi_j6 z11+?g(Hf`Jj`XV?>CrCf7rK&aa*9+#lWPk>+UbZHL=zx^AW3NG4(50$E)oG95XNz4 zm=PUb#ZS~YcCMXpO#*>%GXVrnlaPc!AYANxzh~`JeF>LwX5RV!`p|Tpea=36@3Z&X z&t7}2wb#;N%$24Ib^dI6sl^?~oLbL=JQXOjL~^*iA>6yOtd!DRx(u?2EXmQRoIzG+ z)FppXYgKZa-M$#kxW_qs=taRK3KJUlADuuW;tfou4)()?)0gbV694 zD@zmmwGeLP|Bv%G>Vc{U@^v^7(l14_?B$sR@nHnvKXdn zYzNO`h9orL2l!>^WZ^c_DSsCaB73>iu=AMj>R(5c#y;cE@XIe^Cg*$k<#*aR+qS%Y z&myC_9wSif=0Jp`-1EmMR3@nxYuvEj!nCK^s7BG zrn}P0q)(c;fmfd8yba##F=Z%ap89X#g1rz{Zr_dd@%K8$!`@g$;@W868&hnlB(u{M zK1%+0zVe@Y%io73UXG|#t3v6@i;LNr?RRyM{4gt(hy0B4QC>;8u&-$?!8n zdm;yDa+1*#oyv0FwbB#`T-55orJ)DUrGXcFS)*V0^=<0Va3`RtUbNsWqJ4n2%3z(JQo`<4uIIkbMMb2;Q3 zyP=QOx`nacO%9b=br=(EeCb&gh{XL$e)Jzb+l+u`ov!=X&*;}w*7jYIl~&m)kw<@` zH$snhlVN<(-@y+1a8d$nT&}j@hSjN0%j>1GN*$r~t(V_?Q2ilYSMUO|G`rU1(f&Re zG+0`b3YR=IyA4e!8%IeA+3e`NPu7vr`Z}NZ*aQ|oXzx{F|ELpzI{1eS3(>e0V2&1@6Jh7d z*PhKa8$7C_BFJHg z|Cargc#-}O8>8Jtc`p62L(-W;(&LBH@JIdiB+Gc-0yG2Uv@4`E-4)`90^OnwkE^4c zX-sQAt$Pk&F5tt<=zEl6zC@5iKt8lHO%E$AZWwIVEsoh^p&NSxzwbg08SS{U+ghla z6)?sy48%~B487dJFC4p_Hvl^ECk~y%(rcUCfw76PQa31h}#;ww-8%hWXrzD z86EKs!lchiLC!XQ=V6kcVF0Q*wLXx@6g@U4@4NVLo9=53Jvgw2X%9~0FV6+jmr2pL z{91KQC-la$iu6MEpd1yJRtD1CQYL1&L7;ebFR}@>L-Ja{v z{SgUwsuV+01U8&wb(ti1Xj4P!$_PK`WD%}vRtt&bBPUBjhGm};GDHLf?)J1Hc%)Ob z*kGo=jMmF;M=SzMSDTV?$2#yM#+AEi-DNbA0F0*X&QG~@8d*d#CJt)Q6Znr1#Zj8p zM>;rNCM4m*0q&JasqBrrmwTA?e0m}$iRB4gBhGs@GV?)!vcf0|2i-(~=%f?e(gibu zM{qd++~YpvpX&&UgR!Aj&Xlarc0MJ zTy)7Ix(M4Eogwa%vtQ(UI%@m?^n)vKQDsMwvzHu!*cKeoXUjfNUm-(f^5P=?N= zi_q+dW!cxaop5(1NPPvGE@@h=q1_tG%vnS9Rw9rCx@xqW=EC~`UgOZ3;@bD#)UvPA zgS10~uTPSU%JrcDt)07N%fvSS4^wp9mrt&zJh;ltwUX~VJ-9GvQiNx|HehFsRV5-B zfxiX^-cO=5X~r1oHtrz(l48s!Uh*T{GJ_75BhB}z;uts4Q~2*D{AuvPyQ)&|G%=-` zkEzvdtV?cr34<@lD9s%+bBCKn?mTsil{0QciqjVUMb`SO*4wr3}5%P0d?q zz6N*NdGn{8mMJA#r=bbS*QRNL?Gk3O$SVd#)9RLGe@tC9{q>>QIH^R8?9QE1HJ9CJ zYqr;FcCDetL#7h8Hp`znd3Hpn7qRL=XcX^0j%a# zv2z`6D9wF-#9icL+l>5eV)cuIkf(=r(X1MB?O>T6G2nGeK%ZB_%9ELuw43+Zis2@f zSU-ASGfU`fGq2ZXRv$C#?$okQ&8(YjW@&waZsdav4pS$TtNd-!n&lNk?KwHkeuxhk zoJm4M5pkG$MZDGRMi@{oj^UmcQ?CIq62hcL=i$B28DS$fQj*dZlL4Tbu6+LUQYv#5 z+lD)rGtNAzl&BxnD39e$OqGALyX@qey^vg~VLese1F`q%qH+4K|4A5z^2*WwGvv@b z6m;0bk8%Dr_^eceM<-!}lLoLQj=^O^#mlJrh*R#2y=H-u_VCJ*s1 zDXFwRi$+Aw2^8mMjN;s|bcVOoOU8Nvf<`(3TTi!@E(f~lf3cd=53ZC)@l%Bp{9K%|&yq;v{xgs95@u&luuUvYM72IDb;TsO3m44q zML|H)#AJxXo|OQB-zWLEm4AQY-+laBC)XNh=edaF3OZ6c=4EbF#`U*a{>#?C%w5jk zae2Qk8xkD#9U!*oz4$9$O7Ex2{?^amBYiPoL z11*FWe_+`d*sVKC&{`#yhP*ycivK5*xmcfySvPn`Ehm|px67WBvSX-N&Ob zEZxD4XJT|5S)Wi%Pw0jc%0Xi3Hf}sdjw)`N^zSil;C#)++En5ZC3tt~HnQP7dw?7B zcyMUQPDU=u`7Tb{czql>-qkHMv{2`-CLCzKbWSV}sMls!r+Qw75G5L{HCoYW$+{qM zjmkobwj!Rmj!Q_!exVt}dPX#fYxzx+z9ycyp34lS*P_C>J|AzAhwfZBS5OyTD;n4oRQJfs9Af1F)gi9awk+*)QQK^P7W?GpQQO*ZYtr&RQp*v^*cyBM1Zz2UV-fh65) zF~IIU%j@3J%Wv}cOq~8}V2OSIkIMt4qy09O= zT!kQ!@#1r9DE2$b7+X`&-1f3hUNl-wT4Necuk_cIho$hm9exjUai{b=15Rt01(WKB z7DqocMpoK~^-UJTQ;|AiaeRvwyX8(r?s0d3byU=Zv9y z7i9N&FE}x*QqKi5utE4LIQI=;Z3#Wi*cF3?IEsa(IT{pdDHSsZbKS24N@tN|L4_&& zxe}*Txrgw03Tq9L)Y6=Z~vz$Y;0bQpGp(*@yYr*TO_=$v(97 zf3wSamn=D^O-pv|(hqaQV6yhklaXfRvybrl8~N;de)hI@K7`TDr0nAFRZL2?>TkB; z2Cf?Lq(1{C(}A471@|r%-*rFZ-XeB*KqBPas{T<0b`Gtv*Bw32`zC5@1$`b^SIpkO zP`JB+?1oCY?x)_t#45FPeJ;iU=kxfGdl$%Q@8o#()%f*RdhsV=2%|J#@zkY9lkQ$0 z*R5RkmBoi4l4^-p|BB<&nHa~K8zqUY%$F*O2JmWWiR6N^H#rqgyJ}{kbkhXmUv@Jm z!f&zxz_OjIwI9{Cg*Qp${YSz(NxLWi$`$4%Y>Xkm`ylyTDW5qw;c<8hEx-V>`1+Q3 z;uh>GZ%*X>J{?fV^QaP`PIx#~h&iNS4(o1{rK5K|uPwZ9!sF5>^9XEe_#w8D81%%O zZizSCs7&FO7eS`jx?J{&C78FgoT!b8VBw52y;5^?nPp^7bEJSkOZxSG+`NXkXemVO1Uj^11N>gmsU&ZyRkeR+`BaT7v{>msA z2U51rW@1klN1=IsHY@Mr%=ztFf!AoxkBhW(JM}J4&Ss`QPR{N8YWD8DM@E5~_m3hw z&|<3vd)|Zoqsj~QqT)F84P{nFmit4C+({KnK?jErD(O&!06wfO?*<_T0(UQHM^vW< zwFI+&SAlx#_xbE&%-aXE2U5kp(#5gtn@j&1`Q1A{?p`%2d(YCNCBO|9mw3DK>g*e- z;v?A&sp2Z>3Ro+bpt*x~a;aAS;-o*9#Z_vmHE}hQNgKsH7U8i2T#V};z|e94&Ja}+ zW-o#Or$r)&wc-1`4(7jcVr$ixHvidYsV^N{;-hMsx50%Ze^fjYePHa>IC=Sac)=-% zUvj~?8`wHWrl2*vKQ8;=;s(k2Y_4^R#|36!8`&5x zg>5JVV+xqBaEztRi|wS^=J>gpJi1W87uIMePWvgcs=%oR0JYPrvq;a6h5Oi4@gU8E zTI0Jqy_)FJBkoZxkLvYKGY>F;ek}ZV_kg-N#HafV3bmSWE$tz)%dWZTTL5MI5ZlEP zY;qUTxh%q$RQT%=AOnnBa;d_CQUx^93;{Zeb{BUCL|2?p0@8irE+!NoK?uXrq zoRd%Gu}pF%Z0tV+2~U}++ViKeVyc=yTxP1q0DaLu2!1Lz>kxZz7^__O#Yt?!?~HUb zZf72}&y`Z{M#x8CIKTUGlt;+LkhLyfJ6b#6`S3<_@t*RQUhPu8U_OJyCk1lWSWNXy zvV3iH+5B2-pSIO5r&1_x{q6(Hf}@n_nUk?@$0-wmztp&$3Fiaulsjq*Ijttt|IDzRJbbDxw5qf3|Ex{cpbVlLu}aa>g(FQl3( zZ~QhiJeV{{0PN-$-`Q-e1nv)h5mbzyo{^ztaXiIKmT(HH51??r6@pYoG;M}Z==mw7 zc1kI6Aw{xuVByP+4*Uv{9!)y!p(R*OewqpW#CSSa2OzKKF_t(!&R@3ioHJcWG|~CM`>5R@G*+25vJufV}j4x8r#Gn zcgQ-H8^0+h8ClOb^0o$(YegfRe_JErysyK{rb{{k`;YKEHk;NcB$~1Zp+^hBMN)Ke z`U*7AYpO)UT?51WX*v6Y^Nd%7XV)CW@k$kCsQ~O(<;7?{=F=RngBoD5+W4n+5q~#i zM29+VQhbrDRM2#wn3!W^1isi-YH0rP0D z8*NoZAH76}FS}Bx$^N!Li-8-N`|f3pwVqe$))wfB&rcsXT6ax)wn2OkQ)pJC_ zn7}c?oWClZzij{x=S-x4Hh?AyOkMS18Z%g8d{j{7KfG%9$4Zr-)4;sMkS!eDeRz!9 zo9)5o*JCdYf1(#QGh`wIjVDZAZ2acI-1 z8eM&cz7cO$IHeF@YQ5vdt7OtCt{%lj_qzIU{gVR++fUDBKXz%~+igap`Gm-%fc%xd zV;iBlG?xLt2RhNX8=*88;~zF%sOoOjpSII-Gu5eWr+K9!=VE6{r-LQ5(0l%rIEVNr z^qyAqALVtY@D88&3yDBmXx3WkHK8*{H{xYBL8Bs4^X)ZXmk^j2`vJk%gggI z{iU;jlMe?`qg(^~Z!~2}j|hyBCxWdAk-ZU86!vWlG{ z@oK|PUgpso!^{{%gb!XBl}R<)4~nj;^u_y9U)tn9EP|t99ag5{3O3nZ@cx!~oW14u zP&3Ik!%m3oO6J=5-%R(n5TA^Gu~(nk27F%s%Gk&#edTt|*+Kd;ojFv7GaylisO&q| zU;#28Qmb!=-VQrCs6H~7puz9IT4$<M#7O1xJGE{U0)t$%~%J4TTNTbJFPfJn9&ncW!+cMus@L5E1r|Cf8l8BNhG6 zqf)W_Bjj1rFs4@j8$Y*5{M-VLE{KWKh_jwI!@-GpyI+_UF>iPzRlbfg=T>W+TSKa6 zmSyr-j^?M0b8GeQ6{`R#h-;%Drh0u4gInXG$vCzpxYqIC(11$)R%;BKiS4yTowv$n zp#<}T{#9Vos>QC=jS+*EF}c~GC>??Rz*_#XK^|bkv1y@p$(vO^T$XL%-kHI=hp}le zCW{wUF`_Q#&`}A;e!+`L*xf`9iO}q(n6&a{uwI+NH&Q(^P!h~SlUSD7*0AXYc&+-N z=~~3eTs6TkU5^xu%LY5XB<`z8j8_AD#xL3aa1?oN$i?0uC$ecai`N^61v!e%YJ$y@ z$43yW0e0(?+CEl)I~UtR(8o9moFH*U>=ChA^)Nq8IZ24f=|b=>$TT=sOVfqZjIhaw zeZz*)Vzds3%Yt{R<|CwXs<5XfW zV=it!eyLWMp%||()jv1h=+}FP@kTHkU<)0ja_@)8~fxqEqYyWJ3mK2!^OH zVu+|IpKXAz`9002sPZ{R)vCGF8=f5(7t@p(WNWrYY>gMRMqJGSxLem@jHHbqo?@!+ zP)p|;OT#$5ou#??fig>@K94w>xin+2wEsFsa|k!Xsj7qGX5Mn#%qdddgSjA&<6~+g z{KKg*q0Stu_SCzwF@^56_2I#X%52Pr=%n=9_uWAvQwXYI+z0uXwjz#;8Kk>4tPuxe zV+Q5Hz|5lm>f25upxcGHS;P33j*%lRk5ygQ*uI8( zG^f&9+u=`CPkY3!@UaM`dX1ppfi?Nx#v#FbDMLH&YhD;@Vym{;lS(#PfANVI58+Q> zKSV^l0CSR$n`_(nRO)L*n)8xRu^0Z02DY=oPGy%!|C>_yb2cjz9PYl??I+8q2xEMqbb;_Fxp59D6WDyvH#9fRM8$A|G@C)9wXd z=>8TKe5cIqe$+gJUcjXHID;K5MWl0fBMxJW!OYrXa0*>?WNc! zH{ntti~ptWHxZ3D3w+VN@7efUIERW=FOFhejh6jq*;ubD>&2>TOc?VaTk$`vzYXKq zoIp_nhphNJ7d}84WvR;B7kk!qx3(u^NNWZjg|N|Hzsquu+vtH+F~uPpvDmMPZTIi# zeg=(nXDoZqf(v9`kS=sSkqhdi;Qc`^UibZ6F#F~T1S{3K(rm^RVyuqs&OVAFxbt!H zeLu>_vaNi(bFn`tBj>`&NH)$LJdOaxvbDr3UABDee)3rsXstR%$feimJWOra(|Mh^ z0{J5#@L0Z7cY$tst(`9x#{a44E2K|h)wA(}|0e78`01HyEzCEtW?@6Z`ueP`nw=D> z1IeC5&5A#ar&vTCrHhgNB9CCY?EHe{zlGpb6u#J+ml|YLK9-I61{S)pII}vIF<}#q zXUullSjVz2q^|m3wououZe8}r1#7*)n@bbdoE%gh5mf$*e?R-)VVm0@+cr#$1?!yK z8$Av70Ax($0zUAAwzQGE8S1bfJ2{TaH9#PZjp(ffe=oi{e{>G}hwxX36ZF8AH3J@D z1w1^!gfqAwS$;g=Fn5|Iv(@DFIN+*1h-x_{KJLhf=b2gbA7ev`A>R4nSau7}6mKA$ zvtA-@nu@R)q);g!47@K}Arqw(>2NwcdQ}Al^nnx6h+(G!t;DYvpm^B4cIu2ey?US0r zmyC&7OWo5rnvA*A6fQ4F=lCQ%8cu2qdrc~#4qPMAemYr=7e^M|SX zU3`WA3;p8-?;F`aN2-5ZZ@jIGMA3s?WWDo=!$qMNun%r!8os6#EBp$ajtF19V?8sa zL%pM(X|+{zQd{^Uhd0z8>I{zKsz2~nM~8%Ka1qpg`g?aq3N~<9XO!(ZB;Eh-(#wCd zKlbB`j*np{JJcV4|L>YRQ=p(}&DI|mG`8}%HArN_h2{|H0w@TRMPjP#3IE{@H`Cso z6pn!Vi{fjbNpc^}R`wC|au%H`W$#Xifulwm3Yg&_6a?%d}v?@RzW{mc>sX6TuC3xZ4>lnPwgj|p<{|X<<7I9tD zN|>!_nKvN96He9E^^UyAVHB?kxz+rG=+gBH1vDV*)QoWU@7>tG~e}soWvYNt=p42ggFf zKCxrupA57T83ayqaEoU`IUVqs&VdkgO zC3Gd(M;3gNw{m{syE)`K*?mh0Znq^T<3Dbxn!~VUn&c-g$HXVsm-xAQFw*lf+Wi+W zh-hRd^Aq6{{I~Ea^1nEr#o0u2iuVL{9x&gEOdO_bK64i-<|?ogOlF?uGpyy4nWXv5 z1}*@gd}b3D^}5)`MWZfuaM7%bkPA;2dx0{3>Hs^4@BEr#aB>v@2sb~GGT5Fe-ZUu4 zTS(Zw;AD?n;4fZb#NUI^d6-8SMdmevXz9wB+&I?L7I!wRKF6?B&TXiTw4q$CO4G=i z#Dr9MTnK9_Bs#l8a-~k&w$9ER5Q*L<4h>~jw`4(_7^q&Ryc7NE-|EI)sLq0;az0=- zJ=1$~sqRP+e3Ct!p4ot4=*XNNSJOjm3>gKo)#QBWo34q@=Ujuuu@4~9M>w6u!l|Xg zKTOFTu*f9GO!O1KQ93-O)I|C)e@qbx&R$t0TS3c%s_r*pOHp-A*Edej+-O@Xf!wWp zk9faZ`G8#PNL^mT28nCAnA7hOvT`HT@8x>N=^ZNIMvPcj)!&XC&EG5ZP|Cy@EaCLb z^5Dqq>PvruQD`j_oJo!=T~ylYj5|DLz0T*zQ?an%Hv7BWZ7PqSok=e^N?m^kZX5cq z2Mr?mLb<=8l?9@y#gLB<#jz_rz(KjV%HHP1ATz=ymJ(x>t(TFo2RppjdQ@|E9dgdj zE-*$xRlERl?ZnnQ7koyEBmn(A4IXDKu9wkAeG4u-cugZ zLeW0RxR?V$!5HPlxB><8VFFgKC3qidjut;D-oA~K0EO797XJu&D+RN1e&zKHy-xGd z`i`7`q!Md#K8G?8bEzAM>L~)fofZvR6mJp{(gm~C1y7GsoxR%S@%cQOlL-sN?{(VR z8$}X(3$ceWJ}JZ=(ya;D5~g`dTn&O7nH0$K~SMvID_^Khm8;-U$yrLV@Y zh@!>TE*eAI!o|3`@$kEaPutiZ)UfdMJ6Q>79cBm%S(To}1Ym8_>O14iB76JUxikAe zPoAOmZV=d$UgtNH)b%;-sB`|G3jQnb9~3bpA1W{d?KLr`3b9g>_hFqooABb$__0zHg3lM>xPWBQh8(pJ?zOXU6hGV<1Il6A+ z+T*$qKgkyTX1X~br5wSp6>KZfh>?X+v1`~Ic4+RJ4xGWL*!>Q! z85O&3$zMrDt5hyYet3Z@_uUu1B0Bt#Rg`+77rlM9ZAE?alCnCVT@iy;gb$G zMO)gcmiX7+;=tI+@!8|sa68+sa1gi{z_<|2@peIdQcDPh?h(ejI-Tz>;^nTOK`d;2DRP`E@l~td|il_z;IU&8hOA_ zqkuqs$~8l6*9`B)47FWD8u;9D*O0@-47J^7*e_Q9 zWe2J^oR<{D8*QqsgF~qTl?wA=;Jmfyururj!SFZ*5W80%vKf97gToo2y0;^xNzs{uA`P zbR<2)@PI_a33{dNaYhidJ}1c*BO>bK2sZbP3Uqv*F++Dcy~qMm%FFWQXi%_3Z*zGH z2&zsgHQlEhu)u{FVIkW+6PFiao6*G#lDN@#G59i3=P|7@Irz+UA@-nlVIKFZ>^^!j z%Ju9%M};YXeH}t}GBi`5lye)EF_;3`@h-w8Q`imCH*Y}S2JX?J`QU=KsLBoxbp;l` zcKo%}5ZSXo2VLX%)j95?VCZ_<++uJh6#(Tc9MMYAM;1rdQ>t2H&xrbqv3%m zSDNq)MTcszwFXCgdZ*X=3IU?L3Pk}sh&oK^dMX0fllT-+;m+s`EZYqpIU*k`JwxoJ z@O7i<2FMw}*Kiry2V*Tfpq8-MK3t-bvnYS{2SfjH&4Jdoa$fp%20>RE#%uO7F;l1D z#-f!EFp~aijgd5~;nSX|`XB{yr?ocv_4HeeFJv@uq`xJuJjmWje@zKW>I`3``#>hNHYkNe|Q|MK8c_%f4EFxs0@@X>#Hl|TACQbTP z_A|^g(KPWQWJVXDogg#U;P=vWbPY}~1+(owWVsX~uxo&3DVS~7;P+Bhq{1I4&!sXw zchDGKyr4~_tCrju1%e-AA?NV!hz>jesG+*)9k8;VzUG zGGa=KCM#QFkBJ(qQp>8@UIMG9mL>0U4(1vNbF$|ZbHwrZhFCa(5q$L z>ynVHC3mWJGg{zyE4;dB}`#P8pF)i1vLfy5jn`bNKeVN+~s1f zS}vNh3Ik5a8@mn}GSe9R&06hYdF!V8G|bZ1@Uco$w54y1(rIzc{L!Mt6Z)7=Ejvat zCWemfhZo2C1zPXD-9c}nCHAEF3|R0dBhVEojwJJK+!;>h*Zo?cJMuiS_+?McHX!q{ zjNyw$GR=)V7LAW8GtEC3!DPJb`CB8!h$CkKZZDq)lac1fD7uk)%lZvZF1c94PA%2V zVaJh1k(z9cz1Ui+o-Tfx&X_>NmSUWh2=3Y|HJPxkCHNAZ&k~MQ4x%;ILSV-|5j2bD z0)`t(k1~O*qHKGigKGxDu0hN5=xV#3%XMRPy@=~(u7$+wxmIPm=FE+_=V)>Ugll05 zbuYQ)-7(M$;I2yz5d&>BZInNPf!`jV0g^$gyb_A z1>Ut+$W{Z}@p109730T%<`^u0JU6w_y*6pNRC*0YK34hqnYK8@?3b}vk3oJ5Ur!vy z-tg0QKzMd0F9xL9F(7l$`Y-!_G#5m&BjeE3}W_Wz@~&_!R_eE9$MKZojn2IZ&t zP;enwP}3xZv4SpPlpu|JU}|}CI6Q3TX01q@>E(1j9v8rxu_}|Bbpc&W_H+TcO>WZ# zBsAG31QTp>hwd~=9n_PNTwL<;zdYO&IH1ZR?pOZ+YMI#`NrR&M_@L)bAdiG zQH+yBfpd{`OEB$~*H4M>*=4Zp5WrIgvT?oF#U*#-hYd0KKoJkQ|N<=s8caQ5au@H~T2-k;-nhTeR4&oh;j@m?OO zS!v1fE}my{v9-<>_2TS480#289&#m}!m}k&0wcTz2GJQF%=Yt` z_XF39Gwt}0Zi#2E3o5VYH02FCi&-t=uZ>sUz;%)=RT%qSlaFDYrU{YeqLN`rQgo%+ z4$5-B1}eT*kC~!O&s@CnTJntxs;^PY&;)kK%$37+s9dFlo}QVLx@w#)D>OA9+d}(j zi5lLl=D0)1h+VE_0Z+y&uTimWvHNL>uG(4AD`O8tsWfw`D#r$cw`-jVV&%2D!N(j* zpn;U3rsQH9^311k3UWibQEG5nnTu`K(S;5?P13M#CZ3ot8n($ggD6SWm(#B9*n?`1 zlyA+Ez8ACRZl&AoynJQQV5#=lYtfZ!T+q~I)kQDW=VGtsVjO9}qLTM@0@WHB+nkH7 z5*S!xz#Bmifkym2)G&t?ZBaNLi6?jPtcF=pk-ajx1V@%D{JTDGEYNm@cfy}y-=|@6 zsR!7~kg9U2zbBj@scM%xN~s8+rhd$vRW0H7NKzzV(7^iyZ<2oj>P>9*(Rfm;fZJ zd*xD83yB6J*@744<8&5Q*sD9tsxJgz6ui?JMrXnCA`~vGWu5b!AUWr}dOe{dM*6}C zhe0dDTYk-aUYu>tINn1Isjwtzc!FP|di)KF2F6feKyC5`~ ziyO?>C+rR^c9UJOVQgU24r&X_bza}Bk`wY2HqQKoWex+I>7c+ANir}_RF$A|8VdP4Q_i1(@|3uj7-DrnP zfah+(Gq4Rj2aA_gfZ;$9EDY!74PNTS^a9_QQDB-uh^os0mzH=#4**=rh`^^*ck@Ce z_8MK}s?w5C&BGd@9n1#`DlAYmFr^%Z;rs%K9jPlN8I=!Lay5g6*Z~+GQm}!pHdslH zb_synVHnhEzOoW;VwcNN7{<}lGqoUR6+BapgV5wlOT<>qQzhzonV&fv8*?ooZw)q4 zG$t48HNeg?=@ws-NA}vu{v2O6blWl<1vT_UWwc|0u67ur;FCf(c5UFW}oA-h!-Co$6An*g=z8>{3Uqj!^nl zGJf(Edt@-YJ@kGRJsRNAM*vUWGECkcmVQHO2=upy_ugfX4Wze+SG`~6Zg?izsKAgRU(dcg(Ohin{7WD77LnIBo1v)H7}BHBUr^s9S1F zRIObq;a@>)o&VTA zqxLtZVcdvSvePhL{oDv$jP?_zk9kM?3^XM_9c>00akH;+_8Gevdp56w_8GO|57ZcE zpV5M5W%^Iy<_8Tl!0}CHp|OBm<@&tNlah*do|}PAziTvh^cdW@Qq4Z2HvHWmRoO`U3>(L?->bLjmu%kGcZ8F zk+^^j>E-n}1VZakRx?$3B`3T>;K`6ebS%c8y4EM`O!0fZ9@Te|)`}bGUWZNNdSnl_ zL^H3^zg<)n$trZP+F2YRxw{Baj#(cxbB*A`uCX|{2yu^HV~KDPoPk{8PANp19Gq=*@!%Ul+n zpHEX)NpiF*%Gv93B7m}T?r=%>5oK-B`L4ZXdF}aX@(AhE4!sqhSg+0YCM!Y05;~V@ z4F_*#6}u6K*M>K9e4L1DGOO?i<%l4>TY~UGvx=2vfq2tMvkFnc5VWibzsiA-q4{67 zSFxkluMyw&oRJOHP) ze7B>jtMLMX7Uxy-)+iw=RMpE@%RLnK^7`ts;G9kfi~h4M{wr7$;<$vCMRJ`&a4Gb* zR*{nF7&$n>l&}x#vY}(?f_^R)wHf_89Xh0gdn|X2T=!B&$?4X)hScmDlDZU9vunuc zQijg1A)`wfH?CdaERs;0CB>$agy1YGzLR7}l)Tj?AtQU*Wyup!SDUbTg?%|T^T{Z= zf#)Qlyi<9_EIYQjTeLi)Ji_X@Cp+8|h|e-Q(wW6)Su%7_`2tHGa7jL5wTSO-c-haC z#h<1(jU&&|Mo?6^*D`A;7>{NzNFW}J>_%$CntOzS5N1pKtJsaybFS+%ZOnJy<4>YVI^#e~Gig4%dIRW8h%; z-5&w&BkhAE+**V@dk!BE9Y*TO%;m-ittl;D&`n=&2jH@`y?R0!{vcOkOX)X=_q7c zdsN1)dM-C7GF2mbl6f5>TeJEjn8t967ePwE)Fe?h^x*uKRtB89>Ek>-a7v|VFjVND z?G1OTg;JQ9=qgtj#ufc7=sF?R6=mT!glMwmvGe5}bmtm==N)#9_hB(@@@dKei@t zWJp;~2@;33##`CS@lFw)rGJ0;Abg_bY(?*>gEknh05<5P>6ljXSW)QUt&mCtKMic0 zVeDx_&BTKZhml8ls|vx1q;gZq*v36Bbf|(oL~DFTbF05e{nj2<-{-1>Mj}g!wU8Ov z+8EK^A_~JQ<^gWclk6eS*PnB0fVKm4M{Ddsl7CanpVK+aH*cz^>@{RRJM~b=D|&Kr zJBc0q+s405sUvPT?OY+YlO)GkA6lO}0(&A#)4PqXs;blx9H!=;@Mj&>6d6Ey-q{f4 zOS^paBH3E5DNe_cwndMd-Q&j85ts#WkH;$AbGNY~v1A)bL~Q0)5hrz0p$eo1jr+EU9N z;h?lc3WBDQnhl}X@OAsAH;>?8|4Vq+Xn429R$%aMe;MzzXR4D@n>q03;2km!kdSfE z3+?zP!{I4-mx%C=Gb;Ga2JavN4MeGO5cI^0GC3--KtIaV$43eTiou6D43dN!6W%%W zdd3XF5KR?>(;d_yoFNxD_slS0YZe_Wx3^9Q38D<<^wuEm z2_*loLmpTLTpDtZguJ7Tb2Zef{-Oa^7;<__#17OPBO-#H4nl|6Bh2sNkfrQNvNO9w83Vi{WC@GB*dFo>Le_0Y&npdn zc+VhfIrj{*sHO~Af;QD56eXWQmLQVi1X&xR#{v}|8)ON_c&xVsh}>?Dk`CB_4nry) zq(!b4Gx|+I65X3N#GEXJG_w$h2NKCOi{zwZ_K? z-r6julnYv8U}FmL!*pnIyR7p`*Q&$u9p5wprT!9^5Pu#@9rpbZ`qvTuJ(sI)ru#@U z1mWBWrtCOCN}tfd4isGB_^{BXi%AAsX08_%j-Cwjb>H|`60lHYvc8v?T2HJx__1i; zaB3;>cOK3N5W39Hq)#nXPCB`ixQ}8%DMV7RWoKB=VJiw>Puxj?S-X&F@GB>&;oo7jpq%0r7jU?{f0IxgEY6xEt>-ah&BPQPch4}fk;ZY9> ziDbvBVbm)o{$i}|w(l}bIv+d9>ecpl`UKDD{P zVMN%vzUW=H|EpC(>%+zm8aj|2VF=6>7lvW&%+E4w1%dE2+=xI*OO9w$6a5WtPu1Kv zrYe;66BBm_|F(&-x{(*T-o(ER;;L>_x4@nolXa6ZS)v-D9k14k$y%;gpoTRBRxQJk za6|cOT|SVFjfL{jg1afNHU2d<-z;N=YLrFi{@55l#b@PJp&7RsJxdD3NHThE*>lOX zR22-CdXI(AhC;nImj+u@6T~zQvQ`*msb{q`)R%!u9|l+fSX!IPcolv;VxZg9&nhr2 zDR+Sr%U%`>_tdg7J_$=ka#6yZ;at?$R=hhtdE}LlW0AJ#(0Hvzstalxv5C~D3$G@! zmnj=dOsvwio6Am?b1ScrhiT&Yi(zLIMvYcAz?bkXXl&KCv&T3GU=4_CKDOB?p=CpP3Cb@uB-4T zQ!DIH!>spWn*<{@Hp)Gj4SLTH%`{OqfgDrd4lh+Gg0t#UXw`wT~4BRd%s^VF2ab@J$AxWz2NC7%)W@A2^b12YI zJDwhJx!@nrFB4aj&GU0r;m3MppvdT>-ue(3&&_U2UHK1IqZ16DT$=uYn8>$^RzUp@v*ufFs^96tH+1GeM9rnay%mogcETMbV zDe+=0Gr%03vc>XB)f0s_=*CWrA0O10T``37zb$ucY&=ZTVp%k!(-U3{lG6OD|2e?b z>c3UsV3WU2@_Ab;5}7HbhD+se0lPLFCp2Z`g8^?01bh5+ef)SY-hk)YAvX-3bHmUa zxna2N*0LLhrPmvKtg7V>RK{#pn!W-j3^KhAMqf;Jgw2T2PR5@@OYC(acW?Z7&XC2z z+p!O@mglpLsjI)ti7e}xUGlkr z&vfz4Y*J?%eH3G%&K-By05cPtF+na9JeGVF5Y7w!WAd@#oHR7|<&wNV=%gWaxf-`b z8nt$UO73x<8s_@*ou>wgMIujQ^A}XJnV{lxMG=h!e|7)q(WX~WIufo0!NcMDuB!ys zJ*wI1Ylh_2E*@Agc`o6=z@)D+YtE`Am;m9x*jda{+>BaG&Z>w4#>Y(t*|hqc+Kz~| z!)P<$)b|NNT+zTV=OBA!-fzmZ1joyo17m_O>Nn96WcACKWrHya4Mqa1urHFon!~)Q ztF~f9kSn!d53prvO8fLGK zrB=21B1O?ZO#N@_`+d^kuO8I*TePs3ol0M3UPR`I;)uPo30K(wz>L0R+hrDA!z=;% z7Iv8>0LLH{I}G;VUCjZ?p~0L6eiyG13(ST5+C?iBL&)^|OG8%ewA#%b7fwuGPRTqN zIPAY`3b2_PDAO5d;nzcM`JK1b&1wl-J+sPMx`}*AwS+tK$miVV4?4x?sZ6}gfJ-?r z3AkXJ+NuO*1}=zEF_YmI$&pUndSgVF13&TNHrL{c)yaMS1VJU{B?P9UH$ehL>$*G_ z#}c3=sJkN8lnI)!5I8r3bHb(`ZgnFns??1nc}bJEsY_`nvhFfnK$3Nr0WWprtk#Wm zcW;b|Uz@se4b#OclKa;Gp{hoys>QQq;1GQg?O^(h>u$lmAmU zHq(!>mSu!H{R@HKa)RGFhO(mnvtaA6)5(MO_Z$x%=3j*clzpLBgXiNSPLA0g(puBh zd-Yz>+z3-vuN%z{8v{;xAr1>X+P;AAt;_^obW_a zE@r945A7XgWTW*oPUgHz2M$gQrhlShQoQ<;1^j{JVq4o!w`1K4Z-Mq`{F4~*_B^cy zkoMex812uYTq3fx_`8dxj)bxjn1ut=(g4+V%V7)4mG!e*oh6^c)9PC|*5uWyc+*cJ z_oH6C4HrmEM zK10XXA@U?04u7IAz=h|l_&Q<`aElc2-MCK0$5>-Yp)DuxR21 z6_}4FfC<+WH2h<@#l>rmng=niXjxv*TH~RLAMm9dS%PfGL{q%z%-(;7DK>bHyA9G}}Bv zAae=~ay5RydgUI3Ps+tllo#IIbj{n`bZbX3@DPV{jEF!AON4GT=&T%l$j8%EHptnU zOaIN}^T6D*u+_PMs0As`9pTl-agOO&>^>EiHeX)GIF_;ygba+?)IAH)8(GwGN)>F% z`!Dh-^aecFxy*OP{hgE1IUp4RZ!U+)#BZ}E2Msr|Z2b`EztI}RsF0~)VyUw6vvR@{i0o99q|99O$WKL~MFa6*tEo z_s4O#25klo3eLsWeek>=h0g}hehH&bIJoLNInYDwh37^y?O+VqJd3UrJAess2WgR> zrrPvx4IDfve(o=^E?6JS0|E6*?qsQ$JB%-VyZyv87*QQW$D$?YuZ0!myUwbGPlV;v zV7~$@3b`L8d=FtyVNoYHiZu=J%z!JX05;&$$R73re)xMm0%i)&Q2F4vTpa7@^%ik2 z2Xkx@_pk~wR`bi;aIEI5>WdxffDnG<4-u>RFxR`qUY2=KG+y)85AxG+L`?}~PZ@EG zeOxS7w77u>B*kgcz31&p9ON!tW%TqByC;@$J8v|VL(M+VjaR&E-@<>P3Z#KFgvPuA zuk-}V0BzHAA9?${=enPNOSi>O@!Z9SO~M53h3$HRoBVr+ps~F2coO>sbHP6{WGtR~ zjpI{v*@3M&MKgMPv?Q^Hd!vkR_uebUj(@Ox?`8zbfRrZ3vU^fJzp+_;F5TUEK;0g; z#OrAj)i2DjYEyfiS(~5@B0NBH7Oe7n= z4I9an6W2xkOpw*ZC2^Xd@e>gG-n@T8-oG&EpHxZeiM;>Knlt)N5)V;vCb8dg zv;C8*b7$thj61!%jMpJjQe;l; zp1-~Zb|L0rDuR<2)oK^FozG_zXVdB_uKgE~0T9rP4|vT*jS%f(-u5)EqoejRB@%}@ z!g+hpR85lvG(HcaJR1x0$N7-$vLxSmc4}v6Pnkp(l9g#{Az3wD(M#{#%f!NTzFWs> zM3rab>BGXL@;eX5%sUS#07VyxJ~flwh#jjrH0ygtRGo*Z^G-HAZY!gLRT21L74iGt zZ!^V)-i{{E+3aZTZGPYT?X|<-CWw#KeQ)$Ozwh}r_2yDb?`wW_T|CwE11PGfXs_dL zXvsd=k}B@xaz#G-C=ns=;quy(h;Nb4ZX>y-9Okt&6;$84^lvOm?4SQ$_v%r+RmWKu z_BYhFmD*n5lG-*?{M}q$Yn5#xwL1F<5oNv3t?<+>*}5BAQmu&_;MBjH%T}%+nk0X+ ziE9;B@>=ml)!C-&Q>{(cqhd;sG$um_7HWgG1V)+5a?G4Oa>mk4ACc%+T$PA+7q=l= z$8+;uotBbhnLd$;O^(;yIVo81EA~};BvN|cSDB}{Wr9IvZBy{oI zwx0@YeVY_C{AyCX;Z9}6b$xj69D6yR-M{3I;6`=6G(W~~TKil$gKD`?yNx{V8r~SE zJL|*ixl@e2Os$iGrhj8=aXZ{fEIjykQDHAGd4Ac8OKbUz*3vv;(w_TmKA?*>Xg6R6 ze&K^IZz22Q!kwUACQiqM|ISSkX$lU1t_<4Dm3rde=FPGXxd_Cf^5Qzv*S%(b)O%O6 z^EsGPm^%F5e~-5a)>TJ9PelOt(ap1B?dfIeYsw>Smc7M1c%-3lo&tmoHUS@vBETRg z5Ggx}1Bs=*qS6Huyv}_{neHr|AIt7u@>lZIX{k}!{YyVcs(ZDBPQ(^_& zaCK>MEQayW%ueg5e!>D6d)Gp*tH&42($Ot-dLE~C_2lAL?%e!!;m7Trb;O|Tms8g0 z`zI(q<3@(1e-0+#gQ*Ey`hSCZSFs!Wrvdc2;0w8ULq;S1*x$IiKdQZMWM7BULiXMF zN1o>Joo?}Mv278a!tA3r3BQ8B2e>XpEPov!p!QUNpg~KWH?(8W3@d!hNmFn*_84)T)}pJ z5vu`J_Vfr+PxJISo<77=e&@k~?A*u5Rv#_jtK3lGXxJ@QF2D2GPw#xg%WgKjeI#e& z{TOi>pN5G!G_zvdss>|j=E29DO=*umLsk6F1KxLX&#@{hT2&DPz^dZ+J>S-wOBKO@ ze|CQH(D=G8WN$fx79JWiJf^P&;8n+>O|&-rR|4>Yy!SffRnXVg&c`MPbw6y$?g9&+ z8{!SDC zL%ncTkTBre)B++-DK$;SPH41OI`1%X)n4~=2R#2cGz%vvB*(H_V8L+^mPHUUr7E_% zP`bFIP%5CtkaMg{@UVhA7`zXaPGx3Xf=``3rpc7%lS{>8&T^r0FR6#?Z@>?~0R3bg zNNZv0u)(o?*G5`b6sl7idve%ncB1>)`?UDGTK)Tmm7<&XeMq+oqU;gzzf) z*z;`2#!L(r{4@dn%aU5)`|+!p*K`Z=C?fOZPawYSdhhtzMB@I1Zl`fhZF{VOzgxnN zDtJtURkk|3#*#ypD5z2umkmfAt$|3IXrB|3sEOupN>w8~YG4!kl8h;U6*U1s4R1m; zC-KzDoyCtUjBlPDcyh@`D(9}^9M@An7{>ursIp*9Sb+9Bstch zK3d<%D%KPpFrmaGXzqe$ZW7hmqx$>Qq$k~@W=TuZ;`W>M(Eqx}^RVJawAY{1rUR24 zkXe`d_Vo-5YvAw*4iWA{aHxw`)pMp$R0>UQCin*Q$ac7U)uj;`HV={E)fclq=&alN zei^}(`3crXoOfK2p(2EOE*#XV-K>w#u|5vi`iQqtf#|?`FZ=L9w?6I$ZCD?l11{fw zv~G2IeH>u>@_T3_yGQFI?jw~NmHm^}$Ag5p*9}%nMY!kkSE9hu;woCmurSh3g{NL= z1Bj2+bLyan{sK#uBh2A6${e$>H(ELs+6vOTo42)sj9zqLLSJ)LmI!Na zpyKiFLC|LUzrTUvSwT0Vw43n9{_D_Z4W@8X)&C3!t_X$vYX8SsP|LRK`5v=f|Leam z+pfQf%zBsz*_}r!_#0&LggbJ@*qE@zd5`ri%Eqo__!HM$h(WN8#=8`UuYxJ<3TJWfg7m=)yzm~e1>I|FogiaPxxTkY^Prv+zpi=|U!xoa zp)^}><5!nLEoMek3k_7v3@^M={9*Unny7fkFT7*)2g<(`1uf;LhsuBP4BY`MgK5}A zDE;t(?0E=FtYu!36EKMFe1o36YB@#4=05L<3^oke&R&l8X= zc28)u6p*XVM}aHI&Gv|9iJ;2ILRXmrtXcS!E70COx9R8V0A@NbifWg&%_S zK1h*`BT0{PjP5#E*L%HSyok;J0wnW`)!FV2wNxX>U%bZA~{B;nOA~163NHMXM0N-SlNimCsTHwB*7oL=81&5V8yh zvhVo{5#HdCN5Q&HbnOc4HW0JUqj^(+l2P)eUyrs$;e(Sua}0eWqXC; z{a+LHe--^-`5*Vc7a&W%G%cz@7ja%Kb3Ykeg5~;v0^Vz?fugW}2N^ax_VJHz4w9Qr8bR{t)+8I<$bpLUg3(n*QcTNb8IGL$fp7KUtqI`+D~O@ z`Vqc&mdfInu6Rom?-ReU?yqmn?otgeEFD|1@OGPLj`nMP_+H_a>Rfiq;=e>t;Jlw)XRb@Qxq5M>wEc;0PTooy6BH6H zm^v-{CH|H8VAk!6(nZXl(Kaq(?LeO=ESOusKDjkOTc$n7$)zT7d>pU&6Ml$5oW+^> z%yV=TDOr(S!y~2xX3{vwU*TLF;ph*27Exm{i;)+U6}ukWZ5elZtwcZ5ys@RE zNHJX$L%vo#k-fiV z3el9<=ntF&YY*awm;8eu+cbP93K5BJBgAN54BrLKy_EA0g#Qe+M)B9gy$M{%3X_4j zBK+1l!rFxuFn^8!iG!6R>WX5o?@-q7tZ|hz`-wlnHt-30g=K3_hritrh3+EYY73)RflMr z*ZMm>o?G|v!u+DYRTBrgc0=QJzreuKqj=FJ=y~=nje?yCQ!(_v>w2D+W)2|M8s!e4 zjNfd-B*_gH^%FNKpL(}YYQELxMMUqpp!F?;Lo^y>9nY|EgXMBJD=eHlh~RGs53n4P z$8c9>a^wUoEy3is=vsBzt<>QmlxuWBiZpVVO;?S+?OWnTTHZ3di11aI zWFb^8xRe)r8B`#OG96AWCNHCLkS&s7G~EHgexcxR1f6$E+S$Ffo*TR=$eQ(*jRWDn z-%?E{@V5WbR8c4N|3Y}TSX_GeMp~vuvSR{mF~m^i!WgE^X0<>s$9Ne6f~Ka?#9VOE zTg3a+w?#xlo)0{E@mTeZt9@pwcGX}bPb*&$<`uF@-(pRrB3VF8%lVH&k|J8%wZDj} zc26RF5*}uTf>@*j9)M$5R_z})(xP?=+ zddxHxiIv7vG&hD!tZMW}DaAU>-^`|D&@12VV(PW4n4lR{~xqaJ8SM-OVlU(pdfjtIy; za>7QsP@W_GE#Y$c&A5c+Xl**8B4LuoXzqk9YVy|?VFD7wTjGa>EB{T++?Axb;CQs> z5l<*~>s&CUk67PTqmL(l4n{(E!7AA{_fjHtsXdM1Kf66sJ%!jP21l_siHPVJ74XJI z5e>;il@?xcI<-Pj?bv~c&Y8Q^?HYGKc||ZEr)9x$!8CC=x%d}`P=?)*(e(#@OisR9 zEUr-v1ina(%*_b*-(;ia**DEF5cB&Z0Q)r*&qxM`Gm0}xiBFdr-p_!=HqdncrMCXL zVCT>Vg;UmALEpyjmFGV%30-x*u8VwNth%H|%?PW0sIPn*)V_G(FsN-}1NE*@o3fU^ zH`LxjY3~8Gv$3#s_y;**bn^iiAU(o~q56@j*7}y_uG}{`F~&z!oV75D^_%)CYfap@=_mEbk%M`jE)5QAld1dBWRor_=?Ac9z>mw$TO}X|qEmVntns)qmy1B0+1JCU5?P1~(iVOt+p8y7G}9@X0XP zZW?XVB!X6^$y~LQ-}yEP!#;;`2COaqQ<8}wG1KN3%N}8CxC|b17_RamaV&sk+qq{W zhAN*_$9|AHp+q|AG+Rn@({j^KQ?GitY2pKn1<+&)`>u066I32+EWq@LD6MlGS#G#M zWGNc4&SC$)FKnBs*Urr}*5g>gK`Q(<5L++PByB7K)ir=>tN%u1a>J%T{G$Nw5>bjw zyl*_2#dr@40f>MOaZc@&;$E!Y57Xx06FN*GQ_FNA+JLtk?4NQgZl!hhw~eCZEY%0_SyWj`<4Y31o)a~ zh)BmR0td^US^)4cxWUHKkoJV!EcIB6|GEjJc88}Ripm8?2Z!fo0XP#&i7x`SFcD?2 zwND7c8)*Y_-T3hck&0kU7b9fT+UMw3)##Pyu^+qakT@y*TQE-5`G?7K`O0b$>(jw; z^mZ?TXB4#pliC6s8=dc?v$MPZ6^7Ljv@$Ex38aP)9+jqoK^zKM2XQjq^85 z59iI!| zKV?KmYA_jEZ29*L?QNn#jo6_&8g7%cZ7Pue|5Eoh;8j&;-gh?VplKUCK~s&E(bM*{ z4V8|eMw@7=IdUTV=m|zeiArmpC)Q@HEwqBAoj`+0lUx zX&b&o@C#58#R_V*J0gNm75sqb_rKRZNx+X@&%AS8@B89q&faUUwf5R;-S@rj?%pPTl}KBv=8-ZnlF^O_M&I*X4@I z5#AHjd<3yd@(FI_)p$e>z*d&@!p4o}gI5iXEpW)_lK>ZpI2o`(*Cr66f}AcHKvSHe zZTyBIZfRf~up_@)Aw_Fg23}P;cd65N>Rrg52b?z$?B>H}Nnde_hXSbQnKESBN)~Y5 z^QNk(m8!z<+36|doFqg~^?prhfKV;?588tJ0LC+kJHQFDvWo$_LMbIGRh%oH={2KxIFd6+ zd+r;dqHM#iE?H@C>%SxXR#Na;#pe~miz?Awv1k1qHWYmP#W1DSAxtw#OYn#+_lu21 zfE}G^1!1>UzPHEs-m#R&s3F?n@X*qVMzrRGwVD|lkKaYR!o z&!mD;})}gC}83%T26*8UABCTm+ff?N`$M zG2a(b&8C!^@of2;ylAJ>zE=w;L0l)CZ@I##t^2!ip|LG%8{SzWe0Rd>vp_-&bh&Go3`TZA zX@%hGPQyB)Xs>fcIKIFuC1+fiopP=uE~8LRr4zjo;8?>K`8_Uxj#;_g1O&T~#K6VM zR&i*4st-i~mSNO}ED<@&as&$mk7^mwLjv!Bp){>>^K1)Skb%F)R7whFF_~ZH@m_X* zS8OURBT;n7lLcmISjl&sj;=wcLJM$rQNvC4xBkvP0(l~qX6I+R8C~?^WTV|oGVP|g zcF2*a0E|s;({J{If#bl0SU!0-LVSWzy&poH2}>_LE+GWpsyThWa|j{oO9=7T(Bb-d z2cpAch7N4YqQSOLbPC4Xb>N6}GY&zZA8<8ROA@?X!QUVn^>E;xz18ps;Q#R3U<-J| z%CrQpWrO>A%(ZL8ATBI&Vp(Z716(u_oO~UH#A7;i@O!o;k|MtUPPg$|Cv zCY)jzR+zk#HMy)EJy9)Mg=m9p+3G-;@oJVM77$8HxbY8g&xRXJpm0N(p%gZu@qrm9 zqc+L~br}*MQWqEz9gR4=rd>N;kRjWx;|j| zjJw&=7hHHbG-a1VjQB5e;!;1N7mk)Y{stlO18_vz-%%CnNk&5^3>gFB6n#ZB98Ovj zP6v#DtwKX=&61{^EhD4xON#4RIctc0Sg}a_y~u*atSCLc_(q(Qf}p6H=sV^KroHiYNZAw(_hy>2f6!lc zim7U$hUtkVzP%Qf{Wh`ezoY$xaLn!5;sqY@gw-(Cm8tpH0e*5gl6hGJ&5nJdMP49V zsIBqTo?V>!b-upq-!Sk!PuvhY27K~uVq8|xR?}mQHH+#PYbG(e^K>!R_SwQAn>;>Q z5XdlN*womc?vNmWaYFBE9pbFdkAC82uXTXP7iX`<-}b%cF*}7Wkt9yjRL*z(4NWD& z4|7S!7oH!TUFz~RxnjiOl%MTFZ06b zsFi2+a1OHgCjrj+Jku&NdNA-pf&uR~?GwR}(cMmGl~y5$oK|xK>R%p+$0_xKz=sg?P8lF8lB^qDO8m6(z-k+2kNSOkERMefb66=fp<^y< z(J()}*eC(L)ZHz4nVeKA8%n z)yn6})XUgv2BQ{bLOr*VV>r#Q>WBH-{IWmecbGe=Y*KL?E*^-tVZGS#_DRJ|gNAP0 zj4uIr{08*64XmStBwPE+=$8}R0!zK9MHY|SQk=3?tNis#57P;sm#F%529Nzm3&ZKs z&*G~A=iz&6HO=}$#{^1Hm~PNDsaSDTSZ@-NM&1?`0E=eWASDNEVlsU#s=PURuP|7K zf6!>t8UbJpeIV=*>iZG>l=g417W$}VF(WUj; z;0N~MSviPrVFf-_PzP(sD-)+ix6x&IghF@+2CORjBV(I|oPO}C;%C2vabc=YMtbKT zHPc2xyL)eqX`v(3@1 zHNg>*N;P&pt6b&2D~D_>jkEF2s#JNaD~Ad-S(|#@Q+~56ZwYx7+V6cEDnI98|s$6JVX19nHmUdP?pnx>{tHXVADeKCsaxylS#?m!>LI zChlGW}HE(mPu-FqbKP&^Nz85WkOf zSM!lWeWJn0-l6X+1R31V$QKmm{=p6uJBq^MbW&?oaA0P|;+}oU)U~a=CmXCFa_k$g zviHJ#KAc^)jmXyN&%m`!TbqaX%X?%P2QJW?{VioH{GR*p?YW##w>Jsu*+S6TzIFMA z-7~lFkT_I}oZGjqbVJ02-4OQUV(JL@*muA82G6ZX&*^5)!zil|K3{zMw#1C-e#bM| z?~D@WPObPMd5M65EDk}%FMAD}=Cq!9YT@W)YQbHDr6VXkMWyAb1%e+}H$iDer4^|K zKaWd^@NU>S=P;F4rWP)t6cvZ^0^}PAC|d_zagtaj0}OR*URgxVtH^md|5x|CgSYpz zkFxA;y=>lK{&%uE7EtU1+kz&Y36$oSPbH#o+VVJPikA^9*`)bw#MZ6@!(5ngV;tMK zPgC@W->`fp!7bCCq8EV$^S2pL*!QSB%%_op2$#^SHf#C_Xqlt=Uhk1q%GLbfQ@9#w z`97y`(S?AL5PTx_dS4L0@qT@pTyX5GI@U?l(T54-G&Ii1x+xpheIJLc8$9yOtAke0 zn{QpwTszUE#Q!ydC_t4w!bUztI~gwvK>SG=h3+Mw6~7 z=HHcg-xgMV*I)gRLd8u$Q4TJu`YK0VufPnFx9^47whm2TkNvfYDg4MDAt{x zi)VzQ+i6WTa&%VxQmy=A((-K+(g1ZKky{R5F_*_*y+e`W6IvB2#v8o7(gyRySR*K; zqrKT;<8dNfchIF1joYHjO+nl1$YsawLO3Q9!!IiOI9F%KYGuA%_TmVUp#0&i;#vE9 z_5$tSvfELkPu#SvCJfXO9X%Tyd`9&Do!Ez+q3~V!WUmR?j(%<9(9X zvEZ&V5}A@blQ{4BnV0EXr)Sf-5p8%dtb>n>;erS`=+?lNuuupdl~gz1WEO3eU9&ip zm%D2MB8zojSlw%fO1r9)k65S9=V_st=B<i7D~=A33>%LFemlv0@;Oy%=K&1SljXXpQ7;`8N9(jGjfSU~E6B+~O59uA#_By>C?*X~&kCo$ zg;Qij0^`B)QnOSG+`4HdBSxp*yX@H!zW0Dn-}6*VAFT{yQrPuY`Ps!Wq;BV23nras zoazXkxD3=N`xmqzsqhh68hRlg&HVU00eB3$D4v7nXSMRp6vq^kR6>+!3AWim83+U8 zHxHalE%8$fh3P;}(ahaO&0NAQ&F<9byDLy>et+wMM0w8ba8r(!MKHf$G!yq7JkP=_KKy7&G901EeOJtj7|nt ze$Va+T+db0Syu4-VGsXP>()*8u9C*~dkwY82BwFa)xl!)k$jPgHKBvWu-tL6Owe&L z9+mbzNQ&ibe5A|6={ue3eUw=ve~x`nwO~*6kIOYAGpU8r-wUub|GDFnSQ$wHNtgMK zof2@2(OJhmVjLFhTP!~#%`5JVZCdvjo!p(8isbc(&QoViE9uL=_%8moRUD`5itF`K zRw3sWr;Ds4Swt#eEIE5BneW+s;OH5(91y5_(oz0=C) zzxR^5hb)gnk)b91wL}72(mVCAw4_#SOKQcXCADH(QY$VkDaG-U{)#279JZu=pOch? z?=c^~RbScj5~_ih&?ZMlCU#PW)kcVh1NG5wGXz6Al=)-tF=8vURLh>d7~y)w;5$-5 z@x2reWRWcv!EcHQ=-d9C!(|TUa+o%k;2^km>~=8XVJDb49RQhcw17v047n&)fmk9G z#p1LzGF7|TiV4QBswO_~ig=b>VYOb#Ze; ziCi7$pA$Qu>O1u>gvSxW)AxBUavZGAoQM;^*8x8tZ#5hNx~Q{GL?k3)5m}1@!BaJL zRCgA?^4U5)QBS>3SiSS5$d(_H`!3NDeJ(0-(;yDQ@yxPl{YjF9q|*u3MboLLlC*u#Vz=LvB}SmBTX38afeCFIkUbBckgBac z9uTf>bF)EJw+0j*nK-Ip`S-RDY{NAR{(2zCbe7~*-Jf;Ped|H@7Y@4@FF$ax6?6j^ zhQFFXV$NHF-GkhVpxiA(Z#-X%ZJ6Q5!ZMDHaS+?LdCuomQIO(yI3O>C9iW3_Zu}0; zYIhQzZJq@B6~`=@8Y&fCakPXn%KWn%m zK-&3qj)P|L=Xx_`OZe>}-5dL)G!!s!Nsg3)Ci~$rdOlC};4ZYTcUV84l@5uck5S`7 zu=5qKR*(|$r*bOhuj6m`8; zC6#)XN-Q>(z)Fp@b?aFeFks&g-YDUc()aVR`~}SF!Q@> zs8W-ki1$i5yUEp6V;Sj^zZ%wdktyM<;CfxQS&w(b1+bWe{jwXf;q`42tjGMHwA3fy zt5Iy-PpYy$KBNo`5$g^+ahv<;<|h|C8dMR;_Z$pit!lLdPnUQ`SU)Y|woky9^o+}R zp)%BNPxkDAt~SEN(4lB33uOo?@D{!c&CcqmLLWEogc72vTyV~65>B10IWw#{fp+Bt zwaodr@2%77cgF1n<|OESseDp6c?Y1JxdwzS2PP^RkR#fu1y{-smjTqPaHv<1uI7$=1@qVi(ya{%93j#E zG8Rr3#LdE0X3?VXP*!Z-&-F&O*LJ^Squa*E+#u1ygROnQ7XJH)hTbN^-o%#;f#WrT zhg@F_o75Zx)L!u=RZ8$uE4H2Kp8`q@i4k_p!Wm2 zSH>A~k>wiprTUr_6vl?C3Fc=BKk?>4^aT@>gZ65kq#v%%!Kz@VBk6O%QFz;l0VO!; z8UVW*6NY1s(AvTsR7bO>nPL{bjVy5(mTz&SeX(UR*Ctl1XacYn(>G`{ve8E~ipmoG zxQyQ7*Ou_;ys;>&)zOi$4~YDnLz`LF;@Jrq1L754oT4=cmtMZAb`$Uan)rb!TUc|R`) zxa!4vol`6vu{ApR@12@>{04oFJHAgny>ccq3A4I1MoRY@ye#o+h;I1AKH}lIVW6cE zsL}6e#B+LL@}sX&i)3j{Z5PL+Lp*o2Xh+{l7_bszwgk_&1dnBP^c6aa7{3Z~p5xk4 zlv|9V_#KrYtcBb6wX4PCB<**Ug&zYuYuF}_JAjlp-pUWpD=&oqLwn*iO3N|o^&!fU zD_;3IQcmgk*>Y$&nn`RQNYlKmm^>VC70EF$xS2c>XBU$vXvbp^hVL=@T}vI(J2bvznSpCrqOyV6ig}7vE+t)w zbS~1hn4$%RiS!TpH*hi_#eFw@+x<6RRtlzpE!+3+*B6uxe0|#o+0R=7o!A@5L}8>x zf!%EMB#t!Ggcu%(NE#tvKAkCr*ram!FtawOBsK3-+#{v14cC+Yovlfu45!z~bdU=H zTBik2x9z#`WTz<-n+~byrUz+xL>*w=d6%ODT({}kk+=!Iu0{C55nSA6m3y6<5NMbV zoJ$Idv4sVn#l~6%0nq@FfTWO~$E6%2PpW_~nTC)zCW8X_Q|D%5D}g0qMe`vPY{yPO zxUNW$239CufRW-#kV6@I(d)mrec(p34_qs)`Tq~>1DoK_oX&r^ePF|2>2UkNgM+2R z?F0A4r3cyv`YC;veZVGjjO+uqYenMa5EoLOW4>#8-a=)%9Uo*i3ayzl5WtEIS_ir* zH48I~cCcvx;t|^g4k_6M4vp2T@3ITH-v6py04*%3x$G2oz3n{{yH|ans#ZW6+XTM* zAyEeOCEA5hlCBf4p%2oorvagFzsYmzZW#qo{8#aNgH*Uq4adewUiR0e&%YX3fIy7q zu<<66tKER32LWYd4E(+{yRp*FTi+Z=S5GYWx~^uYYOzuk-e*1|!uM86lR2-=qPg2(71Z>5O7yO;jg`0%|UykR>&Zd41~I zQG9>Au(tiEy(~NE!le>_e|>noSnp?eSv{xsI6qT_7;{47mPn)YiL3dyo%=tn2fj>>!?MY zCKVgW;pNU}^~Xj9=0r5ZY!DJ`@gi*r-VkxRnPSBhJ~N(OWQD$-L3c_oLHhk;^~PCY zh5b%PTM89z?R$OBC^0#re9)z5%4vWf-!}T4j3REq#nAVj0S{X_FrWyTSijpGJd?+B zn`IfldE3_%|3mCTzT+=SeO;all4!%qO_w*TQF^;{HW-KA@l_Odt+qwqh1w2dnd(Ck` zNqVX>Js8@lStk}c_D%?{{2eSP$0zh7p;ab-%NYzE@0hyvH@4Fc4`v>yLG`vYYz0#9>)NV)z1iPdfI8Xgibp zmu6fg-Vn!G4AUDPl{nJ$hAu40y|(G+kmlk9u#@2mbMPGnQS3&1(JX8+|~q37xX4wK;b$I zes$X+vOg){v}&%m3$+&)-F+KojW2ZU%6TsZO;2UvXlCf-LdU~rU_>h*(Rs@?OQ;ELnjW%gTBvElp`}<=Gi;h}lIBkG z7N~qLLupmYm|BR1{G3XtokOm3YWRM=jgb;;TO1Q8XtQ<9=H2$su< zT9Yaqt%>5hpqLeTX$|ODo0(Q0Akc0rWr-9e0vHMLdrrnpjD+G8Gp@@KSFIVFTSmItB>oRaA6aj!4Xu+>5uB!KvEK5fjn5zRL9pZoJ!I%a$hjBnG z6JY5djKyUA4%}@SatF?)XD6OLcd&bN6LT@-KSn;!l6=<2cAs^JYgKY-XA9orEF<6Q zM3~x2|4@*=Kaz<^iEdm;YThR93naTEWPlG@4 zlQ(B<4zuJD*YF7?Rf}7nu$ubyY6qXzPXm-Lx0oW1`>ovL>)kuMl0C0@{(gW*nLA4ftwO9Ox*AUH_ zlyh!hN0bUjP}rbCyuJ=A6(%Ums1T>G54l1o)1_31&sUN{?FoZUUyp)bKu3mYDj09$ z?0H#}<4&eFL~JWV0^cyJIk%cYQO+=*nHS={hcw4NAbIXk{?rZ1bG+KpV;v;Vxy45g zi~YRFeT<)%GKAviRh;C-u3pMjydIN^VACW!%HLb8V-FBKnq&tI-fY+J&NkXlJ%MAx z?o{tB)RBVIP^ANwD;C0_7h8G6F;;iwywYjmPRd9me+AsCb5fArUHqn(#m^r{t=I7~ zyo-mfbVSa304TR=JpTgEdzK}JRT=*L{;s2BVh`)CPb3%8KJ<3SZTaqMh6>^}@*@PQ z(Qik5e{(_RxQ<(FF@3hTV#N`{&UuWEh@oO#kqaylPqN-QYq->ggt4KeSoHqMpNe^cD!7A9{U|g*J0Z8V z8d;NG>uu7gd|eik*NFMUxxh^^G_QAh1sL15Ut$-8wX5n-&(n< zwfdwJtoKO(z3k5;aGirZvImJ#SVzrCVPP%7$qyI22jKI?K^DaA@KmI&N9SXqdeDTy zKl)+SS$d-s<)a2bM0eFG9G-In@xu!0VGkI1P9KC{P_;A|vM!?5HOA{2yEg7Z zJ~@s_m6{kMC48B)?JSg;Q=nAncvCD+C$^t@ctd06e$VGi@9X-wkyqbLO|np$VCkOS zq*kpMm@1Qh&}EY~&{f+|p?Q8RRAG%3{&uiZ`%tA?E9_zYJ;x$4Auc@wY$x*~4R&R+ zJFK`yTYRNXo@;n0wXn?WODGw0V+X)6rJPsA3Y?aVeMRxhpyjBO$N2$fM%ZAC*KeuU zR$DvmW$Kyr7;z7|2Xt-a(z1d>>?uDPhpA66! z;RG?#f^tIx(9`y8h^67J5-b<%nBG`ag9M2po4iL|ZQ4F@AH|AFKUe{R6qBa|lWCv? zv9vg@QuwZyaN?*RB=O*PUP&_Eu>UV`{DE`Kl*R!1^Xv%HSA=E72{^#XxY5Dt+aZ*) zeRhgifMbW|y%kWMk(e=@P_DX_+eQtAoRQmEAbq14QqDH6&;bIYliyd_VRngGQigO2 z?JA|KMar2tPcl)yV;~1$Xy`2+Z>y6gfl%__ThOt{tPey;`d%ROo=~8fljqQ_lpYt^ zU!=gnx|bN2F^FCuNJ`$TzRYOlcx=aAKPx+RBk)c{56Z@ z;3Fq{Wvcg8(I%2U(x{()2gN}Ka4+{^9V}{0gd0x?ah1eg4+E#w>)StvJ#OP5mG!{d z$*&klVSW2wYPX3la>eAw*g4t@9s4m;15Mj0UQ(FA1q(i^S+lAa zlP5#qt9f55Mb;i?^D^u&OGL)HNv;!ir7-89gnDxdMe6b#;fXYOO}#?orz08y=lGmjSh#(O>@5;@zNgY;Npl_&-UoWr}i%rM7OJDu!SK9$xO5-fyp^`i2~fo;cNikm%^4ONHLg;p}ZvHJ55*YTlul@ z!zD9xzb4sPo^jT4lmkA-_#nEvjmPH1RibzTd#GHM#3@lxFu=b#CY_FOE{kap3Rd!YfCqsVxF6ok|?klqLeNS&jN61HJNG5MJo_(f!|Ig8|jW?$ArtJtHq zM)huV_o8pt8GY-~r8zX=jv_i&Xjn6IuEryp#P9|6GVyNZGh|QJ=V90t_=&s$F#0f5 z?1CvR8K>f4!Hur}Fc5IN(K(%yqX_xL?J+~`n&xoNj@vYQf5~~LZsMTn?H{-Y9wsIFD;aj+ zIm@Zk%>do~pAWi!`Jnq(hwi0U-NP*!p{AHSm9%IVcQh6=^yym$R!VK z7J41kbx6Roo2kOHda4X`?6MAo9sq`B>(}b!pjtn8Rq^;k`k_1tNG6xgAi#ugdvthc zI9hUclF$sbgkQn#`pt57x-O$ZTr_cli%j?J6u^JYkS5aE@O488 zYEjhENM(||a84+Mvs)!yi6U@cDK)$m>~sY+df4r*t931ypkkYPn2vh`efApcacvc} zeq5(jeuq__V0E^-YwF0!O=o970P1z;D`B0g-)l{qC@k@WFbuKfe-qEbk7=9X+wdNT zvM#Y6r^36Oj3$Zwo`+p94YP<#9Fxh@y_sJ=e}qk!GI-XQ{%AaWB)b*rgilp}9Mw|s z%Og0omkFLnhsu%H#(7qk<8KoO5Ng#A)+2qIHOL0b4-6oDX?OtPa|95s72othdZc{A zuK&jP!K(`$FHR~}{fy5jJM+|v|D9UUhBaRJk}F=0dSd15$;AJSCW5easGOsew$5o( zIoc$Ds63%^MG=lhq0|(Y4|xfUMx%5JrGs7qkEZ(8f}*0!ZXO*Yr)XvN5+*fk0feuQ zW*sMIx|~vpg^V>yK&K2P_3x=m>ed%YpX9NdC82t1J;wNi=HG~H;Ktd##v_{Bh7i2`~uJ)-GD7* z5n)|=C&mqqIu9$@iU8LfTZu}Uq*)|$k24zRT)}ZS<-7rYtZh({$$F3GyjSESKvn6| z)lB){FF?HN=|`>f-Ys19r-oWA@A{;FGlRM=2j54(9Kud?V7K?Mw_J6h^y-;ILuaw4 z8tO3*-ZIG1)0ATgN%ej)VT1s|j_D>5rBIWvp8;I~r_lfgm7XG=!gv@dTvxR8Ra|r}FsfaW@C?N)= ze6XjvIAf$Z->z+BYmD$cJ2(hoQ zi%xu^`^%~L2R)yjD_*oe>-_~`6_?^Bzvw54ZTsQ2LzFLOSyD$f%&N;4XM7ZC@^$fL zB;Tk@nm6(5u|4@NN{+}!{m-0c$2|DsI#zrmx^4uvl{4=Z z{Vjb*v7R}I?S7IcZst9N2+5^wlw@j(w14^?C|yhRo za{@#l>CvJ%hOJ(thc0Z}m0`3v?zn>4;n_#=BK2P5O;; zzhfDmC{o#`o5ycPZQMsqxXWHA_>8^ZsS6wrr(#ihA0J*ePjO4*v7+5>x-;-6KHuSm z(tO%t3G~_hyD~ny(rx7&$6BKzhRqOBg6=4n-~U-1703G=ONL4h?GK7)MCUcIJ_}ar zeY9>xYLVFArUe||KPk*i6Q(cX@wRj0`OSAcYRbecuI?t4+=TMgfV10f{~)=#$1ZP_ zF3}#%wfo=e(OiW@%zznM*L-SK@AvL7tp(Oo?5!XIyn!{azI0=qNpLZEjytn^^GvVzsj!&}GI(-0ZSgp(r_gaO^Ljnu#=vDQka#kNqR=J+O%5P3!&N3U0o`Yj!t zdx0|4khW2qSZj1^A#Nsrd2M)b8EUKQ^QjJn|AP9lM(2_s7yg;CqG?(aG1e(A7#(Zzv&E6}p?wLZ+g13AeyB5_UfAmg{4he#0x$up!v!6#Wa zlpH9}#ELgao*i|wm-Jue6SR{HF)e*>a8bgmT-XGG$Le$(K&ssq1{8sbX%ft^9qFvM zd^ocjF*KVv4UrXTb>f@vxPZd5p^&5lK8*_L@f&zpnj&ximj=(?I-1&unO`A@Z&18Y z;<|kBvfv6}bpE2bU`z&#=w}v6WK1IfGoZxLqkG@czA4Tt$G$0lQxR?J;ml$MwMxCP zo5tOk3a=s)-9%eCQ&>_#Ug@i{_J_fGPeoT*9VOE_@X@gMkXUeSbT;**wPDz9Ls*^6 z&=Ujc(z-0REkHU2kzsZUhy!a>;LH0}(9_RS?vt7x2S%Tqoq17sq;J1C`KY&Bf=BY4 z5KPa~vB>QZCJo*6*ySR6Zl59YOAXo_znJehS+JgN4v+JLH!u)*`|}+uI3Lm{M34u} z3t+H$&UYY0zheLl>BSL#M^&a7t+>LM+FCe}fY2#V>S0+qy@&Az`P&ja+fGpB2g_nw zj$(@_iJ0iSYpD9QpkeX|xzvfqTUe^RahB)2F&RUqw9w4xCa6`N7E+PO>2wiBriXbm zhUHeL$B9=mJURr(0gzncRwqp*G8yzvihgj@i(X1JSrc2l@=ImWf4mXXJhV>|!Hg$R z4<+CT>&02l%0Y}ZnK^6_3W4}G?daI{VC(bNSZD`SLM5q>F0h{MR(=&aZ3iTSn zBulo0A0dr1j5eb-^CDtaeoV1Ev8wsTt`;4w3De?_g_TL#BR@!HwjEuX?|ktL_Qa|T zA^9{@ViB6v6Xpk&u9@?!?!?4a+KdqyXcx5v(yg;e^hwUJPJHdJRAIXJQ1P4if+7WY z+u>|miK>z`2C(@s_?bJm!Mn%&ZJ}A=FU78rs0{DN+5kU`lktAjXewS*CvrRPK*m7D zR*J*zF>4RYKRW4(|I=6i<9i>R`BSM=(}SPX_c^06eE#t7&GNk!eDC3GlX*q^+4oif zwHx%kCAJ6x{}J7B8f_9DX(}dW?%0OehPY{1HRm1y%XD-?nqfXT>%DYV*qE1Yo)50L z4nszvc-eKZwmH^IdfegPyv}{|=>PgRGn4s-2WS3F2%jF1J<1P~01(`{7zAzZB)6+C z?cmZW>ZH_WWn>Sl{bkfT)CXfq_omlH&xYVxA`_@>4@0_airn7icHax`-E0#q+8Y#gMe6(!6 zO1P&zwuTzCEow7zl$R9E99_&F4IhzKp;#H^K!YRJNLow0SD~03%fVB3eNj>9Es#JA z723g_jY6G2kYc|-i*o@qPxsT7UO7ud=aS%4oee)vV%n(+VsS|*`0Vh+==(YK$1yRd z(jzC6^_%qjV)U=Zx+F6LUXi3_I+BFP@d{r^lzXF zWv9X2z0mctfhS(kW_zoNIieu_6k2Bn;q+!_);}3Nvx{X6o^%DJEss4$qcNY7XsK$j zOR_iZpa)SOZEWIdIb`ga=oafimsEIH544jjidsb9qNcb__9~Mt!7nH$FWbaZ0nKgv zhVTJjm-0W@VHEd^G-ByRXrr_M2`<^POqd(y|rcdBFaw!>}S znQ5=*Ei?pdlPC z*S7?ZC+hXyHtEbUU|2Mu^GdBZp#hia^x!K6T;F7d zB@Ps)E-DxN)&Mh{dGMD)u@k}cn|uotjmRo;zO%olHB;4t`WC=bWK-Y)#Z(IDT=*s2 z3@tW1K4BP`a3>6pXYc}!?1kQzgAN#;EHK$la0u(m=$;$O%@4>SN%9yJemi=z*6kq{Gy(b1ID`d#d$pbUC(%(N-pGHUiuSR96NbH~qWg$6wQRQ> zFs#G%;3c)+G8LUn1rx?xq3|HLQ6{zEXTs)=&FZxk4`}eFlNjbp^1&Z1!6Dj$NyV<} zb$Z@jKo{4Bfw=&i$chV<(K`lj;8yGK*w%&h0PHxe3dJb%hfezzC6v}20%A7l-b*ZII$Hc0*=`YmG;QUu z<11=yb`~CQJUA#ME$0lh1qbHul@iW)7^V)eZOn+>sNf7V^ihk&=~x=w@I-G%p?GCE z$NxfCqC-#6){JW&yt5t8OwuXN|2fpw5HI|>E5&_#FAYi31AqB6R%updU^?Tm(~t*F zgPIJp4;nFd=i6p`>e0^?w9y;6pw>+7gWZT8Pk31B>V~;lf z9du@vc;l0bqqmtk~`yQvxq~^E)A2K|ynDG?} zm~GfpP#Bbw+)2c=qjA|%-9r|nNl#tW20AzR0jsqhWuu*rdqCS|U2%526>tHnr!4UG z<;-C-Mr{IF|D9_#iud^}_-O1=fRugLilOs`;0#u2I?o}0 zsBBs|SVu5IzJ76}0yy09F>Q>wT8;wU+#7xi?b<{LOxh;f^fBa$KISa;ZulBaNdX40 zl8rPxb?hFD^}M87r|Uxa)1X#KP1z=Dv?qnDsV z7d=iZ5=YA=HIFG{3W)XH6xcqU3!cP8_!E&-7@nNBH=O)qtVqm3ZtVK(*xg23y(i9X z4mN4TKTNpOX)b6UyN&~D&`-o4uGk-pg7W`mr8=X+C*35db3Q&Ss8^c;K9mt@z=;dW z&Qqs3I`y!iJr`|!;hp`^$x5#|(fAVpr1H{i8|1P+b;~mFjolX@Nd4n-F&8=oMKX1o z(8}80_4+b)BT2d%3LKi76w_JJMQv)q3nMf~;3K+u2?3e|AI}-sH=Vm8KjvaGPcmK@ z$RaMO@&r#BCWXL5;*&q1q<@4E*9$hHKgf+wNCnAI1n>fLygHGRFl!9I_;eB zk{kOJa`Uk|)&ZJC4ZV^IP!YDs`0N+5dPas)tZT&KFwJ5oRxdIL)JZ7@DP!et(1K#; zWt|0UC$$9ka|B&9628Dnm~$oOHV-p9@^~{@xedJI0~HhIdJL}xsHUCVyVSGeYK6T( z{Kj0OxeN=R))C;)DKf~Ub`TPKjdP#7o)&DkB;iiGa~a^E+;PWmOAv-1p`8sSK1Pux z*NUGPxl!Ob1Cz;SFk1mk3dA6DAoO?4TA)OWRj6@m(CxtYm}NGX{9a^Y0wB-OVS z|3Jqsai)!IUv$s>Vd;T4EI~Tp5`&7J@T7xEIYjGmZU2IxAuf^}@PedYe1RwKaAS@g z)Ys$K*(_#Ix;E#oqETPBe^goY%*pQoB{D;(##gApq|k2(PD;TH(Z-iP2ujA$a0p6v zMfX0d*_c73Wa%{HSpbNG-7@L|8gPfvxeVq5U}NLHQDyr25^RVmbPxhue9F;ociItT zP$z6g`ef}G5il*qhn=$N&Rssk9kt0?dH35Pey!0ppMv9y&jvu(at9PcXKfn6J5}>> z0Rw;)yPpS_1aA&WWqb$3{@=(y^H<{(56Vx!wcI?wTzdLRN-mFc+2&HAvd=WA z5YMuWrNZIKXV$yII8ISD@hW?e!Yr)*0da~wOKrvzER$w5#}djX(=2voyYo9F;X!fJ zscWxbjaVW9C|d=z(jDSH7`qs0Jz1MQR^eTlXkvYV^EXQki(h=-d^GM&C6BWY%2O{u z{wp45j>Y)^r?X!3^v3ZF#-&{8>ODG@w2ES9u|cm=w&~+UTg!0$?kN>+?!7jYnvJL%RV=a&UHI~O-FM>{zR3Oe{?_LM`+c1|JM0h1) z(Om6!%r{^GMJ1RD?c3-=-m7+GsPk-VCa)<^eD?$m?BdLox~44c%4$Gey<0^!R^%w9 zIr?Lq)Z5XJBA1h*RH90846Y_?VOVg&r@O71v|`nDl4WLz@Yc#}Uo#cwniGxpi^=PX zU4O$%uyI6p$SU9Qkoa9AKHih8sG!X6sFzEO6lOmg1@6UYk-D~rcwIu7#TWEbPajN1 zJQKus64mJ9ao;0fz3cV^plx-qNmBari5aI#d#%oqTzQR#6Q}6eNZP!?6g|6U4@=Q= z-yD~s2ajNK&tvb@CbeB188v33Y10?uG3n?umV^29tA9*c6AG7Ax?T+ruU|6>+egJ- zPNgJ1T0TrP*vW2*bGL0}89u_D2sIf=o5@n@%#(xrI}gPgl*mIatU6lPWi&vSW!FL; ziB#obo!twml!qP;7E2=n#(V=3S!D$oWPXJrxn%Ti)2KKa&!v{Iu%*1E*ciYSmLqyJ zk?et_H#FA!8CEwxm>D&A?PMspA1GIvdYTp|7dbrv*S!wF>0vorw&sgde+lS3Ed6Jv zyWwiOwx3-lVBGbPD?Hn4gy*kx<4HHhrBYg{kx=a6lYz|qsJhkvc5<$WGCH580 z&X+r$M+Fw`W-IhrsGY*oP24JYViCXam=$*_c?up$;?`;uQo7Y1OkJiyD8YMM#WAG> z?-e9?M^15+-;8})K)FY%V0g$+cM0x91K($>C%<%p#39>Rz-1+N>hN((!Qh-XssYD2JS49<6YtTWY4aFD87{V5@ zp#;RiIJ`36v3~Bg8aFm$X*c>}^g?;C;xi`N?74!|M<;)6*EncOocGqcO41<6G^oJf zm~Q+=R}c$=c?GY(k$0$X6rt}kq6jhIKyQ0}^nuKYHW{4U`xw3aVZ=bNWwq)y;TQyd z#IRDDRw=7c4q*|xZu^Y0ub^2d_ry63X2|{#CNX-`M$7D`O8_Lgu!ph|2$ix__fv+u z%xBLa;#L{5wbHb!mir8bjKu~?B(CNpgpMZ$;m2D?rfen2CWSJUA?|4grgzE>^sD;b z7({c$DkW^u27>c*$Tb&JXEl)}8g0rh6Obx9+^Bb(=}IN9}TCB&7kj*#3p@|Gf7 z(AZuOn*{XGLcD!EQw*dvSWS7Sx)RXkiWyiC%nZR^JbF6KVh@E~6!YuhAu@x^wRfXp zByEFuUeUZ@+)Yxpd-pq_(NdEFJBmRC$WZLI_7V$CEakwqNO0%cb6uXH%VkVq*(Il8 z6!IuU1t9n8s@{6A)YkWsU5(SiNP+ucs_!ezAmh;D>ysqcuK|;78-|M#M_TgvYdu?G zv3znZ{lP|NZVaA5uQ9kG2q~hURiRtKkx8#a6(piB`XRN(ob98;_*sr7ELE&{D^Jj5 zvEL_yWF~lmB6FZ%I}*O1Cw6fiGc@*z3tLHRO}H)HwzaYsxh*9+6dzYjqwPamM0*A5 zJT-btYtdTNc&*Xr)|<1!bZw;Vq!Mtztrpd^(5HJ(Dasc3iahE{rK!R4fztgrmYUho zhy3#8Q6+zx8XDtTqtCQg@k)&x0wo$Tok-wgx8gzZA5OnVq!9q-^ifyR34y)L-mNj) zevb_Wg9cWLXw?0Tm#H?3h2}xS)XVE>N1ef#;6JT?XFp*&?H6A(aM1z#2V*vhYaaF` zC5EHwc-w`a)JOGedKkO#6<9|mfeHJVCPA)=Sbc^Nky>enU^2lJHg%oB(0EOA-=9(o zF55eVg%gTnGA$k3WY#UEm=>1T2&-1`#)D7@Uu;V)-Cn{Xv4!Q$#i|O2Kgz3nnOt8l zgA*r>{E+{n%O~H%z(rVvPlb+Eq-nHt@-a7&^EMgS8$u0;V^Ai9IEnnVL#YoO;WdKW zYuDEy?;IhFD!0PjMA=R7^Mg^ls$Y& zunDVGI;S(lUQ|-)$M}Fj3=$O-07sXO7B6+X0S4Ga;6YHQr~rYQta`zp;TPs{z(&71V`CHHU`-b9z=rk3NFAfi3OCfKW{WtmIzNO2vo?gi!x+8ZLfgIq#`Lu#8C# zFumuzCvbv3;~^;_-N}Dw<+K7J+BV0RB^qS)a44O&fbuF;fGB)~p?&{f?n#zWs^HR#!_y9~J%T`W5Wdq+~tx>>kv{2(F zXvr}D9TcRoKh{LJq;FS-Q`o=0n)bs>YYX9}z!#m!@2U_^X=2M9R|qHih437v=i8Xy z&uPzwr{RYKy&{e$n}C$(GGoF7fALDw52=Pn@KZ^W_-q0YZr~nze>){m*==}d#dC7ibJDGE4;@~Wp) zLpM+8hPHDyQH|u%`zV;kgM*K(WaZ+T?~}xg=3}VwzG;(zg>k`hj>F6qukdmZ^_qs| zsovd!Y*c|Y1}iz%cU+iUq`uIaElwnVz$GAKzrYM?JOi5K4g99LUzAAR$YqA!YcNsV zm`gM&gLSsy@zi`%{s{!7%Zs1=mpkw2M=!Xl>rAoQoL4}9*YYm1Jk(tySwNW}$Kdq9 zQN9e)PhE@p8RZ zffQuV)%CwWZr7E%-muZG)4IO#5ZJ3O*W+(CiA`Avc5+C;{voMw;LZ*22<^q2lnoRS zU!9-Jv2Tj08Q&kxJyOQFl{hr_U+VX4?dP{?h0%k_3jTOoxgdA@#jo-3h5l&8?|BU; zBL=IZO;FZ+Fh<`)7PqKj3%6mCb4WU?>Gnse=&i^LxBZ4cetV>bw8BjKe(|KK&y6B8 zb@al%clKLR^b5%CFb`3^A6Oijp&n-#ubwD|rz|ykV)<&(L9Uw0R~%H|hlI?j4rW!Y zaQ>xsUmny;Ey-xWjoBo_+a))F4!Q3%}EzrN&mjVc*Q(aI>;G0K++rH;Y1(`-3WPQ+zkCFkF^V z;BqQ#CTaW@ey6RVC(7Jez7NN($rNd?;`akvtL2mSM{q+0a1q3$NG>c}*Ifef_SU<2t;?0nPLSodbJ!~3K!&Mq4mkw;Q! z@%F}kTi?JAuS1XbttvD;JX3V{eyST)%Jw|V9KJgy{=TfYD@!uO=7tAnD5>qlMAbs_ zUhBT=(=debJzw$c_m1Sg=e~rBvJHRfIusRDbE4{3gd1l(!hq|gd9BD5E6A4rdU3Y& zfbs=7hfK5@X1;jKcx}+2ink&jKjs9W)rtYg@u&hNPPF-#>X;kfBe->wTn+tG zX*Tnr@tc8DQCw(K-B0je#MLIz_>YOYn^h_LDGh0pZFqV%?kCNsLmFyC7B2acUuK{Lu!iq+ZB*mlye(jSN}0@;4*Wgbr90^SOEezh_Z>dxgGX?N4mQRvKE{ zIheYcnp^l4_HKJXPI!1@?m3nKN4a@VC@VX8TV>H&EeGF$(;3vT*Fh~?V}BQQiavU?|2Pw%{d8u-FrUn zSvI2KfjLJ?VHL+nO4uZRRVlM#nF8-1VF4*nY_wo-Fx_>Y{WxpZtvu#0)P z-yO;Qq&Ohvz}TxXN{g{^#w64(*>KEFh|gmnZu)H6k7;1nIby(xuw(c@|pmkYM#O zh@*0i7lJxYU?3&TS;gefgrH;!64+Pff~_sV%h_PnwhGZpxp0_Wf%xd=#JJ<2A!Bf@ zGb3m!e;p{{}mH*jmqmeRiBOU|@)-oQYWTn$qyL1TPc<`QE>r56^7M z2N=t)%=qIsFDew1mq3>Eo?8%s@_aObLq>M+FIg>O6DBi>Mn`id@gj9_R>$OeUh=q% z9kZ9_!y^mo5#5oZWZc08h>`WtGk2-}A$iOmc6YPv^*uI>hvd#sao_}iK)5} z{xFt-iKnG72_iIGaNT7G(s8(rw90i)Wmna#$dv-;3W9MZR$n4@q+ z>YTSIxlE~?7c4Djr!)K}jV3DYlEC*|*2u5(e-Jq3Nm(&$KY+-N!vr*K%!mVAAy$d{ zjvEnEhAm({?9e{Mqzw+QaiNx6IKR&&GQS78?j`L_V{w+J+&rl93YkW66I@NrT-NYbS0}_-Tv(X2 z2F>1bdiyoK{ifMlVSR~hfp?+0ddqs88>UWzN1$h)Rob13Yst66+pbMiAnYW&o4vJJ z?*VJ(t+s}2wY-(BR^m~oOL;HW<1)%PM|3lJ<)ZnUVMl_U$2S8}&hW>IOD%kv6pjXI zAmc3LlremIna>4)Af~4(>0{C19hEM`dXvbRjxuO0CY3PL;;uJI)s9D2F5|~|$GbsS z2wzzBldx>;SRGnSW1#qgM*xS0H-y^B^*+TdxxGp&PGTy>av%Pqj|N+Sa$s$ z*uSi|jxccm*~{W^Ewxzh;Z$u2C)Nloq=FA#l-{^(<0xdJ(a7G>#~~MP6iiY+{I*P4 ztO|Iqe&J}3aVqME9v@}u#%i&diLxj_^G8>)_}(N{Ix^%6JgrwU^R4hxtP=?tZ5arm zUr%K)bn0^6>y^qm>zc$wn72}Ghf{ud!L9qBptHx5ET#&|uk){^-mn8EOyK zsIu&8Y+1~1-#>{wBFJ(oI`MPp!nd-NHp5y*dpCPm728d3rkfUtdWOYv*PVxUbJ>d= z1~Gp8n$~QvENWOGj`>Sv*^9JJru9P?#S#p5m>3aO1{;^X!YX}UYw;?pH1m*b&tV?r z>+Q+e9i zThNEYr<&71h=E0IA#sdEHcb}mHfr6Y7&-8bCfJv0t#3USXO~N|lVz*HZCRAM^Xvq{ z@8kQ=Ed)=b0z-Vg?$)G7(Y|U;7$>GvPzUa%K0ytLB zA}eM@$ir@D)<8H=jdsz7QJkHU4^yOX2xs6CgASU^55ZpEIgS}voy;&^I%F}jn2;>+ zY<&ND=V?4Pz3OlJSqZF?yIP~aAxxJS)@p*|7nbWaWA&QJVm=A$5Z8_DMP``9x9g)* zh|g6=T6LC5n(l7i2*R`0XK{Kl(zpDU*F@cLV|4b>t?ON9ynt>5B1qRefRBsT1serB zjHu1J6t%C$N!kxT+?+_95l&W$p~{i}iw@vq-I@u~vaS3#+5OEYCPI(;^{K7VA72;l z4F?##&Up#RFTyP{j@fE2 zF~E^F6&DinHapO)fAgnV@#H* z*(Fj=TP9>PMfxCqz*JRL0Gi1g`gH zh#p|2>WnF(p{%aqgbUhmB_)k=7KGu8V;phN_-T|5&!I-*?TO)y8b zVdD*`f5j$IawCErmvv&2(DzLh<5I}fA>&Ood2WPSWxY44i7q+I`KzJM9#Un95Ny$w zW`tA{8**0%hxBPld{x(Fy$zgWnBa`8FnO)_gf$RXl$BD4n(kED8JW5v=BQE3QWKim z=$q(gb7;EYjY{kf3sx-0n&_o+XGR1R|IC96Vqg@K@b!d`=x-RX3In#OM4) zuu)7}_9=^xcdX9U!&x1*u66tjJ0&s=j(S5(O&v^)$Zv5>4`Mgx3KC*FAX8cC{pt~X zAmzm`DFs|(o(7z_>%Ev7g-*XWQ`7xkOpTOU@4?lqgtcm6;9_fPAy6oIjQ%}DbV;jX ziFj7YGZ-4c0gk3ll%oj%8ABGL&pVFhtp{U{#x!TJH2?WC*Rlq#1U_ap^cB}TdMM8| z)WMkigt2p1OE;ojdaaPCKl-XGpk5JIqFIi)l1aQ|u($W*N=Ej4nwD8l#sa%VleJpY&H4k$+~Fa*W9RGCnge!x)hv_+dw?LK!j0h+qIWMg$hb z+_9J-<3S1!lz5OS%pg1nhm&2Mo$`J>$XqiuikAc>%o*Kkby7sd>2o;Nu51UT4u|uKy9Cp zTnWDqOXk_b~OW3gA}d?L+0wuk!0Su(tMhWnbV^7;@jGZ^ zF`^>MZu^4T=fiUlp7HfA)*TIw9EO~HKYa6HByBq0HX%iTx50sp_8MMFE%=l)3pLAI z`u;S7@J+unRkMODkE6qie+eE&J{=~5{@jt{Km1L;C<5$Xr4MwLxUj5$1}JQo&%rw|_%rypGrXB0tzI})Ox9-t#|wU}nqwNE#U z;`zrMwM{bsBxHtzJTmem`c2h53JNdL`)dboXAjf7a*k*o!>w01^|DOU48kHL;spQFqIq9{34Zq5{f=uo>%`zl+TmpXD{Q|icbkVEHy#ve`{Ii*@5 zvJ=}=08wS?$V*v@H&SCo+{GxQ#&THjSiUJW`Z6+P-u*j$|M|=)E_cBj$n3xlxf3t> zXLh^&Gs|SfqMRz#+5L4HsPozE?y~-}%mP0q4JMe*Ex52hPvonjbeC@0ube zWoU|QcAEc1vvZZSL7?Fq+Nz=(ex&b3_+)l$X3lQc>}Y0Mm^nVVH7aUOm=8?`;jfwx zX_A5+(fRCne{C`zx#NJz5ZwKD!OQ={`FMP#%}34f`N;lIWoH9H=Qr7WTvgw~bhU)Z zOmxyutUeGlfX+?dKj9ITd=MzC|BrV{`CqW?NW?X}z!PyI7)lf zjtlV#s4axwsvm|kz2;(!hu#@1#o;nSYz3ESSso6D&(|N5U>tqs*S~B zkf-nzU6&tD;k9;p-4xz~+c+iQtBV;>sInXV5?-&2ReB;toC2zK?Yx68v?1Q>%B^)j z*WtMpbXXsLvogq%2@UzpCF%=yt9`0(;EsO3UBT4v?wu_a1WI1mtnITEgO|xptQSkyP zuq^A<>LGqO&%4t3a0-UdI?e;eWQ>OM#U!uMM#G*oR@=u}e;9?jh>Z4E$Qzt}289(r z^@T)Jd9*hc`vCTD*FsTyR>B;eW(p%dAQzf~tehV5iF2Chm{X*rG>w+sBnVGt8mdzZ zXCeZXCgMd|HOY>ECaK)mCImvPPAl1`ljId^xJ(H|9%B<}33f2@Cbl~^L#%o&?l4+` z9wur^TJzyyteEk}!MPRMME%uSEkxPsY^8ppskfL#lP7gSTLoP;2Cd~m8#v-nVu%nQ zjwmIZD$(Nq!`{1qM_FC_{|pl}YIFu0ZK~9cZERy}HBqpMVx55rOmKn`K_a52#tXGp zP>G--ph?uXucK(yWAFUW>F?CjdRp7lp4!&70pw<@0#-$mlD zH+pZjbYg38{u-I>vdgXG0v8_wV7>^`1vn!TGV?F%$cWzCstoW5iGSs^=iHrfXO6P^X7AJV0^M zwd;9k)Wb#|l6nYuU=gNkw?NAH!F+gHCE<{3vi^iP1Q2e140TMGhT>9DK}sPR&-fEE zKtX@;3@82`3Z2$!#bVVnV&dPfQKJb2Nn%1wk0x3( zM53`fbbjw7M-rWFLe<;EqoE<=c8CZcxwdaY?vTBq z;A1Uwa_uSvK6|H>&56Y4M93@vR+I9;bA~3oC$k6(U=Om#7~xbEi=*Zt-mZGx2#d_? zE@M2dEb$&p%#G%KS8tar(wZ%@5Vnf0@#EcZMdqKU%K660wYS>QNv{x#RTQoF4LWEdN)mKOML^&LZ;8%2V?KS@1M7IAK zMw&=~?q;#$Di$@f2IGzlt6%qIy$Or=^F@qzNiY!li%|rYnQ{G5&pilm>GToqR59%mM zmcNT2F?K+oyQ>IlrrF;O=|QtsDefN6RC{xOuGt&zJb1OIysBH6dTvDte>zg$-g;S< z^0*`+834mT6iOlJ?ewT%;k!mi$Pi4|5In^VJ$)%%<@3opvo>fU-fZLn6~9#|v86?B zw;ybY{7u(Pz>1AF>&jnbNMQ0PO{`yab)=Q6q}`Fd^e0Aw)YY2OMX3mW6tkc~WH*v~ zQjy)YB`ij%EeNJQ72!5xw)UE)$mPFe)0kC6SPRRDK5x;tvaGYU8--2lH8M=9=F@!$ zB%}XKJ9aSd#{W%D=gx^tIgvxGq7U?LpSypl3e=@%F`q{M^qI z+*Ia%NjB>Zh)9E*|3iBnp=+Y^K2yW)muP8vtm_|XuM1bEZv?w8ViS_NU~{~+Y4e;I zqfuEj8hJC5n%|n4$-xig%}h4qj;To7k>7ue(4w}Gtu66RX5uRP#kmcwxw9fzDA8l< z7oDMR!3%e(V_ie=;Tm~D9SgpD8=awdk1LszHY)hLE3FHsoSNlqKRtD7|HZ%*$yf4( zy)x5zekE-kt`l|2d$+}V4gP~F2KLAnR)C!MhK)V`Et+q7yek;^{;3c~E7TGCb1Y5>pcK!G36Mn`u2t*K7DlxVb7fAT0DQadaB4 zqNNth<3L)pq(c5B9A8^gSG=&whIkr7j1EK{sS{%WUk`t73`EJe>zDx_?@WPPJvNN$ z@A3acoHne5Zamf3>OHJ;!Z4i^AXgfGERJD7(0*f&C(Gj(<*AnH6IpL#%6ly9J%BdI zMS+Qr>98PHNnE{-_tXO&n&&-NFxADxZ z*z*jYnH77U%QLgWbN;+Fg^5|3i8kBB%+lro33L{0m>nLBRiMq$R`fcFV6hfEAo1qt zIqz_bYeIDT!EJb7ZCAJhuK3q8V=amj%V25?q0-Hw@pZwG*Q%sUk-YJh+UpuynpVy^ zNKCqhcYsMhj49Rh6isQKdhY!zqn?}Y%~Q``KJo7M;M&FSGvBh;Yt*!O@LHPUl8M#D z4kp&$1;fM{n!!_~=qQ}@-qx(WZnLKOtk34$Jy`a(8hS{jaD>=vj+V{YkPKJ*VJm95 zLws1)?}R7JB8#{2^qaz{2=Q!`L@z@mgzpzP z4lW!@j*#(3*9v5f1Bqv%V`oq*6GdnSj!Sx=Mr|o#Q!dax2TFg47?KZtbX+5qDtEnd zb7vxwB|CazrZw`kFm)Tr$QxQC8zqvVSRgoW1xGUsoP%%P;!qZRq(6Ku1gO>l}&CAXXQj!f;nX^P1bTk*91Irf5xTWLw6fUb6%sx0TswW$FY&>#QH6 zHm+0z^RAac#`M}lWLQ{-01WTViCFK0(w>0r!npx#7XcG~7GZnvhaZ$6w9|hS?GGOT zpXZn2GYk)us2HK|GI>e~LhExXIbwuJeFVYMzBvS)-l1lY%b;i3KyqoBwd@NOlzxfF ziBM1tN^bPMN`M7!i4fVaJ!1lMLcpMlDWJIFch*0RDCwBZm?Zq9L`&oWox(Dt$1R=v zv(%fhbgu4F0p~iz>;NRG@a&e@`Ktx!9|zh|7U7c8IL3zfO$qSZhw3cn6-rE=UL%OUw* z(lkU~4*LI9I6V?{hTv9cAKrP&Ri$_T5sQxPexb>VMDzu9OPvw`3EEx|W@hw!7#k~YI$ zE{ElyymZ4K4&_%NXwK^scEES77}4(%(k=2)Lb?wWs&gjEaFxY-9W#D*Pj+IiVn1!S zu?;!3|K{$hT?~c_U2z$ogmxU!(s@s-zl&^7w6}*CeH^38DQ{d7$|6K2s+u>`AJ#MUdwwE(PVx-Rwsg>PK1g8C5SsbWYqNjAAEdYpVaczJz|*E-uf!q3-fZM~;ka;8IPP#+%b^ID4eJMRS+tweNMe9K6Xg7* zV3rvG;Pcl9wZWAx1`q6gk~|s-)?OjJrHev7<-^Bt6}}Z{g*og82FTXPldX{e97MJT43Ho;gfCWk%w9%TQqTfDoogDu*qTXLrQhT=zRINRcZA(&0epG%PNDsr8CSB;z?I zOB#4>=o|1j&M;C$gziEkOXxG-b}bR5)c@mHk;X7{bU{r4e*^@;Gn5O|TnEKkH7L5W z1glKY8_Tm6W{1vjUa{5#aXZ)WaZ0CS_P!{5r)qn@HGEHqYvzwOEpnjk3~JsnnW}|v ziS)yZV<|$+gN|NDdMlbEkBiTM1%EswxpF8AC zP8d(I%{G_D^Q;*}{>^3C)Gjx2g8XdqQY7 z&Dl)1JmfndD625r3Uu(yMA$Rb5}bC=Gk9(cpXc(N1k7dO1}pDVxhc#7 zWvvKcHLx9@CABRZ{Up>JyX4QNCbe|m4VGIAY-1StSmP^Bvdtl4zs$u74CkBKl!!Cf z7fg8At%9@|kfdWk&P40qi@MfGzEHMwEeOYp*TVQ43vqfx@Be@O&tUzJ&L1W}#izj) zFWhN3#z9^*R?i@sNE)geXlj0OIC_{Qu}-MX@^U(#3=fbsV^zi{^#ERs&*%Yk8{eh} zP-wg@h=$i8_6iBndos^IOI5LNNmT zUcqm*c6 z64G^>P*mZvvpX0{fqCZHq4zU%( z;S0(_H49C}L~Z8d*<8iGcu1KE>o8IY56W|Hks}zGQFI2}$Jhf znENo!85xyhmzQ~SUSQCNhF)MIJUK5gXru801MIGX9dH7J@-puPW>wd>>Bj5K$vo`k zoDUce%lMhEyQ{~89KoOm>HzNX$`9oUW(4i;)DsM*pC8}}2BW+W;|WFoLA=ZVpsD+FU7 z;l0oQe(gd~3iP`rjrl(08Y@$LI5$UYzwKAu!u7^~((TAYvmJK;jttElNia zd1yf-c&K7pDyh24YzJkQUjr83plp^X%QF?Nx&gQme)S^t3{7B%>{>_Ynm~t?(APjlID$RNtk@l9D&4bk7^fDD$s~ejgczmQ|-Hei$FFLly z1|xGTerV`dcjN)}N6NQksPE5= z0gFn?>q4k?GO{)mSt4R!oq^tn^bo4U+e`~*vZKun-bBSi3ZBj|E1HfB7ATP;g9`r8 zL&9twM<^3K&Z%3|8-@UDv zZ*Lv$ZT)51&h2so*MyG#e(hpKt=DC8$&(d_MthKdB);zare?e3 z19d*!O+6f0v)=$o*6_-;r#2EzMrAWzjE~U$Q_)_fu&Tb|A0_h6U>KhHM+;GStd@1o zvm(hU=Vj}$3f0CA6VhQ8ve8?9&B2y77eM%Mw=0tF9Bw&BTQiC2uA`iGN0j_};JZiI zLy}Ll2Zp5D9@-fwd*JIU>;V>gv^{WO9Lb>_sx4YxpxYJl+%>}dch&9+ZIa)u*+uTV zjBvk7#)lx>yNGbVCSt2vMwSq6tWqG{>|qeF_jBF_d4UjrSegx4-Z{+{MgEZ9yUN~6 zcOJatNbKX2Y$MmH2$A#3U$Lt6w%~&ZyCG2L%%&}|o}Y4&m33OhZMoWaFgB0OTCz0$Twybm}U^o=}L%8 zb97`61h|SBflsOKra%?;8ViAnYs;u66W$tZk&vVY3lvREE`?z@zYxTZ*tI&U%a?9VNkF6$|}5jT`9+1m`6{pt%GvL;h9nqT>XkI;s{qw zu3qXX%+HzJ33ELGwg#IhI+Kd@8NqI1(apOojqJ6P^Etj{7`9#<_l)cb>uARWUHveM zf{!r$))#fg$I}@7v9_&0k+-c!>Y`mb3)|-2n(JD{l{!7v2-Vhw4ItGaThF}Co z5lR}7-VnUB)ZUCHZwQi?*qhPh4X6(8q;mtunEuqo=C4f7D~I$y?p#ee?wDz96n51M zQR;+Z35^HXhmnd#EhD5t)Gf7Tc)zv(r%-X&=`*dmah#bnE(Y#Yr(CIH6h8_Kq zXQC+_%xQEm(7;Lrq95+Jv?ZUVW}i_PJbsNDE49zCdCdEv+Cjc)C;!e#NCpP$OC^-R z0AY+7AXF`6>babO0Rj%aMc9yD-hzW3v>tgiQ#_M%!Yc%xOesjmW(-x={)DY59>-Up z`p(i@aRc4Uv1wd^?7_BZ*0t+9hUCSe!L(6V zGJMrRPezKEAv)l^==?&ux>zPr;REV z9aFEv_8O}}!V(6TWeo>!D*KlZht~yPz7yd{v`c0c86q7b2=A64ykJ&wmnpT7j*Kof zs}L3pkd|e^LCE3<)_-1JXjiM>Aja!okP1ksqJibKUqvj=2{X);!fY&dDn?|_F^h_k zU{l4c)?9W878OroQE@+<)-^9Vthye54QO$;n)gHrQNgM{zFO{@@S~%z&I`^NgdpoZ z)9k&5HDQY`<`8IkA+X@v0`To_MFwVI2sbEG!j)i`O&wbo__ILRX87-P=%5bns@!u2 z&&-n3t@8}3*)u4$093PQ&}acuXV0L~0_Kfp7qE&~u+3h@rt%8H*{k?YUOU3q#qJd} zvfPFCdMxT{6E@!zO7@X=OZI$IDAGqr?H^AOOIlA6v#QTy9!4B4XQRsj`3%sZ&Qw0j zUIUlQ7uf4I_sT~s74qE!FMDWS{Aq&xDDoT~1cilL3|Ldad?Yh|1>!+$dt^6K7tGZ~ z5+P=D^epT~>bci-XdCMtyAeal4p1^HYqJd;|0q~&Idc0?RaPf3Z|oq;ky5#yEJuWb zMOd4+99e(8(e+>cEKk?xaH0w(ai%G@4-Bte%tfAkCLd6+yP82liB{)IIC}1cn{mk7 zQZA7cjNQ=@@uvw;WqF900JMZ}A?+o@7L?D&aC$ zrt`eoku5qH#CATqr)so>A)#y`Fxns;a33Cj4 z=2rK4DmgfZemDG6nCo}%O}Hjm zKK5z^z(0}IYOlgFs$zM*qOwNiEM`%~-rAJtCTp+is22Zn*uqC!qB6W<VnvylV)IAfw8L4!CNI&2!2L#aE7s`3pHc5kK7d)!NyzsV|hzW0~8@@lry27myP^i&;K>C;kP@*U1Srl$iM!!A~qa*B5G6F zM%Pq4Hk@13q>274rZ zl$nlX)@hf^md1hW2qTn4Fx8xy^A)axH+2!?1X>rZo}4&e+?(7AsIOBOoFr10Px zd!7c7+n8kw6A2V?^2b2u{^1Cl2$Vn_0h|Vv+bBXMa1p@}?(J-Dgd@Z{ zKx=NXT4#yUsbZQ}&oZwpNB2bUM52t$sZ@dP{g045Xc^?PV@Hy=uW_zM^{S6Df)!L6 zvNA;wL?JN@sF}%#!f^*?wrEmU90lL*!7&{&v)fm+EN@oF#Bw}^N}&%FN$hZxOcc$| zsWc^$1`SiS5KNG#HQL-344nS#iE_0@cM*AOGe{{Hw8y~4v_yB+rNxai1Y757pt zbODhRY}qL`a@dLvL5BMS3gp zEaNsJxPx~jiqyC;Xhq0`h*X)w0mqYw9PtXLVJymG_gWLaija|;XMRMec!iHQ;5*$b zr;q2=LV;oHv-DY@7`@0FOv;df7Y&z=$XREz)8<}bXjt9zq+URlBvV?3s7>&d4xo-D zzf{DWN;!(5>{MpLWn$>{tX@UNsGHtCq1QV1S`+5hxK~&;%dK~>s!za;?v=r_*9@<+ ze1vWghLTe}Anl~#3t~B6XK2L2yRRjBVO{VlhGMK0_6<#=ikSF|xw^-`%V^St*hy9| zwU31C1;^rUPmTiy%D|8v&+J;!jBjNoK*A>_Teyq}TemVS%kh7yYS`20-zSYa0FE#O z=86YnyV_ZwdDaR9q30xoL`qwZa8qmiEs`f`?Hf}S$oh$eyOICv#aP`+L7vy}f0ekZ z+cYe&=f-4x-%?STqat7ff!-d?lOxd$!PPl9C>sY0!O??zsIN78j=FD_ zv7&0!#o+$f7(c2}QBgCJnLT?I72}oJbK9PKO-RMeWNGx+_-raPYBT7tMHxX%<4D#` zMzS=r+8XNfgi0RZZ2ngZ1jSBox((Lk^m+l!>|0Q(~}Jls^SbxbB4qIQ)!N#LOz>% z{X*L?=agoX#pSFjWKR!fG`egA+gv>0IZW0Hrc@$)ZF8@X3M-U!uWVlIPy>b9T{6gH zP&3?XGJKuoUNbFNlqe~(MeWa3s&nw-))m>6a#e*tnL5!9>zD>CvPNV?i$w!_r^{%5wYEJ^uvAJ~wz$3Dq;#nzwn*O9oG<%zA5Fe_frfRKAiSt_c>1tO#69 z(7+A)*f@FqB~`xj>`=*D-jM=$mgHbQoqH(YP&=L;aJk@Z(=Q8Gi!I}&;=!M%%QTDG zN4@na0MBb$AG`K@)}j*(PsmN)Ga~ej(<_i*qwS7j8nkPL@9dAEGp*bR1Sk~`8f^5( z&gO_}Av-wOd#=U1`|L#-{}AkncQHpe9I`~I;}fIVI#xiMyCb|}L|8pW)CS$yvC*Ub z`n)TKpdRO7+}OxfnWV))w4xIYFZz|z{Ax9EfveSfr-h46-g3$3ZLg@PJuWwLfgCPi z*M{?i(Y5lyfHww$E`G5-dUPf_5)VZ2VaqNrb;FQxZWxm0hT%fQQ~8~T?hF3gz{{Xb zHR6Xsme=|oB+basCg8-OIr3)oXzq?hf{`aVRt$%Gjj`*`<|dYn$P4nhfa8T_6&W8x zYuz&IX0+?|x5l0;36aYX#(I|`g;N>sL;1JYF63Jwp3@C4vXMadjXP{&S)WNvkMoF* z1>X=5=6UZ?`POhw8j>qhm6R_yY546lb?$O$(%K2?xY>DXnBmQGo*E<$F^8j^+h$jD zh@j!~gb9r;-qQXv!o#jeXepT%=`AMH3(uAR)uMP@7L1E6E{*WlpNZ|tIW=jlqSF4% z>P+Y9aj14;Y;N>dgO%9AYDO8E;14{5-U$Nden|nBs>bCG`MjZJDmQPRAp}PI4iMb> zRa#weEiWyde{1Q)t-nNaQYi|@RCgxws-m1k@v;EX#CIfKlXJiR;dVx*C9)NDAXh1x zb91=1c`x;$pbDNiQ&nTbYHw?JvqO+Hhjj0OOzaoygWnsB^jY9=72Qk(KQTbD_1$+v z1_Lm>MT9G|K7TPf#RciUWa>1WUKEJ~3iiKTPaKE`65c$9d`)INj>gCs7YT}6lsJo_ zX|Bq4ev@l%w=5Edw4zWxtD2Fh@tf;(Q^uQ`;H|bLH?F?L8z1Kvj(j{`0k!(@4czL@ zft;7er{I06ao&(L#-rHx)_1qQyLBhV{9GHU<5RJgJqLCZaLn z45_3YC2@LEbSH|bg{4(YQqelccAUdbPI&s(f zl1%5cnrs9M?MH))Eo;1zN08l!F?UTmvY9L2D2kdRToQX!4Il5oIGoQ(>eBZ>V24pI zCrM7GG-rR8!+txbO^V2YG7*>4rrH|Ty#jrkIR`(Yg?fWo;Fs*n7^XQKTQ~P<9qNAl zR5G}hKYu?rh6`piY%QMDTaiDsc!$aBQtnDUj-Mr)Ae^Esv%C0>MMNU66G+cBoxl&i zo!1F;{#ZO79Jj^yg2wKW@!o=j`*SN5m~>pF_a=S-elU+E*?o5n>eC)j%lRt+wnZK< zC)Po?f6w;DE^!~_`nO{!RkGZYI59q*|9?_ zcD*!)qqmjpM>x{4@5=bYZkWyP?G1JB{9UMAZwZLM=fz}M!+o1Dh|)N8?Vnj$u~ZP5 z+$;uY$?SVG-WJup^0I%t5*uE%bknnWdw|qr8!)Udgru(y3{~3is7^&*NJl#e-9j9+ zU_RnsUCT;)PkEcQ;{N?2{QS0m%?&51r+7%S7|Yc3{#QtK^5KlXSK4oh`zKWUDaDnJ zUH5bK%b%D`NA8I%O=10(6;s%GPfKp(D%=FFVySUq?f7^q_U)xiZtUAjQkD0_dWyz- zwZ{7wT^~H&yT~G#@7H**?tYT-ev=(}{!u01jihQ6 z=1MHJdnqVG{;TGx2G3T(zg;+Z6|nwG>5k&q!})6=tm=BYXd%>>E(A_PD=w|-+C9Gz zbpF}rQ+#5XbpQ*B?rL*iL!VZ}Ycuy8vZ?0mMMif%U>)o*{(1Wk@zl9I?9g-ZUqqlYD$- zrfL1`b6Ao?E*(01v#ATjMdCV~ZJCfAa7=0MWmEz12yoMH1Vg$wzp0IBNMmQD3<+8z z&t)V3SXptKwq?TdVXu5h?d((4JSR9hL#yA1en^>Br8cOW>e%wt7PZoZTB2386nr01 z^=YT85m{1JHTSOTx>41-c2s%SzlcF7VmXS2FQl)f^n|ObZ8d(f+IzDNK7-4d4+Tfc zYD{c~!*{nqnt{TO8)S{BK4>2wmpkvQA)@<1a24K~9ol2dExP}kZ-Vw6h%dE`re)!+ zPYB*~DFge%FfETDgwu9gvPa-K=&$4zeNfSQsqr&$zpdIxl-7$@N#q_a8SW);gM5}Y z4BIiJW)*itE!}#dU0fI3hg8v2*#vrlrQu(ZA}e*i?IH75-&u$j^z*yNV-| z>-O4Weva>T{IHyq+bT4ExUPRQa?~Q^$@4P<@I}A`7wiA?2f)`A!NUPo_Fp%+Kq>z` z7(Nx*)_+QA5tAS6gumjPmk4Vmi{N1w!zt>T+9pw&@|f46WK>R~gJ#O$9kiSID6Kgzcn&03VsJTK)C1>q9(; zh=|)yehkpZT^~l+&?OD9vgg+MW2&QL;?XfRHpOi1_&+q3_Gjmp8}3n4HQdkzJHPq+ zru_+w_n0`<_!HcCi@DB^_bNzI>LYC9*HPBCaqZ{fHvZJPrGxJH_z$^{V^C7e(Aqv; zjYF%Dp&jr)z<%B`qwM&X?&q}teeCgfcRLlb8slApOkdAr23MZ_?)Kik*@TirA-I_n z0@VM2W)`qJ-{eMp11OAlgOeTLKE;4td3#Z6YYyQW46f_TB#jJ{4sb=kSX}h?9QFpj zzc?qckg3m&`tsGT-6xR>^!8bOt>?R>`z&d8Nqv`&-dTMetybS1oJ#Z^H@LzGc?LfC zMevFyuV!=wa()?*XkLE_saopFfJ{EmKn@RU8CC}53itVYgta_{ z(2w~k>F#Nqi0SUD@b_xy&t*XN;EVytz2`WP@0S6&^+E&rRS0rP8IXM#RBE{~1Uab; z$l+%g$b})u5oJIYa7CM1riLK#G9c<7AYTbVUgQG@fAl3>nE>RIA;>*tK#n}eK;Agp zee_Stfc%H+%flf^PZ^Ln7NazDX9)7OG9Z6MI0(oOLy)hQ0XbLPRc_RR5M=){AYWiv z0CH9cvW<}({L#O5ST{Zdd9VyfWdfESEb_>RX!YS(_y(>ef0zNRrOyvP@lhRK_2AfUsH?ZUoU^4{A+0Ob~#x7 zHTqlLXz@$M@-Iuv%fG6xEa8{(@~_cXdZXc&Ryuy^{!)G^9G}L4?}_*S>axQ4$lbEW zFIk*_VIV9B0Ym~^fpJHhjR?RMuFZJ=TGW>H!^#&_dEibJr6xo zTg&>j&r9`*TK?=s>|b{9JUcdZNo;s>3zCtpO>l=t=*>koY2fIz2|df<+PEI8{#5k-E;R z21iZ`{y3p9^^X56sXrVq&i?+~>i#RJHRP8|+Yj@W6+3wl<~Ts!XIwDOrAlIc1}(juz!^qXAyt%$pU-CsiKw>MTQFm41dL< z49Z@P6y0xapB>e?f~(gOrUiNVV>o_g&JzpVc>=NN!ETBCFFH?z^Vv3l|8@O0LS^#u z%F=xBtuF1|1HSctQ$PdU#31;P9znbOxpn<1U`z2Q%vXKDIQ;G3PgR!qhcb8QTAu}c z1bOS+c`dT8zaR2dIPaC_1d^7|{_TRvUX7lCECp5s3{Y%j0gzu1(Bm1v_(+S_J zqY4>wWd8wQEkTEHyuA4}(fN4pII{n}W6I^9=#NYA>E8Sik!{g&wTdb49~qp1#$8^c z{3A~3f9NLbVtD>I(LV|Qov-?U&QC<%iY(=#(MeWjB7>eU^Uo?{{R``-lsCT?=(h^+ z2Nc0ySb+EN9!F=tMuL?OZ%vASH}?IJ(_r71O*5tezez9Ux%ZAFGm#thN3z8o{yz>v ze0v_^hiK9Y{Qa#V+$9Frbr40b36sutML%0q^i_&7o<%7QN76qEofH;o7^r7lm~?=v zNBnxe3zusHp!o2AYifo_-Eq#pL0PXwo&D6MV(;kINQ+6oKn$?5hbKSd<&zGN0dt@TatfN|}@s0HnO+Q7Hv|&G(N6R0RepbFbSLdMD*x~J zz1T^9Qu%*jFbCypd}b@}7paoG{P-xpL;3f^w-=Qy&c_QVwZNxl3CQlXcOUT$Q&(hJwzo(Pt6-74)}M!*8Q-u6V0p6GIMBKkz| z@ObQzzA-D4J5To1Pplk!p1LXfhHth3#>$60kXapkns-=sx|{YcnrpB#43>J^W5dU0 zg749iTIo>52OQ>J;(BRdI?~=Z=BYBOUV4bCw#T1@{jL$%cgMLhY-vvf?}FiJ?_Xs5 zBm1}+1%s?uZ;GLV>dY@QirHndHeGra!D> z8u2av~P-5Hx&3|g(~>7KnwFvwoQ@EHl$(Lr}8O`z0krR_QuH}OvdJ)H!3V( zQCU2og|c|i|EhUFupSE?&9|(>*-NaI_^>Nn{qKXbQ}g{P^!coON} zINJ~Uk=0q? zZ=Rug@j>5lfF<(FQAP60l_A^(250idJHw>YT+tGF;}=KKM&Wz_XH$@#E^_k5p0MzN z1NF@fllCf8-?W~c)z_hYKQ}5J7G4W27%V3Gv^3qhQC|!}eqIJ-zpR18LlD0V$ZwA` zkgZePfXyfa@-(L+8u~{F(p(1QYdR+6M*SfK`D_`GCzyReeiDLgKYqtA+P}^~ZVW*l zD+3aA8_1#%(K7OL1 z^yj~sN9h+XXLbkB#qsk4`gPn{n~MENY*%5gjtvfhBjk{UdxPML!@|cE!Np(U(LegQ z-0fePgL^8XuLTcGwxq0#`Jke~vZHv9o`+*(jPkr?!2@DeBb%Vu7(`c$En_HJkT*Ez zEL*kAih_8yQ}p zk*XVLdd5t&cSiV(X=(4`@HxTrw(z+wkbQo#GXs|boqKecgBjGaG&mRpQVeKm;p$r5G3|l+f@S{P} zGQzaZal`>yfsHRG+eghmhRR@piMfL;CAsuMd^t8>40ZL5joBuQ8wS{Nv&A*c0E4!k zU}zBo+)=s*k<}H(-yc8H+a-+8X8ij2`jU5iWG0~J$vUVta0A#Bo^SK{^&gPm_yPIJ zo#r3g|F^5u;k>@{*#2L$*z@l@(C7E~+~Bj&ox$|^fC)wPc>_?Q&u>C~04$-;H?x59 zdZ@cXxU~jn^!Y!-q(8Z$CG`1u+8r38V*1>7Axk1RswXUb#Xx=Y!=&@e)OQqT*z)zw znq~D}-{!uf!4)o1*zEaP1Nl-||6XN4CVaVsg1mjap~}@a^o_Ve4a8$^PV(6sDYa^S8aC`jyzp2(5=l z_&-7qBR)YrRV=Bxr856}C3YHSnZwh|DPD)c;As`)4^Qs{aa)QN5}w>ck_HzHI$od!@AEVH(7NjH_(>1st4SObSW61#c^w=vYOi?1h#rq!`LaM&$i`_Bh# zksw@kRMlRT+eR+yS!036rR#5k^g?gqvbkCW5yX;P7U?zEJ&)jjRirOBXM3(1%kEWn zSH2V1&b9aI*n{3UyuZpRh1i1cy2Q~(5cT6rSUrv6Luc34Psmlh3-l1YZ+)3%2#L&s z8;!gkMB2#DWt&(lBljTA4J;+*ZmmsQWc=2=+XLMkk|L?tKP_!O(7f;0r(!=`-qQ3} ze#qX`eBcAwruS4Ndo)Crhn3B{y(Q12jbybpy-dmqY8OQFZVxpd_-eN4$yDqIygb<4 zvnkf|HE_(;fTxE>_{xg z{RutFd*8$yN?=@EH1Bpd28w&&H$L0+5@WigMT5_X?RF0n6^}AeOPUY7r}@CATAJP< zV~Lx{70tW7OmenqgQlaU>0V_u?{;7Ffv+_8JQeG?mTj3HXp&l*?zc=?{C1IShH-q| zCjLK#IUPU4e$%oiLsI=B|DR@P{$7}mG2r`N{HchIjSWAflBe3;2p>rty|rEc&`=d7 zfg|2AUmdR*z@6Z&nuV1tSpCa*qqR_MMkYU~Luh-^A=L5vjBrA8nyZ+rCDYEOA`BEo zqc*!6${~${UNsug8X#7g;48-#6D!D6qh0s~C-6bjUqxL7`9A%?&z|IXx(?E?ns=_M z>Q+Mn#TK7VN_^PG<5)B&rB2X)+}oean9U=e3*J_^>J!u)1lQ2{P;+iPtR8pwo{DnA zWPFtS^?1jq=JV?9Mp4x#SbY1SP32-ylv-)mdQ=BLcSK9%HEwcm`U>qMaW|qSf;GGX z-_Vl`V?p&w@Q~r^<9B$)L)oUKv7X!b7#xyNkq{Y#onG-U54^-hQKwh@P07^Ah@UPH z>N>1u&aI>%2Rv;XzbiN0GO!=!F%8eo)Rf^E6 zEZ15 zNgV`Zc*P!SM}u6YCkUm+T%md&>3__SM)Lx`(8zEI8Gj*O!ND`%)yP$}Fcz>Tt zgy#eHcd$uYyz|PQ@$Qu^?hI*B{Z_bG$Oo5b8rki!%|)50J0{xVvuWHEw^y zH`llu8Quo0JQKmcHN3n1KGXb0(VT+o`4hF9l)Q7}OVA;rt1kM(>efjqQKAzZ}E z)`b;+*vc_`4)Hb+J6l1{bWgsoVO?+|f^5FMj&Y`QtFeDPZOG=5i6E+jnkfdl zbS>yyzGT<}>Jb1|n+dC?#akWh$I(TcP(qb)rPC}bIN@Qp##N9+@Y74(7*mPenxf+x z=#0Uytf8k{+`tD))i~3dErq%-xRw;`i-(8#!E1`XrR^cAZ*zk%( zOZVNC?bFgn&#q0!diSzBs1K$IYRfgT)<*kg-3H^(8oYc~h=$zR6UCfed#3VNgOG8V ze_!wg>R#7A^tdjzF9Xw0x=&A>0bsP z46K_0`j_@EAEd=I#q`%&fER*2!5eP25cku6Ah!0Ky zfAi$7t~|M0e@q#2_b<}-@0{FKOSe3T+%2M>9mw66IL>NpYj#5J{=g*;(GOc&5D^ID z`;xoAGXq8aDkgUeVcUwyo%&Or+}%>BWJhxMd~2b@@Q)&Q>_`7qa!0__##nC*awp;f z&L-`9nds51i_E}M>VLa~L>@#e;-x>ZN zRSy5_OYn?YD%tLnV_brXGV65=f)?Gd`zY72F09Nk07XnfhvWX( zG+Qh6owREV!ktL*!X^ISy<#3mv-LqMX7#D8m^?r3)(bsz%<}6pyO5UJr0SB2Rbscr z4Xn9b7noJ$w_~VS!!1pSE!48AVcnSA=(&-{gD&diGvg*+Htv8bgVRYxq>ePKRrD$y zdDn!@;ws<@Qm#zGSH@;7j z%gE=)m(O5B*8McGnVdJoWH#&0c=2UChYxag@tzJ(^w*F9dxc%I{(*A$!p8LVO3p}< zl!60ooS!&i%<$W}qh}EfGXBZ(dV_}&me%fBe=eMOl5N+V6eG-R@kgNb!04a_zcop? zJ-xK2c)5lbo=5Ashi7C@7bMx^S_F}a9gL75HvB&}8biN%E9c<*VplzY5EhVOV~><3 zcom1qCH^Aaa%WTT-omfY+%F*rh;-@;2< zJhtzI$`&0uuo1ws*B-Ee&2j!*j!E7a67cNUiq*n|48Lvsw(;A}FRU$J2;kST+dDVG zHE>*BXfteK5aegd!vQ*sJHL31;HZ+XxXUW$VAU5Je)#)UE)TMoQ2C`+xm6t2X$?_t z#;yeMi9=E=dunge1OGB!TV1Z=D~dbME*kfiGNjqy)1QOyH!h1~1_HfXW7mBhfgl@dW5&Oc?-6M<}h$a&dsa!nbhFKya0V&JqE~e<1O=ojeQFAtOE^j)e6NSZ&>;l z{`~!J{{J<$V#zgxC0Ua}zVSCISgJ}dT3^}yrthK60h9k1ozW z%4Kg?_Grs~4D_aES9*UgAX?+JeLt6eqa-6i=s6Z)H|c$>gFTNvf|1Zs-TSue3|@(S zkuRz1_mlIO@C3^vU}5}hJzpa1SPX~|gfxFKzi{ZizmwJyG)`K?!nc^7;PZPLnn(i2 zHS!USTA(T_@Co#g@Uw~x5)bx-Q4~F|CnZ??C8;@aA1Hz|KBHhNx}mRy*+*OR8!Wv} zy=^BkS#R$M>4qm8?XgjhNqbD{F@rQ_e-RQ6EO^@8%olY~_&iRoaPLqDt_#w58|mkf zK3XW=zl^2^wvZ4fF$(WaRO21s!hKDbUMz_ zBR*DNoQ_L8H&$Pqjw2L^kJT5a;}Xh_#=&vc$`WJfuyK|PNukA*Kd~J}f+aJzluyR~ zX~k}@SVEoEQj|bD@r8@&U{P9J&&i%wVm)Voh)w;m>x8cwC0am2)#_~|j-*gUS`__b z>3;GP8Z#`jG=RIKNN*1DK`vE#z5~~8y%WC&Gt$V;BMyLTEq7{d!$Tv6`2=QDr=8w^ z9>CH|Io^<1<&w(bg%prB+Mkv3`eFZ++g#>e!wOx3W3BW+9TKjcj%_NWe{ zGWW%<|BZkn02|ebU3<6QU*?Z(TI?TukVtOEL&(%x$#%oGh68mEg2+_x%jQ>p2O~W} zJ_AjpQf~swQvy?Y?R0_Pc~0V1kNV7y*S8naD{OBA-M7;WVIPmV{JPc<-2a2-929UO zxmh*m4hdFbjsd;G8ypK53grvL&$Mgv=EIE=vAbssIq|RZBZ+~HW~5F|O-uGX6ziE{ zB=4|fee9|UJi=T;T}Dd<*I~}*9`~G|lM+lu^eE?H(`yrWxeUI>d9E;fX)KHEiyT?d zZZupUWGHMhG5?S1W&X3yh3^-07`%-Yx4wnX$O*uG!|>idhUN5B`_TxT)V0Lrd+BT2 zTRNo|ORr&Av9wG_=`>%Otmo^s5}W=K`~FfsaJKZLy^Es~4qO~o3wtDzA!wWMahva+zfr7Pow{ViR|=P+U5LjU2AriOJ5tNX7dP!;E%K|Q;9 z7)MBY@`GA!vEW!UQq7OPpIXzI{Nn0wg@9o2nBX&q^#6dPB%dZ!vl+pjK&Ul~QD^OU zHAdD&wzn}A8tZXLx88p?Y8`~l1{*=1D1>xdbqoapn*4n}d;r}-d8Q7h;YozE=$ty7 zk>azk%0C_T%}gYi=hScxofUirvMzfh%)6?2B<)M-$a9&9F=-1bgUc~% z$xjQjbUv?W5@c`JX^%K5T^_XaXwGL_ay`R7BeI6C##{wTYuy>qoeh)#IBSH$+x^*s zJx^ZQ_xg#;URc8m?C)}_4Vq^}{pIr6w|@u?hIX`K~vRcy*%T!ORVg=7;53O49k=Ny*} zg#CJ>#2OhgI4GhgBvV`;RPjuO;kYS`)k9=7|9HYHOhCqc8bdt|?$^Rn&uMS2l{`*H8SGT8-WF+ADOKOF?%W|Ko;xHpDcEm+qjHJnUfmfutDW4teLiugUclz3?$K7$v%==A_Fkgm?Z38ATGj~9 z8X!xQghl#d6!CL2z6)bS@g>37Q>_~vs?S7E((n?lH|^gQj=lWdL*Hszyhk9~!LLw! z=$$S85sYHBPuAk>Ns4^a>tbt==kj;0wqw_MwKFhWJ5Lg+p4x?UgfcTS#JSqW^MDi) zGLcjdhpqXXq_y~el21*#Wlbd1FNm`2?2;J*3;h)OpckjTWC;5yCXmLlGW81kpwQ>n z&Za$NFmkm>@J#{j}4#phwBWmjH6YRtDr(|G|RRc5F;;qL* zfaI0zCr7dPm<)7lDA;NKTnc6Rz*WHFzck;lYw_`;G-$)d0i%R9j9(*;K&OVYC@Zg} zVviU~7U16-R)d7DTT_|JwyEA`hc+j*$pN|K8=6ERaBwAC1XXYPEMMP!cZaJUrbjke zmqV0OT6Mis4~g|&0|Ty@!CHNyP>rqJr-9nsZYc_~Ep%y7ba~nSgcc1l$AkEAYH!9Z z0;UztEqkcv#KD;NgnBRDIyKn_LpbhjPE9uBBCUn?;LFJAjSp5#EX}IPkXya}tk=(! zR}2-O&b?ck>SS;q+qQ75o%&3s--@@j%@W)P7r{wU0vVkloU4N40_Q4agUN9IJ7bxv|Se)sDc z?aL3`bI7!Glfx|=@>uW7a707E=WE_cZBes(Wbyv((bd*i;YVo6HoVTjZ$Us?APXAthN>i#^4U_-mEmywE! zZI{iLY`>;gtY8YU_!tvuA?AEb(<5eavnMq%F^1OoyfakzHzZ;wpVt2!_<#H*Qot?4 zx_g+mDa?4oy7!;+laG0K``Fy%FAvex1`+c3r%yV7t@x(e*EA#|zlIZ3E3?GuwFzh; z`#?}n!_!Xg|4*F&^5;>fkHB|oU4eP9J0BcDP)IC*s?Nc*U}g=bTBe|yQ5~HTkIq0B zLGn3?$b-4jZPGUd|3a@ax#N)A$bGo%lUxG3G3Bfep_jK|wG>+5BM!+3o`mZe#(hT@N+tvAhs2uQ@QQ3cn?xlt2KW;hn zSsewWW1!Ric#fOLSIP1D%xeF15*Cb5ByOPqlzC>YB6-M*ew}NzgZ3r=uA;ijtU61_ z^+wQaSrjGf+~TaI!57K$o+o|!Kzgu`5+5b;grdYDs>FNCDwS$^)K|$2o=3Qr_FfrC zJwmB}v()DYQtOoZC*6xO^88do-zn7aX;M!$ARV&|^S>)$UBO-{uOGDYxVugGDqDpq zjBR}8FaSgoI9*QfEB*1T%5!S0LfJ}IkXXgcj8nV*6k`n8h*^=_gQ4zj+s~*i>}Bv7 z2PQKk?blkk!QT1lS}yR!Eb9O^G9(osAVH3 zHljqX`%DY#y{ zd~VckMS#-*lqnwcYD&97hl8;-6?q~!jRN7vHWThCH|k;NaZqPvbgswLpkdwkT>8tL zYFd-r05Wn2j%_-Lwf(GY(P#c>dL$j zB(87W9{jt57+VA}4G3pcz#a!w}Gsoa7HWTeHzFZ}}umws?P%Qx#25hp_U)aAi~A*xwqhJ@e( zdwZ5?)mtZTCX~|5u>T;8idZOmlS#oMWOB)5=#LnA$HPK7;%}SO(wq|41RX2{*bfN} zU#GT|-(1ns2@&+Ws+#??Wcg4*8t36P&5ZgHuNJj(U~*d&kj?lHq-n;r=aLQY#tXJ5v z&Kc^Wy0B`*TvYvhu-s*_+o;d}Mce?92uvYhIAA^BoU4U`K23|k_Z(zBKv2_AAZB%3 z;c^g4T$C)$ImhKB$;p5mVclxC)#YSVn~T};hfgSGN50j}+OF#Em3oID{|MFYkf9!))orVHBpidXacV#TbvC zTA2^%g(2uMXB2vg>&2m7TF1#Ab0DIe_6*{Hgv$7zNc+=R<418#5rg2NKiEltP-9b3|pD`9lU zoeZh6XHXYaf|tkygV!i!dLI)xQ7v^k_s5h8&L;Qd)2upFx!1j7LvmbOt_u=yLM(gm z^O^3quvz~JinQ+cuvz~h58g`+>z2Gks1Vt#4^hxQPM2J!8IB?di}hE!Uhtmpe(Ig% z*5%RJhk6gC{CU3_B7^k{BTcW*|FjI&mk!M*Ha$3h4^&7gfA-BoypcE1H)er}rJtem zei1qxnvWV1{C;1p3I~8$8fSYOn&6);{UWR|Lm%gBh#g)6S+H{d?JfbZ zFl9GOsi-BQOzgUUwz;mT)qpyoi5tzij!rV5HQ0)uAEaMVHOJN4@$LiTmGsP!Wic`3NSx^i5G2ozet zK-61ab7bpyVq66$xO~J_+rs=t_YI9<#`s*t7s8yR%Q?X1)Hi!u!VG7e6$~q6FeKJi zn=A1`wT%Y^=oK;_ROSjGb@7?18NCUVMH$z%Wp1^bHyB zVK*RebiL63{Q-X{#r%D%_g8#QI?r~4sSImT0_qu#fE4JA?h4M*YT~y>>t3WrFYilY zDg~J*F-C9lB*y4Xp2Qfvk&ayz8okMrn2ur+WAw(682tP}a-cCHHAw99bcS=>xzHJc z&`+&QO%>zzYpl0LVfd1+M8i6FFeD@Etqn3bb9O1_OWs5N=wGJ1(Z3dvdE#=+$3$epw5CTd z-yIs`SKW~Es&3N3L0#nAy$!CL8oSEiI67*CP!Qe*xUD1d*-L^$^O@cwNOShwz8A7$ zZ>b9wL!2z2h$_c$!22EiO<_U*|S)rrK-~g9p zBZ+R2T-Q=CEX-i|h?-~BwS`O7K@2R|{zs5R4}6cqGIzF3og^9SLY9=VPuqh(y(Tir zywDVc)hQIhCU2w~<>3JHG;$?~jJqtPi@|Ji+oz?v-^2UVQ&u4ckac?=v!{fQPq`T+ zHtC`r=WlJ-CWsRgnA*C6!qbq}WqdY>(wVHFA~%yS#;zYP7)Q4TzSG&~8Z;bz zQxRE$PVWRI@BG~Rpyc<+Y*^R*cEpf2*7IFxTmPy2X;{}k85wOE{Uj?nPK<>xo>`+f zk8v)X*?p3?!taZS4)9-s#Z?61xANPE-`IZypy7G5w7$6&R$<()_udRH&sR98zH;%= zD*87d5G~hVi{}NS!*wd>zxkv#aqa(Gwi*WWS!koJ8^Hy=*MKd$jZ#aH|DZesNrf%l0s1@$b%`y$ZX zM~JW1u&bzX+4ySvl4TA5H^o;Ahl=Z)n+f~c5&?FU16W8l^;CL zVf>Ge5m#;XFpYEBxN1KqtIRmBFpS2rO8K~IMN8h~BZKHkmC=(LqbK)1%W#T771NU) z2_J;_AAdfY zDt0;_2axq&pO5n6%h;lHDvHE%;BJE!KwFdr@b;E+3*hFLL;igL{|n>&JCQ$sT%(+w zD;f^tu4qvo6e{Xl6k`#@uDXJUFeb~yJU)g5$NbqB#dCy_%*Rz*QW96KQDhpL8wMD} z8f(R+JB!EiyP;*rzce1pO#pq|cr4(fMN_kQQSBcuUR0(knz5kOH{0l*)Gnf8_tD?e zee^K?r*|L0qVi|@g+M}|O5%TV58Yd(yWmR{FKR{FulQ}+tN4{}p}(wnQM!emPi$K9 zG2?$`i2c(!hQrGQ3n6zE-LQptIS(JOfqvBev;E`jpUj6d&lLSnvt`PehQ#}G@NY5y z!q(2e;vBu<@2Nk)$K$H!$*7PiwDV}rr9Tbzd_LUVTRZ#k=hrOWR!#qcVLRimLU=oqJ7C4zI{pLT z6GiZ8e@OplJ{r8jZM{%Y(j_M!-yz)9v5H?BAwr|VHyW)OMB8^3XiF@dHrh)<`ts6{Qo=r%zCd0+K3)IXdsXH3 z+0r;$qXF8K7{H$$<2NGfT_s8u#*HP8Y0;J!jyIZ!e7r3i>T_%SF;&qql{UXcB>t<> za^t;f)8O$|d{W~7RpMyIR&E&oukpVp{@?bhLA$B($Hj4ai|G1CkC)m1PT6rUrRR48 z^l|C=4*m}hV-kbUzWvS){txft@|EKgQRM9bl=(k=%K%IKAC4l;$-{D^o_W{7tu;9F zf4KVy9dn-x{^W|5_&op$V*~dg&w9(C0#s2g-mP|Ga@jLy$YlfK+LP=SDsKwi}mR z8IXSQdAU*dgdpdZ0a>xt8v0oXGQJGR=hhg=4IxNv8IZ+d9f^zuk<}bU%7DE1l!3H` zwLJ00j^pzDVb6f$LNjP~HHg1jm|1x}#{6+N@ z$IGfnP4ZEOJRGdn!L)3BVLTh^n_JO8i3**3fc6wUvq;`DwMgFbzc^l2QT(sU{wp!D zEPRg}zfhv7@5TP9kiWC|PxY{N>|lzQ6^sO$&`(no`e~}PG2+FY?17 zl3+Ob?+3)ox__HY^>-dGE9@_usrnJd8~Q0CwE_BS{9d_uSydc*GQA}3k)}6wp4#rtQ zr~V7^X)#%PEgYgm1!!^;iN`htGRSLcn{2YM4wk&)Y|0=|H9V{IJ_6gH1uh>4}Tbt?TsIZ#@_6ZM!DMj`PeI)%i+9#|vW#7*26MpIPceGEqHO$!2 zK4HGg+0j1XtU|^h`-H4zNWxICPk7|9!VK-yKH)dOxbxT!YCuP4@_N9JVxRE-mI+zz zV94~T$U?!{Jq=hW6pRvN429j@%jN8!Q1buJ8Yg^qkZ}UH9%Y>HtQjXvW=MmFA9CcS z4wYz;al&&lPWag_1>*z^-*#f0z?G@cI019U>9r$*UTTza!U$^I(Kul$bvp85#tE-8 zig}U{~fb_dZ}QRP-kWd-sn4=Rl=tVRtY3w zl(3sKO7N-{l~Di(cOgl1p7r-6d_`g5rKF`b~1Z{tRUy^yA=bpQrd+s^so_p@O?#|Y1 z>?22C+v`8;OEi*j;O76Bl=>4F3{ob6i?&)(Xdc0Tmc(39T4J7Eu*UU6gdjIAIXBs+;WZ4wkN zm?x@3xUKAga?ic)->e8fy=tFkl-|ENa&K> zoV}wby`)#pUt*Q@>0|fL>VCx*4QaT44jZ%kC@aG83Ey{O5N&@wO0Vjkend2$QF={} z^tvAJ^*!Dj!gsZ2MoD}0-rD2+-KRK@4o2@$dlEhBOZRwhF4k}NHAeYW zp6+KXeP7h$U49#->DQFJNB1|PZ@fQ-(}x$osnn9|8{Ns}!JQn9?ih(IB!)_*y9Dn8 zg=_cA&xa)FMfnY{R4P$!i{ZtN?(#;t+BE<--rzX+p4OKHU$W7cW_@Yp%ZTU;;gYJ# zhx0~>if9PKNYzf`#aITv`|wSPHQ{fqfOxM9-v@=i_2GAm3k26hL9)NI_#66zt6TLqL;j#GqM{NHwM?e>{2+Ju`xL0 zCA1K4w%zhrr4N6S7cnIc(92I(D)p4<+1$zUUEjE}(XM$EYxu6*XBFV;wct>UqA73Y zEM=Q+P}9_{pzTpt)GQTs2X^yx?%vvYr0D%OOj%{XQ+{Kx^&ypT=0*CzC5{_|7PI+s zY*sHnF{X4;Eg>s61~;&Z@UFj3r2t%~Pj6^7!pk7Ne92D05RqRm`%0A6Vpp_>B$c>9U zI?}RBB9r%)@ws>_qzLT&*Gw+TDL*8zepZw&^wbx0l=2rMm{t+kGrUo5?*tYSP7U=4 zXomzArtg@*@^aIY-bg9o)Om{|dbH+HYYp)|T|2CQOsekl=Ic?D3*8zNeJONn*XWCN ztIQSm|F2tLIB)Vyig4ZIm!I>h8?+zut8RWBtaKYR6_>o15iYrCQiGPTj0{nscSl8E z3cVW?eX-sh;Hvol*Sjwsv{wiV5FEtDg-I_&-*YdSKs)Wz@KbPbcMnEqFWYXv@7$MB z2)>PNwkU*emSYZN{XD9!j?9ALNozS~)ry7gsl==Van7nqso1qYM0F8uU}eVWmI zzwhrm+MlZ;Xb_p-qf@H)r^WJu@Y9@L%KM1Z4n-MWx|!GKZ(;~TZwoUgUFL54eY5Es zAlHTRej0r(*?*(LEr;dFy1o>Gn zARD`^l>I}HvwH#A?K1;;??s2iY%d_yfr0c1OBvV;$n9uv^yq=Gl%0A3StrW`qlJjo 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 z�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

irtSCZVbz&Qbb6 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 z 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

Runf=*Zwy_i8w`6>G0k`xY@FTZ~Y(;w_yaCvV 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 i@U?Eu5wNvsBn{SVD5Sk}e&Kh9wiT$McHoJ5*VL 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

-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 PI$%F(VS7c=u= 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 Zo&V>&{EqNEH~zbS*T%nb7%+TY{69rKRP6u& literal 0 HcmV?d00001 diff --git a/phonelibs/acados/x86_64/lib/libqpOASES_e.so b/phonelibs/acados/x86_64/lib/libqpOASES_e.so new file mode 120000 index 0000000000..14048625a1 --- /dev/null +++ b/phonelibs/acados/x86_64/lib/libqpOASES_e.so @@ -0,0 +1 @@ +libqpOASES_e.so.3.1 \ No newline at end of file diff --git a/phonelibs/acados/x86_64/lib/libqpOASES_e.so.3.1 b/phonelibs/acados/x86_64/lib/libqpOASES_e.so.3.1 new file mode 100644 index 0000000000000000000000000000000000000000..93967ae04a9ba488a3235659f6f1aae0ba64141a GIT binary patch literal 262824 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 C!{QqN literal 0 HcmV?d00001 diff --git a/pyextra/acados_template/.gitignore b/pyextra/acados_template/.gitignore new file mode 100644 index 0000000000..c18dd8d83c --- /dev/null +++ b/pyextra/acados_template/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/pyextra/acados_template/__init__.py b/pyextra/acados_template/__init__.py new file mode 100644 index 0000000000..f33b75bb7b --- /dev/null +++ b/pyextra/acados_template/__init__.py @@ -0,0 +1,43 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +from .acados_model import * +from .generate_c_code_explicit_ode import * +from .generate_c_code_implicit_ode import * +from .generate_c_code_constraint import * +from .generate_c_code_nls_cost import * +from .acados_ocp import * +from .acados_sim import * +from .acados_ocp_solver import * +from .acados_sim_solver import * +from .utils import * diff --git a/pyextra/acados_template/acados_layout.json b/pyextra/acados_template/acados_layout.json new file mode 100644 index 0000000000..9458afb10c --- /dev/null +++ b/pyextra/acados_template/acados_layout.json @@ -0,0 +1,772 @@ +{ + "code_export_directory": [ + "str" + ], + "acados_include_path": [ + "str" + ], + "model": { + "name" : [ + "str" + ], + "dyn_ext_fun_type" : [ + "str" + ], + "dyn_source_discrete" : [ + "str" + ], + "dyn_disc_fun_jac_hess" : [ + "str" + ], + "dyn_disc_fun_jac" : [ + "str" + ], + "dyn_disc_fun" : [ + "str" + ] + }, + "parameter_values": [ + "ndarray", + [ + "np" + ] + ], + "acados_lib_path": [ + "str" + ], + "problem_class": [ + "str" + ], + "constraints": { + "constr_type": [ + "str" + ], + "constr_type_e": [ + "str" + ], + "lbx": [ + "ndarray", + [ + "nbx" + ] + ], + "lbu": [ + "ndarray", + [ + "nbu" + ] + ], + "ubx": [ + "ndarray", + [ + "nbx" + ] + ], + "ubu": [ + "ndarray", + [ + "nbu" + ] + ], + "idxbx": [ + "ndarray", + [ + "nbx" + ] + ], + "idxbu": [ + "ndarray", + [ + "nbu" + ] + ], + "lbx_e": [ + "ndarray", + [ + "nbx_e" + ] + ], + "ubx_e": [ + "ndarray", + [ + "nbx_e" + ] + ], + "idxbx_e": [ + "ndarray", + [ + "nbx_e" + ] + ], + "lbx_0": [ + "ndarray", + [ + "nbx_0" + ] + ], + "ubx_0": [ + "ndarray", + [ + "nbx_0" + ] + ], + "idxbx_0": [ + "ndarray", + [ + "nbx_0" + ] + ], + "idxbxe_0": [ + "ndarray", + [ + "nbxe_0" + ] + ], + "lg": [ + "ndarray", + [ + "ng" + ] + ], + "ug": [ + "ndarray", + [ + "ng" + ] + ], + "D": [ + "ndarray", + [ + "ng", + "nu" + ] + ], + "C": [ + "ndarray", + [ + "ng", + "nx" + ] + ], + "C_e": [ + "ndarray", + [ + "ng_e", + "nx" + ] + ], + "lg_e": [ + "ndarray", + [ + "ng_e" + ] + ], + "ug_e": [ + "ndarray", + [ + "ng_e" + ] + ], + "lh": [ + "ndarray", + [ + "nh" + ] + ], + "uh": [ + "ndarray", + [ + "nh" + ] + ], + "lh_e": [ + "ndarray", + [ + "nh_e" + ] + ], + "uh_e": [ + "ndarray", + [ + "nh_e" + ] + ], + "lphi": [ + "ndarray", + [ + "nphi" + ] + ], + "uphi": [ + "ndarray", + [ + "nphi" + ] + ], + "lphi_e": [ + "ndarray", + [ + "nphi_e" + ] + ], + "uphi_e": [ + "ndarray", + [ + "nphi_e" + ] + ], + "lsbx": [ + "ndarray", + [ + "nsbx" + ] + ], + "usbx": [ + "ndarray", + [ + "nsbx" + ] + ], + "lsbu": [ + "ndarray", + [ + "nsbu" + ] + ], + "usbu": [ + "ndarray", + [ + "nsbu" + ] + ], + "idxsbx": [ + "ndarray", + [ + "nsbx" + ] + ], + "idxsbu": [ + "ndarray", + [ + "nsbu" + ] + ], + "lsbx_e": [ + "ndarray", + [ + "nsbx_e" + ] + ], + "usbx_e": [ + "ndarray", + [ + "nsbx_e" + ] + ], + "idxsbx_e": [ + "ndarray", + [ + "nsbx_e" + ] + ], + "lsg": [ + "ndarray", + [ + "nsg" + ] + ], + "usg": [ + "ndarray", + [ + "nsg" + ] + ], + "idxsg": [ + "ndarray", + [ + "nsg" + ] + ], + "lsg_e": [ + "ndarray", + [ + "nsg_e" + ] + ], + "usg_e": [ + "ndarray", + [ + "nsg_e" + ] + ], + "idxsg_e": [ + "ndarray", + [ + "nsg_e" + ] + ], + "lsh": [ + "ndarray", + [ + "nsh" + ] + ], + "ush": [ + "ndarray", + [ + "nsh" + ] + ], + "idxsh": [ + "ndarray", + [ + "nsh" + ] + ], + "lsh_e": [ + "ndarray", + [ + "nsh_e" + ] + ], + "ush_e": [ + "ndarray", + [ + "nsh_e" + ] + ], + "idxsh_e": [ + "ndarray", + [ + "nsh_e" + ] + ], + "lsphi": [ + "ndarray", + [ + "nsphi" + ] + ], + "usphi": [ + "ndarray", + [ + "nsphi" + ] + ], + "idxsphi": [ + "ndarray", + [ + "nsphi" + ] + ], + "lsphi_e": [ + "ndarray", + [ + "nsphi_e" + ] + ], + "usphi_e": [ + "ndarray", + [ + "nsphi_e" + ] + ], + "idxsphi_e": [ + "ndarray", + [ + "nsphi_e" + ] + ] + }, + "cost": { + "cost_type_0": [ + "str" + ], + "cost_type": [ + "str" + ], + "cost_type_e": [ + "str" + ], + "cost_ext_fun_type_0": [ + "str" + ], + "cost_ext_fun_type": [ + "str" + ], + "cost_ext_fun_type_e": [ + "str" + ], + "Vu_0": [ + "ndarray", + [ + "ny_0", + "nu" + ] + ], + "Vu": [ + "ndarray", + [ + "ny", + "nu" + ] + ], + "Vx_0": [ + "ndarray", + [ + "ny_0", + "nx" + ] + ], + "Vx": [ + "ndarray", + [ + "ny", + "nx" + ] + ], + "Vx_e": [ + "ndarray", + [ + "ny_e", + "nx" + ] + ], + "Vz_0": [ + "ndarray", + [ + "ny_0", + "nz" + ] + ], + "Vz": [ + "ndarray", + [ + "ny", + "nz" + ] + ], + "W_0": [ + "ndarray", + [ + "ny_0", + "ny_0" + ] + ], + "W": [ + "ndarray", + [ + "ny", + "ny" + ] + ], + "Zl": [ + "ndarray", + [ + "ns" + ] + ], + "Zu": [ + "ndarray", + [ + "ns" + ] + ], + "zl": [ + "ndarray", + [ + "ns" + ] + ], + "zu": [ + "ndarray", + [ + "ns" + ] + ], + "W_e": [ + "ndarray", + [ + "ny_e", + "ny_e" + ] + ], + "yref_0": [ + "ndarray", + [ + "ny_0" + ] + ], + "yref": [ + "ndarray", + [ + "ny" + ] + ], + "yref_e": [ + "ndarray", + [ + "ny_e" + ] + ], + "Zl_e": [ + "ndarray", + [ + "ns_e" + ] + ], + "Zu_e": [ + "ndarray", + [ + "ns_e" + ] + ], + "zl_e": [ + "ndarray", + [ + "ns_e" + ] + ], + "zu_e": [ + "ndarray", + [ + "ns_e" + ] + ] + }, + "dims": { + "N": [ + "int" + ], + "nbu": [ + "int" + ], + "nbx": [ + "int" + ], + "nsbu": [ + "int" + ], + "nsbx": [ + "int" + ], + "nsbx_e": [ + "int" + ], + "nbx_0": [ + "int" + ], + "nbx_e": [ + "int" + ], + "nbxe_0": [ + "int" + ], + "nsg": [ + "int" + ], + "nsg_e": [ + "int" + ], + "nsh": [ + "int" + ], + "nsh_e": [ + "int" + ], + "nsphi": [ + "int" + ], + "nsphi_e": [ + "int" + ], + "ns": [ + "int" + ], + "ns_e": [ + "int" + ], + "ng": [ + "int" + ], + "ng_e": [ + "int" + ], + "np": [ + "int" + ], + "nr": [ + "int" + ], + "nr_e": [ + "int" + ], + "nh": [ + "int" + ], + "nh_e": [ + "int" + ], + "nphi": [ + "int" + ], + "nphi_e": [ + "int" + ], + "nu": [ + "int" + ], + "nx": [ + "int" + ], + "ny": [ + "int" + ], + "ny_0": [ + "int" + ], + "ny_e": [ + "int" + ], + "nz": [ + "int" + ], + "gnsf_nx1": [ + "int" + ], + "gnsf_nz1": [ + "int" + ], + "gnsf_nuhat": [ + "int" + ], + "gnsf_ny": [ + "int" + ], + "gnsf_nout": [ + "int" + ] + }, + "solver_options": { + "time_steps": [ + "ndarray", + [ + "N" + ] + ], + "hessian_approx": [ + "str" + ], + "regularize_method": [ + "str" + ], + "integrator_type": [ + "str" + ], + "nlp_solver_type": [ + "str" + ], + "globalization": [ + "str" + ], + "nlp_solver_step_length": [ + "float" + ], + "levenberg_marquardt": [ + "float" + ], + "qp_solver": [ + "str" + ], + "tf": [ + "float" + ], + "Tsim": [ + "float" + ], + "alpha_min": [ + "float" + ], + "alpha_reduction": [ + "float" + ], + "sim_method_num_stages": [ + "ndarray", + [ + "N" + ] + ], + "sim_method_num_steps": [ + "ndarray", + [ + "N" + ] + ], + "sim_method_newton_iter": [ + "int" + ], + "sim_method_jac_reuse": [ + "bool" + ], + "qp_solver_cond_N": [ + "int" + ], + "qp_solver_warm_start": [ + "int" + ], + "qp_solver_tol_stat": [ + "float" + ], + "qp_solver_tol_eq": [ + "float" + ], + "qp_solver_tol_ineq": [ + "float" + ], + "qp_solver_tol_comp": [ + "float" + ], + "qp_solver_iter_max": [ + "int" + ], + "nlp_solver_tol_stat": [ + "float" + ], + "nlp_solver_tol_eq": [ + "float" + ], + "nlp_solver_tol_ineq": [ + "float" + ], + "nlp_solver_tol_comp": [ + "float" + ], + "nlp_solver_max_iter": [ + "int" + ], + "print_level": [ + "int" + ], + "initialize_t_slacks": [ + "int" + ], + "exact_hess_cost": [ + "int" + ], + "exact_hess_constr": [ + "int" + ], + "exact_hess_dyn": [ + "int" + ], + "ext_cost_num_hess": [ + "int" + ], + "model_external_shared_lib_dir": [ + "str" + ], + "model_external_shared_lib_name": [ + "str" + ] + } +} diff --git a/pyextra/acados_template/acados_model.py b/pyextra/acados_template/acados_model.py new file mode 100644 index 0000000000..379ade0d95 --- /dev/null +++ b/pyextra/acados_template/acados_model.py @@ -0,0 +1,151 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + + +class AcadosModel(): + """ + Class containing all the information to code generate the external CasADi functions + that are needed when creating an acados ocp solver or acados integrator. + Thus, this class contains: + + a) the :py:attr:`name` of the model, + b) all CasADi variables/expressions needed in the CasADi function generation process. + """ + def __init__(self): + ## common for OCP and Integrator + self.name = None + """ + The model name is used for code generation. Type: string. Default: :code:`None` + """ + self.x = None #: CasADi variable describing the state of the system; Default: :code:`None` + self.xdot = None #: CasADi variable describing the derivative of the state wrt time; Default: :code:`None` + self.u = None #: CasADi variable describing the input of the system; Default: :code:`None` + self.z = [] #: CasADi variable describing the algebraic variables of the DAE; Default: :code:`empty` + self.p = [] #: CasADi variable describing parameters of the DAE; Default: :code:`empty` + # dynamics + self.f_impl_expr = None + """ + CasADi expression for the implicit dynamics :math:`f_\\text{impl}(\dot{x}, x, u, z, p) = 0`. + Used if :py:attr:`acados_template.acados_ocp.AcadosOcpOptions.integrator_type` == 'IRK'. + Default: :code:`None` + """ + self.f_expl_expr = None + """ + CasADi expression for the explicit dynamics :math:`\dot{x} = f_\\text{expl}(x, u, p)`. + Used if :py:attr:`acados_template.acados_ocp.AcadosOcpOptions.integrator_type` == 'ERK'. + Default: :code:`None` + """ + self.disc_dyn_expr = None + """ + CasADi expression for the discrete dynamics :math:`x_{+} = f_\\text{disc}(x, u, p)`. + Used if :py:attr:`acados_template.acados_ocp.AcadosOcpOptions.integrator_type` == 'DISCRETE'. + Default: :code:`None` + """ + + self.dyn_ext_fun_type = 'casadi' #: type of external functions for dynamics module; 'casadi' or 'generic'; Default: 'casadi' + self.dyn_source_discrete = None #: name of source file for discrete dyanamics; Default: :code:`None` + self.dyn_disc_fun_jac_hess = None #: name of function discrete dyanamics + jacobian and hessian; Default: :code:`None` + self.dyn_disc_fun_jac = None #: name of function discrete dyanamics + jacobian; Default: :code:`None` + self.dyn_disc_fun = None #: name of function discrete dyanamics; Default: :code:`None` + + ## for OCP + # constraints + self.con_h_expr = None #: CasADi expression for the constraint :math:`h`; Default: :code:`None` + self.con_phi_expr = None #: CasADi expression for the constraint phi; Default: :code:`None` + self.con_r_expr = None #: CasADi expression for the constraint phi(r); Default: :code:`None` + self.con_r_in_phi = None + # terminal + self.con_h_expr_e = None #: CasADi expression for the terminal constraint :math:`h^e`; Default: :code:`None` + self.con_r_expr_e = None #: CasADi expression for the terminal constraint; Default: :code:`None` + self.con_phi_expr_e = None #: CasADi expression for the terminal constraint; Default: :code:`None` + self.con_r_in_phi_e = None + # cost + self.cost_y_expr = None #: CasADi expression for nonlinear least squares; Default: :code:`None` + self.cost_y_expr_e = None #: CasADi expression for nonlinear least squares, terminal; Default: :code:`None` + self.cost_y_expr_0 = None #: CasADi expression for nonlinear least squares, initial; Default: :code:`None` + self.cost_expr_ext_cost = None #: CasADi expression for external cost; Default: :code:`None` + self.cost_expr_ext_cost_e = None #: CasADi expression for external cost, terminal; Default: :code:`None` + self.cost_expr_ext_cost_0 = None #: CasADi expression for external cost, initial; Default: :code:`None` + + +def acados_model_strip_casadi_symbolics(model): + out = model + if 'f_impl_expr' in out.keys(): + del out['f_impl_expr'] + if 'f_expl_expr' in out.keys(): + del out['f_expl_expr'] + if 'disc_dyn_expr' in out.keys(): + del out['disc_dyn_expr'] + if 'x' in out.keys(): + del out['x'] + if 'xdot' in out.keys(): + del out['xdot'] + if 'u' in out.keys(): + del out['u'] + if 'z' in out.keys(): + del out['z'] + if 'p' in out.keys(): + del out['p'] + # constraints + if 'con_phi_expr' in out.keys(): + del out['con_phi_expr'] + if 'con_h_expr' in out.keys(): + del out['con_h_expr'] + if 'con_r_expr' in out.keys(): + del out['con_r_expr'] + if 'con_r_in_phi' in out.keys(): + del out['con_r_in_phi'] + # terminal + if 'con_phi_expr_e' in out.keys(): + del out['con_phi_expr_e'] + if 'con_h_expr_e' in out.keys(): + del out['con_h_expr_e'] + if 'con_r_expr_e' in out.keys(): + del out['con_r_expr_e'] + if 'con_r_in_phi_e' in out.keys(): + del out['con_r_in_phi_e'] + # cost + if 'cost_y_expr' in out.keys(): + del out['cost_y_expr'] + if 'cost_y_expr_e' in out.keys(): + del out['cost_y_expr_e'] + if 'cost_y_expr_0' in out.keys(): + del out['cost_y_expr_0'] + if 'cost_expr_ext_cost' in out.keys(): + del out['cost_expr_ext_cost'] + if 'cost_expr_ext_cost_e' in out.keys(): + del out['cost_expr_ext_cost_e'] + if 'cost_expr_ext_cost_0' in out.keys(): + del out['cost_expr_ext_cost_0'] + + return out diff --git a/pyextra/acados_template/acados_ocp.py b/pyextra/acados_template/acados_ocp.py new file mode 100644 index 0000000000..eec9b7e09f --- /dev/null +++ b/pyextra/acados_template/acados_ocp.py @@ -0,0 +1,2822 @@ +# -*- coding: future_fstrings -*- +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import numpy as np +import os +from .acados_model import AcadosModel +from .utils import get_acados_path, J_to_idx, J_to_idx_slack + +class AcadosOcpDims: + """ + Class containing the dimensions of the optimal control problem. + """ + def __init__(self): + self.__nx = None + self.__nu = None + self.__nz = 0 + self.__np = 0 + self.__ny = 0 + self.__ny_e = 0 + self.__ny_0 = 0 + self.__nr = 0 + self.__nr_e = 0 + self.__nh = 0 + self.__nh_e = 0 + self.__nphi = 0 + self.__nphi_e = 0 + self.__nbx = 0 + self.__nbx_0 = 0 + self.__nbx_e = 0 + self.__nbu = 0 + self.__nsbx = 0 + self.__nsbx_e = 0 + self.__nsbu = 0 + self.__nsh = 0 + self.__nsh_e = 0 + self.__nsphi = 0 + self.__nsphi_e = 0 + self.__ns = 0 + self.__ns_e = 0 + self.__ng = 0 + self.__ng_e = 0 + self.__nsg = 0 + self.__nsg_e = 0 + self.__nbxe_0 = None + self.__N = None + + + @property + def nx(self): + """:math:`n_x` - number of states. + Type: int; default: None""" + return self.__nx + + @property + def nz(self): + """:math:`n_z` - number of algebraic variables. + Type: int; default: 0""" + return self.__nz + + @property + def nu(self): + """:math:`n_u` - number of inputs. + Type: int; default: None""" + return self.__nu + + @property + def np(self): + """:math:`n_p` - number of parameters. + Type: int; default: 0""" + return self.__np + + @property + def ny(self): + """:math:`n_y` - number of residuals in Lagrange term. + Type: int; default: 0""" + return self.__ny + + @property + def ny_0(self): + """:math:`n_{y}^0` - number of residuals in Mayer term. + Type: int; default: 0""" + return self.__ny_0 + + @property + def ny_e(self): + """:math:`n_{y}^e` - number of residuals in Mayer term. + Type: int; default: 0""" + return self.__ny_e + + @property + def nr(self): + """:math:`n_{\pi}` - dimension of the image of the inner nonlinear function in positive definite constraints. + Type: int; default: 0""" + return self.__nr + + @property + def nr_e(self): + """:math:`n_{\pi}^e` - dimension of the image of the inner nonlinear function in positive definite constraints. + Type: int; default: 0""" + return self.__nr_e + + @property + def nh(self): + """:math:`n_h` - number of nonlinear constraints. + Type: int; default: 0""" + return self.__nh + + @property + def nh_e(self): + """:math:`n_{h}^e` - number of nonlinear constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__nh_e + + @property + def nphi(self): + """:math:`n_{\phi}` - number of convex-over-nonlinear constraints. + Type: int; default: 0""" + return self.__nphi + + @property + def nphi_e(self): + """:math:`n_{\phi}^e` - number of convex-over-nonlinear constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__nphi_e + + @property + def nbx(self): + """:math:`n_{b_x}` - number of state bounds. + Type: int; default: 0""" + return self.__nbx + + @property + def nbxe_0(self): + """:math:`n_{be_{x0}}` - number of state bounds at initial shooting node that are equalities. + Type: int; default: None""" + return self.__nbxe_0 + + @property + def nbx_0(self): + """:math:`n_{b_{x0}}` - number of state bounds for initial state. + Type: int; default: 0""" + return self.__nbx_0 + + @property + def nbx_e(self): + """:math:`n_{b_x}` - number of state bounds at terminal shooting node N. + Type: int; default: 0""" + return self.__nbx_e + + @property + def nbu(self): + """:math:`n_{b_u}` - number of input bounds. + Type: int; default: 0""" + return self.__nbu + + @property + def nsbx(self): + """:math:`n_{{sb}_x}` - number of soft state bounds. + Type: int; default: 0""" + return self.__nsbx + + @property + def nsbx_e(self): + """:math:`n_{{sb}^e_{x}}` - number of soft state bounds at terminal shooting node N. + Type: int; default: 0""" + return self.__nsbx_e + + @property + def nsbu(self): + """:math:`n_{{sb}_u}` - number of soft input bounds. + Type: int; default: 0""" + return self.__nsbu + + @property + def nsg(self): + """:math:`n_{{sg}}` - number of soft general linear constraints. + Type: int; default: 0""" + return self.__nsg + + @property + def nsg_e(self): + """:math:`n_{{sg}^e}` - number of soft general linear constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__nsg_e + + @property + def nsh(self): + """:math:`n_{{sh}}` - number of soft nonlinear constraints. + Type: int; default: 0""" + return self.__nsh + + @property + def nsh_e(self): + """:math:`n_{{sh}}^e` - number of soft nonlinear constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__nsh_e + + @property + def nsphi(self): + """:math:`n_{{s\phi}}` - number of soft convex-over-nonlinear constraints. + Type: int; default: 0""" + return self.__nsphi + + @property + def nsphi_e(self): + """:math:`n_{{s\phi}^e}` - number of soft convex-over-nonlinear constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__nsphi_e + + @property + def ns(self): + """:math:`n_{s}` - total number of slacks. + Type: int; default: 0""" + return self.__ns + + @property + def ns_e(self): + """:math:`n_{s}^e` - total number of slacks at terminal shooting node N. + Type: int; default: 0""" + return self.__ns_e + + @property + def ng(self): + """:math:`n_{g}` - number of general polytopic constraints. + Type: int; default: 0""" + return self.__ng + + @property + def ng_e(self): + """:math:`n_{g}^e` - number of general polytopic constraints at terminal shooting node N. + Type: int; default: 0""" + return self.__ng_e + + @property + def N(self): + """:math:`N` - prediction horizon. + Type: int; default: None""" + return self.__N + + @nx.setter + def nx(self, nx): + if type(nx) == int and nx > 0: + self.__nx = nx + else: + raise Exception('Invalid nx value, expected positive integer. Exiting.') + + @nz.setter + def nz(self, nz): + if type(nz) == int and nz > -1: + self.__nz = nz + else: + raise Exception('Invalid nz value, expected nonnegative integer. Exiting.') + + @nu.setter + def nu(self, nu): + if type(nu) == int and nu > -1: + self.__nu = nu + else: + raise Exception('Invalid nu value, expected nonnegative integer. Exiting.') + + @np.setter + def np(self, np): + if type(np) == int and np > -1: + self.__np = np + else: + raise Exception('Invalid np value, expected nonnegative integer. Exiting.') + + @ny_0.setter + def ny_0(self, ny_0): + if isinstance(ny_0, int) and ny_0 > -1: + self.__ny_0 = ny_0 + else: + raise Exception('Invalid ny_0 value, expected nonnegative integer. Exiting.') + + @ny.setter + def ny(self, ny): + if isinstance(ny, int) and ny > -1: + self.__ny = ny + else: + raise Exception('Invalid ny value, expected nonnegative integer. Exiting.') + + @ny_e.setter + def ny_e(self, ny_e): + if type(ny_e) == int and ny_e > -1: + self.__ny_e = ny_e + else: + raise Exception('Invalid ny_e value, expected nonnegative integer. Exiting.') + + @nr.setter + def nr(self, nr): + if type(nr) == int and nr > -1: + self.__nr = nr + else: + raise Exception('Invalid nr value, expected nonnegative integer. Exiting.') + + @nr_e.setter + def nr_e(self, nr_e): + if type(nr_e) == int and nr_e > -1: + self.__nr_e = nr_e + else: + raise Exception('Invalid nr_e value, expected nonnegative integer. Exiting.') + + @nh.setter + def nh(self, nh): + if type(nh) == int and nh > -1: + self.__nh = nh + else: + raise Exception('Invalid nh value, expected nonnegative integer. Exiting.') + + @nh_e.setter + def nh_e(self, nh_e): + if type(nh_e) == int and nh_e > -1: + self.__nh_e = nh_e + else: + raise Exception('Invalid nh_e value, expected nonnegative integer. Exiting.') + + @nphi.setter + def nphi(self, nphi): + if type(nphi) == int and nphi > -1: + self.__nphi = nphi + else: + raise Exception('Invalid nphi value, expected nonnegative integer. Exiting.') + + @nphi_e.setter + def nphi_e(self, nphi_e): + if type(nphi_e) == int and nphi_e > -1: + self.__nphi_e = nphi_e + else: + raise Exception('Invalid nphi_e value, expected nonnegative integer. Exiting.') + + @nbx.setter + def nbx(self, nbx): + if isinstance(nbx, int) and nbx > -1: + self.__nbx = nbx + else: + raise Exception('Invalid nbx value, expected nonnegative integer. Exiting.') + + @nbxe_0.setter + def nbxe_0(self, nbxe_0): + if isinstance(nbxe_0, int) and nbxe_0 > -1: + self.__nbxe_0 = nbxe_0 + else: + raise Exception('Invalid nbxe_0 value, expected nonnegative integer. Exiting.') + + @nbx_0.setter + def nbx_0(self, nbx_0): + if type(nbx_0) == int and nbx_0 > -1: + self.__nbx_0 = nbx_0 + else: + raise Exception('Invalid nbx_0 value, expected nonnegative integer. Exiting.') + + @nbx_e.setter + def nbx_e(self, nbx_e): + if type(nbx_e) == int and nbx_e > -1: + self.__nbx_e = nbx_e + else: + raise Exception('Invalid nbx_e value, expected nonnegative integer. Exiting.') + + @nbu.setter + def nbu(self, nbu): + if type(nbu) == int and nbu > -1: + self.__nbu = nbu + else: + raise Exception('Invalid nbu value, expected nonnegative integer. Exiting.') + + @nsbx.setter + def nsbx(self, nsbx): + if type(nsbx) == int and nsbx > -1: + self.__nsbx = nsbx + else: + raise Exception('Invalid nsbx value, expected nonnegative integer. Exiting.') + + @nsbx_e.setter + def nsbx_e(self, nsbx_e): + if type(nsbx_e) == int and nsbx_e > -1: + self.__nsbx_e = nsbx_e + else: + raise Exception('Invalid nsbx_e value, expected nonnegative integer. Exiting.') + + @nsbu.setter + def nsbu(self, nsbu): + if type(nsbu) == int and nsbu > -1: + self.__nsbu = nsbu + else: + raise Exception('Invalid nsbu value, expected nonnegative integer. Exiting.') + + @nsg.setter + def nsg(self, nsg): + if isinstance(nsg, int) and nsg > -1: + self.__nsg = nsg + else: + raise Exception('Invalid nsg value, expected nonnegative integer. Exiting.') + + @nsg_e.setter + def nsg_e(self, nsg_e): + if isinstance(nsg_e, int) and nsg_e > -1: + self.__nsg_e = nsg_e + else: + raise Exception('Invalid nsg_e value, expected nonnegative integer. Exiting.') + + @nsh.setter + def nsh(self, nsh): + if isinstance(nsh, int) and nsh > -1: + self.__nsh = nsh + else: + raise Exception('Invalid nsh value, expected nonnegative integer. Exiting.') + + @nsh_e.setter + def nsh_e(self, nsh_e): + if isinstance(nsh_e, int) and nsh_e > -1: + self.__nsh_e = nsh_e + else: + raise Exception('Invalid nsh_e value, expected nonnegative integer. Exiting.') + + @nsphi.setter + def nsphi(self, nsphi): + if isinstance(nsphi, int) and nsphi > -1: + self.__nsphi = nsphi + else: + raise Exception('Invalid nsphi value, expected nonnegative integer. Exiting.') + + @nsphi_e.setter + def nsphi_e(self, nsphi_e): + if isinstance(nsphi_e, int) and nsphi_e > -1: + self.__nsphi_e = nsphi_e + else: + raise Exception('Invalid nsphi_e value, expected nonnegative integer. Exiting.') + + @ns.setter + def ns(self, ns): + if isinstance(ns, int) and ns > -1: + self.__ns = ns + else: + raise Exception('Invalid ns value, expected nonnegative integer. Exiting.') + + @ns_e.setter + def ns_e(self, ns_e): + if isinstance(ns_e, int) and ns_e > -1: + self.__ns_e = ns_e + else: + raise Exception('Invalid ns_e value, expected nonnegative integer. Exiting.') + + @ng.setter + def ng(self, ng): + if isinstance(ng, int) and ng > -1: + self.__ng = ng + else: + raise Exception('Invalid ng value, expected nonnegative integer. Exiting.') + + @ng_e.setter + def ng_e(self, ng_e): + if isinstance(ng_e, int) and ng_e > -1: + self.__ng_e = ng_e + else: + raise Exception('Invalid ng_e value, expected nonnegative integer. Exiting.') + + @N.setter + def N(self, N): + if isinstance(N, int) and N > 0: + self.__N = N + else: + raise Exception('Invalid N value, expected positive integer. Exiting.') + + def set(self, attr, value): + setattr(self, attr, value) + + +class AcadosOcpCost: + """ + Class containing the numerical data of the cost: + + In case of LINEAR_LS: + stage cost is + :math:`l(x,u,z) = || V_x \, x + V_u \, u + V_z \, z - y_\\text{ref}||^2_W`, + terminal cost is + :math:`m(x) = || V^e_x \, x - y_\\text{ref}^e||^2_{W^e}` + + In case of NONLINEAR_LS: + stage cost is + :math:`l(x,u,z) = || y(x,u,z) - y_\\text{ref}||^2_W`, + terminal cost is + :math:`m(x) = || y^e(x) - y_\\text{ref}^e||^2_{W^e}` + """ + def __init__(self): + # initial stage + self.__cost_type_0 = None + self.__W_0 = None + self.__Vx_0 = None + self.__Vu_0 = None + self.__Vz_0 = None + self.__yref_0 = None + self.__cost_ext_fun_type_0 = 'casadi' + # Lagrange term + self.__cost_type = 'LINEAR_LS' # cost type + self.__W = np.zeros((0,0)) + self.__Vx = np.zeros((0,0)) + self.__Vu = np.zeros((0,0)) + self.__Vz = np.zeros((0,0)) + self.__yref = np.array([]) + self.__Zl = np.array([]) + self.__Zu = np.array([]) + self.__zl = np.array([]) + self.__zu = np.array([]) + self.__cost_ext_fun_type = 'casadi' + # Mayer term + self.__cost_type_e = 'LINEAR_LS' + self.__W_e = np.zeros((0,0)) + self.__Vx_e = np.zeros((0,0)) + self.__yref_e = np.array([]) + self.__Zl_e = np.array([]) + self.__Zu_e = np.array([]) + self.__zl_e = np.array([]) + self.__zu_e = np.array([]) + self.__cost_ext_fun_type_e = 'casadi' + + # initial stage + @property + def cost_type_0(self): + """Cost type at initial shooting node (0) + -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS} or :code:`None`. + Default: :code:`None`. + + .. note:: Cost at initial stage is the same as for intermediate shooting nodes if not set differently explicitly. + + .. note:: If :py:attr:`cost_type_0` is set to :code:`None` values in :py:attr:`W_0`, :py:attr:`Vx_0`, :py:attr:`Vu_0`, :py:attr:`Vz_0` and :py:attr:`yref_0` are ignored (set to :code:`None`). + """ + return self.__cost_type_0 + + @property + def W_0(self): + """:math:`W_0` - weight matrix at initial shooting node (0). + Default: :code:`None`. + """ + return self.__W_0 + + @property + def Vx_0(self): + """:math:`V_x^0` - x matrix coefficient at initial shooting node (0). + Default: :code:`None`. + """ + return self.__Vx_0 + + @property + def Vu_0(self): + """:math:`V_u^0` - u matrix coefficient at initial shooting node (0). + Default: :code:`None`. + """ + return self.__Vu_0 + + @property + def Vz_0(self): + """:math:`V_z^0` - z matrix coefficient at initial shooting node (0). + Default: :code:`None`. + """ + return self.__Vz_0 + + @property + def yref_0(self): + """:math:`y_\\text{ref}^0` - reference at initial shooting node (0). + Default: :code:`None`. + """ + return self.__yref_0 + + @property + def cost_ext_fun_type_0(self): + """Type of external function for cost at initial shooting node (0) + -- string in {casadi, generic} or :code:`None` + Default: :code:'casadi'. + + .. note:: Cost at initial stage is the same as for intermediate shooting nodes if not set differently explicitly. + """ + return self.__cost_ext_fun_type_0 + + @yref_0.setter + def yref_0(self, yref_0): + if isinstance(yref_0, np.ndarray): + self.__yref_0 = yref_0 + else: + raise Exception('Invalid yref_0 value, expected numpy array. Exiting.') + + @W_0.setter + def W_0(self, W_0): + if isinstance(W_0, np.ndarray) and len(W_0.shape) == 2: + self.__W_0 = W_0 + else: + raise Exception('Invalid cost W_0 value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vx_0.setter + def Vx_0(self, Vx_0): + if isinstance(Vx_0, np.ndarray) and len(Vx_0.shape) == 2: + self.__Vx_0 = Vx_0 + else: + raise Exception('Invalid cost Vx_0 value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vu_0.setter + def Vu_0(self, Vu_0): + if isinstance(Vu_0, np.ndarray) and len(Vu_0.shape) == 2: + self.__Vu_0 = Vu_0 + else: + raise Exception('Invalid cost Vu_0 value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vz_0.setter + def Vz_0(self, Vz_0): + if isinstance(Vz_0, np.ndarray) and len(Vz_0.shape) == 2: + self.__Vz_0 = Vz_0 + else: + raise Exception('Invalid cost Vz_0 value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @cost_ext_fun_type_0.setter + def cost_ext_fun_type_0(self, cost_ext_fun_type_0): + if cost_ext_fun_type_0 in ['casadi', 'generic']: + self.__cost_ext_fun_type_0 = cost_ext_fun_type_0 + else: + raise Exception('Invalid cost_ext_fun_type_0 value, expected numpy array. Exiting.') + + # Lagrange term + @property + def cost_type(self): + """ + Cost type at intermediate shooting nodes (1 to N-1) + -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS}. + Default: 'LINEAR_LS'. + """ + return self.__cost_type + + @property + def W(self): + """:math:`W` - weight matrix at intermediate shooting nodes (1 to N-1). + Default: :code:`np.zeros((0,0))`. + """ + return self.__W + + @property + def Vx(self): + """:math:`V_x` - x matrix coefficient at intermediate shooting nodes (1 to N-1). + Default: :code:`np.zeros((0,0))`. + """ + return self.__Vx + + @property + def Vu(self): + """:math:`V_u` - u matrix coefficient at intermediate shooting nodes (1 to N-1). + Default: :code:`np.zeros((0,0))`. + """ + return self.__Vu + + @property + def Vz(self): + """:math:`V_z` - z matrix coefficient at intermediate shooting nodes (1 to N-1). + Default: :code:`np.zeros((0,0))`. + """ + return self.__Vz + + @property + def yref(self): + """:math:`y_\\text{ref}` - reference at intermediate shooting nodes (1 to N-1). + Default: :code:`np.array([])`. + """ + return self.__yref + + @property + def Zl(self): + """:math:`Z_l` - diagonal of Hessian wrt lower slack at intermediate shooting nodes (1 to N-1). + Default: :code:`np.array([])`. + """ + return self.__Zl + + @property + def Zu(self): + """:math:`Z_u` - diagonal of Hessian wrt upper slack at intermediate shooting nodes (1 to N-1). + Default: :code:`np.array([])`. + """ + return self.__Zu + + @property + def zl(self): + """:math:`z_l` - gradient wrt lower slack at intermediate shooting nodes (1 to N-1). + Default: :code:`np.array([])`. + """ + return self.__zl + + @property + def zu(self): + """:math:`z_u` - gradient wrt upper slack at intermediate shooting nodes (1 to N-1). + Default: :code:`np.array([])`. + """ + return self.__zu + + @property + def cost_ext_fun_type(self): + """Type of external function for cost at intermediate shooting nodes (1 to N-1). + -- string in {casadi, generic} + Default: :code:'casadi'. + """ + return self.__cost_ext_fun_type + + @cost_type.setter + def cost_type(self, cost_type): + cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') + if cost_type in cost_types: + self.__cost_type = cost_type + else: + raise Exception('Invalid cost_type value. Exiting.') + + @cost_type_0.setter + def cost_type_0(self, cost_type_0): + cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') + if cost_type_0 in cost_types: + self.__cost_type_0 = cost_type_0 + else: + raise Exception('Invalid cost_type_0 value. Exiting.') + + @W.setter + def W(self, W): + if isinstance(W, np.ndarray) and len(W.shape) == 2: + self.__W = W + else: + raise Exception('Invalid cost W value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + + @Vx.setter + def Vx(self, Vx): + if isinstance(Vx, np.ndarray) and len(Vx.shape) == 2: + self.__Vx = Vx + else: + raise Exception('Invalid cost Vx value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vu.setter + def Vu(self, Vu): + if isinstance(Vu, np.ndarray) and len(Vu.shape) == 2: + self.__Vu = Vu + else: + raise Exception('Invalid cost Vu value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vz.setter + def Vz(self, Vz): + if isinstance(Vz, np.ndarray) and len(Vz.shape) == 2: + self.__Vz = Vz + else: + raise Exception('Invalid cost Vz value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @yref.setter + def yref(self, yref): + if isinstance(yref, np.ndarray): + self.__yref = yref + else: + raise Exception('Invalid yref value, expected numpy array. Exiting.') + + @Zl.setter + def Zl(self, Zl): + if isinstance(Zl, np.ndarray): + self.__Zl = Zl + else: + raise Exception('Invalid Zl value, expected numpy array. Exiting.') + + @Zu.setter + def Zu(self, Zu): + if isinstance(Zu, np.ndarray): + self.__Zu = Zu + else: + raise Exception('Invalid Zu value, expected numpy array. Exiting.') + + @zl.setter + def zl(self, zl): + if isinstance(zl, np.ndarray): + self.__zl = zl + else: + raise Exception('Invalid zl value, expected numpy array. Exiting.') + + @zu.setter + def zu(self, zu): + if isinstance(zu, np.ndarray): + self.__zu = zu + else: + raise Exception('Invalid zu value, expected numpy array. Exiting.') + + @cost_ext_fun_type.setter + def cost_ext_fun_type(self, cost_ext_fun_type): + if cost_ext_fun_type in ['casadi', 'generic']: + self.__cost_ext_fun_type = cost_ext_fun_type + else: + raise Exception('Invalid cost_ext_fun_type value, expected numpy array. Exiting.') + + # Mayer term + @property + def cost_type_e(self): + """ + Cost type at terminal shooting node (N) + -- string in {EXTERNAL, LINEAR_LS, NONLINEAR_LS}. + Default: 'LINEAR_LS'. + """ + return self.__cost_type_e + + @property + def W_e(self): + """:math:`W_e` - weight matrix at terminal shooting node (N). + Default: :code:`np.zeros((0,0))`. + """ + return self.__W_e + + @property + def Vx_e(self): + """:math:`V_x^e` - x matrix coefficient for cost at terminal shooting node (N). + Default: :code:`np.zeros((0,0))`. + """ + return self.__Vx_e + + @property + def yref_e(self): + """:math:`y_\\text{ref}^e` - cost reference at terminal shooting node (N). + Default: :code:`np.array([])`. + """ + return self.__yref_e + + @property + def Zl_e(self): + """:math:`Z_l^e` - diagonal of Hessian wrt lower slack at terminal shooting node (N). + Default: :code:`np.array([])`. + """ + return self.__Zl_e + + @property + def Zu_e(self): + """:math:`Z_u^e` - diagonal of Hessian wrt upper slack at terminal shooting node (N). + Default: :code:`np.array([])`. + """ + return self.__Zu_e + + @property + def zl_e(self): + """:math:`z_l^e` - gradient wrt lower slack at terminal shooting node (N). + Default: :code:`np.array([])`. + """ + return self.__zl_e + + @property + def zu_e(self): + """:math:`z_u^e` - gradient wrt upper slack at terminal shooting node (N). + Default: :code:`np.array([])`. + """ + return self.__zu_e + + @property + def cost_ext_fun_type_e(self): + """Type of external function for cost at intermediate shooting nodes (1 to N-1). + -- string in {casadi, generic} + Default: :code:'casadi'. + """ + return self.__cost_ext_fun_type_e + + @cost_type_e.setter + def cost_type_e(self, cost_type_e): + cost_types = ('LINEAR_LS', 'NONLINEAR_LS', 'EXTERNAL') + + if cost_type_e in cost_types: + self.__cost_type_e = cost_type_e + else: + raise Exception('Invalid cost_type_e value. Exiting.') + + @W_e.setter + def W_e(self, W_e): + if isinstance(W_e, np.ndarray) and len(W_e.shape) == 2: + self.__W_e = W_e + else: + raise Exception('Invalid cost W_e value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @Vx_e.setter + def Vx_e(self, Vx_e): + if isinstance(Vx_e, np.ndarray) and len(Vx_e.shape) == 2: + self.__Vx_e = Vx_e + else: + raise Exception('Invalid cost Vx_e value. ' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @yref_e.setter + def yref_e(self, yref_e): + if isinstance(yref_e, np.ndarray): + self.__yref_e = yref_e + else: + raise Exception('Invalid yref_e value, expected numpy array. Exiting.') + + @Zl_e.setter + def Zl_e(self, Zl_e): + if isinstance(Zl_e, np.ndarray): + self.__Zl_e = Zl_e + else: + raise Exception('Invalid Zl_e value, expected numpy array. Exiting.') + + @Zu_e.setter + def Zu_e(self, Zu_e): + if isinstance(Zu_e, np.ndarray): + self.__Zu_e = Zu_e + else: + raise Exception('Invalid Zu_e value, expected numpy array. Exiting.') + + @zl_e.setter + def zl_e(self, zl_e): + if isinstance(zl_e, np.ndarray): + self.__zl_e = zl_e + else: + raise Exception('Invalid zl_e value, expected numpy array. Exiting.') + + @zu_e.setter + def zu_e(self, zu_e): + if isinstance(zu_e, np.ndarray): + self.__zu_e = zu_e + else: + raise Exception('Invalid zu_e value, expected numpy array. Exiting.') + + @cost_ext_fun_type_e.setter + def cost_ext_fun_type_e(self, cost_ext_fun_type_e): + if cost_ext_fun_type_e in ['casadi', 'generic']: + self.__cost_ext_fun_type_e = cost_ext_fun_type_e + else: + raise Exception('Invalid cost_ext_fun_type_e value, expected numpy array. Exiting.') + + def set(self, attr, value): + setattr(self, attr, value) + + +def print_J_to_idx_note(): + print("NOTE: J* matrix is converted to zero based vector idx* vector, which is returned here.") + + +class AcadosOcpConstraints: + """ + class containing the description of the constraints + """ + def __init__(self): + self.__constr_type = 'BGH' + self.__constr_type_e = 'BGH' + # initial x + self.__lbx_0 = np.array([]) + self.__ubx_0 = np.array([]) + self.__idxbx_0 = np.array([]) + self.__idxbxe_0 = np.array([]) + # state bounds + self.__lbx = np.array([]) + self.__ubx = np.array([]) + self.__idxbx = np.array([]) + # bounds on x at shooting node N + self.__lbx_e = np.array([]) + self.__ubx_e = np.array([]) + self.__idxbx_e = np.array([]) + # bounds on u + self.__lbu = np.array([]) + self.__ubu = np.array([]) + self.__idxbu = np.array([]) + # polytopic constraints + self.__lg = np.array([]) + self.__ug = np.array([]) + self.__D = np.zeros((0,0)) + self.__C = np.zeros((0,0)) + # polytopic constraints at shooting node N + self.__C_e = np.zeros((0,0)) + self.__lg_e = np.array([]) + self.__ug_e = np.array([]) + # nonlinear constraints + self.__lh = np.array([]) + self.__uh = np.array([]) + # nonlinear constraints at shooting node N + self.__uh_e = np.array([]) + self.__lh_e = np.array([]) + # convex-over-nonlinear constraints + self.__lphi = np.array([]) + self.__uphi = np.array([]) + # nonlinear constraints at shooting node N + self.__uphi_e = np.array([]) + self.__lphi_e = np.array([]) + # SLACK BOUNDS + # soft bounds on x + self.__lsbx = np.array([]) + self.__usbx = np.array([]) + self.__idxsbx = np.array([]) + # soft bounds on u + self.__lsbu = np.array([]) + self.__usbu = np.array([]) + self.__idxsbu = np.array([]) + # soft bounds on x at shooting node N + self.__lsbx_e = np.array([]) + self.__usbx_e = np.array([]) + self.__idxsbx_e= np.array([]) + # soft bounds on general linear constraints + self.__lsg = np.array([]) + self.__usg = np.array([]) + self.__idxsg = np.array([]) + # soft bounds on nonlinear constraints + self.__lsh = np.array([]) + self.__ush = np.array([]) + self.__idxsh = np.array([]) + # soft bounds on nonlinear constraints + self.__lsphi = np.array([]) + self.__usphi = np.array([]) + self.__idxsphi = np.array([]) + # soft bounds on general linear constraints at shooting node N + self.__lsg_e = np.array([]) + self.__usg_e = np.array([]) + self.__idxsg_e = np.array([]) + # soft bounds on nonlinear constraints at shooting node N + self.__lsh_e = np.array([]) + self.__ush_e = np.array([]) + self.__idxsh_e = np.array([]) + # soft bounds on nonlinear constraints at shooting node N + self.__lsphi_e = np.array([]) + self.__usphi_e = np.array([]) + self.__idxsphi_e = np.array([]) + + + # types + @property + def constr_type(self): + """Constraints type for shooting nodes (0 to N-1). string in {BGH, BGP}. + Default: BGH; BGP is for convex over nonlinear.""" + return self.__constr_type + + @property + def constr_type_e(self): + """Constraints type for terminal shooting node N. string in {BGH, BGP}. + Default: BGH; BGP is for convex over nonlinear.""" + return self.__constr_type_e + + # initial bounds on x + @property + def lbx_0(self): + """:math:`\\underline{x_0}` - lower bounds on x at initial stage 0. + Type: :code:`np.ndarray`; default: :code:`np.array([])`.""" + return self.__lbx_0 + + @property + def ubx_0(self): + """:math:`\\bar{x_0}` - upper bounds on x at initial stage 0. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__ubx_0 + + @property + def Jbx_0(self): + """:math:`J_{bx,0}` - matrix coefficient for bounds on x at initial stage 0. + Translated internally to :py:attr:`idxbx_0`""" + print_J_to_idx_note() + return self.__idxbx_0 + + @property + def idxbx_0(self): + """Indices of bounds on x at initial stage 0 + -- can be set automatically via x0. + Can be set by using :py:attr:`Jbx_0`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxbx_0 + + @property + def idxbxe_0(self): + """Indices of bounds on x0 that are equalities -- can be set automatically via :py:attr:`x0`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxbxe_0 + + # bounds on x + @property + def lbx(self): + """:math:`\\underline{x}` - lower bounds on x at intermediate shooting nodes (1 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__lbx + + @property + def ubx(self): + """:math:`\\bar{x}` - upper bounds on x at intermediate shooting nodes (1 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__ubx + + @property + def idxbx(self): + """indices of bounds on x (defines :math:`J_{bx}`) at intermediate shooting nodes (1 to N-1). + Can be set by using :py:attr:`Jbx`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxbx + + @property + def Jbx(self): + """:math:`J_{bx}` - matrix coefficient for bounds on x + at intermediate shooting nodes (1 to N-1). + Translated internally into :py:attr:`idxbx`.""" + print_J_to_idx_note() + return self.__idxbx + + # bounds on x at shooting node N + @property + def lbx_e(self): + """:math:`\\underline{x}^e` - lower bounds on x at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__lbx_e + + @property + def ubx_e(self): + """:math:`\\bar{x}^e` - upper bounds on x at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__ubx_e + + @property + def idxbx_e(self): + """Indices for bounds on x at terminal shooting node N (defines :math:`J_{bx}^e`). + Can be set by using :py:attr:`Jbx_e`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxbx_e + + @property + def Jbx_e(self): + """:math:`J_{bx}^e` matrix coefficient for bounds on x at terminal shooting node N. + Translated internally into :py:attr:`idxbx_e`.""" + print_J_to_idx_note() + return self.__idxbx_e + + # bounds on u + @property + def lbu(self): + """:math:`\\underline{u}` - lower bounds on u at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])` + """ + return self.__lbu + + @property + def ubu(self): + """:math:`\\bar{u}` - upper bounds on u at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])` + """ + return self.__ubu + + @property + def idxbu(self): + """Indices of bounds on u (defines :math:`J_{bu}`) at shooting nodes (0 to N-1). + Can be set by using :py:attr:`Jbu`. + Type: :code:`np.ndarray`; default: :code:`np.array([])` + """ + return self.__idxbu + + @property + def Jbu(self): + """:math:`J_{bu}` - matrix coefficient for bounds on u at shooting nodes (0 to N-1). + Translated internally to :py:attr:`idxbu`. + """ + print_J_to_idx_note() + return self.__idxbu + + # polytopic constraints + @property + def C(self): + """:math:`C` - C matrix in :math:`\\underline{g} \\leq D \, u + C \, x \\leq \\bar{g}` + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array((0,0))`. + """ + return self.__C + + @property + def D(self): + """:math:`D` - D matrix in :math:`\\underline{g} \\leq D \, u + C \, x \\leq \\bar{g}` + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array((0,0))` + """ + return self.__D + + @property + def lg(self): + """:math:`\\underline{g}` - lower bound for general polytopic inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])` + """ + return self.__lg + + @property + def ug(self): + """:math:`\\bar{g}` - upper bound for general polytopic inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__ug + + # polytopic constraints at shooting node N + @property + def C_e(self): + """:math:`C^e` - C matrix at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array((0,0))`. + """ + return self.__C_e + + @property + def lg_e(self): + """:math:`\\underline{g}^e` - lower bound on general polytopic inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__lg_e + + @property + def ug_e(self): + """:math:`\\bar{g}^e` - upper bound on general polytopic inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__ug_e + + + # nonlinear constraints + @property + def lh(self): + """:math:`\\underline{h}` - lower bound for nonlinear inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__lh + + @property + def uh(self): + """:math:`\\bar{h}` - upper bound for nonlinear inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__uh + + # nonlinear constraints at shooting node N + @property + def lh_e(self): + """:math:`\\underline{h}^e` - lower bound on nonlinear inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__lh_e + + @property + def uh_e(self): + """:math:`\\bar{h}^e` - upper bound on nonlinear inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__uh_e + + # convex-over-nonlinear constraints + @property + def lphi(self): + """:math:`\\underline{\phi}` - lower bound for convex-over-nonlinear inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__lphi + + @property + def uphi(self): + """:math:`\\bar{\phi}` - upper bound for convex-over-nonlinear inequalities + at shooting nodes (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__uphi + + # convex-over-nonlinear constraints at shooting node N + @property + def lphi_e(self): + """:math:`\\underline{\phi}^e` - lower bound on convex-over-nonlinear inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__lphi_e + + @property + def uphi_e(self): + """:math:`\\bar{\phi}^e` - upper bound on convex-over-nonlinear inequalities + at terminal shooting node N. + Type: :code:`np.ndarray`; default: :code:`np.array([])`. + """ + return self.__uphi_e + + + # SLACK bounds + # soft bounds on x + @property + def lsbx(self): + """Lower bounds on slacks corresponding to soft lower bounds on x + at stages (1 to N-1); + not required - zeros by default""" + return self.__lsbx + + @property + def usbx(self): + """Lower bounds on slacks corresponding to soft upper bounds on x + at stages (1 to N-1); + not required - zeros by default""" + return self.__usbx + + @property + def idxsbx(self): + """Indices of soft bounds on x within the indices of bounds on x + at stages (1 to N-1). + Can be set by using :py:attr:`Jsbx`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsbx + + @property + def Jsbx(self): + """:math:`J_{sbx}` - matrix coefficient for soft bounds on x + at stages (1 to N-1); + Translated internally into :py:attr:`idxsbx`.""" + print_J_to_idx_note() + return self.__idxsbx + + # soft bounds on u + @property + def lsbu(self): + """Lower bounds on slacks corresponding to soft lower bounds on u + at stages (0 to N-1). + Not required - zeros by default.""" + return self.__lsbu + + @property + def usbu(self): + """Lower bounds on slacks corresponding to soft upper bounds on u + at stages (0 to N-1); + not required - zeros by default""" + return self.__usbu + + @property + def idxsbu(self): + """Indices of soft bounds on u within the indices of bounds on u + at stages (0 to N-1). + Can be set by using :py:attr:`Jsbu`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsbu + + @property + def Jsbu(self): + """:math:`J_{sbu}` - matrix coefficient for soft bounds on u + at stages (0 to N-1); + internally translated into :py:attr:`idxsbu`""" + print_J_to_idx_note() + return self.__idxsbu + + # soft bounds on x at shooting node N + @property + def lsbx_e(self): + """Lower bounds on slacks corresponding to soft lower bounds on x at shooting node N. + Not required - zeros by default""" + return self.__lsbx_e + + @property + def usbx_e(self): + """Lower bounds on slacks corresponding to soft upper bounds on x at shooting node N. + Not required - zeros by default""" + return self.__usbx_e + + @property + def idxsbx_e(self): + """Indices of soft bounds on x at shooting node N, within the indices of bounds on x at terminal shooting node N. + Can be set by using :py:attr:`Jsbx_e`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsbx_e + + @property + def Jsbx_e(self): + """:math:`J_{sbx}^e` - matrix coefficient for soft bounds on x at terminal shooting node N. + Translated internally to :py:attr:`idxsbx_e`""" + print_J_to_idx_note() + return self.__idxsbx_e + + # soft general linear constraints + @property + def lsg(self): + """Lower bounds on slacks corresponding to soft lower bounds for general linear constraints + at stages (0 to N-1). + Type: :code:`np.ndarray`; default: :code:`np.array([])` + """ + return self.__lsg + + @property + def usg(self): + """Lower bounds on slacks corresponding to soft upper bounds for general linear constraints. + Not required - zeros by default""" + return self.__usg + + @property + def idxsg(self): + """Indices of soft general linear constraints within the indices of general linear constraints. + Can be set by using :py:attr:`Jsg`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsg + + @property + def Jsg(self): + """:math:`J_{sg}` - matrix coefficient for soft bounds on general linear constraints. + Translated internally to :py:attr:`idxsg`""" + print_J_to_idx_note() + return self.__idxsg + + # soft nonlinear constraints + @property + def lsh(self): + """Lower bounds on slacks corresponding to soft lower bounds for nonlinear constraints. + Not required - zeros by default""" + return self.__lsh + + @property + def ush(self): + """Lower bounds on slacks corresponding to soft upper bounds for nonlinear constraints. + Not required - zeros by default""" + return self.__ush + + @property + def idxsh(self): + """Indices of soft nonlinear constraints within the indices of nonlinear constraints. + Can be set by using :py:attr:`Jbx`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsh + + @property + def Jsh(self): + """:math:`J_{sh}` - matrix coefficient for soft bounds on nonlinear constraints. + Translated internally to :py:attr:`idxsh`""" + print_J_to_idx_note() + return self.__idxsh + + # soft bounds on convex-over-nonlinear constraints + @property + def lsphi(self): + """Lower bounds on slacks corresponding to soft lower bounds for convex-over-nonlinear constraints. + Not required - zeros by default""" + return self.__lsphi + + @property + def usphi(self): + """Lower bounds on slacks corresponding to soft upper bounds for convex-over-nonlinear constraints. + Not required - zeros by default""" + return self.__usphi + + @property + def idxsphi(self): + """Indices of soft convex-over-nonlinear constraints within the indices of nonlinear constraints. + Can be set by using :py:attr:`Jsphi`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsphi + + @property + def Jsphi(self): + """:math:`J_{s, \phi}` - matrix coefficient for soft bounds on convex-over-nonlinear constraints. + Translated internally into :py:attr:`idxsphi`.""" + print_J_to_idx_note() + return self.__idxsphi + + + # soft bounds on general linear constraints at shooting node N + @property + def lsg_e(self): + """Lower bounds on slacks corresponding to soft lower bounds for general linear constraints at shooting node N. + Not required - zeros by default""" + return self.__lsg_e + + @property + def usg_e(self): + """Lower bounds on slacks corresponding to soft upper bounds for general linear constraints at shooting node N. + Not required - zeros by default""" + return self.__usg_e + + @property + def idxsg_e(self): + """Indices of soft general linear constraints at shooting node N within the indices of general linear constraints at shooting node N. + Can be set by using :py:attr:`Jsg_e`.""" + return self.__idxsg_e + + @property + def Jsg_e(self): + """:math:`J_{s,h}^e` - matrix coefficient for soft bounds on general linear constraints at terminal shooting node N. + Translated internally to :py:attr:`idxsg_e`""" + print_J_to_idx_note() + return self.__idxsg_e + + + # soft bounds on nonlinear constraints at shooting node N + @property + def lsh_e(self): + """Lower bounds on slacks corresponding to soft lower bounds for nonlinear constraints at terminal shooting node N. + Not required - zeros by default""" + return self.__lsh_e + + @property + def ush_e(self): + """Lower bounds on slacks corresponding to soft upper bounds for nonlinear constraints at terminal shooting node N. + Not required - zeros by default""" + return self.__ush_e + + @property + def idxsh_e(self): + """Indices of soft nonlinear constraints at shooting node N within the indices of nonlinear constraints at terminal shooting node N. + Can be set by using :py:attr:`Jsh_e`.""" + return self.__idxsh_e + + @property + def Jsh_e(self): + """:math:`J_{s,h}^e` - matrix coefficient for soft bounds on nonlinear constraints at terminal shooting node N; fills :py:attr:`idxsh_e`""" + print_J_to_idx_note() + return self.__idxsh_e + + # soft bounds on convex-over-nonlinear constraints at shooting node N + @property + def lsphi_e(self): + """Lower bounds on slacks corresponding to soft lower bounds for convex-over-nonlinear constraints at terminal shooting node N. + Not required - zeros by default""" + return self.__lsphi_e + + @property + def usphi_e(self): + """Lower bounds on slacks corresponding to soft upper bounds for convex-over-nonlinear constraints at terminal shooting node N. + Not required - zeros by default""" + return self.__usphi_e + + @property + def idxsphi_e(self): + """Indices of soft nonlinear constraints at shooting node N within the indices of nonlinear constraints at terminal shooting node N. + Can be set by using :py:attr:`Jsphi_e`. + Type: :code:`np.ndarray`; default: :code:`np.array([])`""" + return self.__idxsphi_e + + @property + def Jsphi_e(self): + """:math:`J_{sh}^e` - matrix coefficient for soft bounds on convex-over-nonlinear constraints at shooting node N. + Translated internally to :py:attr:`idxsphi_e`""" + print_J_to_idx_note() + return self.__idxsphi_e + + @property + def x0(self): + """:math:`x_0 \\in \mathbb{R}^{n_x}` - initial state -- + Translated internally to :py:attr:`idxbx_0`, :py:attr:`lbx_0`, :py:attr:`ubx_0`, :py:attr:`idxbxe_0` """ + print("x0 is converted to lbx_0, ubx_0, idxbx_0") + print("idxbx_0: ", self.__idxbx_0) + print("lbx_0: ", self.__lbx_0) + print("ubx_0: ", self.__ubx_0) + print("idxbxe_0: ", self.__idxbxe_0) + return None + + # SETTERS + @constr_type.setter + def constr_type(self, constr_type): + constr_types = ('BGH', 'BGP') + if constr_type in constr_types: + self.__constr_type = constr_type + else: + raise Exception('Invalid constr_type value. Possible values are:\n\n' \ + + ',\n'.join(constr_types) + '.\n\nYou have: ' + constr_type + '.\n\nExiting.') + + @constr_type_e.setter + def constr_type_e(self, constr_type_e): + constr_types = ('BGH', 'BGP') + if constr_type_e in constr_types: + self.__constr_type_e = constr_type_e + else: + raise Exception('Invalid constr_type_e value. Possible values are:\n\n' \ + + ',\n'.join(constr_types) + '.\n\nYou have: ' + constr_type_e + '.\n\nExiting.') + + # initial x + @lbx_0.setter + def lbx_0(self, lbx_0): + if type(lbx_0) == np.ndarray: + self.__lbx_0 = lbx_0 + else: + raise Exception('Invalid lbx_0 value. Exiting.') + + @ubx_0.setter + def ubx_0(self, ubx_0): + if type(ubx_0) == np.ndarray: + self.__ubx_0 = ubx_0 + else: + raise Exception('Invalid ubx_0 value. Exiting.') + + @idxbx_0.setter + def idxbx_0(self, idxbx_0): + if isinstance(idxbx_0, np.ndarray): + self.__idxbx_0 = idxbx_0 + else: + raise Exception('Invalid idxbx_0 value. Exiting.') + + @Jbx_0.setter + def Jbx_0(self, Jbx_0): + if type(Jbx_0) == np.ndarray: + self.__idxbx_0 = J_to_idx(Jbx_0) + else: + raise Exception('Invalid Jbx_0 value. Exiting.') + + @idxbxe_0.setter + def idxbxe_0(self, idxbxe_0): + if isinstance(idxbxe_0, np.ndarray): + self.__idxbxe_0 = idxbxe_0 + else: + raise Exception('Invalid idxbxe_0 value. Exiting.') + + + @x0.setter + def x0(self, x0): + if isinstance(x0, np.ndarray): + self.__lbx_0 = x0 + self.__ubx_0 = x0 + self.__idxbx_0 = np.arange(x0.size) + self.__idxbxe_0 = np.arange(x0.size) + else: + raise Exception('Invalid x0 value. Exiting.') + + # bounds on x + @lbx.setter + def lbx(self, lbx): + if type(lbx) == np.ndarray: + self.__lbx = lbx + else: + raise Exception('Invalid lbx value. Exiting.') + + @ubx.setter + def ubx(self, ubx): + if type(ubx) == np.ndarray: + self.__ubx = ubx + else: + raise Exception('Invalid ubx value. Exiting.') + + @idxbx.setter + def idxbx(self, idxbx): + if type(idxbx) == np.ndarray: + self.__idxbx = idxbx + else: + raise Exception('Invalid idxbx value. Exiting.') + + @Jbx.setter + def Jbx(self, Jbx): + if type(Jbx) == np.ndarray: + self.__idxbx = J_to_idx(Jbx) + else: + raise Exception('Invalid Jbx value. Exiting.') + + # bounds on u + @lbu.setter + def lbu(self, lbu): + if type(lbu) == np.ndarray: + self.__lbu = lbu + else: + raise Exception('Invalid lbu value. Exiting.') + + @ubu.setter + def ubu(self, ubu): + if type(ubu) == np.ndarray: + self.__ubu = ubu + else: + raise Exception('Invalid ubu value. Exiting.') + + @idxbu.setter + def idxbu(self, idxbu): + if type(idxbu) == np.ndarray: + self.__idxbu = idxbu + else: + raise Exception('Invalid idxbu value. Exiting.') + + @Jbu.setter + def Jbu(self, Jbu): + if type(Jbu) == np.ndarray: + self.__idxbu = J_to_idx(Jbu) + else: + raise Exception('Invalid Jbu value. Exiting.') + + # bounds on x at shooting node N + @lbx_e.setter + def lbx_e(self, lbx_e): + if type(lbx_e) == np.ndarray: + self.__lbx_e = lbx_e + else: + raise Exception('Invalid lbx_e value. Exiting.') + + @ubx_e.setter + def ubx_e(self, ubx_e): + if type(ubx_e) == np.ndarray: + self.__ubx_e = ubx_e + else: + raise Exception('Invalid ubx_e value. Exiting.') + + @idxbx_e.setter + def idxbx_e(self, idxbx_e): + if type(idxbx_e) == np.ndarray: + self.__idxbx_e = idxbx_e + else: + raise Exception('Invalid idxbx_e value. Exiting.') + + @Jbx_e.setter + def Jbx_e(self, Jbx_e): + if type(Jbx_e) == np.ndarray: + self.__idxbx_e = J_to_idx(Jbx_e) + else: + raise Exception('Invalid Jbx_e value. Exiting.') + + # polytopic constraints + @D.setter + def D(self, D): + if isinstance(D, np.ndarray) and len(D.shape) == 2: + self.__D = D + else: + raise Exception('Invalid constraint D value.' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @C.setter + def C(self, C): + if isinstance(C, np.ndarray) and len(C.shape) == 2: + self.__C = C + else: + raise Exception('Invalid constraint C value.' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @lg.setter + def lg(self, lg): + if type(lg) == np.ndarray: + self.__lg = lg + else: + raise Exception('Invalid lg value. Exiting.') + + @ug.setter + def ug(self, ug): + if type(ug) == np.ndarray: + self.__ug = ug + else: + raise Exception('Invalid ug value. Exiting.') + + # polytopic constraints at shooting node N + @C_e.setter + def C_e(self, C_e): + if isinstance(C_e, np.ndarray) and len(C_e.shape) == 2: + self.__C_e = C_e + else: + raise Exception('Invalid constraint C_e value.' \ + + 'Should be 2 dimensional numpy array. Exiting.') + + @lg_e.setter + def lg_e(self, lg_e): + if type(lg_e) == np.ndarray: + self.__lg_e = lg_e + else: + raise Exception('Invalid lg_e value. Exiting.') + + @ug_e.setter + def ug_e(self, ug_e): + if type(ug_e) == np.ndarray: + self.__ug_e = ug_e + else: + raise Exception('Invalid ug_e value. Exiting.') + + # nonlinear constraints + @lh.setter + def lh(self, lh): + if type(lh) == np.ndarray: + self.__lh = lh + else: + raise Exception('Invalid lh value. Exiting.') + + @uh.setter + def uh(self, uh): + if type(uh) == np.ndarray: + self.__uh = uh + else: + raise Exception('Invalid uh value. Exiting.') + + # convex-over-nonlinear constraints + @lphi.setter + def lphi(self, lphi): + if type(lphi) == np.ndarray: + self.__lphi = lphi + else: + raise Exception('Invalid lphi value. Exiting.') + + @uphi.setter + def uphi(self, uphi): + if type(uphi) == np.ndarray: + self.__uphi = uphi + else: + raise Exception('Invalid uphi value. Exiting.') + + # nonlinear constraints at shooting node N + @lh_e.setter + def lh_e(self, lh_e): + if type(lh_e) == np.ndarray: + self.__lh_e = lh_e + else: + raise Exception('Invalid lh_e value. Exiting.') + + @uh_e.setter + def uh_e(self, uh_e): + if type(uh_e) == np.ndarray: + self.__uh_e = uh_e + else: + raise Exception('Invalid uh_e value. Exiting.') + + # convex-over-nonlinear constraints at shooting node N + @lphi_e.setter + def lphi_e(self, lphi_e): + if type(lphi_e) == np.ndarray: + self.__lphi_e = lphi_e + else: + raise Exception('Invalid lphi_e value. Exiting.') + + @uphi_e.setter + def uphi_e(self, uphi_e): + if type(uphi_e) == np.ndarray: + self.__uphi_e = uphi_e + else: + raise Exception('Invalid uphi_e value. Exiting.') + + # SLACK bounds + # soft bounds on x + @lsbx.setter + def lsbx(self, lsbx): + if type(lsbx) == np.ndarray: + self.__lsbx = lsbx + else: + raise Exception('Invalid lsbx value. Exiting.') + + @usbx.setter + def usbx(self, usbx): + if type(usbx) == np.ndarray: + self.__usbx = usbx + else: + raise Exception('Invalid usbx value. Exiting.') + + @idxsbx.setter + def idxsbx(self, idxsbx): + if type(idxsbx) == np.ndarray: + self.__idxsbx = idxsbx + else: + raise Exception('Invalid idxsbx value. Exiting.') + + @Jsbx.setter + def Jsbx(self, Jsbx): + if isinstance(Jsbx, np.ndarray): + self.__idxsbx = J_to_idx_slack(Jsbx) + else: + raise Exception('Invalid Jsbx value, expected numpy array. Exiting.') + + # soft bounds on u + @lsbu.setter + def lsbu(self, lsbu): + if type(lsbu) == np.ndarray: + self.__lsbu = lsbu + else: + raise Exception('Invalid lsbu value. Exiting.') + + @usbu.setter + def usbu(self, usbu): + if type(usbu) == np.ndarray: + self.__usbu = usbu + else: + raise Exception('Invalid usbu value. Exiting.') + + @idxsbu.setter + def idxsbu(self, idxsbu): + if type(idxsbu) == np.ndarray: + self.__idxsbu = idxsbu + else: + raise Exception('Invalid idxsbu value. Exiting.') + + @Jsbu.setter + def Jsbu(self, Jsbu): + if type(Jsbu) == np.ndarray: + self.__idxsbu = J_to_idx_slack(Jsbu) + else: + raise Exception('Invalid Jsbu value. Exiting.') + + # soft bounds on x at shooting node N + @lsbx_e.setter + def lsbx_e(self, lsbx_e): + if type(lsbx_e) == np.ndarray: + self.__lsbx_e = lsbx_e + else: + raise Exception('Invalid lsbx_e value. Exiting.') + + @usbx_e.setter + def usbx_e(self, usbx_e): + if type(usbx_e) == np.ndarray: + self.__usbx_e = usbx_e + else: + raise Exception('Invalid usbx_e value. Exiting.') + + @idxsbx_e.setter + def idxsbx_e(self, idxsbx_e): + if type(idxsbx_e) == np.ndarray: + self.__idxsbx_e = idxsbx_e + else: + raise Exception('Invalid idxsbx_e value. Exiting.') + + @Jsbx_e.setter + def Jsbx_e(self, Jsbx_e): + if type(Jsbx_e) == np.ndarray: + self.__idxsbx_e = J_to_idx_slack(Jsbx_e) + else: + raise Exception('Invalid Jsbx_e value. Exiting.') + + + # soft bounds on general linear constraints + @lsg.setter + def lsg(self, lsg): + if isinstance(lsg, np.ndarray): + self.__lsg = lsg + else: + raise Exception('Invalid lsg value. Exiting.') + + @usg.setter + def usg(self, usg): + if isinstance(usg, np.ndarray): + self.__usg = usg + else: + raise Exception('Invalid usg value. Exiting.') + + @idxsg.setter + def idxsg(self, idxsg): + if isinstance(idxsg, np.ndarray): + self.__idxsg = idxsg + else: + raise Exception('Invalid idxsg value. Exiting.') + + @Jsg.setter + def Jsg(self, Jsg): + if isinstance(Jsg, np.ndarray): + self.__idxsg = J_to_idx_slack(Jsg) + else: + raise Exception('Invalid Jsg value, expected numpy array. Exiting.') + + + # soft bounds on nonlinear constraints + @lsh.setter + def lsh(self, lsh): + if type(lsh) == np.ndarray: + self.__lsh = lsh + else: + raise Exception('Invalid lsh value. Exiting.') + + @ush.setter + def ush(self, ush): + if type(ush) == np.ndarray: + self.__ush = ush + else: + raise Exception('Invalid ush value. Exiting.') + + @idxsh.setter + def idxsh(self, idxsh): + if type(idxsh) == np.ndarray: + self.__idxsh = idxsh + else: + raise Exception('Invalid idxsh value. Exiting.') + + + @Jsh.setter + def Jsh(self, Jsh): + if isinstance(Jsh, np.ndarray): + self.__idxsh = J_to_idx_slack(Jsh) + else: + raise Exception('Invalid Jsh value, expected numpy array. Exiting.') + + # soft bounds on convex-over-nonlinear constraints + @lsphi.setter + def lsphi(self, lsphi): + if type(lsphi) == np.ndarray: + self.__lsphi = lsphi + else: + raise Exception('Invalid lsphi value. Exiting.') + + @usphi.setter + def usphi(self, usphi): + if type(usphi) == np.ndarray: + self.__usphi = usphi + else: + raise Exception('Invalid usphi value. Exiting.') + + @idxsphi.setter + def idxsphi(self, idxsphi): + if type(idxsphi) == np.ndarray: + self.__idxsphi = idxsphi + else: + raise Exception('Invalid idxsphi value. Exiting.') + + @Jsphi.setter + def Jsphi(self, Jsphi): + if isinstance(Jsphi, np.ndarray): + self.__idxsphi = J_to_idx_slack(Jsphi) + else: + raise Exception('Invalid Jsphi value, expected numpy array. Exiting.') + + # soft bounds on general linear constraints at shooting node N + @lsg_e.setter + def lsg_e(self, lsg_e): + if isinstance(lsg_e, np.ndarray): + self.__lsg_e = lsg_e + else: + raise Exception('Invalid lsg_e value. Exiting.') + + @usg_e.setter + def usg_e(self, usg_e): + if isinstance(usg_e, np.ndarray): + self.__usg_e = usg_e + else: + raise Exception('Invalid usg_e value. Exiting.') + + @idxsg_e.setter + def idxsg_e(self, idxsg_e): + if isinstance(idxsg_e, np.ndarray): + self.__idxsg_e = idxsg_e + else: + raise Exception('Invalid idxsg_e value. Exiting.') + + @Jsg_e.setter + def Jsg_e(self, Jsg_e): + if isinstance(Jsg_e, np.ndarray): + self.__idxsg_e = J_to_idx_slack(Jsg_e) + else: + raise Exception('Invalid Jsg_e value, expected numpy array. Exiting.') + + # soft bounds on nonlinear constraints at shooting node N + @lsh_e.setter + def lsh_e(self, lsh_e): + if isinstance(lsh_e, np.ndarray): + self.__lsh_e = lsh_e + else: + raise Exception('Invalid lsh_e value. Exiting.') + + @ush_e.setter + def ush_e(self, ush_e): + if isinstance(ush_e, np.ndarray): + self.__ush_e = ush_e + else: + raise Exception('Invalid ush_e value. Exiting.') + + @idxsh_e.setter + def idxsh_e(self, idxsh_e): + if isinstance(idxsh_e, np.ndarray): + self.__idxsh_e = idxsh_e + else: + raise Exception('Invalid idxsh_e value. Exiting.') + + @Jsh_e.setter + def Jsh_e(self, Jsh_e): + if isinstance(Jsh_e, np.ndarray): + self.__idxsh_e = J_to_idx_slack(Jsh_e) + else: + raise Exception('Invalid Jsh_e value, expected numpy array. Exiting.') + + + # soft bounds on convex-over-nonlinear constraints at shooting node N + @lsphi_e.setter + def lsphi_e(self, lsphi_e): + if isinstance(lsphi_e, np.ndarray): + self.__lsphi_e = lsphi_e + else: + raise Exception('Invalid lsphi_e value. Exiting.') + + @usphi_e.setter + def usphi_e(self, usphi_e): + if isinstance(usphi_e, np.ndarray): + self.__usphi_e = usphi_e + else: + raise Exception('Invalid usphi_e value. Exiting.') + + @idxsphi_e.setter + def idxsphi_e(self, idxsphi_e): + if isinstance(idxsphi_e, np.ndarray): + self.__idxsphi_e = idxsphi_e + else: + raise Exception('Invalid idxsphi_e value. Exiting.') + + @Jsphi_e.setter + def Jsphi_e(self, Jsphi_e): + if isinstance(Jsphi_e, np.ndarray): + self.__idxsphi_e = J_to_idx_slack(Jsphi_e) + else: + raise Exception('Invalid Jsphi_e value. Exiting.') + + def set(self, attr, value): + setattr(self, attr, value) + + +class AcadosOcpOptions: + """ + class containing the description of the solver options + """ + def __init__(self): + self.__qp_solver = 'PARTIAL_CONDENSING_HPIPM' # qp solver to be used in the NLP solver + self.__hessian_approx = 'GAUSS_NEWTON' # hessian approximation + self.__integrator_type = 'ERK' # integrator type + self.__tf = None # prediction horizon + self.__nlp_solver_type = 'SQP_RTI' # NLP solver + self.__globalization = 'FIXED_STEP' + self.__nlp_solver_step_length = 1.0 # fixed Newton step length + self.__levenberg_marquardt = 0.0 + self.__sim_method_num_stages = 4 # number of stages in the integrator + self.__sim_method_num_steps = 1 # number of steps in the integrator + self.__sim_method_newton_iter = 3 # number of Newton iterations in simulation method + self.__sim_method_jac_reuse = False + self.__qp_solver_tol_stat = None # QP solver stationarity tolerance + self.__qp_solver_tol_eq = None # QP solver equality tolerance + self.__qp_solver_tol_ineq = None # QP solver inequality + self.__qp_solver_tol_comp = None # QP solver complementarity + self.__qp_solver_iter_max = 50 # QP solver max iter + self.__qp_solver_cond_N = None # QP solver: new horizon after partial condensing + self.__qp_solver_warm_start = 0 + self.__nlp_solver_tol_stat = 1e-6 # NLP solver stationarity tolerance + self.__nlp_solver_tol_eq = 1e-6 # NLP solver equality tolerance + self.__nlp_solver_tol_ineq = 1e-6 # NLP solver inequality + self.__nlp_solver_tol_comp = 1e-6 # NLP solver complementarity + self.__nlp_solver_max_iter = 100 # NLP solver maximum number of iterations + self.__Tsim = None # automatically calculated as tf/N + self.__print_level = 0 # print level + self.__initialize_t_slacks = 0 # possible values: 0, 1 + self.__model_external_shared_lib_dir = None # path to the the .so lib + self.__model_external_shared_lib_name = None # name of the the .so lib + self.__regularize_method = None + self.__time_steps = None + self.__shooting_nodes = None + self.__exact_hess_cost = 1 + self.__exact_hess_dyn = 1 + self.__exact_hess_constr = 1 + self.__ext_cost_num_hess = 0 + self.__alpha_min = 0.05 + self.__alpha_reduction = 0.7 + + + @property + def qp_solver(self): + """QP solver to be used in the NLP solver. + String in ('PARTIAL_CONDENSING_HPIPM', 'FULL_CONDENSING_QPOASES', 'FULL_CONDENSING_HPIPM', 'PARTIAL_CONDENSING_QPDUNES', 'PARTIAL_CONDENSING_OSQP'). + Default: 'PARTIAL_CONDENSING_HPIPM'. + """ + return self.__qp_solver + + @property + def hessian_approx(self): + """Hessian approximation. + String in ('GAUSS_NEWTON', 'EXACT'). + Default: 'GAUSS_NEWTON'. + """ + return self.__hessian_approx + + @property + def integrator_type(self): + """ + Integrator type. + String in ('ERK', 'IRK', 'GNSF', 'DISCRETE'). + Default: 'ERK'. + """ + return self.__integrator_type + + @property + def nlp_solver_type(self): + """NLP solver. + String in ('SQP', 'SQP_RTI'). + Default: 'SQP_RTI'. + """ + return self.__nlp_solver_type + + @property + def globalization(self): + """Globalization type. + String in ('FIXED_STEP', 'MERIT_BACKTRACKING'). + Default: 'FIXED_STEP'. + + .. note:: preliminary implementation. + """ + return self.__globalization + + @property + def regularize_method(self): + """Regularization method for the Hessian. + String in ('NO_REGULARIZE', 'MIRROR', 'PROJECT', 'PROJECT_REDUC_HESS', 'CONVEXIFY') or :code:`None`. + + Default: :code:`None`. + """ + return self.__regularize_method + + @property + def nlp_solver_step_length(self): + """ + Fixed Newton step length. + Type: float > 0. + Default: 1.0. + """ + return self.__nlp_solver_step_length + + @property + def levenberg_marquardt(self): + """ + Factor for LM regularization. + Type: float >= 0 + Default: 0.0. + """ + return self.__levenberg_marquardt + + @property + def sim_method_num_stages(self): + """ + Number of stages in the integrator. + Type: int > 0 or ndarray of ints > 0 of shape (N,). + Default: 4 + """ + return self.__sim_method_num_stages + + @property + def sim_method_num_steps(self): + """ + Number of steps in the integrator. + Type: int > 0 or ndarray of ints > 0 of shape (N,). + Default: 1 + """ + return self.__sim_method_num_steps + + @property + def sim_method_newton_iter(self): + """ + Number of Newton iterations in simulation method. + Type: int > 0 + Default: 3 + """ + return self.__sim_method_newton_iter + + @property + def sim_method_jac_reuse(self): + """ + Boolean determining if jacobians are reused within integrator. + Default: False + """ + return self.__sim_method_jac_reuse + + @property + def qp_solver_tol_stat(self): + """ + QP solver stationarity tolerance. + Default: :code:`None` + """ + return self.__qp_solver_tol_stat + + @property + def qp_solver_tol_eq(self): + """ + QP solver equality tolerance. + Default: :code:`None` + """ + return self.__qp_solver_tol_eq + + @property + def qp_solver_tol_ineq(self): + """ + QP solver inequality. + Default: :code:`None` + """ + return self.__qp_solver_tol_ineq + + @property + def qp_solver_tol_comp(self): + """ + QP solver complementarity. + Default: :code:`None` + """ + return self.__qp_solver_tol_comp + + @property + def qp_solver_cond_N(self): + """QP solver: New horizon after partial condensing. + Set to N by default -> no condensing.""" + return self.__qp_solver_cond_N + + @property + def qp_solver_warm_start(self): + """QP solver: Warm starting. + 0: no warm start; 1: warm start; 2: hot start.""" + return self.__qp_solver_warm_start + + @property + def qp_solver_iter_max(self): + """ + QP solver: maximum number of iterations. + Type: int > 0 + Default: 50 + """ + return self.__qp_solver_iter_max + + @property + def tol(self): + """ + NLP solver tolerance. Sets or gets the max of :py:attr:`nlp_solver_tol_eq`, + :py:attr:`nlp_solver_tol_ineq`, :py:attr:`nlp_solver_tol_comp` + and :py:attr:`nlp_solver_tol_stat`. + """ + return max([self.__nlp_solver_tol_eq, self.__nlp_solver_tol_ineq,\ + self.__nlp_solver_tol_comp, self.__nlp_solver_tol_stat]) + + @property + def qp_tol(self): + """ + QP solver tolerance. + Sets all of the following at once or gets the max of + :py:attr:`qp_solver_tol_eq`, :py:attr:`qp_solver_tol_ineq`, + :py:attr:`qp_solver_tol_comp` and + :py:attr:`qp_solver_tol_stat`. + """ + return max([self.__qp_solver_tol_eq, self.__qp_solver_tol_ineq,\ + self.__qp_solver_tol_comp, self.__qp_solver_tol_stat]) + + @property + def nlp_solver_tol_stat(self): + """ + NLP solver stationarity tolerance. + Type: float > 0 + Default: 1e-6 + """ + return self.__nlp_solver_tol_stat + + @property + def nlp_solver_tol_eq(self): + """NLP solver equality tolerance""" + return self.__nlp_solver_tol_eq + + @property + def alpha_min(self): + """Minimal step size for globalization MERIT_BACKTRACKING, default: 0.05.""" + return self.__alpha_min + + @property + def alpha_reduction(self): + """Step size reduction factor for globalization MERIT_BACKTRACKING, default: 0.7.""" + return self.__alpha_reduction + + @property + def nlp_solver_tol_ineq(self): + """NLP solver inequality tolerance""" + return self.__nlp_solver_tol_ineq + + @property + def nlp_solver_tol_comp(self): + """NLP solver complementarity tolerance""" + return self.__nlp_solver_tol_comp + + @property + def nlp_solver_max_iter(self): + """ + NLP solver maximum number of iterations. + Type: int > 0 + Default: 100 + """ + return self.__nlp_solver_max_iter + + @property + def time_steps(self): + """ + Vector with time steps between the shooting nodes. Set automatically to uniform discretization if :py:attr:`N` and :py:attr:`tf` are provided. + Default: :code:`None` + """ + return self.__time_steps + + @property + def shooting_nodes(self): + """ + Vector with the shooting nodes, time_steps will be computed from it automatically. + Default: :code:`None` + """ + return self.__shooting_nodes + + @property + def tf(self): + """ + Prediction horizon + Type: float > 0 + Default: :code:`None` + """ + return self.__tf + + @property + def Tsim(self): + """ + Time horizon for one integrator step. Automatically calculated as :py:attr:`tf`/:py:attr:`N`. + Default: :code:`None` + """ + return self.__Tsim + + @property + def print_level(self): + """ + Verbosity of printing. + Type: int >= 0 + Default: 0 + """ + return self.__print_level + + @property + def model_external_shared_lib_dir(self): + """Path to the .so lib""" + return self.__model_external_shared_lib_dir + + @property + def model_external_shared_lib_name(self): + """Name of the .so lib""" + return self.__model_external_shared_lib_name + + @property + def exact_hess_constr(self): + """ + Used in case of hessian_approx == 'EXACT'.\n + Can be used to turn off exact hessian contributions from the constraints module. + """ + return self.__exact_hess_constr + + @property + def exact_hess_cost(self): + """ + Used in case of hessian_approx == 'EXACT'.\n + Can be used to turn off exact hessian contributions from the cost module. + """ + return self.__exact_hess_cost + + @property + def exact_hess_dyn(self): + """ + Used in case of hessian_approx == 'EXACT'.\n + Can be used to turn off exact hessian contributions from the dynamics module. + """ + return self.__exact_hess_dyn + + @property + def ext_cost_num_hess(self): + """ + Determines if custom hessian approximation for cost contribution is used (> 0).\n + Or if hessian contribution is evaluated exactly using CasADi external function (=0 - default). + """ + return self.__ext_cost_num_hess + + @qp_solver.setter + def qp_solver(self, qp_solver): + qp_solvers = ('PARTIAL_CONDENSING_HPIPM', \ + 'FULL_CONDENSING_QPOASES', 'FULL_CONDENSING_HPIPM', \ + 'PARTIAL_CONDENSING_QPDUNES', 'PARTIAL_CONDENSING_OSQP') + if qp_solver in qp_solvers: + self.__qp_solver = qp_solver + else: + raise Exception('Invalid qp_solver value. Possible values are:\n\n' \ + + ',\n'.join(qp_solvers) + '.\n\nYou have: ' + qp_solver + '.\n\nExiting.') + + @regularize_method.setter + def regularize_method(self, regularize_method): + regularize_methods = ('NO_REGULARIZE', 'MIRROR', 'PROJECT', \ + 'PROJECT_REDUC_HESS', 'CONVEXIFY') + if regularize_method in regularize_methods: + self.__regularize_method = regularize_method + else: + raise Exception('Invalid regularize_method value. Possible values are:\n\n' \ + + ',\n'.join(regularize_methods) + '.\n\nYou have: ' + regularize_method + '.\n\nExiting.') + + @hessian_approx.setter + def hessian_approx(self, hessian_approx): + hessian_approxs = ('GAUSS_NEWTON', 'EXACT') + if hessian_approx in hessian_approxs: + self.__hessian_approx = hessian_approx + else: + raise Exception('Invalid hessian_approx value. Possible values are:\n\n' \ + + ',\n'.join(hessian_approxs) + '.\n\nYou have: ' + hessian_approx + '.\n\nExiting.') + + @integrator_type.setter + def integrator_type(self, integrator_type): + integrator_types = ('ERK', 'IRK', 'GNSF', 'DISCRETE') + if integrator_type in integrator_types: + self.__integrator_type = integrator_type + else: + raise Exception('Invalid integrator_type value. Possible values are:\n\n' \ + + ',\n'.join(integrator_types) + '.\n\nYou have: ' + integrator_type + '.\n\nExiting.') + + @tf.setter + def tf(self, tf): + self.__tf = tf + + @time_steps.setter + def time_steps(self, time_steps): + self.__time_steps = time_steps + + @shooting_nodes.setter + def shooting_nodes(self, shooting_nodes): + self.__shooting_nodes = shooting_nodes + + + @Tsim.setter + def Tsim(self, Tsim): + self.__Tsim = Tsim + + @globalization.setter + def globalization(self, globalization): + self.__globalization = globalization + + @alpha_min.setter + def alpha_min(self, alpha_min): + self.__alpha_min = alpha_min + + @alpha_reduction.setter + def alpha_reduction(self, alpha_reduction): + self.__alpha_reduction = alpha_reduction + + @sim_method_num_stages.setter + def sim_method_num_stages(self, sim_method_num_stages): + + # if type(sim_method_num_stages) == int: + # self.__sim_method_num_stages = sim_method_num_stages + # else: + # raise Exception('Invalid sim_method_num_stages value. sim_method_num_stages must be an integer. Exiting.') + + self.__sim_method_num_stages = sim_method_num_stages + + @sim_method_num_steps.setter + def sim_method_num_steps(self, sim_method_num_steps): + + # if type(sim_method_num_steps) == int: + # self.__sim_method_num_steps = sim_method_num_steps + # else: + # raise Exception('Invalid sim_method_num_steps value. sim_method_num_steps must be an integer. Exiting.') + self.__sim_method_num_steps = sim_method_num_steps + + + @sim_method_newton_iter.setter + def sim_method_newton_iter(self, sim_method_newton_iter): + + if type(sim_method_newton_iter) == int: + self.__sim_method_newton_iter = sim_method_newton_iter + else: + raise Exception('Invalid sim_method_newton_iter value. sim_method_newton_iter must be an integer. Exiting.') + + @sim_method_jac_reuse.setter + def sim_method_jac_reuse(self, sim_method_jac_reuse): + if sim_method_jac_reuse in (True, False): + self.__sim_method_jac_reuse = sim_method_jac_reuse + else: + raise Exception('Invalid sim_method_jac_reuse value. sim_method_jac_reuse must be a Boolean.') + + @nlp_solver_type.setter + def nlp_solver_type(self, nlp_solver_type): + nlp_solver_types = ('SQP', 'SQP_RTI') + if nlp_solver_type in nlp_solver_types: + self.__nlp_solver_type = nlp_solver_type + else: + raise Exception('Invalid nlp_solver_type value. Possible values are:\n\n' \ + + ',\n'.join(nlp_solver_types) + '.\n\nYou have: ' + nlp_solver_type + '.\n\nExiting.') + + @nlp_solver_step_length.setter + def nlp_solver_step_length(self, nlp_solver_step_length): + if type(nlp_solver_step_length) == float and nlp_solver_step_length > 0: + self.__nlp_solver_step_length = nlp_solver_step_length + else: + raise Exception('Invalid nlp_solver_step_length value. nlp_solver_step_length must be a positive float. Exiting') + + @levenberg_marquardt.setter + def levenberg_marquardt(self, levenberg_marquardt): + if isinstance(levenberg_marquardt, float) and levenberg_marquardt >= 0: + self.__levenberg_marquardt = levenberg_marquardt + else: + raise Exception('Invalid levenberg_marquardt value. levenberg_marquardt must be a positive float. Exiting') + + @qp_solver_iter_max.setter + def qp_solver_iter_max(self, qp_solver_iter_max): + if isinstance(qp_solver_iter_max, int) and qp_solver_iter_max > 0: + self.__qp_solver_iter_max = qp_solver_iter_max + else: + raise Exception('Invalid qp_solver_iter_max value. qp_solver_iter_max must be a positive int. Exiting') + + @qp_solver_cond_N.setter + def qp_solver_cond_N(self, qp_solver_cond_N): + if isinstance(qp_solver_cond_N, int) and qp_solver_cond_N > 0: + self.__qp_solver_cond_N = qp_solver_cond_N + else: + raise Exception('Invalid qp_solver_cond_N value. qp_solver_cond_N must be a positive int. Exiting') + + @qp_solver_warm_start.setter + def qp_solver_warm_start(self, qp_solver_warm_start): + if qp_solver_warm_start in [0, 1, 2]: + self.__qp_solver_warm_start = qp_solver_warm_start + else: + raise Exception('Invalid qp_solver_warm_start value. qp_solver_warm_start must be 0 or 1 or 2. Exiting') + + @qp_tol.setter + def qp_tol(self, qp_tol): + if isinstance(qp_tol, float) and qp_tol > 0: + self.__qp_solver_tol_eq = qp_tol + self.__qp_solver_tol_ineq = qp_tol + self.__qp_solver_tol_stat = qp_tol + self.__qp_solver_tol_comp = qp_tol + else: + raise Exception('Invalid qp_tol value. qp_tol must be a positive float. Exiting') + + @qp_solver_tol_stat.setter + def qp_solver_tol_stat(self, qp_solver_tol_stat): + if isinstance(qp_solver_tol_stat, float) and qp_solver_tol_stat > 0: + self.__qp_solver_tol_stat = qp_solver_tol_stat + else: + raise Exception('Invalid qp_solver_tol_stat value. qp_solver_tol_stat must be a positive float. Exiting') + + @qp_solver_tol_eq.setter + def qp_solver_tol_eq(self, qp_solver_tol_eq): + if isinstance(qp_solver_tol_eq, float) and qp_solver_tol_eq > 0: + self.__qp_solver_tol_eq = qp_solver_tol_eq + else: + raise Exception('Invalid qp_solver_tol_eq value. qp_solver_tol_eq must be a positive float. Exiting') + + @qp_solver_tol_ineq.setter + def qp_solver_tol_ineq(self, qp_solver_tol_ineq): + if isinstance(qp_solver_tol_ineq, float) and qp_solver_tol_ineq > 0: + self.__qp_solver_tol_ineq = qp_solver_tol_ineq + else: + raise Exception('Invalid qp_solver_tol_ineq value. qp_solver_tol_ineq must be a positive float. Exiting') + + @qp_solver_tol_comp.setter + def qp_solver_tol_comp(self, qp_solver_tol_comp): + if isinstance(qp_solver_tol_comp, float) and qp_solver_tol_comp > 0: + self.__qp_solver_tol_comp = qp_solver_tol_comp + else: + raise Exception('Invalid qp_solver_tol_comp value. qp_solver_tol_comp must be a positive float. Exiting') + + @tol.setter + def tol(self, tol): + if isinstance(tol, float) and tol > 0: + self.__nlp_solver_tol_eq = tol + self.__nlp_solver_tol_ineq = tol + self.__nlp_solver_tol_stat = tol + self.__nlp_solver_tol_comp = tol + else: + raise Exception('Invalid tol value. tol must be a positive float. Exiting') + + @nlp_solver_tol_stat.setter + def nlp_solver_tol_stat(self, nlp_solver_tol_stat): + if isinstance(nlp_solver_tol_stat, float) and nlp_solver_tol_stat > 0: + self.__nlp_solver_tol_stat = nlp_solver_tol_stat + else: + raise Exception('Invalid nlp_solver_tol_stat value. nlp_solver_tol_stat must be a positive float. Exiting') + + @nlp_solver_tol_eq.setter + def nlp_solver_tol_eq(self, nlp_solver_tol_eq): + if isinstance(nlp_solver_tol_eq, float) and nlp_solver_tol_eq > 0: + self.__nlp_solver_tol_eq = nlp_solver_tol_eq + else: + raise Exception('Invalid nlp_solver_tol_eq value. nlp_solver_tol_eq must be a positive float. Exiting') + + @nlp_solver_tol_ineq.setter + def nlp_solver_tol_ineq(self, nlp_solver_tol_ineq): + if isinstance(nlp_solver_tol_ineq, float) and nlp_solver_tol_ineq > 0: + self.__nlp_solver_tol_ineq = nlp_solver_tol_ineq + else: + raise Exception('Invalid nlp_solver_tol_ineq value. nlp_solver_tol_ineq must be a positive float. Exiting') + + @nlp_solver_tol_comp.setter + def nlp_solver_tol_comp(self, nlp_solver_tol_comp): + if isinstance(nlp_solver_tol_comp, float) and nlp_solver_tol_comp > 0: + self.__nlp_solver_tol_comp = nlp_solver_tol_comp + else: + raise Exception('Invalid nlp_solver_tol_comp value. nlp_solver_tol_comp must be a positive float. Exiting') + + @nlp_solver_max_iter.setter + def nlp_solver_max_iter(self, nlp_solver_max_iter): + + if type(nlp_solver_max_iter) == int and nlp_solver_max_iter > 0: + self.__nlp_solver_max_iter = nlp_solver_max_iter + else: + raise Exception('Invalid nlp_solver_max_iter value. nlp_solver_max_iter must be a positive int. Exiting') + + @print_level.setter + def print_level(self, print_level): + if type(print_level) == int and print_level >= 0: + self.__print_level = print_level + else: + raise Exception('Invalid print_level value. print_level takes one of the values >=0. Exiting') + + @model_external_shared_lib_dir.setter + def model_external_shared_lib_dir(self, model_external_shared_lib_dir): + if type(model_external_shared_lib_dir) == str : + self.__model_external_shared_lib_dir = model_external_shared_lib_dir + else: + raise Exception('Invalid model_external_shared_lib_dir value. Str expected.' \ + + '.\n\nYou have: ' + type(model_external_shared_lib_dir) + '.\n\nExiting.') + + @model_external_shared_lib_name.setter + def model_external_shared_lib_name(self, model_external_shared_lib_name): + if type(model_external_shared_lib_name) == str : + if model_external_shared_lib_name[-3:] == '.so' : + raise Exception('Invalid model_external_shared_lib_name value. Remove the .so extension.' \ + + '.\n\nYou have: ' + type(model_external_shared_lib_name) + '.\n\nExiting.') + else : + self.__model_external_shared_lib_name = model_external_shared_lib_name + else: + raise Exception('Invalid model_external_shared_lib_name value. Str expected.' \ + + '.\n\nYou have: ' + type(model_external_shared_lib_name) + '.\n\nExiting.') + + @exact_hess_constr.setter + def exact_hess_constr(self, exact_hess_constr): + if exact_hess_constr in [0, 1]: + self.__exact_hess_constr = exact_hess_constr + else: + raise Exception('Invalid exact_hess_constr value. exact_hess_constr takes one of the values 0, 1. Exiting') + + @exact_hess_cost.setter + def exact_hess_cost(self, exact_hess_cost): + if exact_hess_cost in [0, 1]: + self.__exact_hess_cost = exact_hess_cost + else: + raise Exception('Invalid exact_hess_cost value. exact_hess_cost takes one of the values 0, 1. Exiting') + + @exact_hess_dyn.setter + def exact_hess_dyn(self, exact_hess_dyn): + if exact_hess_dyn in [0, 1]: + self.__exact_hess_dyn = exact_hess_dyn + else: + raise Exception('Invalid exact_hess_dyn value. exact_hess_dyn takes one of the values 0, 1. Exiting') + + @ext_cost_num_hess.setter + def ext_cost_num_hess(self, ext_cost_num_hess): + if ext_cost_num_hess in [0, 1]: + self.__ext_cost_num_hess = ext_cost_num_hess + else: + raise Exception('Invalid ext_cost_num_hess value. ext_cost_num_hess takes one of the values 0, 1. Exiting') + + def set(self, attr, value): + setattr(self, attr, value) + + +class AcadosOcp: + """ + Class containing the full description of the optimal control problem. + This object can be used to create an :py:class:`acados_template.acados_ocp_solver.AcadosOcpSolver`. + + The class has the following properties that can be modified to formulate a specific OCP, see below: + + - :py:attr:`dims` of type :py:class:`acados_template.acados_ocp.AcadosOcpDims` + - :py:attr:`model` of type :py:class:`acados_template.acados_model.AcadosModel` + - :py:attr:`cost` of type :py:class:`acados_template.acados_ocp.AcadosOcpCost` + - :py:attr:`constraints` of type :py:class:`acados_template.acados_ocp.AcadosOcpConstraints` + - :py:attr:`solver_options` of type :py:class:`acados_template.acados_ocp.AcadosOcpOptions` + + - :py:attr:`acados_include_path` (set automatically) + - :py:attr:`acados_lib_path` (set automatically) + - :py:attr:`parameter_values` - used to initialize the parameters (can be changed) + """ + def __init__(self, acados_path=''): + """ + Keyword arguments: + acados_path -- path of your acados installation + """ + if acados_path == '': + acados_path = get_acados_path() + + self.dims = AcadosOcpDims() + """Dimension definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpDims`""" + self.model = AcadosModel() + """Model definitions, type :py:class:`acados_template.acados_model.AcadosModel`""" + self.cost = AcadosOcpCost() + """Cost definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpCost`""" + self.constraints = AcadosOcpConstraints() + """Constraints definitions, type :py:class:`acados_template.acados_ocp.AcadosOcpConstraints`""" + self.solver_options = AcadosOcpOptions() + """Solver Options, type :py:class:`acados_template.acados_ocp.AcadosOcpOptions`""" + + self.acados_include_path = f'{acados_path}/include' + """Path to acados include directory, type: string""" + self.acados_lib_path = f'{acados_path}/lib' + """Path to where acados library is located, type: string""" + + self.__parameter_values = np.array([]) + self.__problem_class = 'OCP' + + self.code_export_directory = 'c_generated_code' + """Path to where code will be exported. Default: `c_generated_code`.""" + + @property + def parameter_values(self): + """:math:`p` - initial values for parameter - can be updated stagewise""" + return self.__parameter_values + + @parameter_values.setter + def parameter_values(self, parameter_values): + if isinstance(parameter_values, np.ndarray): + self.__parameter_values = parameter_values + else: + raise Exception('Invalid parameter_values value. ' + + f'Expected numpy array, got {type(parameter_values)}.') + + def set(self, attr, value): + # tokenize string + tokens = attr.split('_', 1) + if len(tokens) > 1: + setter_to_call = getattr(getattr(self, tokens[0]), 'set') + else: + setter_to_call = getattr(self, 'set') + + setter_to_call(tokens[1], value) + + return diff --git a/pyextra/acados_template/acados_ocp_solver.py b/pyextra/acados_template/acados_ocp_solver.py new file mode 100644 index 0000000000..aa780e1a26 --- /dev/null +++ b/pyextra/acados_template/acados_ocp_solver.py @@ -0,0 +1,1470 @@ +# -*- coding: future_fstrings -*- +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import sys, os, json +import numpy as np +from datetime import datetime + +from ctypes import * + +from copy import deepcopy + +from .generate_c_code_explicit_ode import generate_c_code_explicit_ode +from .generate_c_code_implicit_ode import generate_c_code_implicit_ode +from .generate_c_code_gnsf import generate_c_code_gnsf +from .generate_c_code_discrete_dynamics import generate_c_code_discrete_dynamics +from .generate_c_code_constraint import generate_c_code_constraint +from .generate_c_code_nls_cost import generate_c_code_nls_cost +from .generate_c_code_external_cost import generate_c_code_external_cost +from .acados_ocp import AcadosOcp +from .acados_model import acados_model_strip_casadi_symbolics +from .utils import is_column, is_empty, casadi_length, render_template, acados_class2dict,\ + format_class_dict, ocp_check_against_layout, np_array_to_list, make_model_consistent,\ + set_up_imported_gnsf_model, get_acados_path + + +def make_ocp_dims_consistent(acados_ocp): + dims = acados_ocp.dims + cost = acados_ocp.cost + constraints = acados_ocp.constraints + model = acados_ocp.model + opts = acados_ocp.solver_options + + # nx + if is_column(model.x): + dims.nx = casadi_length(model.x) + else: + raise Exception('model.x should be column vector!') + + # nu + if is_empty(model.u): + dims.nu = 0 + else: + dims.nu = casadi_length(model.u) + + # nz + if is_empty(model.z): + dims.nz = 0 + else: + dims.nz = casadi_length(model.z) + + # np + if is_empty(model.p): + dims.np = 0 + else: + dims.np = casadi_length(model.p) + if acados_ocp.parameter_values.shape[0] != dims.np: + raise Exception('inconsistent dimension np, regarding model.p and parameter_values.' + \ + f'\nGot np = {dims.np}, acados_ocp.parameter_values.shape = {acados_ocp.parameter_values.shape[0]}\n') + + ## cost + # initial stage - if not set, copy fields from path constraints + if cost.cost_type_0 == None: + cost.cost_type_0 = cost.cost_type + cost.W_0 = cost.W + cost.Vx_0 = cost.Vx + cost.Vu_0 = cost.Vu + cost.Vz_0 = cost.Vz + cost.yref_0 = cost.yref + cost.cost_ext_fun_type_0 = cost.cost_ext_fun_type + model.cost_y_expr_0 = model.cost_y_expr + model.cost_expr_ext_cost_0 = model.cost_expr_ext_cost + + if cost.cost_type_0 == 'LINEAR_LS': + ny_0 = cost.W_0.shape[0] + if cost.Vx_0.shape[0] != ny_0 or cost.Vu_0.shape[0] != ny_0: + raise Exception('inconsistent dimension ny_0, regarding W_0, Vx_0, Vu_0.' + \ + f'\nGot W_0[{cost.W_0.shape}], Vx_0[{cost.Vx_0.shape}], Vu_0[{cost.Vu_0.shape}]\n') + if dims.nz != 0 and cost.Vz_0.shape[0] != ny_0: + raise Exception('inconsistent dimension ny_0, regarding W_0, Vx_0, Vu_0, Vz_0.' + \ + f'\nGot W_0[{cost.W_0.shape}], Vx_0[{cost.Vx_0.shape}], Vu_0[{cost.Vu_0.shape}], Vz_0[{cost.Vz_0.shape}]\n') + if cost.Vx_0.shape[1] != dims.nx and ny_0 != 0: + raise Exception('inconsistent dimension: Vx_0 should have nx columns.') + if cost.Vu_0.shape[1] != dims.nu and ny_0 != 0: + raise Exception('inconsistent dimension: Vu_0 should have nu columns.') + if cost.yref_0.shape[0] != ny_0: + raise Exception('inconsistent dimension: regarding W_0, yref_0.' + \ + f'\nGot W_0[{cost.W_0.shape}], yref_0[{cost.yref_0.shape}]\n') + dims.ny_0 = ny_0 + + elif cost.cost_type_0 == 'NONLINEAR_LS': + ny_0 = cost.W_0.shape[0] + if is_empty(model.cost_y_expr_0) and ny_0 != 0: + raise Exception('inconsistent dimension ny_0: regarding W_0, cost_y_expr.') + elif casadi_length(model.cost_y_expr_0) != ny_0: + raise Exception('inconsistent dimension ny_0: regarding W_0, cost_y_expr.') + if cost.yref_0.shape[0] != ny_0: + raise Exception('inconsistent dimension: regarding W_0, yref_0.' + \ + f'\nGot W_0[{cost.W.shape}], yref_0[{cost.yref_0.shape}]\n') + dims.ny_0 = ny_0 + + # path + if cost.cost_type == 'LINEAR_LS': + ny = cost.W.shape[0] + if cost.Vx.shape[0] != ny or cost.Vu.shape[0] != ny: + raise Exception('inconsistent dimension ny, regarding W, Vx, Vu.' + \ + f'\nGot W[{cost.W.shape}], Vx[{cost.Vx.shape}], Vu[{cost.Vu.shape}]\n') + if dims.nz != 0 and cost.Vz.shape[0] != ny: + raise Exception('inconsistent dimension ny, regarding W, Vx, Vu, Vz.' + \ + f'\nGot W[{cost.W.shape}], Vx[{cost.Vx.shape}], Vu[{cost.Vu.shape}], Vz[{cost.Vz.shape}]\n') + if cost.Vx.shape[1] != dims.nx and ny != 0: + raise Exception('inconsistent dimension: Vx should have nx columns.') + if cost.Vu.shape[1] != dims.nu and ny != 0: + raise Exception('inconsistent dimension: Vu should have nu columns.') + if cost.yref.shape[0] != ny: + raise Exception('inconsistent dimension: regarding W, yref.' + \ + f'\nGot W[{cost.W.shape}], yref[{cost.yref.shape}]\n') + dims.ny = ny + + elif cost.cost_type == 'NONLINEAR_LS': + ny = cost.W.shape[0] + if is_empty(model.cost_y_expr) and ny != 0: + raise Exception('inconsistent dimension ny: regarding W, cost_y_expr.') + elif casadi_length(model.cost_y_expr) != ny: + raise Exception('inconsistent dimension ny: regarding W, cost_y_expr.') + if cost.yref.shape[0] != ny: + raise Exception('inconsistent dimension: regarding W, yref.' + \ + f'\nGot W[{cost.W.shape}], yref[{cost.yref.shape}]\n') + dims.ny = ny + + # terminal + if cost.cost_type_e == 'LINEAR_LS': + ny_e = cost.W_e.shape[0] + if cost.Vx_e.shape[0] != ny_e: + raise Exception('inconsistent dimension ny_e: regarding W_e, cost_y_expr_e.' + \ + f'\nGot W_e[{cost.W_e.shape}], Vx_e[{cost.Vx_e.shape}]') + if cost.Vx_e.shape[1] != dims.nx and ny_e != 0: + raise Exception('inconsistent dimension: Vx_e should have nx columns.') + if cost.yref_e.shape[0] != ny_e: + raise Exception('inconsistent dimension: regarding W_e, yref_e.') + dims.ny_e = ny_e + + elif cost.cost_type_e == 'NONLINEAR_LS': + ny_e = cost.W_e.shape[0] + if is_empty(model.cost_y_expr_e) and ny_e != 0: + raise Exception('inconsistent dimension ny_e: regarding W_e, cost_y_expr_e.') + elif casadi_length(model.cost_y_expr_e) != ny_e: + raise Exception('inconsistent dimension ny_e: regarding W_e, cost_y_expr_e.') + if cost.yref_e.shape[0] != ny_e: + raise Exception('inconsistent dimension: regarding W_e, yref_e.') + dims.ny_e = ny_e + + + ## constraints + # initial + if (constraints.lbx_0 == [] and constraints.ubx_0 == []): + dims.nbx_0 = 0 + else: + this_shape = constraints.lbx_0.shape + other_shape = constraints.ubx_0.shape + if not this_shape == other_shape: + raise Exception('lbx_0, ubx_0 have different shapes!') + if not is_column(constraints.lbx_0): + raise Exception('lbx_0, ubx_0 must be column vectors!') + dims.nbx_0 = constraints.lbx_0.size + + if all(constraints.lbx_0 == constraints.ubx_0) and dims.nbx_0 == dims.nx \ + and dims.nbxe_0 == None \ + and (constraints.idxbxe_0.shape == constraints.idxbx_0.shape)\ + and all(constraints.idxbxe_0 == constraints.idxbx_0): + # case: x0 was set: nbx0 are all equlities. + dims.nbxe_0 = dims.nbx_0 + elif dims.nbxe_0 == None: + # case: x0 was not set -> dont assume nbx0 to be equality constraints. + dims.nbxe_0 = 0 + + # path + nbx = constraints.idxbx.shape[0] + if constraints.ubx.shape[0] != nbx or constraints.lbx.shape[0] != nbx: + raise Exception('inconsistent dimension nbx, regarding idxbx, ubx, lbx.') + else: + dims.nbx = nbx + + nbu = constraints.idxbu.shape[0] + if constraints.ubu.shape[0] != nbu or constraints.lbu.shape[0] != nbu: + raise Exception('inconsistent dimension nbu, regarding idxbu, ubu, lbu.') + else: + dims.nbu = nbu + + ng = constraints.lg.shape[0] + if constraints.ug.shape[0] != ng or constraints.C.shape[0] != ng \ + or constraints.D.shape[0] != ng: + raise Exception('inconsistent dimension ng, regarding lg, ug, C, D.') + else: + dims.ng = ng + + if not is_empty(model.con_h_expr): + nh = casadi_length(model.con_h_expr) + else: + nh = 0 + + if constraints.uh.shape[0] != nh or constraints.lh.shape[0] != nh: + raise Exception('inconsistent dimension nh, regarding lh, uh, con_h_expr.') + else: + dims.nh = nh + + if is_empty(model.con_phi_expr): + dims.nphi = 0 + dims.nr = 0 + else: + dims.nphi = casadi_length(model.con_phi_expr) + if is_empty(model.con_r_expr): + raise Exception('convex over nonlinear constraints: con_r_expr but con_phi_expr is nonempty') + else: + dims.nr = casadi_length(model.con_r_expr) + + # terminal + nbx_e = constraints.idxbx_e.shape[0] + if constraints.ubx_e.shape[0] != nbx_e or constraints.lbx_e.shape[0] != nbx_e: + raise Exception('inconsistent dimension nbx_e, regarding idxbx_e, ubx_e, lbx_e.') + else: + dims.nbx_e = nbx_e + + ng_e = constraints.lg_e.shape[0] + if constraints.ug_e.shape[0] != ng_e or constraints.C_e.shape[0] != ng_e: + raise Exception('inconsistent dimension ng_e, regarding_e lg_e, ug_e, C_e.') + else: + dims.ng_e = ng_e + + if not is_empty(model.con_h_expr_e): + nh_e = casadi_length(model.con_h_expr_e) + else: + nh_e = 0 + + if constraints.uh_e.shape[0] != nh_e or constraints.lh_e.shape[0] != nh_e: + raise Exception('inconsistent dimension nh_e, regarding lh_e, uh_e, con_h_expr_e.') + else: + dims.nh_e = nh_e + + if is_empty(model.con_phi_expr_e): + dims.nphi_e = 0 + dims.nr_e = 0 + else: + dims.nphi_e = casadi_length(model.con_phi_expr_e) + if is_empty(model.con_r_expr_e): + raise Exception('convex over nonlinear constraints: con_r_expr_e but con_phi_expr_e is nonempty') + else: + dims.nr_e = casadi_length(model.con_r_expr_e) + + # Slack dimensions + nsbx = constraints.idxsbx.shape[0] + if is_empty(constraints.lsbx): + constraints.lsbx = np.zeros((nsbx,)) + elif constraints.lsbx.shape[0] != nsbx: + raise Exception('inconsistent dimension nsbx, regarding idxsbx, lsbx.') + if is_empty(constraints.usbx): + constraints.usbx = np.zeros((nsbx,)) + elif constraints.usbx.shape[0] != nsbx: + raise Exception('inconsistent dimension nsbx, regarding idxsbx, usbx.') + dims.nsbx = nsbx + + nsbu = constraints.idxsbu.shape[0] + if is_empty(constraints.lsbu): + constraints.lsbu = np.zeros((nsbu,)) + elif constraints.lsbu.shape[0] != nsbu: + raise Exception('inconsistent dimension nsbu, regarding idxsbu, lsbu.') + if is_empty(constraints.usbu): + constraints.usbu = np.zeros((nsbu,)) + elif constraints.usbu.shape[0] != nsbu: + raise Exception('inconsistent dimension nsbu, regarding idxsbu, usbu.') + dims.nsbu = nsbu + + nsh = constraints.idxsh.shape[0] + if is_empty(constraints.lsh): + constraints.lsh = np.zeros((nsh,)) + elif constraints.lsh.shape[0] != nsh: + raise Exception('inconsistent dimension nsh, regarding idxsh, lsh.') + if is_empty(constraints.ush): + constraints.ush = np.zeros((nsh,)) + elif constraints.ush.shape[0] != nsh: + raise Exception('inconsistent dimension nsh, regarding idxsh, ush.') + dims.nsh = nsh + + nsphi = constraints.idxsphi.shape[0] + if is_empty(constraints.lsphi): + constraints.lsphi = np.zeros((nsphi,)) + elif constraints.lsphi.shape[0] != nsphi: + raise Exception('inconsistent dimension nsphi, regarding idxsphi, lsphi.') + if is_empty(constraints.usphi): + constraints.usphi = np.zeros((nsphi,)) + elif constraints.usphi.shape[0] != nsphi: + raise Exception('inconsistent dimension nsphi, regarding idxsphi, usphi.') + dims.nsphi = nsphi + + nsg = constraints.idxsg.shape[0] + if is_empty(constraints.lsg): + constraints.lsg = np.zeros((nsg,)) + elif constraints.lsg.shape[0] != nsg: + raise Exception('inconsistent dimension nsg, regarding idxsg, lsg.') + if is_empty(constraints.usg): + constraints.usg = np.zeros((nsg,)) + elif constraints.usg.shape[0] != nsg: + raise Exception('inconsistent dimension nsg, regarding idxsg, usg.') + dims.nsg = nsg + + ns = nsbx + nsbu + nsh + nsg + nsphi + wrong_field = "" + if cost.Zl.shape[0] != ns: + wrong_field = "Zl" + dim = cost.Zl.shape[0] + elif cost.Zu.shape[0] != ns: + wrong_field = "Zu" + dim = cost.Zu.shape[0] + elif cost.zl.shape[0] != ns: + wrong_field = "zl" + dim = cost.zl.shape[0] + elif cost.zu.shape[0] != ns: + wrong_field = "zu" + dim = cost.zu.shape[0] + + if wrong_field != "": + raise Exception(f'Inconsistent size for field {wrong_field}, with dimension {dim}, \n\t'\ + + f'Detected ns = {ns} = nsbx + nsbu + nsg + nsh + nsphi.\n\t'\ + + f'With nsbx = {nsbx}, nsbu = {nsbu}, nsg = {nsg}, nsh = {nsh}, nsphi = {nsphi}') + + dims.ns = ns + + nsbx_e = constraints.idxsbx_e.shape[0] + if is_empty(constraints.lsbx_e): + constraints.lsbx_e = np.zeros((nsbx_e,)) + elif constraints.lsbx_e.shape[0] != nsbx_e: + raise Exception('inconsistent dimension nsbx_e, regarding idxsbx_e, lsbx_e.') + if is_empty(constraints.usbx_e): + constraints.usbx_e = np.zeros((nsbx_e,)) + elif constraints.usbx_e.shape[0] != nsbx_e: + raise Exception('inconsistent dimension nsbx_e, regarding idxsbx_e, usbx_e.') + dims.nsbx_e = nsbx_e + + nsh_e = constraints.idxsh_e.shape[0] + if is_empty(constraints.lsh_e): + constraints.lsh_e = np.zeros((nsh_e,)) + elif constraints.lsh_e.shape[0] != nsh_e: + raise Exception('inconsistent dimension nsh_e, regarding idxsh_e, lsh_e.') + if is_empty(constraints.ush_e): + constraints.ush_e = np.zeros((nsh_e,)) + elif constraints.ush_e.shape[0] != nsh_e: + raise Exception('inconsistent dimension nsh_e, regarding idxsh_e, ush_e.') + dims.nsh_e = nsh_e + + nsg_e = constraints.idxsg_e.shape[0] + if is_empty(constraints.lsg_e): + constraints.lsg_e = np.zeros((nsg_e,)) + elif constraints.lsg_e.shape[0] != nsg_e: + raise Exception('inconsistent dimension nsg_e, regarding idxsg_e, lsg_e.') + if is_empty(constraints.usg_e): + constraints.usg_e = np.zeros((nsg_e,)) + elif constraints.usg_e.shape[0] != nsg_e: + raise Exception('inconsistent dimension nsg_e, regarding idxsg_e, usg_e.') + dims.nsg_e = nsg_e + + nsphi_e = constraints.idxsphi_e.shape[0] + if is_empty(constraints.lsphi_e): + constraints.lsphi_e = np.zeros((nsphi_e,)) + elif constraints.lsphi_e.shape[0] != nsphi_e: + raise Exception('inconsistent dimension nsphi_e, regarding idxsphi_e, lsphi_e.') + if is_empty(constraints.usphi_e): + constraints.usphi_e = np.zeros((nsphi_e,)) + elif constraints.usphi_e.shape[0] != nsphi_e: + raise Exception('inconsistent dimension nsphi_e, regarding idxsphi_e, usphi_e.') + dims.nsphi_e = nsphi_e + + # terminal + ns_e = nsbx_e + nsh_e + nsg_e + nsphi_e + wrong_field = "" + if cost.Zl_e.shape[0] != ns_e: + wrong_field = "Zl_e" + dim = cost.Zl_e.shape[0] + elif cost.Zu_e.shape[0] != ns_e: + wrong_field = "Zu_e" + dim = cost.Zu_e.shape[0] + elif cost.zl_e.shape[0] != ns_e: + wrong_field = "zl_e" + dim = cost.zl_e.shape[0] + elif cost.zu_e.shape[0] != ns_e: + wrong_field = "zu_e" + dim = cost.zu_e.shape[0] + + if wrong_field != "": + raise Exception(f'Inconsistent size for field {wrong_field}, with dimension {dim}, \n\t'\ + + f'Detected ns_e = {ns_e} = nsbx_e + nsg_e + nsh_e + nsphi_e.\n\t'\ + + f'With nsbx_e = {nsbx_e}, nsg_e = {nsg_e}, nsh_e = {nsh_e}, nsphi_e = {nsphi_e}') + + dims.ns_e = ns_e + + # discretization + if is_empty(opts.time_steps) and is_empty(opts.shooting_nodes): + # uniform discretization + opts.time_steps = opts.tf / dims.N * np.ones((dims.N,)) + + elif not is_empty(opts.shooting_nodes): + if np.shape(opts.shooting_nodes)[0] != dims.N+1: + raise Exception('inconsistent dimension N, regarding shooting_nodes.') + + time_steps = np.zeros((dims.N,)) + for i in range(dims.N): + time_steps[i] = opts.shooting_nodes[i+1] - opts.shooting_nodes[i] + opts.time_steps = time_steps + + elif (not is_empty(opts.time_steps)) and (not is_empty(opts.shooting_nodes)): + Exception('Please provide either time_steps or shooting_nodes for nonuniform discretization') + + tf = np.sum(opts.time_steps) + if (tf - opts.tf) / tf > 1e-15: + raise Exception(f'Inconsistent discretization: {opts.tf}'\ + f' = tf != sum(opts.time_steps) = {tf}.') + + # num_steps + if isinstance(opts.sim_method_num_steps, np.ndarray) and opts.sim_method_num_steps.size == 1: + opts.sim_method_num_steps = opts.sim_method_num_steps.item() + + if isinstance(opts.sim_method_num_steps, (int, float)) and opts.sim_method_num_steps % 1 == 0: + opts.sim_method_num_steps = opts.sim_method_num_steps * np.ones((dims.N,), dtype=np.int64) + elif isinstance(opts.sim_method_num_steps, np.ndarray) and opts.sim_method_num_steps.size == dims.N \ + and np.all(np.equal(np.mod(opts.sim_method_num_steps, 1), 0)): + opts.sim_method_num_steps = np.reshape(opts.sim_method_num_steps, (dims.N,)).astype(np.int64) + else: + raise Exception("Wrong value for sim_method_num_steps. Should be either int or array of ints of shape (N,).") + + # num_stages + if isinstance(opts.sim_method_num_stages, np.ndarray) and opts.sim_method_num_stages.size == 1: + opts.sim_method_num_stages = opts.sim_method_num_stages.item() + + if isinstance(opts.sim_method_num_stages, (int, float)) and opts.sim_method_num_stages % 1 == 0: + opts.sim_method_num_stages = opts.sim_method_num_stages * np.ones((dims.N,), dtype=np.int64) + elif isinstance(opts.sim_method_num_stages, np.ndarray) and opts.sim_method_num_stages.size == dims.N \ + and np.all(np.equal(np.mod(opts.sim_method_num_stages, 1), 0)): + opts.sim_method_num_stages = np.reshape(opts.sim_method_num_stages, (dims.N,)).astype(np.int64) + else: + raise Exception("Wrong value for sim_method_num_stages. Should be either int or array of ints of shape (N,).") + + + + +def get_ocp_nlp_layout(): + current_module = sys.modules[__name__] + acados_path = os.path.dirname(current_module.__file__) + with open(acados_path + '/acados_layout.json', 'r') as f: + ocp_nlp_layout = json.load(f) + return ocp_nlp_layout + + +def ocp_formulation_json_dump(acados_ocp, simulink_opts, json_file='acados_ocp_nlp.json'): + # Load acados_ocp_nlp structure description + ocp_layout = get_ocp_nlp_layout() + + # Copy input ocp object dictionary + ocp_nlp_dict = dict(deepcopy(acados_ocp).__dict__) + # TODO: maybe make one function with formatting + + for acados_struct, v in ocp_layout.items(): + # skip non dict attributes + if not isinstance(v, dict): continue + # setattr(ocp_nlp, acados_struct, dict(getattr(acados_ocp, acados_struct).__dict__)) + # Copy ocp object attributes dictionaries + ocp_nlp_dict[acados_struct]=dict(getattr(acados_ocp, acados_struct).__dict__) + + ocp_nlp_dict = format_class_dict(ocp_nlp_dict) + + # strip symbolics + ocp_nlp_dict['model'] = acados_model_strip_casadi_symbolics(ocp_nlp_dict['model']) + + # strip shooting_nodes + ocp_nlp_dict['solver_options'].pop('shooting_nodes', None) + + dims_dict = acados_class2dict(acados_ocp.dims) + + ocp_check_against_layout(ocp_nlp_dict, dims_dict) + + # add simulink options + ocp_nlp_dict['simulink_opts'] = simulink_opts + + with open(json_file, 'w') as f: + json.dump(ocp_nlp_dict, f, default=np_array_to_list, indent=4, sort_keys=True) + + + +def ocp_formulation_json_load(json_file='acados_ocp_nlp.json'): + # Load acados_ocp_nlp structure description + ocp_layout = get_ocp_nlp_layout() + + with open(json_file, 'r') as f: + ocp_nlp_json = json.load(f) + + ocp_nlp_dict = json2dict(ocp_nlp_json, ocp_nlp_json['dims']) + + # Instantiate AcadosOcp object + acados_ocp = AcadosOcp() + + # load class dict + acados_ocp.__dict__ = ocp_nlp_dict + + # laod class attributes dict, dims, constraints, etc + for acados_struct, v in ocp_layout.items(): + # skip non dict attributes + if not isinstance(v, dict): continue + acados_attribute = getattr(acados_ocp, acados_struct) + acados_attribute.__dict__ = ocp_nlp_dict[acados_struct] + setattr(acados_ocp, acados_struct, acados_attribute) + + return acados_ocp + + +def ocp_generate_external_functions(acados_ocp, model): + + model = make_model_consistent(model) + + if acados_ocp.solver_options.hessian_approx == 'EXACT': + opts = dict(generate_hess=1) + else: + opts = dict(generate_hess=0) + code_export_dir = acados_ocp.code_export_directory + opts['code_export_directory'] = code_export_dir + + if acados_ocp.model.dyn_ext_fun_type != 'casadi': + raise Exception("ocp_generate_external_functions: dyn_ext_fun_type only supports 'casadi' for now.\ + Extending the Python interface with generic function support is welcome.") + + if acados_ocp.solver_options.integrator_type == 'ERK': + # explicit model -- generate C code + generate_c_code_explicit_ode(model, opts) + elif acados_ocp.solver_options.integrator_type == 'IRK': + # implicit model -- generate C code + generate_c_code_implicit_ode(model, opts) + elif acados_ocp.solver_options.integrator_type == 'GNSF': + generate_c_code_gnsf(model, opts) + elif acados_ocp.solver_options.integrator_type == 'DISCRETE': + generate_c_code_discrete_dynamics(model, opts) + else: + raise Exception("ocp_generate_external_functions: unknown integrator type.") + + if acados_ocp.dims.nphi > 0 or acados_ocp.dims.nh > 0: + generate_c_code_constraint(model, model.name, False, opts) + + if acados_ocp.dims.nphi_e > 0 or acados_ocp.dims.nh_e > 0: + generate_c_code_constraint(model, model.name, True, opts) + + # dummy matrices + if not acados_ocp.cost.cost_type_0 == 'LINEAR_LS': + acados_ocp.cost.Vx_0 = np.zeros((acados_ocp.dims.ny_0, acados_ocp.dims.nx)) + acados_ocp.cost.Vu_0 = np.zeros((acados_ocp.dims.ny_0, acados_ocp.dims.nu)) + if not acados_ocp.cost.cost_type == 'LINEAR_LS': + acados_ocp.cost.Vx = np.zeros((acados_ocp.dims.ny, acados_ocp.dims.nx)) + acados_ocp.cost.Vu = np.zeros((acados_ocp.dims.ny, acados_ocp.dims.nu)) + if not acados_ocp.cost.cost_type_e == 'LINEAR_LS': + acados_ocp.cost.Vx_e = np.zeros((acados_ocp.dims.ny_e, acados_ocp.dims.nx)) + + if acados_ocp.cost.cost_type_0 == 'NONLINEAR_LS': + generate_c_code_nls_cost(model, model.name, 'initial', opts) + elif acados_ocp.cost.cost_type_0 == 'EXTERNAL': + generate_c_code_external_cost(model, 'initial', opts) + + if acados_ocp.cost.cost_type == 'NONLINEAR_LS': + generate_c_code_nls_cost(model, model.name, 'path', opts) + elif acados_ocp.cost.cost_type == 'EXTERNAL': + generate_c_code_external_cost(model, 'path', opts) + + if acados_ocp.cost.cost_type_e == 'NONLINEAR_LS': + generate_c_code_nls_cost(model, model.name, 'terminal', opts) + elif acados_ocp.cost.cost_type_e == 'EXTERNAL': + generate_c_code_external_cost(model, 'terminal', opts) + + +def ocp_render_templates(acados_ocp, json_file): + + name = acados_ocp.model.name + + # setting up loader and environment + json_path = '{cwd}/{json_file}'.format( + cwd=os.getcwd(), + json_file=json_file) + + if not os.path.exists(json_path): + raise Exception('{} not found!'.format(json_path)) + + code_export_dir = acados_ocp.code_export_directory + template_dir = code_export_dir + + ## Render templates + in_file = 'main.in.c' + out_file = f'main_{name}.c' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'acados_solver.in.c' + out_file = f'acados_solver_{name}.c' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'acados_solver.in.h' + out_file = f'acados_solver_{name}.h' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'Makefile.in' + out_file = 'Makefile' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'acados_solver_sfun.in.c' + out_file = f'acados_solver_sfunction_{name}.c' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'make_sfun.in.m' + out_file = f'make_sfun_{name}.m' + render_template(in_file, out_file, template_dir, json_path) + + # sim + in_file = 'acados_sim_solver.in.c' + out_file = f'acados_sim_solver_{name}.c' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'acados_sim_solver.in.h' + out_file = f'acados_sim_solver_{name}.h' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'main_sim.in.c' + out_file = f'main_sim_{name}.c' + render_template(in_file, out_file, template_dir, json_path) + + ## folder model + template_dir = f'{code_export_dir}/{name}_model/' + + in_file = 'model.in.h' + out_file = f'{name}_model.h' + render_template(in_file, out_file, template_dir, json_path) + + # constraints on convex over nonlinear function + if acados_ocp.constraints.constr_type == 'BGP' and acados_ocp.dims.nphi > 0: + # constraints on outer function + template_dir = f'{code_export_dir}/{name}_constraints/' + in_file = 'phi_constraint.in.h' + out_file = f'{name}_phi_constraint.h' + render_template(in_file, out_file, template_dir, json_path) + + # terminal constraints on convex over nonlinear function + if acados_ocp.constraints.constr_type_e == 'BGP' and acados_ocp.dims.nphi_e > 0: + # terminal constraints on outer function + template_dir = f'{code_export_dir}/{name}_constraints/' + in_file = 'phi_e_constraint.in.h' + out_file = f'{name}_phi_e_constraint.h' + render_template(in_file, out_file, template_dir, json_path) + + # nonlinear constraints + if acados_ocp.constraints.constr_type == 'BGH' and acados_ocp.dims.nh > 0: + template_dir = f'{code_export_dir}/{name}_constraints/' + in_file = 'h_constraint.in.h' + out_file = f'{name}_h_constraint.h' + render_template(in_file, out_file, template_dir, json_path) + + # terminal nonlinear constraints + if acados_ocp.constraints.constr_type_e == 'BGH' and acados_ocp.dims.nh_e > 0: + template_dir = f'{code_export_dir}/{name}_constraints/' + in_file = 'h_e_constraint.in.h' + out_file = f'{name}_h_e_constraint.h' + render_template(in_file, out_file, template_dir, json_path) + + # initial stage Nonlinear LS cost function + if acados_ocp.cost.cost_type_0 == 'NONLINEAR_LS': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'cost_y_0_fun.in.h' + out_file = f'{name}_cost_y_0_fun.h' + render_template(in_file, out_file, template_dir, json_path) + # external cost - terminal + elif acados_ocp.cost.cost_type_0 == 'EXTERNAL': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'external_cost_0.in.h' + out_file = f'{name}_external_cost_0.h' + render_template(in_file, out_file, template_dir, json_path) + + # path Nonlinear LS cost function + if acados_ocp.cost.cost_type == 'NONLINEAR_LS': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'cost_y_fun.in.h' + out_file = f'{name}_cost_y_fun.h' + render_template(in_file, out_file, template_dir, json_path) + + # terminal Nonlinear LS cost function + if acados_ocp.cost.cost_type_e == 'NONLINEAR_LS': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'cost_y_e_fun.in.h' + out_file = f'{name}_cost_y_e_fun.h' + render_template(in_file, out_file, template_dir, json_path) + + # external cost + if acados_ocp.cost.cost_type == 'EXTERNAL': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'external_cost.in.h' + out_file = f'{name}_external_cost.h' + render_template(in_file, out_file, template_dir, json_path) + + # external cost - terminal + if acados_ocp.cost.cost_type_e == 'EXTERNAL': + template_dir = f'{code_export_dir}/{name}_cost/' + in_file = 'external_cost_e.in.h' + out_file = f'{name}_external_cost_e.h' + render_template(in_file, out_file, template_dir, json_path) + + +def remove_x0_elimination(acados_ocp): + acados_ocp.constraints.idxbxe_0 = np.zeros((0,)) + acados_ocp.dims.nbxe_0 = 0 + + +class AcadosOcpSolver: + """ + Class to interact with the acados ocp solver C object. + + :param acados_ocp: type AcadosOcp - description of the OCP for acados + :param json_file: name for the json file used to render the templated code - default: acados_ocp_nlp.json + :param simulink_opts: Options to configure Simulink S-function blocks, mainly to activate possible Inputs and Outputs + :param build: Option to disable rendering templates and compiling if previously built - default: True + """ + if sys.platform=="win32": + from ctypes import wintypes + dlclose = WinDLL('kernel32', use_last_error=True).FreeLibrary + dlclose.argtypes = [wintypes.HMODULE] + else: + dlclose = CDLL(None).dlclose + dlclose.argtypes = [c_void_p] + + def __init__(self, acados_ocp, json_file='acados_ocp_nlp.json', simulink_opts=None, build=True): + + self.solver_created = False + self.N = acados_ocp.dims.N + model = acados_ocp.model + + if simulink_opts == None: + acados_path = get_acados_path() + json_path = os.path.join(acados_path, 'interfaces/acados_template/acados_template') + with open(json_path + '/simulink_default_opts.json', 'r') as f: + simulink_opts = json.load(f) + + # make dims consistent + make_ocp_dims_consistent(acados_ocp) + + # module dependent post processing + if acados_ocp.solver_options.integrator_type == 'GNSF': + set_up_imported_gnsf_model(acados_ocp) + + if acados_ocp.solver_options.qp_solver == 'PARTIAL_CONDENSING_QPDUNES': + remove_x0_elimination(acados_ocp) + + # set integrator time automatically + acados_ocp.solver_options.Tsim = acados_ocp.solver_options.time_steps[0] + + # generate external functions + ocp_generate_external_functions(acados_ocp, model) + + # dump to json + ocp_formulation_json_dump(acados_ocp, simulink_opts, json_file) + + code_export_dir = acados_ocp.code_export_directory + if build: + # render templates + ocp_render_templates(acados_ocp, json_file) + + ## Compile solver + cwd = os.getcwd() + os.chdir(code_export_dir) + os.system('make clean_ocp_shared_lib') + os.system('make ocp_shared_lib') + os.chdir(cwd) + + self.shared_lib_name = f'{code_export_dir}/libacados_ocp_solver_{model.name}.so' + + # get shared_lib + self.shared_lib = CDLL(self.shared_lib_name) + + # create capsule + getattr(self.shared_lib, f"{model.name}_acados_create_capsule").restype = c_void_p + self.capsule = getattr(self.shared_lib, f"{model.name}_acados_create_capsule")() + + # create solver + getattr(self.shared_lib, f"{model.name}_acados_create").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_create").restype = c_int + assert getattr(self.shared_lib, f"{model.name}_acados_create")(self.capsule)==0 + self.solver_created = True + + # get pointers solver + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_opts").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_opts").restype = c_void_p + self.nlp_opts = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_opts")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_dims").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_dims").restype = c_void_p + self.nlp_dims = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_dims")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_config").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_config").restype = c_void_p + self.nlp_config = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_config")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_out").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_out").restype = c_void_p + self.nlp_out = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_out")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_in").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_in").restype = c_void_p + self.nlp_in = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_in")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_solver").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_get_nlp_solver").restype = c_void_p + self.nlp_solver = getattr(self.shared_lib, f"{model.name}_acados_get_nlp_solver")(self.capsule) + + self.acados_ocp = acados_ocp + + + def solve(self): + """ + Solve the ocp with current input. + """ + model = self.acados_ocp.model + + getattr(self.shared_lib, f"{model.name}_acados_solve").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_solve").restype = c_int + status = getattr(self.shared_lib, f"{model.name}_acados_solve")(self.capsule) + return status + + + def get(self, stage_, field_): + """ + Get the last solution of the solver: + + :param stage: integer corresponding to shooting node + :param field: string in ['x', 'u', 'z', 'pi', 'lam', 't', 'sl', 'su',] + + .. note:: regarding lam, t: \n + the inequalities are internally organized in the following order: \n + [ lbu lbx lg lh lphi ubu ubx ug uh uphi; \n + lsbu lsbx lsg lsh lsphi usbu usbx usg ush usphi] + + .. note:: pi: multipliers for dynamics equality constraints \n + lam: multipliers for inequalities \n + t: slack variables corresponding to evaluation of all inequalities (at the solution) \n + sl: slack variables of soft lower inequality constraints \n + su: slack variables of soft upper inequality constraints \n + """ + + out_fields = ['x', 'u', 'z', 'pi', 'lam', 't'] + mem_fields = ['sl', 'su'] + field = field_ + field = field.encode('utf-8') + + if (field_ not in out_fields + mem_fields): + raise Exception('AcadosOcpSolver.get(): {} is an invalid argument.\ + \n Possible values are {}. Exiting.'.format(field_, out_fields + mem_fields)) + + if not isinstance(stage_, int): + raise Exception('AcadosOcpSolver.get(): stage index must be Integer.') + + if stage_ < 0 or stage_ > self.N: + raise Exception('AcadosOcpSolver.get(): stage index must be in [0, N], got: {}.'.format(self.N)) + + if stage_ == self.N and field_ == 'pi': + raise Exception('AcadosOcpSolver.get(): field {} does not exist at final stage {}.'\ + .format(field_, stage_)) + + self.shared_lib.ocp_nlp_dims_get_from_attr.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p] + self.shared_lib.ocp_nlp_dims_get_from_attr.restype = c_int + + dims = self.shared_lib.ocp_nlp_dims_get_from_attr(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field) + + out = np.ascontiguousarray(np.zeros((dims,)), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + if (field_ in out_fields): + self.shared_lib.ocp_nlp_out_get.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_out_get(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field, out_data) + elif field_ in mem_fields: + self.shared_lib.ocp_nlp_get_at_stage.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_get_at_stage(self.nlp_config, \ + self.nlp_dims, self.nlp_solver, stage_, field, out_data) + + return out + + + def print_statistics(self): + """ + prints statistics of previous solver run as a table: + - iter: iteration number + - res_stat: stationarity residual + - res_eq: residual wrt equality constraints (dynamics) + - res_ineq: residual wrt inequality constraints (constraints) + - res_comp: residual wrt complementarity conditions + - qp_stat: status of QP solver + - qp_iter: number of QP iterations + - qp_res_stat: stationarity residual of the last QP solution + - qp_res_eq: residual wrt equality constraints (dynamics) of the last QP solution + - qp_res_ineq: residual wrt inequality constraints (constraints) of the last QP solution + - qp_res_comp: residual wrt complementarity conditions of the last QP solution + """ + stat = self.get_stats("statistics") + + if self.acados_ocp.solver_options.nlp_solver_type == 'SQP': + print('\niter\tres_stat\tres_eq\t\tres_ineq\tres_comp\tqp_stat\tqp_iter') + if stat.shape[0]>7: + print('\tqp_res_stat\tqp_res_eq\tqp_res_ineq\tqp_res_comp') + for jj in range(stat.shape[1]): + print('{:d}\t{:e}\t{:e}\t{:e}\t{:e}\t{:d}\t{:d}'.format( \ + int(stat[0][jj]), stat[1][jj], stat[2][jj], \ + stat[3][jj], stat[4][jj], int(stat[5][jj]), int(stat[6][jj]))) + if stat.shape[0]>7: + print('\t{:e}\t{:e}\t{:e}\t{:e}'.format( \ + stat[7][jj], stat[8][jj], stat[9][jj], stat[10][jj])) + print('\n') + elif self.acados_ocp.solver_options.nlp_solver_type == 'SQP_RTI': + print('\niter\tqp_stat\tqp_iter') + if stat.shape[0]>3: + print('\tqp_res_stat\tqp_res_eq\tqp_res_ineq\tqp_res_comp') + for jj in range(stat.shape[1]): + print('{:d}\t{:d}\t{:d}'.format( int(stat[0][jj]), int(stat[1][jj]), int(stat[2][jj]))) + if stat.shape[0]>3: + print('\t{:e}\t{:e}\t{:e}\t{:e}'.format( \ + stat[3][jj], stat[4][jj], stat[5][jj], stat[6][jj])) + print('\n') + + return + + + def store_iterate(self, filename='', overwrite=False): + """ + Stores the current iterate of the ocp solver in a json file. + + :param filename: if not set, use model_name + timestamp + '.json' + :param overwrite: if false and filename exists add timestamp to filename + """ + if filename == '': + filename += self.acados_ocp.model.name + '_' + 'iterate' + '.json' + + if not overwrite: + # append timestamp + if os.path.isfile(filename): + filename = filename[:-5] + filename += datetime.utcnow().strftime('%Y-%m-%d-%H:%M:%S.%f') + '.json' + + # get iterate: + solution = dict() + + for i in range(self.N+1): + solution['x_'+str(i)] = self.get(i,'x') + solution['u_'+str(i)] = self.get(i,'u') + solution['z_'+str(i)] = self.get(i,'z') + solution['lam_'+str(i)] = self.get(i,'lam') + solution['t_'+str(i)] = self.get(i, 't') + solution['sl_'+str(i)] = self.get(i, 'sl') + solution['su_'+str(i)] = self.get(i, 'su') + for i in range(self.N): + solution['pi_'+str(i)] = self.get(i,'pi') + + # save + with open(filename, 'w') as f: + json.dump(solution, f, default=np_array_to_list, indent=4, sort_keys=True) + print("stored current iterate in ", os.path.join(os.getcwd(), filename)) + + + def load_iterate(self, filename): + """ + Loads the iterate stored in json file with filename into the ocp solver. + """ + if not os.path.isfile(filename): + raise Exception('load_iterate: failed, file does not exist: ' + os.path.join(os.getcwd(), filename)) + + with open(filename, 'r') as f: + solution = json.load(f) + + for key in solution.keys(): + (field, stage) = key.split('_') + self.set(int(stage), field, np.array(solution[key])) + + + def get_stats(self, field_): + """ + Get the information of the last solver call. + + :param field: string in ['statistics', 'time_tot', 'time_lin', 'time_sim', 'time_sim_ad', 'time_sim_la', 'time_qp', 'time_qp_solver_call', 'time_reg', 'sqp_iter'] + """ + + fields = ['time_tot', # total cpu time previous call + 'time_lin', # cpu time for linearization + 'time_sim', # cpu time for integrator + 'time_sim_ad', # cpu time for integrator contribution of external function calls + 'time_sim_la', # cpu time for integrator contribution of linear algebra + 'time_qp', # cpu time qp solution + 'time_qp_solver_call', # cpu time inside qp solver (without converting the QP) + 'time_qp_xcond', + 'time_glob', # cpu time globalization + 'time_reg', # cpu time regularization + 'sqp_iter', # number of SQP iterations + 'qp_iter', # vector of QP iterations for last SQP call + 'statistics', # table with info about last iteration + 'stat_m', + 'stat_n', + ] + + field = field_ + field = field.encode('utf-8') + if (field_ not in fields): + raise Exception('AcadosOcpSolver.get_stats(): {} is not a valid argument.\ + \n Possible values are {}. Exiting.'.format(fields, fields)) + + if field_ in ['sqp_iter', 'stat_m', 'stat_n']: + out = np.ascontiguousarray(np.zeros((1,)), dtype=np.int64) + out_data = cast(out.ctypes.data, POINTER(c_int64)) + + elif field_ == 'statistics': + sqp_iter = self.get_stats("sqp_iter") + stat_m = self.get_stats("stat_m") + stat_n = self.get_stats("stat_n") + + min_size = min([stat_m, sqp_iter+1]) + + out = np.ascontiguousarray( + np.zeros( (stat_n[0]+1, min_size[0]) ), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + elif field_ == 'qp_iter': + full_stats = self.get_stats('statistics') + if self.acados_ocp.solver_options.nlp_solver_type == 'SQP': + out = full_stats[6, :] + elif self.acados_ocp.solver_options.nlp_solver_type == 'SQP_RTI': + out = full_stats[2, :] + + else: + out = np.ascontiguousarray(np.zeros((1,)), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + if not field_ == 'qp_iter': + self.shared_lib.ocp_nlp_get.argtypes = [c_void_p, c_void_p, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + return out + + + def get_cost(self): + """ + Returns the cost value of the current solution. + """ + # compute cost internally + self.shared_lib.ocp_nlp_eval_cost.argtypes = [c_void_p, c_void_p, c_void_p] + self.shared_lib.ocp_nlp_eval_cost(self.nlp_solver, self.nlp_in, self.nlp_out) + + # create output array + out = np.ascontiguousarray(np.zeros((1,)), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + # call getter + self.shared_lib.ocp_nlp_get.argtypes = [c_void_p, c_void_p, c_char_p, c_void_p] + + field = "cost_value".encode('utf-8') + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + return out[0] + + + def get_residuals(self): + """ + Returns an array of the form [res_stat, res_eq, res_ineq, res_comp]. + """ + # compute residuals if RTI + if self.acados_ocp.solver_options.nlp_solver_type == 'SQP_RTI': + self.shared_lib.ocp_nlp_eval_residuals.argtypes = [c_void_p, c_void_p, c_void_p] + self.shared_lib.ocp_nlp_eval_residuals(self.nlp_solver, self.nlp_in, self.nlp_out) + + # create output array + out = np.ascontiguousarray(np.zeros((4, 1)), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + # call getters + self.shared_lib.ocp_nlp_get.argtypes = [c_void_p, c_void_p, c_char_p, c_void_p] + + field = "res_stat".encode('utf-8') + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + out_data = cast(out[1].ctypes.data, POINTER(c_double)) + field = "res_eq".encode('utf-8') + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + out_data = cast(out[2].ctypes.data, POINTER(c_double)) + field = "res_ineq".encode('utf-8') + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + out_data = cast(out[3].ctypes.data, POINTER(c_double)) + field = "res_comp".encode('utf-8') + self.shared_lib.ocp_nlp_get(self.nlp_config, self.nlp_solver, field, out_data) + + return out.flatten() + + + # Note: this function should not be used anymore, better use cost_set, constraints_set + def set(self, stage_, field_, value_): + """ + Set numerical data inside the solver. + + :param stage: integer corresponding to shooting node + :param field: string in ['x', 'u', 'pi', 'lam', 't', 'p'] + + .. note:: regarding lam, t: \n + the inequalities are internally organized in the following order: \n + [ lbu lbx lg lh lphi ubu ubx ug uh uphi; \n + lsbu lsbx lsg lsh lsphi usbu usbx usg ush usphi] + + .. note:: pi: multipliers for dynamics equality constraints \n + lam: multipliers for inequalities \n + t: slack variables corresponding to evaluation of all inequalities (at the solution) \n + sl: slack variables of soft lower inequality constraints \n + su: slack variables of soft upper inequality constraints \n + """ + cost_fields = ['y_ref', 'yref'] + constraints_fields = ['lbx', 'ubx', 'lbu', 'ubu'] + out_fields = ['x', 'u', 'pi', 'lam', 't', 'z'] + mem_fields = ['sl', 'su'] + + # cast value_ to avoid conversion issues + if isinstance(value_, (float, int)): + value_ = np.array([value_]) + value_ = value_.astype(float) + + model = self.acados_ocp.model + + field = field_ + field = field.encode('utf-8') + + stage = c_int(stage_) + + # treat parameters separately + if field_ == 'p': + getattr(self.shared_lib, f"{model.name}_acados_update_params").argtypes = [c_void_p, c_int, POINTER(c_double)] + getattr(self.shared_lib, f"{model.name}_acados_update_params").restype = c_int + + value_data = cast(value_.ctypes.data, POINTER(c_double)) + + assert getattr(self.shared_lib, f"{model.name}_acados_update_params")(self.capsule, stage, value_data, value_.shape[0])==0 + else: + if field_ not in constraints_fields + cost_fields + out_fields + mem_fields: + raise Exception("AcadosOcpSolver.set(): {} is not a valid argument.\ + \nPossible values are {}. Exiting.".format(field, \ + constraints_fields + cost_fields + out_fields + ['p'])) + + self.shared_lib.ocp_nlp_dims_get_from_attr.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p] + self.shared_lib.ocp_nlp_dims_get_from_attr.restype = c_int + + dims = self.shared_lib.ocp_nlp_dims_get_from_attr(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field) + + if value_.shape[0] != dims: + msg = 'AcadosOcpSolver.set(): mismatching dimension for field "{}" '.format(field_) + msg += 'with dimension {} (you have {})'.format(dims, value_.shape[0]) + raise Exception(msg) + + value_data = cast(value_.ctypes.data, POINTER(c_double)) + value_data_p = cast((value_data), c_void_p) + + if field_ in constraints_fields: + self.shared_lib.ocp_nlp_constraints_model_set.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_constraints_model_set(self.nlp_config, \ + self.nlp_dims, self.nlp_in, stage, field, value_data_p) + elif field_ in cost_fields: + self.shared_lib.ocp_nlp_cost_model_set.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_cost_model_set(self.nlp_config, \ + self.nlp_dims, self.nlp_in, stage, field, value_data_p) + elif field_ in out_fields: + self.shared_lib.ocp_nlp_out_set.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_out_set(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage, field, value_data_p) + elif field_ in mem_fields: + self.shared_lib.ocp_nlp_set.argtypes = \ + [c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_set(self.nlp_config, \ + self.nlp_solver, stage, field, value_data_p) + return + + + def cost_set(self, stage_, field_, value_, api='warn'): + """ + Set numerical data in the cost module of the solver. + + :param stage: integer corresponding to shooting node + :param field: string, e.g. 'yref', 'W', 'ext_cost_num_hess' + :param value: of appropriate size + """ + # cast value_ to avoid conversion issues + if isinstance(value_, (float, int)): + value_ = np.array([value_]) + value_ = value_.astype(float) + + field = field_ + field = field.encode('utf-8') + + stage = c_int(stage_) + self.shared_lib.ocp_nlp_cost_dims_get_from_attr.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, POINTER(c_int)] + self.shared_lib.ocp_nlp_cost_dims_get_from_attr.restype = c_int + + dims = np.ascontiguousarray(np.zeros((2,)), dtype=np.intc) + dims_data = cast(dims.ctypes.data, POINTER(c_int)) + + self.shared_lib.ocp_nlp_cost_dims_get_from_attr(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field, dims_data) + + value_shape = value_.shape + if len(value_shape) == 1: + value_shape = (value_shape[0], 0) + + elif len(value_shape) == 2: + if api=='old': + pass + elif api=='warn': + if not np.all(np.ravel(value_, order='F')==np.ravel(value_, order='K')): + raise Exception("Ambiguity in API detected.\n" + "Are you making an acados model from scrach? Add api='new' to cost_set and carry on.\n" + "Are you seeing this error suddenly in previously running code? Read on.\n" + " You are relying on a now-fixed bug in cost_set for field '{}'.\n".format(field_) + + " acados_template now correctly passes on any matrices to acados in column major format.\n" + + " Two options to fix this error: \n" + + " * Add api='old' to cost_set to restore old incorrect behaviour\n" + + " * Add api='new' to cost_set and remove any unnatural manipulation of the value argument " + + "such as non-mathematical transposes, reshaping, casting to fortran order, etc... " + + "If there is no such manipulation, then you have probably been getting an incorrect solution before.") + # Get elements in column major order + value_ = np.ravel(value_, order='F') + elif api=='new': + # Get elements in column major order + value_ = np.ravel(value_, order='F') + else: + raise Exception("Unknown api: '{}'".format(api)) + + if value_shape != tuple(dims): + raise Exception('AcadosOcpSolver.cost_set(): mismatching dimension', \ + ' for field "{}" with dimension {} (you have {})'.format( \ + field_, tuple(dims), value_shape)) + + value_data = cast(value_.ctypes.data, POINTER(c_double)) + value_data_p = cast((value_data), c_void_p) + + self.shared_lib.ocp_nlp_cost_model_set.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_cost_model_set(self.nlp_config, \ + self.nlp_dims, self.nlp_in, stage, field, value_data_p) + + return + + + def constraints_set(self, stage_, field_, value_, api='warn'): + """ + Set numerical data in the constraint module of the solver. + + :param stage: integer corresponding to shooting node + :param field: string in ['lbx', 'ubx', 'lbu', 'ubu', 'lg', 'ug', 'lh', 'uh', 'uphi'] + :param value: of appropriate size + """ + # cast value_ to avoid conversion issues + if isinstance(value_, (float, int)): + value_ = np.array([value_]) + value_ = value_.astype(float) + + field = field_ + field = field.encode('utf-8') + + stage = c_int(stage_) + self.shared_lib.ocp_nlp_constraint_dims_get_from_attr.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, POINTER(c_int)] + self.shared_lib.ocp_nlp_constraint_dims_get_from_attr.restype = c_int + + dims = np.ascontiguousarray(np.zeros((2,)), dtype=np.intc) + dims_data = cast(dims.ctypes.data, POINTER(c_int)) + + self.shared_lib.ocp_nlp_constraint_dims_get_from_attr(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field, dims_data) + + value_shape = value_.shape + if len(value_shape) == 1: + value_shape = (value_shape[0], 0) + elif len(value_shape) == 2: + if api=='old': + pass + elif api=='warn': + if not np.all(np.ravel(value_, order='F')==np.ravel(value_, order='K')): + raise Exception("Ambiguity in API detected.\n" + "Are you making an acados model from scrach? Add api='new' to constraints_set and carry on.\n" + "Are you seeing this error suddenly in previously running code? Read on.\n" + " You are relying on a now-fixed bug in constraints_set for field '{}'.\n".format(field_) + + " acados_template now correctly passes on any matrices to acados in column major format.\n" + + " Two options to fix this error: \n" + + " * Add api='old' to constraints_set to restore old incorrect behaviour\n" + + " * Add api='new' to constraints_set and remove any unnatural manipulation of the value argument " + + "such as non-mathematical transposes, reshaping, casting to fortran order, etc... " + + "If there is no such manipulation, then you have probably been getting an incorrect solution before.") + # Get elements in column major order + value_ = np.ravel(value_, order='F') + elif api=='new': + # Get elements in column major order + value_ = np.ravel(value_, order='F') + else: + raise Exception("Unknown api: '{}'".format(api)) + + if value_shape != tuple(dims): + raise Exception('AcadosOcpSolver.constraints_set(): mismatching dimension' \ + ' for field "{}" with dimension {} (you have {})'.format(field_, tuple(dims), value_shape)) + + value_data = cast(value_.ctypes.data, POINTER(c_double)) + value_data_p = cast((value_data), c_void_p) + + self.shared_lib.ocp_nlp_constraints_model_set.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_constraints_model_set(self.nlp_config, \ + self.nlp_dims, self.nlp_in, stage, field, value_data_p) + + return + + + def dynamics_get(self, stage_, field_): + """ + Get numerical data from the dynamics module of the solver: + + :param stage: integer corresponding to shooting node + :param field: string, e.g. 'A' + """ + + field = field_ + field = field.encode('utf-8') + stage = c_int(stage_) + + # get dims + self.shared_lib.ocp_nlp_dynamics_dims_get_from_attr.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, POINTER(c_int)] + self.shared_lib.ocp_nlp_dynamics_dims_get_from_attr.restype = c_int + + dims = np.ascontiguousarray(np.zeros((2,)), dtype=np.intc) + dims_data = cast(dims.ctypes.data, POINTER(c_int)) + + self.shared_lib.ocp_nlp_dynamics_dims_get_from_attr(self.nlp_config, \ + self.nlp_dims, self.nlp_out, stage_, field, dims_data) + + # create output data + out = np.ascontiguousarray(np.zeros((np.prod(dims),)), dtype=np.float64) + out = out.reshape(dims[0], dims[1], order='F') + + out_data = cast(out.ctypes.data, POINTER(c_double)) + out_data_p = cast((out_data), c_void_p) + + # call getter + self.shared_lib.ocp_nlp_get_at_stage.argtypes = \ + [c_void_p, c_void_p, c_void_p, c_int, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_get_at_stage(self.nlp_config, \ + self.nlp_dims, self.nlp_solver, stage, field, out_data_p) + + return out + + + def options_set(self, field_, value_): + """ + Set options of the solver. + + :param field: string, e.g. 'print_level', 'rti_phase', 'initialize_t_slacks', 'step_length', 'alpha_min', 'alpha_reduction' + :param value: of type int, float + """ + int_fields = ['print_level', 'rti_phase', 'initialize_t_slacks'] + double_fields = ['step_length', 'tol_eq', 'tol_stat', 'tol_ineq', 'tol_comp', 'alpha_min', 'alpha_reduction'] + string_fields = ['globalization'] + + # check field availability and type + if field_ in int_fields: + if not isinstance(value_, int): + raise Exception('solver option {} must be of type int. You have {}.'.format(field_, type(value_))) + else: + value_ctypes = c_int(value_) + + elif field_ in double_fields: + if not isinstance(value_, float): + raise Exception('solver option {} must be of type float. You have {}.'.format(field_, type(value_))) + else: + value_ctypes = c_double(value_) + + elif field_ in string_fields: + if not isinstance(value_, str): + raise Exception('solver option {} must be of type str. You have {}.'.format(field_, type(value_))) + else: + value_ctypes = value_.encode('utf-8') + else: + raise Exception('AcadosOcpSolver.options_set() does not support field {}.'\ + '\n Possible values are {}.'.format(field_, ', '.join(int_fields + double_fields + string_fields))) + + + if field_ == 'rti_phase': + if value_ < 0 or value_ > 2: + raise Exception('AcadosOcpSolver.solve(): argument \'rti_phase\' can ' + 'take only values 0, 1, 2 for SQP-RTI-type solvers') + if self.acados_ocp.solver_options.nlp_solver_type != 'SQP_RTI' and value_ > 0: + raise Exception('AcadosOcpSolver.solve(): argument \'rti_phase\' can ' + 'take only value 0 for SQP-type solvers') + + # encode + field = field_ + field = field.encode('utf-8') + + # call C interface + if field_ in string_fields: + self.shared_lib.ocp_nlp_solver_opts_set.argtypes = \ + [c_void_p, c_void_p, c_char_p, c_char_p] + self.shared_lib.ocp_nlp_solver_opts_set(self.nlp_config, \ + self.nlp_opts, field, value_ctypes) + else: + self.shared_lib.ocp_nlp_solver_opts_set.argtypes = \ + [c_void_p, c_void_p, c_char_p, c_void_p] + self.shared_lib.ocp_nlp_solver_opts_set(self.nlp_config, \ + self.nlp_opts, field, byref(value_ctypes)) + + return + + + def __del__(self): + model = self.acados_ocp.model + + if self.solver_created: + getattr(self.shared_lib, f"{model.name}_acados_free").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_free").restype = c_int + getattr(self.shared_lib, f"{model.name}_acados_free")(self.capsule) + + getattr(self.shared_lib, f"{model.name}_acados_free_capsule").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model.name}_acados_free_capsule").restype = c_int + getattr(self.shared_lib, f"{model.name}_acados_free_capsule")(self.capsule) + + try: + self.dlclose(self.shared_lib._handle) + except: + pass diff --git a/pyextra/acados_template/acados_sim.py b/pyextra/acados_template/acados_sim.py new file mode 100644 index 0000000000..9f2e19c5df --- /dev/null +++ b/pyextra/acados_template/acados_sim.py @@ -0,0 +1,294 @@ +# -*- coding: future_fstrings -*- +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import numpy as np +import casadi as ca +import os +from .acados_model import AcadosModel +from .utils import get_acados_path + +class AcadosSimDims: + """ + Class containing the dimensions of the model to be simulated. + """ + def __init__(self): + self.__nx = None + self.__nu = None + self.__nz = 0 + self.__np = 0 + + @property + def nx(self): + """:math:`n_x` - number of states. Type: int > 0""" + return self.__nx + + @property + def nz(self): + """:math:`n_z` - number of algebraic variables. Type: int >= 0""" + return self.__nz + + @property + def nu(self): + """:math:`n_u` - number of inputs. Type: int >= 0""" + return self.__nu + + @property + def np(self): + """:math:`n_p` - number of parameters. Type: int >= 0""" + return self.__np + + @nx.setter + def nx(self, nx): + if type(nx) == int and nx > 0: + self.__nx = nx + else: + raise Exception('Invalid nx value, expected positive integer. Exiting.') + + @nz.setter + def nz(self, nz): + if type(nz) == int and nz > -1: + self.__nz = nz + else: + raise Exception('Invalid nz value, expected nonnegative integer. Exiting.') + + @nu.setter + def nu(self, nu): + if type(nu) == int and nu > -1: + self.__nu = nu + else: + raise Exception('Invalid nu value, expected nonnegative integer. Exiting.') + + @np.setter + def np(self, np): + if type(np) == int and np > -1: + self.__np = np + else: + raise Exception('Invalid np value, expected nonnegative integer. Exiting.') + + def set(self, attr, value): + setattr(self, attr, value) + + +class AcadosSimOpts: + """ + class containing the solver options + """ + def __init__(self): + self.__integrator_type = 'ERK' + self.__tf = None + # ints + self.__sim_method_num_stages = 1 + self.__sim_method_num_steps = 1 + self.__sim_method_newton_iter = 3 + # bools + self.__sens_forw = True + self.__sens_adj = False + self.__sens_algebraic = False + self.__sens_hess = False + self.__output_z = False + self.__sim_method_jac_reuse = False + + @property + def integrator_type(self): + """Integrator type. Default: 'ERK'.""" + return self.__integrator_type + + @property + def num_stages(self): + """Number of stages in the integrator. Default: 1""" + return self.__sim_method_num_stages + + @property + def num_steps(self): + """Number of steps in the integrator. Default: 1""" + return self.__sim_method_num_steps + + @property + def newton_iter(self): + """Number of Newton iterations in simulation method. Default: 3""" + return self.__sim_method_newton_iter + + @property + def sens_forw(self): + """Boolean determining if forward sensitivities are computed. Default: True""" + return self.__sens_forw + + @property + def sens_adj(self): + """Boolean determining if adjoint sensitivities are computed. Default: False""" + return self.__sens_adj + + @property + def sens_algebraic(self): + """Boolean determining if sensitivities wrt algebraic variables are computed. Default: False""" + return self.__sens_algebraic + + @property + def sens_hess(self): + """Boolean determining if hessians are computed. Default: False""" + return self.__sens_hess + + @property + def output_z(self): + """Boolean determining if values for algebraic variables (corresponding to start of simulation interval) are computed. Default: False""" + return self.__output_z + + @property + def sim_method_jac_reuse(self): + """Boolean determining if jacobians are reused. Default: False""" + return self.__sim_method_jac_reuse + + @property + def T(self): + """Time horizon""" + return self.__Tsim + + @integrator_type.setter + def integrator_type(self, integrator_type): + integrator_types = ('ERK', 'IRK', 'GNSF') + if integrator_type in integrator_types: + self.__integrator_type = integrator_type + else: + raise Exception('Invalid integrator_type value. Possible values are:\n\n' \ + + ',\n'.join(integrator_types) + '.\n\nYou have: ' + integrator_type + '.\n\nExiting.') + + @T.setter + def T(self, T): + self.__Tsim = T + + @num_stages.setter + def num_stages(self, num_stages): + if isinstance(num_stages, int): + self.__sim_method_num_stages = num_stages + else: + raise Exception('Invalid num_stages value. num_stages must be an integer.') + + @num_steps.setter + def num_steps(self, num_steps): + if isinstance(num_steps, int): + self.__sim_method_num_steps = num_steps + else: + raise Exception('Invalid num_steps value. num_steps must be an integer.') + + @newton_iter.setter + def newton_iter(self, newton_iter): + if isinstance(newton_iter, int): + self.__sim_method_newton_iter = newton_iter + else: + raise Exception('Invalid newton_iter value. newton_iter must be an integer.') + + @sens_forw.setter + def sens_forw(self, sens_forw): + if sens_forw in (True, False): + self.__sens_forw = sens_forw + else: + raise Exception('Invalid sens_forw value. sens_forw must be a Boolean.') + + @sens_adj.setter + def sens_adj(self, sens_adj): + if sens_adj in (True, False): + self.__sens_adj = sens_adj + else: + raise Exception('Invalid sens_adj value. sens_adj must be a Boolean.') + + @sens_hess.setter + def sens_hess(self, sens_hess): + if sens_hess in (True, False): + self.__sens_hess = sens_hess + else: + raise Exception('Invalid sens_hess value. sens_hess must be a Boolean.') + + @sens_algebraic.setter + def sens_algebraic(self, sens_algebraic): + if sens_algebraic in (True, False): + self.__sens_algebraic = sens_algebraic + else: + raise Exception('Invalid sens_algebraic value. sens_algebraic must be a Boolean.') + + @output_z.setter + def output_z(self, output_z): + if output_z in (True, False): + self.__output_z = output_z + else: + raise Exception('Invalid output_z value. output_z must be a Boolean.') + + @sim_method_jac_reuse.setter + def sim_method_jac_reuse(self, sim_method_jac_reuse): + if sim_method_jac_reuse in (True, False): + self.__sim_method_jac_reuse = sim_method_jac_reuse + else: + raise Exception('Invalid sim_method_jac_reuse value. sim_method_jac_reuse must be a Boolean.') + +class AcadosSim: + """ + The class has the following properties that can be modified to formulate a specific simulation problem, see below: + + :param acados_path: string with the path to acados. It is used to generate the include and lib paths. + + - :py:attr:`dims` of type :py:class:`acados_template.acados_ocp.AcadosSimDims` - are automatically detected from model + - :py:attr:`model` of type :py:class:`acados_template.acados_model.AcadosModel` + - :py:attr:`solver_options` of type :py:class:`acados_template.acados_sim.AcadosSimOpts` + + - :py:attr:`acados_include_path` (set automatically) + - :py:attr:`acados_lib_path` (set automatically) + """ + def __init__(self, acados_path=''): + if acados_path == '': + acados_path = get_acados_path() + self.dims = AcadosSimDims() + """Dimension definitions, automatically detected from :py:attr:`model`. Type :py:class:`acados_template.acados_sim.AcadosSimDims`""" + self.model = AcadosModel() + """Model definitions, type :py:class:`acados_template.acados_model.AcadosModel`""" + self.solver_options = AcadosSimOpts() + """Solver Options, type :py:class:`acados_template.acados_sim.AcadosSimOpts`""" + + self.acados_include_path = f'{acados_path}/include' + """Path to acados include directors (set automatically), type: `string`""" + self.acados_lib_path = f'{acados_path}/lib' + """Path to where acados library is located (set automatically), type: `string`""" + + self.code_export_directory = 'c_generated_code' + """Path to where code will be exported. Default: `c_generated_code`.""" + + def set(self, attr, value): + # tokenize string + tokens = attr.split('_', 1) + if len(tokens) > 1: + setter_to_call = getattr(getattr(self, tokens[0]), 'set') + else: + setter_to_call = getattr(self, 'set') + + setter_to_call(tokens[1], value) + + return diff --git a/pyextra/acados_template/acados_sim_layout.json b/pyextra/acados_template/acados_sim_layout.json new file mode 100644 index 0000000000..c8f4280b84 --- /dev/null +++ b/pyextra/acados_template/acados_sim_layout.json @@ -0,0 +1,44 @@ +{ + "acados_include_path": [ + "str" + ], + "model": { + "name" : [ + "str" + ] + }, + "acados_lib_path": [ + "str" + ], + "dims": { + "np": [ + "int" + ], + "nu": [ + "int" + ], + "nx": [ + "int" + ], + "nz": [ + "int" + ] + }, + "solver_options": { + "integrator_type": [ + "str" + ], + "Tsim": [ + "float" + ], + "sim_method_num_stages": [ + "int" + ], + "sim_method_num_steps": [ + "int" + ], + "sim_method_newton_iter": [ + "int" + ] + } +} diff --git a/pyextra/acados_template/acados_sim_solver.py b/pyextra/acados_template/acados_sim_solver.py new file mode 100644 index 0000000000..a5447907b9 --- /dev/null +++ b/pyextra/acados_template/acados_sim_solver.py @@ -0,0 +1,407 @@ +# -*- coding: future_fstrings -*- +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import sys, os, json + +import numpy as np + +from ctypes import * +from copy import deepcopy + +from .generate_c_code_explicit_ode import generate_c_code_explicit_ode +from .generate_c_code_implicit_ode import generate_c_code_implicit_ode +from .generate_c_code_gnsf import generate_c_code_gnsf +from .acados_sim import AcadosSim +from .acados_ocp import AcadosOcp +from .acados_model import acados_model_strip_casadi_symbolics +from .utils import is_column, render_template, format_class_dict, np_array_to_list,\ + make_model_consistent, set_up_imported_gnsf_model + + +def make_sim_dims_consistent(acados_sim): + dims = acados_sim.dims + model = acados_sim.model + # nx + if is_column(model.x): + dims.nx = model.x.shape[0] + else: + raise Exception("model.x should be column vector!") + + # nu + if is_column(model.u): + dims.nu = model.u.shape[0] + elif model.u == None or model.u == []: + dims.nu = 0 + else: + raise Exception("model.u should be column vector or None!") + + # nz + if is_column(model.z): + dims.nz = model.z.shape[0] + elif model.z == None or model.z == []: + dims.nz = 0 + else: + raise Exception("model.z should be column vector or None!") + + # np + if is_column(model.p): + dims.np = model.p.shape[0] + elif model.p == None or model.p == []: + dims.np = 0 + else: + raise Exception("model.p should be column vector or None!") + + +def get_sim_layout(): + current_module = sys.modules[__name__] + acados_path = os.path.dirname(current_module.__file__) + with open(acados_path + '/acados_sim_layout.json', 'r') as f: + sim_layout = json.load(f) + return sim_layout + + + +def sim_formulation_json_dump(acados_sim, json_file='acados_sim.json'): + # Load acados_sim structure description + sim_layout = get_sim_layout() + + # Copy input sim object dictionary + sim_dict = dict(deepcopy(acados_sim).__dict__) + + for key, v in sim_layout.items(): + # skip non dict attributes + if not isinstance(v, dict): continue + # Copy sim object attributes dictionaries + sim_dict[key]=dict(getattr(acados_sim, key).__dict__) + + sim_dict['model'] = acados_model_strip_casadi_symbolics(sim_dict['model']) + sim_json = format_class_dict(sim_dict) + + with open(json_file, 'w') as f: + json.dump(sim_json, f, default=np_array_to_list, indent=4, sort_keys=True) + + +def sim_render_templates(json_file, model_name, code_export_dir): + # setting up loader and environment + json_path = '{cwd}/{json_file}'.format( + cwd=os.getcwd(), + json_file=json_file) + + if not os.path.exists(json_path): + raise Exception(f"{json_path} not found!") + + template_dir = code_export_dir + + ## Render templates + in_file = 'acados_sim_solver.in.c' + out_file = f'acados_sim_solver_{model_name}.c' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'acados_sim_solver.in.h' + out_file = f'acados_sim_solver_{model_name}.h' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'Makefile.in' + out_file = f'Makefile' + render_template(in_file, out_file, template_dir, json_path) + + in_file = 'main_sim.in.c' + out_file = f'main_sim_{model_name}.c' + render_template(in_file, out_file, template_dir, json_path) + + ## folder model + template_dir = f'{code_export_dir}/{model_name}_model/' + + in_file = 'model.in.h' + out_file = f'{model_name}_model.h' + render_template(in_file, out_file, template_dir, json_path) + + +def sim_generate_casadi_functions(acados_sim): + model = acados_sim.model + model = make_model_consistent(model) + + integrator_type = acados_sim.solver_options.integrator_type + + opts = dict(generate_hess = acados_sim.solver_options.sens_hess, + code_export_directory = acados_sim.code_export_directory) + # generate external functions + if integrator_type == 'ERK': + generate_c_code_explicit_ode(model, opts) + elif integrator_type == 'IRK': + generate_c_code_implicit_ode(model, opts) + elif integrator_type == 'GNSF': + generate_c_code_gnsf(model, opts) + +class AcadosSimSolver: + """ + Class to interact with the acados integrator C object. + + :param acados_sim: type :py:class:`acados_template.acados_ocp.AcadosOcp` (takes values to generate an instance :py:class:`acados_template.acados_sim.AcadosSim`) or :py:class:`acados_template.acados_sim.AcadosSim` + :param json_file: Default: 'acados_sim.json' + :param build: Default: True + """ + def __init__(self, acados_sim_, json_file='acados_sim.json', build=True): + + self.solver_created = False + + if isinstance(acados_sim_, AcadosOcp): + # set up acados_sim_ + acados_sim = AcadosSim() + acados_sim.model = acados_sim_.model + acados_sim.dims.nx = acados_sim_.dims.nx + acados_sim.dims.nu = acados_sim_.dims.nu + acados_sim.dims.nz = acados_sim_.dims.nz + acados_sim.dims.np = acados_sim_.dims.np + acados_sim.solver_options.integrator_type = acados_sim_.solver_options.integrator_type + acados_sim.code_export_directory = acados_sim_.code_export_directory + + elif isinstance(acados_sim_, AcadosSim): + acados_sim = acados_sim_ + + acados_sim.__problem_class = 'SIM' + + model_name = acados_sim.model.name + make_sim_dims_consistent(acados_sim) + + # reuse existing json and casadi functions, when creating integrator from ocp + if isinstance(acados_sim_, AcadosSim): + if acados_sim.solver_options.integrator_type == 'GNSF': + set_up_imported_gnsf_model(acados_sim) + + sim_generate_casadi_functions(acados_sim) + sim_formulation_json_dump(acados_sim, json_file) + + code_export_dir = acados_sim.code_export_directory + if build: + # render templates + sim_render_templates(json_file, model_name, code_export_dir) + + ## Compile solver + cwd = os.getcwd() + os.chdir(code_export_dir) + os.system('make sim_shared_lib') + os.chdir(cwd) + + self.sim_struct = acados_sim + model_name = self.sim_struct.model.name + self.model_name = model_name + + # Ctypes + shared_lib = f'{code_export_dir}/libacados_sim_solver_{model_name}.so' + self.shared_lib = CDLL(shared_lib) + + + # create capsule + getattr(self.shared_lib, f"{model_name}_acados_sim_solver_create_capsule").restype = c_void_p + self.capsule = getattr(self.shared_lib, f"{model_name}_acados_sim_solver_create_capsule")() + + # create solver + getattr(self.shared_lib, f"{model_name}_acados_sim_create").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_sim_create").restype = c_int + assert getattr(self.shared_lib, f"{model_name}_acados_sim_create")(self.capsule)==0 + self.solver_created = True + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_opts").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_opts").restype = c_void_p + self.sim_opts = getattr(self.shared_lib, f"{model_name}_acados_get_sim_opts")(self.capsule) + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_dims").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_dims").restype = c_void_p + self.sim_dims = getattr(self.shared_lib, f"{model_name}_acados_get_sim_dims")(self.capsule) + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_config").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_config").restype = c_void_p + self.sim_config = getattr(self.shared_lib, f"{model_name}_acados_get_sim_config")(self.capsule) + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_out").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_out").restype = c_void_p + self.sim_out = getattr(self.shared_lib, f"{model_name}_acados_get_sim_out")(self.capsule) + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_in").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_in").restype = c_void_p + self.sim_in = getattr(self.shared_lib, f"{model_name}_acados_get_sim_in")(self.capsule) + + getattr(self.shared_lib, f"{model_name}_acados_get_sim_solver").argtypes = [c_void_p] + getattr(self.shared_lib, f"{model_name}_acados_get_sim_solver").restype = c_void_p + self.sim_solver = getattr(self.shared_lib, f"{model_name}_acados_get_sim_solver")(self.capsule) + + nu = self.sim_struct.dims.nu + nx = self.sim_struct.dims.nx + nz = self.sim_struct.dims.nz + self.gettable = { + 'x': nx, + 'xn': nx, + 'u': nu, + 'z': nz, + 'S_forw': nx*(nx+nu), + 'Sx': nx*nx, + 'Su': nx*nu, + 'S_adj': nx+nu, + 'S_hess': (nx+nu)*(nx+nu), + 'S_algebraic': (nz)*(nx+nu), + } + + self.settable = ['S_adj', 'T', 'x', 'u', 'xdot', 'z', 'p'] # S_forw + + + def solve(self): + """ + Solve the simulation problem with current input. + """ + getattr(self.shared_lib, f"{self.model_name}_acados_sim_solve").argtypes = [c_void_p] + getattr(self.shared_lib, f"{self.model_name}_acados_sim_solve").restype = c_int + + status = getattr(self.shared_lib, f"{self.model_name}_acados_sim_solve")(self.capsule) + return status + + + def get(self, field_): + """ + Get the last solution of the solver. + + :param str field: string in ['x', 'u', 'z', 'S_forw', 'Sx', 'Su', 'S_adj', 'S_hess', 'S_algebraic'] + """ + field = field_ + field = field.encode('utf-8') + + if field_ in self.gettable.keys(): + + # allocate array + dims = self.gettable[field_] + out = np.ascontiguousarray(np.zeros((dims,)), dtype=np.float64) + out_data = cast(out.ctypes.data, POINTER(c_double)) + + self.shared_lib.sim_out_get.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p, c_void_p] + self.shared_lib.sim_out_get(self.sim_config, self.sim_dims, self.sim_out, field, out_data) + + if field_ == 'S_forw': + nu = self.sim_struct.dims.nu + nx = self.sim_struct.dims.nx + out = out.reshape(nx, nx+nu, order='F') + elif field_ == 'Sx': + nx = self.sim_struct.dims.nx + out = out.reshape(nx, nx, order='F') + elif field_ == 'Su': + nx = self.sim_struct.dims.nx + nu = self.sim_struct.dims.nu + out = out.reshape(nx, nu, order='F') + elif field_ == 'S_hess': + nx = self.sim_struct.dims.nx + nu = self.sim_struct.dims.nu + out = out.reshape(nx+nu, nx+nu, order='F') + elif field_ == 'S_algebraic': + nx = self.sim_struct.dims.nx + nu = self.sim_struct.dims.nu + nz = self.sim_struct.dims.nz + out = out.reshape(nz, nx+nu, order='F') + else: + raise Exception(f'AcadosSimSolver.get(): Unknown field {field_},' \ + f' available fields are {", ".join(self.gettable.keys())}') + + return out + + + def set(self, field_, value_): + """ + Set numerical data inside the solver. + + :param field: string in ['p', 'S_adj', 'T', 'x', 'u', 'xdot', 'z'] + :param value: the value with appropriate size. + """ + # cast value_ to avoid conversion issues + if isinstance(value_, (float, int)): + value_ = np.array([value_]) + + value_ = value_.astype(float) + value_data = cast(value_.ctypes.data, POINTER(c_double)) + value_data_p = cast((value_data), c_void_p) + + field = field_ + field = field.encode('utf-8') + + # treat parameters separately + if field_ == 'p': + model_name = self.sim_struct.model.name + getattr(self.shared_lib, f"{model_name}_acados_sim_update_params").argtypes = [c_void_p, POINTER(c_double), c_int] + value_data = cast(value_.ctypes.data, POINTER(c_double)) + getattr(self.shared_lib, f"{model_name}_acados_sim_update_params")(self.capsule, value_data, value_.shape[0]) + return + else: + # dimension check + dims = np.ascontiguousarray(np.zeros((2,)), dtype=np.intc) + dims_data = cast(dims.ctypes.data, POINTER(c_int)) + + self.shared_lib.sim_dims_get_from_attr.argtypes = [c_void_p, c_void_p, c_char_p, POINTER(c_int)] + self.shared_lib.sim_dims_get_from_attr(self.sim_config, self.sim_dims, field, dims_data) + + value_ = np.ravel(value_, order='F') + + value_shape = value_.shape + if len(value_shape) == 1: + value_shape = (value_shape[0], 0) + + if value_shape != tuple(dims): + raise Exception('AcadosSimSolver.set(): mismatching dimension' \ + ' for field "{}" with dimension {} (you have {})'.format(field_, tuple(dims), value_shape)) + + # set + if field_ in ['xdot', 'z']: + self.shared_lib.sim_solver_set.argtypes = [c_void_p, c_char_p, c_void_p] + self.shared_lib.sim_solver_set(self.sim_solver, field, value_data_p) + elif field_ in self.settable: + self.shared_lib.sim_in_set.argtypes = [c_void_p, c_void_p, c_void_p, c_char_p, c_void_p] + self.shared_lib.sim_in_set(self.sim_config, self.sim_dims, self.sim_in, field, value_data_p) + else: + raise Exception(f'AcadosSimSolver.set(): Unknown field {field_},' \ + f' available fields are {", ".join(self.settable)}') + + return + + + def __del__(self): + + if self.solver_created: + getattr(self.shared_lib, f"{self.model_name}_acados_sim_free").argtypes = [c_void_p] + getattr(self.shared_lib, f"{self.model_name}_acados_sim_free").restype = c_int + getattr(self.shared_lib, f"{self.model_name}_acados_sim_free")(self.capsule) + + getattr(self.shared_lib, f"{self.model_name}_acados_sim_solver_free_capsule").argtypes = [c_void_p] + getattr(self.shared_lib, f"{self.model_name}_acados_sim_solver_free_capsule").restype = c_int + getattr(self.shared_lib, f"{self.model_name}_acados_sim_solver_free_capsule")(self.capsule) + + try: + self.dlclose(self.shared_lib._handle) + except: + pass diff --git a/pyextra/acados_template/c_templates_tera/CPPLINT.cfg b/pyextra/acados_template/c_templates_tera/CPPLINT.cfg new file mode 100644 index 0000000000..bbd1caf057 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/CPPLINT.cfg @@ -0,0 +1 @@ +exclude_files=[main, acados_solver, acados_solver_sfun, Makefile, model].*\.? diff --git a/pyextra/acados_template/c_templates_tera/Makefile.in b/pyextra/acados_template/c_templates_tera/Makefile.in new file mode 100644 index 0000000000..4f1899de4e --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/Makefile.in @@ -0,0 +1,485 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +{%- if solver_options.qp_solver %} + {%- set qp_solver = solver_options.qp_solver %} +{%- else %} + {%- set qp_solver = "FULL_CONDENSING_HPIPM" %} +{%- endif %} + +{%- if solver_options.hessian_approx %} + {%- set hessian_approx = solver_options.hessian_approx %} +{%- elif solver_options.sens_hess %} + {%- set hessian_approx = "EXACT" %} +{%- else %} + {%- set hessian_approx = "GAUSS_NEWTON" %} +{%- endif %} + +{%- if constraints.constr_type %} + {%- set constr_type = constraints.constr_type %} +{%- else %} + {%- set constr_type = "NONE" %} +{%- endif %} + +{%- if constraints.constr_type_e %} + {%- set constr_type_e = constraints.constr_type_e %} +{%- else %} + {%- set constr_type_e = "NONE" %} +{%- endif %} + +{%- if cost.cost_type %} + {%- set cost_type = cost.cost_type %} +{%- else %} + {%- set cost_type = "NONE" %} +{%- endif %} + +{%- if cost.cost_type_e %} + {%- set cost_type_e = cost.cost_type_e %} +{%- else %} + {%- set cost_type_e = "NONE" %} +{%- endif %} + +{%- if cost.cost_type_0 %} + {%- set cost_type_0 = cost.cost_type_0 %} +{%- else %} + {%- set cost_type_0 = "NONE" %} +{%- endif %} + +{%- if dims.nh %} + {%- set dims_nh = dims.nh %} +{%- else %} + {%- set dims_nh = 0 %} +{%- endif %} + +{%- if dims.nphi %} + {%- set dims_nphi = dims.nphi %} +{%- else %} + {%- set dims_nphi = 0 %} +{%- endif %} + +{%- if dims.nh_e %} + {%- set dims_nh_e = dims.nh_e %} +{%- else %} + {%- set dims_nh_e = 0 %} +{%- endif %} + +{%- if dims.nphi_e %} + {%- set dims_nphi_e = dims.nphi_e %} +{%- else %} + {%- set dims_nphi_e = 0 %} +{%- endif %} +{%- if solver_options.model_external_shared_lib_dir %} + {%- set model_external_shared_lib_dir = solver_options.model_external_shared_lib_dir %} +{%- endif %} +{%- if solver_options.model_external_shared_lib_name %} + {%- set model_external_shared_lib_name = solver_options.model_external_shared_lib_name %} +{%- endif %} + +{# control operator #} +{%- if os and os == "pc" %} + {%- set control = "&" %} +{%- else %} + {%- set control = ";" %} +{%- endif %} + +{# acados linking libraries and flags #} +{%- if acados_link_libs and os and os == "pc" %} + {%- set link_libs = acados_link_libs.qpoases ~ " " ~ acados_link_libs.hpmpc ~ " " ~ acados_link_libs.osqp -%} + {%- set openmp_flag = acados_link_libs.openmp %} +{%- else %} + {%- set openmp_flag = " " %} + {%- if qp_solver == "FULL_CONDENSING_QPOASES" %} + {%- set link_libs = "-lqpOASES_e" %} + {%- else %} + {%- set link_libs = "" %} + {%- endif %} +{%- endif %} + +{# acados flags #} +ACADOS_FLAGS = -fPIC -std=c99 {{ openmp_flag }} #-fno-diagnostics-show-line-numbers -g +{%- if qp_solver == "FULL_CONDENSING_QPOASES" %} +ACADOS_FLAGS += -DACADOS_WITH_QPOASES +{%- endif %} +{%- if qp_solver == "PARTIAL_CONDENSING_OSQP" %} +ACADOS_FLAGS += -DACADOS_WITH_OSQP +{%- endif %} +{%- if qp_solver == "PARTIAL_CONDENSING_QPDUNES" %} +ACADOS_FLAGS += -DACADOS_WITH_QPDUNES +{%- endif %} +# # Debugging +# ACADOS_FLAGS += -g3 + +MODEL_OBJ= +{%- if solver_options.integrator_type == "ERK" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_expl_ode_fun.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_expl_vde_forw.o +{%- if hessian_approx == "EXACT" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_expl_ode_hess.o +{%- endif %} +{%- elif solver_options.integrator_type == "IRK" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_impl_dae_fun.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_impl_dae_fun_jac_x_xdot_z.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_impl_dae_jac_x_xdot_u_z.o +{%- if hessian_approx == "EXACT" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_impl_dae_hess.o +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_gnsf_phi_fun.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_gnsf_phi_fun_jac_y.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_gnsf_phi_jac_y_uhat.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_gnsf_get_matrices_fun.o +{%- elif solver_options.integrator_type == "DISCRETE" %} +{%- if model.dyn_ext_fun_type == "casadi" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun.o +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac.o +{%- if hessian_approx == "EXACT" %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac_hess.o +{%- endif %} +{%- else %} +MODEL_OBJ+= {{ model.name }}_model/{{ model.dyn_source_discrete }} +{%- endif %} +{%- endif %} + + +OCP_OBJ= +{%- if constr_type == "BGP" and dims_nphi > 0 %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_phi_constraint.o +{%- endif %} +{%- if constr_type_e == "BGP" and dims_nphi_e > 0 %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_phi_e_constraint.o +{%- endif %} + +{%- if constr_type == "BGH" and dims_nh > 0 %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt.o +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_fun.o +{%- if hessian_approx == "EXACT" %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt_hess.o +{%- endif %} +{%- endif %} + +{%- if constr_type_e == "BGH" and dims_nh_e > 0 %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt.o +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun.o +{%- if hessian_approx == "EXACT" %} +OCP_OBJ+= {{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess.o +{%- endif %} +{%- endif %} + +{%- if cost_type_0 == "NONLINEAR_LS" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_0_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_0_fun_jac_ut_xt.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_0_hess.c +{%- elif cost_type_0 == "EXTERNAL" %} +{% if cost.cost_ext_fun_type_0 == "casadi" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac_hess.c +{% else %} +OCP_OBJ+= {{ model.name }}_cost/{{ cost.cost_source_ext_cost_0 }} +{% endif %} +{%- endif %} +{%- if cost_type == "NONLINEAR_LS" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_fun_jac_ut_xt.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_hess.c +{%- elif cost_type == "EXTERNAL" %} +{% if cost.cost_ext_fun_type == "casadi" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac_hess.c +{% elif cost.cost_source_ext_cost != cost.cost_source_ext_cost_0 %} +OCP_OBJ+= {{ model.name }}_cost/{{ cost.cost_source_ext_cost }} +{% endif %} +{%- endif %} +{%- if cost_type_e == "NONLINEAR_LS" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_e_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_e_fun_jac_ut_xt.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_y_e_hess.c +{%- elif cost_type_e == "EXTERNAL" %} +{% if cost.cost_ext_fun_type_e == "casadi" %} +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac.c +OCP_OBJ+= {{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac_hess.c +{% elif cost.cost_source_ext_cost_e != cost.cost_source_ext_cost_0 %} +OCP_OBJ+= {{ model.name }}_cost/{{ cost.cost_source_ext_cost_e }} +{% endif %} +{%- endif %} +OCP_OBJ+= acados_solver_{{ model.name }}.o + + +SIM_OBJ= +SIM_OBJ+= acados_sim_solver_{{ model.name }}.o + +EX_OBJ= +EX_OBJ+= main_{{ model.name }}.o + +EX_SIM_OBJ= +EX_SIM_OBJ+= main_sim_{{ model.name }}.o + +OBJ= +OBJ+= $(MODEL_OBJ) +{%- if solver_options.integrator_type != "DISCRETE" %} +OBJ+= $(SIM_OBJ) +{%- endif %} +OBJ+= $(OCP_OBJ) + +EXTERNAL_DIR= +EXTERNAL_LIB= + +{%- if model_external_shared_lib_dir and model_external_shared_lib_name %} +EXTERNAL_DIR+= {{ model_external_shared_lib_dir }} +EXTERNAL_LIB+= {{ model_external_shared_lib_name }} +{%- endif %} + +INCLUDE_PATH = {{ acados_include_path }} +LIB_PATH = {{ acados_lib_path }} + +{%- if solver_options.integrator_type == "DISCRETE" %} +all: clean casadi_fun example +shared_lib: ocp_shared_lib +{%- else %} +all: clean casadi_fun example_sim example +shared_lib: bundled_shared_lib ocp_shared_lib sim_shared_lib +{%- endif %} + +CASADI_MODEL_SOURCE= +{%- if solver_options.integrator_type == "ERK" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_expl_ode_fun.c +CASADI_MODEL_SOURCE+= {{ model.name }}_expl_vde_forw.c +{%- if hessian_approx == "EXACT" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_expl_ode_hess.c +{%- endif %} +{%- elif solver_options.integrator_type == "IRK" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_impl_dae_fun.c +CASADI_MODEL_SOURCE+= {{ model.name }}_impl_dae_fun_jac_x_xdot_z.c +CASADI_MODEL_SOURCE+= {{ model.name }}_impl_dae_jac_x_xdot_u_z.c +{%- if hessian_approx == "EXACT" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_impl_dae_hess.c +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_gnsf_phi_fun.c +CASADI_MODEL_SOURCE+= {{ model.name }}_gnsf_phi_fun_jac_y.c +CASADI_MODEL_SOURCE+= {{ model.name }}_gnsf_phi_jac_y_uhat.c +CASADI_MODEL_SOURCE+= {{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz.c +CASADI_MODEL_SOURCE+= {{ model.name }}_gnsf_get_matrices_fun.c +{%- elif solver_options.integrator_type == "DISCRETE" and model.dyn_ext_fun_type == "casadi" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_dyn_disc_phi_fun.c +CASADI_MODEL_SOURCE+= {{ model.name }}_dyn_disc_phi_fun_jac.c +{%- if hessian_approx == "EXACT" %} +CASADI_MODEL_SOURCE+= {{ model.name }}_dyn_disc_phi_fun_jac_hess.c +{%- endif %} +{%- endif %} +{%- if constr_type == "BGP" and dims_nphi > 0 %} +CASADI_CON_PHI_SOURCE= +CASADI_CON_PHI_SOURCE+= {{ model.name }}_phi_constraint.c +{%- endif %} +{%- if constr_type_e == "BGP" and dims_nphi_e > 0 %} +CASADI_CON_PHI_E_SOURCE= +CASADI_CON_PHI_E_SOURCE+= {{ model.name }}_phi_e_constraint.c +{%- endif %} +{%- if constr_type == "BGH" and dims_nh > 0 %} +CASADI_CON_H_SOURCE= +CASADI_CON_H_SOURCE+= {{ model.name }}_constr_h_fun_jac_uxt_zt.c +CASADI_CON_H_SOURCE+= {{ model.name }}_constr_h_fun.c +{%- if hessian_approx == "EXACT" %} +CASADI_CON_H_SOURCE+= {{ model.name }}_constr_h_fun_jac_uxt_zt_hess.c +{%- endif %} +{%- endif %} + +{%- if dims_nh_e > 0 %} +CASADI_CON_H_E_SOURCE= +CASADI_CON_H_E_SOURCE+= {{ model.name }}_constr_h_e_fun_jac_uxt_zt.c +CASADI_CON_H_E_SOURCE+= {{ model.name }}_constr_h_e_fun.c +{%- if hessian_approx == "EXACT" %} +CASADI_CON_H_E_SOURCE+= {{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess.c +{%- endif %} +{%- endif %} + +{%- if cost_type == "NONLINEAR_LS" %} +CASADI_COST_Y_SOURCE= +CASADI_COST_Y_SOURCE+= {{ model.name }}_cost_y_fun.c +CASADI_COST_Y_SOURCE+= {{ model.name }}_cost_y_fun_jac_ut_xt.c +CASADI_COST_Y_SOURCE+= {{ model.name }}_cost_y_hess.c +{%- endif %} +{%- if cost_type_e == "NONLINEAR_LS" %} +CASADI_COST_Y_E_SOURCE= +CASADI_COST_Y_E_SOURCE+= {{ model.name }}_cost_y_e_fun.c +CASADI_COST_Y_E_SOURCE+= {{ model.name }}_cost_y_e_fun_jac_ut_xt.c +CASADI_COST_Y_E_SOURCE+= {{ model.name }}_cost_y_e_hess.c +{%- endif %} +{%- if cost_type_0 == "NONLINEAR_LS" %} +CASADI_COST_Y_0_SOURCE= +CASADI_COST_Y_0_SOURCE+= {{ model.name }}_cost_y_0_fun.c +CASADI_COST_Y_0_SOURCE+= {{ model.name }}_cost_y_0_fun_jac_ut_xt.c +CASADI_COST_Y_0_SOURCE+= {{ model.name }}_cost_y_0_hess.c +{%- endif %} + +casadi_fun: + {%- if model.dyn_ext_fun_type == "casadi" %} + ( cd {{ model.name }}_model {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_MODEL_SOURCE)) + {%- endif %} + {%- if constr_type == "BGP" and dims_nphi > 0 %} + ( cd {{ model.name }}_constraints {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_CON_PHI_SOURCE)) + {%- endif %} + {%- if constr_type_e == "BGP" and dims_nphi_e > 0 %} + ( cd {{ model.name }}_constraints {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_CON_PHI_E_SOURCE)) + {%- endif %} + {%- if constr_type == "BGH" and dims_nh > 0 %} + ( cd {{ model.name }}_constraints {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_CON_H_SOURCE)) + {%- endif %} + {%- if constr_type_e == "BGH" and dims_nh_e > 0 %} + ( cd {{ model.name }}_constraints {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_CON_H_E_SOURCE)) + {%- endif %} + {%- if cost_type == "NONLINEAR_LS" %} + ( cd {{ model.name }}_cost {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_COST_Y_SOURCE)) + {%- endif %} + {%- if cost_type_e == "NONLINEAR_LS" %} + ( cd {{ model.name }}_cost {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_COST_Y_E_SOURCE)) + {%- endif %} + {%- if cost_type_0 == "NONLINEAR_LS" %} + ( cd {{ model.name }}_cost {{ control }} gcc $(ACADOS_FLAGS) -c $(CASADI_COST_Y_0_SOURCE)) + {%- endif %} + +main: + gcc $(ACADOS_FLAGS) -c main_{{ model.name }}.c -I $(INCLUDE_PATH)/blasfeo/include/ -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) -I $(INCLUDE_PATH)/acados/ \ + {%- if qp_solver == "FULL_CONDENSING_QPOASES" %} + -I $(INCLUDE_PATH)/qpOASES_e/ + {%- endif %} + +main_sim: + gcc $(ACADOS_FLAGS) -c main_sim_{{ model.name }}.c -I $(INCLUDE_PATH)/blasfeo/include/ -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) -I $(INCLUDE_PATH)/acados/ + +ocp_solver: + gcc $(ACADOS_FLAGS) -c acados_solver_{{ model.name }}.c -I $(INCLUDE_PATH)/blasfeo/include/ -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) -I $(INCLUDE_PATH)/acados/ \ + {%- if qp_solver == "FULL_CONDENSING_QPOASES" %} + -I $(INCLUDE_PATH)/qpOASES_e/ + {%- endif %} + +sim_solver: + gcc $(ACADOS_FLAGS) -c acados_sim_solver_{{ model.name }}.c -I $(INCLUDE_PATH)/blasfeo/include/ -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) -I $(INCLUDE_PATH)/acados/ \ + {%- if qp_solver == "FULL_CONDENSING_QPOASES" %} + -I $(INCLUDE_PATH)/qpOASES_e/ + {%- endif %} + +example: ocp_solver main + gcc $(ACADOS_FLAGS) -o main_{{ model.name }} $(EX_OBJ) $(OBJ) -L $(LIB_PATH) \ + -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + -I $(INCLUDE_PATH)/blasfeo/include/ \ + -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) \ + -I $(INCLUDE_PATH)/acados/ \ + {%- if qp_solver == "FULL_CONDENSING_QPOASES" %} + -I $(INCLUDE_PATH)/qpOASES_e/ + {%- endif %} + + +example_sim: sim_solver main_sim + gcc $(ACADOS_FLAGS) -o main_sim_{{ model.name }} $(EX_SIM_OBJ) $(MODEL_OBJ) $(SIM_OBJ) -L $(LIB_PATH) \ + -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + -I $(INCLUDE_PATH)/blasfeo/include/ \ + -I $(INCLUDE_PATH)/acados/ \ + +{%- if solver_options.integrator_type != "DISCRETE" %} + +bundled_shared_lib: casadi_fun ocp_solver sim_solver + gcc $(ACADOS_FLAGS) -shared -o libacados_solver_{{ model.name }}.so $(OBJ) \ + -I $(INCLUDE_PATH)/blasfeo/include/ \ + -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) \ + -L $(LIB_PATH) \ + -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + +ocp_shared_lib: casadi_fun ocp_solver + gcc $(ACADOS_FLAGS) -shared -o libacados_ocp_solver_{{ model.name }}.so $(OCP_OBJ) $(MODEL_OBJ) \ + -I $(INCLUDE_PATH)/blasfeo/include/ \ + -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) \ + -L$(EXTERNAL_DIR) -l$(EXTERNAL_LIB) \ + -L $(LIB_PATH) -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + +{%- else %} + +ocp_shared_lib: casadi_fun ocp_solver + gcc $(ACADOS_FLAGS) -shared -o libacados_ocp_solver_{{ model.name }}.so $(OCP_OBJ) $(MODEL_OBJ) \ + -I $(INCLUDE_PATH)/blasfeo/include/ \ + -I $(INCLUDE_PATH)/hpipm/include/ \ + -I $(INCLUDE_PATH) \ + -L$(EXTERNAL_DIR) -l$(EXTERNAL_LIB) \ + -L $(LIB_PATH) -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + +{%- endif %} + +sim_shared_lib: casadi_fun sim_solver + gcc $(ACADOS_FLAGS) -shared -o libacados_sim_solver_{{ model.name }}.so $(SIM_OBJ) $(MODEL_OBJ) -L$(EXTERNAL_DIR) -l$(EXTERNAL_LIB) \ + -L $(LIB_PATH) -lacados -lhpipm -lblasfeo \ + {{ link_libs }} \ + -lm \ + +{%- if os and os == "pc" %} + +clean: + del \Q *.o 2>nul + del \Q *.so 2>nul + del \Q main_{{ model.name }} 2>nul + +clean_ocp_shared_lib: + del \Q libacados_ocp_solver_{{ model.name }}.so 2>nul + del \Q acados_solver_{{ model.name }}.o 2>nul + +{%- else %} + +clean: + rm -f *.o + rm -f *.so + rm -f main_{{ model.name }} + +clean_ocp_shared_lib: + rm -f libacados_ocp_solver_{{ model.name }}.so + rm -f acados_solver_{{ model.name }}.o + +{%- endif %} diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_create.in.c b/pyextra/acados_template/c_templates_tera/acados_mex_create.in.c new file mode 100644 index 0000000000..7fa08d8f73 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_mex_create.in.c @@ -0,0 +1,383 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +// standard +#include +#include +#include + +// acados +#include "acados/utils/print.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_solver_{{ model.name }}.h" + +// mex +#include "mex.h" + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + long long *l_ptr; + int status = 0; + + // create solver + nlp_solver_capsule *acados_ocp_capsule = {{ model.name }}_acados_create_capsule(); + + status = {{ model.name }}_acados_create(acados_ocp_capsule); + + if (status) + { + mexPrintf("{{ model.name }}_acados_create() returned status %d.\n", status); + } + mexPrintf("{{ model.name }}_acados_create() -> success!\n"); + + // get pointers to nlp solver related objects + ocp_nlp_plan *nlp_plan = {{ model.name }}_acados_get_nlp_plan(acados_ocp_capsule); + ocp_nlp_config *nlp_config = {{ model.name }}_acados_get_nlp_config(acados_ocp_capsule); + ocp_nlp_dims *nlp_dims = {{ model.name }}_acados_get_nlp_dims(acados_ocp_capsule); + ocp_nlp_in *nlp_in = {{ model.name }}_acados_get_nlp_in(acados_ocp_capsule); + ocp_nlp_out *nlp_out = {{ model.name }}_acados_get_nlp_out(acados_ocp_capsule); + ocp_nlp_solver *nlp_solver = {{ model.name }}_acados_get_nlp_solver(acados_ocp_capsule); + void *nlp_opts = {{ model.name }}_acados_get_nlp_opts(acados_ocp_capsule); + + // mexPrintf("acados: got pointer to objectes!\n"); + + // field names of output struct + #define FIELDS_OCP 9 + #define FIELDS_EXT_FUN 25 + #define MAX_FIELDS 25 + char *fieldnames[MAX_FIELDS]; + + for (int i = 0; i < MAX_FIELDS; i++) + { + fieldnames[i] = (char*) mxMalloc(50); + } + + memcpy(fieldnames[0],"config",sizeof("config")); + memcpy(fieldnames[1],"dims",sizeof("dims")); + memcpy(fieldnames[2],"opts",sizeof("opts")); + memcpy(fieldnames[3],"in",sizeof("in")); + memcpy(fieldnames[4],"out",sizeof("out")); + memcpy(fieldnames[5],"solver",sizeof("solver")); + memcpy(fieldnames[6],"sens_out",sizeof("sens_out")); + memcpy(fieldnames[7],"plan",sizeof("plan")); + memcpy(fieldnames[8],"capsule",sizeof("capsule")); + + // create output struct - C_ocp + plhs[0] = mxCreateStructMatrix(1, 1, 9, (const char **) fieldnames); + + // MEX: config, dims, opts, in, out, solver, sens_out, plan + // plan + mxArray *plan_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(plan_mat); + l_ptr[0] = (long long) nlp_plan; + mxSetField(plhs[0], 0, "plan", plan_mat); + + // config + mxArray *config_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(config_mat); + l_ptr[0] = (long long) nlp_config; + mxSetField(plhs[0], 0, "config", config_mat); + + // dims + mxArray *dims_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(dims_mat); + l_ptr[0] = (long long) nlp_dims; + mxSetField(plhs[0], 0, "dims", dims_mat); + + // opts + mxArray *opts_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(opts_mat); + l_ptr[0] = (long long) nlp_opts; + mxSetField(plhs[0], 0, "opts", opts_mat); + + // in + mxArray *in_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(in_mat); + l_ptr[0] = (long long) nlp_in; + mxSetField(plhs[0], 0, "in", in_mat); + + // out + mxArray *out_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(out_mat); + l_ptr[0] = (long long) nlp_out; + mxSetField(plhs[0], 0, "out", out_mat); + + // solver + mxArray *solver_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(solver_mat); + l_ptr[0] = (long long) nlp_solver; + mxSetField(plhs[0], 0, "solver", solver_mat); + + // TODO: sens_out not actually implemented in templates.. + // sens_out + mxArray *sens_out_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(sens_out_mat); + l_ptr[0] = (long long) 1; + mxSetField(plhs[0], 0, "sens_out", sens_out_mat); + + // capsule + mxArray *capsule_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(capsule_mat); + l_ptr[0] = (long long) acados_ocp_capsule; + mxSetField(plhs[0], 0, "capsule", capsule_mat); + + /* store external function pointers */ + // dyn + memcpy(fieldnames[0],"expl_ode_fun",sizeof("expl_ode_fun")); + memcpy(fieldnames[1],"forw_vde",sizeof("forw_vde")); + memcpy(fieldnames[2],"hess_vde",sizeof("hess_vde")); + memcpy(fieldnames[3],"impl_dae_fun",sizeof("impl_dae_fun")); + memcpy(fieldnames[4],"impl_dae_fun_jac_x_xdot_z",sizeof("impl_dae_fun_jac_x_xdot_z")); + memcpy(fieldnames[5],"impl_dae_jac_x_xdot_u_z",sizeof("impl_dae_jac_x_xdot_u_z")); + memcpy(fieldnames[6],"impl_dae_hess",sizeof("impl_dae_hess")); + + memcpy(fieldnames[7],"gnsf_phi_fun",sizeof("gnsf_phi_fun")); + memcpy(fieldnames[8],"gnsf_phi_fun_jac_y",sizeof("gnsf_phi_fun_jac_y")); + memcpy(fieldnames[9],"gnsf_phi_jac_y_uhat",sizeof("gnsf_phi_jac_y_uhat")); + memcpy(fieldnames[10],"gnsf_f_lo_jac_x1_x1dot_u_z",sizeof("gnsf_f_lo_jac_x1_x1dot_u_z")); + memcpy(fieldnames[11],"gnsf_get_matrices_fun",sizeof("gnsf_get_matrices_fun")); + + memcpy(fieldnames[12],"disc_phi_fun",sizeof("disc_phi_fun")); + memcpy(fieldnames[13],"disc_phi_fun_jac",sizeof("disc_phi_fun_jac")); + memcpy(fieldnames[14],"disc_phi_fun_jac_hess",sizeof("disc_phi_fun_jac_hess")); + + // cost + memcpy(fieldnames[15],"cost_y_fun",sizeof("cost_y_fun")); + memcpy(fieldnames[16],"cost_y_fun_jac_ut_xt",sizeof("cost_y_fun_jac_ut_xt")); + memcpy(fieldnames[17],"cost_y_hess",sizeof("cost_y_hess")); + memcpy(fieldnames[18],"ext_cost_fun",sizeof("ext_cost_fun")); + memcpy(fieldnames[19],"ext_cost_fun_jac",sizeof("ext_cost_fun_jac")); + memcpy(fieldnames[20],"ext_cost_fun_jac_hess",sizeof("ext_cost_fun_jac_hess")); + + // constraints + memcpy(fieldnames[21],"phi_constraint",sizeof("phi_constraint")); + memcpy(fieldnames[22],"nl_constr_h_fun_jac",sizeof("nl_constr_h_fun_jac")); + memcpy(fieldnames[23],"nl_constr_h_fun",sizeof("nl_constr_h_fun")); + memcpy(fieldnames[24],"nl_constr_h_fun_jac_hess",sizeof("nl_constr_h_fun_jac_hess")); + + + // create output struct - C_ocp_ext_fun + plhs[1] = mxCreateStructMatrix(1, 1, FIELDS_EXT_FUN, (const char **) fieldnames); + + + for (int i = 0; i < FIELDS_EXT_FUN; i++) + { + mxFree( fieldnames[i] ); + } + +/* dynamics */ + mxArray *expl_ode_fun_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *forw_vde_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *hess_vde_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *impl_dae_fun_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *impl_dae_fun_jac_x_xdot_z_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *impl_dae_jac_x_xdot_u_z_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *impl_dae_hess_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + + mxArray *gnsf_phi_fun_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *gnsf_phi_fun_jac_y_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *gnsf_phi_jac_y_uhat_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *gnsf_f_lo_jac_x1_x1dot_u_z_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *gnsf_get_matrices_fun_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + + mxArray *disc_phi_fun_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *disc_phi_fun_jac_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + mxArray *disc_phi_fun_jac_hess_mat = mxCreateNumericMatrix(1, 1, mxINT64_CLASS, mxREAL); + +{% if solver_options.integrator_type == "ERK" %} + {# TODO: remove _casadi from these names.. #} + l_ptr = mxGetData(forw_vde_mat); + l_ptr[0] = (long long) acados_ocp_capsule->forw_vde_casadi; + l_ptr = mxGetData(expl_ode_fun_mat); + l_ptr[0] = (long long) acados_ocp_capsule->expl_ode_fun; +{% if solver_options.hessian_approx == "EXACT" %} + l_ptr = mxGetData(hess_vde_mat); + l_ptr[0] = (long long) acados_ocp_capsule->hess_vde_casadi; +{%- endif %} +{% elif solver_options.integrator_type == "IRK" %} + l_ptr = mxGetData(impl_dae_fun_mat); + l_ptr[0] = (long long) acados_ocp_capsule->impl_dae_fun; + l_ptr = mxGetData(impl_dae_fun_jac_x_xdot_z_mat); + l_ptr[0] = (long long) acados_ocp_capsule->impl_dae_fun_jac_x_xdot_z; + l_ptr = mxGetData(impl_dae_jac_x_xdot_u_z_mat); + l_ptr[0] = (long long) acados_ocp_capsule->impl_dae_jac_x_xdot_u_z; +{% if solver_options.hessian_approx == "EXACT" %} + l_ptr = mxGetData(impl_dae_hess_mat); + l_ptr[0] = (long long) acados_ocp_capsule->impl_dae_hess; +{%- endif %} +{% elif solver_options.integrator_type == "GNSF" %} + l_ptr = mxGetData(gnsf_phi_fun_mat); + l_ptr[0] = (long long) acados_ocp_capsule->gnsf_phi_fun; + l_ptr = mxGetData(gnsf_phi_fun_jac_y_mat); + l_ptr[0] = (long long) acados_ocp_capsule->gnsf_phi_fun_jac_y; + l_ptr = mxGetData(gnsf_phi_jac_y_uhat_mat); + l_ptr[0] = (long long) acados_ocp_capsule->gnsf_phi_jac_y_uhat; + l_ptr = mxGetData(gnsf_f_lo_jac_x1_x1dot_u_z_mat); + l_ptr[0] = (long long) acados_ocp_capsule->gnsf_f_lo_jac_x1_x1dot_u_z; + l_ptr = mxGetData(gnsf_get_matrices_fun_mat); + l_ptr[0] = (long long) acados_ocp_capsule->gnsf_get_matrices_fun; +{% elif solver_options.integrator_type == "DISCRETE" %} + l_ptr = mxGetData(disc_phi_fun_mat); + l_ptr[0] = (long long) acados_ocp_capsule->discr_dyn_phi_fun; + l_ptr = mxGetData(disc_phi_fun_jac_mat); + l_ptr[0] = (long long) acados_ocp_capsule->discr_dyn_phi_fun_jac_ut_xt; +{% if solver_options.hessian_approx == "EXACT" %} + l_ptr = mxGetData(disc_phi_fun_jac_hess_mat); + l_ptr[0] = (long long) acados_ocp_capsule->discr_dyn_phi_fun_jac_ut_xt_hess; +{%- endif %} +{%- endif %} + mxSetField(plhs[1], 0, "expl_ode_fun", expl_ode_fun_mat); + mxSetField(plhs[1], 0, "forw_vde", forw_vde_mat); + mxSetField(plhs[1], 0, "hess_vde", hess_vde_mat); + + mxSetField(plhs[1], 0, "gnsf_phi_fun", gnsf_phi_fun_mat); + mxSetField(plhs[1], 0, "gnsf_phi_fun_jac_y", gnsf_phi_fun_jac_y_mat); + mxSetField(plhs[1], 0, "gnsf_phi_jac_y_uhat", gnsf_phi_jac_y_uhat_mat); + mxSetField(plhs[1], 0, "gnsf_f_lo_jac_x1_x1dot_u_z", gnsf_f_lo_jac_x1_x1dot_u_z_mat); + mxSetField(plhs[1], 0, "gnsf_get_matrices_fun", gnsf_get_matrices_fun_mat); + + mxSetField(plhs[1], 0, "impl_dae_fun", impl_dae_fun_mat); + mxSetField(plhs[1], 0, "impl_dae_fun_jac_x_xdot_z", impl_dae_fun_jac_x_xdot_z_mat); + mxSetField(plhs[1], 0, "impl_dae_jac_x_xdot_u_z", impl_dae_jac_x_xdot_u_z_mat); + mxSetField(plhs[1], 0, "impl_dae_hess", impl_dae_hess_mat); + + mxSetField(plhs[1], 0, "disc_phi_fun", disc_phi_fun_mat); + mxSetField(plhs[1], 0, "disc_phi_fun_jac", disc_phi_fun_jac_mat); + mxSetField(plhs[1], 0, "disc_phi_fun_jac_hess", disc_phi_fun_jac_hess_mat); +/* constaints */ + mxArray *phi_constraint_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(phi_constraint_mat); +{%- if constraints.constr_type == "BGP" %} + l_ptr[0] = (long long) acados_ocp_capsule->phi_constraint; +{% endif %} +{% if constraints.constr_type_e == "BGP" %} + l_ptr[1] = (long long) &acados_ocp_capsule->phi_e_constraint; +{% endif %} + mxSetField(plhs[1], 0, "phi_constraint", phi_constraint_mat); + + mxArray *nl_constr_h_fun_jac_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(nl_constr_h_fun_jac_mat); +{% if constraints.constr_type == "BGH" and dims.nh > 0 %} + l_ptr[0] = (long long) acados_ocp_capsule->nl_constr_h_fun_jac; +{% endif %} +{% if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + l_ptr[1] = (long long) &acados_ocp_capsule->nl_constr_h_e_fun_jac; +{%- endif %} + mxSetField(plhs[1], 0, "nl_constr_h_fun_jac", nl_constr_h_fun_jac_mat); + + mxArray *nl_constr_h_fun_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(nl_constr_h_fun_mat); +{% if constraints.constr_type == "BGH" and dims.nh > 0 %} + l_ptr[0] = (long long) acados_ocp_capsule->nl_constr_h_fun; +{% endif %} +{% if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + l_ptr[1] = (long long) &acados_ocp_capsule->nl_constr_h_e_fun; +{%- endif %} + mxSetField(plhs[1], 0, "nl_constr_h_fun", nl_constr_h_fun_mat); + + mxArray *nl_constr_h_fun_jac_hess_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(nl_constr_h_fun_jac_hess_mat); +{% if constraints.constr_type == "BGH" and dims.nh > 0 and solver_options.hessian_approx == "EXACT" %} + l_ptr[0] = (long long) acados_ocp_capsule->nl_constr_h_fun_jac_hess; +{% endif %} +{% if constraints.constr_type_e == "BGH" and dims.nh_e > 0 and solver_options.hessian_approx == "EXACT" %} + l_ptr[1] = (long long) &acados_ocp_capsule->nl_constr_h_e_fun_jac_hess; +{%- endif %} + mxSetField(plhs[1], 0, "nl_constr_h_fun_jac_hess", nl_constr_h_fun_jac_hess_mat); + +/* cost */ + mxArray *cost_y_fun_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(cost_y_fun_mat); +{% if cost.cost_type == "NONLINEAR_LS" %} + l_ptr[0] = (long long) acados_ocp_capsule->cost_y_fun; +{% endif %} +{% if cost.cost_type_e == "NONLINEAR_LS" %} + l_ptr[1] = (long long) &acados_ocp_capsule->cost_y_e_fun; +{%- endif %} + mxSetField(plhs[1], 0, "cost_y_fun", cost_y_fun_mat); + + mxArray *cost_y_fun_jac_ut_xt_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(cost_y_fun_jac_ut_xt_mat); +{% if cost.cost_type == "NONLINEAR_LS" %} + l_ptr[0] = (long long) acados_ocp_capsule->cost_y_fun_jac_ut_xt; +{% endif %} +{% if cost.cost_type_e == "NONLINEAR_LS" %} + l_ptr[1] = (long long) &acados_ocp_capsule->cost_y_e_fun_jac_ut_xt; +{%- endif %} + mxSetField(plhs[1], 0, "cost_y_fun_jac_ut_xt", cost_y_fun_jac_ut_xt_mat); + + mxArray *cost_y_hess_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(cost_y_hess_mat); +{% if cost.cost_type == "NONLINEAR_LS" %} + l_ptr[0] = (long long) acados_ocp_capsule->cost_y_hess; +{% endif %} +{% if cost.cost_type_e == "NONLINEAR_LS" %} + l_ptr[1] = (long long) &acados_ocp_capsule->cost_y_e_hess; +{%- endif %} + mxSetField(plhs[1], 0, "cost_y_hess", cost_y_hess_mat); + + mxArray *ext_cost_fun_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(ext_cost_fun_mat); +{% if cost.cost_type == "EXTERNAL" %} + l_ptr[0] = (long long) acados_ocp_capsule->ext_cost_fun; +{% endif -%} +{% if cost.cost_type_e == "EXTERNAL" %} + l_ptr[1] = (long long) &acados_ocp_capsule->ext_cost_e_fun; +{%- endif %} + mxSetField(plhs[1], 0, "ext_cost_fun", ext_cost_fun_mat); + + mxArray *ext_cost_fun_jac_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(ext_cost_fun_jac_mat); +{% if cost.cost_type == "EXTERNAL" %} + l_ptr[0] = (long long) acados_ocp_capsule->ext_cost_fun_jac; +{% endif -%} +{% if cost.cost_type_e == "EXTERNAL" %} + l_ptr[1] = (long long) &acados_ocp_capsule->ext_cost_e_fun_jac; +{%- endif %} + mxSetField(plhs[1], 0, "ext_cost_fun_jac", ext_cost_fun_jac_mat); + + mxArray *ext_cost_fun_jac_hess_mat = mxCreateNumericMatrix(1, 2, mxINT64_CLASS, mxREAL); + l_ptr = mxGetData(ext_cost_fun_jac_hess_mat); +{% if cost.cost_type == "EXTERNAL" %} + l_ptr[0] = (long long) acados_ocp_capsule->ext_cost_fun_jac_hess; +{% endif -%} +{% if cost.cost_type_e == "EXTERNAL" %} + l_ptr[1] = (long long) &acados_ocp_capsule->ext_cost_e_fun_jac_hess; +{%- endif %} + mxSetField(plhs[1], 0, "ext_cost_fun_jac_hess", ext_cost_fun_jac_hess_mat); + + + return; +} diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_free.in.c b/pyextra/acados_template/c_templates_tera/acados_mex_free.in.c new file mode 100644 index 0000000000..843440297c --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_mex_free.in.c @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +// system +#include +#include +#include +// acados +#include "acados_solver_{{ model.name }}.h" + +// mex +#include "mex.h" + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + int status = 0; + long long *ptr; + + // mexPrintf("\nin mex_acados_free\n"); + const mxArray *C_ocp = prhs[0]; + // capsule + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "capsule" ) ); + nlp_solver_capsule *capsule = (nlp_solver_capsule *) ptr[0]; + + status = {{ model.name }}_acados_free(capsule); + if (status) + { + mexPrintf("{{ model.name }}_acados_free() returned status %d.\n", status); + } + + status = {{ model.name }}_acados_free_capsule(capsule); + if (status) + { + mexPrintf("{{ model.name }}_acados_free_capsule() returned status %d.\n", status); + } + + return; +} + diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_set.in.c b/pyextra/acados_template/c_templates_tera/acados_mex_set.in.c new file mode 100644 index 0000000000..ee86a31164 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_mex_set.in.c @@ -0,0 +1,570 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +// standard +#include +#include +#include + +// acados +#include "acados/utils/print.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_solver_{{ model.name }}.h" + +// mex +#include "mex.h" +#include "mex_macros.h" + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + + long long *ptr; + int acados_size; + mxArray *mex_field; + char fun_name[20] = "ocp_set"; + char buffer [500]; // for error messages + + /* RHS */ + int min_nrhs = 6; + + char *ext_fun_type = mxArrayToString( prhs[0] ); + char *ext_fun_type_e = mxArrayToString( prhs[1] ); + + // C ocp + const mxArray *C_ocp = prhs[2]; + // capsule + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "capsule" ) ); + nlp_solver_capsule *capsule = (nlp_solver_capsule *) ptr[0]; + // plan + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "plan" ) ); + ocp_nlp_plan *plan = (ocp_nlp_plan *) ptr[0]; + // config + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "config" ) ); + ocp_nlp_config *config = (ocp_nlp_config *) ptr[0]; + // dims + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "dims" ) ); + ocp_nlp_dims *dims = (ocp_nlp_dims *) ptr[0]; + // opts + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "opts" ) ); + void *opts = (void *) ptr[0]; + // in + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "in" ) ); + ocp_nlp_in *in = (ocp_nlp_in *) ptr[0]; + // out + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "out" ) ); + ocp_nlp_out *out = (ocp_nlp_out *) ptr[0]; + // solver + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "solver" ) ); + ocp_nlp_solver *solver = (ocp_nlp_solver *) ptr[0]; + + const mxArray *C_ext_fun_pointers = prhs[3]; + // field + char *field = mxArrayToString( prhs[4] ); + // value + double *value = mxGetPr( prhs[5] ); + + // for checks + int matlab_size = (int) mxGetNumberOfElements( prhs[5] ); + int nrow = (int) mxGetM( prhs[5] ); + int ncol = (int) mxGetN( prhs[5] ); + + int N = dims->N; + int nu = dims->nu[0]; + int nx = dims->nx[0]; + + // stage + int s0, se; + if (nrhs==min_nrhs) + { + s0 = 0; + se = N; + } + else if (nrhs==min_nrhs+1) + { + s0 = mxGetScalar( prhs[6] ); + if (s0 > N) + { + sprintf(buffer, "ocp_set: N < specified stage = %d\n", s0); + mexErrMsgTxt(buffer); + } + se = s0 + 1; + } + else + { + sprintf(buffer, "ocp_set: wrong nrhs: %d\n", nrhs); + mexErrMsgTxt(buffer); + } + + /* Set value */ + // constraints + if (!strcmp(field, "constr_x0")) + { + acados_size = nx; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_constraints_model_set(config, dims, in, 0, "lbx", value); + ocp_nlp_constraints_model_set(config, dims, in, 0, "ubx", value); + } + else if (!strcmp(field, "constr_C")) + { + for (int ii=s0; iinlp_cost[ii] == LINEAR_LS) || (plan->nlp_cost[ii] == NONLINEAR_LS)) + { + acados_size = ocp_nlp_dims_get_from_attr(config, dims, out, ii, "y_ref"); + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_cost_model_set(config, dims, in, ii, "y_ref", value); + } + else + { + MEX_FIELD_NOT_SUPPORTED_FOR_COST_STAGE(fun_name, field, plan->nlp_cost[ii], ii); + } + } + } + else if (!strcmp(field, "cost_y_ref_e")) + { + acados_size = ocp_nlp_dims_get_from_attr(config, dims, out, N, "y_ref"); + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_cost_model_set(config, dims, in, N, "y_ref", value); + } + else if (!strcmp(field, "cost_Vu")) + { + for (int ii=s0; iinlp_cost[ii] == LINEAR_LS) || (plan->nlp_cost[ii] == NONLINEAR_LS)) + { + int ny = ocp_nlp_dims_get_from_attr(config, dims, out, ii, "y_ref"); + int nu = ocp_nlp_dims_get_from_attr(config, dims, out, ii, "u"); + acados_size = ny * nu; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_cost_model_set(config, dims, in, ii, "Vu", value); + } + else + { + MEX_FIELD_NOT_SUPPORTED_FOR_COST_STAGE(fun_name, field, plan->nlp_cost[ii], ii); + } + } + } + else if (!strcmp(field, "cost_Vx")) + { + for (int ii=s0; iinlp_cost[ii] == LINEAR_LS) || (plan->nlp_cost[ii] == NONLINEAR_LS)) + { + int ny = ocp_nlp_dims_get_from_attr(config, dims, out, ii, "y_ref"); + int nx = ocp_nlp_dims_get_from_attr(config, dims, out, ii, "x"); + acados_size = ny * nx; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_cost_model_set(config, dims, in, ii, "Vx", value); + } + else + { + MEX_FIELD_NOT_SUPPORTED_FOR_COST_STAGE(fun_name, field, plan->nlp_cost[ii], ii); + } + } + } + else if (!strcmp(field, "cost_W")) + { + for (int ii=s0; iinlp_cost[ii] == LINEAR_LS) || (plan->nlp_cost[ii] == NONLINEAR_LS)) + { + int ny = ocp_nlp_dims_get_from_attr(config, dims, out, s0, "y_ref"); + acados_size = ny * ny; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + ocp_nlp_cost_model_set(config, dims, in, ii, "W", value); + } + else + { + MEX_FIELD_NOT_SUPPORTED_FOR_COST_STAGE(fun_name, field, plan->nlp_cost[ii], ii); + } + } + } + else if (!strcmp(field, "cost_Z")) + { + acados_size = ocp_nlp_dims_get_from_attr(config, dims, out, s0, "cost_Z"); + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + for (int ii=s0; iisim_solver_plan[0]; + sim_solver_t type = sim_plan.sim_solver; + if (type == IRK) + { + int nz = ocp_nlp_dims_get_from_attr(config, dims, out, 0, "z"); + if (nrhs!=min_nrhs) + MEX_SETTER_NO_STAGE_SUPPORT(fun_name, field) + + acados_size = N*nz; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + for (int ii=0; iisim_solver_plan[0]; + sim_solver_t type = sim_plan.sim_solver; + if (type == IRK) + { + int nx = ocp_nlp_dims_get_from_attr(config, dims, out, 0, "x"); + if (nrhs!=min_nrhs) + MEX_SETTER_NO_STAGE_SUPPORT(fun_name, field) + + acados_size = N*nx; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + for (int ii=0; iisim_solver_plan[0]; + sim_solver_t type = sim_plan.sim_solver; + if (type == GNSF) + { + int nout = ocp_nlp_dims_get_from_attr(config, dims, out, 0, "init_gnsf_phi"); + + if (nrhs!=min_nrhs) + MEX_SETTER_NO_STAGE_SUPPORT(fun_name, field) + + acados_size = N*nout; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + for (int ii=0; iinlp_solver == SQP && rti_phase != 0) + { + MEX_FIELD_ONLY_SUPPORTED_FOR_SOLVER(fun_name, field, "sqp_rti") + } + ocp_nlp_solver_opts_set(config, opts, "rti_phase", &rti_phase); + } + else if (!strcmp(field, "qp_warm_start")) + { + acados_size = 1; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + int qp_warm_start = (int) value[0]; + ocp_nlp_solver_opts_set(config, opts, "qp_warm_start", &qp_warm_start); + } + else if (!strcmp(field, "warm_start_first_qp")) + { + acados_size = 1; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + int warm_start_first_qp = (int) value[0]; + ocp_nlp_solver_opts_set(config, opts, "warm_start_first_qp", &warm_start_first_qp); + } + else if (!strcmp(field, "print_level")) + { + acados_size = 1; + MEX_DIM_CHECK_VEC(fun_name, field, matlab_size, acados_size); + int print_level = (int) value[0]; + ocp_nlp_solver_opts_set(config, opts, "print_level", &print_level); + } + else + { + MEX_FIELD_NOT_SUPPORTED_SUGGEST(fun_name, field, "p, constr_x0,\ + constr_lbx, constr_ubx, constr_C, constr_D, constr_lg, constr_ug, constr_lh, constr_uh\ + constr_lbu, constr_ubu, cost_y_ref[_e],\ + cost_Vu, cost_Vx, cost_Vz, cost_W, cost_Z, cost_Zl, cost_Zu, cost_z,\ + cost_zl, cost_zu, init_x, init_u, init_z, init_xdot, init_gnsf_phi,\ + init_pi, nlp_solver_max_iter, qp_warm_start, warm_start_first_qp, print_level"); + } + + return; +} + diff --git a/pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c b/pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c new file mode 100644 index 0000000000..9fd4feab29 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_mex_solve.in.c @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +// system +#include +#include +#include +// acados +#include "acados_solver_{{ model.name }}.h" + +// mex +#include "mex.h" + + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + // C_ocp + long long *ptr; + const mxArray *C_ocp = prhs[0]; + + // capsule + ptr = (long long *) mxGetData( mxGetField( C_ocp, 0, "capsule" ) ); + nlp_solver_capsule *capsule = (nlp_solver_capsule *) ptr[0]; + + // solve + {{ model.name }}_acados_solve(capsule); + +} diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c b/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c new file mode 100644 index 0000000000..615978d02f --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.c @@ -0,0 +1,501 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +{%- if solver_options.hessian_approx %} + {%- set hessian_approx = solver_options.hessian_approx %} +{%- elif solver_options.sens_hess %} + {%- set hessian_approx = "EXACT" %} +{%- else %} + {%- set hessian_approx = "GAUSS_NEWTON" %} +{%- endif %} +// standard +#include +#include + +// acados +#include "acados_c/external_function_interface.h" +#include "acados_c/sim_interface.h" +#include "acados_c/external_function_interface.h" + +#include "acados/sim/sim_common.h" +#include "acados/utils/external_function_generic.h" +#include "acados/utils/print.h" + + +// example specific +#include "{{ model.name }}_model/{{ model.name }}_model.h" +#include "acados_sim_solver_{{ model.name }}.h" + + +// ** solver data ** + +sim_solver_capsule * {{ model.name }}_acados_sim_solver_create_capsule() +{ + void* capsule_mem = malloc(sizeof(sim_solver_capsule)); + sim_solver_capsule *capsule = (sim_solver_capsule *) capsule_mem; + + return capsule; +} + + +int {{ model.name }}_acados_sim_solver_free_capsule(sim_solver_capsule * capsule) +{ + free(capsule); + return 0; +} + + +int {{ model.name }}_acados_sim_create(sim_solver_capsule * capsule) +{ + // initialize + int nx = {{ dims.nx }}; + int nu = {{ dims.nu }}; + int nz = {{ dims.nz }}; + + {#// double Tsim = {{ solver_options.tf / dims.N }};#} + double Tsim = {{ solver_options.Tsim }}; + + {% if solver_options.integrator_type == "IRK" %} + capsule->sim_impl_dae_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_impl_dae_fun_jac_x_xdot_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_impl_dae_jac_x_xdot_u_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + + // external functions (implicit model) + capsule->sim_impl_dae_fun->casadi_fun = &{{ model.name }}_impl_dae_fun; + capsule->sim_impl_dae_fun->casadi_work = &{{ model.name }}_impl_dae_fun_work; + capsule->sim_impl_dae_fun->casadi_sparsity_in = &{{ model.name }}_impl_dae_fun_sparsity_in; + capsule->sim_impl_dae_fun->casadi_sparsity_out = &{{ model.name }}_impl_dae_fun_sparsity_out; + capsule->sim_impl_dae_fun->casadi_n_in = &{{ model.name }}_impl_dae_fun_n_in; + capsule->sim_impl_dae_fun->casadi_n_out = &{{ model.name }}_impl_dae_fun_n_out; + external_function_param_casadi_create(capsule->sim_impl_dae_fun, {{ dims.np }}); + + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_fun = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z; + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_work = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_work; + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_sparsity_in = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_in; + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_sparsity_out = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_out; + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_n_in = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_in; + capsule->sim_impl_dae_fun_jac_x_xdot_z->casadi_n_out = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_out; + external_function_param_casadi_create(capsule->sim_impl_dae_fun_jac_x_xdot_z, {{ dims.np }}); + + // external_function_param_casadi impl_dae_jac_x_xdot_u_z; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_fun = &{{ model.name }}_impl_dae_jac_x_xdot_u_z; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_work = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_work; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_sparsity_in = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_in; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_sparsity_out = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_out; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_n_in = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_n_in; + capsule->sim_impl_dae_jac_x_xdot_u_z->casadi_n_out = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_n_out; + external_function_param_casadi_create(capsule->sim_impl_dae_jac_x_xdot_u_z, {{ dims.np }}); + +{%- if hessian_approx == "EXACT" %} + capsule->sim_impl_dae_hess = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + // external_function_param_casadi impl_dae_jac_x_xdot_u_z; + capsule->sim_impl_dae_hess->casadi_fun = &{{ model.name }}_impl_dae_hess; + capsule->sim_impl_dae_hess->casadi_work = &{{ model.name }}_impl_dae_hess_work; + capsule->sim_impl_dae_hess->casadi_sparsity_in = &{{ model.name }}_impl_dae_hess_sparsity_in; + capsule->sim_impl_dae_hess->casadi_sparsity_out = &{{ model.name }}_impl_dae_hess_sparsity_out; + capsule->sim_impl_dae_hess->casadi_n_in = &{{ model.name }}_impl_dae_hess_n_in; + capsule->sim_impl_dae_hess->casadi_n_out = &{{ model.name }}_impl_dae_hess_n_out; + external_function_param_casadi_create(capsule->sim_impl_dae_hess, {{ dims.np }}); +{%- endif %} + + {% elif solver_options.integrator_type == "ERK" %} + // explicit ode + capsule->sim_forw_vde_casadi = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_expl_ode_fun_casadi = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + + capsule->sim_forw_vde_casadi->casadi_fun = &{{ model.name }}_expl_vde_forw; + capsule->sim_forw_vde_casadi->casadi_n_in = &{{ model.name }}_expl_vde_forw_n_in; + capsule->sim_forw_vde_casadi->casadi_n_out = &{{ model.name }}_expl_vde_forw_n_out; + capsule->sim_forw_vde_casadi->casadi_sparsity_in = &{{ model.name }}_expl_vde_forw_sparsity_in; + capsule->sim_forw_vde_casadi->casadi_sparsity_out = &{{ model.name }}_expl_vde_forw_sparsity_out; + capsule->sim_forw_vde_casadi->casadi_work = &{{ model.name }}_expl_vde_forw_work; + external_function_param_casadi_create(capsule->sim_forw_vde_casadi, {{ dims.np }}); + + capsule->sim_expl_ode_fun_casadi->casadi_fun = &{{ model.name }}_expl_ode_fun; + capsule->sim_expl_ode_fun_casadi->casadi_n_in = &{{ model.name }}_expl_ode_fun_n_in; + capsule->sim_expl_ode_fun_casadi->casadi_n_out = &{{ model.name }}_expl_ode_fun_n_out; + capsule->sim_expl_ode_fun_casadi->casadi_sparsity_in = &{{ model.name }}_expl_ode_fun_sparsity_in; + capsule->sim_expl_ode_fun_casadi->casadi_sparsity_out = &{{ model.name }}_expl_ode_fun_sparsity_out; + capsule->sim_expl_ode_fun_casadi->casadi_work = &{{ model.name }}_expl_ode_fun_work; + external_function_param_casadi_create(capsule->sim_expl_ode_fun_casadi, {{ dims.np }}); + +{%- if hessian_approx == "EXACT" %} + capsule->sim_expl_ode_hess = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + // external_function_param_casadi impl_dae_jac_x_xdot_u_z; + capsule->sim_expl_ode_hess->casadi_fun = &{{ model.name }}_expl_ode_hess; + capsule->sim_expl_ode_hess->casadi_work = &{{ model.name }}_expl_ode_hess_work; + capsule->sim_expl_ode_hess->casadi_sparsity_in = &{{ model.name }}_expl_ode_hess_sparsity_in; + capsule->sim_expl_ode_hess->casadi_sparsity_out = &{{ model.name }}_expl_ode_hess_sparsity_out; + capsule->sim_expl_ode_hess->casadi_n_in = &{{ model.name }}_expl_ode_hess_n_in; + capsule->sim_expl_ode_hess->casadi_n_out = &{{ model.name }}_expl_ode_hess_n_out; + external_function_param_casadi_create(capsule->sim_expl_ode_hess, {{ dims.np }}); +{%- endif %} + + {% elif solver_options.integrator_type == "GNSF" -%} + capsule->sim_gnsf_phi_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_gnsf_phi_fun_jac_y = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_gnsf_phi_jac_y_uhat = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + capsule->sim_gnsf_get_matrices_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)); + + capsule->sim_gnsf_phi_fun->casadi_fun = &{{ model.name }}_gnsf_phi_fun; + capsule->sim_gnsf_phi_fun->casadi_n_in = &{{ model.name }}_gnsf_phi_fun_n_in; + capsule->sim_gnsf_phi_fun->casadi_n_out = &{{ model.name }}_gnsf_phi_fun_n_out; + capsule->sim_gnsf_phi_fun->casadi_sparsity_in = &{{ model.name }}_gnsf_phi_fun_sparsity_in; + capsule->sim_gnsf_phi_fun->casadi_sparsity_out = &{{ model.name }}_gnsf_phi_fun_sparsity_out; + capsule->sim_gnsf_phi_fun->casadi_work = &{{ model.name }}_gnsf_phi_fun_work; + external_function_param_casadi_create(capsule->sim_gnsf_phi_fun, {{ dims.np }}); + + capsule->sim_gnsf_phi_fun_jac_y->casadi_fun = &{{ model.name }}_gnsf_phi_fun_jac_y; + capsule->sim_gnsf_phi_fun_jac_y->casadi_n_in = &{{ model.name }}_gnsf_phi_fun_jac_y_n_in; + capsule->sim_gnsf_phi_fun_jac_y->casadi_n_out = &{{ model.name }}_gnsf_phi_fun_jac_y_n_out; + capsule->sim_gnsf_phi_fun_jac_y->casadi_sparsity_in = &{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_in; + capsule->sim_gnsf_phi_fun_jac_y->casadi_sparsity_out = &{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_out; + capsule->sim_gnsf_phi_fun_jac_y->casadi_work = &{{ model.name }}_gnsf_phi_fun_jac_y_work; + external_function_param_casadi_create(capsule->sim_gnsf_phi_fun_jac_y, {{ dims.np }}); + + capsule->sim_gnsf_phi_jac_y_uhat->casadi_fun = &{{ model.name }}_gnsf_phi_jac_y_uhat; + capsule->sim_gnsf_phi_jac_y_uhat->casadi_n_in = &{{ model.name }}_gnsf_phi_jac_y_uhat_n_in; + capsule->sim_gnsf_phi_jac_y_uhat->casadi_n_out = &{{ model.name }}_gnsf_phi_jac_y_uhat_n_out; + capsule->sim_gnsf_phi_jac_y_uhat->casadi_sparsity_in = &{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_in; + capsule->sim_gnsf_phi_jac_y_uhat->casadi_sparsity_out = &{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_out; + capsule->sim_gnsf_phi_jac_y_uhat->casadi_work = &{{ model.name }}_gnsf_phi_jac_y_uhat_work; + external_function_param_casadi_create(capsule->sim_gnsf_phi_jac_y_uhat, {{ dims.np }}); + + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_fun = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz; + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_n_in = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_in; + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_n_out = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_out; + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_sparsity_in = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_in; + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_sparsity_out = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_out; + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z->casadi_work = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_work; + external_function_param_casadi_create(capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z, {{ dims.np }}); + + capsule->sim_gnsf_get_matrices_fun->casadi_fun = &{{ model.name }}_gnsf_get_matrices_fun; + capsule->sim_gnsf_get_matrices_fun->casadi_n_in = &{{ model.name }}_gnsf_get_matrices_fun_n_in; + capsule->sim_gnsf_get_matrices_fun->casadi_n_out = &{{ model.name }}_gnsf_get_matrices_fun_n_out; + capsule->sim_gnsf_get_matrices_fun->casadi_sparsity_in = &{{ model.name }}_gnsf_get_matrices_fun_sparsity_in; + capsule->sim_gnsf_get_matrices_fun->casadi_sparsity_out = &{{ model.name }}_gnsf_get_matrices_fun_sparsity_out; + capsule->sim_gnsf_get_matrices_fun->casadi_work = &{{ model.name }}_gnsf_get_matrices_fun_work; + external_function_param_casadi_create(capsule->sim_gnsf_get_matrices_fun, {{ dims.np }}); + {% endif %} + + // sim plan & config + sim_solver_plan plan; + plan.sim_solver = {{ solver_options.integrator_type }}; + + // create correct config based on plan + sim_config * {{ model.name }}_sim_config = sim_config_create(plan); + capsule->acados_sim_config = {{ model.name }}_sim_config; + + // sim dims + void *{{ model.name }}_sim_dims = sim_dims_create({{ model.name }}_sim_config); + capsule->acados_sim_dims = {{ model.name }}_sim_dims; + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nx", &nx); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nu", &nu); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nz", &nz); +{% if solver_options.integrator_type == "GNSF" %} + int gnsf_nx1 = {{ dims.gnsf_nx1 }}; + int gnsf_nz1 = {{ dims.gnsf_nz1 }}; + int gnsf_nout = {{ dims.gnsf_nout }}; + int gnsf_ny = {{ dims.gnsf_ny }}; + int gnsf_nuhat = {{ dims.gnsf_nuhat }}; + + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nx1", &gnsf_nx1); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nz1", &gnsf_nz1); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nout", &gnsf_nout); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "ny", &gnsf_ny); + sim_dims_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, "nuhat", &gnsf_nuhat); +{% endif %} + + // sim opts + sim_opts *{{ model.name }}_sim_opts = sim_opts_create({{ model.name }}_sim_config, {{ model.name }}_sim_dims); + capsule->acados_sim_opts = {{ model.name }}_sim_opts; + int tmp_int = {{ solver_options.sim_method_newton_iter }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "newton_iter", &tmp_int); + bool tmp_bool = {{ solver_options.sim_method_jac_reuse }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "jac_reuse", &tmp_bool); + +{% if problem_class == "SIM" %} + tmp_int = {{ solver_options.sim_method_num_stages }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "num_stages", &tmp_int); + tmp_int = {{ solver_options.sim_method_num_steps }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "num_steps", &tmp_int); + + // options that are not available to AcadosOcpSolver + // (in OCP they will be determined by other options, like exact_hessian) + tmp_bool = {{ solver_options.sens_forw }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "sens_forw", &tmp_bool); + tmp_bool = {{ solver_options.sens_adj }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "sens_adj", &tmp_bool); + tmp_bool = {{ solver_options.sens_algebraic }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "sens_algebraic", &tmp_bool); + tmp_bool = {{ solver_options.sens_hess }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "sens_hess", &tmp_bool); + tmp_bool = {{ solver_options.output_z }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "output_z", &tmp_bool); + +{% else %} {# num_stages and num_steps of first shooting interval are used #} + tmp_int = {{ solver_options.sim_method_num_stages[0] }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "num_stages", &tmp_int); + tmp_int = {{ solver_options.sim_method_num_steps[0] }}; + sim_opts_set({{ model.name }}_sim_config, {{ model.name }}_sim_opts, "num_steps", &tmp_int); +{% endif %} + + // sim in / out + sim_in *{{ model.name }}_sim_in = sim_in_create({{ model.name }}_sim_config, {{ model.name }}_sim_dims); + capsule->acados_sim_in = {{ model.name }}_sim_in; + sim_out *{{ model.name }}_sim_out = sim_out_create({{ model.name }}_sim_config, {{ model.name }}_sim_dims); + capsule->acados_sim_out = {{ model.name }}_sim_out; + + sim_in_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, + {{ model.name }}_sim_in, "T", &Tsim); + + // model functions +{%- if solver_options.integrator_type == "IRK" %} + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "impl_ode_fun", capsule->sim_impl_dae_fun); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "impl_ode_fun_jac_x_xdot", capsule->sim_impl_dae_fun_jac_x_xdot_z); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "impl_ode_jac_x_xdot_u", capsule->sim_impl_dae_jac_x_xdot_u_z); +{%- if hessian_approx == "EXACT" %} + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "impl_dae_hess", capsule->sim_impl_dae_hess); +{%- endif %} + +{%- elif solver_options.integrator_type == "ERK" %} + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "expl_vde_for", capsule->sim_forw_vde_casadi); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "expl_ode_fun", capsule->sim_expl_ode_fun_casadi); +{%- if hessian_approx == "EXACT" %} + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "expl_ode_hess", capsule->sim_expl_ode_hess); +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "phi_fun", capsule->sim_gnsf_phi_fun); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "phi_fun_jac_y", capsule->sim_gnsf_phi_fun_jac_y); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "phi_jac_y_uhat", capsule->sim_gnsf_phi_jac_y_uhat); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "f_lo_jac_x1_x1dot_u_z", capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z); + {{ model.name }}_sim_config->model_set({{ model.name }}_sim_in->model, + "gnsf_get_matrices_fun", capsule->sim_gnsf_get_matrices_fun); +{%- endif %} + + // sim solver + sim_solver *{{ model.name }}_sim_solver = sim_solver_create({{ model.name }}_sim_config, + {{ model.name }}_sim_dims, {{ model.name }}_sim_opts); + capsule->acados_sim_solver = {{ model.name }}_sim_solver; + + /* initialize parameter values */ + {% if dims.np > 0 %} + // initialize parameters to nominal value + double p[{{ dims.np }}]; + {% for i in range(end=dims.np) %} + p[{{ i }}] = {{ parameter_values[i] }}; + {%- endfor %} + +{%- if solver_options.integrator_type == "ERK" %} + capsule->sim_forw_vde_casadi[0].set_param(capsule->sim_forw_vde_casadi, p); + capsule->sim_expl_ode_fun_casadi[0].set_param(capsule->sim_expl_ode_fun_casadi, p); +{%- if hessian_approx == "EXACT" %} + capsule->sim_expl_ode_hess[0].set_param(capsule->sim_expl_ode_hess, p); +{%- endif %} +{%- elif solver_options.integrator_type == "IRK" %} + capsule->sim_impl_dae_fun[0].set_param(capsule->sim_impl_dae_fun, p); + capsule->sim_impl_dae_fun_jac_x_xdot_z[0].set_param(capsule->sim_impl_dae_fun_jac_x_xdot_z, p); + capsule->sim_impl_dae_jac_x_xdot_u_z[0].set_param(capsule->sim_impl_dae_jac_x_xdot_u_z, p); +{%- if hessian_approx == "EXACT" %} + capsule->sim_impl_dae_hess[0].set_param(capsule->sim_impl_dae_hess, p); +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} + capsule->sim_gnsf_phi_fun[0].set_param(capsule->sim_gnsf_phi_fun, p); + capsule->sim_gnsf_phi_fun_jac_y[0].set_param(capsule->sim_gnsf_phi_fun_jac_y, p); + capsule->sim_gnsf_phi_jac_y_uhat[0].set_param(capsule->sim_gnsf_phi_jac_y_uhat, p); + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z[0].set_param(capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z, p); + capsule->sim_gnsf_get_matrices_fun[0].set_param(capsule->sim_gnsf_get_matrices_fun, p); +{% endif %} + {% endif %}{# if dims.np #} + + /* initialize input */ + // x + double x0[{{ dims.nx }}]; + for (int ii = 0; ii < {{ dims.nx }}; ii++) + x0[ii] = 0.0; + + sim_in_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, + {{ model.name }}_sim_in, "x", x0); + + + // u + double u0[{{ dims.nu }}]; + for (int ii = 0; ii < {{ dims.nu }}; ii++) + u0[ii] = 0.0; + + sim_in_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, + {{ model.name }}_sim_in, "u", u0); + + // S_forw + double S_forw[{{ dims.nx * (dims.nx + dims.nu) }}]; + for (int ii = 0; ii < {{ dims.nx * (dims.nx + dims.nu) }}; ii++) + S_forw[ii] = 0.0; + for (int ii = 0; ii < {{ dims.nx }}; ii++) + S_forw[ii + ii * {{ dims.nx }} ] = 1.0; + + + sim_in_set({{ model.name }}_sim_config, {{ model.name }}_sim_dims, + {{ model.name }}_sim_in, "S_forw", S_forw); + + int status = sim_precompute({{ model.name }}_sim_solver, {{ model.name }}_sim_in, {{ model.name }}_sim_out); + + return status; +} + + +int {{ model.name }}_acados_sim_solve(sim_solver_capsule *capsule) +{ + // integrate dynamics using acados sim_solver + int status = sim_solve(capsule->acados_sim_solver, + capsule->acados_sim_in, capsule->acados_sim_out); + if (status != 0) + printf("error in {{ model.name }}_acados_sim_solve()! Exiting.\n"); + + return status; +} + + +int {{ model.name }}_acados_sim_free(sim_solver_capsule *capsule) +{ + // free memory + sim_solver_destroy(capsule->acados_sim_solver); + sim_in_destroy(capsule->acados_sim_in); + sim_out_destroy(capsule->acados_sim_out); + sim_opts_destroy(capsule->acados_sim_opts); + sim_dims_destroy(capsule->acados_sim_dims); + sim_config_destroy(capsule->acados_sim_config); + + // free external function +{%- if solver_options.integrator_type == "IRK" %} + external_function_param_casadi_free(capsule->sim_impl_dae_fun); + external_function_param_casadi_free(capsule->sim_impl_dae_fun_jac_x_xdot_z); + external_function_param_casadi_free(capsule->sim_impl_dae_jac_x_xdot_u_z); +{%- if hessian_approx == "EXACT" %} + external_function_param_casadi_free(capsule->sim_impl_dae_hess); +{%- endif %} +{%- elif solver_options.integrator_type == "ERK" %} + external_function_param_casadi_free(capsule->sim_forw_vde_casadi); + external_function_param_casadi_free(capsule->sim_expl_ode_fun_casadi); +{%- if hessian_approx == "EXACT" %} + external_function_param_casadi_free(capsule->sim_expl_ode_hess); +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} + external_function_param_casadi_free(capsule->sim_gnsf_phi_fun); + external_function_param_casadi_free(capsule->sim_gnsf_phi_fun_jac_y); + external_function_param_casadi_free(capsule->sim_gnsf_phi_jac_y_uhat); + external_function_param_casadi_free(capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z); + external_function_param_casadi_free(capsule->sim_gnsf_get_matrices_fun); +{% endif %} + + return 0; +} + + +int {{ model.name }}_acados_sim_update_params(sim_solver_capsule *capsule, double *p, int np) +{ + int status = 0; + int casadi_np = {{ dims.np }}; + + if (casadi_np != np) { + printf("{{ model.name }}_acados_sim_update_params: trying to set %i parameters for external functions." + " External function has %i parameters. Exiting.\n", np, casadi_np); + exit(1); + } + +{%- if solver_options.integrator_type == "ERK" %} + capsule->sim_forw_vde_casadi[0].set_param(capsule->sim_forw_vde_casadi, p); + capsule->sim_expl_ode_fun_casadi[0].set_param(capsule->sim_expl_ode_fun_casadi, p); +{%- if hessian_approx == "EXACT" %} + capsule->sim_expl_ode_hess[0].set_param(capsule->sim_expl_ode_hess, p); +{%- endif %} +{%- elif solver_options.integrator_type == "IRK" %} + capsule->sim_impl_dae_fun[0].set_param(capsule->sim_impl_dae_fun, p); + capsule->sim_impl_dae_fun_jac_x_xdot_z[0].set_param(capsule->sim_impl_dae_fun_jac_x_xdot_z, p); + capsule->sim_impl_dae_jac_x_xdot_u_z[0].set_param(capsule->sim_impl_dae_jac_x_xdot_u_z, p); +{%- if hessian_approx == "EXACT" %} + capsule->sim_impl_dae_hess[0].set_param(capsule->sim_impl_dae_hess, p); +{%- endif %} +{%- elif solver_options.integrator_type == "GNSF" %} + capsule->sim_gnsf_phi_fun[0].set_param(capsule->sim_gnsf_phi_fun, p); + capsule->sim_gnsf_phi_fun_jac_y[0].set_param(capsule->sim_gnsf_phi_fun_jac_y, p); + capsule->sim_gnsf_phi_jac_y_uhat[0].set_param(capsule->sim_gnsf_phi_jac_y_uhat, p); + capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z[0].set_param(capsule->sim_gnsf_f_lo_jac_x1_x1dot_u_z, p); + capsule->sim_gnsf_get_matrices_fun[0].set_param(capsule->sim_gnsf_get_matrices_fun, p); +{% endif %} + + return status; +} + +/* getters pointers to C objects*/ +sim_config * {{ model.name }}_acados_get_sim_config(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_config; +}; + +sim_in * {{ model.name }}_acados_get_sim_in(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_in; +}; + +sim_out * {{ model.name }}_acados_get_sim_out(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_out; +}; + +void * {{ model.name }}_acados_get_sim_dims(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_dims; +}; + +sim_opts * {{ model.name }}_acados_get_sim_opts(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_opts; +}; + +sim_solver * {{ model.name }}_acados_get_sim_solver(sim_solver_capsule *capsule) +{ + return capsule->acados_sim_solver; +}; + diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h b/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h new file mode 100644 index 0000000000..88a1787416 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_sim_solver.in.h @@ -0,0 +1,98 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#ifndef ACADOS_SIM_{{ model.name }}_H_ +#define ACADOS_SIM_{{ model.name }}_H_ + +#include "acados_c/sim_interface.h" +#include "acados_c/external_function_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +// ** capsule for solver data ** +typedef struct sim_solver_capsule +{ + // acados objects + sim_in *acados_sim_in; + sim_out *acados_sim_out; + sim_solver *acados_sim_solver; + sim_opts *acados_sim_opts; + sim_config *acados_sim_config; + void *acados_sim_dims; + + /* external functions */ + // ERK + external_function_param_casadi * sim_forw_vde_casadi; + external_function_param_casadi * sim_expl_ode_fun_casadi; + external_function_param_casadi * sim_expl_ode_hess; + + // IRK + external_function_param_casadi * sim_impl_dae_fun; + external_function_param_casadi * sim_impl_dae_fun_jac_x_xdot_z; + external_function_param_casadi * sim_impl_dae_jac_x_xdot_u_z; + external_function_param_casadi * sim_impl_dae_hess; + + // GNSF + external_function_param_casadi * sim_gnsf_phi_fun; + external_function_param_casadi * sim_gnsf_phi_fun_jac_y; + external_function_param_casadi * sim_gnsf_phi_jac_y_uhat; + external_function_param_casadi * sim_gnsf_f_lo_jac_x1_x1dot_u_z; + external_function_param_casadi * sim_gnsf_get_matrices_fun; + +} sim_solver_capsule; + + +int {{ model.name }}_acados_sim_create(sim_solver_capsule *capsule); +int {{ model.name }}_acados_sim_solve(sim_solver_capsule *capsule); +int {{ model.name }}_acados_sim_free(sim_solver_capsule *capsule); +int {{ model.name }}_acados_sim_update_params(sim_solver_capsule *capsule, double *value, int np); + +sim_config * {{ model.name }}_acados_get_sim_config(sim_solver_capsule *capsule); +sim_in * {{ model.name }}_acados_get_sim_in(sim_solver_capsule *capsule); +sim_out * {{ model.name }}_acados_get_sim_out(sim_solver_capsule *capsule); +void * {{ model.name }}_acados_get_sim_dims(sim_solver_capsule *capsule); +sim_opts * {{ model.name }}_acados_get_sim_opts(sim_solver_capsule *capsule); +sim_solver * {{ model.name }}_acados_get_sim_solver(sim_solver_capsule *capsule); + + +sim_solver_capsule * {{ model.name }}_acados_sim_solver_create_capsule(); +int {{ model.name }}_acados_sim_solver_free_capsule(sim_solver_capsule *capsule); + +#ifdef __cplusplus +} +#endif + +#endif // ACADOS_SIM_{{ model.name }}_H_ diff --git a/pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c b/pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c new file mode 100644 index 0000000000..234295fa29 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_sim_solver_sfun.in.c @@ -0,0 +1,233 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#define S_FUNCTION_NAME acados_sim_solver_sfunction_{{ model.name }} +#define S_FUNCTION_LEVEL 2 + +#define MDL_START + +// acados +#include "acados/utils/print.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_c/external_function_interface.h" + +// example specific +#include "{{ model.name }}_model/{{ model.name }}_model.h" +#include "acados_sim_solver_{{ model.name }}.h" + +#include "simstruc.h" + +#define SAMPLINGTIME {{ solver_options.Tsim }} + + +static void mdlInitializeSizes (SimStruct *S) +{ + // specify the number of continuous and discrete states + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + {# compute number of input ports #} + {%- set n_inputs = 1 %} {# x0 #} + {%- if dims.nu > 0 %} {# u0 -#} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif %} + {%- if dims.np > 0 %} {# parameters #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif %} + + // specify the number of input ports + if ( !ssSetNumInputPorts(S, {{ n_inputs }}) ) + return; + + // specify the number of output ports + if ( !ssSetNumOutputPorts(S, 1) ) + return; + + // specify dimension information for the input ports + {%- set i_input = 0 %} + // x0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nx }}); + + {%- if dims.nu > 0 %} + {%- set i_input = i_input + 1 %} + // u0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nu }}); + {%- endif %} + + {%- if dims.np > 0 %} + {%- set i_input = i_input + 1 %} + // parameters + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.np }}); + {%- endif %} + + // specify dimension information for the output ports + ssSetOutputPortVectorDimension(S, 0, {{ dims.nx }} ); // xnext + + // specify the direct feedthrough status + // should be set to 1 for all inputs used in mdlOutputs + {%- for i in range(end=n_inputs) %} + ssSetInputPortDirectFeedThrough(S, {{ i }}, 1); + {%- endfor %} + + // one sample time + ssSetNumSampleTimes(S, 1); +} + + +#if defined(MATLAB_MEX_FILE) + +#define MDL_SET_INPUT_PORT_DIMENSION_INFO +#define MDL_SET_OUTPUT_PORT_DIMENSION_INFO + +static void mdlSetInputPortDimensionInfo(SimStruct *S, int_T port, const DimsInfo_T *dimsInfo) +{ + if ( !ssSetInputPortDimensionInfo(S, port, dimsInfo) ) + return; +} + +static void mdlSetOutputPortDimensionInfo(SimStruct *S, int_T port, const DimsInfo_T *dimsInfo) +{ + if ( !ssSetOutputPortDimensionInfo(S, port, dimsInfo) ) + return; +} + +#endif /* MATLAB_MEX_FILE */ + + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, SAMPLINGTIME); + ssSetOffsetTime(S, 0, 0.0); +} + + +static void mdlStart(SimStruct *S) +{ + sim_solver_capsule *capsule = {{ model.name }}_acados_sim_solver_create_capsule(); + {{ model.name }}_acados_sim_create(capsule); + + ssSetUserData(S, (void*)capsule); +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + sim_solver_capsule *capsule = ssGetUserData(S); + + sim_config *acados_sim_config = {{ model.name }}_acados_get_sim_config(capsule); + sim_in *acados_sim_in = {{ model.name }}_acados_get_sim_in(capsule); + sim_out *acados_sim_out = {{ model.name }}_acados_get_sim_out(capsule); + void *acados_sim_dims = {{ model.name }}_acados_get_sim_dims(capsule); + // sim_opts * {{ model.name }}_acados_get_sim_opts(capsule); + // sim_solver * {{ model.name }}_acados_get_sim_solver(capsule); + + InputRealPtrsType in_sign; + {% set input_sizes = [dims.nx, dims.nu, dims.np] %} + + // local buffer + {%- set buffer_size = input_sizes | sort | last %} + real_t buffer[{{ buffer_size }}]; + + + /* go through inputs */ + {%- set i_input = 0 %} + // initial condition + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.nx }}; i++) + buffer[i] = (double)(*in_sign[i]); + + sim_in_set(acados_sim_config, acados_sim_dims, + acados_sim_in, "x", buffer); + + + // ssPrintf("\nin acados sim:\n"); + // for (int i = 0; i < {{ dims.nx }}; i++) ssPrintf("x0[%d] = %f\n", i, buffer[i]); + // ssPrintf("\n"); + +{% if dims.nu > 0 %} + // control input - u + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.nu }}; i++) + buffer[i] = (double)(*in_sign[i]); + + sim_in_set(acados_sim_config, acados_sim_dims, + acados_sim_in, "u", buffer); +{%- endif %} + + +{% if dims.np > 0 %} + // parameters + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.np }}; i++) + buffer[i] = (double)(*in_sign[i]); + + // update value of parameters + {{ model.name }}_acados_sim_update_params(capsule, buffer, {{ dims.np }}); +{%- endif %} + + + /* call solver */ + int acados_status = {{ model.name }}_acados_sim_solve(capsule); + + + /* set outputs */ + real_t *out_x = ssGetOutputPortRealSignal(S, 0); + + // get simulated state + sim_out_get(acados_sim_config, acados_sim_dims, acados_sim_out, + "xn", (void *) out_x); + + // ssPrintf("\nacados sim solve: returned %d\n", acados_status); + // for (int i = 0; i < {{ dims.nx }}; i++) ssPrintf("x_sim[%d] = %f\n", i, out_x[i]); + // ssPrintf("\n"); + +} + + +static void mdlTerminate(SimStruct *S) +{ + sim_solver_capsule *capsule = ssGetUserData(S); + + {{ model.name }}_acados_sim_free(capsule); + {{ model.name }}_acados_sim_solver_free_capsule(capsule); +} + + +#ifdef MATLAB_MEX_FILE +#include "simulink.c" +#else +#include "cg_sfun.h" +#endif diff --git a/pyextra/acados_template/c_templates_tera/acados_solver.in.c b/pyextra/acados_template/c_templates_tera/acados_solver.in.c new file mode 100644 index 0000000000..c79e826365 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_solver.in.c @@ -0,0 +1,2115 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +// standard +#include +#include +// acados +#include "acados/utils/print.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_c/external_function_interface.h" + +// example specific +#include "{{ model.name }}_model/{{ model.name }}_model.h" +{% if constraints.constr_type == "BGP" and dims.nphi %} +#include "{{ model.name }}_constraints/{{ model.name }}_phi_constraint.h" +{% endif %} +{% if constraints.constr_type_e == "BGP" and dims.nphi_e > 0 %} +#include "{{ model.name }}_constraints/{{ model.name }}_phi_e_constraint.h" +{% endif %} +{% if constraints.constr_type == "BGH" and dims.nh > 0 %} +#include "{{ model.name }}_constraints/{{ model.name }}_h_constraint.h" +{% endif %} +{% if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} +#include "{{ model.name }}_constraints/{{ model.name }}_h_e_constraint.h" +{% endif %} +{%- if cost.cost_type == "NONLINEAR_LS" %} +#include "{{ model.name }}_cost/{{ model.name }}_cost_y_fun.h" +{%- elif cost.cost_type == "EXTERNAL" %} +#include "{{ model.name }}_cost/{{ model.name }}_external_cost.h" +{%- endif %} +{%- if cost.cost_type_0 == "NONLINEAR_LS" %} +#include "{{ model.name }}_cost/{{ model.name }}_cost_y_0_fun.h" +{%- elif cost.cost_type_0 == "EXTERNAL" %} +#include "{{ model.name }}_cost/{{ model.name }}_external_cost_0.h" +{%- endif %} +{%- if cost.cost_type_e == "NONLINEAR_LS" %} +#include "{{ model.name }}_cost/{{ model.name }}_cost_y_e_fun.h" +{%- elif cost.cost_type_e == "EXTERNAL" %} +#include "{{ model.name }}_cost/{{ model.name }}_external_cost_e.h" +{%- endif %} + +#include "acados_solver_{{ model.name }}.h" + +#define NX {{ dims.nx }} +#define NZ {{ dims.nz }} +#define NU {{ dims.nu }} +#define NP {{ dims.np }} +#define NBX {{ dims.nbx }} +#define NBX0 {{ dims.nbx_0 }} +#define NBU {{ dims.nbu }} +#define NSBX {{ dims.nsbx }} +#define NSBU {{ dims.nsbu }} +#define NSH {{ dims.nsh }} +#define NSG {{ dims.nsg }} +#define NSPHI {{ dims.nsphi }} +#define NSHN {{ dims.nsh_e }} +#define NSGN {{ dims.nsg_e }} +#define NSPHIN {{ dims.nsphi_e }} +#define NSBXN {{ dims.nsbx_e }} +#define NS {{ dims.ns }} +#define NSN {{ dims.ns_e }} +#define NG {{ dims.ng }} +#define NBXN {{ dims.nbx_e }} +#define NGN {{ dims.ng_e }} +#define NY0 {{ dims.ny_0 }} +#define NY {{ dims.ny }} +#define NYN {{ dims.ny_e }} +#define N {{ dims.N }} +#define NH {{ dims.nh }} +#define NPHI {{ dims.nphi }} +#define NHN {{ dims.nh_e }} +#define NPHIN {{ dims.nphi_e }} +#define NR {{ dims.nr }} + + +// ** solver data ** + +nlp_solver_capsule * {{ model.name }}_acados_create_capsule() +{ + void* capsule_mem = malloc(sizeof(nlp_solver_capsule)); + nlp_solver_capsule *capsule = (nlp_solver_capsule *) capsule_mem; + + return capsule; +} + + +int {{ model.name }}_acados_free_capsule(nlp_solver_capsule *capsule) +{ + free(capsule); + return 0; +} + + +int {{ model.name }}_acados_create(nlp_solver_capsule * capsule) +{ + int status = 0; + + // number of expected runtime parameters + capsule->nlp_np = NP; + + /************************************************ + * plan & config + ************************************************/ + ocp_nlp_plan * nlp_solver_plan = ocp_nlp_plan_create(N); + capsule->nlp_solver_plan = nlp_solver_plan; + + {%- if solver_options.nlp_solver_type == "SQP" %} + nlp_solver_plan->nlp_solver = SQP; + {% else %} + nlp_solver_plan->nlp_solver = SQP_RTI; + {%- endif %} + + nlp_solver_plan->ocp_qp_solver_plan.qp_solver = {{ solver_options.qp_solver }}; + + nlp_solver_plan->nlp_cost[0] = {{ cost.cost_type_0 }}; + for (int i = 1; i < N; i++) + nlp_solver_plan->nlp_cost[i] = {{ cost.cost_type }}; + + nlp_solver_plan->nlp_cost[N] = {{ cost.cost_type_e }}; + + for (int i = 0; i < N; i++) + { + {% if solver_options.integrator_type == "DISCRETE" %} + nlp_solver_plan->nlp_dynamics[i] = DISCRETE_MODEL; + // discrete dynamics does not need sim solver option, this field is ignored + nlp_solver_plan->sim_solver_plan[i].sim_solver = INVALID_SIM_SOLVER; + {% else %} + nlp_solver_plan->nlp_dynamics[i] = CONTINUOUS_MODEL; + nlp_solver_plan->sim_solver_plan[i].sim_solver = {{ solver_options.integrator_type }}; + {%- endif %} + } + + for (int i = 0; i < N; i++) + { + {% if constraints.constr_type == "BGP" %} + nlp_solver_plan->nlp_constraints[i] = BGP; + {%- else -%} + nlp_solver_plan->nlp_constraints[i] = BGH; + {%- endif %} + } + + {%- if constraints.constr_type_e == "BGP" %} + nlp_solver_plan->nlp_constraints[N] = BGP; + {% else %} + nlp_solver_plan->nlp_constraints[N] = BGH; + {%- endif %} + +{%- if solver_options.hessian_approx == "EXACT" %} + {%- if solver_options.regularize_method == "NO_REGULARIZE" %} + nlp_solver_plan->regularization = NO_REGULARIZE; + {%- elif solver_options.regularize_method == "MIRROR" %} + nlp_solver_plan->regularization = MIRROR; + {%- elif solver_options.regularize_method == "PROJECT" %} + nlp_solver_plan->regularization = PROJECT; + {%- elif solver_options.regularize_method == "PROJECT_REDUC_HESS" %} + nlp_solver_plan->regularization = PROJECT_REDUC_HESS; + {%- elif solver_options.regularize_method == "CONVEXIFY" %} + nlp_solver_plan->regularization = CONVEXIFY; + {%- endif %} +{%- endif %} + ocp_nlp_config * nlp_config = ocp_nlp_config_create(*nlp_solver_plan); + capsule->nlp_config = nlp_config; + + + /************************************************ + * dimensions + ************************************************/ + int nx[N+1]; + int nu[N+1]; + int nbx[N+1]; + int nbu[N+1]; + int nsbx[N+1]; + int nsbu[N+1]; + int nsg[N+1]; + int nsh[N+1]; + int nsphi[N+1]; + int ns[N+1]; + int ng[N+1]; + int nh[N+1]; + int nphi[N+1]; + int nz[N+1]; + int ny[N+1]; + int nr[N+1]; + int nbxe[N+1]; + + for (int i = 0; i < N+1; i++) + { + // common + nx[i] = NX; + nu[i] = NU; + nz[i] = NZ; + ns[i] = NS; + // cost + ny[i] = NY; + // constraints + nbx[i] = NBX; + nbu[i] = NBU; + nsbx[i] = NSBX; + nsbu[i] = NSBU; + nsg[i] = NSG; + nsh[i] = NSH; + nsphi[i] = NSPHI; + ng[i] = NG; + nh[i] = NH; + nphi[i] = NPHI; + nr[i] = NR; + nbxe[i] = 0; + } + + // for initial state + nbx[0] = NBX0; + nsbx[0] = 0; + ns[0] = NS - NSBX; + nbxe[0] = {{ dims.nbxe_0 }}; + ny[0] = NY0; + + // terminal - common + nu[N] = 0; + nz[N] = 0; + ns[N] = NSN; + // cost + ny[N] = NYN; + // constraint + nbx[N] = NBXN; + nbu[N] = 0; + ng[N] = NGN; + nh[N] = NHN; + nphi[N] = NPHIN; + nr[N] = {{ dims.nr_e }}; + + nsbx[N] = NSBXN; + nsbu[N] = 0; + nsg[N] = NSGN; + nsh[N] = NSHN; + nsphi[N] = NSPHIN; + + /* create and set ocp_nlp_dims */ + ocp_nlp_dims * nlp_dims = ocp_nlp_dims_create(nlp_config); + capsule->nlp_dims = nlp_dims; + + ocp_nlp_dims_set_opt_vars(nlp_config, nlp_dims, "nx", nx); + ocp_nlp_dims_set_opt_vars(nlp_config, nlp_dims, "nu", nu); + ocp_nlp_dims_set_opt_vars(nlp_config, nlp_dims, "nz", nz); + ocp_nlp_dims_set_opt_vars(nlp_config, nlp_dims, "ns", ns); + + for (int i = 0; i <= N; i++) + { + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nbx", &nbx[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nbu", &nbu[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nsbx", &nsbx[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nsbu", &nsbu[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "ng", &ng[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nsg", &nsg[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nbxe", &nbxe[i]); + } + + {%- if cost.cost_type_0 == "NONLINEAR_LS" or cost.cost_type_0 == "LINEAR_LS" %} + ocp_nlp_dims_set_cost(nlp_config, nlp_dims, 0, "ny", &ny[0]); + {%- endif %} + + {%- if cost.cost_type == "NONLINEAR_LS" or cost.cost_type == "LINEAR_LS" %} + for (int i = 1; i < N; i++) + ocp_nlp_dims_set_cost(nlp_config, nlp_dims, i, "ny", &ny[i]); + {%- endif %} + + for (int i = 0; i < N; i++) + { + {%- if constraints.constr_type == "BGH" and dims.nh > 0 %} + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nh", &nh[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nsh", &nsh[i]); + {%- elif constraints.constr_type == "BGP" and dims.nphi > 0 %} + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nr", &nr[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nphi", &nphi[i]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, i, "nsphi", &nsphi[i]); + {%- endif %} + } + + {%- if constraints.constr_type_e == "BGH" %} + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, N, "nh", &nh[N]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, N, "nsh", &nsh[N]); + {%- elif constraints.constr_type_e == "BGP" %} + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, N, "nr", &nr[N]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, N, "nphi", &nphi[N]); + ocp_nlp_dims_set_constraints(nlp_config, nlp_dims, N, "nsphi", &nsphi[N]); + {%- endif %} + {%- if cost.cost_type_e == "NONLINEAR_LS" or cost.cost_type_e == "LINEAR_LS" %} + ocp_nlp_dims_set_cost(nlp_config, nlp_dims, N, "ny", &ny[N]); + {%- endif %} + +{% if solver_options.integrator_type == "GNSF" -%} + // GNSF specific dimensions + int gnsf_nx1 = {{ dims.gnsf_nx1 }}; + int gnsf_nz1 = {{ dims.gnsf_nz1 }}; + int gnsf_nout = {{ dims.gnsf_nout }}; + int gnsf_ny = {{ dims.gnsf_ny }}; + int gnsf_nuhat = {{ dims.gnsf_nuhat }}; + + for (int i = 0; i < N; i++) + { + if (nlp_solver_plan->sim_solver_plan[i].sim_solver == GNSF) + { + ocp_nlp_dims_set_dynamics(nlp_config, nlp_dims, i, "gnsf_nx1", &gnsf_nx1); + ocp_nlp_dims_set_dynamics(nlp_config, nlp_dims, i, "gnsf_nz1", &gnsf_nz1); + ocp_nlp_dims_set_dynamics(nlp_config, nlp_dims, i, "gnsf_nout", &gnsf_nout); + ocp_nlp_dims_set_dynamics(nlp_config, nlp_dims, i, "gnsf_ny", &gnsf_ny); + ocp_nlp_dims_set_dynamics(nlp_config, nlp_dims, i, "gnsf_nuhat", &gnsf_nuhat); + } + } +{%- endif %} + + /************************************************ + * external functions + ************************************************/ + {%- if constraints.constr_type == "BGP" %} + capsule->phi_constraint = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) + { + // nonlinear part of convex-composite constraint + capsule->phi_constraint[i].casadi_fun = &{{ model.name }}_phi_constraint; + capsule->phi_constraint[i].casadi_n_in = &{{ model.name }}_phi_constraint_n_in; + capsule->phi_constraint[i].casadi_n_out = &{{ model.name }}_phi_constraint_n_out; + capsule->phi_constraint[i].casadi_sparsity_in = &{{ model.name }}_phi_constraint_sparsity_in; + capsule->phi_constraint[i].casadi_sparsity_out = &{{ model.name }}_phi_constraint_sparsity_out; + capsule->phi_constraint[i].casadi_work = &{{ model.name }}_phi_constraint_work; + + external_function_param_casadi_create(&capsule->phi_constraint[i], {{ dims.np }}); + } + {%- endif %} + + {%- if constraints.constr_type_e == "BGP" %} + // nonlinear part of convex-composite constraint + capsule->phi_e_constraint.casadi_fun = &{{ model.name }}_phi_e_constraint; + capsule->phi_e_constraint.casadi_n_in = &{{ model.name }}_phi_e_constraint_n_in; + capsule->phi_e_constraint.casadi_n_out = &{{ model.name }}_phi_e_constraint_n_out; + capsule->phi_e_constraint.casadi_sparsity_in = &{{ model.name }}_phi_e_constraint_sparsity_in; + capsule->phi_e_constraint.casadi_sparsity_out = &{{ model.name }}_phi_e_constraint_sparsity_out; + capsule->phi_e_constraint.casadi_work = &{{ model.name }}_phi_e_constraint_work; + + external_function_param_casadi_create(&capsule->phi_e_constraint, {{ dims.np }}); + {% endif %} + + {%- if constraints.constr_type == "BGH" and dims.nh > 0 %} + capsule->nl_constr_h_fun_jac = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->nl_constr_h_fun_jac[i].casadi_fun = &{{ model.name }}_constr_h_fun_jac_uxt_zt; + capsule->nl_constr_h_fun_jac[i].casadi_n_in = &{{ model.name }}_constr_h_fun_jac_uxt_zt_n_in; + capsule->nl_constr_h_fun_jac[i].casadi_n_out = &{{ model.name }}_constr_h_fun_jac_uxt_zt_n_out; + capsule->nl_constr_h_fun_jac[i].casadi_sparsity_in = &{{ model.name }}_constr_h_fun_jac_uxt_zt_sparsity_in; + capsule->nl_constr_h_fun_jac[i].casadi_sparsity_out = &{{ model.name }}_constr_h_fun_jac_uxt_zt_sparsity_out; + capsule->nl_constr_h_fun_jac[i].casadi_work = &{{ model.name }}_constr_h_fun_jac_uxt_zt_work; + external_function_param_casadi_create(&capsule->nl_constr_h_fun_jac[i], {{ dims.np }}); + } + capsule->nl_constr_h_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->nl_constr_h_fun[i].casadi_fun = &{{ model.name }}_constr_h_fun; + capsule->nl_constr_h_fun[i].casadi_n_in = &{{ model.name }}_constr_h_fun_n_in; + capsule->nl_constr_h_fun[i].casadi_n_out = &{{ model.name }}_constr_h_fun_n_out; + capsule->nl_constr_h_fun[i].casadi_sparsity_in = &{{ model.name }}_constr_h_fun_sparsity_in; + capsule->nl_constr_h_fun[i].casadi_sparsity_out = &{{ model.name }}_constr_h_fun_sparsity_out; + capsule->nl_constr_h_fun[i].casadi_work = &{{ model.name }}_constr_h_fun_work; + external_function_param_casadi_create(&capsule->nl_constr_h_fun[i], {{ dims.np }}); + } + {% if solver_options.hessian_approx == "EXACT" %} + capsule->nl_constr_h_fun_jac_hess = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->nl_constr_h_fun_jac_hess[i].casadi_fun = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess; + capsule->nl_constr_h_fun_jac_hess[i].casadi_n_in = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_n_in; + capsule->nl_constr_h_fun_jac_hess[i].casadi_n_out = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_n_out; + capsule->nl_constr_h_fun_jac_hess[i].casadi_sparsity_in = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_sparsity_in; + capsule->nl_constr_h_fun_jac_hess[i].casadi_sparsity_out = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_sparsity_out; + capsule->nl_constr_h_fun_jac_hess[i].casadi_work = &{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_work; + + external_function_param_casadi_create(&capsule->nl_constr_h_fun_jac_hess[i], {{ dims.np }}); + } + {% endif %} + {% endif %} + + {%- if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + capsule->nl_constr_h_e_fun_jac.casadi_fun = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt; + capsule->nl_constr_h_e_fun_jac.casadi_n_in = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_n_in; + capsule->nl_constr_h_e_fun_jac.casadi_n_out = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_n_out; + capsule->nl_constr_h_e_fun_jac.casadi_sparsity_in = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_sparsity_in; + capsule->nl_constr_h_e_fun_jac.casadi_sparsity_out = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_sparsity_out; + capsule->nl_constr_h_e_fun_jac.casadi_work = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_work; + external_function_param_casadi_create(&capsule->nl_constr_h_e_fun_jac, {{ dims.np }}); + + capsule->nl_constr_h_e_fun.casadi_fun = &{{ model.name }}_constr_h_e_fun; + capsule->nl_constr_h_e_fun.casadi_n_in = &{{ model.name }}_constr_h_e_fun_n_in; + capsule->nl_constr_h_e_fun.casadi_n_out = &{{ model.name }}_constr_h_e_fun_n_out; + capsule->nl_constr_h_e_fun.casadi_sparsity_in = &{{ model.name }}_constr_h_e_fun_sparsity_in; + capsule->nl_constr_h_e_fun.casadi_sparsity_out = &{{ model.name }}_constr_h_e_fun_sparsity_out; + capsule->nl_constr_h_e_fun.casadi_work = &{{ model.name }}_constr_h_e_fun_work; + external_function_param_casadi_create(&capsule->nl_constr_h_e_fun, {{ dims.np }}); + + {% if solver_options.hessian_approx == "EXACT" %} + capsule->nl_constr_h_e_fun_jac_hess.casadi_fun = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess; + capsule->nl_constr_h_e_fun_jac_hess.casadi_n_in = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_n_in; + capsule->nl_constr_h_e_fun_jac_hess.casadi_n_out = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_n_out; + capsule->nl_constr_h_e_fun_jac_hess.casadi_sparsity_in = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_sparsity_in; + capsule->nl_constr_h_e_fun_jac_hess.casadi_sparsity_out = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_sparsity_out; + capsule->nl_constr_h_e_fun_jac_hess.casadi_work = &{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_work; + external_function_param_casadi_create(&capsule->nl_constr_h_e_fun_jac_hess, {{ dims.np }}); + {% endif %} + {%- endif %} + +{% if solver_options.integrator_type == "ERK" %} + // explicit ode + capsule->forw_vde_casadi = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->forw_vde_casadi[i].casadi_fun = &{{ model.name }}_expl_vde_forw; + capsule->forw_vde_casadi[i].casadi_n_in = &{{ model.name }}_expl_vde_forw_n_in; + capsule->forw_vde_casadi[i].casadi_n_out = &{{ model.name }}_expl_vde_forw_n_out; + capsule->forw_vde_casadi[i].casadi_sparsity_in = &{{ model.name }}_expl_vde_forw_sparsity_in; + capsule->forw_vde_casadi[i].casadi_sparsity_out = &{{ model.name }}_expl_vde_forw_sparsity_out; + capsule->forw_vde_casadi[i].casadi_work = &{{ model.name }}_expl_vde_forw_work; + external_function_param_casadi_create(&capsule->forw_vde_casadi[i], {{ dims.np }}); + } + + capsule->expl_ode_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->expl_ode_fun[i].casadi_fun = &{{ model.name }}_expl_ode_fun; + capsule->expl_ode_fun[i].casadi_n_in = &{{ model.name }}_expl_ode_fun_n_in; + capsule->expl_ode_fun[i].casadi_n_out = &{{ model.name }}_expl_ode_fun_n_out; + capsule->expl_ode_fun[i].casadi_sparsity_in = &{{ model.name }}_expl_ode_fun_sparsity_in; + capsule->expl_ode_fun[i].casadi_sparsity_out = &{{ model.name }}_expl_ode_fun_sparsity_out; + capsule->expl_ode_fun[i].casadi_work = &{{ model.name }}_expl_ode_fun_work; + external_function_param_casadi_create(&capsule->expl_ode_fun[i], {{ dims.np }}); + } + + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->hess_vde_casadi = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->hess_vde_casadi[i].casadi_fun = &{{ model.name }}_expl_ode_hess; + capsule->hess_vde_casadi[i].casadi_n_in = &{{ model.name }}_expl_ode_hess_n_in; + capsule->hess_vde_casadi[i].casadi_n_out = &{{ model.name }}_expl_ode_hess_n_out; + capsule->hess_vde_casadi[i].casadi_sparsity_in = &{{ model.name }}_expl_ode_hess_sparsity_in; + capsule->hess_vde_casadi[i].casadi_sparsity_out = &{{ model.name }}_expl_ode_hess_sparsity_out; + capsule->hess_vde_casadi[i].casadi_work = &{{ model.name }}_expl_ode_hess_work; + external_function_param_casadi_create(&capsule->hess_vde_casadi[i], {{ dims.np }}); + } + {%- endif %} + +{% elif solver_options.integrator_type == "IRK" %} + // implicit dae + capsule->impl_dae_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->impl_dae_fun[i].casadi_fun = &{{ model.name }}_impl_dae_fun; + capsule->impl_dae_fun[i].casadi_work = &{{ model.name }}_impl_dae_fun_work; + capsule->impl_dae_fun[i].casadi_sparsity_in = &{{ model.name }}_impl_dae_fun_sparsity_in; + capsule->impl_dae_fun[i].casadi_sparsity_out = &{{ model.name }}_impl_dae_fun_sparsity_out; + capsule->impl_dae_fun[i].casadi_n_in = &{{ model.name }}_impl_dae_fun_n_in; + capsule->impl_dae_fun[i].casadi_n_out = &{{ model.name }}_impl_dae_fun_n_out; + external_function_param_casadi_create(&capsule->impl_dae_fun[i], {{ dims.np }}); + } + + capsule->impl_dae_fun_jac_x_xdot_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_fun = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z; + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_work = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_work; + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_sparsity_in = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_in; + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_sparsity_out = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_out; + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_n_in = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_in; + capsule->impl_dae_fun_jac_x_xdot_z[i].casadi_n_out = &{{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_out; + external_function_param_casadi_create(&capsule->impl_dae_fun_jac_x_xdot_z[i], {{ dims.np }}); + } + + capsule->impl_dae_jac_x_xdot_u_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_fun = &{{ model.name }}_impl_dae_jac_x_xdot_u_z; + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_work = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_work; + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_sparsity_in = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_in; + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_sparsity_out = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_out; + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_n_in = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_n_in; + capsule->impl_dae_jac_x_xdot_u_z[i].casadi_n_out = &{{ model.name }}_impl_dae_jac_x_xdot_u_z_n_out; + external_function_param_casadi_create(&capsule->impl_dae_jac_x_xdot_u_z[i], {{ dims.np }}); + } + + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->impl_dae_hess = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->impl_dae_hess[i].casadi_fun = &{{ model.name }}_impl_dae_hess; + capsule->impl_dae_hess[i].casadi_work = &{{ model.name }}_impl_dae_hess_work; + capsule->impl_dae_hess[i].casadi_sparsity_in = &{{ model.name }}_impl_dae_hess_sparsity_in; + capsule->impl_dae_hess[i].casadi_sparsity_out = &{{ model.name }}_impl_dae_hess_sparsity_out; + capsule->impl_dae_hess[i].casadi_n_in = &{{ model.name }}_impl_dae_hess_n_in; + capsule->impl_dae_hess[i].casadi_n_out = &{{ model.name }}_impl_dae_hess_n_out; + external_function_param_casadi_create(&capsule->impl_dae_hess[i], {{ dims.np }}); + } + {%- endif %} + +{% elif solver_options.integrator_type == "GNSF" %} + capsule->gnsf_phi_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->gnsf_phi_fun[i].casadi_fun = &{{ model.name }}_gnsf_phi_fun; + capsule->gnsf_phi_fun[i].casadi_work = &{{ model.name }}_gnsf_phi_fun_work; + capsule->gnsf_phi_fun[i].casadi_sparsity_in = &{{ model.name }}_gnsf_phi_fun_sparsity_in; + capsule->gnsf_phi_fun[i].casadi_sparsity_out = &{{ model.name }}_gnsf_phi_fun_sparsity_out; + capsule->gnsf_phi_fun[i].casadi_n_in = &{{ model.name }}_gnsf_phi_fun_n_in; + capsule->gnsf_phi_fun[i].casadi_n_out = &{{ model.name }}_gnsf_phi_fun_n_out; + external_function_param_casadi_create(&capsule->gnsf_phi_fun[i], {{ dims.np }}); + } + + capsule->gnsf_phi_fun_jac_y = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->gnsf_phi_fun_jac_y[i].casadi_fun = &{{ model.name }}_gnsf_phi_fun_jac_y; + capsule->gnsf_phi_fun_jac_y[i].casadi_work = &{{ model.name }}_gnsf_phi_fun_jac_y_work; + capsule->gnsf_phi_fun_jac_y[i].casadi_sparsity_in = &{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_in; + capsule->gnsf_phi_fun_jac_y[i].casadi_sparsity_out = &{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_out; + capsule->gnsf_phi_fun_jac_y[i].casadi_n_in = &{{ model.name }}_gnsf_phi_fun_jac_y_n_in; + capsule->gnsf_phi_fun_jac_y[i].casadi_n_out = &{{ model.name }}_gnsf_phi_fun_jac_y_n_out; + external_function_param_casadi_create(&capsule->gnsf_phi_fun_jac_y[i], {{ dims.np }}); + } + + capsule->gnsf_phi_jac_y_uhat = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->gnsf_phi_jac_y_uhat[i].casadi_fun = &{{ model.name }}_gnsf_phi_jac_y_uhat; + capsule->gnsf_phi_jac_y_uhat[i].casadi_work = &{{ model.name }}_gnsf_phi_jac_y_uhat_work; + capsule->gnsf_phi_jac_y_uhat[i].casadi_sparsity_in = &{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_in; + capsule->gnsf_phi_jac_y_uhat[i].casadi_sparsity_out = &{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_out; + capsule->gnsf_phi_jac_y_uhat[i].casadi_n_in = &{{ model.name }}_gnsf_phi_jac_y_uhat_n_in; + capsule->gnsf_phi_jac_y_uhat[i].casadi_n_out = &{{ model.name }}_gnsf_phi_jac_y_uhat_n_out; + external_function_param_casadi_create(&capsule->gnsf_phi_jac_y_uhat[i], {{ dims.np }}); + } + + capsule->gnsf_f_lo_jac_x1_x1dot_u_z = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_fun = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz; + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_work = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_work; + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_sparsity_in = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_in; + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_sparsity_out = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_out; + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_n_in = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_in; + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i].casadi_n_out = &{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_out; + external_function_param_casadi_create(&capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i], {{ dims.np }}); + } + + capsule->gnsf_get_matrices_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N; i++) { + capsule->gnsf_get_matrices_fun[i].casadi_fun = &{{ model.name }}_gnsf_get_matrices_fun; + capsule->gnsf_get_matrices_fun[i].casadi_work = &{{ model.name }}_gnsf_get_matrices_fun_work; + capsule->gnsf_get_matrices_fun[i].casadi_sparsity_in = &{{ model.name }}_gnsf_get_matrices_fun_sparsity_in; + capsule->gnsf_get_matrices_fun[i].casadi_sparsity_out = &{{ model.name }}_gnsf_get_matrices_fun_sparsity_out; + capsule->gnsf_get_matrices_fun[i].casadi_n_in = &{{ model.name }}_gnsf_get_matrices_fun_n_in; + capsule->gnsf_get_matrices_fun[i].casadi_n_out = &{{ model.name }}_gnsf_get_matrices_fun_n_out; + external_function_param_casadi_create(&capsule->gnsf_get_matrices_fun[i], {{ dims.np }}); + } +{% elif solver_options.integrator_type == "DISCRETE" %} + // discrete dynamics + capsule->discr_dyn_phi_fun = (external_function_param_{{ model.dyn_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ model.dyn_ext_fun_type }})*N); + for (int i = 0; i < N; i++) + { + {%- if model.dyn_ext_fun_type == "casadi" %} + capsule->discr_dyn_phi_fun[i].casadi_fun = &{{ model.name }}_dyn_disc_phi_fun; + capsule->discr_dyn_phi_fun[i].casadi_n_in = &{{ model.name }}_dyn_disc_phi_fun_n_in; + capsule->discr_dyn_phi_fun[i].casadi_n_out = &{{ model.name }}_dyn_disc_phi_fun_n_out; + capsule->discr_dyn_phi_fun[i].casadi_sparsity_in = &{{ model.name }}_dyn_disc_phi_fun_sparsity_in; + capsule->discr_dyn_phi_fun[i].casadi_sparsity_out = &{{ model.name }}_dyn_disc_phi_fun_sparsity_out; + capsule->discr_dyn_phi_fun[i].casadi_work = &{{ model.name }}_dyn_disc_phi_fun_work; + {%- else %} + capsule->discr_dyn_phi_fun[i].fun = &{{ model.dyn_disc_fun }}; + {%- endif %} + external_function_param_{{ model.dyn_ext_fun_type }}_create(&capsule->discr_dyn_phi_fun[i], {{ dims.np }}); + } + + capsule->discr_dyn_phi_fun_jac_ut_xt = (external_function_param_{{ model.dyn_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ model.dyn_ext_fun_type }})*N); + for (int i = 0; i < N; i++) + { + {%- if model.dyn_ext_fun_type == "casadi" %} + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_fun = &{{ model.name }}_dyn_disc_phi_fun_jac; + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_n_in = &{{ model.name }}_dyn_disc_phi_fun_jac_n_in; + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_n_out = &{{ model.name }}_dyn_disc_phi_fun_jac_n_out; + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_sparsity_in = &{{ model.name }}_dyn_disc_phi_fun_jac_sparsity_in; + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_sparsity_out = &{{ model.name }}_dyn_disc_phi_fun_jac_sparsity_out; + capsule->discr_dyn_phi_fun_jac_ut_xt[i].casadi_work = &{{ model.name }}_dyn_disc_phi_fun_jac_work; + {%- else %} + capsule->discr_dyn_phi_fun_jac_ut_xt[i].fun = &{{ model.dyn_disc_fun_jac }}; + {%- endif %} + external_function_param_{{ model.dyn_ext_fun_type }}_create(&capsule->discr_dyn_phi_fun_jac_ut_xt[i], {{ dims.np }}); + } + + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->discr_dyn_phi_fun_jac_ut_xt_hess = (external_function_param_{{ model.dyn_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ model.dyn_ext_fun_type }})*N); + for (int i = 0; i < N; i++) + { + {%- if model.dyn_ext_fun_type == "casadi" %} + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_fun = &{{ model.name }}_dyn_disc_phi_fun_jac_hess; + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_n_in = &{{ model.name }}_dyn_disc_phi_fun_jac_hess_n_in; + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_n_out = &{{ model.name }}_dyn_disc_phi_fun_jac_hess_n_out; + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_sparsity_in = &{{ model.name }}_dyn_disc_phi_fun_jac_hess_sparsity_in; + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_sparsity_out = &{{ model.name }}_dyn_disc_phi_fun_jac_hess_sparsity_out; + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].casadi_work = &{{ model.name }}_dyn_disc_phi_fun_jac_hess_work; + {%- else %} + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i].fun = &{{ model.dyn_disc_fun_jac_hess }}; + {%- endif %} + external_function_param_{{ model.dyn_ext_fun_type }}_create(&capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i], {{ dims.np }}); + } + {%- endif %} +{%- endif %} + + +{%- if cost.cost_type_0 == "NONLINEAR_LS" %} + // nonlinear least square function + capsule->cost_y_0_fun.casadi_fun = &{{ model.name }}_cost_y_0_fun; + capsule->cost_y_0_fun.casadi_n_in = &{{ model.name }}_cost_y_0_fun_n_in; + capsule->cost_y_0_fun.casadi_n_out = &{{ model.name }}_cost_y_0_fun_n_out; + capsule->cost_y_0_fun.casadi_sparsity_in = &{{ model.name }}_cost_y_0_fun_sparsity_in; + capsule->cost_y_0_fun.casadi_sparsity_out = &{{ model.name }}_cost_y_0_fun_sparsity_out; + capsule->cost_y_0_fun.casadi_work = &{{ model.name }}_cost_y_0_fun_work; + external_function_param_casadi_create(&capsule->cost_y_0_fun, {{ dims.np }}); + + capsule->cost_y_0_fun_jac_ut_xt.casadi_fun = &{{ model.name }}_cost_y_0_fun_jac_ut_xt; + capsule->cost_y_0_fun_jac_ut_xt.casadi_n_in = &{{ model.name }}_cost_y_0_fun_jac_ut_xt_n_in; + capsule->cost_y_0_fun_jac_ut_xt.casadi_n_out = &{{ model.name }}_cost_y_0_fun_jac_ut_xt_n_out; + capsule->cost_y_0_fun_jac_ut_xt.casadi_sparsity_in = &{{ model.name }}_cost_y_0_fun_jac_ut_xt_sparsity_in; + capsule->cost_y_0_fun_jac_ut_xt.casadi_sparsity_out = &{{ model.name }}_cost_y_0_fun_jac_ut_xt_sparsity_out; + capsule->cost_y_0_fun_jac_ut_xt.casadi_work = &{{ model.name }}_cost_y_0_fun_jac_ut_xt_work; + external_function_param_casadi_create(&capsule->cost_y_0_fun_jac_ut_xt, {{ dims.np }}); + + capsule->cost_y_0_hess.casadi_fun = &{{ model.name }}_cost_y_0_hess; + capsule->cost_y_0_hess.casadi_n_in = &{{ model.name }}_cost_y_0_hess_n_in; + capsule->cost_y_0_hess.casadi_n_out = &{{ model.name }}_cost_y_0_hess_n_out; + capsule->cost_y_0_hess.casadi_sparsity_in = &{{ model.name }}_cost_y_0_hess_sparsity_in; + capsule->cost_y_0_hess.casadi_sparsity_out = &{{ model.name }}_cost_y_0_hess_sparsity_out; + capsule->cost_y_0_hess.casadi_work = &{{ model.name }}_cost_y_0_hess_work; + external_function_param_casadi_create(&capsule->cost_y_0_hess, {{ dims.np }}); + +{%- elif cost.cost_type_0 == "EXTERNAL" %} + // external cost + {% if cost.cost_ext_fun_type_0 == "casadi" %} + capsule->ext_cost_0_fun.casadi_fun = &{{ model.name }}_cost_ext_cost_0_fun; + capsule->ext_cost_0_fun.casadi_n_in = &{{ model.name }}_cost_ext_cost_0_fun_n_in; + capsule->ext_cost_0_fun.casadi_n_out = &{{ model.name }}_cost_ext_cost_0_fun_n_out; + capsule->ext_cost_0_fun.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_0_fun_sparsity_in; + capsule->ext_cost_0_fun.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_0_fun_sparsity_out; + capsule->ext_cost_0_fun.casadi_work = &{{ model.name }}_cost_ext_cost_0_fun_work; + {% else %} + capsule->ext_cost_0_fun.fun = &{{ cost.cost_function_ext_cost_0 }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_0 }}_create(&capsule->ext_cost_0_fun, {{ dims.np }}); + + // external cost + {% if cost.cost_ext_fun_type_0 == "casadi" %} + capsule->ext_cost_0_fun_jac.casadi_fun = &{{ model.name }}_cost_ext_cost_0_fun_jac; + capsule->ext_cost_0_fun_jac.casadi_n_in = &{{ model.name }}_cost_ext_cost_0_fun_jac_n_in; + capsule->ext_cost_0_fun_jac.casadi_n_out = &{{ model.name }}_cost_ext_cost_0_fun_jac_n_out; + capsule->ext_cost_0_fun_jac.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_0_fun_jac_sparsity_in; + capsule->ext_cost_0_fun_jac.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_0_fun_jac_sparsity_out; + capsule->ext_cost_0_fun_jac.casadi_work = &{{ model.name }}_cost_ext_cost_0_fun_jac_work; + {% else %} + capsule->ext_cost_0_fun_jac.fun = &{{ cost.cost_function_ext_cost_0 }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_0 }}_create(&capsule->ext_cost_0_fun_jac, {{ dims.np }}); + + // external cost + {% if cost.cost_ext_fun_type_0 == "casadi" %} + capsule->ext_cost_0_fun_jac_hess.casadi_fun = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess; + capsule->ext_cost_0_fun_jac_hess.casadi_n_in = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess_n_in; + capsule->ext_cost_0_fun_jac_hess.casadi_n_out = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess_n_out; + capsule->ext_cost_0_fun_jac_hess.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess_sparsity_in; + capsule->ext_cost_0_fun_jac_hess.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess_sparsity_out; + capsule->ext_cost_0_fun_jac_hess.casadi_work = &{{ model.name }}_cost_ext_cost_0_fun_jac_hess_work; + {% else %} + capsule->ext_cost_0_fun_jac_hess.fun = &{{ cost.cost_function_ext_cost_0 }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_0 }}_create(&capsule->ext_cost_0_fun_jac_hess, {{ dims.np }}); +{%- endif %} + +{%- if cost.cost_type == "NONLINEAR_LS" %} + // nonlinear least squares cost + capsule->cost_y_fun = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N-1; i++) + { + capsule->cost_y_fun[i].casadi_fun = &{{ model.name }}_cost_y_fun; + capsule->cost_y_fun[i].casadi_n_in = &{{ model.name }}_cost_y_fun_n_in; + capsule->cost_y_fun[i].casadi_n_out = &{{ model.name }}_cost_y_fun_n_out; + capsule->cost_y_fun[i].casadi_sparsity_in = &{{ model.name }}_cost_y_fun_sparsity_in; + capsule->cost_y_fun[i].casadi_sparsity_out = &{{ model.name }}_cost_y_fun_sparsity_out; + capsule->cost_y_fun[i].casadi_work = &{{ model.name }}_cost_y_fun_work; + + external_function_param_casadi_create(&capsule->cost_y_fun[i], {{ dims.np }}); + } + + capsule->cost_y_fun_jac_ut_xt = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N-1; i++) + { + capsule->cost_y_fun_jac_ut_xt[i].casadi_fun = &{{ model.name }}_cost_y_fun_jac_ut_xt; + capsule->cost_y_fun_jac_ut_xt[i].casadi_n_in = &{{ model.name }}_cost_y_fun_jac_ut_xt_n_in; + capsule->cost_y_fun_jac_ut_xt[i].casadi_n_out = &{{ model.name }}_cost_y_fun_jac_ut_xt_n_out; + capsule->cost_y_fun_jac_ut_xt[i].casadi_sparsity_in = &{{ model.name }}_cost_y_fun_jac_ut_xt_sparsity_in; + capsule->cost_y_fun_jac_ut_xt[i].casadi_sparsity_out = &{{ model.name }}_cost_y_fun_jac_ut_xt_sparsity_out; + capsule->cost_y_fun_jac_ut_xt[i].casadi_work = &{{ model.name }}_cost_y_fun_jac_ut_xt_work; + + external_function_param_casadi_create(&capsule->cost_y_fun_jac_ut_xt[i], {{ dims.np }}); + } + + capsule->cost_y_hess = (external_function_param_casadi *) malloc(sizeof(external_function_param_casadi)*N); + for (int i = 0; i < N-1; i++) + { + capsule->cost_y_hess[i].casadi_fun = &{{ model.name }}_cost_y_hess; + capsule->cost_y_hess[i].casadi_n_in = &{{ model.name }}_cost_y_hess_n_in; + capsule->cost_y_hess[i].casadi_n_out = &{{ model.name }}_cost_y_hess_n_out; + capsule->cost_y_hess[i].casadi_sparsity_in = &{{ model.name }}_cost_y_hess_sparsity_in; + capsule->cost_y_hess[i].casadi_sparsity_out = &{{ model.name }}_cost_y_hess_sparsity_out; + capsule->cost_y_hess[i].casadi_work = &{{ model.name }}_cost_y_hess_work; + + external_function_param_casadi_create(&capsule->cost_y_hess[i], {{ dims.np }}); + } +{%- elif cost.cost_type == "EXTERNAL" %} + // external cost + capsule->ext_cost_fun = (external_function_param_{{ cost.cost_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ cost.cost_ext_fun_type }})*N); + for (int i = 0; i < N-1; i++) + { + {% if cost.cost_ext_fun_type == "casadi" %} + capsule->ext_cost_fun[i].casadi_fun = &{{ model.name }}_cost_ext_cost_fun; + capsule->ext_cost_fun[i].casadi_n_in = &{{ model.name }}_cost_ext_cost_fun_n_in; + capsule->ext_cost_fun[i].casadi_n_out = &{{ model.name }}_cost_ext_cost_fun_n_out; + capsule->ext_cost_fun[i].casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_fun_sparsity_in; + capsule->ext_cost_fun[i].casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_fun_sparsity_out; + capsule->ext_cost_fun[i].casadi_work = &{{ model.name }}_cost_ext_cost_fun_work; + {% else %} + capsule->ext_cost_fun[i].fun = &{{ cost.cost_function_ext_cost }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type }}_create(&capsule->ext_cost_fun[i], {{ dims.np }}); + } + + capsule->ext_cost_fun_jac = (external_function_param_{{ cost.cost_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ cost.cost_ext_fun_type }})*N); + for (int i = 0; i < N-1; i++) + { + {% if cost.cost_ext_fun_type == "casadi" %} + capsule->ext_cost_fun_jac[i].casadi_fun = &{{ model.name }}_cost_ext_cost_fun_jac; + capsule->ext_cost_fun_jac[i].casadi_n_in = &{{ model.name }}_cost_ext_cost_fun_jac_n_in; + capsule->ext_cost_fun_jac[i].casadi_n_out = &{{ model.name }}_cost_ext_cost_fun_jac_n_out; + capsule->ext_cost_fun_jac[i].casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_fun_jac_sparsity_in; + capsule->ext_cost_fun_jac[i].casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_fun_jac_sparsity_out; + capsule->ext_cost_fun_jac[i].casadi_work = &{{ model.name }}_cost_ext_cost_fun_jac_work; + {% else %} + capsule->ext_cost_fun_jac[i].fun = &{{ cost.cost_function_ext_cost }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type }}_create(&capsule->ext_cost_fun_jac[i], {{ dims.np }}); + } + + capsule->ext_cost_fun_jac_hess = (external_function_param_{{ cost.cost_ext_fun_type }} *) malloc(sizeof(external_function_param_{{ cost.cost_ext_fun_type }})*N); + for (int i = 0; i < N-1; i++) + { + {% if cost.cost_ext_fun_type == "casadi" %} + capsule->ext_cost_fun_jac_hess[i].casadi_fun = &{{ model.name }}_cost_ext_cost_fun_jac_hess; + capsule->ext_cost_fun_jac_hess[i].casadi_n_in = &{{ model.name }}_cost_ext_cost_fun_jac_hess_n_in; + capsule->ext_cost_fun_jac_hess[i].casadi_n_out = &{{ model.name }}_cost_ext_cost_fun_jac_hess_n_out; + capsule->ext_cost_fun_jac_hess[i].casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_fun_jac_hess_sparsity_in; + capsule->ext_cost_fun_jac_hess[i].casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_fun_jac_hess_sparsity_out; + capsule->ext_cost_fun_jac_hess[i].casadi_work = &{{ model.name }}_cost_ext_cost_fun_jac_hess_work; + {% else %} + capsule->ext_cost_fun_jac_hess[i].fun = &{{ cost.cost_function_ext_cost }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type }}_create(&capsule->ext_cost_fun_jac_hess[i], {{ dims.np }}); + } +{%- endif %} + +{%- if cost.cost_type_e == "NONLINEAR_LS" %} + // nonlinear least square function + capsule->cost_y_e_fun.casadi_fun = &{{ model.name }}_cost_y_e_fun; + capsule->cost_y_e_fun.casadi_n_in = &{{ model.name }}_cost_y_e_fun_n_in; + capsule->cost_y_e_fun.casadi_n_out = &{{ model.name }}_cost_y_e_fun_n_out; + capsule->cost_y_e_fun.casadi_sparsity_in = &{{ model.name }}_cost_y_e_fun_sparsity_in; + capsule->cost_y_e_fun.casadi_sparsity_out = &{{ model.name }}_cost_y_e_fun_sparsity_out; + capsule->cost_y_e_fun.casadi_work = &{{ model.name }}_cost_y_e_fun_work; + external_function_param_casadi_create(&capsule->cost_y_e_fun, {{ dims.np }}); + + capsule->cost_y_e_fun_jac_ut_xt.casadi_fun = &{{ model.name }}_cost_y_e_fun_jac_ut_xt; + capsule->cost_y_e_fun_jac_ut_xt.casadi_n_in = &{{ model.name }}_cost_y_e_fun_jac_ut_xt_n_in; + capsule->cost_y_e_fun_jac_ut_xt.casadi_n_out = &{{ model.name }}_cost_y_e_fun_jac_ut_xt_n_out; + capsule->cost_y_e_fun_jac_ut_xt.casadi_sparsity_in = &{{ model.name }}_cost_y_e_fun_jac_ut_xt_sparsity_in; + capsule->cost_y_e_fun_jac_ut_xt.casadi_sparsity_out = &{{ model.name }}_cost_y_e_fun_jac_ut_xt_sparsity_out; + capsule->cost_y_e_fun_jac_ut_xt.casadi_work = &{{ model.name }}_cost_y_e_fun_jac_ut_xt_work; + external_function_param_casadi_create(&capsule->cost_y_e_fun_jac_ut_xt, {{ dims.np }}); + + capsule->cost_y_e_hess.casadi_fun = &{{ model.name }}_cost_y_e_hess; + capsule->cost_y_e_hess.casadi_n_in = &{{ model.name }}_cost_y_e_hess_n_in; + capsule->cost_y_e_hess.casadi_n_out = &{{ model.name }}_cost_y_e_hess_n_out; + capsule->cost_y_e_hess.casadi_sparsity_in = &{{ model.name }}_cost_y_e_hess_sparsity_in; + capsule->cost_y_e_hess.casadi_sparsity_out = &{{ model.name }}_cost_y_e_hess_sparsity_out; + capsule->cost_y_e_hess.casadi_work = &{{ model.name }}_cost_y_e_hess_work; + external_function_param_casadi_create(&capsule->cost_y_e_hess, {{ dims.np }}); + +{%- elif cost.cost_type_e == "EXTERNAL" %} + // external cost + {% if cost.cost_ext_fun_type_e == "casadi" %} + capsule->ext_cost_e_fun.casadi_fun = &{{ model.name }}_cost_ext_cost_e_fun; + capsule->ext_cost_e_fun.casadi_n_in = &{{ model.name }}_cost_ext_cost_e_fun_n_in; + capsule->ext_cost_e_fun.casadi_n_out = &{{ model.name }}_cost_ext_cost_e_fun_n_out; + capsule->ext_cost_e_fun.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_e_fun_sparsity_in; + capsule->ext_cost_e_fun.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_e_fun_sparsity_out; + capsule->ext_cost_e_fun.casadi_work = &{{ model.name }}_cost_ext_cost_e_fun_work; + {% else %} + capsule->ext_cost_e_fun.fun = &{{ cost.cost_function_ext_cost_e }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_e }}_create(&capsule->ext_cost_e_fun, {{ dims.np }}); + + // external cost + {% if cost.cost_ext_fun_type_e == "casadi" %} + capsule->ext_cost_e_fun_jac.casadi_fun = &{{ model.name }}_cost_ext_cost_e_fun_jac; + capsule->ext_cost_e_fun_jac.casadi_n_in = &{{ model.name }}_cost_ext_cost_e_fun_jac_n_in; + capsule->ext_cost_e_fun_jac.casadi_n_out = &{{ model.name }}_cost_ext_cost_e_fun_jac_n_out; + capsule->ext_cost_e_fun_jac.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_e_fun_jac_sparsity_in; + capsule->ext_cost_e_fun_jac.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_e_fun_jac_sparsity_out; + capsule->ext_cost_e_fun_jac.casadi_work = &{{ model.name }}_cost_ext_cost_e_fun_jac_work; + {% else %} + capsule->ext_cost_e_fun_jac.fun = &{{ cost.cost_function_ext_cost_e }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_e }}_create(&capsule->ext_cost_e_fun_jac, {{ dims.np }}); + + // external cost + {% if cost.cost_ext_fun_type_e == "casadi" %} + capsule->ext_cost_e_fun_jac_hess.casadi_fun = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess; + capsule->ext_cost_e_fun_jac_hess.casadi_n_in = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess_n_in; + capsule->ext_cost_e_fun_jac_hess.casadi_n_out = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess_n_out; + capsule->ext_cost_e_fun_jac_hess.casadi_sparsity_in = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess_sparsity_in; + capsule->ext_cost_e_fun_jac_hess.casadi_sparsity_out = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess_sparsity_out; + capsule->ext_cost_e_fun_jac_hess.casadi_work = &{{ model.name }}_cost_ext_cost_e_fun_jac_hess_work; + {% else %} + capsule->ext_cost_e_fun_jac_hess.fun = &{{ cost.cost_function_ext_cost_e }}; + {% endif %} + external_function_param_{{ cost.cost_ext_fun_type_e }}_create(&capsule->ext_cost_e_fun_jac_hess, {{ dims.np }}); +{%- endif %} + + /************************************************ + * nlp_in + ************************************************/ + ocp_nlp_in * nlp_in = ocp_nlp_in_create(nlp_config, nlp_dims); + capsule->nlp_in = nlp_in; + + double time_steps[N]; + {%- for j in range(end=dims.N) %} + time_steps[{{ j }}] = {{ solver_options.time_steps[j] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_in_set(nlp_config, nlp_dims, nlp_in, i, "Ts", &time_steps[i]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "scaling", &time_steps[i]); + } + + /**** Dynamics ****/ + for (int i = 0; i < N; i++) + { + {%- if solver_options.integrator_type == "ERK" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "expl_vde_forw", &capsule->forw_vde_casadi[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "expl_ode_fun", &capsule->expl_ode_fun[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "expl_ode_hess", &capsule->hess_vde_casadi[i]); + {%- endif %} + {% elif solver_options.integrator_type == "IRK" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "impl_dae_fun", &capsule->impl_dae_fun[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, + "impl_dae_fun_jac_x_xdot_z", &capsule->impl_dae_fun_jac_x_xdot_z[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, + "impl_dae_jac_x_xdot_u", &capsule->impl_dae_jac_x_xdot_u_z[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "impl_dae_hess", &capsule->impl_dae_hess[i]); + {%- endif %} + {% elif solver_options.integrator_type == "GNSF" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "phi_fun", &capsule->gnsf_phi_fun[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "phi_fun_jac_y", &capsule->gnsf_phi_fun_jac_y[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "phi_jac_y_uhat", &capsule->gnsf_phi_jac_y_uhat[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "f_lo_jac_x1_x1dot_u_z", + &capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "gnsf_get_matrices_fun", + &capsule->gnsf_get_matrices_fun[i]); + {% elif solver_options.integrator_type == "DISCRETE" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "disc_dyn_fun", &capsule->discr_dyn_phi_fun[i]); + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "disc_dyn_fun_jac", + &capsule->discr_dyn_phi_fun_jac_ut_xt[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + ocp_nlp_dynamics_model_set(nlp_config, nlp_dims, nlp_in, i, "disc_dyn_fun_jac_hess", + &capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i]); + {%- endif %} + {%- endif %} + } + + + /**** Cost ****/ +{%- if cost.cost_type_0 == "NONLINEAR_LS" or cost.cost_type_0 == "LINEAR_LS" %} +{% if dims.ny_0 > 0 %} + double W_0[NY0*NY0]; + {% for j in range(end=dims.ny_0) %} + {%- for k in range(end=dims.ny_0) %} + W_0[{{ j }}+(NY0) * {{ k }}] = {{ cost.W_0[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "W", W_0); + + double yref_0[NY0]; + {% for j in range(end=dims.ny_0) %} + yref_0[{{ j }}] = {{ cost.yref_0[j] }}; + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "yref", yref_0); +{% endif %} +{% endif %} + +{%- if cost.cost_type == "NONLINEAR_LS" or cost.cost_type == "LINEAR_LS" %} +{% if dims.ny > 0 %} + double W[NY*NY]; + {% for j in range(end=dims.ny) %} + {%- for k in range(end=dims.ny) %} + W[{{ j }}+(NY) * {{ k }}] = {{ cost.W[j][k] }}; + {%- endfor %} + {%- endfor %} + + double yref[NY]; + {% for j in range(end=dims.ny) %} + yref[{{ j }}] = {{ cost.yref[j] }}; + {%- endfor %} + + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "W", W); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "yref", yref); + } +{% endif %} +{% endif %} + +{%- if cost.cost_type_0 == "LINEAR_LS" %} + double Vx_0[NY0*NX]; + {% for j in range(end=dims.ny_0) %} + {%- for k in range(end=dims.nx) %} + Vx_0[{{ j }}+(NY0) * {{ k }}] = {{ cost.Vx_0[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "Vx", Vx_0); + +{% if dims.ny_0 > 0 and dims.nu > 0 %} + double Vu_0[NY0*NU]; + {% for j in range(end=dims.ny_0) %} + {%- for k in range(end=dims.nu) %} + Vu_0[{{ j }}+(NY0) * {{ k }}] = {{ cost.Vu_0[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "Vu", Vu_0); +{% endif %} +{% if dims.ny_0 > 0 and dims.nz > 0 %} + double Vz_0[NY0*NZ]; + {% for j in range(end=dims.ny_0) %} + {%- for k in range(end=dims.nz) %} + Vz_0[{{ j }}+(NY0) * {{ k }}] = {{ cost.Vz_0[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "Vz", Vz_0); +{%- endif %} +{%- endif %}{# LINEAR LS #} + + +{%- if cost.cost_type == "LINEAR_LS" %} + double Vx[NY*NX]; + {% for j in range(end=dims.ny) %} + {%- for k in range(end=dims.nx) %} + Vx[{{ j }}+(NY) * {{ k }}] = {{ cost.Vx[j][k] }}; + {%- endfor %} + {%- endfor %} + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "Vx", Vx); + } + +{% if dims.ny > 0 and dims.nu > 0 %} + double Vu[NY*NU]; + {% for j in range(end=dims.ny) %} + {%- for k in range(end=dims.nu) %} + Vu[{{ j }}+(NY) * {{ k }}] = {{ cost.Vu[j][k] }}; + {%- endfor %} + {%- endfor %} + + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "Vu", Vu); + } +{% endif %} + +{% if dims.ny > 0 and dims.nz > 0 %} + double Vz[NY*NZ]; + {% for j in range(end=dims.ny) %} + {%- for k in range(end=dims.nz) %} + Vz[{{ j }}+(NY) * {{ k }}] = {{ cost.Vz[j][k] }}; + {%- endfor %} + {%- endfor %} + + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "Vz", Vz); + } +{%- endif %} +{%- endif %}{# LINEAR LS #} + + +{%- if cost.cost_type_0 == "NONLINEAR_LS" %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "nls_y_fun", &capsule->cost_y_0_fun); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "nls_y_fun_jac", &capsule->cost_y_0_fun_jac_ut_xt); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "nls_y_hess", &capsule->cost_y_0_hess); +{%- elif cost.cost_type_0 == "EXTERNAL" %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "ext_cost_fun", &capsule->ext_cost_0_fun); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "ext_cost_fun_jac", &capsule->ext_cost_0_fun_jac); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "ext_cost_fun_jac_hess", &capsule->ext_cost_0_fun_jac_hess); +{%- endif %} + +{%- if cost.cost_type == "NONLINEAR_LS" %} + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "nls_y_fun", &capsule->cost_y_fun[i-1]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "nls_y_fun_jac", &capsule->cost_y_fun_jac_ut_xt[i-1]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "nls_y_hess", &capsule->cost_y_hess[i-1]); + } +{%- elif cost.cost_type == "EXTERNAL" %} + for (int i = 1; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "ext_cost_fun", &capsule->ext_cost_fun[i-1]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "ext_cost_fun_jac", &capsule->ext_cost_fun_jac[i-1]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "ext_cost_fun_jac_hess", &capsule->ext_cost_fun_jac_hess[i-1]); + } +{%- endif %} + + +{% if dims.ns > 0 %} + double Zl[NS]; + double Zu[NS]; + double zl[NS]; + double zu[NS]; + {% for j in range(end=dims.ns) %} + Zl[{{ j }}] = {{ cost.Zl[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns) %} + Zu[{{ j }}] = {{ cost.Zu[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns) %} + zl[{{ j }}] = {{ cost.zl[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns) %} + zu[{{ j }}] = {{ cost.zu[j] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "Zl", Zl); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "Zu", Zu); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "zl", zl); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, i, "zu", zu); + } +{% endif %} + + // terminal cost +{% if cost.cost_type_e == "LINEAR_LS" or cost.cost_type_e == "NONLINEAR_LS" %} +{% if dims.ny_e > 0 %} + double yref_e[NYN]; + {% for j in range(end=dims.ny_e) %} + yref_e[{{ j }}] = {{ cost.yref_e[j] }}; + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "yref", yref_e); + + double W_e[NYN*NYN]; + {% for j in range(end=dims.ny_e) %} + {%- for k in range(end=dims.ny_e) %} + W_e[{{ j }}+(NYN) * {{ k }}] = {{ cost.W_e[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "W", W_e); + + {%- if cost.cost_type_e == "LINEAR_LS" %} + double Vx_e[NYN*NX]; + {% for j in range(end=dims.ny_e) %} + {%- for k in range(end=dims.nx) %} + Vx_e[{{ j }}+(NYN) * {{ k }}] = {{ cost.Vx_e[j][k] }}; + {%- endfor %} + {%- endfor %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "Vx", Vx_e); + {%- endif %} + + {%- if cost.cost_type_e == "NONLINEAR_LS" %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "nls_y_fun", &capsule->cost_y_e_fun); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "nls_y_fun_jac", &capsule->cost_y_e_fun_jac_ut_xt); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "nls_y_hess", &capsule->cost_y_e_hess); + {%- endif %} +{%- endif %}{# ny_e > 0 #} + +{%- elif cost.cost_type_e == "EXTERNAL" %} + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "ext_cost_fun", &capsule->ext_cost_e_fun); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "ext_cost_fun_jac", &capsule->ext_cost_e_fun_jac); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "ext_cost_fun_jac_hess", &capsule->ext_cost_e_fun_jac_hess); +{%- endif %} + +{% if dims.ns_e > 0 %} + double Zl_e[NSN]; + double Zu_e[NSN]; + double zl_e[NSN]; + double zu_e[NSN]; + + {% for j in range(end=dims.ns_e) %} + Zl_e[{{ j }}] = {{ cost.Zl_e[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns_e) %} + Zu_e[{{ j }}] = {{ cost.Zu_e[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns_e) %} + zl_e[{{ j }}] = {{ cost.zl_e[j] }}; + {%- endfor %} + + {% for j in range(end=dims.ns_e) %} + zu_e[{{ j }}] = {{ cost.zu_e[j] }}; + {%- endfor %} + + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "Zl", Zl_e); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "Zu", Zu_e); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "zl", zl_e); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, N, "zu", zu_e); +{%- endif %} + + /**** Constraints ****/ + + // bounds for initial stage +{% if dims.nbx_0 > 0 %} + // x0 + int idxbx0[{{ dims.nbx_0 }}]; + {% for i in range(end=dims.nbx_0) %} + idxbx0[{{ i }}] = {{ constraints.idxbx_0[i] }}; + {%- endfor %} + + double lbx0[{{ dims.nbx_0 }}]; + double ubx0[{{ dims.nbx_0 }}]; + {% for i in range(end=dims.nbx_0) %} + lbx0[{{ i }}] = {{ constraints.lbx_0[i] }}; + ubx0[{{ i }}] = {{ constraints.ubx_0[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "idxbx", idxbx0); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "lbx", lbx0); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "ubx", ubx0); +{% endif %} +{% if dims.nbxe_0 > 0 %} + // idxbxe_0 + int idxbxe_0[{{ dims.nbxe_0 }}]; + {% for i in range(end=dims.nbxe_0) %} + idxbxe_0[{{ i }}] = {{ constraints.idxbxe_0[i] }}; + {%- endfor %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "idxbxe", idxbxe_0); +{% endif %} + + /* constraints that are the same for initial and intermediate */ +{%- if dims.nsbx > 0 %} +{# TODO: introduce nsbx0 & move this block down!! #} + // ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "idxsbx", idxsbx); + // ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "lsbx", lsbx); + // ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "usbx", usbx); + + // soft bounds on x + int idxsbx[NSBX]; + {% for i in range(end=dims.nsbx) %} + idxsbx[{{ i }}] = {{ constraints.idxsbx[i] }}; + {%- endfor %} + double lsbx[NSBX]; + double usbx[NSBX]; + {% for i in range(end=dims.nsbx) %} + lsbx[{{ i }}] = {{ constraints.lsbx[i] }}; + usbx[{{ i }}] = {{ constraints.usbx[i] }}; + {%- endfor %} + + for (int i = 1; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxsbx", idxsbx); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lsbx", lsbx); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "usbx", usbx); + } +{%- endif %} + + +{% if dims.nbu > 0 %} + // u + int idxbu[NBU]; + {% for i in range(end=dims.nbu) %} + idxbu[{{ i }}] = {{ constraints.idxbu[i] }}; + {%- endfor %} + double lbu[NBU]; + double ubu[NBU]; + {% for i in range(end=dims.nbu) %} + lbu[{{ i }}] = {{ constraints.lbu[i] }}; + ubu[{{ i }}] = {{ constraints.ubu[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxbu", idxbu); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lbu", lbu); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "ubu", ubu); + } +{% endif %} + +{% if dims.nsbu > 0 %} + // set up soft bounds for u + int idxsbu[NSBU]; + {% for i in range(end=dims.nsbu) %} + idxsbu[{{ i }}] = {{ constraints.idxsbu[i] }}; + {%- endfor %} + double lsbu[NSBU]; + double usbu[NSBU]; + {% for i in range(end=dims.nsbu) %} + lsbu[{{ i }}] = {{ constraints.lsbu[i] }}; + usbu[{{ i }}] = {{ constraints.usbu[i] }}; + {%- endfor %} + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxsbu", idxsbu); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lsbu", lsbu); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "usbu", usbu); + } +{% endif %} + +{% if dims.nsg > 0 %} + // set up soft bounds for general linear constraints + int idxsg[NSG]; + {% for i in range(end=dims.nsg) %} + idxsg[{{ i }}] = {{ constraints.idxsg[i] }}; + {%- endfor %} + double lsg[NSG]; + double usg[NSG]; + {% for i in range(end=dims.nsg) %} + lsg[{{ i }}] = {{ constraints.lsg[i] }}; + usg[{{ i }}] = {{ constraints.usg[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxsg", idxsg); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lsg", lsg); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "usg", usg); + } +{% endif %} + +{% if dims.nsh > 0 %} + // set up soft bounds for nonlinear constraints + int idxsh[NSH]; + {% for i in range(end=dims.nsh) %} + idxsh[{{ i }}] = {{ constraints.idxsh[i] }}; + {%- endfor %} + double lsh[NSH]; + double ush[NSH]; + {% for i in range(end=dims.nsh) %} + lsh[{{ i }}] = {{ constraints.lsh[i] }}; + ush[{{ i }}] = {{ constraints.ush[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxsh", idxsh); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lsh", lsh); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "ush", ush); + } +{% endif %} + +{% if dims.nsphi > 0 %} + // set up soft bounds for convex-over-nonlinear constraints + int idxsphi[NSPHI]; + {% for i in range(end=dims.nsphi) %} + idxsphi[{{ i }}] = {{ constraints.idxsphi[i] }}; + {%- endfor %} + double lsphi[NSPHI]; + double usphi[NSPHI]; + {% for i in range(end=dims.nsphi) %} + lsphi[{{ i }}] = {{ constraints.lsphi[i] }}; + usphi[{{ i }}] = {{ constraints.usphi[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxsphi", idxsphi); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lsphi", lsphi); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "usphi", usphi); + } +{% endif %} + +{% if dims.nbx > 0 %} + // x + int idxbx[NBX]; + {% for i in range(end=dims.nbx) %} + idxbx[{{ i }}] = {{ constraints.idxbx[i] }}; + {%- endfor %} + double lbx[NBX]; + double ubx[NBX]; + {% for i in range(end=dims.nbx) %} + lbx[{{ i }}] = {{ constraints.lbx[i] }}; + ubx[{{ i }}] = {{ constraints.ubx[i] }}; + {%- endfor %} + + for (int i = 1; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "idxbx", idxbx); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lbx", lbx); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "ubx", ubx); + } +{% endif %} + +{% if dims.ng > 0 %} + // set up general constraints for stage 0 to N-1 + double D[NG*NU]; + double C[NG*NX]; + double lg[NG]; + double ug[NG]; + + {% for j in range(end=dims.ng) %} + {%- for k in range(end=dims.nu) %} + D[{{ j }}+NG * {{ k }}] = {{ constraints.D[j][k] }}; + {%- endfor %} + {%- endfor %} + + {% for j in range(end=dims.ng) %} + {%- for k in range(end=dims.nx) %} + C[{{ j }}+NG * {{ k }}] = {{ constraints.C[j][k] }}; + {%- endfor %} + {%- endfor %} + + {% for i in range(end=dims.ng) %} + lg[{{ i }}] = {{ constraints.lg[i] }}; + {%- endfor %} + + {% for i in range(end=dims.ng) %} + ug[{{ i }}] = {{ constraints.ug[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "D", D); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "C", C); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lg", lg); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "ug", ug); + } +{% endif %} + +{% if dims.nh > 0 %} + // set up nonlinear constraints for stage 0 to N-1 + double lh[NH]; + double uh[NH]; + + {% for i in range(end=dims.nh) %} + lh[{{ i }}] = {{ constraints.lh[i] }}; + {%- endfor %} + + {% for i in range(end=dims.nh) %} + uh[{{ i }}] = {{ constraints.uh[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + // nonlinear constraints for stages 0 to N-1 + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "nl_constr_h_fun_jac", + &capsule->nl_constr_h_fun_jac[i]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "nl_constr_h_fun", + &capsule->nl_constr_h_fun[i]); + {% if solver_options.hessian_approx == "EXACT" %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, + "nl_constr_h_fun_jac_hess", &capsule->nl_constr_h_fun_jac_hess[i]); + {% endif %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lh", lh); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "uh", uh); + } +{% endif %} + +{% if dims.nphi > 0 and constraints.constr_type == "BGP" %} + // set up convex-over-nonlinear constraints for stage 0 to N-1 + double lphi[NPHI]; + double uphi[NPHI]; + + {% for i in range(end=dims.nphi) %} + lphi[{{ i }}] = {{ constraints.lphi[i] }}; + {%- endfor %} + + {% for i in range(end=dims.nphi) %} + uphi[{{ i }}] = {{ constraints.uphi[i] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, + "nl_constr_phi_o_r_fun_phi_jac_ux_z_phi_hess_r_jac_ux", &capsule->phi_constraint[i]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "lphi", lphi); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, i, "uphi", uphi); + } +{% endif %} + + /* terminal constraints */ +{% if dims.nbx_e > 0 %} + // set up bounds for last stage + // x + int idxbx_e[NBXN]; + {% for i in range(end=dims.nbx_e) %} + idxbx_e[{{ i }}] = {{ constraints.idxbx_e[i] }}; + {%- endfor %} + double lbx_e[NBXN]; + double ubx_e[NBXN]; + {% for i in range(end=dims.nbx_e) %} + lbx_e[{{ i }}] = {{ constraints.lbx_e[i] }}; + ubx_e[{{ i }}] = {{ constraints.ubx_e[i] }}; + {%- endfor %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "idxbx", idxbx_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lbx", lbx_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "ubx", ubx_e); +{%- endif %} + +{% if dims.nsg_e > 0 %} + // set up soft bounds for general linear constraints + int idxsg_e[NSGN]; + {% for i in range(end=dims.nsg_e) %} + idxsg_e[{{ i }}] = {{ constraints.idxsg_e[i] }}; + {%- endfor %} + double lsg_e[NSGN]; + double usg_e[NSGN]; + {% for i in range(end=dims.nsg_e) %} + lsg_e[{{ i }}] = {{ constraints.lsg_e[i] }}; + usg_e[{{ i }}] = {{ constraints.usg_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "idxsg", idxsg_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lsg", lsg_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "usg", usg_e); +{%- endif %} + +{% if dims.nsh_e > 0 %} + // set up soft bounds for nonlinear constraints + int idxsh_e[NSHN]; + {% for i in range(end=dims.nsh_e) %} + idxsh_e[{{ i }}] = {{ constraints.idxsh_e[i] }}; + {%- endfor %} + double lsh_e[NSHN]; + double ush_e[NSHN]; + {% for i in range(end=dims.nsh_e) %} + lsh_e[{{ i }}] = {{ constraints.lsh_e[i] }}; + ush_e[{{ i }}] = {{ constraints.ush_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "idxsh", idxsh_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lsh", lsh_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "ush", ush_e); +{%- endif %} + +{% if dims.nsphi_e > 0 %} + // set up soft bounds for convex-over-nonlinear constraints + int idxsphi_e[NSPHIN]; + {% for i in range(end=dims.nsphi_e) %} + idxsphi_e[{{ i }}] = {{ constraints.idxsphi_e[i] }}; + {%- endfor %} + double lsphi_e[NSPHIN]; + double usphi_e[NSPHIN]; + {% for i in range(end=dims.nsphi_e) %} + lsphi_e[{{ i }}] = {{ constraints.lsphi_e[i] }}; + usphi_e[{{ i }}] = {{ constraints.usphi_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "idxsphi", idxsphi_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lsphi", lsphi_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "usphi", usphi_e); +{%- endif %} + +{% if dims.nsbx_e > 0 %} + // soft bounds on x + int idxsbx_e[NSBXN]; + {% for i in range(end=dims.nsbx_e) %} + idxsbx_e[{{ i }}] = {{ constraints.idxsbx_e[i] }}; + {%- endfor %} + double lsbx_e[NSBXN]; + double usbx_e[NSBXN]; + {% for i in range(end=dims.nsbx_e) %} + lsbx_e[{{ i }}] = {{ constraints.lsbx_e[i] }}; + usbx_e[{{ i }}] = {{ constraints.usbx_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "idxsbx", idxsbx_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lsbx", lsbx_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "usbx", usbx_e); +{% endif %} + +{% if dims.ng_e > 0 %} + // set up general constraints for last stage + double C_e[NGN*NX]; + double lg_e[NGN]; + double ug_e[NGN]; + + {% for j in range(end=dims.ng) %} + {%- for k in range(end=dims.nx) %} + C_e[{{ j }}+NG * {{ k }}] = {{ constraints.C_e[j][k] }}; + {%- endfor %} + {%- endfor %} + + {% for i in range(end=dims.ng_e) %} + lg_e[{{ i }}] = {{ constraints.lg_e[i] }}; + ug_e[{{ i }}] = {{ constraints.ug_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "C", C_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lg", lg_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "ug", ug_e); +{%- endif %} + +{% if dims.nh_e > 0 %} + // set up nonlinear constraints for last stage + double lh_e[NHN]; + double uh_e[NHN]; + + {% for i in range(end=dims.nh_e) %} + lh_e[{{ i }}] = {{ constraints.lh_e[i] }}; + {%- endfor %} + + {% for i in range(end=dims.nh_e) %} + uh_e[{{ i }}] = {{ constraints.uh_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "nl_constr_h_fun_jac", &capsule->nl_constr_h_e_fun_jac); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "nl_constr_h_fun", &capsule->nl_constr_h_e_fun); + {% if solver_options.hessian_approx == "EXACT" %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "nl_constr_h_fun_jac_hess", + &capsule->nl_constr_h_e_fun_jac_hess); + {% endif %} + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lh", lh_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "uh", uh_e); +{%- endif %} + +{% if dims.nphi_e > 0 and constraints.constr_type_e == "BGP" %} + // set up convex-over-nonlinear constraints for last stage + double lphi_e[NPHIN]; + double uphi_e[NPHIN]; + + {% for i in range(end=dims.nphi_e) %} + lphi_e[{{ i }}] = {{ constraints.lphi_e[i] }}; + {%- endfor %} + + {% for i in range(end=dims.nphi_e) %} + uphi_e[{{ i }}] = {{ constraints.uphi_e[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "lphi", lphi_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, "uphi", uphi_e); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, N, + "nl_constr_phi_o_r_fun_phi_jac_ux_z_phi_hess_r_jac_ux", &capsule->phi_e_constraint); +{% endif %} + + + /************************************************ + * opts + ************************************************/ + + capsule->nlp_opts = ocp_nlp_solver_opts_create(nlp_config, nlp_dims); + +{% if solver_options.hessian_approx == "EXACT" %} + bool nlp_solver_exact_hessian = true; + // TODO: this if should not be needed! however, calling the setter with false leads to weird behavior. Investigate! + if (nlp_solver_exact_hessian) + { + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "exact_hess", &nlp_solver_exact_hessian); + } + int exact_hess_dyn = {{ solver_options.exact_hess_dyn }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "exact_hess_dyn", &exact_hess_dyn); + + int exact_hess_cost = {{ solver_options.exact_hess_cost }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "exact_hess_cost", &exact_hess_cost); + + int exact_hess_constr = {{ solver_options.exact_hess_constr }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "exact_hess_constr", &exact_hess_constr); +{%- endif -%} + +{%- if solver_options.globalization == "FIXED_STEP" %} + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "globalization", "fixed_step"); +{%- elif solver_options.globalization == "MERIT_BACKTRACKING" %} + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "globalization", "merit_backtracking"); + + double alpha_min = {{ solver_options.alpha_min }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "alpha_min", &alpha_min); + + double alpha_reduction = {{ solver_options.alpha_reduction }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "alpha_reduction", &alpha_reduction); +{%- endif -%} + +{%- if dims.nz > 0 %} + // TODO: these options are lower level -> should be encapsulated! maybe through hessian approx option. + bool output_z_val = true; + bool sens_algebraic_val = true; + + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_output_z", &output_z_val); + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_sens_algebraic", &sens_algebraic_val); +{%- endif %} + +{%- if solver_options.integrator_type != "DISCRETE" %} + + int sim_method_num_steps[N]; + {%- for j in range(end=dims.N) %} + sim_method_num_steps[{{ j }}] = {{ solver_options.sim_method_num_steps[j] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_num_steps", &sim_method_num_steps[i]); + + int sim_method_num_stages[N]; + {%- for j in range(end=dims.N) %} + sim_method_num_stages[{{ j }}] = {{ solver_options.sim_method_num_stages[j] }}; + {%- endfor %} + + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_num_stages", &sim_method_num_stages[i]); + + int newton_iter_val = {{ solver_options.sim_method_newton_iter }}; + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_newton_iter", &newton_iter_val); + + bool tmp_bool = {{ solver_options.sim_method_jac_reuse }}; + for (int i = 0; i < N; i++) + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "dynamics_jac_reuse", &tmp_bool); + +{%- endif %} + + double nlp_solver_step_length = {{ solver_options.nlp_solver_step_length }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "step_length", &nlp_solver_step_length); + + {%- if solver_options.nlp_solver_warm_start_first_qp %} + int nlp_solver_warm_start_first_qp = {{ solver_options.nlp_solver_warm_start_first_qp }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "warm_start_first_qp", &nlp_solver_warm_start_first_qp); + {%- endif %} + + double levenberg_marquardt = {{ solver_options.levenberg_marquardt }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "levenberg_marquardt", &levenberg_marquardt); + + /* options QP solver */ +{%- if solver_options.qp_solver is starting_with("PARTIAL_CONDENSING") %} + int qp_solver_cond_N; + + {%- if solver_options.qp_solver_cond_N %} + qp_solver_cond_N = {{ solver_options.qp_solver_cond_N }}; + {% else %} + // NOTE: there is no condensing happening here! + qp_solver_cond_N = N; + {%- endif %} + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_cond_N", &qp_solver_cond_N); +{% endif %} + + int qp_solver_iter_max = {{ solver_options.qp_solver_iter_max }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_iter_max", &qp_solver_iter_max); + + {%- if solver_options.qp_solver_tol_stat %} + double qp_solver_tol_stat = {{ solver_options.qp_solver_tol_stat }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_tol_stat", &qp_solver_tol_stat); + {%- endif -%} + + {%- if solver_options.qp_solver_tol_eq %} + double qp_solver_tol_eq = {{ solver_options.qp_solver_tol_eq }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_tol_eq", &qp_solver_tol_eq); + {%- endif -%} + + {%- if solver_options.qp_solver_tol_ineq %} + double qp_solver_tol_ineq = {{ solver_options.qp_solver_tol_ineq }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_tol_ineq", &qp_solver_tol_ineq); + {%- endif -%} + + {%- if solver_options.qp_solver_tol_comp %} + double qp_solver_tol_comp = {{ solver_options.qp_solver_tol_comp }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_tol_comp", &qp_solver_tol_comp); + {%- endif -%} + + {%- if solver_options.qp_solver_warm_start %} + int qp_solver_warm_start = {{ solver_options.qp_solver_warm_start }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "qp_warm_start", &qp_solver_warm_start); + {%- endif -%} + +{% if solver_options.nlp_solver_type == "SQP" %} + // set SQP specific options + double nlp_solver_tol_stat = {{ solver_options.nlp_solver_tol_stat }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "tol_stat", &nlp_solver_tol_stat); + + double nlp_solver_tol_eq = {{ solver_options.nlp_solver_tol_eq }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "tol_eq", &nlp_solver_tol_eq); + + double nlp_solver_tol_ineq = {{ solver_options.nlp_solver_tol_ineq }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "tol_ineq", &nlp_solver_tol_ineq); + + double nlp_solver_tol_comp = {{ solver_options.nlp_solver_tol_comp }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "tol_comp", &nlp_solver_tol_comp); + + int nlp_solver_max_iter = {{ solver_options.nlp_solver_max_iter }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "max_iter", &nlp_solver_max_iter); + + int initialize_t_slacks = {{ solver_options.initialize_t_slacks }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "initialize_t_slacks", &initialize_t_slacks); +{%- endif %} + + int print_level = {{ solver_options.print_level }}; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "print_level", &print_level); + + + int ext_cost_num_hess = {{ solver_options.ext_cost_num_hess }}; +{%- if cost.cost_type == "EXTERNAL" %} + for (int i = 0; i < N; i++) + { + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, i, "cost_numerical_hessian", &ext_cost_num_hess); + } +{%- endif %} +{%- if cost.cost_type_e == "EXTERNAL" %} + ocp_nlp_solver_opts_set_at_stage(nlp_config, capsule->nlp_opts, N, "cost_numerical_hessian", &ext_cost_num_hess); +{%- endif %} + + + /* out */ + ocp_nlp_out * nlp_out = ocp_nlp_out_create(nlp_config, nlp_dims); + capsule->nlp_out = nlp_out; + + // initialize primal solution + double x0[{{ dims.nx }}]; +{% if dims.nbx_0 == dims.nx %} + // initialize with x0 + {% for item in constraints.lbx_0 %} + x0[{{ loop.index0 }}] = {{ item }}; + {%- endfor %} +{% else %} + // initialize with zeros + {% for i in range(end=dims.nx) %} + x0[{{ i }}] = 0.0; + {%- endfor %} +{%- endif %} + + double u0[NU]; + {% for i in range(end=dims.nu) %} + u0[{{ i }}] = 0.0; + {%- endfor %} + + for (int i = 0; i < N; i++) + { + // x0 + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "x", x0); + // u0 + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "u", u0); + } + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, N, "x", x0); + + capsule->nlp_solver = ocp_nlp_solver_create(nlp_config, nlp_dims, capsule->nlp_opts); + + +{% if dims.np > 0 %} + // initialize parameters to nominal value + double p[{{ dims.np }}]; + {% for i in range(end=dims.np) %} + p[{{ i }}] = {{ parameter_values[i] }}; + {%- endfor %} + + for (int i = 0; i <= N; i++) + { + {{ model.name }}_acados_update_params(capsule, i, p, NP); + } +{%- endif %}{# if dims.np #} + + status = ocp_nlp_precompute(capsule->nlp_solver, nlp_in, nlp_out); + + if (status != ACADOS_SUCCESS) + { + printf("\nocp_precompute failed!\n\n"); + exit(1); + } + + return status; +} + + +int {{ model.name }}_acados_update_params(nlp_solver_capsule * capsule, int stage, double *p, int np) +{ + int solver_status = 0; + + int casadi_np = {{ dims.np }}; + if (casadi_np != np) { + printf("acados_update_params: trying to set %i parameters for external functions." + " External function has %i parameters. Exiting.\n", np, casadi_np); + exit(1); + } + +{%- if dims.np > 0 %} + if (stage < {{ dims.N }} && stage >= 0) + { + {%- if solver_options.integrator_type == "IRK" %} + capsule->impl_dae_fun[stage].set_param(capsule->impl_dae_fun+stage, p); + capsule->impl_dae_fun_jac_x_xdot_z[stage].set_param(capsule->impl_dae_fun_jac_x_xdot_z+stage, p); + capsule->impl_dae_jac_x_xdot_u_z[stage].set_param(capsule->impl_dae_jac_x_xdot_u_z+stage, p); + + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->impl_dae_hess[stage].set_param(capsule->impl_dae_hess+stage, p); + {%- endif %} + {% elif solver_options.integrator_type == "ERK" %} + capsule->forw_vde_casadi[stage].set_param(capsule->forw_vde_casadi+stage, p); + capsule->expl_ode_fun[stage].set_param(capsule->expl_ode_fun+stage, p); + + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->hess_vde_casadi[stage].set_param(capsule->hess_vde_casadi+stage, p); + {%- endif %} + {% elif solver_options.integrator_type == "GNSF" %} + capsule->gnsf_phi_fun[stage].set_param(capsule->gnsf_phi_fun+stage, p); + capsule->gnsf_phi_fun_jac_y[stage].set_param(capsule->gnsf_phi_fun_jac_y+stage, p); + capsule->gnsf_phi_jac_y_uhat[stage].set_param(capsule->gnsf_phi_jac_y_uhat+stage, p); + + capsule->gnsf_f_lo_jac_x1_x1dot_u_z[stage].set_param(capsule->gnsf_f_lo_jac_x1_x1dot_u_z+stage, p); + {% elif solver_options.integrator_type == "DISCRETE" %} + capsule->discr_dyn_phi_fun[stage].set_param(capsule->discr_dyn_phi_fun+stage, p); + capsule->discr_dyn_phi_fun_jac_ut_xt[stage].set_param(capsule->discr_dyn_phi_fun_jac_ut_xt+stage, p); + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->discr_dyn_phi_fun_jac_ut_xt_hess[stage].set_param(capsule->discr_dyn_phi_fun_jac_ut_xt_hess+stage, p); + {% endif %} + {%- endif %}{# integrator_type #} + + // constraints + {% if constraints.constr_type == "BGP" %} + capsule->phi_constraint[stage].set_param(capsule->phi_constraint+stage, p); + {% elif constraints.constr_type == "BGH" and dims.nh > 0 %} + capsule->nl_constr_h_fun_jac[stage].set_param(capsule->nl_constr_h_fun_jac+stage, p); + capsule->nl_constr_h_fun[stage].set_param(capsule->nl_constr_h_fun+stage, p); + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->nl_constr_h_fun_jac_hess[stage].set_param(capsule->nl_constr_h_fun_jac_hess+stage, p); + {%- endif %} + {%- endif %} + + // cost + if (stage == 0) + { + {%- if cost.cost_type_0 == "NONLINEAR_LS" %} + capsule->cost_y_0_fun.set_param(&capsule->cost_y_0_fun, p); + capsule->cost_y_0_fun_jac_ut_xt.set_param(&capsule->cost_y_0_fun_jac_ut_xt, p); + capsule->cost_y_0_hess.set_param(&capsule->cost_y_0_hess, p); + {%- elif cost.cost_type_0 == "EXTERNAL" %} + capsule->ext_cost_0_fun.set_param(&capsule->ext_cost_0_fun, p); + capsule->ext_cost_0_fun_jac.set_param(&capsule->ext_cost_0_fun_jac, p); + capsule->ext_cost_0_fun_jac_hess.set_param(&capsule->ext_cost_0_fun_jac_hess, p); + {% endif %} + } + else // 0 < stage < N + { + {%- if cost.cost_type == "NONLINEAR_LS" %} + capsule->cost_y_fun[stage-1].set_param(capsule->cost_y_fun+stage-1, p); + capsule->cost_y_fun_jac_ut_xt[stage-1].set_param(capsule->cost_y_fun_jac_ut_xt+stage-1, p); + capsule->cost_y_hess[stage-1].set_param(capsule->cost_y_hess+stage-1, p); + {%- elif cost.cost_type == "EXTERNAL" %} + capsule->ext_cost_fun[stage-1].set_param(capsule->ext_cost_fun+stage-1, p); + capsule->ext_cost_fun_jac[stage-1].set_param(capsule->ext_cost_fun_jac+stage-1, p); + capsule->ext_cost_fun_jac_hess[stage-1].set_param(capsule->ext_cost_fun_jac_hess+stage-1, p); + {%- endif %} + } + } + + else // stage == N + { + // terminal shooting node has no dynamics + // cost + {%- if cost.cost_type_e == "NONLINEAR_LS" %} + capsule->cost_y_e_fun.set_param(&capsule->cost_y_e_fun, p); + capsule->cost_y_e_fun_jac_ut_xt.set_param(&capsule->cost_y_e_fun_jac_ut_xt, p); + capsule->cost_y_e_hess.set_param(&capsule->cost_y_e_hess, p); + {%- elif cost.cost_type_e == "EXTERNAL" %} + capsule->ext_cost_e_fun.set_param(&capsule->ext_cost_e_fun, p); + capsule->ext_cost_e_fun_jac.set_param(&capsule->ext_cost_e_fun_jac, p); + capsule->ext_cost_e_fun_jac_hess.set_param(&capsule->ext_cost_e_fun_jac_hess, p); + {% endif %} + // constraints + {% if constraints.constr_type_e == "BGP" %} + capsule->phi_e_constraint.set_param(&capsule->phi_e_constraint, p); + {% elif constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + capsule->nl_constr_h_e_fun_jac.set_param(&capsule->nl_constr_h_e_fun_jac, p); + capsule->nl_constr_h_e_fun.set_param(&capsule->nl_constr_h_e_fun, p); + {%- if solver_options.hessian_approx == "EXACT" %} + capsule->nl_constr_h_e_fun_jac_hess.set_param(&capsule->nl_constr_h_e_fun_jac_hess, p); + {%- endif %} + {% endif %} + } +{% endif %}{# if dims.np #} + + return solver_status; +} + + + +int {{ model.name }}_acados_solve(nlp_solver_capsule * capsule) +{ + // solve NLP + int solver_status = ocp_nlp_solve(capsule->nlp_solver, capsule->nlp_in, capsule->nlp_out); + + return solver_status; +} + + +int {{ model.name }}_acados_free(nlp_solver_capsule * capsule) +{ + // free memory + ocp_nlp_solver_opts_destroy(capsule->nlp_opts); + ocp_nlp_in_destroy(capsule->nlp_in); + ocp_nlp_out_destroy(capsule->nlp_out); + ocp_nlp_solver_destroy(capsule->nlp_solver); + ocp_nlp_dims_destroy(capsule->nlp_dims); + ocp_nlp_config_destroy(capsule->nlp_config); + ocp_nlp_plan_destroy(capsule->nlp_solver_plan); + + /* free external function */ + // dynamics +{%- if solver_options.integrator_type == "IRK" %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->impl_dae_fun[i]); + external_function_param_casadi_free(&capsule->impl_dae_fun_jac_x_xdot_z[i]); + external_function_param_casadi_free(&capsule->impl_dae_jac_x_xdot_u_z[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + external_function_param_casadi_free(&capsule->impl_dae_hess[i]); + {%- endif %} + } + free(capsule->impl_dae_fun); + free(capsule->impl_dae_fun_jac_x_xdot_z); + free(capsule->impl_dae_jac_x_xdot_u_z); + {%- if solver_options.hessian_approx == "EXACT" %} + free(capsule->impl_dae_hess); + {%- endif %} + +{%- elif solver_options.integrator_type == "ERK" %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->forw_vde_casadi[i]); + external_function_param_casadi_free(&capsule->expl_ode_fun[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + external_function_param_casadi_free(&capsule->hess_vde_casadi[i]); + {%- endif %} + } + free(capsule->forw_vde_casadi); + free(capsule->expl_ode_fun); + {%- if solver_options.hessian_approx == "EXACT" %} + free(capsule->hess_vde_casadi); + {%- endif %} + +{%- elif solver_options.integrator_type == "GNSF" %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->gnsf_phi_fun[i]); + external_function_param_casadi_free(&capsule->gnsf_phi_fun_jac_y[i]); + external_function_param_casadi_free(&capsule->gnsf_phi_jac_y_uhat[i]); + external_function_param_casadi_free(&capsule->gnsf_f_lo_jac_x1_x1dot_u_z[i]); + external_function_param_casadi_free(&capsule->gnsf_get_matrices_fun[i]); + } + free(capsule->gnsf_phi_fun); + free(capsule->gnsf_phi_fun_jac_y); + free(capsule->gnsf_phi_jac_y_uhat); + free(capsule->gnsf_f_lo_jac_x1_x1dot_u_z); + free(capsule->gnsf_get_matrices_fun); +{%- elif solver_options.integrator_type == "DISCRETE" %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_{{ model.dyn_ext_fun_type }}_free(&capsule->discr_dyn_phi_fun[i]); + external_function_param_{{ model.dyn_ext_fun_type }}_free(&capsule->discr_dyn_phi_fun_jac_ut_xt[i]); + {%- if solver_options.hessian_approx == "EXACT" %} + external_function_param_{{ model.dyn_ext_fun_type }}_free(&capsule->discr_dyn_phi_fun_jac_ut_xt_hess[i]); + {%- endif %} + } + free(capsule->discr_dyn_phi_fun); + free(capsule->discr_dyn_phi_fun_jac_ut_xt); + {%- if solver_options.hessian_approx == "EXACT" %} + free(capsule->discr_dyn_phi_fun_jac_ut_xt_hess); + {%- endif %} + +{%- endif %} + + // cost +{%- if cost.cost_type_0 == "NONLINEAR_LS" %} + external_function_param_casadi_free(&capsule->cost_y_0_fun); + external_function_param_casadi_free(&capsule->cost_y_0_fun_jac_ut_xt); + external_function_param_casadi_free(&capsule->cost_y_0_hess); +{%- elif cost.cost_type_0 == "EXTERNAL" %} + external_function_param_{{ cost.cost_ext_fun_type_0 }}_free(&capsule->ext_cost_0_fun); + external_function_param_{{ cost.cost_ext_fun_type_0 }}_free(&capsule->ext_cost_0_fun_jac); + external_function_param_{{ cost.cost_ext_fun_type_0 }}_free(&capsule->ext_cost_0_fun_jac_hess); +{%- endif %} +{%- if cost.cost_type == "NONLINEAR_LS" %} + for (int i = 0; i < {{ dims.N }} - 1; i++) + { + external_function_param_casadi_free(&capsule->cost_y_fun[i]); + external_function_param_casadi_free(&capsule->cost_y_fun_jac_ut_xt[i]); + external_function_param_casadi_free(&capsule->cost_y_hess[i]); + } + free(capsule->cost_y_fun); + free(capsule->cost_y_fun_jac_ut_xt); + free(capsule->cost_y_hess); +{%- elif cost.cost_type == "EXTERNAL" %} + for (int i = 0; i < {{ dims.N }} - 1; i++) + { + external_function_param_{{ cost.cost_ext_fun_type }}_free(&capsule->ext_cost_fun[i]); + external_function_param_{{ cost.cost_ext_fun_type }}_free(&capsule->ext_cost_fun_jac[i]); + external_function_param_{{ cost.cost_ext_fun_type }}_free(&capsule->ext_cost_fun_jac_hess[i]); + } + free(capsule->ext_cost_fun); + free(capsule->ext_cost_fun_jac); + free(capsule->ext_cost_fun_jac_hess); +{%- endif %} +{%- if cost.cost_type_e == "NONLINEAR_LS" %} + external_function_param_casadi_free(&capsule->cost_y_e_fun); + external_function_param_casadi_free(&capsule->cost_y_e_fun_jac_ut_xt); + external_function_param_casadi_free(&capsule->cost_y_e_hess); +{%- elif cost.cost_type_e == "EXTERNAL" %} + external_function_param_{{ cost.cost_ext_fun_type_e }}_free(&capsule->ext_cost_e_fun); + external_function_param_{{ cost.cost_ext_fun_type_e }}_free(&capsule->ext_cost_e_fun_jac); + external_function_param_{{ cost.cost_ext_fun_type_e }}_free(&capsule->ext_cost_e_fun_jac_hess); +{%- endif %} + + // constraints +{%- if constraints.constr_type == "BGH" and dims.nh > 0 %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->nl_constr_h_fun_jac[i]); + external_function_param_casadi_free(&capsule->nl_constr_h_fun[i]); + } + {%- if solver_options.hessian_approx == "EXACT" %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->nl_constr_h_fun_jac_hess[i]); + } + {%- endif %} + free(capsule->nl_constr_h_fun_jac); + free(capsule->nl_constr_h_fun); + {%- if solver_options.hessian_approx == "EXACT" %} + free(capsule->nl_constr_h_fun_jac_hess); + {%- endif %} + +{%- elif constraints.constr_type == "BGP" and dims.nphi > 0 %} + for (int i = 0; i < {{ dims.N }}; i++) + { + external_function_param_casadi_free(&capsule->phi_constraint[i]); + } + free(capsule->phi_constraint); +{%- endif %} + +{%- if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + external_function_param_casadi_free(&capsule->nl_constr_h_e_fun_jac); + external_function_param_casadi_free(&capsule->nl_constr_h_e_fun); +{%- if solver_options.hessian_approx == "EXACT" %} + external_function_param_casadi_free(&capsule->nl_constr_h_e_fun_jac_hess); +{%- endif %} +{%- elif constraints.constr_type_e == "BGP" and dims.nphi_e > 0 %} + external_function_param_casadi_free(&capsule->phi_e_constraint); +{%- endif %} + + return 0; +} + +ocp_nlp_in *{{ model.name }}_acados_get_nlp_in(nlp_solver_capsule * capsule) { return capsule->nlp_in; } +ocp_nlp_out *{{ model.name }}_acados_get_nlp_out(nlp_solver_capsule * capsule) { return capsule->nlp_out; } +ocp_nlp_solver *{{ model.name }}_acados_get_nlp_solver(nlp_solver_capsule * capsule) { return capsule->nlp_solver; } +ocp_nlp_config *{{ model.name }}_acados_get_nlp_config(nlp_solver_capsule * capsule) { return capsule->nlp_config; } +void *{{ model.name }}_acados_get_nlp_opts(nlp_solver_capsule * capsule) { return capsule->nlp_opts; } +ocp_nlp_dims *{{ model.name }}_acados_get_nlp_dims(nlp_solver_capsule * capsule) { return capsule->nlp_dims; } +ocp_nlp_plan *{{ model.name }}_acados_get_nlp_plan(nlp_solver_capsule * capsule) { return capsule->nlp_solver_plan; } + + +void {{ model.name }}_acados_print_stats(nlp_solver_capsule * capsule) +{ + int sqp_iter, stat_m, stat_n, tmp_int; + ocp_nlp_get(capsule->nlp_config, capsule->nlp_solver, "sqp_iter", &sqp_iter); + ocp_nlp_get(capsule->nlp_config, capsule->nlp_solver, "stat_n", &stat_n); + ocp_nlp_get(capsule->nlp_config, capsule->nlp_solver, "stat_m", &stat_m); + + {% set stat_n_max = 10 %} + double stat[{{ solver_options.nlp_solver_max_iter * stat_n_max }}]; + ocp_nlp_get(capsule->nlp_config, capsule->nlp_solver, "statistics", stat); + + int nrow = sqp_iter+1 < stat_m ? sqp_iter+1 : stat_m; + + printf("iter\tres_stat\tres_eq\t\tres_ineq\tres_comp\tqp_stat\tqp_iter\n"); + for (int i = 0; i < nrow; i++) + { + for (int j = 0; j < stat_n + 1; j++) + { + if (j == 0 || j > 4) + { + tmp_int = (int) stat[i + j * nrow]; + printf("%d\t", tmp_int); + } + else + { + printf("%e\t", stat[i + j * nrow]); + } + } + printf("\n"); + } +} + diff --git a/pyextra/acados_template/c_templates_tera/acados_solver.in.h b/pyextra/acados_template/c_templates_tera/acados_solver.in.h new file mode 100644 index 0000000000..e630b8fe4e --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_solver.in.h @@ -0,0 +1,132 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#ifndef ACADOS_SOLVER_{{ model.name }}_H_ +#define ACADOS_SOLVER_{{ model.name }}_H_ + +#include "acados_c/ocp_nlp_interface.h" +#include "acados_c/external_function_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ** capsule for solver data ** +typedef struct nlp_solver_capsule +{ + // acados objects + ocp_nlp_in *nlp_in; + ocp_nlp_out *nlp_out; + ocp_nlp_solver *nlp_solver; + void *nlp_opts; + ocp_nlp_plan *nlp_solver_plan; + ocp_nlp_config *nlp_config; + ocp_nlp_dims *nlp_dims; + + // number of expected runtime parameters + unsigned int nlp_np; + + /* external functions */ + // dynamics + external_function_param_casadi *forw_vde_casadi; + external_function_param_casadi *expl_ode_fun; + external_function_param_casadi *hess_vde_casadi; + external_function_param_casadi *impl_dae_fun; + external_function_param_casadi *impl_dae_fun_jac_x_xdot_z; + external_function_param_casadi *impl_dae_jac_x_xdot_u_z; + external_function_param_casadi *impl_dae_hess; + external_function_param_casadi *gnsf_phi_fun; + external_function_param_casadi *gnsf_phi_fun_jac_y; + external_function_param_casadi *gnsf_phi_jac_y_uhat; + external_function_param_casadi *gnsf_f_lo_jac_x1_x1dot_u_z; + external_function_param_casadi *gnsf_get_matrices_fun; + external_function_param_{{ model.dyn_ext_fun_type }} *discr_dyn_phi_fun; + external_function_param_{{ model.dyn_ext_fun_type }} *discr_dyn_phi_fun_jac_ut_xt; + external_function_param_{{ model.dyn_ext_fun_type }} *discr_dyn_phi_fun_jac_ut_xt_hess; + + // cost + external_function_param_casadi *cost_y_fun; + external_function_param_casadi *cost_y_fun_jac_ut_xt; + external_function_param_casadi *cost_y_hess; + external_function_param_{{ cost.cost_ext_fun_type }} *ext_cost_fun; + external_function_param_{{ cost.cost_ext_fun_type }} *ext_cost_fun_jac; + external_function_param_{{ cost.cost_ext_fun_type }} *ext_cost_fun_jac_hess; + + external_function_param_casadi cost_y_0_fun; + external_function_param_casadi cost_y_0_fun_jac_ut_xt; + external_function_param_casadi cost_y_0_hess; + external_function_param_{{ cost.cost_ext_fun_type_0 }} ext_cost_0_fun; + external_function_param_{{ cost.cost_ext_fun_type_0 }} ext_cost_0_fun_jac; + external_function_param_{{ cost.cost_ext_fun_type_0 }} ext_cost_0_fun_jac_hess; + + external_function_param_casadi cost_y_e_fun; + external_function_param_casadi cost_y_e_fun_jac_ut_xt; + external_function_param_casadi cost_y_e_hess; + external_function_param_{{ cost.cost_ext_fun_type_e }} ext_cost_e_fun; + external_function_param_{{ cost.cost_ext_fun_type_e }} ext_cost_e_fun_jac; + external_function_param_{{ cost.cost_ext_fun_type_e }} ext_cost_e_fun_jac_hess; + + // constraints + external_function_param_casadi *phi_constraint; + external_function_param_casadi *nl_constr_h_fun_jac; + external_function_param_casadi *nl_constr_h_fun; + external_function_param_casadi *nl_constr_h_fun_jac_hess; + + external_function_param_casadi phi_e_constraint; + external_function_param_casadi nl_constr_h_e_fun_jac; + external_function_param_casadi nl_constr_h_e_fun; + external_function_param_casadi nl_constr_h_e_fun_jac_hess; +} nlp_solver_capsule; + +nlp_solver_capsule * {{ model.name }}_acados_create_capsule(); +int {{ model.name }}_acados_free_capsule(nlp_solver_capsule *capsule); + +int {{ model.name }}_acados_create(nlp_solver_capsule * capsule); +int {{ model.name }}_acados_update_params(nlp_solver_capsule * capsule, int stage, double *value, int np); +int {{ model.name }}_acados_solve(nlp_solver_capsule * capsule); +int {{ model.name }}_acados_free(nlp_solver_capsule * capsule); +void {{ model.name }}_acados_print_stats(nlp_solver_capsule * capsule); + +ocp_nlp_in *{{ model.name }}_acados_get_nlp_in(nlp_solver_capsule * capsule); +ocp_nlp_out *{{ model.name }}_acados_get_nlp_out(nlp_solver_capsule * capsule); +ocp_nlp_solver *{{ model.name }}_acados_get_nlp_solver(nlp_solver_capsule * capsule); +ocp_nlp_config *{{ model.name }}_acados_get_nlp_config(nlp_solver_capsule * capsule); +void *{{ model.name }}_acados_get_nlp_opts(nlp_solver_capsule * capsule); +ocp_nlp_dims *{{ model.name }}_acados_get_nlp_dims(nlp_solver_capsule * capsule); +ocp_nlp_plan *{{ model.name }}_acados_get_nlp_plan(nlp_solver_capsule * capsule); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // ACADOS_SOLVER_{{ model.name }}_H_ diff --git a/pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c b/pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c new file mode 100644 index 0000000000..93635d9848 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/acados_solver_sfun.in.c @@ -0,0 +1,728 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#define S_FUNCTION_NAME acados_solver_sfunction_{{ model.name }} +#define S_FUNCTION_LEVEL 2 + +#define MDL_START + +// acados +#include "acados/utils/print.h" +#include "acados_c/sim_interface.h" +#include "acados_c/external_function_interface.h" + +// example specific +#include "{{ model.name }}_model/{{ model.name }}_model.h" +#include "acados_solver_{{ model.name }}.h" + +#include "simstruc.h" + +{% if simulink_opts.samplingtime == "t0" -%} +#define SAMPLINGTIME {{ solver_options.time_steps[0] }} +{%- elif simulink_opts.samplingtime == "-1" -%} +#define SAMPLINGTIME -1 +{%- else -%} + {{ throw(message = "simulink_opts.samplingtime must be '-1' or 't0', got val") }} +{%- endif %} + +static void mdlInitializeSizes (SimStruct *S) +{ + // specify the number of continuous and discrete states + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + {%- for key, val in simulink_opts.inputs -%} + {%- if val != 0 and val != 1 -%} + {{ throw(message = "simulink_opts.inputs must be 0 or 1, got val") }} + {%- endif -%} + {%- endfor -%} + + {#- compute number of input ports #} + {%- set n_inputs = 0 -%} + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.lbx_0 -%} {#- lbx_0 #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.ubx_0 -%} {#- ubx_0 #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.np > 0 and simulink_opts.inputs.parameter_traj -%} {#- parameter_traj #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.ny_0 > 0 and simulink_opts.inputs.y_ref_0 -%} {#- y_ref_0 -#} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.ny > 0 and dims.N > 1 and simulink_opts.inputs.y_ref -%} {#- y_ref -#} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.ny_e > 0 and dims.N > 0 and simulink_opts.inputs.y_ref_e -%} {#- y_ref_e #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.lbx -%} {#- lbx #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.ubx -%} {#- ubx #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.lbx_e -%} {#- lbx_e #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.ubx_e -%} {#- ubx_e #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.lbu -%} {#- lbu #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.ubu -%} {#- ubu #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.ng > 0 and simulink_opts.inputs.lg -%} {#- lg #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.ng > 0 and simulink_opts.inputs.ug -%} {#- ug #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nh > 0 and simulink_opts.inputs.lh -%} {#- lh #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + {%- if dims.nh > 0 and simulink_opts.inputs.uh -%} {#- uh #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + + {%- for key, val in simulink_opts.inputs -%} + {%- if val != 0 and val != 1 -%} + {{ throw(message = "simulink_opts.inputs must be 0 or 1, got val") }} + {%- endif -%} + {%- endfor -%} + {%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #} + {%- set n_inputs = n_inputs + 1 %} + {%- endif -%} + {%- if dims.ny > 0 and simulink_opts.inputs.cost_W -%} {#- cost_W #} + {%- set n_inputs = n_inputs + 1 %} + {%- endif -%} + {%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e -%} {#- cost_W_e #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + + {%- if simulink_opts.inputs.x_init -%} {#- x_init #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + + {%- if simulink_opts.inputs.u_init -%} {#- u_init #} + {%- set n_inputs = n_inputs + 1 -%} + {%- endif -%} + + // specify the number of input ports + if ( !ssSetNumInputPorts(S, {{ n_inputs }}) ) + return; + + // specify the number of output ports + {%- set_global n_outputs = 0 %} + {%- for key, val in simulink_opts.outputs %} + {%- if val == 1 %} + {%- set_global n_outputs = n_outputs + val %} + {%- elif val != 0 %} + {{ throw(message = "simulink_opts.outputs must be 0 or 1, got val") }} + {%- endif %} + {%- endfor %} + if ( !ssSetNumOutputPorts(S, {{ n_outputs }}) ) + return; + + // specify dimension information for the input ports + {%- set i_input = -1 %}{# note here i_input is 0-based #} + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.lbx_0 -%} {#- lbx_0 #} + {%- set i_input = i_input + 1 %} + // lbx_0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nbx_0 }}); + {%- endif %} + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.ubx_0 -%} {#- ubx_0 #} + {%- set i_input = i_input + 1 %} + // ubx_0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nbx_0 }}); + {%- endif %} + + {%- if dims.np > 0 and simulink_opts.inputs.parameter_traj -%} {#- parameter_traj #} + {%- set i_input = i_input + 1 %} + // parameters + ssSetInputPortVectorDimension(S, {{ i_input }}, ({{ dims.N }}+1) * {{ dims.np }}); + {%- endif %} + + {%- if dims.ny > 0 and simulink_opts.inputs.y_ref_0 %} + {%- set i_input = i_input + 1 %} + // y_ref_0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_0 }}); + {%- endif %} + + {%- if dims.ny > 0 and dims.N > 1 and simulink_opts.inputs.y_ref %} + {%- set i_input = i_input + 1 %} + // y_ref + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ (dims.N-1) * dims.ny }}); + {%- endif %} + + {%- if dims.ny_e > 0 and dims.N > 0 and simulink_opts.inputs.y_ref_e %} + {%- set i_input = i_input + 1 %} + // y_ref_e + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_e }}); + {%- endif %} + + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.lbx -%} {#- lbx #} + {%- set i_input = i_input + 1 %} + // lbx + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ (dims.N-1) * dims.nbx }}); + {%- endif %} + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.ubx -%} {#- ubx #} + {%- set i_input = i_input + 1 %} + // ubx + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ (dims.N-1) * dims.nbx }}); + {%- endif %} + + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.lbx_e -%} {#- lbx_e #} + {%- set i_input = i_input + 1 %} + // lbx_e + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nbx_e }}); + {%- endif %} + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.ubx_e -%} {#- ubx_e #} + {%- set i_input = i_input + 1 %} + // ubx_e + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nbx_e }}); + {%- endif %} + + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.lbu -%} {#- lbu #} + {%- set i_input = i_input + 1 %} + // lbu + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.N*dims.nbu }}); + {%- endif -%} + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.ubu -%} {#- ubu #} + {%- set i_input = i_input + 1 %} + // ubu + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.N*dims.nbu }}); + {%- endif -%} + + + {%- if dims.ng > 0 and simulink_opts.inputs.lg -%} {#- lg #} + {%- set i_input = i_input + 1 %} + // lg + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ng }}); + {%- endif -%} + {%- if dims.ng > 0 and simulink_opts.inputs.ug -%} {#- ug #} + {%- set i_input = i_input + 1 %} + // ug + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ng }}); + {%- endif -%} + + {%- if dims.nh > 0 and simulink_opts.inputs.lh -%} {#- lh #} + {%- set i_input = i_input + 1 %} + // lh + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nh }}); + {%- endif -%} + {%- if dims.nh > 0 and simulink_opts.inputs.uh -%} {#- uh #} + {%- set i_input = i_input + 1 %} + // uh + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nh }}); + {%- endif -%} + + {%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #} + {%- set i_input = i_input + 1 %} + // cost_W_0 + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_0 * dims.ny_0 }}); + {%- endif %} + + {%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #} + {%- set i_input = i_input + 1 %} + // cost_W + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny * dims.ny }}); + {%- endif %} + + {%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {#- cost_W_e #} + {%- set i_input = i_input + 1 %} + // cost_W_e + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.ny_e * dims.ny_e }}); + {%- endif %} + + {%- if simulink_opts.inputs.x_init -%} {#- x_init #} + {%- set i_input = i_input + 1 %} + // x_init + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nx * (dims.N+1) }}); + {%- endif -%} + + {%- if simulink_opts.inputs.u_init -%} {#- u_init #} + {%- set i_input = i_input + 1 %} + // u_init + ssSetInputPortVectorDimension(S, {{ i_input }}, {{ dims.nu * (dims.N) }}); + {%- endif -%} + + /* specify dimension information for the OUTPUT ports */ + {%- set i_output = -1 %}{# note here i_output is 0-based #} + {%- if dims.nu > 0 and simulink_opts.outputs.u0 == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, {{ dims.nu }} ); + {%- endif %} + + {%- if simulink_opts.outputs.utraj == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, {{ dims.nu * dims.N }} ); + {%- endif %} + + {%- if simulink_opts.outputs.xtraj == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, {{ dims.nx * (dims.N+1) }} ); + {%- endif %} + + {%- if simulink_opts.outputs.solver_status == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, 1 ); + {%- endif %} + + {%- if simulink_opts.outputs.KKT_residual == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, 1 ); + {%- endif %} + + {%- if dims.N > 0 and simulink_opts.outputs.x1 == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, {{ dims.nx }} ); // state at shooting node 1 + {%- endif %} + + {%- if simulink_opts.outputs.CPU_time == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, 1); + {%- endif %} + + {%- if simulink_opts.outputs.sqp_iter == 1 %} + {%- set i_output = i_output + 1 %} + ssSetOutputPortVectorDimension(S, {{ i_output }}, 1 ); + {%- endif %} + + // specify the direct feedthrough status + // should be set to 1 for all inputs used in mdlOutputs + {%- for i in range(end=n_inputs) %} + ssSetInputPortDirectFeedThrough(S, {{ i }}, 1); + {%- endfor %} + + // one sample time + ssSetNumSampleTimes(S, 1); +} + + +#if defined(MATLAB_MEX_FILE) + +#define MDL_SET_INPUT_PORT_DIMENSION_INFO +#define MDL_SET_OUTPUT_PORT_DIMENSION_INFO + +static void mdlSetInputPortDimensionInfo(SimStruct *S, int_T port, const DimsInfo_T *dimsInfo) +{ + if ( !ssSetInputPortDimensionInfo(S, port, dimsInfo) ) + return; +} + +static void mdlSetOutputPortDimensionInfo(SimStruct *S, int_T port, const DimsInfo_T *dimsInfo) +{ + if ( !ssSetOutputPortDimensionInfo(S, port, dimsInfo) ) + return; +} + +#endif /* MATLAB_MEX_FILE */ + + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, SAMPLINGTIME); + ssSetOffsetTime(S, 0, 0.0); +} + + +static void mdlStart(SimStruct *S) +{ + nlp_solver_capsule *capsule = {{ model.name }}_acados_create_capsule(); + {{ model.name }}_acados_create(capsule); + + ssSetUserData(S, (void*)capsule); +} + + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + nlp_solver_capsule *capsule = ssGetUserData(S); + ocp_nlp_config *nlp_config = {{ model.name }}_acados_get_nlp_config(capsule); + ocp_nlp_dims *nlp_dims = {{ model.name }}_acados_get_nlp_dims(capsule); + ocp_nlp_in *nlp_in = {{ model.name }}_acados_get_nlp_in(capsule); + ocp_nlp_out *nlp_out = {{ model.name }}_acados_get_nlp_out(capsule); + + InputRealPtrsType in_sign; + + {%- set buffer_sizes = [dims.nbx_0, dims.np, dims.nbx, dims.nbu, dims.ng, dims.nh, dims.nx] -%} + + {%- if dims.ny_0 > 0 and simulink_opts.inputs.y_ref_0 %} {# y_ref_0 #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_0)) %} + {%- endif %} + {%- if dims.ny > 0 and dims.N > 1 and simulink_opts.inputs.y_ref %} {# y_ref #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny)) %} + {%- endif %} + {%- if dims.ny_e > 0 and dims.N > 0 and simulink_opts.inputs.y_ref_e %} {# y_ref_e #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_e)) %} + {%- endif %} + + {%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {# cost_W_0 #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_0 * dims.ny_0)) %} + {%- endif %} + {%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {# cost_W #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny * dims.ny)) %} + {%- endif %} + {%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {# cost_W_e #} + {%- set buffer_sizes = buffer_sizes | concat(with=(dims.ny_e * dims.ny_e)) %} + {%- endif %} + + // local buffer + {%- set buffer_size = buffer_sizes | sort | last %} + real_t buffer[{{ buffer_size }}]; + + /* go through inputs */ + {%- set i_input = -1 %} + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.lbx_0 -%} {#- lbx_0 #} + // lbx_0 + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.nbx_0 }}; i++) + buffer[i] = (double)(*in_sign[i]); + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "lbx", buffer); + {%- endif %} + + {%- if dims.nbx_0 > 0 and simulink_opts.inputs.ubx_0 -%} {#- ubx_0 #} + // ubx_0 + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.nbx_0 }}; i++) + buffer[i] = (double)(*in_sign[i]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "ubx", buffer); + {%- endif %} + + {%- if dims.np > 0 and simulink_opts.inputs.parameter_traj -%} {#- parameter_traj #} + // parameters - stage-variant !!! + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + // update value of parameters + for (int ii = 0; ii <= {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.np }}; jj++) + buffer[jj] = (double)(*in_sign[ii*{{dims.np}}+jj]); + {{ model.name }}_acados_update_params(capsule, ii, buffer, {{ dims.np }}); + } + {%- endif %} + + {% if dims.ny_0 > 0 and simulink_opts.inputs.y_ref_0 %} + // y_ref_0 + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.ny_0 }}; i++) + buffer[i] = (double)(*in_sign[i]); + + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "yref", (void *) buffer); + {%- endif %} + + {% if dims.ny > 0 and dims.N > 1 and simulink_opts.inputs.y_ref %} + // y_ref - for stages 1 to N-1 + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int ii = 1; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.ny }}; jj++) + buffer[jj] = (double)(*in_sign[(ii-1)*{{ dims.ny }}+jj]); + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, ii, "yref", (void *) buffer); + } + {%- endif %} + + {% if dims.ny_e > 0 and dims.N > 0 and simulink_opts.inputs.y_ref_e %} + // y_ref_e + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.ny_e }}; i++) + buffer[i] = (double)(*in_sign[i]); + + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, {{ dims.N }}, "yref", (void *) buffer); + {%- endif %} + + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.lbx -%} {#- lbx #} + // lbx + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 1; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.nbx }}; jj++) + buffer[jj] = (double)(*in_sign[(ii-1)*{{ dims.nbx }}+jj]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "lbx", (void *) buffer); + } + {%- endif %} + {%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.ubx -%} {#- ubx #} + // ubx + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 1; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.nbx }}; jj++) + buffer[jj] = (double)(*in_sign[(ii-1)*{{ dims.nbx }}+jj]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "ubx", (void *) buffer); + } + {%- endif %} + + + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.lbx_e -%} {#- lbx_e #} + // lbx_e + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.nbx_e }}; i++) + buffer[i] = (double)(*in_sign[i]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, {{ dims.N }}, "lbx", buffer); + {%- endif %} + {%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.ubx_e -%} {#- ubx_e #} + // ubx_e + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.nbx_e }}; i++) + buffer[i] = (double)(*in_sign[i]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, {{ dims.N }}, "ubx", buffer); + {%- endif %} + + + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.lbu -%} {#- lbu #} + // lbu + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 0; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.nbu }}; jj++) + buffer[jj] = (double)(*in_sign[ii*{{ dims.nbu }}+jj]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "lbu", (void *) buffer); + } + {%- endif -%} + {%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.ubu -%} {#- ubu #} + // ubu + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 0; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.nbu }}; jj++) + buffer[jj] = (double)(*in_sign[ii*{{ dims.nbu }}+jj]); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "ubu", (void *) buffer); + } + {%- endif -%} + + {%- if dims.ng > 0 and simulink_opts.inputs.lg -%} {#- lg #} + // lg + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.ng }}; i++) + buffer[i] = (double)(*in_sign[i]); + + for (int ii = 0; ii < {{ dims.N }}; ii++) + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "lg", buffer); + {%- endif -%} + {%- if dims.ng > 0 and simulink_opts.inputs.ug -%} {#- ug #} + // ug + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.ng }}; i++) + buffer[i] = (double)(*in_sign[i]); + + for (int ii = 0; ii < {{ dims.N }}; ii++) + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "ug", buffer); + {%- endif -%} + {%- if dims.nh > 0 and simulink_opts.inputs.lh -%} {#- lh #} + // lh + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.nh }}; i++) + buffer[i] = (double)(*in_sign[i]); + + for (int ii = 0; ii < {{ dims.N }}; ii++) + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "lh", buffer); + {%- endif -%} + {%- if dims.nh > 0 and simulink_opts.inputs.uh -%} {#- uh #} + // uh + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + + for (int i = 0; i < {{ dims.nh }}; i++) + buffer[i] = (double)(*in_sign[i]); + + for (int ii = 0; ii < {{ dims.N }}; ii++) + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, ii, "uh", buffer); + {%- endif -%} + + {%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {# cost_W_0 #} + // cost_W_0 + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.ny_0 * dims.ny_0 }}; i++) + buffer[i] = (double)(*in_sign[i]); + + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, 0, "W", buffer); + {%- endif %} + + {%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {# cost_W #} + // cost_W + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.ny * dims.ny }}; i++) + buffer[i] = (double)(*in_sign[i]); + + for (int ii = 1; ii < {{ dims.N }}; ii++) + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, ii, "W", buffer); + {%- endif %} + + {%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {#- cost_W_e #} + // cost_W_e + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int i = 0; i < {{ dims.ny_e * dims.ny_e }}; i++) + buffer[i] = (double)(*in_sign[i]); + + ocp_nlp_cost_model_set(nlp_config, nlp_dims, nlp_in, {{ dims.N }}, "W", buffer); + {%- endif %} + + {%- if simulink_opts.inputs.x_init %} {#- x_init #} + // x_init + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 0; ii < {{ dims.N + 1 }}; ii++) + { + for (int jj = 0; jj < {{ dims.nx }}; jj++) + buffer[jj] = (double)(*in_sign[(ii)*{{ dims.nx }}+jj]); + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, ii, "x", (void *) buffer); + } + {%- endif %} + + {%- if simulink_opts.inputs.u_init %} {#- u_init #} + // u_init + {%- set i_input = i_input + 1 %} + in_sign = ssGetInputPortRealSignalPtrs(S, {{ i_input }}); + for (int ii = 0; ii < {{ dims.N }}; ii++) + { + for (int jj = 0; jj < {{ dims.nu }}; jj++) + buffer[jj] = (double)(*in_sign[(ii)*{{ dims.nu }}+jj]); + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, ii, "u", (void *) buffer); + } + {%- endif %} + + /* call solver */ + int rti_phase = 0; + ocp_nlp_solver_opts_set(nlp_config, capsule->nlp_opts, "rti_phase", &rti_phase); + int acados_status = {{ model.name }}_acados_solve(capsule); + + + /* set outputs */ + // assign pointers to output signals + real_t *out_u0, *out_utraj, *out_xtraj, *out_status, *out_sqp_iter, *out_KKT_res, *out_x1, *out_cpu_time; + int tmp_int; + + {%- set i_output = -1 -%}{# note here i_output is 0-based #} + {%- if dims.nu > 0 and simulink_opts.outputs.u0 == 1 %} + {%- set i_output = i_output + 1 %} + out_u0 = ssGetOutputPortRealSignal(S, {{ i_output }}); + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, 0, "u", (void *) out_u0); + {%- endif %} + + {%- if simulink_opts.outputs.utraj == 1 %} + {%- set i_output = i_output + 1 %} + out_utraj = ssGetOutputPortRealSignal(S, {{ i_output }}); + for (int ii = 0; ii < {{ dims.N }}; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, + "u", (void *) (out_utraj + ii * {{ dims.nu }})); + {%- endif %} + + {% if simulink_opts.outputs.xtraj == 1 %} + {%- set i_output = i_output + 1 %} + + out_xtraj = ssGetOutputPortRealSignal(S, {{ i_output }}); + for (int ii = 0; ii < {{ dims.N + 1 }}; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, + "x", (void *) (out_xtraj + ii * {{ dims.nx }})); + {%- endif %} + + {%- if simulink_opts.outputs.solver_status == 1 %} + {%- set i_output = i_output + 1 %} + out_status = ssGetOutputPortRealSignal(S, {{ i_output }}); + *out_status = (real_t) acados_status; + {%- endif %} + + {%- if simulink_opts.outputs.KKT_residual == 1 %} + {%- set i_output = i_output + 1 %} + out_KKT_res = ssGetOutputPortRealSignal(S, {{ i_output }}); + *out_KKT_res = (real_t) nlp_out->inf_norm_res; + {%- endif %} + + {%- if dims.N > 0 and simulink_opts.outputs.x1 == 1 %} + {%- set i_output = i_output + 1 %} + out_x1 = ssGetOutputPortRealSignal(S, {{ i_output }}); + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, 1, "x", (void *) out_x1); + {%- endif %} + + {%- if simulink_opts.outputs.CPU_time == 1 %} + {%- set i_output = i_output + 1 %} + out_cpu_time = ssGetOutputPortRealSignal(S, {{ i_output }}); + // get solution time + ocp_nlp_get(nlp_config, capsule->nlp_solver, "time_tot", (void *) out_cpu_time); + {%- endif -%} + + {%- if simulink_opts.outputs.sqp_iter == 1 %} + {%- set i_output = i_output + 1 %} + out_sqp_iter = ssGetOutputPortRealSignal(S, {{ i_output }}); + // get sqp iter + ocp_nlp_get(nlp_config, capsule->nlp_solver, "sqp_iter", (void *) &tmp_int); + *out_sqp_iter = (real_t) tmp_int; + {%- endif %} + +} + +static void mdlTerminate(SimStruct *S) +{ + nlp_solver_capsule *capsule = ssGetUserData(S); + + {{ model.name }}_acados_free(capsule); + {{ model.name }}_acados_free_capsule(capsule); +} + + +#ifdef MATLAB_MEX_FILE +#include "simulink.c" +#else +#include "cg_sfun.h" +#endif diff --git a/pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h b/pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h new file mode 100644 index 0000000000..5dc611dd38 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/cost_y_0_fun.in.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_Y_0_COST +#define {{ model.name }}_Y_0_COST + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_type_0 == "NONLINEAR_LS" %} +int {{ model.name }}_cost_y_0_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_0_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_0_fun_sparsity_in(int); +const int *{{ model.name }}_cost_y_0_fun_sparsity_out(int); +int {{ model.name }}_cost_y_0_fun_n_in(); +int {{ model.name }}_cost_y_0_fun_n_out(); + +int {{ model.name }}_cost_y_0_fun_jac_ut_xt(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_0_fun_jac_ut_xt_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_0_fun_jac_ut_xt_sparsity_in(int); +const int *{{ model.name }}_cost_y_0_fun_jac_ut_xt_sparsity_out(int); +int {{ model.name }}_cost_y_0_fun_jac_ut_xt_n_in(); +int {{ model.name }}_cost_y_0_fun_jac_ut_xt_n_out(); + +int {{ model.name }}_cost_y_0_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_0_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_0_hess_sparsity_in(int); +const int *{{ model.name }}_cost_y_0_hess_sparsity_out(int); +int {{ model.name }}_cost_y_0_hess_n_in(); +int {{ model.name }}_cost_y_0_hess_n_out(); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_Y_0_COST diff --git a/pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h b/pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h new file mode 100644 index 0000000000..041f356939 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/cost_y_e_fun.in.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_Y_E_COST +#define {{ model.name }}_Y_E_COST + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_type_e == "NONLINEAR_LS" %} +int {{ model.name }}_cost_y_e_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_e_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_e_fun_sparsity_in(int); +const int *{{ model.name }}_cost_y_e_fun_sparsity_out(int); +int {{ model.name }}_cost_y_e_fun_n_in(); +int {{ model.name }}_cost_y_e_fun_n_out(); + +int {{ model.name }}_cost_y_e_fun_jac_ut_xt(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_e_fun_jac_ut_xt_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_e_fun_jac_ut_xt_sparsity_in(int); +const int *{{ model.name }}_cost_y_e_fun_jac_ut_xt_sparsity_out(int); +int {{ model.name }}_cost_y_e_fun_jac_ut_xt_n_in(); +int {{ model.name }}_cost_y_e_fun_jac_ut_xt_n_out(); + +int {{ model.name }}_cost_y_e_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_e_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_e_hess_sparsity_in(int); +const int *{{ model.name }}_cost_y_e_hess_sparsity_out(int); +int {{ model.name }}_cost_y_e_hess_n_in(); +int {{ model.name }}_cost_y_e_hess_n_out(); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_Y_E_COST diff --git a/pyextra/acados_template/c_templates_tera/cost_y_fun.in.h b/pyextra/acados_template/c_templates_tera/cost_y_fun.in.h new file mode 100644 index 0000000000..ec2119603f --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/cost_y_fun.in.h @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_Y_COST +#define {{ model.name }}_Y_COST + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_type == "NONLINEAR_LS" %} +int {{ model.name }}_cost_y_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_fun_sparsity_in(int); +const int *{{ model.name }}_cost_y_fun_sparsity_out(int); +int {{ model.name }}_cost_y_fun_n_in(); +int {{ model.name }}_cost_y_fun_n_out(); + +int {{ model.name }}_cost_y_fun_jac_ut_xt(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_fun_jac_ut_xt_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_fun_jac_ut_xt_sparsity_in(int); +const int *{{ model.name }}_cost_y_fun_jac_ut_xt_sparsity_out(int); +int {{ model.name }}_cost_y_fun_jac_ut_xt_n_in(); +int {{ model.name }}_cost_y_fun_jac_ut_xt_n_out(); + +int {{ model.name }}_cost_y_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_y_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_y_hess_sparsity_in(int); +const int *{{ model.name }}_cost_y_hess_sparsity_out(int); +int {{ model.name }}_cost_y_hess_n_in(); +int {{ model.name }}_cost_y_hess_n_out(); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_Y_COST diff --git a/pyextra/acados_template/c_templates_tera/external_cost.in.h b/pyextra/acados_template/c_templates_tera/external_cost.in.h new file mode 100644 index 0000000000..19280182e5 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/external_cost.in.h @@ -0,0 +1,74 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_EXT_COST +#define {{ model.name }}_EXT_COST + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_ext_fun_type == "casadi" %} +{% if cost.cost_type == "EXTERNAL" %} +int {{ model.name }}_cost_ext_cost_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_fun_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_fun_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_fun_n_in(); +int {{ model.name }}_cost_ext_cost_fun_n_out(); + +int {{ model.name }}_cost_ext_cost_fun_jac_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_fun_jac_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_fun_jac_hess_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_fun_jac_hess_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_fun_jac_hess_n_in(); +int {{ model.name }}_cost_ext_cost_fun_jac_hess_n_out(); + +int {{ model.name }}_cost_ext_cost_fun_jac(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_fun_jac_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_fun_jac_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_fun_jac_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_fun_jac_n_in(); +int {{ model.name }}_cost_ext_cost_fun_jac_n_out(); +{% endif %} + +{% else %} +int {{ cost.cost_function_ext_cost }}(void **, void **, void *); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_EXT_COST diff --git a/pyextra/acados_template/c_templates_tera/external_cost_0.in.h b/pyextra/acados_template/c_templates_tera/external_cost_0.in.h new file mode 100644 index 0000000000..0367e3d0c6 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/external_cost_0.in.h @@ -0,0 +1,75 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_EXT_COST_0 +#define {{ model.name }}_EXT_COST_0 + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_ext_fun_type_0 == "casadi" %} + +{% if cost.cost_type_0 == "EXTERNAL" %} +int {{ model.name }}_cost_ext_cost_0_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_0_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_0_fun_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_0_fun_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_0_fun_n_in(); +int {{ model.name }}_cost_ext_cost_0_fun_n_out(); + +int {{ model.name }}_cost_ext_cost_0_fun_jac_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_0_fun_jac_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_0_fun_jac_hess_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_0_fun_jac_hess_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_0_fun_jac_hess_n_in(); +int {{ model.name }}_cost_ext_cost_0_fun_jac_hess_n_out(); + +int {{ model.name }}_cost_ext_cost_0_fun_jac(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_0_fun_jac_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_0_fun_jac_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_0_fun_jac_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_0_fun_jac_n_in(); +int {{ model.name }}_cost_ext_cost_0_fun_jac_n_out(); +{% endif %} + +{% else %} +int {{ cost.cost_function_ext_cost_0 }}(void **, void **, void *); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_EXT_COST_0 diff --git a/pyextra/acados_template/c_templates_tera/external_cost_e.in.h b/pyextra/acados_template/c_templates_tera/external_cost_e.in.h new file mode 100644 index 0000000000..de4f608b62 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/external_cost_e.in.h @@ -0,0 +1,74 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_EXT_COST_E +#define {{ model.name }}_EXT_COST_E + +#ifdef __cplusplus +extern "C" { +#endif + +{% if cost.cost_ext_fun_type_e == "casadi" %} +{% if cost.cost_type_e == "EXTERNAL" %} +int {{ model.name }}_cost_ext_cost_e_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_e_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_e_fun_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_e_fun_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_e_fun_n_in(); +int {{ model.name }}_cost_ext_cost_e_fun_n_out(); + +int {{ model.name }}_cost_ext_cost_e_fun_jac_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_e_fun_jac_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_e_fun_jac_hess_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_e_fun_jac_hess_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_e_fun_jac_hess_n_in(); +int {{ model.name }}_cost_ext_cost_e_fun_jac_hess_n_out(); + +int {{ model.name }}_cost_ext_cost_e_fun_jac(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_cost_ext_cost_e_fun_jac_work(int *, int *, int *, int *); +const int *{{ model.name }}_cost_ext_cost_e_fun_jac_sparsity_in(int); +const int *{{ model.name }}_cost_ext_cost_e_fun_jac_sparsity_out(int); +int {{ model.name }}_cost_ext_cost_e_fun_jac_n_in(); +int {{ model.name }}_cost_ext_cost_e_fun_jac_n_out(); +{% endif %} + +{% else %} +int {{ cost.cost_function_ext_cost_e }}(void **, void **, void *); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_EXT_COST_E diff --git a/pyextra/acados_template/c_templates_tera/h_constraint.in.h b/pyextra/acados_template/c_templates_tera/h_constraint.in.h new file mode 100644 index 0000000000..82e3d60840 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/h_constraint.in.h @@ -0,0 +1,70 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#ifndef {{ model.name }}_H_CONSTRAINT +#define {{ model.name }}_H_CONSTRAINT + +#ifdef __cplusplus +extern "C" { +#endif + +{% if dims.nh > 0 %} +int {{ model.name }}_constr_h_fun_jac_uxt_zt(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_fun_jac_uxt_zt_sparsity_in(int); +const int *{{ model.name }}_constr_h_fun_jac_uxt_zt_sparsity_out(int); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_n_in(); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_n_out(); + +int {{ model.name }}_constr_h_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_fun_sparsity_in(int); +const int *{{ model.name }}_constr_h_fun_sparsity_out(int); +int {{ model.name }}_constr_h_fun_n_in(); +int {{ model.name }}_constr_h_fun_n_out(); + +{% if solver_options.hessian_approx == "EXACT" -%} +int {{ model.name }}_constr_h_fun_jac_uxt_zt_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_sparsity_in(int); +const int *{{ model.name }}_constr_h_fun_jac_uxt_zt_hess_sparsity_out(int); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_hess_n_in(); +int {{ model.name }}_constr_h_fun_jac_uxt_zt_hess_n_out(); +{% endif %} +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_H_CONSTRAINT diff --git a/pyextra/acados_template/c_templates_tera/h_e_constraint.in.h b/pyextra/acados_template/c_templates_tera/h_e_constraint.in.h new file mode 100644 index 0000000000..7ac689faa6 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/h_e_constraint.in.h @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +#ifndef {{ model.name }}_H_E_CONSTRAINT +#define {{ model.name }}_H_E_CONSTRAINT + +#ifdef __cplusplus +extern "C" { +#endif + +{% if dims.nh_e > 0 %} +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_e_fun_jac_uxt_zt_sparsity_in(int); +const int *{{ model.name }}_constr_h_e_fun_jac_uxt_zt_sparsity_out(int); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_n_in(); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_n_out(); + +int {{ model.name }}_constr_h_e_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_e_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_e_fun_sparsity_in(int); +const int *{{ model.name }}_constr_h_e_fun_sparsity_out(int); +int {{ model.name }}_constr_h_e_fun_n_in(); +int {{ model.name }}_constr_h_e_fun_n_out(); + +{% if solver_options.hessian_approx == "EXACT" -%} +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_sparsity_in(int); +const int *{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_sparsity_out(int); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_n_in(); +int {{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess_n_out(); +{% endif %} +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_H_E_CONSTRAINT diff --git a/pyextra/acados_template/c_templates_tera/main.in.c b/pyextra/acados_template/c_templates_tera/main.in.c new file mode 100644 index 0000000000..1cae69aea6 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/main.in.c @@ -0,0 +1,181 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +// standard +#include +#include +// acados +#include "acados/utils/print.h" +#include "acados/utils/math.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_c/external_function_interface.h" +#include "acados_solver_{{ model.name }}.h" + + +int main() +{ + + nlp_solver_capsule *acados_ocp_capsule = {{ model.name }}_acados_create_capsule(); + int status = {{ model.name }}_acados_create(acados_ocp_capsule); + + if (status) + { + printf("{{ model.name }}_acados_create() returned status %d. Exiting.\n", status); + exit(1); + } + + ocp_nlp_config *nlp_config = {{ model.name }}_acados_get_nlp_config(acados_ocp_capsule); + ocp_nlp_dims *nlp_dims = {{ model.name }}_acados_get_nlp_dims(acados_ocp_capsule); + ocp_nlp_in *nlp_in = {{ model.name }}_acados_get_nlp_in(acados_ocp_capsule); + ocp_nlp_out *nlp_out = {{ model.name }}_acados_get_nlp_out(acados_ocp_capsule); + ocp_nlp_solver *nlp_solver = {{ model.name }}_acados_get_nlp_solver(acados_ocp_capsule); + void *nlp_opts = {{ model.name }}_acados_get_nlp_opts(acados_ocp_capsule); + + // initial condition + int idxbx0[{{ dims.nbx_0 }}]; + {%- for i in range(end=dims.nbx_0) %} + idxbx0[{{ i }}] = {{ constraints.idxbx_0[i] }}; + {%- endfor %} + + double lbx0[{{ dims.nbx_0 }}]; + double ubx0[{{ dims.nbx_0 }}]; + {%- for i in range(end=dims.nbx_0) %} + lbx0[{{ i }}] = {{ constraints.lbx_0[i] }}; + ubx0[{{ i }}] = {{ constraints.ubx_0[i] }}; + {%- endfor %} + + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "idxbx", idxbx0); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "lbx", lbx0); + ocp_nlp_constraints_model_set(nlp_config, nlp_dims, nlp_in, 0, "ubx", ubx0); + + // initialization for state values + double x_init[{{ dims.nx }}]; + {%- for i in range(end=dims.nx) %} + x_init[{{ i }}] = 0.0; + {%- endfor %} + + // initial value for control input + double u0[{{ dims.nu }}]; + {%- for i in range(end=dims.nu) %} + u0[{{ i }}] = 0.0; + {%- endfor %} + + + {%- if dims.np > 0 %} + // set parameters + double p[{{ dims.np }}]; + {% for item in parameter_values %} + p[{{ loop.index0 }}] = {{ item }}; + {% endfor %} + + for (int ii = 0; ii <= {{ dims.N }}; ii++) + { + {{ model.name }}_acados_update_params(acados_ocp_capsule, ii, p, {{ dims.np }}); + } + {% endif %}{# if np > 0 #} + + // prepare evaluation + int NTIMINGS = 1; + double min_time = 1e12; + double kkt_norm_inf; + double elapsed_time; + int sqp_iter; + + double xtraj[{{ dims.nx }} * ({{ dims.N }}+1)]; + double utraj[{{ dims.nu }} * ({{ dims.N }})]; + + + // solve ocp in loop + int rti_phase = 0; + + for (int ii = 0; ii < NTIMINGS; ii++) + { + // initialize solution + for (int i = 0; i <= nlp_dims->N; i++) + { + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "x", x_init); + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "u", u0); + } + ocp_nlp_solver_opts_set(nlp_config, nlp_opts, "rti_phase", &rti_phase); + status = {{ model.name }}_acados_solve(acados_ocp_capsule); + ocp_nlp_get(nlp_config, nlp_solver, "time_tot", &elapsed_time); + min_time = MIN(elapsed_time, min_time); + } + + /* print solution and statistics */ + for (int ii = 0; ii <= nlp_dims->N; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, "x", &xtraj[ii*{{ dims.nx }}]); + for (int ii = 0; ii < nlp_dims->N; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, "u", &utraj[ii*{{ dims.nu }}]); + + printf("\n--- xtraj ---\n"); + d_print_exp_tran_mat( {{ dims.nx }}, {{ dims.N }}+1, xtraj, {{ dims.nx }} ); + printf("\n--- utraj ---\n"); + d_print_exp_tran_mat( {{ dims.nu }}, {{ dims.N }}, utraj, {{ dims.nu }} ); + // ocp_nlp_out_print(nlp_solver->dims, nlp_out); + + printf("\nsolved ocp %d times, solution printed above\n\n", NTIMINGS); + + if (status == ACADOS_SUCCESS) + { + printf("{{ model.name }}_acados_solve(): SUCCESS!\n"); + } + else + { + printf("{{ model.name }}_acados_solve() failed with status %d.\n", status); + } + + // get solution + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, 0, "kkt_norm_inf", &kkt_norm_inf); + ocp_nlp_get(nlp_config, nlp_solver, "sqp_iter", &sqp_iter); + + {{ model.name }}_acados_print_stats(acados_ocp_capsule); + + printf("\nSolver info:\n"); + printf(" SQP iterations %2d\n minimum time for %d solve %f [ms]\n KKT %e\n", + sqp_iter, NTIMINGS, min_time*1000, kkt_norm_inf); + + // free solver + status = {{ model.name }}_acados_free(acados_ocp_capsule); + if (status) { + printf("{{ model.name }}_acados_free() returned status %d. \n", status); + } + // free solver capsule + status = {{ model.name }}_acados_free_capsule(acados_ocp_capsule); + if (status) { + printf("{{ model.name }}_acados_free_capsule() returned status %d. \n", status); + } + + return status; +} diff --git a/pyextra/acados_template/c_templates_tera/main_mex.in.c b/pyextra/acados_template/c_templates_tera/main_mex.in.c new file mode 100644 index 0000000000..8da5db29a0 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/main_mex.in.c @@ -0,0 +1,184 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +// standard +#include +#include +// acados +#include "acados/utils/print.h" +#include "acados/utils/math.h" +#include "acados_c/ocp_nlp_interface.h" +#include "acados_solver_{{ model.name }}.h" +// mex +#include "mex.h" + +/* auxilary mex */ +// prints a matrix in column-major format (exponential notation) +void MEX_print_exp_mat(int m, int n, double *A, int lda) +{ + for (int i=0; iN; i++) + { + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "x", x_init); + ocp_nlp_out_set(nlp_config, nlp_dims, nlp_out, i, "u", u0); + } + status = {{ model.name }}_acados_solve(); + ocp_nlp_get(nlp_config, nlp_solver, "time_tot", &elapsed_time); + min_time = MIN(elapsed_time, min_time); + } + + /* print solution and statistics */ + for (int ii = 0; ii <= nlp_dims->N; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, "x", &xtraj[ii*{{ dims.nx }}]); + for (int ii = 0; ii < nlp_dims->N; ii++) + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, ii, "u", &utraj[ii*{{ dims.nu }}]); + + mexPrintf("\n--- xtraj ---\n"); + MEX_print_exp_tran_mat( {{ dims.nx }}, {{ dims.N }}+1, xtraj, {{ dims.nx }} ); + mexPrintf("\n--- utraj ---\n"); + MEX_print_exp_tran_mat( {{ dims.nu }}, {{ dims.N }}, utraj, {{ dims.nu }} ); + + mexPrintf("\nsolved ocp %d times, solution printed above\n\n", NTIMINGS); + + if (status == ACADOS_SUCCESS) + mexPrintf("{{ model.name }}_acados_solve(): SUCCESS!\n"); + else + mexPrintf("{{ model.name }}_acados_solve() failed with status %d.\n", status); + + // get solution + ocp_nlp_out_get(nlp_config, nlp_dims, nlp_out, 0, "kkt_norm_inf", &kkt_norm_inf); + ocp_nlp_get(nlp_config, nlp_solver, "sqp_iter", &sqp_iter); + + mexPrintf("\nSolver info:\n"); + mexPrintf(" SQP iterations %2d\n minimum time for 1 solve %f [ms]\n KKT %e\n", + sqp_iter, min_time*1000, kkt_norm_inf); + + // free solver + status = {{ model.name }}_acados_free(); + if (status) + { + mexPrintf("{{ model.name }}_acados_free() returned status %d.\n", status); + } + + return; +} diff --git a/pyextra/acados_template/c_templates_tera/main_sim.in.c b/pyextra/acados_template/c_templates_tera/main_sim.in.c new file mode 100644 index 0000000000..b62bf8f7fd --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/main_sim.in.c @@ -0,0 +1,130 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + + +// standard +#include +#include +// acados +#include "acados/utils/print.h" +#include "acados/utils/math.h" +#include "acados_c/sim_interface.h" +#include "acados_sim_solver_{{ model.name }}.h" + + +int main() +{ + int status = 0; + sim_solver_capsule *capsule = {{ model.name }}_acados_sim_solver_create_capsule(); + status = {{ model.name }}_acados_sim_create(capsule); + + if (status) + { + printf("acados_create() returned status %d. Exiting.\n", status); + exit(1); + } + + sim_config *acados_sim_config = {{ model.name }}_acados_get_sim_config(capsule); + sim_in *acados_sim_in = {{ model.name }}_acados_get_sim_in(capsule); + sim_out *acados_sim_out = {{ model.name }}_acados_get_sim_out(capsule); + void *acados_sim_dims = {{ model.name }}_acados_get_sim_dims(capsule); + + // initial condition + double x_current[{{ dims.nx }}]; + {%- for i in range(end=dims.nx) %} + x_current[{{ i }}] = 0.0; + {%- endfor %} + + {% if constraints.lbx_0 %} + {%- for i in range(end=dims.nbx_0) %} + x_current[{{ constraints.idxbx_0[i] }}] = {{ constraints.lbx_0[i] }}; + {%- endfor %} + {% if dims.nbx_0 != dims.nx %} + printf("main_sim: NOTE: initial state not fully defined via lbx_0, using 0.0 for indices that are not in idxbx_0."); + {%- endif %} + {% else %} + printf("main_sim: initial state not defined, should be in lbx_0, using zero vector."); + {%- endif %} + + + // initial value for control input + double u0[{{ dims.nu }}]; + {%- for i in range(end=dims.nu) %} + u0[{{ i }}] = 0.0; + {%- endfor %} + + {%- if dims.np > 0 %} + // set parameters + double p[{{ dims.np }}]; + {% for item in parameter_values %} + p[{{ loop.index0 }}] = {{ item }}; + {% endfor %} + + {{ model.name }}_acados_sim_update_params(capsule, p, {{ dims.np }}); + {% endif %}{# if np > 0 #} + + int n_sim_steps = 3; + // solve ocp in loop + for (int ii = 0; ii < n_sim_steps; ii++) + { + sim_in_set(acados_sim_config, acados_sim_dims, + acados_sim_in, "x", x_current); + status = {{ model.name }}_acados_sim_solve(capsule); + + if (status != ACADOS_SUCCESS) + { + printf("acados_solve() failed with status %d.\n", status); + } + + sim_out_get(acados_sim_config, acados_sim_dims, + acados_sim_out, "x", x_current); + + printf("\nx_current, %d\n", ii); + for (int jj = 0; jj < {{ dims.nx }}; jj++) + { + printf("%e\n", x_current[jj]); + } + } + + printf("\nPerformed %d simulation steps with acados integrator successfully.\n\n", n_sim_steps); + + // free solver + status = {{ model.name }}_acados_sim_free(capsule); + if (status) { + printf("{{ model.name }}_acados_sim_free() returned status %d. \n", status); + } + + {{ model.name }}_acados_sim_solver_free_capsule(capsule); + + return status; +} diff --git a/pyextra/acados_template/c_templates_tera/make_main_mex.in.m b/pyextra/acados_template/c_templates_tera/make_main_mex.in.m new file mode 100644 index 0000000000..9188686a0d --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/make_main_mex.in.m @@ -0,0 +1,105 @@ +% +% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +% +% This file is part of acados. +% +% The 2-Clause BSD License +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% 1. Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE.; +% + +function make_main_mex_{{ model.name }}() + + opts.output_dir = pwd; + + % get acados folder + acados_folder = getenv('ACADOS_INSTALL_DIR'); + + % set paths + acados_include = ['-I' fullfile(acados_folder, 'include')]; + template_lib_include = ['-l' 'acados_solver_{{ model.name }}']; + template_lib_path = ['-L' fullfile(pwd)]; + + acados_lib_path = ['-L' fullfile(acados_folder, 'lib')]; + external_include = ['-I', fullfile(acados_folder, 'external')]; + blasfeo_include = ['-I', fullfile(acados_folder, 'external', 'blasfeo', 'include')]; + hpipm_include = ['-I', fullfile(acados_folder, 'external', 'hpipm', 'include')]; + + mex_names = { ... + 'main_mex_{{ model.name }}' ... + }; + + mex_files = cell(length(mex_names), 1); + for k=1:length(mex_names) + mex_files{k} = fullfile([mex_names{k}, '.c']); + end + + %% octave C flags + if is_octave() + if ~exist(fullfile(opts.output_dir, 'cflags_octave.txt'), 'file') + diary(fullfile(opts.output_dir, 'cflags_octave.txt')); + diary on + mkoctfile -p CFLAGS + diary off + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'r'); + cflags_tmp = fscanf(input_file, '%[^\n]s'); + fclose(input_file); + if ~ismac() + cflags_tmp = [cflags_tmp, ' -std=c99 -fopenmp']; + else + cflags_tmp = [cflags_tmp, ' -std=c99']; + end + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'w'); + fprintf(input_file, '%s', cflags_tmp); + fclose(input_file); + end + % read cflags from file + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'r'); + cflags_tmp = fscanf(input_file, '%[^\n]s'); + fclose(input_file); + setenv('CFLAGS', cflags_tmp); + end + + %% compile mex + for ii=1:length(mex_files) + disp(['compiling ', mex_files{ii}]) + if is_octave() + % mkoctfile -p CFLAGS + mex(acados_include, template_lib_include, external_include, blasfeo_include, hpipm_include,... + acados_lib_path, template_lib_path, '-lacados', '-lhpipm', '-lblasfeo', mex_files{ii}) + else + if ismac() + FLAGS = 'CFLAGS=$CFLAGS -std=c99'; + else + FLAGS = 'CFLAGS=$CFLAGS -std=c99 -fopenmp'; + end + mex(FLAGS, acados_include, template_lib_include, external_include, blasfeo_include, hpipm_include,... + acados_lib_path, template_lib_path, '-lacados', '-lhpipm', '-lblasfeo', mex_files{ii}) + end + end + + +end \ No newline at end of file diff --git a/pyextra/acados_template/c_templates_tera/make_mex.in.m b/pyextra/acados_template/c_templates_tera/make_mex.in.m new file mode 100644 index 0000000000..cde30f6f41 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/make_mex.in.m @@ -0,0 +1,110 @@ +% +% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +% +% This file is part of acados. +% +% The 2-Clause BSD License +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% 1. Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE.; +% + +function make_mex_{{ model.name }}() + + opts.output_dir = pwd; + + % get acados folder + acados_folder = getenv('ACADOS_INSTALL_DIR'); + + % set paths + acados_include = ['-I' fullfile(acados_folder, 'include')]; + template_lib_include = ['-l' 'acados_ocp_solver_{{ model.name }}']; + template_lib_path = ['-L' fullfile(pwd)]; + + acados_lib_path = ['-L' fullfile(acados_folder, 'lib')]; + external_include = ['-I', fullfile(acados_folder, 'external')]; + blasfeo_include = ['-I', fullfile(acados_folder, 'external', 'blasfeo', 'include')]; + hpipm_include = ['-I', fullfile(acados_folder, 'external', 'hpipm', 'include')]; + + mex_include = ['-I', fullfile(acados_folder, 'interfaces', 'acados_matlab_octave')]; + + mex_names = { ... + 'acados_mex_create_{{ model.name }}' ... + 'acados_mex_free_{{ model.name }}' ... + 'acados_mex_solve_{{ model.name }}' ... + 'acados_mex_set_{{ model.name }}' ... + }; + + mex_files = cell(length(mex_names), 1); + for k=1:length(mex_names) + mex_files{k} = fullfile([mex_names{k}, '.c']); + end + + %% octave C flags + if is_octave() + if ~exist(fullfile(opts.output_dir, 'cflags_octave.txt'), 'file') + diary(fullfile(opts.output_dir, 'cflags_octave.txt')); + diary on + mkoctfile -p CFLAGS + diary off + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'r'); + cflags_tmp = fscanf(input_file, '%[^\n]s'); + fclose(input_file); + if ~ismac() + cflags_tmp = [cflags_tmp, ' -std=c99 -fopenmp']; + else + cflags_tmp = [cflags_tmp, ' -std=c99']; + end + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'w'); + fprintf(input_file, '%s', cflags_tmp); + fclose(input_file); + end + % read cflags from file + input_file = fopen(fullfile(opts.output_dir, 'cflags_octave.txt'), 'r'); + cflags_tmp = fscanf(input_file, '%[^\n]s'); + fclose(input_file); + setenv('CFLAGS', cflags_tmp); + end + + %% compile mex + for ii=1:length(mex_files) + disp(['compiling ', mex_files{ii}]) + if is_octave() + % mkoctfile -p CFLAGS + mex(acados_include, template_lib_include, external_include, blasfeo_include, hpipm_include,... + acados_lib_path, template_lib_path, mex_include, '-lacados', '-lhpipm', '-lblasfeo', mex_files{ii}) + else + if ismac() + FLAGS = 'CFLAGS=$CFLAGS -std=c99'; + else + FLAGS = 'CFLAGS=$CFLAGS -std=c99 -fopenmp'; + end + mex(FLAGS, acados_include, template_lib_include, external_include, blasfeo_include, hpipm_include,... + acados_lib_path, template_lib_path, mex_include, '-lacados', '-lhpipm', '-lblasfeo', mex_files{ii}) + end + end + + +end \ No newline at end of file diff --git a/pyextra/acados_template/c_templates_tera/make_sfun.in.m b/pyextra/acados_template/c_templates_tera/make_sfun.in.m new file mode 100644 index 0000000000..3299ceb39d --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/make_sfun.in.m @@ -0,0 +1,320 @@ +% +% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +% +% This file is part of acados. +% +% The 2-Clause BSD License +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% 1. Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE.; +% + +SOURCES = { ... + {%- if solver_options.integrator_type == 'ERK' %} + '{{ model.name }}_model/{{ model.name }}_expl_ode_fun.c', ... + '{{ model.name }}_model/{{ model.name }}_expl_vde_forw.c',... + {%- if solver_options.hessian_approx == 'EXACT' %} + '{{ model.name }}_model/{{ model.name }}_expl_ode_hess.c',... + {%- endif %} + {%- elif solver_options.integrator_type == "IRK" %} + '{{ model.name }}_model/{{ model.name }}_impl_dae_fun.c', ... + '{{ model.name }}_model/{{ model.name }}_impl_dae_fun_jac_x_xdot_z.c', ... + '{{ model.name }}_model/{{ model.name }}_impl_dae_jac_x_xdot_u_z.c', ... + {%- if solver_options.hessian_approx == 'EXACT' %} + '{{ model.name }}_model/{{ model.name }}_impl_dae_hess.c',... + {%- endif %} + {%- elif solver_options.integrator_type == "GNSF" %} + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun.c',... + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun_jac_y.c',... + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_jac_y_uhat.c',... + '{{ model.name }}_model/{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz.c',... + '{{ model.name }}_model/{{ model.name }}_gnsf_get_matrices_fun.c',... + {%- elif solver_options.integrator_type == "DISCRETE" %} + '{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun.c',... + '{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac.c',... + {%- if solver_options.hessian_approx == "EXACT" %} + '{{ model.name }}_model/{{ model.name }}_dyn_disc_phi_fun_jac_hess.c',... + {%- endif %} + {%- endif %} + {%- if cost.cost_type_0 == "NONLINEAR_LS" %} + '{{ model.name }}_cost/{{ model.name }}_cost_y_0_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_0_fun_jac_ut_xt.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_0_hess.c',... + {%- elif cost.cost_type_0 == "EXTERNAL" %} + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_0_fun_jac_hess.c',... + {%- endif %} + + {%- if cost.cost_type == "NONLINEAR_LS" %} + '{{ model.name }}_cost/{{ model.name }}_cost_y_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_fun_jac_ut_xt.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_hess.c',... + {%- elif cost.cost_type == "EXTERNAL" %} + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_fun_jac_hess.c',... + {%- endif %} + {%- if cost.cost_type_e == "NONLINEAR_LS" %} + '{{ model.name }}_cost/{{ model.name }}_cost_y_e_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_e_fun_jac_ut_xt.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_y_e_hess.c',... + {%- elif cost.cost_type_e == "EXTERNAL" %} + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac.c',... + '{{ model.name }}_cost/{{ model.name }}_cost_ext_cost_e_fun_jac_hess.c',... + {%- endif %} + {%- if constraints.constr_type == "BGH" and dims.nh > 0 %} + '{{ model.name }}_constraints/{{ model.name }}_constr_h_fun.c', ... + '{{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt_hess.c', ... + '{{ model.name }}_constraints/{{ model.name }}_constr_h_fun_jac_uxt_zt.c', ... + {%- elif constraints.constr_type == "BGP" and dims.nphi > 0 %} + '{{ model.name }}_constraints/{{ model.name }}_phi_constraint.c', ... + {%- endif %} + {%- if constraints.constr_type_e == "BGH" and dims.nh_e > 0 %} + '{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun.c', ... + '{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt_hess.c', ... + '{{ model.name }}_constraints/{{ model.name }}_constr_h_e_fun_jac_uxt_zt.c', ... + {%- elif constraints.constr_type_e == "BGP" and dims.nphi_e > 0 %} + '{{ model.name }}_constraints/{{ model.name }}_phi_e_constraint.c', ... + {%- endif %} + 'acados_solver_sfunction_{{ model.name }}.c', ... + 'acados_solver_{{ model.name }}.c' + }; + +INC_PATH = '{{ acados_include_path }}'; + +INCS = {['-I', fullfile(INC_PATH, 'blasfeo', 'include')], ... + ['-I', fullfile(INC_PATH, 'hpipm', 'include')], ... + ['-I', fullfile(INC_PATH, 'acados')], ... + ['-I', fullfile(INC_PATH)]}; + +{% if solver_options.qp_solver is containing("QPOASES") %} +INCS{end+1} = ['-I', fullfile(INC_PATH, 'qpOASES_e')]; +{% endif %} + +CFLAGS = 'CFLAGS=$CFLAGS'; +LDFLAGS = 'LDFLAGS=$LDFLAGS'; +COMPFLAGS = 'COMPFLAGS=$COMPFLAGS'; +COMPDEFINES = 'COMPDEFINES=$COMPDEFINES'; + +{% if solver_options.qp_solver is containing("QPOASES") %} +CFLAGS = [ CFLAGS, ' -DACADOS_WITH_QPOASES ' ]; +COMPDEFINES = [ COMPDEFINES, ' -DACADOS_WITH_QPOASES ' ]; +{%- elif solver_options.qp_solver is containing("OSQP") %} +CFLAGS = [ CFLAGS, ' -DACADOS_WITH_OSQP ' ]; +COMPDEFINES = [ COMPDEFINES, ' -DACADOS_WITH_OSQP ' ]; +{%- elif solver_options.qp_solver is containing("QPDUNES") %} +CFLAGS = [ CFLAGS, ' -DACADOS_WITH_QPDUNES ' ]; +COMPDEFINES = [ COMPDEFINES, ' -DACADOS_WITH_QPDUNES ' ]; +{%- elif solver_options.qp_solver is containing("HPMPC") %} +CFLAGS = [ CFLAGS, ' -DACADOS_WITH_HPMPC ' ]; +COMPDEFINES = [ COMPDEFINES, ' -DACADOS_WITH_HPMPC ' ]; +{% endif %} + +LIB_PATH = ['-L', fullfile('{{ acados_lib_path }}')]; + +LIBS = {'-lacados', '-lhpipm', '-lblasfeo'}; + +% acados linking libraries and flags +{%- if acados_link_libs and os and os == "pc" %} +LDFLAGS = [LDFLAGS ' {{ acados_link_libs.openmp }}']; +COMPFLAGS = [COMPFLAGS ' {{ acados_link_libs.openmp }}']; +LIBS{end+1} = '{{ acados_link_libs.qpoases }}'; +LIBS{end+1} = '{{ acados_link_libs.hpmpc }}'; +LIBS{end+1} = '{{ acados_link_libs.osqp }}'; +{%- else %} + {% if solver_options.qp_solver is containing("QPOASES") %} +LIBS{end+1} = '-lqpOASES_e'; + {% endif %} +{%- endif %} + +mex('-v', '-O', CFLAGS, LDFLAGS, COMPFLAGS, COMPDEFINES, INCS{:}, ... + LIB_PATH, LIBS{:}, SOURCES{:}, ... + '-output', 'acados_solver_sfunction_{{ model.name }}' ); + +fprintf( [ '\n\nSuccessfully created sfunction:\nacados_solver_sfunction_{{ model.name }}', '.', ... + eval('mexext')] ); + + +%% print note on usage of s-function +fprintf('\n\nNote: Usage of Sfunction is as follows:\n') +input_note = 'Inputs are:\n'; +i_in = 1; + + +{%- if dims.nbx_0 > 0 and simulink_opts.inputs.lbx_0 -%} {#- lbx_0 #} +input_note = strcat(input_note, num2str(i_in), ') lbx_0 - lower bound on x for stage 0,',... + ' size [{{ dims.nbx_0 }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.nbx_0 > 0 and simulink_opts.inputs.ubx_0 -%} {#- ubx_0 #} +input_note = strcat(input_note, num2str(i_in), ') ubx_0 - upper bound on x for stage 0,',... + ' size [{{ dims.nbx_0 }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.np > 0 and simulink_opts.inputs.parameter_traj -%} {#- parameter_traj #} +input_note = strcat(input_note, num2str(i_in), ') parameters - concatenated for all shooting nodes 0 to N+1,',... + ' size [{{ (dims.N+1)*dims.np }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny_0 > 0 and simulink_opts.inputs.y_ref_0 %} +input_note = strcat(input_note, num2str(i_in), ') y_ref_0, size [{{ dims.ny_0 }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny > 0 and dims.N > 1 and simulink_opts.inputs.y_ref %} +input_note = strcat(input_note, num2str(i_in), ') y_ref - concatenated for shooting nodes 1 to N-1,',... + ' size [{{ (dims.N-1) * dims.ny }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny_e > 0 and dims.N > 0 and simulink_opts.inputs.y_ref_e %} +input_note = strcat(input_note, num2str(i_in), ') y_ref_e, size [{{ dims.ny_e }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.lbx -%} {#- lbx #} +input_note = strcat(input_note, num2str(i_in), ') lbx for shooting nodes 1 to N-1, size [{{ (dims.N-1) * dims.nbx }}]\n '); +i_in = i_in + 1; +{%- endif %} +{%- if dims.nbx > 0 and dims.N > 1 and simulink_opts.inputs.ubx -%} {#- ubx #} +input_note = strcat(input_note, num2str(i_in), ') ubx for shooting nodes 1 to N-1, size [{{ (dims.N-1) * dims.nbx }}]\n '); +i_in = i_in + 1; +{%- endif %} + + +{%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.lbx_e -%} {#- lbx_e #} +input_note = strcat(input_note, num2str(i_in), ') lbx_e (lbx at shooting node N), size [{{ dims.nbx_e }}]\n '); +i_in = i_in + 1; +{%- endif %} +{%- if dims.nbx_e > 0 and dims.N > 0 and simulink_opts.inputs.ubx_e -%} {#- ubx_e #} +input_note = strcat(input_note, num2str(i_in), ') ubx_e (ubx at shooting node N), size [{{ dims.nbx_e }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.lbu -%} {#- lbu #} +input_note = strcat(input_note, num2str(i_in), ') lbu for shooting nodes 0 to N-1, size [{{ dims.N*dims.nbu }}]\n '); +i_in = i_in + 1; +{%- endif -%} +{%- if dims.nbu > 0 and dims.N > 0 and simulink_opts.inputs.ubu -%} {#- ubu #} +input_note = strcat(input_note, num2str(i_in), ') ubu for shooting nodes 0 to N-1, size [{{ dims.N*dims.nbu }}]\n '); +i_in = i_in + 1; +{%- endif -%} + +{%- if dims.ng > 0 and simulink_opts.inputs.lg -%} {#- lg #} +input_note = strcat(input_note, num2str(i_in), ') lg, size [{{ dims.ng }}]\n '); +i_in = i_in + 1; +{%- endif %} +{%- if dims.ng > 0 and simulink_opts.inputs.ug -%} {#- ug #} +input_note = strcat(input_note, num2str(i_in), ') ug, size [{{ dims.ng }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.nh > 0 and simulink_opts.inputs.lh -%} {#- lh #} +input_note = strcat(input_note, num2str(i_in), ') lh, size [{{ dims.nh }}]\n '); +i_in = i_in + 1; +{%- endif %} +{%- if dims.nh > 0 and simulink_opts.inputs.uh -%} {#- uh #} +input_note = strcat(input_note, num2str(i_in), ') uh, size [{{ dims.nh }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny_0 > 0 and simulink_opts.inputs.cost_W_0 %} {#- cost_W_0 #} +input_note = strcat(input_note, num2str(i_in), ') cost_W_0 in column-major format, size [{{ dims.ny_0 * dims.ny_0 }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny > 0 and simulink_opts.inputs.cost_W %} {#- cost_W #} +input_note = strcat(input_note, num2str(i_in), ') cost_W in column-major format, that is set for all intermediate shooting nodes: 1 to N-1, size [{{ dims.ny * dims.ny }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.ny_e > 0 and simulink_opts.inputs.cost_W_e %} {#- cost_W_e #} +input_note = strcat(input_note, num2str(i_in), ') cost_W_e in column-major format, size [{{ dims.ny_e * dims.ny_e }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if simulink_opts.inputs.x_init %} {#- x_init #} +input_note = strcat(input_note, num2str(i_in), ') initialization of x for all shooting nodes, size [{{ dims.nx * (dims.N+1) }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if simulink_opts.inputs.u_init %} {#- u_init #} +input_note = strcat(input_note, num2str(i_in), ') initialization of u for shooting nodes 0 to N-1, size [{{ dims.nu * (dims.N) }}]\n '); +i_in = i_in + 1; +{%- endif %} + +fprintf(input_note) + +disp(' ') + +output_note = 'Outputs are:\n'; +i_out = 0; + +{%- if dims.nu > 0 and simulink_opts.outputs.u0 == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') u0, control input at node 0, size [{{ dims.nu }}]\n '); +{%- endif %} + +{%- if simulink_opts.outputs.utraj == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') utraj, control input concatenated for nodes 0 to N-1, size [{{ dims.nu * dims.N }}]\n '); +{%- endif %} + +{%- if simulink_opts.outputs.xtraj == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') xtraj, state concatenated for nodes 0 to N, size [{{ dims.nx * (dims.N + 1) }}]\n '); +{%- endif %} + +{%- if simulink_opts.outputs.solver_status == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') acados solver status (0 = SUCCESS)\n '); +{%- endif %} + +{%- if simulink_opts.outputs.KKT_residual == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') KKT residual\n '); +{%- endif %} + +{%- if dims.N > 0 and simulink_opts.outputs.x1 == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') x1, state at node 1\n '); +{%- endif %} + +{%- if simulink_opts.outputs.CPU_time == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') CPU time\n '); +{%- endif %} + +{%- if simulink_opts.outputs.sqp_iter == 1 %} +i_out = i_out + 1; +output_note = strcat(output_note, num2str(i_out), ') SQP iterations\n '); +{%- endif %} + +fprintf(output_note) diff --git a/pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m b/pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m new file mode 100644 index 0000000000..1c5cf0b123 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/make_sfun_sim.in.m @@ -0,0 +1,99 @@ +% +% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +% +% This file is part of acados. +% +% The 2-Clause BSD License +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% 1. Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE.; +% + +SOURCES = [ 'acados_sim_solver_sfunction_{{ model.name }}.c ', ... + 'acados_sim_solver_{{ model.name }}.c ', ... + {%- if solver_options.integrator_type == 'ERK' %} + '{{ model.name }}_model/{{ model.name }}_expl_ode_fun.c ', ... + '{{ model.name }}_model/{{ model.name }}_expl_vde_forw.c ',... + {%- if solver_options.hessian_approx == 'EXACT' %} + '{{ model.name }}_model/{{ model.name }}_expl_ode_hess.c ',... + {%- endif %} + {%- elif solver_options.integrator_type == "IRK" %} + '{{ model.name }}_model/{{ model.name }}_impl_dae_fun.c ', ... + '{{ model.name }}_model/{{ model.name }}_impl_dae_fun_jac_x_xdot_z.c ', ... + '{{ model.name }}_model/{{ model.name }}_impl_dae_jac_x_xdot_u_z.c ', ... + {%- if solver_options.hessian_approx == 'EXACT' %} + '{{ model.name }}_model/{{ model.name }}_impl_dae_hess.c ',... + {%- endif %} + {%- elif solver_options.integrator_type == "GNSF" %} + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun.c ' + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_fun_jac_y.c ' + '{{ model.name }}_model/{{ model.name }}_gnsf_phi_jac_y_uhat.c ' + '{{ model.name }}_model/{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz.c ' + '{{ model.name }}_model/{{ model.name }}_gnsf_get_matrices_fun.c ' + {%- endif %} + ]; + +INC_PATH = '{{ acados_include_path }}'; + +INCS = [ ' -I', fullfile(INC_PATH, 'blasfeo', 'include'), ... + ' -I', fullfile(INC_PATH, 'hpipm', 'include'), ... + ' -I', INC_PATH, ' -I', fullfile(INC_PATH, 'acados'), ' ']; + +CFLAGS = ' -O'; + +LIB_PATH = '{{ acados_lib_path }}'; + +LIBS = '-lacados -lblasfeo -lhpipm'; + +eval( [ 'mex -v -output acados_sim_solver_sfunction_{{ model.name }} ', ... + CFLAGS, INCS, ' ', SOURCES, ' -L', LIB_PATH, ' ', LIBS ]); + +fprintf( [ '\n\nSuccessfully created sfunction:\nacados_sim_solver_sfunction_{{ model.name }}', '.', ... + eval('mexext')] ); + + +%% print note on usage of s-function +fprintf('\n\nNote: Usage of Sfunction is as follows:\n') +input_note = 'Inputs are:\n1) x0, initial state, size [{{ dims.nx }}]\n '; +i_in = 2; +{%- if dims.nu > 0 %} +input_note = strcat(input_note, num2str(i_in), ') u, size [{{ dims.nu }}]\n '); +i_in = i_in + 1; +{%- endif %} + +{%- if dims.np > 0 %} +input_note = strcat(input_note, num2str(i_in), ') parameters, size [{{ dims.np }}]\n '); +i_in = i_in + 1; +{%- endif %} + + +fprintf(input_note) + +disp(' ') + +output_note = strcat('Outputs are:\n', ... + '1) x1 - simulated state, size [{{ dims.nx }}]\n'); + +fprintf(output_note) diff --git a/pyextra/acados_template/c_templates_tera/mex_solver.in.m b/pyextra/acados_template/c_templates_tera/mex_solver.in.m new file mode 100644 index 0000000000..728741a46e --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/mex_solver.in.m @@ -0,0 +1,166 @@ +% +% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +% +% This file is part of acados. +% +% The 2-Clause BSD License +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are met: +% +% 1. Redistributions of source code must retain the above copyright notice, +% this list of conditions and the following disclaimer. +% +% 2. Redistributions in binary form must reproduce the above copyright notice, +% this list of conditions and the following disclaimer in the documentation +% and/or other materials provided with the distribution. +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE.; +% + +classdef {{ model.name }}_mex_solver < handle + + properties + C_ocp + C_ocp_ext_fun + cost_ext_fun_type + cost_ext_fun_type_e + end % properties + + + + methods + + % constructor + function obj = {{ model.name }}_mex_solver() + make_mex_{{ model.name }}(); + [obj.C_ocp, obj.C_ocp_ext_fun] = acados_mex_create_{{ model.name }}(); + % to have path to destructor when changing directory + addpath('.') + obj.cost_ext_fun_type = '{{ cost.cost_ext_fun_type }}'; + obj.cost_ext_fun_type_e = '{{ cost.cost_ext_fun_type_e }}'; + end + + % destructor + function delete(obj) + if ~isempty(obj.C_ocp) + acados_mex_free_{{ model.name }}(obj.C_ocp); + end + end + + % solve + function solve(obj) + acados_mex_solve_{{ model.name }}(obj.C_ocp); + end + + % set -- borrowed from MEX interface + function set(varargin) + obj = varargin{1}; + field = varargin{2}; + value = varargin{3}; + if ~isa(field, 'char') + error('field must be a char vector, use '' '''); + end + if nargin==3 + acados_mex_set_{{ model.name }}(obj.cost_ext_fun_type, obj.cost_ext_fun_type_e, obj.C_ocp, obj.C_ocp_ext_fun, field, value); + elseif nargin==4 + stage = varargin{4}; + acados_mex_set_{{ model.name }}(obj.cost_ext_fun_type, obj.cost_ext_fun_type_e, obj.C_ocp, obj.C_ocp_ext_fun, field, value, stage); + else + disp('acados_ocp.set: wrong number of input arguments (2 or 3 allowed)'); + end + end + + function value = get_cost(obj) + value = ocp_get_cost(obj.C_ocp); + end + + % get -- borrowed from MEX interface + function value = get(varargin) + % usage: + % obj.get(field, value, [stage]) + obj = varargin{1}; + field = varargin{2}; + if any(strfind('sens', field)) + error('field sens* (sensitivities of optimal solution) not yet supported for templated MEX.') + end + if ~isa(field, 'char') + error('field must be a char vector, use '' '''); + end + + if nargin==2 + value = ocp_get(obj.C_ocp, field); + elseif nargin==3 + stage = varargin{3}; + value = ocp_get(obj.C_ocp, field, stage); + else + disp('acados_ocp.get: wrong number of input arguments (1 or 2 allowed)'); + end + end + + + % print + function print(varargin) + if nargin < 2 + field = 'stat'; + else + field = varargin{2}; + end + + obj = varargin{1}; + + if strcmp(field, 'stat') + stat = obj.get('stat'); + {%- if solver_options.nlp_solver_type == "SQP" %} + fprintf('\niter\tres_stat\tres_eq\t\tres_ineq\tres_comp\tqp_stat\tqp_iter'); + if size(stat,2)>7 + fprintf('\tqp_res_stat\tqp_res_eq\tqp_res_ineq\tqp_res_comp'); + end + fprintf('\n'); + for jj=1:size(stat,1) + fprintf('%d\t%e\t%e\t%e\t%e\t%d\t%d', stat(jj,1), stat(jj,2), stat(jj,3), stat(jj,4), stat(jj,5), stat(jj,6), stat(jj,7)); + if size(stat,2)>7 + fprintf('\t%e\t%e\t%e\t%e', stat(jj,8), stat(jj,9), stat(jj,10), stat(jj,11)); + end + fprintf('\n'); + end + fprintf('\n'); + {%- else %} + fprintf('\niter\tqp_status\tqp_iter'); + if size(stat,2)>3 + fprintf('\tqp_res_stat\tqp_res_eq\tqp_res_ineq\tqp_res_comp'); + end + fprintf('\n'); + for jj=1:size(stat,1) + fprintf('%d\t%d\t\t%d', stat(jj,1), stat(jj,2), stat(jj,3)); + if size(stat,2)>3 + fprintf('\t%e\t%e\t%e\t%e', stat(jj,4), stat(jj,5), stat(jj,6), stat(jj,7)); + end + fprintf('\n'); + end + {% endif %} + + else + fprintf('unsupported field in function print of acados_ocp.print, got %s', field); + keyboard + end + + end + + end % methods + +end % class + diff --git a/pyextra/acados_template/c_templates_tera/model.in.h b/pyextra/acados_template/c_templates_tera/model.in.h new file mode 100644 index 0000000000..fd8871c8b8 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/model.in.h @@ -0,0 +1,209 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#ifndef {{ model.name }}_MODEL +#define {{ model.name }}_MODEL + +#ifdef __cplusplus +extern "C" { +#endif + +{%- if solver_options.hessian_approx %} + {%- set hessian_approx = solver_options.hessian_approx %} +{%- elif solver_options.sens_hess %} + {%- set hessian_approx = "EXACT" %} +{%- else %} + {%- set hessian_approx = "GAUSS_NEWTON" %} +{%- endif %} + +{% if solver_options.integrator_type == "IRK" %} +// implicit ODE +int {{ model.name }}_impl_dae_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_impl_dae_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_impl_dae_fun_sparsity_in(int); +const int *{{ model.name }}_impl_dae_fun_sparsity_out(int); +int {{ model.name }}_impl_dae_fun_n_in(); +int {{ model.name }}_impl_dae_fun_n_out(); + +// implicit ODE +int {{ model.name }}_impl_dae_fun_jac_x_xdot_z(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_impl_dae_fun_jac_x_xdot_z_work(int *, int *, int *, int *); +const int *{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_in(int); +const int *{{ model.name }}_impl_dae_fun_jac_x_xdot_z_sparsity_out(int); +int {{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_in(); +int {{ model.name }}_impl_dae_fun_jac_x_xdot_z_n_out(); + +// implicit ODE +int {{ model.name }}_impl_dae_jac_x_xdot_u_z(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_impl_dae_jac_x_xdot_u_z_work(int *, int *, int *, int *); +const int *{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_in(int); +const int *{{ model.name }}_impl_dae_jac_x_xdot_u_z_sparsity_out(int); +int {{ model.name }}_impl_dae_jac_x_xdot_u_z_n_in(); +int {{ model.name }}_impl_dae_jac_x_xdot_u_z_n_out(); + +// // implicit ODE - for lifted_irk +// int {{ model.name }}_impl_dae_fun_jac_x_xdot_u(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +// int {{ model.name }}_impl_dae_fun_jac_x_xdot_u_work(int *, int *, int *, int *); +// const int *{{ model.name }}_impl_dae_fun_jac_x_xdot_u_sparsity_in(int); +// const int *{{ model.name }}_impl_dae_fun_jac_x_xdot_u_sparsity_out(int); +// int {{ model.name }}_impl_dae_fun_jac_x_xdot_u_n_in(); +// int {{ model.name }}_impl_dae_fun_jac_x_xdot_u_n_out(); + +{%- if hessian_approx == "EXACT" %} +int {{ model.name }}_impl_dae_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_impl_dae_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_impl_dae_hess_sparsity_in(int); +const int *{{ model.name }}_impl_dae_hess_sparsity_out(int); +int {{ model.name }}_impl_dae_hess_n_in(); +int {{ model.name }}_impl_dae_hess_n_out(); +{%- endif %} + +{% elif solver_options.integrator_type == "GNSF" %} +/* GNSF Functions */ +// used to import model matrices +int {{ model.name }}_gnsf_get_matrices_fun(const double** arg, double** res, int* iw, double* w, void *mem); +int {{ model.name }}_gnsf_get_matrices_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_gnsf_get_matrices_fun_sparsity_in(int); +const int *{{ model.name }}_gnsf_get_matrices_fun_sparsity_out(int); +int {{ model.name }}_gnsf_get_matrices_fun_n_in(); +int {{ model.name }}_gnsf_get_matrices_fun_n_out(); + +// phi_fun +int {{ model.name }}_gnsf_phi_fun(const double** arg, double** res, int* iw, double* w, void *mem); +int {{ model.name }}_gnsf_phi_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_gnsf_phi_fun_sparsity_in(int); +const int *{{ model.name }}_gnsf_phi_fun_sparsity_out(int); +int {{ model.name }}_gnsf_phi_fun_n_in(); +int {{ model.name }}_gnsf_phi_fun_n_out(); + +// phi_fun_jac_y +int {{ model.name }}_gnsf_phi_fun_jac_y(const double** arg, double** res, int* iw, double* w, void *mem); +int {{ model.name }}_gnsf_phi_fun_jac_y_work(int *, int *, int *, int *); +const int *{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_in(int); +const int *{{ model.name }}_gnsf_phi_fun_jac_y_sparsity_out(int); +int {{ model.name }}_gnsf_phi_fun_jac_y_n_in(); +int {{ model.name }}_gnsf_phi_fun_jac_y_n_out(); + +// phi_jac_y_uhat +int {{ model.name }}_gnsf_phi_jac_y_uhat(const double** arg, double** res, int* iw, double* w, void *mem); +int {{ model.name }}_gnsf_phi_jac_y_uhat_work(int *, int *, int *, int *); +const int *{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_in(int); +const int *{{ model.name }}_gnsf_phi_jac_y_uhat_sparsity_out(int); +int {{ model.name }}_gnsf_phi_jac_y_uhat_n_in(); +int {{ model.name }}_gnsf_phi_jac_y_uhat_n_out(); + +// f_lo_fun_jac_x1k1uz +int {{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz(const double** arg, double** res, int* iw, double* w, void *mem); +int {{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_work(int *, int *, int *, int *); +const int *{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_in(int); +const int *{{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_sparsity_out(int); +int {{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_in(); +int {{ model.name }}_gnsf_f_lo_fun_jac_x1k1uz_n_out(); + +{% elif solver_options.integrator_type == "ERK" %} +/* explicit ODE */ + +// explicit ODE +int {{ model.name }}_expl_ode_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_expl_ode_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_expl_ode_fun_sparsity_in(int); +const int *{{ model.name }}_expl_ode_fun_sparsity_out(int); +int {{ model.name }}_expl_ode_fun_n_in(); +int {{ model.name }}_expl_ode_fun_n_out(); + +// explicit forward VDE +int {{ model.name }}_expl_vde_forw(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_expl_vde_forw_work(int *, int *, int *, int *); +const int *{{ model.name }}_expl_vde_forw_sparsity_in(int); +const int *{{ model.name }}_expl_vde_forw_sparsity_out(int); +int {{ model.name }}_expl_vde_forw_n_in(); +int {{ model.name }}_expl_vde_forw_n_out(); + +// explicit adjoint VDE +int {{ model.name }}_expl_vde_adj(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_expl_vde_adj_work(int *, int *, int *, int *); +const int *{{ model.name }}_expl_vde_adj_sparsity_in(int); +const int *{{ model.name }}_expl_vde_adj_sparsity_out(int); +int {{ model.name }}_expl_vde_adj_n_in(); +int {{ model.name }}_expl_vde_adj_n_out(); + +{%- if hessian_approx == "EXACT" %} +int {{ model.name }}_expl_ode_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_expl_ode_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_expl_ode_hess_sparsity_in(int); +const int *{{ model.name }}_expl_ode_hess_sparsity_out(int); +int {{ model.name }}_expl_ode_hess_n_in(); +int {{ model.name }}_expl_ode_hess_n_out(); +{%- endif %} + +{% elif solver_options.integrator_type == "DISCRETE" %} + +{% if model.dyn_ext_fun_type == "casadi" %} +int {{ model.name }}_dyn_disc_phi_fun(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_dyn_disc_phi_fun_work(int *, int *, int *, int *); +const int *{{ model.name }}_dyn_disc_phi_fun_sparsity_in(int); +const int *{{ model.name }}_dyn_disc_phi_fun_sparsity_out(int); +int {{ model.name }}_dyn_disc_phi_fun_n_in(); +int {{ model.name }}_dyn_disc_phi_fun_n_out(); + +int {{ model.name }}_dyn_disc_phi_fun_jac(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_dyn_disc_phi_fun_jac_work(int *, int *, int *, int *); +const int *{{ model.name }}_dyn_disc_phi_fun_jac_sparsity_in(int); +const int *{{ model.name }}_dyn_disc_phi_fun_jac_sparsity_out(int); +int {{ model.name }}_dyn_disc_phi_fun_jac_n_in(); +int {{ model.name }}_dyn_disc_phi_fun_jac_n_out(); + +{%- if hessian_approx == "EXACT" %} +int {{ model.name }}_dyn_disc_phi_fun_jac_hess(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_dyn_disc_phi_fun_jac_hess_work(int *, int *, int *, int *); +const int *{{ model.name }}_dyn_disc_phi_fun_jac_hess_sparsity_in(int); +const int *{{ model.name }}_dyn_disc_phi_fun_jac_hess_sparsity_out(int); +int {{ model.name }}_dyn_disc_phi_fun_jac_hess_n_in(); +int {{ model.name }}_dyn_disc_phi_fun_jac_hess_n_out(); +{%- endif %} +{% else %} + {%- if hessian_approx == "EXACT" %} +int {{ model.dyn_disc_fun_jac_hess }}(void **, void **, void *); + {% endif %} +int {{ model.dyn_disc_fun_jac }}(void **, void **, void *); +int {{ model.dyn_disc_fun }}(void **, void **, void *); +{% endif %} + + +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_MODEL diff --git a/pyextra/acados_template/c_templates_tera/phi_constraint.in.h b/pyextra/acados_template/c_templates_tera/phi_constraint.in.h new file mode 100644 index 0000000000..e1c15938b0 --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/phi_constraint.in.h @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, + * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, + * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, + * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl + * + * This file is part of acados. + * + * The 2-Clause BSD License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE.; + */ + +#ifndef {{ model.name }}_PHI_CONSTRAINT +#define {{ model.name }}_PHI_CONSTRAINT + +#ifdef __cplusplus +extern "C" { +#endif + +{% if dims.nphi > 0 %} +// implicit ODE +int {{ model.name }}_phi_constraint(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_phi_constraint_work(int *, int *, int *, int *); +const int *{{ model.name }}_phi_constraint_sparsity_in(int); +const int *{{ model.name }}_phi_constraint_sparsity_out(int); +int {{ model.name }}_phi_constraint_n_in(); +int {{ model.name }}_phi_constraint_n_out(); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_PHI_CONSTRAINT diff --git a/pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h b/pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h new file mode 100644 index 0000000000..a8fc09475d --- /dev/null +++ b/pyextra/acados_template/c_templates_tera/phi_e_constraint.in.h @@ -0,0 +1,21 @@ +#ifndef {{ model.name }}_PHI_E_CONSTRAINT +#define {{ model.name }}_PHI_E_CONSTRAINT + +#ifdef __cplusplus +extern "C" { +#endif + +{% if dims.nphi_e > 0 %} +int {{ model.name }}_phi_e_constraint(const real_t** arg, real_t** res, int* iw, real_t* w, void *mem); +int {{ model.name }}_phi_e_constraint_work(int *, int *, int *, int *); +const int *{{ model.name }}_phi_e_constraint_sparsity_in(int); +const int *{{ model.name }}_phi_e_constraint_sparsity_out(int); +int {{ model.name }}_phi_e_constraint_n_in(); +int {{ model.name }}_phi_e_constraint_n_out(); +{% endif %} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // {{ model.name }}_PHI_E_CONSTRAINT diff --git a/pyextra/acados_template/generate_c_code_constraint.py b/pyextra/acados_template/generate_c_code_constraint.py new file mode 100644 index 0000000000..93e919c56a --- /dev/null +++ b/pyextra/acados_template/generate_c_code_constraint.py @@ -0,0 +1,180 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, is_empty, casadi_length, casadi_version_warning + +def generate_c_code_constraint( model, con_name, is_terminal, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + # load constraint variables and expression + x = model.x + p = model.p + + if isinstance(x, casadi.MX): + symbol = MX.sym + else: + symbol = SX.sym + + if is_terminal: + con_h_expr = model.con_h_expr_e + con_phi_expr = model.con_phi_expr_e + # create dummy u, z + u = symbol('u', 0, 0) + z = symbol('z', 0, 0) + else: + con_h_expr = model.con_h_expr + con_phi_expr = model.con_phi_expr + u = model.u + z = model.z + + if (not is_empty(con_h_expr)) and (not is_empty(con_phi_expr)): + raise Exception("acados: you can either have constraint_h, or constraint_phi, not both.") + + if not (is_empty(con_h_expr) and is_empty(con_phi_expr)): + if is_empty(con_h_expr): + constr_type = 'BGP' + else: + constr_type = 'BGH' + + if is_empty(p): + p = symbol('p', 0, 0) + + if is_empty(z): + z = symbol('z', 0, 0) + + if not (is_empty(con_h_expr)) and opts['generate_hess']: + # multipliers for hessian + nh = casadi_length(con_h_expr) + lam_h = symbol('lam_h', nh, 1) + + # set up & change directory + code_export_dir = opts["code_export_directory"] + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + gen_dir = con_name + '_constraints' + if not os.path.exists(gen_dir): + os.mkdir(gen_dir) + gen_dir_location = './' + gen_dir + os.chdir(gen_dir_location) + + # export casadi functions + if constr_type == 'BGH': + if is_terminal: + fun_name = con_name + '_constr_h_e_fun_jac_uxt_zt' + else: + fun_name = con_name + '_constr_h_fun_jac_uxt_zt' + + jac_ux_t = transpose(jacobian(con_h_expr, vertcat(u,x))) + jac_z_t = jacobian(con_h_expr, z) + constraint_fun_jac_tran = Function(fun_name, [x, u, z, p], \ + [con_h_expr, jac_ux_t, jac_z_t]) + + constraint_fun_jac_tran.generate(fun_name, casadi_opts) + if opts['generate_hess']: + + if is_terminal: + fun_name = con_name + '_constr_h_e_fun_jac_uxt_zt_hess' + else: + fun_name = con_name + '_constr_h_fun_jac_uxt_zt_hess' + + # adjoint + adj_ux = jtimes(con_h_expr, vertcat(u, x), lam_h, True) + # hessian + hess_ux = jacobian(adj_ux, vertcat(u, x)) + + adj_z = jtimes(con_h_expr, z, lam_h, True) + hess_z = jacobian(adj_z, z) + + # set up functions + constraint_fun_jac_tran_hess = \ + Function(fun_name, [x, u, lam_h, z, p], \ + [con_h_expr, jac_ux_t, hess_ux, jac_z_t, hess_z]) + + # generate C code + constraint_fun_jac_tran_hess.generate(fun_name, casadi_opts) + + if is_terminal: + fun_name = con_name + '_constr_h_e_fun' + else: + fun_name = con_name + '_constr_h_fun' + h_fun = Function(fun_name, [x, u, z, p], [con_h_expr]) + h_fun.generate(fun_name, casadi_opts) + + else: # BGP constraint + if is_terminal: + fun_name = con_name + '_phi_e_constraint' + r = model.con_r_in_phi_e + con_r_expr = model.con_r_expr_e + else: + fun_name = con_name + '_phi_constraint' + r = model.con_r_in_phi + con_r_expr = model.con_r_expr + + nphi = casadi_length(con_phi_expr) + con_phi_expr_x_u_z = substitute(con_phi_expr, r, con_r_expr) + phi_jac_u = jacobian(con_phi_expr_x_u_z, u) + phi_jac_x = jacobian(con_phi_expr_x_u_z, x) + phi_jac_z = jacobian(con_phi_expr_x_u_z, z) + + hess = hessian(con_phi_expr[0], r)[0] + for i in range(1, nphi): + hess = vertcat(hess, hessian(con_phi_expr[i], r)[0]) + + r_jac_u = jacobian(con_r_expr, u) + r_jac_x = jacobian(con_r_expr, x) + + constraint_phi = \ + Function(fun_name, [x, u, z, p], \ + [con_phi_expr_x_u_z, \ + vertcat(transpose(phi_jac_u), \ + transpose(phi_jac_x)), \ + transpose(phi_jac_z), \ + hess, vertcat(transpose(r_jac_u), \ + transpose(r_jac_x))]) + + constraint_phi.generate(fun_name, casadi_opts) + + # change directory back + os.chdir(cwd) + + return diff --git a/pyextra/acados_template/generate_c_code_discrete_dynamics.py b/pyextra/acados_template/generate_c_code_discrete_dynamics.py new file mode 100644 index 0000000000..531b5ed9da --- /dev/null +++ b/pyextra/acados_template/generate_c_code_discrete_dynamics.py @@ -0,0 +1,99 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, casadi_length, casadi_version_warning + +def generate_c_code_discrete_dynamics( model, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + # load model + x = model.x + u = model.u + p = model.p + phi = model.disc_dyn_expr + model_name = model.name + nx = x.size()[0] + + + if isinstance(phi, casadi.MX): + symbol = MX.sym + elif isinstance(phi, casadi.SX): + symbol = SX.sym + else: + Exception("generate_c_code_disc_dyn: disc_dyn_expr must be a CasADi expression, you have type: {}".format(type(phi))) + + # assume nx1 = nx !!! + lam = symbol('lam', nx, 1) + + # generate jacobians + ux = vertcat(u,x) + jac_ux = jacobian(phi, ux) + # generate adjoint + adj_ux = jtimes(phi, ux, lam, True) + # generate hessian + hess_ux = jacobian(adj_ux, ux) + + ## change directory + code_export_dir = opts["code_export_directory"] + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + model_dir = model_name + '_model' + if not os.path.exists(model_dir): + os.mkdir(model_dir) + model_dir_location = './' + model_dir + os.chdir(model_dir_location) + + # set up & generate Functions + fun_name = model_name + '_dyn_disc_phi_fun' + phi_fun = Function(fun_name, [x, u, p], [phi]) + phi_fun.generate(fun_name, casadi_opts) + + fun_name = model_name + '_dyn_disc_phi_fun_jac' + phi_fun_jac_ut_xt = Function(fun_name, [x, u, p], [phi, jac_ux.T]) + phi_fun_jac_ut_xt.generate(fun_name, casadi_opts) + + fun_name = model_name + '_dyn_disc_phi_fun_jac_hess' + phi_fun_jac_ut_xt_hess = Function(fun_name, [x, u, lam, p], [phi, jac_ux.T, hess_ux]) + phi_fun_jac_ut_xt_hess.generate(fun_name, casadi_opts) + + os.chdir(cwd) diff --git a/pyextra/acados_template/generate_c_code_explicit_ode.py b/pyextra/acados_template/generate_c_code_explicit_ode.py new file mode 100644 index 0000000000..fb0c020b0b --- /dev/null +++ b/pyextra/acados_template/generate_c_code_explicit_ode.py @@ -0,0 +1,124 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, is_empty, casadi_version_warning + +def generate_c_code_explicit_ode( model, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + + generate_hess = opts["generate_hess"] + code_export_dir = opts["code_export_directory"] + + # load model + x = model.x + u = model.u + p = model.p + f_expl = model.f_expl_expr + model_name = model.name + + ## get model dimensions + nx = x.size()[0] + nu = u.size()[0] + + if isinstance(f_expl, casadi.MX): + symbol = MX.sym + elif isinstance(f_expl, casadi.SX): + symbol = SX.sym + else: + raise Exception("Invalid type for f_expl! Possible types are 'SX' and 'MX'. Exiting.") + ## set up functions to be exported + Sx = symbol('Sx', nx, nx) + Sp = symbol('Sp', nx, nu) + lambdaX = symbol('lambdaX', nx, 1) + + fun_name = model_name + '_expl_ode_fun' + + ## Set up functions + expl_ode_fun = Function(fun_name, [x, u, p], [f_expl]) + + vdeX = jtimes(f_expl,x,Sx) + vdeP = jacobian(f_expl,u) + jtimes(f_expl,x,Sp) + + fun_name = model_name + '_expl_vde_forw' + + expl_vde_forw = Function(fun_name, [x, Sx, Sp, u, p], [f_expl, vdeX, vdeP]) + + adj = jtimes(f_expl, vertcat(x, u), lambdaX, True) + + fun_name = model_name + '_expl_vde_adj' + expl_vde_adj = Function(fun_name, [x, lambdaX, u, p], [adj]) + + if generate_hess: + S_forw = vertcat(horzcat(Sx, Sp), horzcat(DM.zeros(nu,nx), DM.eye(nu))) + hess = mtimes(transpose(S_forw),jtimes(adj, vertcat(x,u), S_forw)) + hess2 = [] + for j in range(nx+nu): + for i in range(j,nx+nu): + hess2 = vertcat(hess2, hess[i,j]) + + fun_name = model_name + '_expl_ode_hess' + expl_ode_hess = Function(fun_name, [x, Sx, Sp, lambdaX, u, p], [adj, hess2]) + + ## generate C code + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + model_dir = model_name + '_model' + if not os.path.exists(model_dir): + os.mkdir(model_dir) + model_dir_location = './' + model_dir + os.chdir(model_dir_location) + fun_name = model_name + '_expl_ode_fun' + expl_ode_fun.generate(fun_name, casadi_opts) + + fun_name = model_name + '_expl_vde_forw' + expl_vde_forw.generate(fun_name, casadi_opts) + + fun_name = model_name + '_expl_vde_adj' + expl_vde_adj.generate(fun_name, casadi_opts) + + if generate_hess: + fun_name = model_name + '_expl_ode_hess' + expl_ode_hess.generate(fun_name, casadi_opts) + os.chdir(cwd) + + return diff --git a/pyextra/acados_template/generate_c_code_external_cost.py b/pyextra/acados_template/generate_c_code_external_cost.py new file mode 100644 index 0000000000..bc21f85f70 --- /dev/null +++ b/pyextra/acados_template/generate_c_code_external_cost.py @@ -0,0 +1,110 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import SX, MX, Function, transpose, vertcat, horzcat, hessian, CasadiMeta +from .utils import ALLOWED_CASADI_VERSIONS, casadi_version_warning + + +def generate_c_code_external_cost(model, stage_type, opts): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int="int", casadi_real="double") + + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + x = model.x + p = model.p + + if isinstance(x, MX): + symbol = MX.sym + else: + symbol = SX.sym + + if stage_type == 'terminal': + suffix_name = "_cost_ext_cost_e_fun" + suffix_name_hess = "_cost_ext_cost_e_fun_jac_hess" + suffix_name_jac = "_cost_ext_cost_e_fun_jac" + u = symbol("u", 0, 0) + ext_cost = model.cost_expr_ext_cost_e + + elif stage_type == 'path': + suffix_name = "_cost_ext_cost_fun" + suffix_name_hess = "_cost_ext_cost_fun_jac_hess" + suffix_name_jac = "_cost_ext_cost_fun_jac" + u = model.u + ext_cost = model.cost_expr_ext_cost + + elif stage_type == 'initial': + suffix_name = "_cost_ext_cost_0_fun" + suffix_name_hess = "_cost_ext_cost_0_fun_jac_hess" + suffix_name_jac = "_cost_ext_cost_0_fun_jac" + u = model.u + ext_cost = model.cost_expr_ext_cost_0 + + # set up functions to be exported + fun_name = model.name + suffix_name + fun_name_hess = model.name + suffix_name_hess + fun_name_jac = model.name + suffix_name_jac + + # generate expression for full gradient and Hessian + full_hess, grad = hessian(ext_cost, vertcat(u, x)) + + ext_cost_fun = Function(fun_name, [x, u, p], [ext_cost]) + ext_cost_fun_jac_hess = Function( + fun_name_hess, [x, u, p], [ext_cost, grad, full_hess] + ) + ext_cost_fun_jac = Function( + fun_name_jac, [x, u, p], [ext_cost, grad] + ) + + # generate C code + code_export_dir = opts["code_export_directory"] + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + gen_dir = model.name + '_cost' + if not os.path.exists(gen_dir): + os.mkdir(gen_dir) + gen_dir_location = "./" + gen_dir + os.chdir(gen_dir_location) + + ext_cost_fun.generate(fun_name, casadi_opts) + ext_cost_fun_jac_hess.generate(fun_name_hess, casadi_opts) + ext_cost_fun_jac.generate(fun_name_jac, casadi_opts) + + os.chdir(cwd) + return diff --git a/pyextra/acados_template/generate_c_code_gnsf.py b/pyextra/acados_template/generate_c_code_gnsf.py new file mode 100644 index 0000000000..3203cbbcce --- /dev/null +++ b/pyextra/acados_template/generate_c_code_gnsf.py @@ -0,0 +1,131 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, is_empty, casadi_version_warning + +def generate_c_code_gnsf( model, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + model_name = model.name + code_export_dir = opts["code_export_directory"] + + # set up directory + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + model_dir = model_name + '_model' + if not os.path.exists(model_dir): + os.mkdir(model_dir) + model_dir_location = './' + model_dir + os.chdir(model_dir_location) + + # obtain gnsf dimensions + get_matrices_fun = model.get_matrices_fun + phi_fun = model.phi_fun + + size_gnsf_A = get_matrices_fun.size_out(0) + gnsf_nx1 = size_gnsf_A[1] + gnsf_nz1 = size_gnsf_A[0] - size_gnsf_A[1] + gnsf_nuhat = max(phi_fun.size_in(1)) + gnsf_ny = max(phi_fun.size_in(0)) + gnsf_nout = max(phi_fun.size_out(0)) + + # set up expressions + # if the model uses MX because of cost/constraints + # the DAE can be exported as SX -> detect GNSF in Matlab + # -> evaluated SX GNSF functions with MX. + u = model.u + + if isinstance(u, casadi.MX): + symbol = MX.sym + else: + symbol = SX.sym + + y = symbol("y", gnsf_ny, 1) + uhat = symbol("uhat", gnsf_nuhat, 1) + p = model.p + x1 = symbol("gnsf_x1", gnsf_nx1, 1) + x1dot = symbol("gnsf_x1dot", gnsf_nx1, 1) + z1 = symbol("gnsf_z1", gnsf_nz1, 1) + dummy = symbol("gnsf_dummy", 1, 1) + empty_var = symbol("gnsf_empty_var", 0, 0) + + ## generate C code + fun_name = model_name + '_gnsf_phi_fun' + phi_fun_ = Function(fun_name, [y, uhat, p], [phi_fun(y, uhat, p)]) + phi_fun_.generate(fun_name, casadi_opts) + + fun_name = model_name + '_gnsf_phi_fun_jac_y' + phi_fun_jac_y = model.phi_fun_jac_y + phi_fun_jac_y_ = Function(fun_name, [y, uhat, p], phi_fun_jac_y(y, uhat, p)) + phi_fun_jac_y_.generate(fun_name, casadi_opts) + + fun_name = model_name + '_gnsf_phi_jac_y_uhat' + phi_jac_y_uhat = model.phi_jac_y_uhat + phi_jac_y_uhat_ = Function(fun_name, [y, uhat, p], phi_jac_y_uhat(y, uhat, p)) + phi_jac_y_uhat_.generate(fun_name, casadi_opts) + + fun_name = model_name + '_gnsf_f_lo_fun_jac_x1k1uz' + f_lo_fun_jac_x1k1uz = model.f_lo_fun_jac_x1k1uz + f_lo_fun_jac_x1k1uz_eval = f_lo_fun_jac_x1k1uz(x1, x1dot, z1, u, p) + + # avoid codegeneration issue + if not isinstance(f_lo_fun_jac_x1k1uz_eval, tuple) and is_empty(f_lo_fun_jac_x1k1uz_eval): + f_lo_fun_jac_x1k1uz_eval = [empty_var] + + f_lo_fun_jac_x1k1uz_ = Function(fun_name, [x1, x1dot, z1, u, p], + f_lo_fun_jac_x1k1uz_eval) + f_lo_fun_jac_x1k1uz_.generate(fun_name, casadi_opts) + + fun_name = model_name + '_gnsf_get_matrices_fun' + get_matrices_fun_ = Function(fun_name, [dummy], get_matrices_fun(1)) + get_matrices_fun_.generate(fun_name, casadi_opts) + + # remove fields for json dump + del model.phi_fun + del model.phi_fun_jac_y + del model.phi_jac_y_uhat + del model.f_lo_fun_jac_x1k1uz + del model.get_matrices_fun + + os.chdir(cwd) + + return diff --git a/pyextra/acados_template/generate_c_code_implicit_ode.py b/pyextra/acados_template/generate_c_code_implicit_ode.py new file mode 100644 index 0000000000..5ef417c414 --- /dev/null +++ b/pyextra/acados_template/generate_c_code_implicit_ode.py @@ -0,0 +1,136 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, is_empty, casadi_length, casadi_version_warning + +def generate_c_code_implicit_ode( model, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + generate_hess = opts["generate_hess"] + code_export_dir = opts["code_export_directory"] + + ## load model + x = model.x + xdot = model.xdot + u = model.u + z = model.z + p = model.p + f_impl = model.f_impl_expr + model_name = model.name + + ## get model dimensions + nx = casadi_length(x) + nu = casadi_length(u) + nz = casadi_length(z) + + ## generate jacobians + jac_x = jacobian(f_impl, x) + jac_xdot = jacobian(f_impl, xdot) + jac_u = jacobian(f_impl, u) + jac_z = jacobian(f_impl, z) + + ## generate hessian + x_xdot_z_u = vertcat(x, xdot, z, u) + + if isinstance(x, casadi.MX): + symbol = MX.sym + else: + symbol = SX.sym + + multiplier = symbol('multiplier', nx + nz) + + ADJ = jtimes(f_impl, x_xdot_z_u, multiplier, True) + HESS = jacobian(ADJ, x_xdot_z_u) + + ## Set up functions + p = model.p + fun_name = model_name + '_impl_dae_fun' + impl_dae_fun = Function(fun_name, [x, xdot, u, z, p], [f_impl]) + + fun_name = model_name + '_impl_dae_fun_jac_x_xdot_z' + impl_dae_fun_jac_x_xdot_z = Function(fun_name, [x, xdot, u, z, p], [f_impl, jac_x, jac_xdot, jac_z]) + + # fun_name = model_name + '_impl_dae_fun_jac_x_xdot_z' + # impl_dae_fun_jac_x_xdot = Function(fun_name, [x, xdot, u, z, p], [f_impl, jac_x, jac_xdot, jac_z]) + + # fun_name = model_name + '_impl_dae_jac_x_xdot_u' + # impl_dae_jac_x_xdot_u = Function(fun_name, [x, xdot, u, z, p], [jac_x, jac_xdot, jac_u, jac_z]) + + fun_name = model_name + '_impl_dae_fun_jac_x_xdot_u_z' + impl_dae_fun_jac_x_xdot_u_z = Function(fun_name, [x, xdot, u, z, p], [f_impl, jac_x, jac_xdot, jac_u, jac_z]) + + fun_name = model_name + '_impl_dae_fun_jac_x_xdot_u' + impl_dae_fun_jac_x_xdot_u = Function(fun_name, [x, xdot, u, z, p], [f_impl, jac_x, jac_xdot, jac_u]) + + fun_name = model_name + '_impl_dae_jac_x_xdot_u_z' + impl_dae_jac_x_xdot_u_z = Function(fun_name, [x, xdot, u, z, p], [jac_x, jac_xdot, jac_u, jac_z]) + + + fun_name = model_name + '_impl_dae_hess' + impl_dae_hess = Function(fun_name, [x, xdot, u, z, multiplier, p], [HESS]) + + # generate C code + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + model_dir = model_name + '_model' + if not os.path.exists(model_dir): + os.mkdir(model_dir) + model_dir_location = './' + model_dir + os.chdir(model_dir_location) + + fun_name = model_name + '_impl_dae_fun' + impl_dae_fun.generate(fun_name, casadi_opts) + + fun_name = model_name + '_impl_dae_fun_jac_x_xdot_z' + impl_dae_fun_jac_x_xdot_z.generate(fun_name, casadi_opts) + + fun_name = model_name + '_impl_dae_jac_x_xdot_u_z' + impl_dae_jac_x_xdot_u_z.generate(fun_name, casadi_opts) + + fun_name = model_name + '_impl_dae_fun_jac_x_xdot_u_z' + impl_dae_fun_jac_x_xdot_u_z.generate(fun_name, casadi_opts) + + if generate_hess: + fun_name = model_name + '_impl_dae_hess' + impl_dae_hess.generate(fun_name, casadi_opts) + + os.chdir(cwd) diff --git a/pyextra/acados_template/generate_c_code_nls_cost.py b/pyextra/acados_template/generate_c_code_nls_cost.py new file mode 100644 index 0000000000..cdb0bf0019 --- /dev/null +++ b/pyextra/acados_template/generate_c_code_nls_cost.py @@ -0,0 +1,113 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os +from casadi import * +from .utils import ALLOWED_CASADI_VERSIONS, casadi_length, casadi_version_warning + +def generate_c_code_nls_cost( model, cost_name, stage_type, opts ): + + casadi_version = CasadiMeta.version() + casadi_opts = dict(mex=False, casadi_int='int', casadi_real='double') + + if casadi_version not in (ALLOWED_CASADI_VERSIONS): + casadi_version_warning(casadi_version) + + x = model.x + p = model.p + + if isinstance(x, casadi.MX): + symbol = MX.sym + else: + symbol = SX.sym + + if stage_type == 'terminal': + middle_name = '_cost_y_e' + u = symbol('u', 0, 0) + cost_expr = model.cost_y_expr_e + + elif stage_type == 'initial': + middle_name = '_cost_y_0' + u = model.u + cost_expr = model.cost_y_expr_0 + + elif stage_type == 'path': + middle_name = '_cost_y' + u = model.u + cost_expr = model.cost_y_expr + + # set up directory + code_export_dir = opts["code_export_directory"] + if not os.path.exists(code_export_dir): + os.makedirs(code_export_dir) + + cwd = os.getcwd() + os.chdir(code_export_dir) + gen_dir = cost_name + '_cost' + if not os.path.exists(gen_dir): + os.mkdir(gen_dir) + gen_dir_location = './' + gen_dir + os.chdir(gen_dir_location) + + # set up expressions + cost_jac_expr = transpose(jacobian(cost_expr, vertcat(u, x))) + + ny = casadi_length(cost_expr) + + y = symbol('y', ny, 1) + + y_adj = jtimes(cost_expr, vertcat(u, x), y, True) + y_hess = jacobian(y_adj, vertcat(u, x)) + + ## generate C code + suffix_name = '_fun' + fun_name = cost_name + middle_name + suffix_name + y_fun = Function( fun_name, [x, u, p], \ + [ cost_expr ]) + y_fun.generate( fun_name, casadi_opts ) + + suffix_name = '_fun_jac_ut_xt' + fun_name = cost_name + middle_name + suffix_name + y_fun_jac_ut_xt = Function(fun_name, [x, u, p], \ + [ cost_expr, cost_jac_expr ]) + y_fun_jac_ut_xt.generate( fun_name, casadi_opts ) + + suffix_name = '_hess' + fun_name = cost_name + middle_name + suffix_name + y_hess = Function(fun_name, [x, u, y, p], [ y_hess ]) + y_hess.generate( fun_name, casadi_opts ) + + os.chdir(cwd) + + return + diff --git a/pyextra/acados_template/simulink_default_opts.json b/pyextra/acados_template/simulink_default_opts.json new file mode 100644 index 0000000000..16074a4027 --- /dev/null +++ b/pyextra/acados_template/simulink_default_opts.json @@ -0,0 +1,36 @@ +{ + "outputs": { + "u0": 1, + "utraj": 0, + "xtraj": 0, + "solver_status": 1, + "KKT_residual": 1, + "x1": 1, + "CPU_time": 1, + "sqp_iter": 1 + }, + "inputs": { + "lbx_0": 1, + "ubx_0": 1, + "parameter_traj": 1, + "y_ref_0": 1, + "y_ref": 1, + "y_ref_e": 1, + "lbx": 1, + "ubx": 1, + "lbx_e": 1, + "ubx_e": 1, + "lbu": 1, + "ubu": 1, + "lg": 1, + "ug": 1, + "lh": 1, + "uh": 1, + "cost_W_0": 0, + "cost_W": 0, + "cost_W_e": 0, + "x_init": 0, + "u_init": 0 + }, + "samplingtime": "t0" +} diff --git a/pyextra/acados_template/utils.py b/pyextra/acados_template/utils.py new file mode 100644 index 0000000000..b749909aeb --- /dev/null +++ b/pyextra/acados_template/utils.py @@ -0,0 +1,438 @@ +# +# Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, +# Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, +# Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, +# Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl +# +# This file is part of acados. +# +# The 2-Clause BSD License +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE.; +# + +import os, sys, json +import urllib.request +import shutil +import numpy as np +from casadi import SX, MX, DM, Function, CasadiMeta + +ALLOWED_CASADI_VERSIONS = ('3.5.5', '3.5.4', '3.5.3', '3.5.2', '3.5.1', '3.4.5', '3.4.0') + +TERA_VERSION = "0.0.34" + +def get_acados_path(): + ACADOS_PATH = os.environ.get('ACADOS_SOURCE_DIR') + if not ACADOS_PATH: + acados_template_path = os.path.dirname(os.path.abspath(__file__)) + acados_path = os.path.join(acados_template_path, '../../../') + ACADOS_PATH = os.path.realpath(acados_path) + msg = 'Warning: Did not find environment variable ACADOS_SOURCE_DIR, ' + msg += 'guessed ACADOS_PATH to be {}.\n'.format(ACADOS_PATH) + msg += 'Please export ACADOS_SOURCE_DIR to not avoid this warning.' + print(msg) + return ACADOS_PATH + + +def get_tera_exec_path(): + ACADOS_PATH = get_acados_path() + return os.path.join(ACADOS_PATH, 'bin/t_renderer') + + +platform2tera = { + "linux": "linux", + "darwin": "osx", + "win32": "window.exe" +} + + +def casadi_version_warning(casadi_version): + msg = 'Warning: Please note that the following versions of CasADi are ' + msg += 'officially supported: {}.\n '.format(" or ".join(ALLOWED_CASADI_VERSIONS)) + msg += 'If there is an incompatibility with the CasADi generated code, ' + msg += 'please consider changing your CasADi version.\n' + msg += 'Version {} currently in use.'.format(casadi_version) + print(msg) + + +def is_column(x): + if isinstance(x, np.ndarray): + if x.ndim == 1: + return True + elif x.ndim == 2 and x.shape[1] == 1: + return True + else: + return False + elif isinstance(x, (MX, SX, DM)): + if x.shape[1] == 1: + return True + elif x.shape[0] == 0 and x.shape[1] == 0: + return True + else: + return False + elif x == None or x == []: + return False + else: + raise Exception("is_column expects one of the following types: np.ndarray, casadi.MX, casadi.SX." + + " Got: " + str(type(x))) + + +def is_empty(x): + if isinstance(x, (MX, SX, DM)): + return x.is_empty() + elif isinstance(x, np.ndarray): + if np.prod(x.shape) == 0: + return True + else: + return False + elif x == None or x == []: + return True + else: + raise Exception("is_empty expects one of the following types: casadi.MX, casadi.SX, " + + "None, numpy array empty list. Got: " + str(type(x))) + + +def casadi_length(x): + if isinstance(x, (MX, SX, DM)): + return int(np.prod(x.shape)) + else: + raise Exception("casadi_length expects one of the following types: casadi.MX, casadi.SX." + + " Got: " + str(type(x))) + + +def make_model_consistent(model): + x = model.x + xdot = model.xdot + u = model.u + z = model.z + p = model.p + + if isinstance(x, MX): + symbol = MX.sym + elif isinstance(x, SX): + symbol = SX.sym + else: + raise Exception("model.x must be casadi.SX or casadi.MX, got {}".format(type(x))) + + if is_empty(p): + model.p = symbol('p', 0, 0) + + if is_empty(z): + model.z = symbol('z', 0, 0) + + return model + + +def get_tera(): + tera_path = get_tera_exec_path() + acados_path = get_acados_path() + + if os.path.exists(tera_path) and os.access(tera_path, os.X_OK): + return tera_path + + repo_url = "https://github.com/acados/tera_renderer/releases" + url = "{}/download/v{}/t_renderer-v{}-{}".format( + repo_url, TERA_VERSION, TERA_VERSION, platform2tera[sys.platform]) + + manual_install = 'For manual installation follow these instructions:\n' + manual_install += '1 Download binaries from {}\n'.format(url) + manual_install += '2 Copy them in {}/bin\n'.format(acados_path) + manual_install += '3 Strip the version and platform from the binaries: ' + manual_install += 'as t_renderer-v0.0.34-X -> t_renderer)\n' + manual_install += '4 Enable execution privilege on the file "t_renderer" with:\n' + manual_install += '"chmod +x {}"\n\n'.format(tera_path) + + msg = "\n" + msg += 'Tera template render executable not found, ' + msg += 'while looking in path:\n{}\n'.format(tera_path) + msg += 'In order to be able to render the templates, ' + msg += 'you need to download the tera renderer binaries from:\n' + msg += '{}\n\n'.format(repo_url) + msg += 'Do you wish to set up Tera renderer automatically?\n' + msg += 'y/N? (press y to download tera or any key for manual installation)\n' + + if input(msg) == 'y': + print("Dowloading {}".format(url)) + with urllib.request.urlopen(url) as response, open(tera_path, 'wb') as out_file: + shutil.copyfileobj(response, out_file) + print("Successfully downloaded t_renderer.") + os.chmod(tera_path, 0o755) + return tera_path + + msg_cancel = "\nYou cancelled automatic download.\n\n" + msg_cancel += manual_install + msg_cancel += "Once installed re-run your script.\n\n" + print(msg_cancel) + + sys.exit(1) + + +def render_template(in_file, out_file, template_dir, json_path): + cwd = os.getcwd() + if not os.path.exists(template_dir): + os.mkdir(template_dir) + os.chdir(template_dir) + + tera_path = get_tera() + + # setting up loader and environment + acados_path = os.path.dirname(os.path.abspath(__file__)) + + template_glob = acados_path + '/c_templates_tera/*' + acados_template_path = acados_path + '/c_templates_tera' + + # call tera as system cmd + os_cmd = "{tera_path} '{template_glob}' '{in_file}' '{json_path}' '{out_file}'".format( + tera_path=tera_path, + template_glob=template_glob, + json_path=json_path, + in_file=in_file, + out_file=out_file + ) + status = os.system(os_cmd) + if (status != 0): + raise Exception('Rendering of {} failed! Exiting.\n'.format(in_file)) + + os.chdir(cwd) + + +## Conversion functions +def np_array_to_list(np_array): + if isinstance(np_array, (np.ndarray)): + return np_array.tolist() + elif isinstance(np_array, (SX)): + return DM(np_array).full() + elif isinstance(np_array, (DM)): + return np_array.full() + else: + raise(Exception( + "Cannot convert to list type {}".format(type(np_array)) + )) + + +def format_class_dict(d): + """ + removes the __ artifact from class to dict conversion + """ + out = {} + for k, v in d.items(): + if isinstance(v, dict): + v = format_class_dict(v) + + out_key = k.split('__', 1)[-1] + out[k.replace(k, out_key)] = v + return out + + +def acados_class2dict(class_instance): + """ + removes the __ artifact from class to dict conversion + """ + + d = dict(class_instance.__dict__) + out = {} + for k, v in d.items(): + if isinstance(v, dict): + v = format_class_dict(v) + + out_key = k.split('__', 1)[-1] + out[k.replace(k, out_key)] = v + return out + + +def ocp_check_against_layout(ocp_nlp, ocp_dims): + """ + Check dimensions against layout + Parameters + --------- + ocp_nlp : dict + dictionary loaded from JSON to be post-processed. + + ocp_dims : instance of AcadosOcpDims + """ + + # load JSON layout + current_module = sys.modules[__name__] + acados_path = os.path.dirname(current_module.__file__) + with open(acados_path + '/acados_layout.json', 'r') as f: + ocp_nlp_layout = json.load(f) + + ocp_check_against_layout_recursion(ocp_nlp, ocp_dims, ocp_nlp_layout) + return + + +def ocp_check_against_layout_recursion(ocp_nlp, ocp_dims, layout): + + for key, item in ocp_nlp.items(): + + try: + layout_of_key = layout[key] + except KeyError: + raise Exception("ocp_check_against_layout_recursion: field" \ + " '{0}' is not in layout but in OCP description.".format(key)) + + if isinstance(item, dict): + ocp_check_against_layout_recursion(item, ocp_dims, layout_of_key) + + if 'ndarray' in layout_of_key: + if isinstance(item, int) or isinstance(item, float): + item = np.array([item]) + if isinstance(item, (list, np.ndarray)) and (layout_of_key[0] != 'str'): + dim_layout = [] + dim_names = layout_of_key[1] + + for dim_name in dim_names: + dim_layout.append(ocp_dims[dim_name]) + + dims = tuple(dim_layout) + + item = np.array(item) + item_dims = item.shape + if len(item_dims) != len(dims): + raise Exception('Mismatching dimensions for field {0}. ' \ + 'Expected {1} dimensional array, got {2} dimensional array.' \ + .format(key, len(dims), len(item_dims))) + + if np.prod(item_dims) != 0 or np.prod(dims) != 0: + if dims != item_dims: + raise Exception('acados -- mismatching dimensions for field {0}. ' \ + 'Provided data has dimensions {1}, ' \ + 'while associated dimensions {2} are {3}' \ + .format(key, item_dims, dim_names, dims)) + return + + +def J_to_idx(J): + nrows = J.shape[0] + idx = np.zeros((nrows, )) + for i in range(nrows): + this_idx = np.nonzero(J[i,:])[0] + if len(this_idx) != 1: + raise Exception('Invalid J matrix structure detected, ' \ + 'must contain one nonzero element per row. Exiting.') + if this_idx.size > 0 and J[i,this_idx[0]] != 1: + raise Exception('J matrices can only contain 1s. Exiting.') + idx[i] = this_idx[0] + return idx + + +def J_to_idx_slack(J): + nrows = J.shape[0] + ncol = J.shape[1] + idx = np.zeros((ncol, )) + i_idx = 0 + for i in range(nrows): + this_idx = np.nonzero(J[i,:])[0] + if len(this_idx) == 1: + idx[i_idx] = i + i_idx = i_idx + 1 + elif len(this_idx) > 1: + raise Exception('J_to_idx_slack: Invalid J matrix. Exiting. ' \ + 'Found more than one nonzero in row ' + str(i)) + if this_idx.size > 0 and J[i,this_idx[0]] != 1: + raise Exception('J_to_idx_slack: J matrices can only contain 1s, ' \ + 'got J(' + str(i) + ', ' + str(this_idx[0]) + ') = ' + str(J[i,this_idx[0]]) ) + if not i_idx == ncol: + raise Exception('J_to_idx_slack: J must contain a 1 in every column!') + return idx + + +def acados_dae_model_json_dump(model): + + # load model + x = model.x + xdot = model.xdot + u = model.u + z = model.z + p = model.p + + f_impl = model.f_impl_expr + model_name = model.name + + # create struct with impl_dae_fun, casadi_version + fun_name = model_name + '_impl_dae_fun' + impl_dae_fun = Function(fun_name, [x, xdot, u, z, p], [f_impl]) + + casadi_version = CasadiMeta.version() + str_impl_dae_fun = impl_dae_fun.serialize() + + dae_dict = {"str_impl_dae_fun": str_impl_dae_fun, "casadi_version": casadi_version} + + # dump + json_file = model_name + '_acados_dae.json' + with open(json_file, 'w') as f: + json.dump(dae_dict, f, default=np_array_to_list, indent=4, sort_keys=True) + print("dumped ", model_name, " dae to file:", json_file, "\n") + + +def set_up_imported_gnsf_model(acados_formulation): + + gnsf = acados_formulation.gnsf_model + + # check CasADi version + # dump_casadi_version = gnsf['casadi_version'] + # casadi_version = CasadiMeta.version() + + # if not casadi_version == dump_casadi_version: + # print("WARNING: GNSF model was dumped with another CasADi version.\n" + # + "This might yield errors. Please use the same version for compatibility, serialize version: " + # + dump_casadi_version + " current Python CasADi verison: " + casadi_version) + # input("Press any key to attempt to continue...") + + # load model + phi_fun = Function.deserialize(gnsf['phi_fun']) + phi_fun_jac_y = Function.deserialize(gnsf['phi_fun_jac_y']) + phi_jac_y_uhat = Function.deserialize(gnsf['phi_jac_y_uhat']) + get_matrices_fun = Function.deserialize(gnsf['get_matrices_fun']) + + # obtain gnsf dimensions + size_gnsf_A = get_matrices_fun.size_out(0) + acados_formulation.dims.gnsf_nx1 = size_gnsf_A[1] + acados_formulation.dims.gnsf_nz1 = size_gnsf_A[0] - size_gnsf_A[1] + acados_formulation.dims.gnsf_nuhat = max(phi_fun.size_in(1)) + acados_formulation.dims.gnsf_ny = max(phi_fun.size_in(0)) + acados_formulation.dims.gnsf_nout = max(phi_fun.size_out(0)) + + # save gnsf functions in model + acados_formulation.model.phi_fun = phi_fun + acados_formulation.model.phi_fun_jac_y = phi_fun_jac_y + acados_formulation.model.phi_jac_y_uhat = phi_jac_y_uhat + acados_formulation.model.get_matrices_fun = get_matrices_fun + + if "f_lo_fun_jac_x1k1uz" in gnsf: + f_lo_fun_jac_x1k1uz = Function.deserialize(gnsf['f_lo_fun_jac_x1k1uz']) + acados_formulation.model.f_lo_fun_jac_x1k1uz = f_lo_fun_jac_x1k1uz + else: + dummy_var_x1 = SX.sym('dummy_var_x1', acados_formulation.dims.gnsf_nx1) + dummy_var_x1dot = SX.sym('dummy_var_x1dot', acados_formulation.dims.gnsf_nx1) + dummy_var_z1 = SX.sym('dummy_var_z1', acados_formulation.dims.gnsf_nz1) + dummy_var_u = SX.sym('dummy_var_z1', acados_formulation.dims.nu) + dummy_var_p = SX.sym('dummy_var_z1', acados_formulation.dims.np) + empty_var = SX.sym('empty_var', 0, 0) + + empty_fun = Function('empty_fun', \ + [dummy_var_x1, dummy_var_x1dot, dummy_var_z1, dummy_var_u, dummy_var_p], + [empty_var]) + acados_formulation.model.f_lo_fun_jac_x1k1uz = empty_fun + + del acados_formulation.gnsf_model diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index e97792f7c8..5aceea2170 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -22,6 +22,8 @@ from selfdrive.version import dirty, get_git_commit, version, origin, branch, co terms_version, training_version, comma_remote, \ get_git_branch, get_git_remote +sys.path.append(os.path.join(BASEDIR, "pyextra")) + def manager_init(): # update system time from panda

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