diff --git a/.github/workflows/conda/environment.yml b/.github/workflows/conda/environment.yml index 8f791028c..312995e4f 100644 --- a/.github/workflows/conda/environment.yml +++ b/.github/workflows/conda/environment.yml @@ -18,3 +18,4 @@ dependencies: - libmatio - numpy - scipy + - osqp diff --git a/CHANGELOG.md b/CHANGELOG.md index b31c8ccba..3ea060f10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Recursive stub generation for Python bindings ([#419](https://github.com/Simple-Robotics/proxsuite/pull/419)) +- Add OSQP solver and refactor code ([#415](https://github.com/Simple-Robotics/proxsuite/pull/415)) ### Changed - Change the default branch to `devel` ([#395](https://github.com/Simple-Robotics/proxsuite/pull/395)) diff --git a/README.md b/README.md index 271cfc1b9..8dcfe4fbb 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,13 @@ where $x \in \mathbb{R}^n$ is the optimization variable. The objective function ### Citing **QPLayer** If you are using **QPLayer** for your work, we encourage you to [cite the related paper](https://inria.hal.science/hal-04133055v2/). +## **WIP: OSQP** + +The **OSQP** algorithm is a numerical optimization approach for solving quadratic programming problems with the same form as problems treated by **ProxQP**. It is based on the Alternating Direction Method of Multipliers. + +### Citing **OSQP** + +**OSQP** was developped by B. Stellato, G. Banjac, P. Goulart, A. Bemporad and S. Boyd. Information about the algorithm and the API of **OSQP** are available in the [related paper](https://web.stanford.edu/~boyd/papers/pdf/osqp.pdf) and [related website](https://osqp.org/). ## Installation procedure Please follow the installation procedure [here](https://github.com/Simple-Robotics/proxsuite/blob/devel/doc/5-installation.md). diff --git a/benchmark/timings-box-constraints.cpp b/benchmark/timings-box-constraints.cpp index d364bd1f4..f716f6e2e 100644 --- a/benchmark/timings-box-constraints.cpp +++ b/benchmark/timings-box-constraints.cpp @@ -3,13 +3,13 @@ // #include #include -#include +#include using T = double; using I = long long; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; int main(int /*argc*/, const char** /*argv*/) @@ -20,23 +20,23 @@ main(int /*argc*/, const char** /*argv*/) T sparsity_factor = 0.75; T eps_abs = T(1e-9); T elapsed_time = 0.0; - proxqp::utils::rand::set_seed(1); + common::utils::rand::set_seed(1); std::cout << "Dense QP" << std::endl; - for (proxqp::isize dim = 100; dim <= 1000; dim = dim + 100) { + for (isize dim = 100; dim <= 1000; dim = dim + 100) { - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(dim / 2); + isize n_eq(dim / 2); + isize n_in(dim / 2); std::cout << "dim: " << dim << " n_eq: " << n_eq << " n_in: " << n_in << " box: " << dim << std::endl; T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -44,8 +44,8 @@ main(int /*argc*/, const char** /*argv*/) u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } diff --git a/benchmark/timings-dense-backend.cpp b/benchmark/timings-dense-backend.cpp index 9fe87ccc4..4f6dda033 100644 --- a/benchmark/timings-dense-backend.cpp +++ b/benchmark/timings-dense-backend.cpp @@ -3,13 +3,13 @@ // #include #include -#include +#include using T = double; using I = long long; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; int main(int /*argc*/, const char** /*argv*/) @@ -20,23 +20,23 @@ main(int /*argc*/, const char** /*argv*/) T sparsity_factor = 0.75; T eps_abs = T(1e-9); T elapsed_time = 0.0; - proxqp::utils::rand::set_seed(1); + common::utils::rand::set_seed(1); std::cout << "Dense QP" << std::endl; - for (proxqp::isize dim = 100; dim <= 1000; dim = dim + 100) { + for (isize dim = 100; dim <= 1000; dim = dim + 100) { - proxqp::isize n_eq(dim * 2); - proxqp::isize n_in(dim * 2); + isize n_eq(dim * 2); + isize n_in(dim * 2); std::cout << "dim: " << dim << " n_eq: " << n_eq << " n_in: " << n_in << " box: " << dim << std::endl; T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -44,8 +44,8 @@ main(int /*argc*/, const char** /*argv*/) u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } @@ -64,7 +64,9 @@ main(int /*argc*/, const char** /*argv*/) elapsed_time = 0.0; timer.stop(); - proxqp::dense::QP qp{ dim, n_eq, n_in, true, DenseBackend::PrimalLDLT }; + proxqp::dense::QP qp{ + dim, n_eq, n_in, true, common::DenseBackend::PrimalLDLT + }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -97,7 +99,7 @@ main(int /*argc*/, const char** /*argv*/) elapsed_time = 0.0; proxqp::dense::QP qp_compare{ - dim, n_eq, n_in, true, DenseBackend::PrimalDualLDLT + dim, n_eq, n_in, true, common::DenseBackend::PrimalDualLDLT }; qp_compare.settings.eps_abs = eps_abs; qp_compare.settings.eps_rel = 0; @@ -131,7 +133,7 @@ main(int /*argc*/, const char** /*argv*/) << elapsed_time * 1e-3 / smooth << "ms" << std::endl; elapsed_time = 0.0; proxqp::dense::QP qp_compare_bis{ - dim, n_eq, n_in, true, DenseBackend::Automatic + dim, n_eq, n_in, true, common::DenseBackend::Automatic }; qp_compare_bis.settings.eps_abs = eps_abs; qp_compare_bis.settings.eps_rel = 0; diff --git a/benchmark/timings-diagonal-hessian.cpp b/benchmark/timings-diagonal-hessian.cpp index 614cfac9b..3a9488ba8 100644 --- a/benchmark/timings-diagonal-hessian.cpp +++ b/benchmark/timings-diagonal-hessian.cpp @@ -3,13 +3,13 @@ // #include #include -#include +#include using T = double; using I = long long; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; int main(int /*argc*/, const char** /*argv*/) @@ -20,23 +20,23 @@ main(int /*argc*/, const char** /*argv*/) T sparsity_factor = 0.75; T eps_abs = T(1e-9); T elapsed_time = 0.0; - proxqp::utils::rand::set_seed(1); + common::utils::rand::set_seed(1); std::cout << "Dense QP" << std::endl; - for (proxqp::isize dim = 100; dim <= 500; dim = dim + 100) { + for (isize dim = 100; dim <= 500; dim = dim + 100) { - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(dim / 2); + isize n_eq(dim / 2); + isize n_in(dim / 2); std::cout << "dim: " << dim << " n_eq: " << n_eq << " n_in: " << n_in << " box: " << dim << std::endl; T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -44,8 +44,8 @@ main(int /*argc*/, const char** /*argv*/) u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } @@ -69,12 +69,9 @@ main(int /*argc*/, const char** /*argv*/) elapsed_time = 0.0; timer.stop(); - proxqp::dense::QP qp{ dim, - n_eq, - n_in, - true, - proxsuite::proxqp::DenseBackend::PrimalDualLDLT, - proxsuite::proxqp::HessianType::Diagonal }; + proxqp::dense::QP qp{ + dim, n_eq, n_in, true, DenseBackend::PrimalDualLDLT, HessianType::Diagonal + }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -107,12 +104,7 @@ main(int /*argc*/, const char** /*argv*/) elapsed_time = 0.0; proxqp::dense::QP qp_compare{ - dim, - n_eq, - n_in, - true, - proxsuite::proxqp::DenseBackend::PrimalDualLDLT, - proxsuite::proxqp::HessianType::Dense + dim, n_eq, n_in, true, DenseBackend::PrimalDualLDLT, HessianType::Dense }; qp_compare.settings.eps_abs = eps_abs; qp_compare.settings.eps_rel = 0; diff --git a/benchmark/timings-lp.cpp b/benchmark/timings-lp.cpp index 1a221e530..1a3a75405 100644 --- a/benchmark/timings-lp.cpp +++ b/benchmark/timings-lp.cpp @@ -3,13 +3,13 @@ // #include #include -#include +#include using T = double; using I = long long; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; int main(int /*argc*/, const char** /*argv*/) @@ -20,10 +20,9 @@ main(int /*argc*/, const char** /*argv*/) T sparsity_factor = 0.75; T eps_abs = T(1e-9); T elapsed_time = 0.0; - proxqp::utils::rand::set_seed(1); + common::utils::rand::set_seed(1); std::cout << "Dense QP" << std::endl; - for (proxqp::isize dim = 10; dim <= 1000; - dim = (dim == 10) ? 100 : dim + 100) { + for (isize dim = 10; dim <= 1000; dim = (dim == 10) ? 100 : dim + 100) { if (dim == 10 || dim == 100) { smooth = 1000; @@ -31,22 +30,22 @@ main(int /*argc*/, const char** /*argv*/) smooth = 100; } - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(dim / 2); + isize n_eq(dim / 2); + isize n_in(dim / 2); T strong_convexity_factor(1.e-2); std::cout << "dim: " << dim << " n_eq: " << n_eq << " n_in: " << n_in << std::endl; - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - auto y_sol = proxqp::utils::rand::vector_rand(n_eq); + auto y_sol = common::utils::rand::vector_rand(n_eq); qp_random.g = -qp_random.A.transpose() * y_sol; elapsed_time = 0.0; timer.stop(); proxqp::dense::QP qp{ - dim, n_eq, n_in, false, proxqp::HessianType::Zero + dim, n_eq, n_in, false, common::HessianType::Zero }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -77,7 +76,7 @@ main(int /*argc*/, const char** /*argv*/) elapsed_time = 0.0; proxqp::dense::QP qp_compare{ - dim, n_eq, n_in, false, proxqp::HessianType::Dense + dim, n_eq, n_in, false, common::HessianType::Dense }; qp_compare.settings.eps_abs = eps_abs; qp_compare.settings.eps_rel = 0; diff --git a/benchmark/timings-parallel.cpp b/benchmark/timings-parallel.cpp index a0d03e57e..c6dbbb616 100644 --- a/benchmark/timings-parallel.cpp +++ b/benchmark/timings-parallel.cpp @@ -4,13 +4,13 @@ #include #include #include -#include +#include using T = double; using I = long long; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; int main(int /*argc*/, const char** /*argv*/) @@ -19,9 +19,9 @@ main(int /*argc*/, const char** /*argv*/) double sparsity_factor = 0.15; T eps_abs = T(1e-9); - dense::isize dim = 100; - dense::isize n_eq(50); - dense::isize n_in(50); + isize dim = 100; + isize n_eq(50); + isize n_in(50); T strong_convexity_factor(1.e-2); int num_qps = 1024; @@ -43,12 +43,12 @@ main(int /*argc*/, const char** /*argv*/) std::vector> qps; qps.reserve(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_random = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; + proxqp::dense::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -68,11 +68,11 @@ main(int /*argc*/, const char** /*argv*/) timer.start(); for (int j = 0; j < smooth; j++) { - dense::BatchQP qps_vector = dense::BatchQP(num_qps); + proxqp::dense::BatchQP qps_vector = proxqp::dense::BatchQP(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_random = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); auto& qp = qps_vector.init_qp_in_place(dim, n_eq, n_in); @@ -96,13 +96,13 @@ main(int /*argc*/, const char** /*argv*/) std::vector> qps; qps.reserve(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_dense = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_dense = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::SparseModel qp_random = qp_dense.to_sparse(); - sparse::QP qp{ dim, n_eq, n_in }; + proxqp::sparse::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -122,11 +122,12 @@ main(int /*argc*/, const char** /*argv*/) timer.start(); for (int j = 0; j < smooth; j++) { - sparse::BatchQP qps_vector = sparse::BatchQP(num_qps); + proxqp::sparse::BatchQP qps_vector = + proxqp::sparse::BatchQP(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_dense = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_dense = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::SparseModel qp_random = qp_dense.to_sparse(); @@ -156,12 +157,12 @@ main(int /*argc*/, const char** /*argv*/) std::vector> qps; qps.reserve(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_random = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; + proxqp::dense::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -175,11 +176,11 @@ main(int /*argc*/, const char** /*argv*/) qps.push_back(std::move(qp)); } - dense::BatchQP qps_vector = dense::BatchQP(num_qps); + proxqp::dense::BatchQP qps_vector = proxqp::dense::BatchQP(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_random = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); auto& qp = qps_vector.init_qp_in_place(dim, n_eq, n_in); @@ -207,7 +208,7 @@ main(int /*argc*/, const char** /*argv*/) const size_t NUM_THREADS = (size_t)omp_get_max_threads(); - std::cout << "\nparallel using dense::BatchQP" << std::endl; + std::cout << "\nparallel using proxqp::dense::BatchQP" << std::endl; for (size_t num_threads = 1; num_threads <= NUM_THREADS; ++num_threads) { timer.start(); for (int j = 0; j < smooth; j++) { @@ -240,13 +241,13 @@ main(int /*argc*/, const char** /*argv*/) std::vector> qps; qps.reserve(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_dense = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_dense = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::SparseModel qp_random = qp_dense.to_sparse(); - sparse::QP qp{ dim, n_eq, n_in }; + proxqp::sparse::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -260,11 +261,12 @@ main(int /*argc*/, const char** /*argv*/) qps.push_back(std::move(qp)); } - sparse::BatchQP qps_vector = sparse::BatchQP(num_qps); + proxqp::sparse::BatchQP qps_vector = + proxqp::sparse::BatchQP(num_qps); for (int i = 0; i < num_qps; i++) { - utils::rand::set_seed(i); - proxqp::dense::Model qp_dense = - proxqp::utils::dense_strongly_convex_qp( + common::utils::rand::set_seed(i); + common::dense::Model qp_dense = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::SparseModel qp_random = qp_dense.to_sparse(); @@ -293,7 +295,7 @@ main(int /*argc*/, const char** /*argv*/) const size_t NUM_THREADS = (size_t)omp_get_max_threads(); - std::cout << "\nparallel using sparse::BatchQP" << std::endl; + std::cout << "\nparallel using proxqp::sparse::BatchQP" << std::endl; for (size_t num_threads = 1; num_threads <= NUM_THREADS; ++num_threads) { timer.start(); for (int j = 0; j < smooth; j++) { diff --git a/bindings/python/src/algorithms.hpp b/bindings/python/src/algorithms.hpp index 887648caa..27b87ef25 100644 --- a/bindings/python/src/algorithms.hpp +++ b/bindings/python/src/algorithms.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022-2023 INRIA +// Copyright (c) 2022-2025 INRIA // #ifndef proxsuite_python_algorithms_hpp #define proxsuite_python_algorithms_hpp @@ -8,12 +8,14 @@ #include "expose-results.hpp" #include "expose-settings.hpp" #include "expose-workspace.hpp" -#include "expose-qpobject.hpp" #include "expose-qpvector.hpp" -#include "expose-solve.hpp" #include "expose-helpers.hpp" #include "expose-backward.hpp" #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP #include "expose-parallel.hpp" #endif +#include "proxqp/expose-qpobject.hpp" +#include "proxqp/expose-solve.hpp" +#include "osqp/expose-qpobject.hpp" +#include "osqp/expose-solve.hpp" #endif /* end of include guard proxsuite_python_algorithms_hpp */ diff --git a/bindings/python/src/expose-all.cpp b/bindings/python/src/expose-all.cpp index cdfc4f87c..b5546fb59 100644 --- a/bindings/python/src/expose-all.cpp +++ b/bindings/python/src/expose-all.cpp @@ -1,7 +1,6 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // -#include #include #include @@ -9,35 +8,28 @@ #include #include "algorithms.hpp" -#include +#include "helpers.hpp" + +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/status.hpp" +#include + +#include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace python { -template -void -exposeCommon(nanobind::module_ m) -{ - exposeResults(m); - exposeSettings(m); -#ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP - m.def("omp_get_max_threads", - &omp_get_max_threads, - "Returns the max number of threads that could be used by OpenMP."); -#endif -} - template void exposeSparseAlgorithms(nanobind::module_ m) { - sparse::python::exposeSparseModel(m); - sparse::python::exposeQpObjectSparse(m); - sparse::python::exposeQPVectorSparse(m); - sparse::python::solveSparseQp(m); - sparse::python::exposeSparseHelpers(m); + proxqp::sparse::python::exposeSparseModel(m); + proxqp::sparse::python::exposeQpObjectSparse(m); + proxqp::sparse::python::exposeQPVectorSparse(m); + proxqp::sparse::python::solveSparseQp(m); + proxqp::sparse::python::exposeSparseHelpers(m); } template @@ -46,16 +38,16 @@ exposeDenseAlgorithms(nanobind::module_ m) { dense::python::exposeWorkspaceDense(m); dense::python::exposeDenseModel(m); - dense::python::exposeQpObjectDense(m); - dense::python::exposeQPVectorDense(m); - dense::python::solveDenseQp(m); + proxqp::dense::python::exposeQpObjectDense(m); + proxqp::dense::python::exposeQPVectorDense(m); + proxqp::dense::python::solveDenseQp(m); dense::python::exposeDenseHelpers(m); } template void exposeBackward(nanobind::module_ m) { - dense::python::backward(m); + proxqp::dense::python::backward(m); } #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP @@ -63,13 +55,13 @@ template void exposeDenseParallel(nanobind::module_ m) { - dense::python::solveDenseQpParallel(m); + proxqp::dense::python::solveDenseQpParallel(m); } template void exposeSparseParallel(nanobind::module_ m) { - sparse::python::solveSparseQpParallel(m); + proxqp::sparse::python::solveSparseQpParallel(m); } #endif @@ -86,15 +78,23 @@ NB_MODULE(PYTHON_MODULE_NAME, m) proxsuite )pbdoc"; + // PROXQP nanobind::module_ proxqp_module = m.def_submodule("proxqp", "The proxQP solvers of the proxSuite library"); - exposeCommon(proxqp_module); - nanobind::module_ dense_module = + exposeResults(proxqp_module); + exposeSettings(proxqp_module); +#ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP + proxqp_module.def( + "omp_get_max_threads", + &omp_get_max_threads, + "Returns the max number of threads that could be used by OpenMP."); +#endif + nanobind::module_ proxqp_dense_module = proxqp_module.def_submodule("dense", "Dense solver of proxQP"); - exposeDenseAlgorithms(dense_module); - exposeBackward(dense_module); + exposeDenseAlgorithms(proxqp_dense_module); + exposeBackward(proxqp_dense_module); #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP - exposeDenseParallel(dense_module); + exposeDenseParallel(proxqp_dense_module); #endif nanobind::module_ sparse_module = proxqp_module.def_submodule("sparse", "Sparse solver of proxQP"); @@ -103,6 +103,47 @@ NB_MODULE(PYTHON_MODULE_NAME, m) exposeSparseParallel(sparse_module); #endif + // OSQP + nanobind::module_ osqp_module = + m.def_submodule("osqp", "The OSQP solvers of the proxSuite library"); + // exposeResults + exposeAndExportValues(osqp_module); + exposeAndExportValues(osqp_module); + osqp_module.attr("Info") = m.attr("proxqp").attr("Info"); + osqp_module.attr("Results") = m.attr("proxqp").attr("Results"); + // exposeSettings + exposeAndExportValues(osqp_module); + exposeAndExportValues(osqp_module); + exposeAndExportValues(osqp_module); + exposeAndExportValues(osqp_module); + osqp_module.attr("Settings") = m.attr("proxqp").attr("Settings"); + // OpenMP +#ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP + osqp_module.def( + "omp_get_max_threads", + &omp_get_max_threads, + "Returns the max number of threads that could be used by OpenMP."); +#endif + // dense_module + nanobind::module_ osqp_dense_module = + osqp_module.def_submodule("dense", "Dense solver of OSQP"); + // exposeDenseAlgorithms: exposeWorkspaceDense + osqp_dense_module.attr("workspace") = + m.attr("proxqp").attr("dense").attr("workspace"); + // exposeDenseAlgorithms: exposeDenseModel + osqp_dense_module.attr("model") = + m.attr("proxqp").attr("dense").attr("model"); + // exposeDenseAlgorithms: exposeQpObjectDense + exposeAndExportValues(osqp_dense_module); + exposeAndExportValues(osqp_dense_module); + osqp::dense::python::exposeQpObjectDense(osqp_dense_module); + // exposeDenseAlgorithms: solveDenseQp + osqp::dense::python::solveDenseQp(osqp_dense_module); + // exposeDenseAlgorithms: exposeDenseHelpers + osqp_dense_module.attr("estimate_minimal_eigen_value_of_symmetric_matrix") = + m.attr("proxqp").attr("dense").attr( + "estimate_minimal_eigen_value_of_symmetric_matrix"); + // Add version m.attr("__version__") = helpers::printVersion(); @@ -123,6 +164,5 @@ NB_MODULE(PYTHON_MODULE_NAME, m) } } // namespace python - -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/bindings/python/src/expose-helpers.hpp b/bindings/python/src/expose-helpers.hpp index 872a60fdd..da87778ba 100644 --- a/bindings/python/src/expose-helpers.hpp +++ b/bindings/python/src/expose-helpers.hpp @@ -1,19 +1,17 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // #include #include #include -#include +#include #include namespace proxsuite { -namespace proxqp { - +namespace common { namespace dense { - namespace python { template @@ -26,7 +24,7 @@ exposeDenseHelpers(nanobind::module_ m) EigenValueEstimateMethodOption estimate_method_option, T power_iteration_accuracy, isize nb_power_iteration) { - return dense::estimate_minimal_eigen_value_of_symmetric_matrix( + return estimate_minimal_eigen_value_of_symmetric_matrix( H, estimate_method_option, power_iteration_accuracy, @@ -46,9 +44,12 @@ exposeDenseHelpers(nanobind::module_ m) } } // namespace python } // namespace dense +} // namespace common +} // namespace proxsuite +namespace proxsuite { +namespace proxqp { namespace sparse { - namespace python { template @@ -68,6 +69,5 @@ exposeSparseHelpers(nanobind::module_ m) } // namespace python } // namespace sparse - } // namespace proxqp -} // namespace proxsuite +} // namespace proxsuite \ No newline at end of file diff --git a/bindings/python/src/expose-model.hpp b/bindings/python/src/expose-model.hpp index 92ea5c6ce..a0d06295c 100644 --- a/bindings/python/src/expose-model.hpp +++ b/bindings/python/src/expose-model.hpp @@ -1,22 +1,22 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // #include #include #include -#include +#include #include -#include #include #include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { namespace python { + template void exposeDenseModel(nanobind::module_ m) @@ -43,7 +43,7 @@ exposeDenseModel(nanobind::module_ m) // .def_ro("dL_dsi", // &proxsuite::proxqp::dense::BackwardData::dL_dsi); - ::nanobind::class_>(m, "model") + ::nanobind::class_>(m, "model") .def(::nanobind::init(), nanobind::arg("n") = 0, nanobind::arg("n_eq") = 0, @@ -67,7 +67,7 @@ exposeDenseModel(nanobind::module_ m) .def(nanobind::self == nanobind::self) .def(nanobind::self != nanobind::self) .def("__getstate__", - [](const proxsuite::proxqp::dense::Model& model) { + [](const dense::Model& model) { return proxsuite::serialization::saveToString(model); }) .def("__setstate__", [](dense::Model& model, const std::string& s) { @@ -78,7 +78,11 @@ exposeDenseModel(nanobind::module_ m) } } // namespace python } // namespace dense +} // namespace common +} // namespace proxsuite +namespace proxsuite { +namespace proxqp { namespace sparse { namespace python { template @@ -106,4 +110,4 @@ exposeSparseModel(nanobind::module_ m) } // namespace sparse } // namespace proxqp -} // namespace proxsuite +} // namespace proxsuite \ No newline at end of file diff --git a/bindings/python/src/expose-parallel.hpp b/bindings/python/src/expose-parallel.hpp index 11ffaa2d0..61618d6fe 100644 --- a/bindings/python/src/expose-parallel.hpp +++ b/bindings/python/src/expose-parallel.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 INRIA +// Copyright (c) 2025 INRIA // #include @@ -11,7 +11,7 @@ #include NB_MAKE_OPAQUE(std::vector>) -NB_MAKE_OPAQUE(std::vector>) +NB_MAKE_OPAQUE(std::vector>) namespace proxsuite { namespace proxqp { using proxsuite::linalg::veg::isize; @@ -24,7 +24,7 @@ void solveDenseQpParallel(nanobind::module_ m) { - nanobind::bind_vector>>( + nanobind::bind_vector>>( m, "VectorLossDerivatives"); nanobind::bind_vector>>( @@ -54,7 +54,7 @@ solveDenseQpParallel(nanobind::module_ m) m.def("solve_backward_in_parallel", nanobind::overload_cast, proxqp::dense::BatchQP&, - std::vector>&, + std::vector>&, T, T, T>(&qp_solve_backward_in_parallel), @@ -69,7 +69,7 @@ solveDenseQpParallel(nanobind::module_ m) m.def("solve_backward_in_parallel", nanobind::overload_cast, std::vector>&, - std::vector>&, + std::vector>&, T, T, T>(&qp_solve_backward_in_parallel), diff --git a/bindings/python/src/expose-qpvector.hpp b/bindings/python/src/expose-qpvector.hpp index 0530ac41a..171ce8f2f 100644 --- a/bindings/python/src/expose-qpvector.hpp +++ b/bindings/python/src/expose-qpvector.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 INRIA +// Copyright (c) 2025 INRIA // #include diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index fa1e63160..5f5b89dce 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -1,7 +1,7 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // -#include + #include #include #include @@ -9,11 +9,13 @@ #include "optional-eigen-fix.hpp" #include +#include +#include #include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace python { template @@ -21,19 +23,31 @@ void exposeResults(nanobind::module_ m) { ::nanobind::enum_(m, "QPSolverOutput") - .value("PROXQP_SOLVED", QPSolverOutput::PROXQP_SOLVED) - .value("PROXQP_MAX_ITER_REACHED", QPSolverOutput::PROXQP_MAX_ITER_REACHED) - .value("PROXQP_PRIMAL_INFEASIBLE", QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) - .value("PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE", - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE) - .value("PROXQP_DUAL_INFEASIBLE", QPSolverOutput::PROXQP_DUAL_INFEASIBLE) - .value("PROXQP_NOT_RUN", QPSolverOutput::PROXQP_NOT_RUN) + .value("QPSOLVER_SOLVED", QPSolverOutput::QPSOLVER_SOLVED) + .value("QPSOLVER_MAX_ITER_REACHED", + QPSolverOutput::QPSOLVER_MAX_ITER_REACHED) + .value("QPSOLVER_PRIMAL_INFEASIBLE", + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) + .value("QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE", + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE) + .value("QPSOLVER_DUAL_INFEASIBLE", QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE) + .value("QPSOLVER_NOT_RUN", QPSolverOutput::QPSOLVER_NOT_RUN) + .export_values(); + + ::nanobind::enum_(m, "PolishOutput") + .value("POLISH_FAILED", PolishOutput::POLISH_FAILED) + .value("POLISH_NOT_RUN", PolishOutput::POLISH_NOT_RUN) + .value("POLISH_SUCCEEDED", PolishOutput::POLISH_SUCCEEDED) + .value("POLISH_NO_ACTIVE_SET_FOUND", + PolishOutput::POLISH_NO_ACTIVE_SET_FOUND) .export_values(); ::nanobind::class_>(m, "Info") .def(::nanobind::init(), "Default constructor.") .def_rw("mu_eq", &Info::mu_eq) .def_rw("mu_in", &Info::mu_in) + .def_rw("mu_eq_inv", &Info::mu_eq_inv) + .def_rw("mu_in_inv", &Info::mu_in_inv) .def_rw("rho", &Info::rho) .def_rw("iter", &Info::iter) .def_rw("iter_ext", &Info::iter_ext) @@ -57,7 +71,10 @@ exposeResults(nanobind::module_ m) &Info::minimal_H_eigenvalue_estimate, "By default it equals 0, in order to get an estimate, set " "appropriately the setting option " - "find_H_minimal_eigenvalue."); + "find_H_minimal_eigenvalue.") + .def_rw("rho_osqp_estimate", &Info::rho_osqp_estimate) + .def_rw("polish_time", &Info::polish_time) + .def_rw("status_polish", &Info::status_polish); ::nanobind::class_>(m, "Results") .def(::nanobind::init(), @@ -99,6 +116,8 @@ exposeResults(nanobind::module_ m) &Results::si, "Optimal shift to the closest feasible problem wrt inequality " "constraints.") + .def_rw("zeta_eq", &Results::zeta_eq, "Equality 'z' in OSQP") + .def_rw("zeta_in", &Results::zeta_in, "Equality 'z' in OSQP") .def_rw("info", &Results::info) .def(nanobind::self == nanobind::self) .def(nanobind::self != nanobind::self) @@ -113,5 +132,5 @@ exposeResults(nanobind::module_ m) ; } } // namespace python -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/bindings/python/src/expose-settings.hpp b/bindings/python/src/expose-settings.hpp index c99d8767d..bf72b031d 100644 --- a/bindings/python/src/expose-settings.hpp +++ b/bindings/python/src/expose-settings.hpp @@ -1,19 +1,21 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // + #include #include #include #include -#include -#include +#include +#include #include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace python { + template void exposeSettings(nanobind::module_ m) @@ -40,12 +42,18 @@ exposeSettings(nanobind::module_ m) .value("MatrixFree", SparseBackend::MatrixFree) .value("SparseCholesky", SparseBackend::SparseCholesky) .export_values(); + ::nanobind::enum_( m, "EigenValueEstimateMethodOption") .value("PowerIteration", EigenValueEstimateMethodOption::PowerIteration) .value("ExactMethod", EigenValueEstimateMethodOption::ExactMethod) .export_values(); + ::nanobind::enum_(m, "CheckSolvedStatus") + .value("ITERATION_BASED", CheckSolvedStatus::ITERATION_BASED) + .value("INTERVAL_BASED", CheckSolvedStatus::INTERVAL_BASED) + .export_values(); + ::nanobind::class_>(m, "Settings") .def(::nanobind::init(), "Default constructor.") // constructor .def_rw("default_rho", &Settings::default_rho) @@ -84,12 +92,25 @@ exposeSettings(nanobind::module_ m) .def_rw("bcl_update", &Settings::bcl_update) .def_rw("merit_function_type", &Settings::merit_function_type) .def_rw("alpha_gpdal", &Settings::alpha_gpdal) + .def_rw("check_solved_option", &Settings::check_solved_option) + .def_rw("check_termination", &Settings::check_termination) .def_rw("primal_infeasibility_solving", &Settings::primal_infeasibility_solving) .def_rw("frequence_infeasibility_check", &Settings::frequence_infeasibility_check) .def_rw("default_H_eigenvalue_estimate", &Settings::default_H_eigenvalue_estimate) + .def_rw("alpha", &Settings::alpha) + .def_rw("mu_max_eq", &Settings::mu_max_eq) + .def_rw("mu_max_in", &Settings::mu_max_in) + .def_rw("mu_min_eq_inv", &Settings::mu_min_eq_inv) + .def_rw("mu_min_in_inv", &Settings::mu_min_in_inv) + .def_rw("adaptive_mu", &Settings::adaptive_mu) + .def_rw("adaptive_mu_interval", &Settings::adaptive_mu_interval) + .def_rw("adaptive_mu_tolerance", &Settings::adaptive_mu_tolerance) + .def_rw("polish", &Settings::polish) + .def_rw("delta", &Settings::delta) + .def_rw("polish_refine_iter", &Settings::polish_refine_iter) .def(nanobind::self == nanobind::self) .def(nanobind::self != nanobind::self) .def("__getstate__", @@ -103,5 +124,5 @@ exposeSettings(nanobind::module_ m) ; } } // namespace python -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/bindings/python/src/expose-workspace.hpp b/bindings/python/src/expose-workspace.hpp index 9fa9fde65..abeed3039 100644 --- a/bindings/python/src/expose-workspace.hpp +++ b/bindings/python/src/expose-workspace.hpp @@ -1,25 +1,27 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // + #include #include #include -#include -#include +#include +#include #include #include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { namespace python { + template void exposeWorkspaceDense(nanobind::module_ m) { - ::nanobind::class_>(m, "workspace") + ::nanobind::class_>(m, "workspace") .def(::nanobind::init(), nanobind::arg("n") = 0, nanobind::arg("n_eq") = 0, @@ -68,6 +70,11 @@ exposeWorkspaceDense(nanobind::module_ m) &Workspace::proximal_parameter_update) .def_ro("is_initialized", &Workspace::is_initialized) .def_ro("n_c", &Workspace::n_c) + .def_ro("x_tilde", &Workspace::x_tilde) + .def_ro("nu_eq", &Workspace::nu_eq) + .def_ro("nu_in", &Workspace::nu_in) + .def_ro("zeta_tilde_eq", &Workspace::zeta_tilde_eq) + .def_ro("zeta_tilde_in", &Workspace::zeta_tilde_in) .def("__getstate__", [](const Workspace& workspace) { return proxsuite::serialization::saveToString(workspace); @@ -81,5 +88,5 @@ exposeWorkspaceDense(nanobind::module_ m) } } // namespace python } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/bindings/python/src/helpers.hpp b/bindings/python/src/helpers.hpp new file mode 100644 index 000000000..2ea25a215 --- /dev/null +++ b/bindings/python/src/helpers.hpp @@ -0,0 +1,66 @@ +// +// Copyright (c) 2025 INRIA +// + +#include + +#include +#include + +#include + +namespace proxsuite { +namespace common { +namespace python { +namespace detail { +inline nanobind::str +type_name_short(nanobind::handle h) +{ + namespace nb = nanobind; + assert(h.is_type()); + // nb::type_name return the type_name with modules. + // In the next step, we will trim the modules to only keep the type name. + auto str = nb::type_name(h); + std::string_view work_str(str.c_str()); + auto dot_index = work_str.find_last_of('.'); + if (dot_index == std::string_view::npos) { + return str; + } else { + return nb::str(work_str.data() + dot_index + 1); + } + // return nb::str(dot_it++, str.end()); +} +} // namespace detail + +/*! + * Exposes a type and export its values given the first definition in + * a first module. It is motivated by the use of .export_values(); + * + * @param m nanobind module (proxsuite). + */ +template +void +exposeAndExportValues(nanobind::module_& m, bool export_values = true) +{ + namespace nb = nanobind; + nb::handle t = nb::type(); + if (!t.is_valid()) { + throw std::runtime_error("Invalid type"); + } +#ifndef NDEBUG + assert(t.is_type()); +#endif + nb::enum_& t_ = static_cast&>(t); + nb::str name = detail::type_name_short(t); + + m.attr(name) = t_; + if (export_values) { + for (nb::handle item : t) { + m.attr(item.attr("name")) = item; + } + } +} + +} // namespace python +} // namespace common +} // namespace proxsuite diff --git a/bindings/python/src/osqp/expose-qpobject.hpp b/bindings/python/src/osqp/expose-qpobject.hpp new file mode 100644 index 000000000..d559a1125 --- /dev/null +++ b/bindings/python/src/osqp/expose-qpobject.hpp @@ -0,0 +1,201 @@ +// +// Copyright (c) 2025 INRIA +// + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace proxsuite { +namespace osqp { +using proxsuite::linalg::veg::isize; + +namespace dense { + +namespace python { + +template +void +exposeQpObjectDense(nanobind::module_ m) +{ + ::nanobind::class_>(m, "QP") + .def( + ::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, + nanobind::arg("box_constraints") = false, + nanobind::arg("hessian_type") = HessianType::Dense, + nanobind::arg("dense_backend") = + DenseBackend::PrimalDualLDLT, // TODO: Automatic when + // PrimalLDLT is coded + "Default constructor using QP model dimensions.") // constructor + .def_rw("results", + &dense::QP::results, + "class containing the solution or certificate of infeasibility, " + "and " + "information statistics in an info subclass.") + .def_rw("settings", &dense::QP::settings, "Settings of the solver.") + .def_rw("model", &dense::QP::model, "class containing the QP model") + .def("is_box_constrained", + &dense::QP::is_box_constrained, + "precise whether or not the QP is designed with box constraints.") + .def("which_hessian_type", + &dense::QP::which_hessian_type, + "precise which problem type is to be solved.") + .def("which_dense_backend", + &dense::QP::which_dense_backend, + "precise which dense backend is chosen.") + .def("init", + static_cast::*)(optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + bool compute_preconditioner, + optional, + optional, + optional, + optional)>(&dense::QP::init), + "function for initialize the QP model.", + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) + .def("init", + static_cast::*)(optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + bool compute_preconditioner, + optional, + optional, + optional, + optional)>(&dense::QP::init), + "function for initialize the QP model.", + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) + .def("solve", + static_cast::*)()>(&dense::QP::solve), + "function used for solving the QP problem, using default parameters.") + .def("solve", + static_cast::*)(optional> x, + optional> y, + optional> z)>( + &dense::QP::solve), + "function used for solving the QP problem, when passing a warm start.") + + .def( + "update", + static_cast::*)(optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + bool update_preconditioner, + optional, + optional, + optional, + optional)>(&dense::QP::update), + "function used for updating matrix or vector entry of the model using " + "dense matrix entries.", + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("update_preconditioner") = false, + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) + .def( + "update", + static_cast::*)(optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + bool update_preconditioner, + optional, + optional, + optional, + optional)>(&dense::QP::update), + "function used for updating matrix or vector entry of the model using " + "dense matrix entries.", + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), + nanobind::arg("update_preconditioner") = false, + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) + .def("cleanup", + &dense::QP::cleanup, + "function used for cleaning the workspace and result " + "classes.") + .def(nanobind::self == nanobind::self) + .def(nanobind::self != nanobind::self) + .def("__getstate__", + [](const dense::QP& qp) { + return proxsuite::serialization::saveToString(qp); + }) + .def("__setstate__", [](dense::QP& qp, const std::string& s) { + new (&qp) dense::QP(1, 1, 1); + proxsuite::serialization::loadFromString(qp, s); + }); + ; +} +} // namespace python +} // namespace dense + +} // namespace osqp +} // namespace proxsuite diff --git a/bindings/python/src/osqp/expose-solve.hpp b/bindings/python/src/osqp/expose-solve.hpp new file mode 100644 index 000000000..bdea59f94 --- /dev/null +++ b/bindings/python/src/osqp/expose-solve.hpp @@ -0,0 +1,319 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include "../optional-eigen-fix.hpp" + +namespace proxsuite { +namespace osqp { +using proxsuite::linalg::veg::isize; + +namespace dense { +namespace python { + +template +void +solveDenseQp(nanobind::module_ m) +{ + m.def( + "solve", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + InitialGuessStatus, + bool, + optional, + optional, + bool, + optional, + bool, + optional, + optional, + bool, + optional, + optional>(&dense::solve), + "Function for solving a QP problem using OSQP dense backend directly " + "without defining a QP object. It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A").none(), + nanobind::arg("b").none(), + nanobind::arg("C").none(), + nanobind::arg("l").none(), + nanobind::arg("u").none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nanobind::none(), + nanobind::arg("initial_guess") = InitialGuessStatus::NO_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::arg("adaptive_mu") = true, + nanobind::arg("adaptive_mu_interval") = nanobind::none(), + nanobind::arg("adaptive_mu_tolerance") = nanobind::none(), + nanobind::arg("polish") = false, + nanobind::arg("delta") = nanobind::none(), + nanobind::arg("polish_refine_iter") = nanobind::none()); + + m.def( + "solve", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + InitialGuessStatus, + bool, + optional, + optional, + bool, + optional, + bool, + optional, + optional, + bool, + optional, + optional>(&dense::solve), + "Function for solving a QP problem using OSQP dense backend directly " + "without defining a QP object. It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nanobind::none(), + nanobind::arg("initial_guess") = InitialGuessStatus::NO_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::arg("adaptive_mu") = true, + nanobind::arg("adaptive_mu_interval") = nanobind::none(), + nanobind::arg("adaptive_mu_tolerance") = nanobind::none(), + nanobind::arg("polish") = false, + nanobind::arg("delta") = nanobind::none(), + nanobind::arg("polish_refine_iter") = nanobind::none()); + + m.def("solve_no_gil", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + InitialGuessStatus, + bool, + optional, + optional, + bool, + optional, + bool, + optional, + optional, + bool, + optional, + optional>(&dense::solve), + "Function for solving a QP problem using OSQP dense backend directly " + "without defining a QP object and while releasing the Global " + "Interpreter Lock (GIL). " + "It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A").none(), + nanobind::arg("b").none(), + nanobind::arg("C").none(), + nanobind::arg("l").none(), + nanobind::arg("u").none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nanobind::none(), + nanobind::arg("initial_guess") = InitialGuessStatus::NO_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::arg("adaptive_mu") = true, + nanobind::arg("adaptive_mu_interval") = nanobind::none(), + nanobind::arg("adaptive_mu_tolerance") = nanobind::none(), + nanobind::arg("polish") = false, + nanobind::arg("delta") = nanobind::none(), + nanobind::arg("polish_refine_iter") = nanobind::none(), + nanobind::call_guard()); + + m.def( + "solve_no_gil", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + InitialGuessStatus, + bool, + optional, + optional, + bool, + optional, + bool, + optional, + optional, + bool, + optional, + optional>(&dense::solve), + "Function for solving a QP problem using OSQP dense backend directly " + "without defining a QP object and while releasing the Global Interpreter " + "Lock (GIL). " + "It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nanobind::none(), + nanobind::arg("initial_guess") = InitialGuessStatus::NO_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::arg("adaptive_mu") = true, + nanobind::arg("adaptive_mu_interval") = nanobind::none(), + nanobind::arg("adaptive_mu_tolerance") = nanobind::none(), + nanobind::arg("polish") = false, + nanobind::arg("delta") = nanobind::none(), + nanobind::arg("polish_refine_iter") = nanobind::none(), + nanobind::call_guard()); +} + +} // namespace python +} // namespace dense + +} // namespace osqp +} // namespace proxsuite diff --git a/bindings/python/src/expose-qpobject.hpp b/bindings/python/src/proxqp/expose-qpobject.hpp similarity index 92% rename from bindings/python/src/expose-qpobject.hpp rename to bindings/python/src/proxqp/expose-qpobject.hpp index 288c1a7d2..67926d20e 100644 --- a/bindings/python/src/expose-qpobject.hpp +++ b/bindings/python/src/proxqp/expose-qpobject.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -32,12 +32,12 @@ exposeQpObjectDense(nanobind::module_ m) .export_values(); ::nanobind::enum_(m, "HessianType") - .value("Dense", proxsuite::proxqp::HessianType::Dense) - .value("Zero", proxsuite::proxqp::HessianType::Zero) - .value("Diagonal", proxsuite::proxqp::HessianType::Diagonal) + .value("Dense", HessianType::Dense) + .value("Zero", HessianType::Zero) + .value("Diagonal", HessianType::Diagonal) .export_values(); - // ::nanobind::class_>(m, + // ::nanobind::class_>(m, // "ruiz") // .def(::nanobind::init(), "Default constructor.") // .def_rw("mu_eq", &RuizEquilibration::delta) @@ -47,7 +47,7 @@ exposeQpObjectDense(nanobind::module_ m) // .def_rw("iter_ext", &RuizEquilibration::max_iter) // .def_rw("run_time", &RuizEquilibration::sym); - // ::nanobind::class_>(m, + // ::nanobind::class_>(m, // "ruiz") // .def(::nanobind::init(), "Default constructor.") // .def_rw("mu_eq", &RuizEquilibration::delta) @@ -58,20 +58,15 @@ exposeQpObjectDense(nanobind::module_ m) // .def_rw("run_time", &RuizEquilibration::sym); ::nanobind::class_>(m, "QP") - .def(::nanobind::init(), - nanobind::arg("n") = 0, - nanobind::arg("n_eq") = 0, - nanobind::arg("n_in") = 0, - nanobind::arg("box_constraints") = false, - nanobind::arg("hessian_type") = proxsuite::proxqp::HessianType::Dense, - nanobind::arg("dense_backend") = - proxsuite::proxqp::DenseBackend::Automatic, - "Default constructor using QP model dimensions.") // constructor + .def( + ::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, + nanobind::arg("box_constraints") = false, + nanobind::arg("hessian_type") = HessianType::Dense, + nanobind::arg("dense_backend") = DenseBackend::Automatic, + "Default constructor using QP model dimensions.") // constructor .def_rw("results", &dense::QP::results, "class containing the solution or certificate of infeasibility, " diff --git a/bindings/python/src/expose-solve.hpp b/bindings/python/src/proxqp/expose-solve.hpp similarity index 97% rename from bindings/python/src/expose-solve.hpp rename to bindings/python/src/proxqp/expose-solve.hpp index 89004cc8b..a33ad606b 100644 --- a/bindings/python/src/expose-solve.hpp +++ b/bindings/python/src/proxqp/expose-solve.hpp @@ -6,7 +6,7 @@ #include #include #include -#include "optional-eigen-fix.hpp" +#include "../optional-eigen-fix.hpp" namespace proxsuite { namespace proxqp { @@ -40,7 +40,7 @@ solveDenseQp(nanobind::module_ m) bool, bool, optional, - proxsuite::proxqp::InitialGuessStatus, + InitialGuessStatus, bool, optional, optional, @@ -101,7 +101,7 @@ solveDenseQp(nanobind::module_ m) bool, bool, optional, - proxsuite::proxqp::InitialGuessStatus, + InitialGuessStatus, bool, optional, optional, @@ -134,7 +134,7 @@ solveDenseQp(nanobind::module_ m) nanobind::arg("compute_timings") = false, nanobind::arg("max_iter") = nanobind::none(), nanobind::arg("initial_guess") = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, nanobind::arg("check_duality_gap") = false, nanobind::arg("eps_duality_gap_abs") = nanobind::none(), nanobind::arg("eps_duality_gap_rel") = nanobind::none(), @@ -161,7 +161,7 @@ solveDenseQp(nanobind::module_ m) bool, bool, optional, - proxsuite::proxqp::InitialGuessStatus, + InitialGuessStatus, bool, optional, optional, @@ -225,7 +225,7 @@ solveDenseQp(nanobind::module_ m) bool, bool, optional, - proxsuite::proxqp::InitialGuessStatus, + InitialGuessStatus, bool, optional, optional, @@ -260,7 +260,7 @@ solveDenseQp(nanobind::module_ m) nanobind::arg("compute_timings") = false, nanobind::arg("max_iter") = nanobind::none(), nanobind::arg("initial_guess") = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, nanobind::arg("check_duality_gap") = false, nanobind::arg("eps_duality_gap_abs") = nanobind::none(), nanobind::arg("eps_duality_gap_rel") = nanobind::none(), diff --git a/doc/2-ProxQP_api.md b/doc/2-ProxQP_api.md index fedebac47..5f0af91a8 100644 --- a/doc/2-ProxQP_api.md +++ b/doc/2-ProxQP_api.md @@ -576,7 +576,7 @@ In this table you have on the three columns from left to right: the name of the | iter_ext | 0 | Total number of outer iterations. | mu_updates | 0 | Total number of mu updates. | rho_updates | 0 | Total number of rho updates. -| status | PROXQP_NOT_RUN | Status of the solver. +| status | QPSOLVER_NOT_RUN | Status of the solver. | setup_time | 0 | Setup time (takes into account the equilibration procedure). | solve_time | 0 | Solve time (takes into account the first factorization). | run_time | 0 | the sum of the setup time and the solve time. @@ -606,12 +606,12 @@ Note finally that when initializing a QP object, by default, the proximal step s \subsection OverviewSolverStatus The solver's status The solver has five status: -* PROXQP_SOLVED: the problem is solved. -* PROXQP_MAX_ITER_REACHED: the maximum number of iterations has been reached. -* PROXQP_PRIMAL_INFEASIBLE: the problem is primal infeasible. -* PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE: the closest feasible problem in L2 sense is solved. -* PROXQP_DUAL_INFEASIBLE: the problem is dual infeasible. -* PROXQP_NOT_RUN: the solver has not been run yet. +* QPSOLVER_SOLVED: the problem is solved. +* QPSOLVER_MAX_ITER_REACHED: the maximum number of iterations has been reached. +* QPSOLVER_PRIMAL_INFEASIBLE: the problem is primal infeasible. +* QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE: the closest feasible problem in L2 sense is solved. +* QPSOLVER_DUAL_INFEASIBLE: the problem is dual infeasible. +* QPSOLVER_NOT_RUN: the solver has not been run yet. Infeasibility is detected using the necessary conditions exposed in [section 3.4](https://web.stanford.edu/~boyd/papers/pdf/osqp.pdf). More precisely, primal infeasibility is assumed if the following conditions are matched for some non zeros dy and dz (according to the eps_prim_inf variable set by the user): diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 6ae1bd686..75212ade5 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -4,17 +4,25 @@ add_custom_target(${PROJECT_NAME}-example-cpp) -function(ADD_PROXSUITE_CPP_EXAMPLE EXAMPLE) - get_filename_component(EXAMPLE_NAME ${EXAMPLE} NAME_WE) - set(EXAMPLE_TARGET "${PROJECT_NAME}-example-cpp-${EXAMPLE_NAME}") - ADD_UNIT_TEST(${EXAMPLE_TARGET} "${EXAMPLE}") - target_link_libraries(${EXAMPLE_TARGET} PRIVATE proxsuite-test-util) +file(GLOB_RECURSE ${PROJECT_NAME}_CPP_EXAMPLES *.cpp) +foreach(EXAMPLE_FILE ${${PROJECT_NAME}_CPP_EXAMPLES}) + get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" EXAMPLE_FILE_REL ${EXAMPLE_FILE}) - add_dependencies(${PROJECT_NAME}-example-cpp ${EXAMPLE_TARGET}) -endfunction() + string(REGEX REPLACE "^examples/cpp/" "" EXAMPLE_SUBPATH ${EXAMPLE_FILE_REL}) + get_filename_component(EXAMPLE_DIR ${EXAMPLE_SUBPATH} DIRECTORY) -file(GLOB_RECURSE ${PROJECT_NAME}_CPP_EXAMPLES *.cpp) + if(EXAMPLE_DIR STREQUAL "") + set(EXAMPLE_TARGET "${PROJECT_NAME}-example-cpp-${EXAMPLE_NAME}") + else() + string(REPLACE "/" "-" EXAMPLE_DIR_CLEAN ${EXAMPLE_DIR}) + set( + EXAMPLE_TARGET + "${PROJECT_NAME}-example-cpp-${EXAMPLE_DIR_CLEAN}-${EXAMPLE_NAME}" + ) + endif() -foreach(EXAMPLE ${${PROJECT_NAME}_CPP_EXAMPLES}) - add_proxsuite_cpp_example(${EXAMPLE}) + ADD_UNIT_TEST(${EXAMPLE_TARGET} "${EXAMPLE_FILE}") + target_link_libraries(${EXAMPLE_TARGET} PRIVATE proxsuite-test-util) + add_dependencies(${PROJECT_NAME}-example-cpp ${EXAMPLE_TARGET}) endforeach() diff --git a/examples/cpp/loading_dense_qp.cpp b/examples/cpp/loading_dense_qp.cpp deleted file mode 100644 index ae263bc6f..000000000 --- a/examples/cpp/loading_dense_qp.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "proxsuite/proxqp/dense/dense.hpp" - -using namespace proxsuite::proxqp; -using T = double; -int -main() -{ - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); - dense::QP qp(dim, n_eq, n_in); -} diff --git a/examples/cpp/overview-simple.cpp b/examples/cpp/osqp/overview-simple.cpp similarity index 52% rename from examples/cpp/overview-simple.cpp rename to examples/cpp/osqp/overview-simple.cpp index b8a3e1082..b21b604ab 100644 --- a/examples/cpp/overview-simple.cpp +++ b/examples/cpp/osqp/overview-simple.cpp @@ -1,27 +1,26 @@ #include -#include -#include // used for generating a random convex qp +#include +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() { // generate a QP problem T sparsity_factor = 0.15; - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - // we generate a qp, so the function used from helpers.hpp is - // in proxqp namespace. The qp is in dense eigen format and - // you can control its sparsity ratio and strong convexity factor. - dense::Model qp_random = utils::dense_strongly_convex_qp( + + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - // load PROXQP solver with dense backend and solve the problem - dense::QP qp(dim, n_eq, n_in); + // load OSQP solver with dense backend and solve the problem + osqp::dense::QP qp(dim, n_eq, n_in); qp.init(qp_random.H, qp_random.g, qp_random.A, diff --git a/examples/cpp/benchmark_dense_qp.cpp b/examples/cpp/proxqp/benchmark_dense_qp.cpp similarity index 89% rename from examples/cpp/benchmark_dense_qp.cpp rename to examples/cpp/proxqp/benchmark_dense_qp.cpp index 43e8637f4..6e92feff7 100644 --- a/examples/cpp/benchmark_dense_qp.cpp +++ b/examples/cpp/proxqp/benchmark_dense_qp.cpp @@ -45,10 +45,11 @@ Solve Time consumption(dense): 0.101507s */ #include #include -#include +#include -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -57,17 +58,17 @@ main() double solve_time = 0.0; double setup_time = 0.0; - dense::isize dim = 100; - dense::isize n_eq(dim / 2); - dense::isize n_in(dim / 2); + isize dim = 100; + isize n_eq(dim / 2); + isize n_in(dim / 2); for (T sparsity_factor = 0.1; sparsity_factor < 0.5; sparsity_factor += 0.1) { T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); for (int i = 0; i < N; i++) { - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.compute_timings = true; // compute all timings qp.settings.max_iter = 10000; qp.settings.max_iter_in = 1000; diff --git a/examples/cpp/estimate_nonconvex_eigenvalue.cpp b/examples/cpp/proxqp/estimate_nonconvex_eigenvalue.cpp similarity index 68% rename from examples/cpp/estimate_nonconvex_eigenvalue.cpp rename to examples/cpp/proxqp/estimate_nonconvex_eigenvalue.cpp index 658077bc1..49eacdee4 100644 --- a/examples/cpp/estimate_nonconvex_eigenvalue.cpp +++ b/examples/cpp/proxqp/estimate_nonconvex_eigenvalue.cpp @@ -1,36 +1,39 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite; -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() { - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // make the QP nonconvex - dense::Vec diag(dim); + common::dense::Vec diag(dim); diag.setOnes(); qp_random.H.diagonal().array() -= 2. * diag.array(); // add some nonpositive values dense matrix - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); // choose scaling for regularizing default_rho accordingly - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object // choose the option for estimating this eigenvalue T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( - qp_random.H, EigenValueEstimateMethodOption::ExactMethod, 1.E-6, 10000); + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); bool compute_preconditioner = false; // input the estimate for making rho appropriate qp.init(qp_random.H, diff --git a/examples/cpp/first_example_dense.cpp b/examples/cpp/proxqp/first_example_dense.cpp similarity index 92% rename from examples/cpp/first_example_dense.cpp rename to examples/cpp/proxqp/first_example_dense.cpp index e2f6492e2..29c8dedbe 100644 --- a/examples/cpp/first_example_dense.cpp +++ b/examples/cpp/proxqp/first_example_dense.cpp @@ -6,7 +6,8 @@ #include // for c++14 #include -using namespace proxsuite::proxqp; +using namespace proxsuite; +using namespace proxsuite::common; using proxsuite::nullopt; // c++17 simply use std::nullopt int @@ -18,7 +19,7 @@ main() // define the problem double eps_abs = 1e-9; - dense::isize dim = 3, n_eq = 0, n_in = 3; + isize dim = 3, n_eq = 0, n_in = 3; // cost H Eigen::MatrixXd H = Eigen::MatrixXd(dim, dim); @@ -44,7 +45,7 @@ main() std::cout << "u.T:" << u.transpose() << std::endl; // create qp object and pass some settings - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; diff --git a/examples/cpp/first_example_sparse.cpp b/examples/cpp/proxqp/first_example_sparse.cpp similarity index 90% rename from examples/cpp/first_example_sparse.cpp rename to examples/cpp/proxqp/first_example_sparse.cpp index 58fa062cb..27c9f1f1b 100644 --- a/examples/cpp/first_example_sparse.cpp +++ b/examples/cpp/proxqp/first_example_sparse.cpp @@ -7,7 +7,7 @@ #include // for c++14 #include -using namespace proxsuite::proxqp; +using namespace proxsuite; using proxsuite::nullopt; // c++17 simply use std::nullopt int @@ -20,7 +20,7 @@ main() // define the problem double eps_abs = 1e-9; - sparse::isize dim = 3, n_eq = 0, n_in = 3, nnz = 2; + proxqp::sparse::isize dim = 3, n_eq = 0, n_in = 3, nnz = 2; // random utils to generate sparse matrix std::random_device rd; // obtain a random number from hardware @@ -34,7 +34,7 @@ main() coefficients; // list of non-zeros coefficients coefficients.reserve(nnz); - for (sparse::isize k = 0; k < nnz; k++) { + for (proxqp::sparse::isize k = 0; k < nnz; k++) { int col = int_distr(gen); int row = int_distr(gen); double val = double_distr(gen); @@ -65,10 +65,10 @@ main() std::cout << "u.T:" << u.transpose() << std::endl; // create qp object and pass some settings - sparse::QP qp(dim, n_eq, n_in); + proxqp::sparse::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; qp.settings.verbose = true; // initialize qp with matrices describing the problem diff --git a/examples/cpp/init_dense_qp.cpp b/examples/cpp/proxqp/init_dense_qp.cpp similarity index 67% rename from examples/cpp/init_dense_qp.cpp rename to examples/cpp/proxqp/init_dense_qp.cpp index ed8955f91..38a15ee28 100644 --- a/examples/cpp/init_dense_qp.cpp +++ b/examples/cpp/proxqp/init_dense_qp.cpp @@ -1,9 +1,10 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -14,10 +15,10 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object qp.init(qp_random.H, qp_random.g, qp_random.A, diff --git a/examples/cpp/init_dense_qp_with_box.cpp b/examples/cpp/proxqp/init_dense_qp_with_box.cpp similarity index 74% rename from examples/cpp/init_dense_qp_with_box.cpp rename to examples/cpp/proxqp/init_dense_qp_with_box.cpp index 0d30ee7ef..a8c07d3e2 100644 --- a/examples/cpp/init_dense_qp_with_box.cpp +++ b/examples/cpp/proxqp/init_dense_qp_with_box.cpp @@ -1,9 +1,10 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -14,17 +15,17 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // specify some trivial box constraints - dense::Vec u_box(dim); - dense::Vec l_box(dim); + common::dense::Vec u_box(dim); + common::dense::Vec l_box(dim); u_box.setZero(); l_box.setZero(); u_box.array() += 1.E10; l_box.array() -= 1.E10; - dense::QP qp(dim, n_eq, n_in, true); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in, true); // create the QP object // and specify with true you take into account box constraints // in the model // you can check that there are constraints with method is_box_constrained diff --git a/examples/cpp/init_dense_qp_with_other_options.cpp b/examples/cpp/proxqp/init_dense_qp_with_other_options.cpp similarity index 79% rename from examples/cpp/init_dense_qp_with_other_options.cpp rename to examples/cpp/proxqp/init_dense_qp_with_other_options.cpp index 51d1ced81..d43b5b254 100644 --- a/examples/cpp/init_dense_qp_with_other_options.cpp +++ b/examples/cpp/proxqp/init_dense_qp_with_other_options.cpp @@ -1,9 +1,11 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { @@ -13,10 +15,10 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp( + proxqp::dense::QP qp( dim, n_eq, n_in); // create the QP // initialize the model, along with another rho parameter qp.init(qp_random.H, diff --git a/examples/cpp/init_dense_qp_with_timings.cpp b/examples/cpp/proxqp/init_dense_qp_with_timings.cpp similarity index 62% rename from examples/cpp/init_dense_qp_with_timings.cpp rename to examples/cpp/proxqp/init_dense_qp_with_timings.cpp index 95b2c3742..891447af6 100644 --- a/examples/cpp/init_dense_qp_with_timings.cpp +++ b/examples/cpp/proxqp/init_dense_qp_with_timings.cpp @@ -1,9 +1,11 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { @@ -13,11 +15,11 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object - qp.settings.compute_timings = true; // compute all timings + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object + qp.settings.compute_timings = true; // compute all timings qp.init(qp_random.H, qp_random.g, qp_random.A, diff --git a/examples/cpp/init_with_default_options.cpp b/examples/cpp/proxqp/init_with_default_options.cpp similarity index 85% rename from examples/cpp/init_with_default_options.cpp rename to examples/cpp/proxqp/init_with_default_options.cpp index b66cd75ee..8309b4fff 100644 --- a/examples/cpp/init_with_default_options.cpp +++ b/examples/cpp/proxqp/init_with_default_options.cpp @@ -1,9 +1,11 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { @@ -13,10 +15,10 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp( + proxqp::dense::QP qp( dim, n_eq, n_in); // create the QP // initialize the model, along with another rho parameter qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; diff --git a/examples/cpp/initializing_with_none.cpp b/examples/cpp/proxqp/initializing_with_none.cpp similarity index 76% rename from examples/cpp/initializing_with_none.cpp rename to examples/cpp/proxqp/initializing_with_none.cpp index dfabc4e6b..834ce3a2a 100644 --- a/examples/cpp/initializing_with_none.cpp +++ b/examples/cpp/proxqp/initializing_with_none.cpp @@ -1,22 +1,24 @@ #include #include "proxsuite/proxqp/dense/dense.hpp" -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { - dense::isize dim = 10; - dense::isize n_eq(0); - dense::isize n_in(0); - dense::QP qp(dim, n_eq, n_in); + isize dim = 10; + isize n_eq(0); + isize n_in(0); + proxqp::dense::QP qp(dim, n_eq, n_in); T strong_convexity_factor(0.1); T sparsity_factor(0.15); // we generate a qp, so the function used from helpers.hpp is // in proxqp namespace. The qp is in dense eigen format and // you can control its sparsity ratio and strong convexity factor. - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp.init(qp_random.H, diff --git a/examples/cpp/initializing_with_none_without_api.cpp b/examples/cpp/proxqp/initializing_with_none_without_api.cpp similarity index 59% rename from examples/cpp/initializing_with_none_without_api.cpp rename to examples/cpp/proxqp/initializing_with_none_without_api.cpp index 0bb05caf4..de0bcc443 100644 --- a/examples/cpp/initializing_with_none_without_api.cpp +++ b/examples/cpp/proxqp/initializing_with_none_without_api.cpp @@ -1,31 +1,33 @@ #include #include "proxsuite/proxqp/dense/dense.hpp" -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { - dense::isize dim = 10; - dense::isize n_eq(0); - dense::isize n_in(0); + isize dim = 10; + isize n_eq(0); + isize n_in(0); T strong_convexity_factor(0.1); T sparsity_factor(0.15); // we generate a qp, so the function used from helpers.hpp is // in proxqp namespace. The qp is in dense eigen format and // you can control its sparsity ratio and strong convexity factor. - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - Results results = - dense::solve(qp_random.H, - qp_random.g, - qp_random.A, - qp_random.b, - qp_random.C, - qp_random.l, - qp_random.u); // initialization with zero shape matrices + common::Results results = proxqp::dense::solve( + qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); // initialization with zero shape matrices // it is equivalent to do dense::solve(qp_random.H, qp_random.g, // nullopt,nullopt,nullopt,nullopt,nullopt); // print an optimal solution x,y and z diff --git a/examples/cpp/proxqp/loading_dense_qp.cpp b/examples/cpp/proxqp/loading_dense_qp.cpp new file mode 100644 index 000000000..c090328e2 --- /dev/null +++ b/examples/cpp/proxqp/loading_dense_qp.cpp @@ -0,0 +1,15 @@ +#include +#include "proxsuite/proxqp/dense/dense.hpp" + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +int +main() +{ + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + proxqp::dense::QP qp(dim, n_eq, n_in); +} diff --git a/examples/cpp/loading_dense_qp_with_box_ineq.cpp b/examples/cpp/proxqp/loading_dense_qp_with_box_ineq.cpp similarity index 75% rename from examples/cpp/loading_dense_qp_with_box_ineq.cpp rename to examples/cpp/proxqp/loading_dense_qp_with_box_ineq.cpp index 55c6b18f7..23877ea2c 100644 --- a/examples/cpp/loading_dense_qp_with_box_ineq.cpp +++ b/examples/cpp/proxqp/loading_dense_qp_with_box_ineq.cpp @@ -1,19 +1,21 @@ #include #include "proxsuite/proxqp/dense/dense.hpp" -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); // we load here a QP model with // n_eq equality constraints // n_in generic type of inequality constraints // and dim box inequality constraints - dense::QP qp(dim, n_eq, n_in, true); + proxqp::dense::QP qp(dim, n_eq, n_in, true); // true specifies we take into accounts box constraints // n_in are any other type of inequality constraints @@ -23,7 +25,7 @@ main() // n_eq equality constraints // O generic type of inequality constraints // and dim box inequality constraints - dense::QP qp2(dim, n_eq, 0, true); + proxqp::dense::QP qp2(dim, n_eq, 0, true); // true specifies we take into accounts box constraints // we don't need to precise n_in = dim, it is taken // into account internally diff --git a/examples/cpp/loading_dense_qp_with_different_backend_choice.cpp b/examples/cpp/proxqp/loading_dense_qp_with_different_backend_choice.cpp similarity index 68% rename from examples/cpp/loading_dense_qp_with_different_backend_choice.cpp rename to examples/cpp/proxqp/loading_dense_qp_with_different_backend_choice.cpp index 5220c3ec5..f1651660c 100644 --- a/examples/cpp/loading_dense_qp_with_different_backend_choice.cpp +++ b/examples/cpp/proxqp/loading_dense_qp_with_different_backend_choice.cpp @@ -1,21 +1,22 @@ #include #include "proxsuite/proxqp/dense/dense.hpp" -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); // we load here a QP model with // n_eq equality constraints // n_in generic type of inequality constraints // and dim box inequality constraints // we specify PrimalDualLDLT backend - dense::QP qp( - dim, n_eq, n_in, true, proxsuite::proxqp::DenseBackend::PrimalDualLDLT); + proxqp::dense::QP qp(dim, n_eq, n_in, true, DenseBackend::PrimalDualLDLT); // true specifies we take into accounts box constraints // n_in are any other type of inequality constraints @@ -26,15 +27,13 @@ main() // O generic type of inequality constraints // and dim box inequality constraints // we specify PrimalLDLT backend - dense::QP qp2( - dim, n_eq, 0, true, proxsuite::proxqp::DenseBackend::PrimalLDLT); + proxqp::dense::QP qp2(dim, n_eq, 0, true, DenseBackend::PrimalLDLT); // true specifies we take into accounts box constraints // we don't need to precise n_in = dim, it is taken // into account internally // we let here the solver decide with Automatic // backend choice. - dense::QP qp3( - dim, n_eq, 0, true, proxsuite::proxqp::DenseBackend::Automatic); + proxqp::dense::QP qp3(dim, n_eq, 0, true, DenseBackend::Automatic); // Note that it is the default choice } diff --git a/examples/cpp/loading_sparse_qp.cpp b/examples/cpp/proxqp/loading_sparse_qp.cpp similarity index 60% rename from examples/cpp/loading_sparse_qp.cpp rename to examples/cpp/proxqp/loading_sparse_qp.cpp index 37f8a0516..51fe94306 100644 --- a/examples/cpp/loading_sparse_qp.cpp +++ b/examples/cpp/proxqp/loading_sparse_qp.cpp @@ -1,9 +1,10 @@ #include #include // get the sparse API of ProxQP -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -12,17 +13,16 @@ main() isize n = 10; isize n_eq(n / 4); isize n_in(n / 4); - sparse::QP qp(n, n_eq, n_in); + proxqp::sparse::QP qp(n, n_eq, n_in); // assume you generate these matrices H, A and C for your QP problem T p = 0.15; // level of sparsity T conditioning = 10.0; // conditioning level for H - auto H = ::proxsuite::proxqp::utils::rand::sparse_positive_definite_rand( - n, conditioning, p); - auto A = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_eq, n, p); - auto C = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_in, n, p); + auto H = utils::rand::sparse_positive_definite_rand(n, conditioning, p); + auto A = utils::rand::sparse_matrix_rand(n_eq, n, p); + auto C = utils::rand::sparse_matrix_rand(n_in, n, p); // design a qp2 object using sparsity masks of H, A and C proxsuite::proxqp::sparse::QP qp2( diff --git a/examples/cpp/proxqp/overview-simple.cpp b/examples/cpp/proxqp/overview-simple.cpp new file mode 100644 index 000000000..b21b604ab --- /dev/null +++ b/examples/cpp/proxqp/overview-simple.cpp @@ -0,0 +1,36 @@ +#include +#include +#include // used for generating a random convex qp + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +int +main() +{ + // generate a QP problem + T sparsity_factor = 0.15; + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + // load OSQP solver with dense backend and solve the problem + osqp::dense::QP qp(dim, n_eq, n_in); + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + // print an optimal solution x,y and z + std::cout << "optimal x: " << qp.results.x << std::endl; + std::cout << "optimal y: " << qp.results.y << std::endl; + std::cout << "optimal z: " << qp.results.z << std::endl; +} diff --git a/examples/cpp/solve_dense_qp.cpp b/examples/cpp/proxqp/solve_dense_qp.cpp similarity index 74% rename from examples/cpp/solve_dense_qp.cpp rename to examples/cpp/proxqp/solve_dense_qp.cpp index 7c1b24e83..1cb12e8f8 100644 --- a/examples/cpp/solve_dense_qp.cpp +++ b/examples/cpp/proxqp/solve_dense_qp.cpp @@ -1,9 +1,10 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -15,10 +16,10 @@ main() T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -27,9 +28,9 @@ main() qp_random.l, qp_random.u); // initialize the model qp.solve(); // solve the problem without warm start - auto x_wm = utils::rand::vector_rand(dim); - auto y_wm = utils::rand::vector_rand(n_eq); - auto z_wm = utils::rand::vector_rand(n_in); + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); qp.solve(x_wm, y_wm, z_wm); // if you have a warm start, put it here // print an optimal solution x,y and z @@ -39,10 +40,10 @@ main() // Another example if you have box constraints (for the dense backend only for // the moment) - dense::QP qp2(dim, n_eq, n_in, true); // create the QP object + proxqp::dense::QP qp2(dim, n_eq, n_in, true); // create the QP object // some trivial boxes - dense::Vec u_box(dim); - dense::Vec l_box(dim); + common::dense::Vec u_box(dim); + common::dense::Vec l_box(dim); u_box.setZero(); l_box.setZero(); u_box.array() += 1.E10; diff --git a/examples/cpp/solve_dense_qp_with_setting.cpp b/examples/cpp/proxqp/solve_dense_qp_with_setting.cpp similarity index 73% rename from examples/cpp/solve_dense_qp_with_setting.cpp rename to examples/cpp/proxqp/solve_dense_qp_with_setting.cpp index 5442bf277..f7f8c7795 100644 --- a/examples/cpp/solve_dense_qp_with_setting.cpp +++ b/examples/cpp/proxqp/solve_dense_qp_with_setting.cpp @@ -1,23 +1,24 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() { - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object qp.init(qp_random.H, qp_random.g, qp_random.A, diff --git a/examples/cpp/solve_without_api.cpp b/examples/cpp/proxqp/solve_without_api.cpp similarity index 55% rename from examples/cpp/solve_without_api.cpp rename to examples/cpp/proxqp/solve_without_api.cpp index 67bee1a85..36c425372 100644 --- a/examples/cpp/solve_without_api.cpp +++ b/examples/cpp/proxqp/solve_without_api.cpp @@ -1,12 +1,14 @@ #include #include "proxsuite/proxqp/sparse/sparse.hpp" // get the sparse backend of ProxQP #include "proxsuite/proxqp/dense/dense.hpp" // get the dense backend of ProxQP -#include "proxsuite/proxqp/utils/random_qp_problems.hpp" // used for generating a random convex qp +#include "proxsuite/common/utils/random_qp_problems.hpp" // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; -using Mat = dense::Mat; -using Vec = dense::Vec; +using namespace proxsuite; +using namespace proxsuite::common; + +using Mat = common::dense::Mat; +using Vec = common::dense::Vec; int main() @@ -16,23 +18,23 @@ main() isize n_in(n / 4); T p = 0.35; // level of sparsity T conditioning = 10.0; // conditioning level for H - auto H = utils::rand::sparse_positive_definite_rand( + auto H = common::utils::rand::sparse_positive_definite_rand( n, conditioning, p); // upper triangular matrix Mat H_dense = Mat(H); H_dense.template triangularView() = H_dense.transpose(); - Vec g = utils::rand::vector_rand(n); - auto A = utils::rand::sparse_matrix_rand(n_eq, n, p); + Vec g = common::utils::rand::vector_rand(n); + auto A = common::utils::rand::sparse_matrix_rand(n_eq, n, p); Mat A_dense = Mat(A); - auto C = utils::rand::sparse_matrix_rand(n_in, n, p); + auto C = common::utils::rand::sparse_matrix_rand(n_in, n, p); Mat C_dense = Mat(C); - Vec x_sol = utils::rand::vector_rand(n); + Vec x_sol = common::utils::rand::vector_rand(n); Vec b = A * x_sol; Vec l = C * x_sol; Vec u = (l.array() + 10).matrix(); // Solve the problem using the sparse backend - Results results_sparse_solver = - sparse::solve(H, g, A, b, C, l, u); + common::Results results_sparse_solver = + proxqp::sparse::solve(H, g, A, b, C, l, u); std::cout << "optimal x from sparse solver: " << results_sparse_solver.x << std::endl; std::cout << "optimal y from sparse solver: " << results_sparse_solver.y @@ -40,8 +42,8 @@ main() std::cout << "optimal z from sparse solver: " << results_sparse_solver.z << std::endl; // Solve the problem using the dense backend - Results results_dense_solver = - dense::solve(H_dense, g, A_dense, b, C_dense, l, u); + common::Results results_dense_solver = + proxqp::dense::solve(H_dense, g, A_dense, b, C_dense, l, u); // print an optimal solution x,y and z std::cout << "optimal x from dense solver: " << results_dense_solver.x @@ -64,24 +66,25 @@ main() // make sure to specify at least next 9 variables of the solve function after // u_box to make sure the overloading work and use the specific feature for // handling more efficiently box constraints - Results results_dense_solver_box = dense::solve(H_dense, - g, - A_dense, - b, - C_dense, - l, - u, - l_box, - u_box, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt, - proxsuite::nullopt); + common::Results results_dense_solver_box = + proxqp::dense::solve(H_dense, + g, + A_dense, + b, + C_dense, + l, + u, + l_box, + u_box, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt, + proxsuite::nullopt); // print an optimal solution x,y and z std::cout << "optimal x from dense solver: " << results_dense_solver.x << std::endl; diff --git a/examples/cpp/proxqp/solve_without_api_and_option.cpp b/examples/cpp/proxqp/solve_without_api_and_option.cpp new file mode 100644 index 000000000..be8ce07a2 --- /dev/null +++ b/examples/cpp/proxqp/solve_without_api_and_option.cpp @@ -0,0 +1,42 @@ +#include +#include // get the sparse backend of ProxQP +#include // get the dense backend of ProxQP +#include // used for generating a random convex qp + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +int +main() +{ + isize n = 10; + isize n_eq(n / 4); + isize n_in(n / 4); + T sparsity_factor(0.15); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + // Solve the problem using the dense backend + // and suppose you want to change the accuracy to 1.E-9 and rho initial value + // to 1.E-7 + Results results = proxsuite::proxqp::dense::solve(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + nullopt, + nullopt, + nullopt, + T(1.E-9), + nullopt, + nullopt, + nullopt, + T(1.E-7)); + // print an optimal solution x,y and z + std::cout << "optimal x: " << results.x << std::endl; + std::cout << "optimal y: " << results.y << std::endl; + std::cout << "optimal z: " << results.z << std::endl; +} diff --git a/examples/cpp/update_dense_qp.cpp b/examples/cpp/proxqp/update_dense_qp.cpp similarity index 76% rename from examples/cpp/update_dense_qp.cpp rename to examples/cpp/proxqp/update_dense_qp.cpp index 6498e9aff..73c6b61fd 100644 --- a/examples/cpp/update_dense_qp.cpp +++ b/examples/cpp/proxqp/update_dense_qp.cpp @@ -1,9 +1,11 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { @@ -14,10 +16,10 @@ main() // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -27,7 +29,7 @@ main() qp_random.u); // initialize the model qp.solve(); // solve the problem // a new qp problem - dense::Model qp2 = utils::dense_strongly_convex_qp( + common::dense::Model qp2 = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // re update the model qp.update(qp2.H, qp2.g, qp2.A, qp2.b, qp2.C, qp2.l, qp2.u); @@ -38,9 +40,9 @@ main() std::cout << "optimal y: " << qp.results.y << std::endl; std::cout << "optimal z: " << qp.results.z << std::endl; // if you have boxes (dense backend only) you proceed the same way - dense::QP qp_box(dim, n_eq, n_in, true); // create the QP object - dense::Vec u_box(dim); - dense::Vec l_box(dim); + proxqp::dense::QP qp_box(dim, n_eq, n_in, true); // create the QP object + common::dense::Vec u_box(dim); + common::dense::Vec l_box(dim); u_box.setZero(); l_box.setZero(); u_box.array() += 1.E10; diff --git a/examples/cpp/update_dense_qp_ws_previous_result.cpp b/examples/cpp/proxqp/update_dense_qp_ws_previous_result.cpp similarity index 80% rename from examples/cpp/update_dense_qp_ws_previous_result.cpp rename to examples/cpp/proxqp/update_dense_qp_ws_previous_result.cpp index cb4426114..1ffa1815e 100644 --- a/examples/cpp/update_dense_qp_ws_previous_result.cpp +++ b/examples/cpp/proxqp/update_dense_qp_ws_previous_result.cpp @@ -1,23 +1,24 @@ #include #include // load the dense solver backend -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite; -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + int main() { - dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP qp(dim, n_eq, n_in); // create the QP object qp.init(qp_random.H, qp_random.g, qp_random.A, diff --git a/examples/cpp/update_sparse_qp.cpp b/examples/cpp/proxqp/update_sparse_qp.cpp similarity index 73% rename from examples/cpp/update_sparse_qp.cpp rename to examples/cpp/proxqp/update_sparse_qp.cpp index 144919c6c..18959c595 100644 --- a/examples/cpp/update_sparse_qp.cpp +++ b/examples/cpp/proxqp/update_sparse_qp.cpp @@ -1,10 +1,10 @@ #include #include // get the sparse API of ProxQP -#include // used for generating a random convex qp +#include // used for generating a random convex qp -using namespace proxsuite; -using namespace proxsuite::proxqp; using T = double; +using namespace proxsuite; +using namespace proxsuite::common; int main() @@ -15,12 +15,11 @@ main() T p = 0.15; // level of sparsity T conditioning = 10.0; // conditioning level for H - auto H = ::proxsuite::proxqp::utils::rand::sparse_positive_definite_rand( - n, conditioning, p); - auto g = ::proxsuite::proxqp::utils::rand::vector_rand(n); - auto A = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_eq, n, p); - auto C = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_in, n, p); - auto x_sol = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto H = ::utils::rand::sparse_positive_definite_rand(n, conditioning, p); + auto g = ::utils::rand::vector_rand(n); + auto A = ::utils::rand::sparse_matrix_rand(n_eq, n, p); + auto C = ::utils::rand::sparse_matrix_rand(n_in, n, p); + auto x_sol = ::utils::rand::vector_rand(n); auto b = A * x_sol; auto l = C * x_sol; auto u = (l.array() + 10).matrix().eval(); @@ -40,8 +39,7 @@ main() nullopt); // update H with H_new, it will work qp.solve(); // generate H2 with another sparsity structure - auto H2 = ::proxsuite::proxqp::utils::rand::sparse_positive_definite_rand( - n, conditioning, p); + auto H2 = ::utils::rand::sparse_positive_definite_rand(n, conditioning, p); qp.update(H2, nullopt, nullopt, @@ -50,7 +48,7 @@ main() nullopt, nullopt); // nothing will happen // if only a vector changes, then the update takes effect - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::utils::rand::vector_rand(n); qp.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp.solve(); // it solves the problem with another vector // to solve the problem with H2 matrix create a new qp object diff --git a/examples/cpp/solve_without_api_and_option.cpp b/examples/cpp/solve_without_api_and_option.cpp deleted file mode 100644 index 365981f31..000000000 --- a/examples/cpp/solve_without_api_and_option.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include // get the sparse backend of ProxQP -#include // get the dense backend of ProxQP -#include // used for generating a random convex qp - -using namespace proxsuite; -using namespace proxsuite::proxqp; -using T = double; - -int -main() -{ - isize n = 10; - isize n_eq(n / 4); - isize n_in(n / 4); - T sparsity_factor(0.15); - T strong_convexity_factor(1.e-2); - dense::Model qp_random = utils::dense_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - // Solve the problem using the dense backend - // and suppose you want to change the accuracy to 1.E-9 and rho initial value - // to 1.E-7 - proxsuite::proxqp::Results results = - proxsuite::proxqp::dense::solve(qp_random.H, - qp_random.g, - qp_random.A, - qp_random.b, - qp_random.C, - qp_random.l, - qp_random.u, - nullopt, - nullopt, - nullopt, - T(1.E-9), - nullopt, - nullopt, - nullopt, - T(1.E-7)); - // print an optimal solution x,y and z - std::cout << "optimal x: " << results.x << std::endl; - std::cout << "optimal y: " << results.y << std::endl; - std::cout << "optimal z: " << results.z << std::endl; -} diff --git a/examples/julia/CMakeLists.txt b/examples/julia/CMakeLists.txt index 051eb61a4..7c5a35edc 100644 --- a/examples/julia/CMakeLists.txt +++ b/examples/julia/CMakeLists.txt @@ -1,13 +1,7 @@ file(GLOB_RECURSE ${PROJECT_NAME}_JULIA_EXAMPLES *.jl) foreach(EXAMPLE ${${PROJECT_NAME}_JULIA_EXAMPLES}) - string( - REGEX REPLACE - "${PROJECT_SOURCE_DIR}/examples/julia/" - "" - EXAMPLE - ${EXAMPLE} - ) + string(REPLACE "${PROJECT_SOURCE_DIR}/examples/julia/" "" EXAMPLE ${EXAMPLE}) ADD_JULIA_UNIT_TEST( "${PROJECT_NAME}-example-jl-${EXAMPLE}" "examples/julia/${EXAMPLE}" diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 31f46b63d..2800167e7 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -1,11 +1,35 @@ file(GLOB_RECURSE ${PROJECT_NAME}_PYTHON_EXAMPLES *.py) - foreach(EXAMPLE_FILE ${${PROJECT_NAME}_PYTHON_EXAMPLES}) get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) - string(REGEX REPLACE "${PROJECT_SOURCE_DIR}" "" EXAMPLE_FILE ${EXAMPLE_FILE}) + + if(EXAMPLE_FILE MATCHES ".*/calibration/.*") + continue() + endif() + + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" EXAMPLE_FILE_REL ${EXAMPLE_FILE}) + + string( + REGEX REPLACE + "^examples/python/" + "" + EXAMPLE_SUBPATH + ${EXAMPLE_FILE_REL} + ) + get_filename_component(EXAMPLE_DIR ${EXAMPLE_SUBPATH} DIRECTORY) + + if(EXAMPLE_DIR STREQUAL "") + set(EXAMPLE_TARGET "${PROJECT_NAME}-example-py-${EXAMPLE_NAME}") + else() + string(REPLACE "/" "-" EXAMPLE_DIR_CLEAN ${EXAMPLE_DIR}) + set( + EXAMPLE_TARGET + "${PROJECT_NAME}-example-py-${EXAMPLE_DIR_CLEAN}-${EXAMPLE_NAME}" + ) + endif() + ADD_PYTHON_UNIT_TEST( - "${PROJECT_NAME}-example-py-${EXAMPLE_NAME}" - "${EXAMPLE_FILE}" + "${EXAMPLE_TARGET}" + "${EXAMPLE_FILE_REL}" "bindings/python" ) endforeach() diff --git a/examples/python/osqp/calibration/box_constrained_qp.py b/examples/python/osqp/calibration/box_constrained_qp.py new file mode 100644 index 000000000..a6114d15e --- /dev/null +++ b/examples/python/osqp/calibration/box_constrained_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="box_constrained_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/calibration_base.py b/examples/python/osqp/calibration/calibration_base.py new file mode 100644 index 000000000..46c85e7cc --- /dev/null +++ b/examples/python/osqp/calibration/calibration_base.py @@ -0,0 +1,696 @@ +import proxsuite +import osqp + +import numpy as np +import scipy.sparse as spa + +from utils import ( + infty_norm, + status_to_string, + status_polish_to_string, + string_to_check_if_solved_option, +) +from utils import ( + unconstrained_qp, + strongly_convex_qp, + not_strongly_convex_qp, + degenerate_qp, + box_constrained_qp, + primal_infeasible_qp, + dual_infeasible_qp, +) + + +def solve_qp( + problem: str, + dim: int, + n_eq: int, + n_in: int, + m: int, + max_iter: int = 4000, + compute_preconditioner: bool = True, + eps_abs: float = 1e-3, + eps_rel: float = 0, + eps_primal_inf: float = 1e-4, + eps_dual_inf: float = 1e-4, + check_if_solved_option_str: str = "Iteration based", + check_termination: int = 25, + frequence_infeasibility_check: int = 1, + sparsity_factor: float = 0.45, + strong_convexity_factor: float = 1e-2, + adaptive_mu: bool = False, + adaptive_mu_interval: int = 50, + adaptive_mu_tolerance: float = 5.0, + polish: bool = False, + delta: float = 1e-6, + polish_refine_iter: int = 3, + verbose_solver: bool = False, + verbose_results_variables: bool = False, + verbose_calibration: bool = False, + verbose_timings: bool = False, +): + if problem == "unconstrained_qp": + H, g, A, b, C, u, l = unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor + ) + elif problem == "strongly_convex_qp": + H, g, A, b, C, u, l = strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor + ) + elif problem == "not_strongly_convex_qp": + H, g, A, b, C, u, l = not_strongly_convex_qp(dim, n_eq, n_in, sparsity_factor) + elif problem == "degenerate_qp": + H, g, A, b, C, u, l = degenerate_qp( + dim, n_eq, m, sparsity_factor, strong_convexity_factor + ) + elif problem == "box_constrained_qp": + H, g, A, b, C, u, l = box_constrained_qp( + dim, n_eq, sparsity_factor, strong_convexity_factor + ) + elif problem == "primal_infeasible_qp": + H, g, A, b, C, u, l = primal_infeasible_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor + ) + elif problem == "dual_infeasible_qp": + H, g, A, b, C, u, l = dual_infeasible_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor + ) + + # OSQP proxsuite + proxsuite_osqp = proxsuite.osqp.dense.QP(dim, n_eq, n_in) + proxsuite_osqp.init(H, g, A, b, C, l, u) + + proxsuite_osqp.settings.verbose = verbose_solver + + proxsuite_osqp.settings.eps_abs = eps_abs + proxsuite_osqp.settings.eps_rel = eps_rel + + proxsuite_osqp.settings.eps_primal_inf = eps_primal_inf + proxsuite_osqp.settings.eps_dual_inf = eps_dual_inf + + check_solved_option = string_to_check_if_solved_option(check_if_solved_option_str) + proxsuite_osqp.settings.check_solved_option = check_solved_option + proxsuite_osqp.settings.check_termination = check_termination + proxsuite_osqp.settings.frequence_infeasibility_check = ( + frequence_infeasibility_check + ) + + proxsuite_osqp.settings.adaptive_mu = adaptive_mu + proxsuite_osqp.settings.adaptive_mu_interval = adaptive_mu_interval + proxsuite_osqp.settings.adaptive_mu_tolerance = adaptive_mu_tolerance + + proxsuite_osqp.settings.polish = polish + proxsuite_osqp.settings.delta = delta + proxsuite_osqp.settings.polish_refine_iter = polish_refine_iter + + proxsuite_osqp.settings.max_iter = max_iter + proxsuite_osqp.settings.compute_preconditioner = compute_preconditioner + + proxsuite_osqp.solve() + + # OSQP source code + H_source = spa.csc_matrix(H) + A_sparse = spa.csc_matrix(A) + C_sparse = spa.csc_matrix(C) + g_source = g + l_source = np.concatenate([b, l]) + u_source = np.concatenate([b, u]) + A_source = spa.vstack([A_sparse, C_sparse], format="csc") + + check_termination_source = ( + check_termination if (check_if_solved_option_str == "Interval based") else 1 + ) + + prob = osqp.OSQP() + prob.setup( + H_source, + g_source, + A_source, + l_source, + u_source, + eps_abs=eps_abs, + eps_rel=eps_rel, + eps_prim_inf=eps_primal_inf, + eps_dual_inf=eps_dual_inf, + sigma=1e-6, + rho=0.1, + verbose=verbose_solver, + scaling=10 if compute_preconditioner else 0, + max_iter=max_iter, + warm_start=False, + check_termination=check_termination_source, + adaptive_rho=adaptive_mu, + adaptive_rho_interval=adaptive_mu_interval, + adaptive_rho_tolerance=adaptive_mu_tolerance, + polish=polish, + delta=delta, + polish_refine_iter=polish_refine_iter, + ) + res_source = prob.solve() + + # Check results + x_proxsuite = proxsuite_osqp.results.x + x_source = res_source.x + + y_proxsuite = proxsuite_osqp.results.y + z_proxsuite = proxsuite_osqp.results.z + yz_proxsuite = np.concatenate([y_proxsuite, z_proxsuite]) + y_source = res_source.y + + r_pri_proxsuite = proxsuite_osqp.results.info.pri_res + r_pri_source = res_source.info.pri_res + + r_dua_proxsuite = proxsuite_osqp.results.info.dua_res + r_dua_source = res_source.info.dua_res + + iter_proxsuite = proxsuite_osqp.results.info.iter_ext + iter_source = res_source.info.iter + + mu_updates_proxsuite = proxsuite_osqp.results.info.mu_updates + mu_updates_source = res_source.info.rho_updates + + status_proxsuite = proxsuite_osqp.results.info.status + status_source = res_source.info.status + + status_polish_proxsuite = proxsuite_osqp.results.info.status_polish + status_polish_source = res_source.info.status_polish + + setup_time_proxsuite = proxsuite_osqp.results.info.setup_time + setup_time_source = res_source.info.setup_time + + solve_time_proxsuite = proxsuite_osqp.results.info.solve_time + solve_time_source = res_source.info.solve_time + + run_time_proxsuite = proxsuite_osqp.results.info.run_time + run_time_source = res_source.info.run_time + + # Prints calibration OSQP proxsuite vs source + if verbose_results_variables or verbose_calibration: + print("-----------------------------------------------------------------") + print("Comparison of results between OSQP proxsuite and source") + print("") + + if verbose_results_variables: + print("x") + print("OSQP proxsuite") + print(x_proxsuite) + print("OSQP source") + print(x_source) + print("") + + print("(y, z) (proxsuite) vs y (source)") + print("OSQP proxsuite") + print(yz_proxsuite) + print("OSQP source") + print(y_source) + print("") + + if verbose_calibration: + print("r_pri") + print("OSQP proxsuite") + print(r_pri_proxsuite) + print("OSQP source") + print(r_pri_source) + print("") + + print("r_dua") + print("OSQP proxsuite") + print(r_dua_proxsuite) + print("OSQP source") + print(r_dua_source) + print("") + + print("iter") + print("OSQP proxsuite") + print(iter_proxsuite) + print("OSQP source") + print(iter_source) + print("") + + print("status") + print("OSQP proxsuite") + print(status_to_string(status_proxsuite, "proxsuite")) + print("OSQP source") + print(status_to_string(status_source, "source")) + + if adaptive_mu: + print("mu_updates") + print("OSQP proxsuite") + print(mu_updates_proxsuite) + print("OSQP source") + print(mu_updates_source) + print("") + + if polish: + print("status_polish") + print("OSQP proxsuite") + print(status_polish_to_string(status_polish_proxsuite, "proxsuite")) + print("OSQP source") + print(status_polish_to_string(status_polish_source, "source")) + print("") + + if verbose_timings: + print("setup_time (micro sec)") + print("OSQP proxsuite") + print(setup_time_proxsuite) + print("OSQP source") + print(1e6 * setup_time_source) + print("") + + print("solve_time (micro sec)") + print("OSQP proxsuite") + print(solve_time_proxsuite) + print("OSQP source") + print(1e6 * solve_time_source) + print("") + + print("run_time (micro sec)") + print("OSQP proxsuite") + print(run_time_proxsuite) + print("OSQP source") + print(1e6 * run_time_source) + print("") + + # Calibration results + cal_res = { + "x_proxsuite": x_proxsuite, + "x_source": x_source, + "yz_proxsuite": yz_proxsuite, + "y_source": y_source, + "r_pri_proxsuite": r_pri_proxsuite, + "r_pri_source": r_pri_source, + "r_dua_proxsuite": r_dua_proxsuite, + "r_dua_source": r_dua_source, + "iter_proxsuite": iter_proxsuite, + "iter_source": iter_source, + "mu_updates_proxsuite": mu_updates_proxsuite, + "mu_updates_source": mu_updates_source, + "status_proxsuite": status_proxsuite, + "status_source": status_source, + "status_polish_proxsuite": status_polish_proxsuite, + "status_polish_source": status_polish_source, + } + + return cal_res + + +def test_calibration_qp( + problem: str, + run_test: bool = False, + dim_start: int = 10, + dim_end: int = 1000, + dim_step: int = 20, + only_eq: bool = False, + only_in: bool = False, + max_iter: int = 4000, + compute_preconditioner: bool = True, + eps_abs: float = 1e-3, + eps_rel: float = 0, + eps_primal_inf: float = 1e-4, + eps_dual_inf: float = 1e-4, + check_if_solved_option_str: str = "Iteration based", + check_termination: int = 25, + frequence_infeasibility_check: int = 1, + sparsity_factor: float = 0.45, + strong_convexity_factor: float = 1e-2, + adaptive_mu: bool = False, + adaptive_mu_interval: int = 50, + adaptive_mu_tolerance: float = 5.0, + polish: bool = False, + delta: float = 1e-6, + polish_refine_iter: int = 3, + verbose_test_settings: bool = False, + verbose_solver: bool = False, + verbose_results_variables: bool = False, + verbose_calibration: bool = False, + verbose_timings: bool = False, + prec_x: float = 1e-3, + prec_yz: float = 1e-3, + prec_r_pri: float = 1e-3, + prec_r_dua: float = 1e-3, + prec_polish: float = 1e-9, + prec_iter: int = 0, + prec_mu_updates: int = 0, +): + # Run test + if not run_test: + return + + # Constraints setting + if only_eq and only_in: + print("only_eq and only_in cannot be set together") + return + + if verbose_test_settings: + function_args = locals().copy() + print("Calibration test settings:") + for param_name, param_value in function_args.items(): + print(f" {param_name}: {param_value}") + print("") + + # Diff lists and failed tests + diff_x_lst = [] + diff_yz_lst = [] + diff_r_pri_lst = [] + diff_r_dua_lst = [] + diff_iter_lst = [] + diff_mu_updates_lst = [] + diff_status_lst = [] + diff_status_polish_lst = [] + + nb_tests = 0 + failed_tests = 0 + + for dim in range(dim_start, dim_end, dim_step): + if problem in [ + "strongly_convex_qp", + "not_strongly_convex_qp", + "primal_infeasible_qp", + "dual_infeasible_qp", + ]: + if only_eq: + n_eq = dim // 2 + n_in = 0 + elif only_in: + n_in = dim // 2 + n_eq = 0 + else: + n_eq = dim // 4 + n_in = dim // 4 + m = 0 + + elif problem == "unconstrained_qp": + n_eq = 0 + n_in = 0 + m = 0 + + elif problem == "degenerate_qp": + if only_eq: + n_eq = dim // 2 + n_in = 0 + m = 0 + elif only_in: + m = dim // 4 + n_in = 2 * m + n_eq = 0 + else: + m = dim // 4 + n_in = 2 * m + n_eq = dim // 4 + + elif problem == "box_constrained_qp": + if only_eq: + print("only_eq makes no sense in box_constrained_qp") + return + elif only_in: + n_eq = 0 + n_in = dim + else: + n_eq = dim // 4 + n_in = dim + m = 0 + + cal_res = solve_qp( + problem=problem, + dim=dim, + n_eq=n_eq, + n_in=n_in, + m=m, + max_iter=max_iter, + compute_preconditioner=compute_preconditioner, + eps_abs=eps_abs, + eps_rel=eps_rel, + eps_primal_inf=eps_primal_inf, + eps_dual_inf=eps_dual_inf, + check_if_solved_option_str=check_if_solved_option_str, + check_termination=check_termination, + frequence_infeasibility_check=frequence_infeasibility_check, + sparsity_factor=sparsity_factor, + strong_convexity_factor=strong_convexity_factor, + adaptive_mu=adaptive_mu, + adaptive_mu_interval=adaptive_mu_interval, + adaptive_mu_tolerance=adaptive_mu_tolerance, + polish=polish, + delta=delta, + polish_refine_iter=polish_refine_iter, + verbose_solver=verbose_solver, + verbose_results_variables=verbose_results_variables, + verbose_calibration=verbose_calibration, + verbose_timings=verbose_timings, + ) + + x_proxsuite = cal_res["x_proxsuite"] + x_source = cal_res["x_source"] + yz_proxsuite = cal_res["yz_proxsuite"] + y_source = cal_res["y_source"] + r_pri_proxsuite = cal_res["r_pri_proxsuite"] + r_pri_source = cal_res["r_pri_source"] + r_dua_proxsuite = cal_res["r_dua_proxsuite"] + r_dua_source = cal_res["r_dua_source"] + iter_proxsuite = cal_res["iter_proxsuite"] + iter_source = cal_res["iter_source"] + mu_updates_proxsuite = cal_res["mu_updates_proxsuite"] + mu_updates_source = cal_res["mu_updates_source"] + status_proxsuite = cal_res["status_proxsuite"] + status_source = cal_res["status_source"] + status_polish_proxsuite = cal_res["status_polish_proxsuite"] + status_polish_source = cal_res["status_polish_source"] + + status_proxsuite_str = status_to_string(status_proxsuite, "proxsuite") + status_source_str = status_to_string(status_source, "source") + + same_status = status_proxsuite_str == status_source_str + + status_polish_proxsuite_str = status_polish_to_string( + status_polish_proxsuite, "proxsuite" + ) + status_polish_source_str = status_polish_to_string( + status_polish_source, "source" + ) + + same_status_polish = status_polish_proxsuite_str == status_polish_source_str + + error_iter = np.abs(iter_proxsuite - iter_source) + same_iter = error_iter <= prec_iter + + error_mu_updates = np.abs(mu_updates_proxsuite - mu_updates_source) + same_mu_updates = error_mu_updates <= prec_mu_updates + + same_x = True + same_yz = True + same_r_pri = True + same_r_dua = True + + same_pol_success = ( + same_status_polish and status_polish_source_str == "Polishing: succeeded" + ) + + eps_x = prec_x + eps_yz = prec_yz + eps_r_pri = prec_r_pri + eps_r_dua = prec_r_dua + + if same_pol_success: + eps_x = prec_polish + eps_yz = prec_polish + eps_r_pri = prec_polish + eps_r_dua = prec_polish + + # Prevent x_source or y_source = [None, None, None, ...] + if not ( + status_source == "primal infeasible" or status_source == "dual infeasible" + ): + error_x = infty_norm(x_proxsuite - x_source) + same_x = error_x <= eps_x + + error_yz = infty_norm(yz_proxsuite - y_source) + same_yz = error_yz <= eps_yz + + error_r_pri = np.abs(r_pri_proxsuite - r_pri_source) + same_r_pri = error_r_pri <= eps_r_pri + + error_r_dua = np.abs(r_dua_proxsuite - r_dua_source) + same_r_dua = error_r_dua <= eps_r_dua + + if not ( + status_source == "primal infeasible" or status_source == "dual infeasible" + ): + if not same_x: + print("x differs in dim = ", dim, " at precision ", eps_x, ":") + if dim <= 30: + print("Proxsuite: ") + print(x_proxsuite) + print("Source: ") + print(x_source) + else: + print("dim ", dim, " > 30 too large for visualization.") + print("Max error: ") + print(error_x) + print("") + diff_x_lst.append(dim) + + if not same_yz: + print("yz differs in dim = ", dim, " at precision ", eps_yz, ":") + if n_eq + n_in <= 30: + print("Proxsuite: ") + print(yz_proxsuite) + print("Source: ") + print(y_source) + else: + print( + "n_eq + n_in ", + n_eq + n_in, + " > 30 too large for visualization.", + ) + print("Max error: ") + print(error_yz) + print("") + diff_yz_lst.append(dim) + + if not same_r_pri: + print("r_pri differs in dim = ", dim, " at precision ", eps_r_pri, ":") + print("Proxsuite: ") + print(r_pri_proxsuite) + print("Source: ") + print(r_pri_source) + print("Error") + print(error_r_pri) + print("") + diff_r_pri_lst.append(dim) + + if not same_r_dua: + print("r_dua differs in dim = ", dim, " at precision ", eps_r_dua, ":") + print("Proxsuite: ") + print(r_dua_proxsuite) + print("Source: ") + print(r_dua_source) + print("Error") + print(error_r_dua) + print("") + diff_r_dua_lst.append(dim) + + if not same_iter: + print("iter differs in dim = ", dim, " at precision ", prec_iter, ":") + print("Proxsuite: ") + print(iter_proxsuite) + print("Source: ") + print(iter_source) + print("Error") + print(error_iter) + print("") + diff_iter_lst.append(dim) + + if not same_mu_updates: + print( + "mu_updates differs in dim = ", + dim, + " at precision ", + prec_mu_updates, + ":", + ) + print("Proxsuite: ") + print(mu_updates_proxsuite) + print("Source: ") + print(mu_updates_source) + print("Error") + print(error_mu_updates) + print("") + diff_mu_updates_lst.append(dim) + + if not same_status: + print("status differs in dim = ", dim, ":") + print("Proxsuite: ") + print(status_proxsuite_str) + print("Source: ") + print(status_source_str) + diff_status_lst.append(dim) + print("") + + if not same_status_polish: + print("status_polish differs in dim = ", dim, ":") + print("Proxsuite: ") + print(status_polish_to_string(status_polish_proxsuite, "proxsuite")) + print("Source: ") + print(status_polish_to_string(status_polish_source, "source")) + diff_status_polish_lst.append(dim) + print("") + + if not ( + same_x + and same_yz + and same_r_pri + and same_r_dua + and same_iter + and same_mu_updates + and same_status + and same_status_polish + ): + failed_tests += 1 + nb_tests += 1 + + print("Results of calibration test") + print("Number of tests: ", nb_tests, " | Tests failed: ", failed_tests) + print("") + + if polish: + print( + "diff criteria at a given dim:\n", + "prec_polish =", + prec_polish, + "if both solvers gave results with polishing, \n else prec_x =", + prec_x, + ", prec_yz =", + prec_yz, + ", prec_r_pri =", + prec_r_pri, + ", prec_r_dua =", + prec_r_dua, + ) + print("") + else: + print( + "diff criteria at a given dim:\n", + " prec_x =", + prec_x, + ", prec_yz =", + prec_yz, + ", prec_r_pri =", + prec_r_pri, + ", prec_r_dua =", + prec_r_dua, + ) + print("") + + print(" diff_x_lst:") + print(" ", diff_x_lst) + print("") + + print(" diff_yz_lst:") + print(" ", diff_yz_lst) + print("") + + print(" diff_r_pri_lst:") + print(" ", diff_r_pri_lst) + print("") + + print(" diff_r_dua_lst:") + print(" ", diff_r_dua_lst) + print("") + + print(" diff_iter_lst:") + print(" ", diff_iter_lst) + print("") + + print(" diff_status_lst:") + print(" ", diff_status_lst) + print("") + + if adaptive_mu: + print(" diff_mu_updates_lst:") + print(" ", diff_mu_updates_lst) + print("") + + if polish: + print(" diff_status_polish_lst:") + print(" ", diff_status_polish_lst) + print("") diff --git a/examples/python/osqp/calibration/degenerate_qp.py b/examples/python/osqp/calibration/degenerate_qp.py new file mode 100644 index 000000000..de778ea8e --- /dev/null +++ b/examples/python/osqp/calibration/degenerate_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="degenerate_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-15, + eps_dual_inf=1e-15, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/dual_infeasible_qp.py b/examples/python/osqp/calibration/dual_infeasible_qp.py new file mode 100644 index 000000000..f7fa964cf --- /dev/null +++ b/examples/python/osqp/calibration/dual_infeasible_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="dual_infeasible_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/maros_meszaros.py b/examples/python/osqp/calibration/maros_meszaros.py new file mode 100644 index 000000000..a41cf2281 --- /dev/null +++ b/examples/python/osqp/calibration/maros_meszaros.py @@ -0,0 +1,434 @@ +import proxsuite +import osqp + +import numpy as np +import scipy.sparse as spa + +from utils import string_to_check_if_solved_option +from utils import load_qp, preprocess_qp +from pathlib import Path + + +def solve_maros_maszaros( + filename: str, + verbose_solver: bool = False, + verbose_results_variables: bool = False, + verbose_calibration: bool = False, +): + # MM problem + qp = load_qp(filename) + proprocessed = preprocess_qp(qp) + H = proprocessed.H + A = proprocessed.A + C = proprocessed.C + g = proprocessed.g + b = proprocessed.b + u = proprocessed.u + l = proprocessed.l + + dim = H.shape[0] + n_eq = A.shape[0] + n_in = C.shape[0] + + eps_abs = 1e-3 + eps_rel = 0 + eps_primal_inf = 1e-12 + eps_dual_inf = 1e-12 + + check_if_solved_option_str = "Iteration based" + check_solved_option = string_to_check_if_solved_option(check_if_solved_option_str) + check_termination = 25 + frequence_infeasibility_check = 1 + + # OSQP proxsuite + proxsuite_osqp = proxsuite.osqp.dense.QP(dim, n_eq, n_in, box_constraints=False) + proxsuite_osqp.init(H, g, A, b, C, l, u) + + proxsuite_osqp.settings.verbose = verbose_solver + proxsuite_osqp.settings.eps_abs = eps_abs + proxsuite_osqp.settings.eps_rel = eps_rel + proxsuite_osqp.settings.eps_primal_inf = eps_primal_inf + proxsuite_osqp.settings.eps_dual_inf = eps_dual_inf + + proxsuite_osqp.settings.check_solved_option = check_solved_option + proxsuite_osqp.settings.check_termination = check_termination + proxsuite_osqp.settings.frequence_infeasibility_check = ( + frequence_infeasibility_check + ) + + proxsuite_osqp.solve() + + # OSQP source code + H_source = spa.csc_matrix(H) + A_sparse = spa.csc_matrix(A) + C_sparse = spa.csc_matrix(C) + g_source = g + l_source = np.concatenate([b, l]) + u_source = np.concatenate([b, u]) + A_source = spa.vstack([A_sparse, C_sparse], format="csc") + + check_termination_source = ( + check_termination if (check_if_solved_option_str == "Interval based") else 1 + ) + + prob = osqp.OSQP() + prob.setup( + H_source, + g_source, + A_source, + l_source, + u_source, + eps_abs=eps_abs, + eps_rel=eps_rel, + eps_prim_inf=eps_primal_inf, + eps_dual_inf=eps_dual_inf, + sigma=1e-6, + rho=0.1, + verbose=verbose_solver, + scaling=10, + max_iter=4000, + warm_start=False, + check_termination=check_termination_source, + adaptive_rho=True, + adaptive_rho_interval=50, + adaptive_rho_tolerance=5.0, + ) + res_source = prob.solve() + + # Check results + x_proxsuite = proxsuite_osqp.results.x + y_proxsuite = proxsuite_osqp.results.y + z_proxsuite = proxsuite_osqp.results.z + + x_source = res_source.x + y_source = res_source.y + + r_pri_proxsuite = proxsuite_osqp.results.info.pri_res + r_dua_proxsuite = proxsuite_osqp.results.info.dua_res + + r_pri_source = res_source.info.pri_res + r_dua_source = res_source.info.dua_res + + iter_proxsuite = proxsuite_osqp.results.info.iter_ext + iter_source = res_source.info.iter + + status_proxsuite = proxsuite_osqp.results.info.status + status_source = res_source.info.status + + if verbose_calibration: + print("primal residual proxsuite :") + print(r_pri_proxsuite) + print("dua residual proxsuite :") + print(r_dua_proxsuite) + print("iter proxsuite :") + print(iter_proxsuite) + + print("primal residual source :") + print(r_pri_source) + print("dua residual source :") + print(r_dua_source) + print("iter source :") + print(iter_source) + + eps = proxsuite_osqp.settings.eps_abs = eps_abs + + proxsuite_pass = ( + r_pri_proxsuite > -eps + and r_dua_proxsuite < 2 * eps + and np.min(C @ x_proxsuite - l) > -eps + and np.min(C @ x_proxsuite - u) < eps + ) + + source_pass = ( + r_pri_source > -eps + and r_dua_source < 2 * eps + and np.min(C @ x_source - l) > -eps + and np.min(C @ x_source - u) < eps + ) + + # Prints calibration OSQP proxsuite vs source + if verbose_results_variables: + print("") + print("x") + print("OSQP proxsuite") + print(x_proxsuite) + print("OSQP source") + print(x_source) + + print("") + print("(y, z) (proxsuite) vs y (source)") + print("OSQP proxsuite") + print(np.concatenate([y_proxsuite, z_proxsuite])) + print("OSQP source") + print(y_source) + + if verbose_calibration: + print("") + print("r_pri") + print("OSQP proxsuite") + print(r_pri_proxsuite) + print("OSQP source") + print(r_pri_source) + + print("") + print("r_dua") + print("OSQP proxsuite") + print(r_dua_proxsuite) + print("OSQP source") + print(r_dua_source) + + print("") + print("iter") + print("OSQP proxsuite") + print(iter_proxsuite) + print("OSQP source") + print(iter_source) + + print("") + print("status") + print("OSQP proxsuite") + print(status_proxsuite) + print("OSQP source") + print(status_source) + + return proxsuite_pass, source_pass + + +def test_calibration_maros_meszaros( + run_test=False, + test_skipped_problems=False, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, +): + """ + This function aims at reproducing the behaviour of the unit test + on the Maros Meszaros problem data (OSQP Dense). + + In particular, we run both our implementation and the source code, + with the same settings, on the same problems (only the first step + with initial guess = NO_INITIAL_GUESS). + + Doing so, we justify the deletion of some Maros Meszaros problems + in the unit test that would fail, not by our fault in the + implementation, but due to the OSQP algorithm itself. + + In order to visualize the gap in performance between OSQP and + PROXQP for example, we refer to the paper of the latter: + https://inria.hal.science/hal-03683733/file/Yet_another_QP_solver_for_robotics_and_beyond.pdf/ + + Args: + - test_skipped_problems: Boolean to deceide whether we test on the problems + that are skipped in the unit test, due to high dimensionality. It filters + data with dim > 1000 or n_eq + n_in > 1000. + """ + + # Run test + if not run_test: + return + + REPO_ROOT = Path(__file__).resolve().parents[4] + MAROS_MESZAROS_DIR = REPO_ROOT / "test" / "data" / "maros_meszaros_data" + + files = [ + # Proxsuite OSQP fails in cpp unit test + MAROS_MESZAROS_DIR / "PRIMALC1.mat", + MAROS_MESZAROS_DIR / "PRIMALC2.mat", + MAROS_MESZAROS_DIR / "PRIMALC5.mat", + MAROS_MESZAROS_DIR / "PRIMALC8.mat", + MAROS_MESZAROS_DIR / "QBANDM.mat", + MAROS_MESZAROS_DIR / "QBORE3D.mat", + MAROS_MESZAROS_DIR / "QBRANDY.mat", + MAROS_MESZAROS_DIR / "QCAPRI.mat", + MAROS_MESZAROS_DIR / "QE226.mat", + MAROS_MESZAROS_DIR / "QFORPLAN.mat", + MAROS_MESZAROS_DIR / "QFORPLAN.mat", + MAROS_MESZAROS_DIR / "QGROW7.mat", + MAROS_MESZAROS_DIR / "QISRAEL.mat", + MAROS_MESZAROS_DIR / "QPCBOEI1.mat", + MAROS_MESZAROS_DIR / "QPCBOEI2.mat", + MAROS_MESZAROS_DIR / "QSCAGR25.mat", + MAROS_MESZAROS_DIR / "QSCAGR7.mat", + MAROS_MESZAROS_DIR / "QSCFXM1.mat", + MAROS_MESZAROS_DIR / "QSCTAP1.mat", + MAROS_MESZAROS_DIR / "QSHARE1B.mat", + MAROS_MESZAROS_DIR / "QSHARE2B.mat", + MAROS_MESZAROS_DIR / "QSTAIR.mat", + # Proxsuite OSQP passes in cpp unit test + MAROS_MESZAROS_DIR / "CVXQP1_S.mat", + MAROS_MESZAROS_DIR / "CVXQP2_S.mat", + MAROS_MESZAROS_DIR / "CVXQP3_S.mat", + MAROS_MESZAROS_DIR / "DPKLO1.mat", + MAROS_MESZAROS_DIR / "DUAL1.mat", + MAROS_MESZAROS_DIR / "DUAL2.mat", + MAROS_MESZAROS_DIR / "DUAL3.mat", + MAROS_MESZAROS_DIR / "DUAL4.mat", + MAROS_MESZAROS_DIR / "DUALC1.mat", + MAROS_MESZAROS_DIR / "DUALC2.mat", + MAROS_MESZAROS_DIR / "DUALC5.mat", + MAROS_MESZAROS_DIR / "DUALC8.mat", + MAROS_MESZAROS_DIR / "GENHS28.mat", + MAROS_MESZAROS_DIR / "HS118.mat", + MAROS_MESZAROS_DIR / "HS21.mat", + MAROS_MESZAROS_DIR / "HS268.mat", + MAROS_MESZAROS_DIR / "HS35.mat", + MAROS_MESZAROS_DIR / "HS35MOD.mat", + MAROS_MESZAROS_DIR / "HS51.mat", + MAROS_MESZAROS_DIR / "HS52.mat", + MAROS_MESZAROS_DIR / "HS53.mat", + MAROS_MESZAROS_DIR / "HS76.mat", + MAROS_MESZAROS_DIR / "LOTSCHD.mat", + MAROS_MESZAROS_DIR / "PRIMAL1.mat", + MAROS_MESZAROS_DIR / "PRIMAL2.mat", + MAROS_MESZAROS_DIR / "PRIMAL3.mat", + MAROS_MESZAROS_DIR / "QADLITTL.mat", + MAROS_MESZAROS_DIR / "QAFIRO.mat", + MAROS_MESZAROS_DIR / "QBEACONF.mat", + MAROS_MESZAROS_DIR / "QPCBLEND.mat", + MAROS_MESZAROS_DIR / "QSCORPIO.mat", + MAROS_MESZAROS_DIR / "QSCSD1.mat", + MAROS_MESZAROS_DIR / "S268.mat", + MAROS_MESZAROS_DIR / "TAME.mat", + MAROS_MESZAROS_DIR / "VALUES.mat", + MAROS_MESZAROS_DIR / "ZECEVIC2.mat", + MAROS_MESZAROS_DIR / "QPCSTAIR.mat", + ] + if test_skipped_problems: + files_skipped = [ + # Skipped problems in unit test + MAROS_MESZAROS_DIR / "AUG2D.mat", + MAROS_MESZAROS_DIR / "AUG2DC.mat", + MAROS_MESZAROS_DIR / "AUG2DCQP.mat", + MAROS_MESZAROS_DIR / "AUG2DQP.mat", + MAROS_MESZAROS_DIR / "AUG3D.mat", + MAROS_MESZAROS_DIR / "AUG3DC.mat", + MAROS_MESZAROS_DIR / "AUG3DCQP.mat", + MAROS_MESZAROS_DIR / "AUG3DQP.mat", + MAROS_MESZAROS_DIR / "BOYD1.mat", + MAROS_MESZAROS_DIR / "BOYD2.mat", + MAROS_MESZAROS_DIR / "CONT-050.mat", + MAROS_MESZAROS_DIR / "CONT-100.mat", + MAROS_MESZAROS_DIR / "CONT-101.mat", + MAROS_MESZAROS_DIR / "CONT-200.mat", + MAROS_MESZAROS_DIR / "CONT-201.mat", + MAROS_MESZAROS_DIR / "CONT-300.mat", + MAROS_MESZAROS_DIR / "CVXQP1_L.mat", + MAROS_MESZAROS_DIR / "CVXQP1_M.mat", + MAROS_MESZAROS_DIR / "CVXQP2_L.mat", + MAROS_MESZAROS_DIR / "CVXQP2_M.mat", + MAROS_MESZAROS_DIR / "CVXQP3_L.mat", + MAROS_MESZAROS_DIR / "CVXQP3_M.mat", + MAROS_MESZAROS_DIR / "DTOC3.mat", + MAROS_MESZAROS_DIR / "EXDATA.mat", + MAROS_MESZAROS_DIR / "GOULDQP2.mat", + MAROS_MESZAROS_DIR / "GOULDQP3.mat", + MAROS_MESZAROS_DIR / "HUES-MOD.mat", + MAROS_MESZAROS_DIR / "HUESTIS.mat", + MAROS_MESZAROS_DIR / "KSIP.mat", + MAROS_MESZAROS_DIR / "LASER.mat", + MAROS_MESZAROS_DIR / "LISWET1.mat", + MAROS_MESZAROS_DIR / "LISWET10.mat", + MAROS_MESZAROS_DIR / "LISWET11.mat", + MAROS_MESZAROS_DIR / "LISWET12.mat", + MAROS_MESZAROS_DIR / "LISWET2.mat", + MAROS_MESZAROS_DIR / "LISWET3.mat", + MAROS_MESZAROS_DIR / "LISWET4.mat", + MAROS_MESZAROS_DIR / "LISWET5.mat", + MAROS_MESZAROS_DIR / "LISWET6.mat", + MAROS_MESZAROS_DIR / "LISWET7.mat", + MAROS_MESZAROS_DIR / "LISWET8.mat", + MAROS_MESZAROS_DIR / "LISWET9.mat", + MAROS_MESZAROS_DIR / "MOSARQP1.mat", + MAROS_MESZAROS_DIR / "MOSARQP2.mat", + MAROS_MESZAROS_DIR / "POWELL20.mat", + MAROS_MESZAROS_DIR / "PRIMAL4.mat", + MAROS_MESZAROS_DIR / "Q25FV47.mat", + MAROS_MESZAROS_DIR / "QETAMACR.mat", + MAROS_MESZAROS_DIR / "QFFFFF80.mat", + MAROS_MESZAROS_DIR / "QGFRDXPN.mat", + MAROS_MESZAROS_DIR / "QGROW22.mat", + MAROS_MESZAROS_DIR / "QPILOTNO.mat", + MAROS_MESZAROS_DIR / "QPTEST.mat", + MAROS_MESZAROS_DIR / "QRECIPE.mat", + MAROS_MESZAROS_DIR / "QSC205.mat", + MAROS_MESZAROS_DIR / "QSCFXM2.mat", + MAROS_MESZAROS_DIR / "QSCFXM3.mat", + MAROS_MESZAROS_DIR / "QSCRS8.mat", + MAROS_MESZAROS_DIR / "QSCSD6.mat", + MAROS_MESZAROS_DIR / "QSCSD8.mat", + MAROS_MESZAROS_DIR / "QSCTAP2.mat", + MAROS_MESZAROS_DIR / "QSCTAP3.mat", + MAROS_MESZAROS_DIR / "QSEBA.mat", + MAROS_MESZAROS_DIR / "QSHELL.mat", + MAROS_MESZAROS_DIR / "QSHIP04L.mat", + MAROS_MESZAROS_DIR / "QSHIP04S.mat", + MAROS_MESZAROS_DIR / "QSHIP08L.mat", + MAROS_MESZAROS_DIR / "QSHIP08S.mat", + MAROS_MESZAROS_DIR / "QSHIP12L.mat", + MAROS_MESZAROS_DIR / "QSHIP12S.mat", + MAROS_MESZAROS_DIR / "QSIERRA.mat", + MAROS_MESZAROS_DIR / "QSTANDAT.mat", + MAROS_MESZAROS_DIR / "STADAT1.mat", + MAROS_MESZAROS_DIR / "STADAT2.mat", + MAROS_MESZAROS_DIR / "STADAT3.mat", + MAROS_MESZAROS_DIR / "STCQP1.mat", + MAROS_MESZAROS_DIR / "STCQP2.mat", + MAROS_MESZAROS_DIR / "UBH1.mat", + MAROS_MESZAROS_DIR / "YAO.mat", + ] + files = files + files_skipped + + both_pass = [] + both_fail = [] + proxsuite_pass_source_fail = [] + source_pass_proxsuite_fail = [] + for file in files: + filename = str(file) + proxsuite_pass, source_pass = solve_maros_maszaros( + filename, + verbose_solver=verbose_solver, + verbose_results_variables=verbose_results_variables, + verbose_calibration=verbose_calibration, + ) + + filename_only = Path(filename).name + + if proxsuite_pass and source_pass: + both_pass.append(filename_only) + + elif proxsuite_pass and not source_pass: + proxsuite_pass_source_fail.append(filename_only) + + elif not proxsuite_pass and source_pass: + source_pass_proxsuite_fail.append(filename_only) + + elif not proxsuite_pass and not source_pass: + both_fail.append(filename_only) + + print("") + print("Which test passed/failed on which solver") + + print("") + print("both_pass:") + print(both_pass) + + print("") + print("both_fail:") + print(both_fail) + + print("") + print("proxsuite_pass_source_fail:") + print(proxsuite_pass_source_fail) + + print("") + print("source_pass_proxsuite_fail:") + print(source_pass_proxsuite_fail) + + +test_calibration_maros_meszaros( + run_test=False, + test_skipped_problems=False, + verbose_solver=True, + verbose_results_variables=False, + verbose_calibration=False, +) diff --git a/examples/python/osqp/calibration/not_strongly_convex_qp.py b/examples/python/osqp/calibration/not_strongly_convex_qp.py new file mode 100644 index 000000000..0f825316a --- /dev/null +++ b/examples/python/osqp/calibration/not_strongly_convex_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="not_strongly_convex_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/primal_infeasible_qp.py b/examples/python/osqp/calibration/primal_infeasible_qp.py new file mode 100644 index 000000000..b7b79b84d --- /dev/null +++ b/examples/python/osqp/calibration/primal_infeasible_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="primal_infeasible_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/strongly_convex_qp.py b/examples/python/osqp/calibration/strongly_convex_qp.py new file mode 100644 index 000000000..4177eaf25 --- /dev/null +++ b/examples/python/osqp/calibration/strongly_convex_qp.py @@ -0,0 +1,40 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="strongly_convex_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + only_eq=False, + only_in=False, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/unconstrained_qp.py b/examples/python/osqp/calibration/unconstrained_qp.py new file mode 100644 index 000000000..1955af1b1 --- /dev/null +++ b/examples/python/osqp/calibration/unconstrained_qp.py @@ -0,0 +1,38 @@ +from calibration_base import test_calibration_qp + +test_calibration_qp( + problem="unconstrained_qp", + run_test=False, + dim_start=10, + dim_end=1000, + dim_step=20, + max_iter=4000, + compute_preconditioner=True, + eps_abs=1e-3, + eps_rel=0, + eps_primal_inf=1e-4, + eps_dual_inf=1e-4, + check_if_solved_option_str="Iteration based", + check_termination=25, + frequence_infeasibility_check=1, + sparsity_factor=0.45, + strong_convexity_factor=1e-2, + adaptive_mu=False, + adaptive_mu_interval=50, + adaptive_mu_tolerance=5.0, + polish=False, + delta=1e-6, + polish_refine_iter=3, + verbose_test_settings=True, + verbose_solver=False, + verbose_results_variables=False, + verbose_calibration=False, + verbose_timings=False, + prec_x=1e-3, + prec_yz=1e-3, + prec_r_pri=1e-3, + prec_r_dua=1e-3, + prec_polish=1e-9, + prec_iter=0, + prec_mu_updates=0, +) diff --git a/examples/python/osqp/calibration/utils.py b/examples/python/osqp/calibration/utils.py new file mode 100644 index 000000000..27dbf345a --- /dev/null +++ b/examples/python/osqp/calibration/utils.py @@ -0,0 +1,382 @@ +import proxsuite + +import numpy as np +import numpy.linalg as la +import scipy.sparse as spa +import scipy.io as spio + +from dataclasses import dataclass + + +def infty_norm(vec: np.ndarray): + return la.norm(vec, np.inf, axis=0) + + +def status_to_string(status, solver): + if solver == "proxsuite": + if status == proxsuite.osqp.QPSOLVER_SOLVED: + return "Solved" + elif status == proxsuite.osqp.QPSOLVER_MAX_ITER_REACHED: + return "Maximum number of iterations reached" + elif status == proxsuite.osqp.QPSOLVER_PRIMAL_INFEASIBLE: + return "Primal infeasible" + elif status == proxsuite.osqp.QPSOLVER_DUAL_INFEASIBLE: + return "Dual infeasible" + elif status == proxsuite.osqp.QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE: + return "Solved closest primal feasible" + elif status == proxsuite.osqp.QPSOLVER_NOT_RUN: + return "Solver not run" + + elif solver == "source": + if status == "solved": + return "Solved" + elif status == "maximum iterations reached": + return "Maximum number of iterations reached" + elif status == "primal infeasible": + return "Primal infeasible" + elif status == "dual infeasible": + return "Dual infeasible" + + else: + print("solver argument must be proxsuite or source") + + +def status_polish_to_string(status, solver): + if solver == "proxsuite": + if status == proxsuite.osqp.POLISH_SUCCEEDED: + return "Polishing: succeeded" + elif status == proxsuite.osqp.POLISH_FAILED: + return "Polishing: failed" + elif status == proxsuite.osqp.POLISH_NOT_RUN: + return "Polishing: not run" + elif status == proxsuite.osqp.POLISH_NO_ACTIVE_SET_FOUND: + return "Polishing: no active set found" + + elif solver == "source": + if status == 1: + return "Polishing: succeeded" + elif status == -1: + return "Polishing: failed" + elif status == 0: + return "Polishing: not run" + elif status == 2: + return "Polishing: no active set found" + + else: + print("solver argument must be proxsuite or source") + + +def string_to_check_if_solved_option(string): + if string == "Iteration based": + return proxsuite.osqp.CheckSolvedStatus.ITERATION_BASED + elif string == "Interval based": + return proxsuite.osqp.CheckSolvedStatus.INTERVAL_BASED + + +def sparse_positive_definite_rand_not_compressed(dim, rho, p, rng): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + H = np.zeros((dim, dim), dtype=np.float64) + + urandom = rng.uniform(size=(dim, dim)) + mask = urandom < (p / 2) + H[mask] = rng.standard_normal(np.count_nonzero(mask)) + H = 0.5 * (H + H.T) + + eigvals = np.linalg.eigvalsh(H) + min_eig = eigvals.min() + H[np.diag_indices(dim)] += rho + abs(min_eig) + + return H + + +def sparse_matrix_rand_not_compressed(nrows, ncols, p, rng): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + mask = rng.uniform(size=(nrows, ncols)) < p + + A = np.zeros((nrows, ncols), dtype=np.float64) + A[mask] = rng.standard_normal(np.count_nonzero(mask)) + + return A + + +def unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor, rng=rng + ) + g = rng.standard_normal(dim) + + A = sparse_matrix_rand_not_compressed(0, dim, sparsity_factor, rng=rng) + C = sparse_matrix_rand_not_compressed(0, dim, sparsity_factor, rng=rng) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + b = rng.standard_normal(0) + u = rng.standard_normal(0) + l = rng.standard_normal(0) + + return H, g, A, b, C, u, l + + +def strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor, rng=rng + ) + g = rng.standard_normal(dim) + + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C = sparse_matrix_rand_not_compressed(n_in, dim, sparsity_factor, rng=rng) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + delta = rng.uniform(size=n_in) + + b = A @ x_sol + + u = C @ x_sol + delta + l = -1.0e20 * np.ones(n_in) + + return H, g, A, b, C, u, l + + +def not_strongly_convex_qp(dim, n_eq, n_in, sparsity_factor, sparse=False, seed=1): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed(dim, 0, sparsity_factor, rng=rng) + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C = sparse_matrix_rand_not_compressed(n_in, dim, sparsity_factor, rng=rng) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + y_sol = rng.standard_normal(n_eq) + z_sol = rng.standard_normal(n_in) + delta = rng.uniform(size=n_in) + + Cx = C @ x_sol + u = Cx + delta + l = Cx - delta + b = A @ x_sol + + g = -(H @ x_sol + A.T @ y_sol + C.T @ z_sol) + + return H, g, A, b, C, u, l + + +def degenerate_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor, rng=rng + ) + g = rng.standard_normal(dim) + + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C_ = sparse_matrix_rand_not_compressed(n_in, dim, sparsity_factor, rng=rng) + C = np.vstack([C_, C_]) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + delta = rng.uniform(size=2 * n_in) + + b = A @ x_sol + + u = C @ x_sol + delta + l = -1.0e20 * np.ones(2 * n_in) + + return H, g, A, b, C, u, l + + +def box_constrained_qp( + dim, n_eq, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + # Inspired from "proxsuite/common/utils/random_qp_problems.hpp" + # Note: n_in is not in argument, as C must be square with size dim + + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor, rng=rng + ) + g = rng.standard_normal(dim) + + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C = np.ones((dim, dim)) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + delta = rng.uniform(size=dim) + + b = A @ x_sol + + u = C @ x_sol + delta + l = C @ x_sol - delta + + return H, g, A, b, C, u, l + + +def primal_infeasible_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + rng = np.random.default_rng(seed) + + H = sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor, rng=rng + ) + g = rng.standard_normal(dim) + + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C = sparse_matrix_rand_not_compressed(n_in, dim, sparsity_factor, rng=rng) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + delta = rng.uniform(size=n_in) + + b = A @ x_sol + + u = C @ x_sol + delta + l = -1.0e20 * np.ones(n_in) + + n_cont = n_in // 2 + for idx in range(n_cont): + i = 2 * idx + j = 2 * idx + 1 + if j < n_in: + C[j] = -C[i] + u[i] = rng.uniform(1.0, 3.0) + u[j] = -rng.uniform(u[i] + 0.5, u[i] + 5.0) + + return H, g, A, b, C, u, l + + +def dual_infeasible_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor=1e-2, sparse=False, seed=1 +): + rng = np.random.default_rng(seed) + + H = np.zeros((dim, dim)) + g = np.ones(dim) + + A = sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor, rng=rng) + C = sparse_matrix_rand_not_compressed(n_in, dim, sparsity_factor, rng=rng) + + if sparse: + H = spa.csc_matrix(H) + A = spa.csc_matrix(A) + C = spa.csc_matrix(C) + + x_sol = rng.standard_normal(dim) + delta = rng.uniform(size=n_in) + + b = A @ x_sol + + u = C @ x_sol + delta + l = -1.0e20 * np.ones(n_in) + + return H, g, A, b, C, u, l + + +@dataclass +class MarosMeszarosQp: + filename: str + P: spa.csc_matrix + q: np.ndarray + A: spa.csc_matrix + l: np.ndarray + u: np.ndarray + + +@dataclass +class PreprocessedQp: + H: np.ndarray + A: np.ndarray + C: np.ndarray + g: np.ndarray + b: np.ndarray + u: np.ndarray + l: np.ndarray + + +def load_qp(filename: str) -> MarosMeszarosQp: + assert filename.endswith(".mat") + mat_dict = spio.loadmat(filename) + + P = mat_dict["P"].astype(float).tocsc() + q = mat_dict["q"].T.flatten().astype(float) + A = mat_dict["A"].astype(float).tocsc() + l = mat_dict["l"].T.flatten().astype(float) + u = mat_dict["u"].T.flatten().astype(float) + + return MarosMeszarosQp(filename=filename, P=P, q=q, A=A, l=l, u=u) + + +def preprocess_qp(qp: MarosMeszarosQp) -> PreprocessedQp: + eq = np.isclose(qp.l, qp.u, atol=1e-4) + + n = qp.P.shape[0] + n_eq = np.sum(eq) + n_in = len(eq) - n_eq + + A = np.zeros((n_eq, n)) + b = np.zeros(n_eq) + + C = np.zeros((n_in, n)) + u = np.zeros(n_in) + l = np.zeros(n_in) + + eq_idx = 0 + in_idx = 0 + + for i in range(len(eq)): + if eq[i]: + A[eq_idx, :] = qp.A[i, :].toarray().flatten() + b[eq_idx] = qp.l[i] + eq_idx += 1 + else: + C[in_idx, :] = qp.A[i, :].toarray().flatten() + l[in_idx] = qp.l[i] + u[in_idx] = qp.u[i] + in_idx += 1 + + return PreprocessedQp(H=qp.P.toarray(), A=A, C=C, g=qp.q, b=b, u=u, l=l) diff --git a/examples/python/osqp/overview-simple.py b/examples/python/osqp/overview-simple.py new file mode 100644 index 000000000..5fae64008 --- /dev/null +++ b/examples/python/osqp/overview-simple.py @@ -0,0 +1,18 @@ +import proxsuite +from util import generate_mixed_qp + + +# generate a qp problem +n = 10 +H, g, A, b, C, u, l = generate_mixed_qp(n) +n_eq = A.shape[0] +n_in = C.shape[0] + +# solve it +qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) +qp.init(H, g, A, b, C, l, u) +qp.solve() +# print an optimal solution +print("optimal x: {}".format(qp.results.x)) +print("optimal y: {}".format(qp.results.y)) +print("optimal z: {}".format(qp.results.z)) diff --git a/examples/python/util.py b/examples/python/osqp/util.py similarity index 100% rename from examples/python/util.py rename to examples/python/osqp/util.py diff --git a/examples/python/estimate_nonconvex_eigenvalue.py b/examples/python/proxqp/estimate_nonconvex_eigenvalue.py similarity index 100% rename from examples/python/estimate_nonconvex_eigenvalue.py rename to examples/python/proxqp/estimate_nonconvex_eigenvalue.py diff --git a/examples/python/init_dense_qp.py b/examples/python/proxqp/init_dense_qp.py similarity index 100% rename from examples/python/init_dense_qp.py rename to examples/python/proxqp/init_dense_qp.py diff --git a/examples/python/init_dense_qp_with_box.py b/examples/python/proxqp/init_dense_qp_with_box.py similarity index 100% rename from examples/python/init_dense_qp_with_box.py rename to examples/python/proxqp/init_dense_qp_with_box.py diff --git a/examples/python/init_dense_qp_with_other_options.py b/examples/python/proxqp/init_dense_qp_with_other_options copy.py similarity index 100% rename from examples/python/init_dense_qp_with_other_options.py rename to examples/python/proxqp/init_dense_qp_with_other_options copy.py diff --git a/examples/python/proxqp/init_dense_qp_with_other_options.py b/examples/python/proxqp/init_dense_qp_with_other_options.py new file mode 100644 index 000000000..c043e4c37 --- /dev/null +++ b/examples/python/proxqp/init_dense_qp_with_other_options.py @@ -0,0 +1,13 @@ +import proxsuite +from util import generate_mixed_qp + + +# load a qp object using qp problem dimensions +n = 10 +n_eq = 2 +n_in = 2 +qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in) +# generate a random QP +H, g, A, b, C, u, l = generate_mixed_qp(n) +# initialize the model of the problem to solve with another rho parameter +qp.init(H, g, A, b, C, l, u, rho=1.0e-7) diff --git a/examples/python/init_dense_qp_with_timings.py b/examples/python/proxqp/init_dense_qp_with_timings.py similarity index 100% rename from examples/python/init_dense_qp_with_timings.py rename to examples/python/proxqp/init_dense_qp_with_timings.py diff --git a/examples/python/init_with_default_options.py b/examples/python/proxqp/init_with_default_options.py similarity index 100% rename from examples/python/init_with_default_options.py rename to examples/python/proxqp/init_with_default_options.py diff --git a/examples/python/initializing_with_none.py b/examples/python/proxqp/initializing_with_none.py similarity index 100% rename from examples/python/initializing_with_none.py rename to examples/python/proxqp/initializing_with_none.py diff --git a/examples/python/initializing_with_none_without_api.py b/examples/python/proxqp/initializing_with_none_without_api.py similarity index 100% rename from examples/python/initializing_with_none_without_api.py rename to examples/python/proxqp/initializing_with_none_without_api.py diff --git a/examples/python/loading_dense_qp.py b/examples/python/proxqp/loading_dense_qp.py similarity index 100% rename from examples/python/loading_dense_qp.py rename to examples/python/proxqp/loading_dense_qp.py diff --git a/examples/python/loading_dense_qp_with_box_ineq.py b/examples/python/proxqp/loading_dense_qp_with_box_ineq.py similarity index 100% rename from examples/python/loading_dense_qp_with_box_ineq.py rename to examples/python/proxqp/loading_dense_qp_with_box_ineq.py diff --git a/examples/python/loading_dense_qp_with_different_backend_choice.py b/examples/python/proxqp/loading_dense_qp_with_different_backend_choice.py similarity index 100% rename from examples/python/loading_dense_qp_with_different_backend_choice.py rename to examples/python/proxqp/loading_dense_qp_with_different_backend_choice.py diff --git a/examples/python/loading_sparse_qp.py b/examples/python/proxqp/loading_sparse_qp.py similarity index 100% rename from examples/python/loading_sparse_qp.py rename to examples/python/proxqp/loading_sparse_qp.py diff --git a/examples/python/overview-simple.py b/examples/python/proxqp/overview-simple.py similarity index 100% rename from examples/python/overview-simple.py rename to examples/python/proxqp/overview-simple.py diff --git a/examples/python/solve_dense_lp.py b/examples/python/proxqp/solve_dense_lp.py similarity index 100% rename from examples/python/solve_dense_lp.py rename to examples/python/proxqp/solve_dense_lp.py diff --git a/examples/python/solve_dense_qp.py b/examples/python/proxqp/solve_dense_qp.py similarity index 100% rename from examples/python/solve_dense_qp.py rename to examples/python/proxqp/solve_dense_qp.py diff --git a/examples/python/solve_dense_qp_with_setting.py b/examples/python/proxqp/solve_dense_qp_with_setting.py similarity index 100% rename from examples/python/solve_dense_qp_with_setting.py rename to examples/python/proxqp/solve_dense_qp_with_setting.py diff --git a/examples/python/solve_without_api.py b/examples/python/proxqp/solve_without_api.py similarity index 100% rename from examples/python/solve_without_api.py rename to examples/python/proxqp/solve_without_api.py diff --git a/examples/python/solve_without_api_and_option.py b/examples/python/proxqp/solve_without_api_and_option.py similarity index 100% rename from examples/python/solve_without_api_and_option.py rename to examples/python/proxqp/solve_without_api_and_option.py diff --git a/examples/python/update_dense_qp.py b/examples/python/proxqp/update_dense_qp.py similarity index 100% rename from examples/python/update_dense_qp.py rename to examples/python/proxqp/update_dense_qp.py diff --git a/examples/python/update_dense_qp_ws_previous_result.py b/examples/python/proxqp/update_dense_qp_ws_previous_result.py similarity index 100% rename from examples/python/update_dense_qp_ws_previous_result.py rename to examples/python/proxqp/update_dense_qp_ws_previous_result.py diff --git a/examples/python/update_sparse_qp.py b/examples/python/proxqp/update_sparse_qp.py similarity index 100% rename from examples/python/update_sparse_qp.py rename to examples/python/proxqp/update_sparse_qp.py diff --git a/examples/python/proxqp/util.py b/examples/python/proxqp/util.py new file mode 100644 index 000000000..1e7c70bb1 --- /dev/null +++ b/examples/python/proxqp/util.py @@ -0,0 +1,31 @@ +import numpy as np +import scipy.sparse as spa + + +def generate_mixed_qp(n, sparse=False, seed=1, reg=1e-2, dens1=0.075): + # A function for generating sparse random convex qps + + np.random.seed(seed) + n_eq = int(n / 4) + n_in = int(n / 4) + m = n_eq + n_in + + P = spa.random( + n, n, density=dens1, data_rvs=np.random.randn, format="csc" + ).toarray() + P = (P + P.T) / 2.0 + + s = max(np.absolute(np.linalg.eigvals(P))) + P += (abs(s) + reg) * spa.eye(n) + P = spa.coo_matrix(P) + q = np.random.randn(n) + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc") + if not sparse: + A = A.toarray() + P = P.toarray() + v = np.random.randn(n) # Fictitious solution + _delta = np.random.rand(m) # To get inequality + u = A @ v + l = -1.0e20 * np.ones(m) + + return P, q, A[:n_eq, :], u[:n_eq], A[n_eq:, :], u[n_eq:], l[n_eq:] diff --git a/include/proxsuite/proxqp/dense/backward_data.hpp b/include/proxsuite/common/dense/backward_data.hpp similarity index 90% rename from include/proxsuite/proxqp/dense/backward_data.hpp rename to include/proxsuite/common/dense/backward_data.hpp index 7ebe8734b..0af6ea9ec 100644 --- a/include/proxsuite/proxqp/dense/backward_data.hpp +++ b/include/proxsuite/common/dense/backward_data.hpp @@ -4,20 +4,20 @@ /** * @file results.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_BACKWARD_DATA_HPP -#define PROXSUITE_PROXQP_DENSE_BACKWARD_DATA_HPP +#ifndef PROXSUITE_COMMON_DENSE_BACKWARD_DATA_HPP +#define PROXSUITE_COMMON_DENSE_BACKWARD_DATA_HPP -#include #include #include "proxsuite/linalg/veg/type_traits/core.hpp" -#include "proxsuite/proxqp/dense/fwd.hpp" +#include +#include "proxsuite/common/dense/fwd.hpp" namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { /// -/// @brief This class stores the jacobians of PROXQP solvers with +/// @brief This class stores the jacobians of the solvers with /// dense backends at a solutions wrt model parameters. /// /*! @@ -128,7 +128,7 @@ struct BackwardData }; } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_BACKWARD_DATA_HPP */ \ No newline at end of file +#endif /* end of include guard PROXSUITE_COMMON_DENSE_BACKWARD_DATA_HPP */ \ No newline at end of file diff --git a/include/proxsuite/proxqp/dense/fwd.hpp b/include/proxsuite/common/dense/fwd.hpp similarity index 84% rename from include/proxsuite/proxqp/dense/fwd.hpp rename to include/proxsuite/common/dense/fwd.hpp index 383a7d420..d0266b1ac 100644 --- a/include/proxsuite/proxqp/dense/fwd.hpp +++ b/include/proxsuite/common/dense/fwd.hpp @@ -2,14 +2,15 @@ // Copyright (c) 2022-2023 INRIA // /** \file */ -#ifndef PROXSUITE_PROXQP_DENSE_FWD_HPP -#define PROXSUITE_PROXQP_DENSE_FWD_HPP +#ifndef PROXSUITE_COMMON_DENSE_FWD_HPP +#define PROXSUITE_COMMON_DENSE_FWD_HPP #include +#include "proxsuite/linalg/veg/vec.hpp" #include "proxsuite/helpers/common.hpp" namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { static constexpr auto DYN = Eigen::Dynamic; @@ -51,7 +52,7 @@ using VecMapBool = Eigen::Map const>; using VecBool = Eigen::Matrix; } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_FWD_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_FWD_HPP */ diff --git a/include/proxsuite/common/dense/helpers.hpp b/include/proxsuite/common/dense/helpers.hpp new file mode 100644 index 000000000..cde7f4173 --- /dev/null +++ b/include/proxsuite/common/dense/helpers.hpp @@ -0,0 +1,1440 @@ +// +// Copyright (c) 2022-2023 INRIA +// +/** + * @file helpers.hpp + */ + +#ifndef PROXSUITE_COMMON_DENSE_HELPERS_HPP +#define PROXSUITE_COMMON_DENSE_HELPERS_HPP + +#include +#include +#include +#include +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/dense/model.hpp" +#include "proxsuite/common/dense/workspace.hpp" +#include "proxsuite/common/dense/iterative_solve.hpp" +#include +#include +#include "proxsuite/common/solvers.hpp" +#include "proxsuite/proxqp/dense/linesearch.hpp" +#include +#include +#include + +namespace proxsuite { +namespace common { +namespace dense { + +template +T +power_iteration(const Eigen::MatrixBase& H, + const Eigen::MatrixBase& dw, + const Eigen::MatrixBase& rhs, + const Eigen::MatrixBase& err_v, + T power_iteration_accuracy, + isize nb_power_iteration) +{ + auto& dw_cc = dw.const_cast_derived(); + auto& rhs_cc = rhs.const_cast_derived(); + auto& err_v_cc = err_v.const_cast_derived(); + // computes maximal eigen value of the bottom right matrix of the LDLT + isize dim = H.rows(); + rhs_cc.setZero(); + // stores eigenvector + rhs_cc.array() += 1. / std::sqrt(dim); + // stores Hx + dw_cc.noalias() = H.template selfadjointView() * rhs_cc; // Hx + T eig = 0; + for (isize i = 0; i < nb_power_iteration; i++) { + + rhs_cc = dw_cc / dw_cc.norm(); + dw_cc.noalias() = (H.template selfadjointView() * rhs_cc); + // calculate associated eigenvalue + eig = rhs.dot(dw_cc); + // calculate associated error + err_v_cc = dw_cc - eig * rhs_cc; + T err = infty_norm(err_v_cc); + // std::cout << "power iteration max: i " << i << " err " << err << + // std::endl; + if (err <= power_iteration_accuracy) { + break; + } + } + return eig; +} +template +T +min_eigen_value_via_modified_power_iteration( + const Eigen::MatrixBase& H, + const Eigen::MatrixBase& dw, + const Eigen::MatrixBase& rhs, + const Eigen::MatrixBase& err_v, + T max_eigen_value, + T power_iteration_accuracy, + isize nb_power_iteration) +{ + // performs power iteration on the matrix: max_eigen_value I - H + // estimates then the minimal eigenvalue with: minimal_eigenvalue = + // max_eigen_value - eig + auto& dw_cc = dw.const_cast_derived(); + auto& rhs_cc = rhs.const_cast_derived(); + auto& err_v_cc = err_v.const_cast_derived(); + isize dim = H.rows(); + rhs_cc.setZero(); + // stores eigenvector + rhs_cc.array() += 1. / std::sqrt(dim); + // stores Hx + dw_cc.noalias() = + -(H.template selfadjointView() * rhs_cc); // Hx + dw_cc += max_eigen_value * rhs_cc; + T eig = 0; + for (isize i = 0; i < nb_power_iteration; i++) { + + rhs_cc = dw_cc / dw_cc.norm(); + dw_cc.noalias() = -(H.template selfadjointView() * rhs_cc); + dw_cc += max_eigen_value * rhs_cc; + // calculate associated eigenvalue + eig = rhs_cc.dot(dw_cc); + // calculate associated error + err_v_cc = dw_cc - eig * rhs_cc; + T err = infty_norm(err_v_cc); + // std::cout << "power iteration min: i " << i << " err " << err << + // std::endl; + if (err <= power_iteration_accuracy) { + break; + } + } + T minimal_eigenvalue = max_eigen_value - eig; + return minimal_eigenvalue; +} +/////// SETUP //////// +/*! + * Estimate minimal eigenvalue of a symmetric Matrix + * @param H symmetric matrix. + * @param EigenValueEstimateMethodOption + * @param power_iteration_accuracy power iteration algorithm accuracy tracked + * @param nb_power_iteration maximal number of power iteration executed + * + */ +template +T +estimate_minimal_eigen_value_of_symmetric_matrix( + const Eigen::MatrixBase& H, + EigenValueEstimateMethodOption estimate_method_option, + T power_iteration_accuracy, + isize nb_power_iteration) +{ + PROXSUITE_THROW_PRETTY( + (!H.isApprox(H.transpose(), std::numeric_limits::epsilon())), + std::invalid_argument, + "H is not symmetric."); + if (H.size()) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.rows(), + H.cols(), + "H has a number of rows different of the number of columns."); + } + isize dim = H.rows(); + T res(0.); + switch (estimate_method_option) { + case EigenValueEstimateMethodOption::PowerIteration: { + Vec dw(dim); + Vec rhs(dim); + Vec err(dim); + T dominant_eigen_value = power_iteration( + H, dw, rhs, err, power_iteration_accuracy, nb_power_iteration); + T min_eigenvalue = + min_eigen_value_via_modified_power_iteration(H, + dw, + rhs, + err, + dominant_eigen_value, + power_iteration_accuracy, + nb_power_iteration); + res = std::min(min_eigenvalue, dominant_eigen_value); + } break; + case EigenValueEstimateMethodOption::ExactMethod: { + Eigen::SelfAdjointEigenSolver> es(H, Eigen::EigenvaluesOnly); + res = T(es.eigenvalues()[0]); + } break; + } + return res; +} +/////// SETUP //////// +/*! + * Estimate H minimal eigenvalue + * @param settings solver settings + * @param results solver results. + * @param manual_minimal_H_eigenvalue minimal H eigenvalue estimate. + */ +template +void +update_default_rho_with_minimal_Hessian_eigen_value( + optional manual_minimal_H_eigenvalue, + Results& results, + Settings& settings) +{ + if (manual_minimal_H_eigenvalue != nullopt) { + settings.default_H_eigenvalue_estimate = + manual_minimal_H_eigenvalue.value(); + results.info.minimal_H_eigenvalue_estimate = + settings.default_H_eigenvalue_estimate; + } + settings.default_rho += std::abs(results.info.minimal_H_eigenvalue_estimate); + results.info.rho = settings.default_rho; +} +/*! + * Computes the equality constrained initial guess of a QP problem. + * + * @param qpwork workspace of the solver. + * @param qpsettings settings of the solver. + * @param qpmodel QP problem as defined by the user (without any scaling + * performed). + * @param qpresults solution results. + */ +template +void +compute_equality_constrained_initial_guess(Workspace& qpwork, + const Settings& qpsettings, + const Model& qpmodel, + const isize n_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + Results& qpresults) +{ + + qpwork.rhs.setZero(); + qpwork.rhs.head(qpmodel.dim) = -qpwork.g_scaled; + qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) = qpwork.b_scaled; + iterative_solve_with_permut_fact( // + qpsettings, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + hessian_type, + T(1), + qpmodel.dim + qpmodel.n_eq); + + qpresults.x = qpwork.dw_aug.head(qpmodel.dim); + qpresults.y = qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq); + qpwork.dw_aug.setZero(); + qpwork.rhs.setZero(); +} + +/*! + * Setups and performs the first factorization of the regularized KKT matrix of + * the problem. + * + * @param qpwork workspace of the solver. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solution results. + */ +template +void +setup_factorization(Workspace& qpwork, + const Model& qpmodel, + Results& qpresults, + const DenseBackend& dense_backend, + const HessianType& hessian_type) +{ + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, + qpwork.ldl_stack.as_mut(), + }; + switch (hessian_type) { + case HessianType::Dense: + qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; + break; + case HessianType::Zero: + qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim).setZero(); + break; + case HessianType::Diagonal: + qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; + break; + } + qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim).diagonal().array() += + qpresults.info.rho; + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: + qpwork.kkt.block(0, qpmodel.dim, qpmodel.dim, qpmodel.n_eq) = + qpwork.A_scaled.transpose(); + qpwork.kkt.block(qpmodel.dim, 0, qpmodel.n_eq, qpmodel.dim) = + qpwork.A_scaled; + qpwork.kkt.bottomRightCorner(qpmodel.n_eq, qpmodel.n_eq).setZero(); + qpwork.kkt.diagonal() + .segment(qpmodel.dim, qpmodel.n_eq) + .setConstant(-qpresults.info.mu_eq); + qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); + break; + case DenseBackend::PrimalLDLT: + qpwork.kkt.noalias() += qpresults.info.mu_eq_inv * + (qpwork.A_scaled.transpose() * qpwork.A_scaled); + qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); + break; + case DenseBackend::Automatic: + break; + } +} + +/*! + * Performs the factorization of the regularized KKT matrix of + * the problem once setup_factorization() is called. + * + * @param qpwork workspace of the solver. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solution results. + */ +template +void +setup_factorization_complete_kkt(Results& qpresults, + const Model& qpmodel, + Workspace& qpwork, + const isize n_constraints, + const DenseBackend& dense_backend) +{ + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + + // Delete columns (from potential previous solve) + if (qpwork.dirty == true) { + auto _planned_to_delete = stack.make_new_for_overwrite( + proxsuite::linalg::veg::Tag{}, isize(n_constraints)); + isize* planned_to_delete = _planned_to_delete.ptr_mut(); + + for (isize i = 0; i < n_constraints; i++) { + planned_to_delete[i] = qpmodel.dim + qpmodel.n_eq + i; + } + + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: { + qpwork.ldl.delete_at(planned_to_delete, n_constraints, stack); + } break; + case DenseBackend::PrimalLDLT: + break; + case DenseBackend::Automatic: + break; + } + } + + // Add columns + { + T mu_in_neg(-qpresults.info.mu_in); + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: { + isize n = qpmodel.dim; + isize n_eq = qpmodel.n_eq; + LDLT_TEMP_MAT_UNINIT( + T, new_cols, n + n_eq + n_constraints, n_constraints, stack); + + for (isize k = 0; k < n_constraints; ++k) { + auto col = new_cols.col(k); + if (k >= qpmodel.n_in) { + col.head(n).setZero(); + col[k - qpmodel.n_in] = qpwork.i_scaled[k - qpmodel.n_in]; + } else { + col.head(n) = (qpwork.C_scaled.row(k)); + } + col.tail(n_eq + n_constraints).setZero(); + col[n + n_eq + k] = mu_in_neg; + } + qpwork.ldl.insert_block_at(n + n_eq, new_cols, stack); + } break; + case DenseBackend::PrimalLDLT: + break; + case DenseBackend::Automatic: + break; + } + } + + qpwork.n_c = n_constraints; +} +/*! + * Performs the equilibration of the QP problem for reducing its + * ill-conditionness. + * + * @param qpwork workspace of the solver. + * @param qpsettings settings of the solver. + * @param ruiz ruiz preconditioner. + * @param execute_preconditioner boolean variable for executing or not the ruiz + * preconditioner. If set to False, it uses the previous preconditioning + * variables (initialized to the identity preconditioner if it is the first + * scaling performed). + */ +template +void +setup_equilibration(Workspace& qpwork, + const Settings& qpsettings, + const bool box_constraints, + const HessianType hessian_type, + preconditioner::RuizEquilibration& ruiz, + bool execute_preconditioner) +{ + + QpViewBoxMut qp_scaled{ + { from_eigen, qpwork.H_scaled }, { from_eigen, qpwork.g_scaled }, + { from_eigen, qpwork.A_scaled }, { from_eigen, qpwork.b_scaled }, + { from_eigen, qpwork.C_scaled }, { from_eigen, qpwork.u_scaled }, + { from_eigen, qpwork.l_scaled }, { from_eigen, qpwork.i_scaled }, + { from_eigen, qpwork.l_box_scaled }, { from_eigen, qpwork.u_box_scaled }, + }; + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, + qpwork.ldl_stack.as_mut(), + }; + ruiz.scale_qp_in_place(qp_scaled, + execute_preconditioner, + qpsettings.primal_infeasibility_solving, + qpsettings.preconditioner_max_iter, + qpsettings.preconditioner_accuracy, + hessian_type, + box_constraints, + stack); + qpwork.correction_guess_rhs_g = infty_norm(qpwork.g_scaled); +} + +/*! + * Setups the solver initial guess. + * + * @param qpwork solver workspace. + * @param qpsettings solver settings. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + */ +template +void +initial_guess(Workspace& qpwork, + Settings& qpsettings, + Model& qpmodel, + Results& qpresults) +{ + + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + compute_equality_constrained_initial_guess( + qpwork, qpsettings, qpmodel, qpresults); + break; + } + } +} +/*! + * Initializes the KKT factorization at the beginning of qp_solve + * when the solver is dirty (qpwork.dirty == true). + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + * @param qpresults solver results. + * @param ruiz ruiz preconditioner. + */ +template +void +init_qp_solve_dirty( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + preconditioner::RuizEquilibration& ruiz, + const isize n_constraints, + const QPSolver solver) +{ + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + qpwork.cleanup(box_constraints); + qpresults.cleanup(qpsettings); + break; + } + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { + // keep solutions but restart workspace and results + qpwork.cleanup(box_constraints); + qpresults.cold_start(qpsettings); + ruiz.scale_primal_in_place({ from_eigen, qpresults.x }); + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + break; + } + case InitialGuessStatus::NO_INITIAL_GUESS: { + qpwork.cleanup(box_constraints); + qpresults.cleanup(qpsettings); + break; + } + case InitialGuessStatus::WARM_START: { + qpwork.cleanup(box_constraints); + qpresults.cold_start( + qpsettings); // because there was already a solve, + // precond was already computed if set so + ruiz.scale_primal_in_place( + { from_eigen, + qpresults.x }); // it contains the value given in entry for warm start + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + break; + } + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { + // keep workspace and results solutions except statistics + // std::cout << "i keep previous solution" << std::endl; + qpresults.cleanup_statistics(); + ruiz.scale_primal_in_place({ from_eigen, qpresults.x }); + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + break; + } + } + if (qpsettings.initial_guess != + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { + switch (hessian_type) { + case HessianType::Zero: + break; + case HessianType::Dense: + qpwork.H_scaled = qpmodel.H; + break; + case HessianType::Diagonal: + qpwork.H_scaled = qpmodel.H; + break; + } + qpwork.g_scaled = qpmodel.g; + qpwork.A_scaled = qpmodel.A; + qpwork.b_scaled = qpmodel.b; + qpwork.C_scaled = qpmodel.C; + qpwork.u_scaled = qpmodel.u; + qpwork.l_scaled = qpmodel.l; + setup_equilibration(qpwork, + qpsettings, + box_constraints, + hessian_type, + ruiz, + false); // reuse previous equilibration + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + } + switch (solver) { + case QPSolver::PROXQP: { + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + compute_equality_constrained_initial_guess(qpwork, + qpsettings, + qpmodel, + n_constraints, + dense_backend, + hessian_type, + qpresults); + break; + } + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { + //!\ TODO in a quicker way + qpwork.n_c = 0; + for (isize i = 0; i < n_constraints; i++) { + if (qpresults.z[i] != 0) { + qpwork.active_inequalities[i] = true; + } else { + qpwork.active_inequalities[i] = false; + } + } + proxsuite::proxqp::dense::linesearch::active_set_change( + qpmodel, qpresults, dense_backend, n_constraints, qpwork); + break; + } + case InitialGuessStatus::NO_INITIAL_GUESS: { + break; + } + case InitialGuessStatus::WARM_START: { + //!\ TODO in a quicker way + qpwork.n_c = 0; + for (isize i = 0; i < n_constraints; i++) { + if (qpresults.z[i] != 0) { + qpwork.active_inequalities[i] = true; + } else { + qpwork.active_inequalities[i] = false; + } + } + proxsuite::proxqp::dense::linesearch::active_set_change( + qpmodel, qpresults, dense_backend, n_constraints, qpwork); + break; + } + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { + // keep workspace and results solutions except statistics + // std::cout << "i use previous solution" << std::endl; + // meaningful for when one wants to warm start with previous result + // with the same QP model + break; + } + } + break; + } + case QPSolver::OSQP: { + if (qpsettings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS) { + compute_equality_constrained_initial_guess(qpwork, + qpsettings, + qpmodel, + n_constraints, + dense_backend, + hessian_type, + qpresults); + } + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + } +} +/*! + * Initializes the solver at the beginning of qp_solve function + * when the solver is not dirty (qpwork.dirty == false). + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + * @param qpresults solver results. + * @param ruiz ruiz preconditioner. + */ +template +void +init_qp_solve_non_dirty( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + preconditioner::RuizEquilibration& ruiz, + const isize n_constraints, + const QPSolver solver) +{ + switch (solver) { + case QPSolver::PROXQP: { + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + compute_equality_constrained_initial_guess(qpwork, + qpsettings, + qpmodel, + n_constraints, + dense_backend, + hessian_type, + qpresults); + break; + } + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { + //!\ TODO in a quicker way + ruiz.scale_primal_in_place( + { from_eigen, qpresults.x }); // meaningful for when there is an + // upate of the model and one wants to + // warm start with previous result + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + qpwork.n_c = 0; + for (isize i = 0; i < n_constraints; i++) { + if (qpresults.z[i] != 0) { + qpwork.active_inequalities[i] = true; + } else { + qpwork.active_inequalities[i] = false; + } + } + proxsuite::proxqp::dense::linesearch::active_set_change( + qpmodel, qpresults, dense_backend, n_constraints, qpwork); + break; + } + case InitialGuessStatus::NO_INITIAL_GUESS: { + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + break; + } + case InitialGuessStatus::WARM_START: { + //!\ TODO in a quicker way + ruiz.scale_primal_in_place({ from_eigen, qpresults.x }); + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + qpwork.n_c = 0; + for (isize i = 0; i < n_constraints; i++) { + if (qpresults.z[i] != 0) { + qpwork.active_inequalities[i] = true; + } else { + qpwork.active_inequalities[i] = false; + } + } + proxsuite::proxqp::dense::linesearch::active_set_change( + qpmodel, qpresults, dense_backend, n_constraints, qpwork); + break; + } + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { + // std::cout << "i refactorize from previous solution" << std::endl; + ruiz.scale_primal_in_place( + { from_eigen, qpresults.x }); // meaningful for when there is an + // upate of the model and one wants to + // warm start with previous result + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + if (qpwork.refactorize) { // refactorization only when one of the + // matrices has changed or one proximal + // parameter has changed + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + qpwork.n_c = 0; + for (isize i = 0; i < n_constraints; i++) { + if (qpresults.z[i] != 0) { + qpwork.active_inequalities[i] = true; + } else { + qpwork.active_inequalities[i] = false; + } + } + proxsuite::proxqp::dense::linesearch::active_set_change( + qpmodel, qpresults, dense_backend, n_constraints, qpwork); + break; + } + } + } + break; + } + case QPSolver::OSQP: { + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + compute_equality_constrained_initial_guess(qpwork, + qpsettings, + qpmodel, + n_constraints, + dense_backend, + hessian_type, + qpresults); + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { + //!\ TODO in a quicker way + ruiz.scale_primal_in_place( + { from_eigen, qpresults.x }); // meaningful for when there is an + // upate of the model and one wants to + // warm start with previous result + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + case InitialGuessStatus::NO_INITIAL_GUESS: { + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + case InitialGuessStatus::WARM_START: { + //!\ TODO in a quicker way + ruiz.scale_primal_in_place({ from_eigen, qpresults.x }); + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { + // std::cout << "i refactorize from previous solution" << std::endl; + ruiz.scale_primal_in_place( + { from_eigen, qpresults.x }); // meaningful for when there is an + // upate of the model and one wants to + // warm start with previous result + ruiz.scale_dual_in_place_eq({ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + { from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + { from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + if (qpwork.refactorize) { // refactorization only when one of the + // matrices has changed or one proximal + // parameter has changed + setup_factorization( + qpwork, qpmodel, qpresults, dense_backend, hessian_type); + setup_factorization_complete_kkt( + qpresults, qpmodel, qpwork, n_constraints, dense_backend); + break; + } + } + } + break; + } + } +} +/*! + * Initializes the solver at the beginning of qp_solve function. + * In particular it: + * Setups Ruiz equilibration, + * Performs the first or only (depends on the solver) KKT factorization. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + * @param qpresults solver results. + * @param ruiz ruiz preconditioner. + */ +template +void +init_qp_solve( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + preconditioner::RuizEquilibration& ruiz, + const isize n_constraints, + const QPSolver solver) +{ + if (qpwork.dirty) { + // Used when a solve has already been executed + // (and without any intermediary model update) + init_qp_solve_dirty(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + dense_backend, + hessian_type, + ruiz, + n_constraints, + solver); + } else { + // Used for a first solve after initializing or + // updating the Qp object + init_qp_solve_non_dirty(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + dense_backend, + hessian_type, + ruiz, + n_constraints, + solver); + } +} +/*! + * Updates the QP solver model. + * + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param qpwork solver workspace. + * @param qpsettings solver settings. + * @param qpmodel solver model. + * @param qpresults solver result. + */ + +template +void +update(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + Model& model, + Workspace& work, + const bool box_constraints) +{ + // check the model is valid + if (g != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE(g.value().size(), + model.dim, + "the dimension wrt the primal variable x " + "variable for updating g is not valid."); + } + if (b != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE(b.value().size(), + model.n_eq, + "the dimension wrt equality constrained " + "variables for updating b is not valid."); + } + if (u != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE(u.value().size(), + model.n_in, + "the dimension wrt inequality constrained " + "variables for updating u is not valid."); + } + if (l != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE(l.value().size(), + model.n_in, + "the dimension wrt inequality constrained " + "variables for updating l is not valid."); + } + if (H != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().rows(), + model.dim, + "the row dimension for updating H is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().cols(), + model.dim, + "the column dimension for updating H is not valid."); + } + if (A != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().rows(), + model.n_eq, + "the row dimension for updating A is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().cols(), + model.dim, + "the column dimension for updating A is not valid."); + } + if (C != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().rows(), + model.n_in, + "the row dimension for updating C is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().cols(), + model.dim, + "the column dimension for updating C is not valid."); + } + + // update the model + if (g != nullopt) { + model.g = g.value().eval(); + } + if (b != nullopt) { + model.b = b.value().eval(); + } + if (u != nullopt) { + model.u = u.value().eval(); + } + if (l != nullopt) { + model.l = l.value().eval(); + } + if (u_box != nullopt && box_constraints) { + model.u_box = u_box.value(); + } // else qpmodel.u_box remains initialized to a matrix with zero elements or + // zero shape + + if (l_box != nullopt && box_constraints) { + model.l_box = l_box.value(); + } // else qpmodel.l_box remains initialized to a matrix with zero elements or + // zero shape + + if (H != nullopt || A != nullopt || C != nullopt) { + work.refactorize = true; + } + + if (H != nullopt) { + model.H = H.value(); + } + if (A != nullopt) { + model.A = A.value(); + } + if (C != nullopt) { + model.C = C.value(); + } + assert(model.is_valid(box_constraints)); +} +/*! + * Setups the QP solver model. + * + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param qpwork solver workspace. + * @param qpsettings solver settings. + * @param qpmodel solver model. + * @param qpresults solver result. + * @param ruiz ruiz preconditioner. + * @param preconditioner_status bool variable for deciding whether executing the + * preconditioning algorithm, or keeping previous preconditioning variables, or + * using the identity preconditioner (i.e., no preconditioner). + */ +template +void +setup( // + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + Settings& qpsettings, + Model& qpmodel, + Workspace& qpwork, + Results& qpresults, + const bool box_constraints, + preconditioner::RuizEquilibration& ruiz, + PreconditionerStatus preconditioner_status, + const HessianType hessian_type) +{ + + switch (qpsettings.initial_guess) { + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { + if (qpwork.proximal_parameter_update) { + qpresults.cleanup_all_except_prox_parameters(); + } else { + qpresults.cleanup(qpsettings); + } + qpwork.cleanup(box_constraints); + break; + } + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { + // keep solutions but restart workspace and results + if (qpwork.proximal_parameter_update) { + qpresults.cleanup_statistics(); + } else { + qpresults.cold_start(qpsettings); + } + qpwork.cleanup(box_constraints); + break; + } + case InitialGuessStatus::NO_INITIAL_GUESS: { + if (qpwork.proximal_parameter_update) { + qpresults.cleanup_all_except_prox_parameters(); + } else { + qpresults.cleanup(qpsettings); + } + qpwork.cleanup(box_constraints); + break; + } + case InitialGuessStatus::WARM_START: { + if (qpwork.proximal_parameter_update) { + qpresults + .cleanup_all_except_prox_parameters(); // the warm start is given at + // the solve function + } else { + qpresults.cleanup(qpsettings); + } + qpwork.cleanup(box_constraints); + break; + } + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { + if (qpwork.refactorize || qpwork.proximal_parameter_update) { + qpwork.cleanup(box_constraints); // meaningful for when there is an + // upate of the model and one wants to + // warm start with previous result + qpwork.refactorize = true; + } + qpresults.cleanup_statistics(); + break; + } + } + if (H != nullopt) { + qpmodel.H = H.value(); + } // else qpmodel.H remains initialzed to a matrix with zero elements + if (g != nullopt) { + qpmodel.g = g.value(); + } + + if (A != nullopt) { + qpmodel.A = A.value(); + } // else qpmodel.A remains initialized to a matrix with zero elements or zero + // shape + + if (b != nullopt) { + qpmodel.b = b.value(); + } // else qpmodel.b remains initialized to a matrix with zero elements or zero + // shape + + if (C != nullopt) { + qpmodel.C = C.value(); + } // else qpmodel.C remains initialized to a matrix with zero elements or zero + // shape + + if (u != nullopt) { + qpmodel.u = u.value(); + } // else qpmodel.u remains initialized to a matrix with zero elements or zero + // shape + + if (l != nullopt) { + qpmodel.l = l.value(); + } // else qpmodel.l remains initialized to a matrix with zero elements or zero + // shape + if (u_box != nullopt) { + qpmodel.u_box = u_box.value(); + } // else qpmodel.u_box remains initialized to a matrix with zero elements or + // zero shape + + if (l_box != nullopt) { + qpmodel.l_box = l_box.value(); + } // else qpmodel.l_box remains initialized to a matrix with zero elements or + // zero shape + assert(qpmodel.is_valid(box_constraints)); + switch (hessian_type) { + case HessianType::Zero: + break; + case HessianType::Dense: + qpwork.H_scaled = qpmodel.H; + break; + case HessianType::Diagonal: + qpwork.H_scaled = qpmodel.H; + break; + } + qpwork.g_scaled = qpmodel.g; + qpwork.A_scaled = qpmodel.A; + qpwork.b_scaled = qpmodel.b; + qpwork.C_scaled = qpmodel.C; + qpwork.u_scaled = + (qpmodel.u.array() <= T(1.E20)) + .select(qpmodel.u, + Eigen::Matrix::Zero(qpmodel.n_in).array() + + T(1.E20)); + qpwork.l_scaled = + (qpmodel.l.array() >= T(-1.E20)) + .select(qpmodel.l, + Eigen::Matrix::Zero(qpmodel.n_in).array() - + T(1.E20)); + if (box_constraints) { + qpwork.u_box_scaled = + (qpmodel.u_box.array() <= T(1.E20)) + .select(qpmodel.u_box, + Eigen::Matrix::Zero(qpmodel.dim).array() + + T(1.E20)); + qpwork.l_box_scaled = + (qpmodel.l_box.array() >= T(-1.E20)) + .select(qpmodel.l_box, + Eigen::Matrix::Zero(qpmodel.dim).array() - + T(1.E20)); + } + + qpwork.dual_feasibility_rhs_2 = infty_norm(qpmodel.g); + switch (preconditioner_status) { + case PreconditionerStatus::EXECUTE: + setup_equilibration( + qpwork, qpsettings, box_constraints, hessian_type, ruiz, true); + break; + case PreconditionerStatus::IDENTITY: + setup_equilibration( + qpwork, qpsettings, box_constraints, hessian_type, ruiz, false); + break; + case PreconditionerStatus::KEEP: + // keep previous one + setup_equilibration( + qpwork, qpsettings, box_constraints, hessian_type, ruiz, false); + break; + } +} +////// UPDATES /////// + +/*! + * Update the proximal parameters of the results object. + * + * @param rho_new primal proximal parameter. + * @param mu_eq_new dual equality proximal parameter. + * @param mu_in_new dual inequality proximal parameter. + * @param results solver results. + */ +template +void +update_proximal_parameters(Settings& settings, + Results& results, + Workspace& work, + optional rho_new, + optional mu_eq_new, + optional mu_in_new) +{ + + if (rho_new != nullopt) { + settings.default_rho = rho_new.value(); + results.info.rho = rho_new.value(); + work.proximal_parameter_update = true; + } + if (mu_eq_new != nullopt) { + settings.default_mu_eq = mu_eq_new.value(); + results.info.mu_eq = mu_eq_new.value(); + results.info.mu_eq_inv = T(1) / results.info.mu_eq; + work.proximal_parameter_update = true; + } + if (mu_in_new != nullopt) { + settings.default_mu_in = mu_in_new.value(); + results.info.mu_in = mu_in_new.value(); + results.info.mu_in_inv = T(1) / results.info.mu_in; + work.proximal_parameter_update = true; + } +} +/*! + * Warm start the primal and dual variables. + * + * @param x_wm primal warm start. + * @param y_wm dual equality warm start. + * @param z_wm dual inequality warm start. + * @param results solver result. + * @param settings solver settings. + */ +template +void +warm_start(optional> x_wm, + optional> y_wm, + optional> z_wm, + Results& results, + Settings& settings, + Model& model) +{ + if (x_wm == nullopt && y_wm == nullopt && z_wm == nullopt) + return; + + settings.initial_guess = InitialGuessStatus::WARM_START; + + // first check problem dimensions + if (x_wm != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + x_wm.value().rows(), + model.dim, + "the dimension wrt primal variable x for warm start is not valid."); + } + + if (y_wm != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE(y_wm.value().rows(), + model.n_eq, + "the dimension wrt equality constrained " + "variables for warm start is not valid."); + } + + if (z_wm != nullopt) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + z_wm.value().rows(), + model.n_in, + "the dimension wrt inequality constrained variables for warm start " + "is not valid."); + } + + if (x_wm != nullopt) { + results.x = x_wm.value().eval(); + } + + if (y_wm != nullopt) { + results.y = y_wm.value().eval(); + } + + if (z_wm != nullopt) { + results.z = z_wm.value().eval(); + } +} + +/*! + * Updates the dual proximal parameters of the solver (i.e., penalization + * parameters of the primal-dual merit function). + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param mu_eq_new new dual equality constrained proximal parameter. + * @param mu_in_new new dual inequality constrained proximal parameter. + */ +template +void +mu_update(const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + isize n_constraints, + const DenseBackend& dense_backend, + T mu_eq_new, + T mu_in_new) +{ + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + + isize n = qpmodel.dim; + isize n_eq = qpmodel.n_eq; + isize n_c = qpwork.n_c; + + if ((n_eq + n_c) == 0) { + return; + } + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: { + LDLT_TEMP_VEC_UNINIT(T, rank_update_alpha, n_eq + n_c, stack); + + rank_update_alpha.head(n_eq).setConstant(qpresults.info.mu_eq - + mu_eq_new); + rank_update_alpha.tail(n_c).setConstant(qpresults.info.mu_in - mu_in_new); + + { + auto _indices = stack.make_new_for_overwrite( + proxsuite::linalg::veg::Tag{}, n_eq + n_c); + isize* indices = _indices.ptr_mut(); + for (isize k = 0; k < n_eq; ++k) { + indices[k] = n + k; + } + for (isize k = 0; k < n_c; ++k) { + indices[n_eq + k] = n + n_eq + k; + } + qpwork.ldl.diagonal_update_clobber_indices( + indices, n_eq + n_c, rank_update_alpha, stack); + } + } break; + case DenseBackend::PrimalLDLT: { + // we refactorize there for the moment + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, + qpwork.ldl_stack.as_mut(), + }; + // qpwork.kkt.noalias() = qpwork.H_scaled + (qpwork.A_scaled.transpose() * + // qpwork.A_scaled) / mu_eq_new; qpwork.kkt.diagonal().array() += + // qpresults.info.rho; for (isize i = 0; i < n_constraints; i++){ + // if (qpwork.active_inequalities(i)){ + // if (i >=qpmodel.n_in){ + // // box constraints + // qpwork.kkt(i-qpmodel.n_in,i-qpmodel.n_in) += + // std::pow(qpwork.i_scaled(i-qpmodel.n_in),2) / mu_in_new ; + // } else{ + // // generic ineq constraint + // qpwork.kkt.noalias() += qpwork.C_scaled.row(i).transpose() * + // qpwork.C_scaled.row(i) / mu_in_new ; + // } + // } + // } + // qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); + + // mu update for C_J + { + LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpwork.n_c, stack); + qpwork.dw_aug.head(qpmodel.dim).setOnes(); + T delta_mu(T(1) / mu_in_new - qpresults.info.mu_in_inv); + qpwork.dw_aug.head(qpmodel.dim).array() *= delta_mu; + for (isize i = 0; i < n_constraints; ++i) { + isize j = qpwork.current_bijection_map[i]; + if (j < n_c) { + auto col = new_cols.col(j); + if (i >= qpmodel.n_in) { + // box constraint + col.setZero(); + col[i - qpmodel.n_in] = qpwork.i_scaled[i - qpmodel.n_in]; + } else { + // generic ineq constraints + col = qpwork.C_scaled.row(i); + } + } + } + qpwork.ldl.rank_r_update( + new_cols, qpwork.dw_aug.head(qpwork.n_c), stack); + } + // mu update for A + { + LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpmodel.n_eq, stack); + qpwork.dw_aug.head(qpmodel.n_eq).setOnes(); + T delta_mu(1 / mu_eq_new - qpresults.info.mu_eq_inv); + qpwork.dw_aug.head(qpmodel.n_eq).array() *= delta_mu; + new_cols = qpwork.A_scaled.transpose(); + qpwork.ldl.rank_r_update( + new_cols, qpwork.dw_aug.head(qpmodel.n_eq), stack); + } + } break; + case DenseBackend::Automatic: + break; + } + qpwork.constraints_changed = true; +} + +/*! + * Save a matrix into a CSV format. Used for debug purposes. + * + * @param filename filename name for the CSV. + * @param mat matrix to save into CSV format. + */ +template +void +save_data(const std::string& filename, const ::Eigen::MatrixBase& mat) +{ + // https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html + const static Eigen::IOFormat CSVFormat( + Eigen::FullPrecision, Eigen::DontAlignCols, ", ", "\n"); + + std::ofstream file(filename); + if (file.is_open()) { + file << mat.format(CSVFormat); + file.close(); + } +} + +} // namespace dense +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_DENSE_HELPERS_HPP */ diff --git a/include/proxsuite/common/dense/iterative_solve.hpp b/include/proxsuite/common/dense/iterative_solve.hpp new file mode 100644 index 000000000..9430e936e --- /dev/null +++ b/include/proxsuite/common/dense/iterative_solve.hpp @@ -0,0 +1,425 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file solver.hpp + */ + +#ifndef PROXSUITE_COMMON_DENSE_ITERATIVE_SOLVE_HPP +#define PROXSUITE_COMMON_DENSE_ITERATIVE_SOLVE_HPP + +#include +#include +#include +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/dense/workspace.hpp" +#include "proxsuite/common/dense/model.hpp" +#include + +namespace proxsuite { +namespace common { +namespace dense { + +/*! + * Performs a refactorization of the KKT matrix used by the solver. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param rho_new new primal proximal parameter used for the refactorization. + */ +template +void +refactorize(const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const isize n_constraints, + const DenseBackend& dense_backend, + T rho_new) +{ + + if (!qpwork.constraints_changed && rho_new == qpresults.info.rho) { + return; + } + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: { + qpwork.kkt.diagonal().head(qpmodel.dim).array() += + rho_new - qpresults.info.rho; + qpwork.kkt.diagonal().segment(qpmodel.dim, qpmodel.n_eq).array() = + -qpresults.info.mu_eq; + qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); + + isize n = qpmodel.dim; + isize n_eq = qpmodel.n_eq; + isize n_in = qpmodel.n_in; + isize n_c = qpwork.n_c; + + LDLT_TEMP_MAT(T, new_cols, n + n_eq + n_c, n_c, stack); + T mu_in_neg(-qpresults.info.mu_in); + for (isize i = 0; i < n_constraints; ++i) { + isize j = qpwork.current_bijection_map[i]; + if (j < n_c) { + auto col = new_cols.col(j); + if (i >= n_in) { + // I_scaled = D which is the diagonal matrix + // scaling x + // col(i-n_in) = ruiz.delta[i-qpmodel.n_in]; + col(i - n_in) = qpwork.i_scaled[i - qpmodel.n_in]; + } else { + col.head(n) = qpwork.C_scaled.row(i); + } + col.segment(n, n_eq + n_c).setZero(); + col(n + n_eq + j) = mu_in_neg; + } + } + qpwork.ldl.insert_block_at(n + n_eq, new_cols, stack); + } break; + case DenseBackend::PrimalLDLT: { + qpwork.kkt.noalias() = + qpwork.H_scaled + (qpwork.A_scaled.transpose() * qpwork.A_scaled) * + qpresults.info.mu_eq_inv; + qpwork.kkt.diagonal().array() += qpresults.info.rho; + for (isize i = 0; i < n_constraints; i++) { + if (qpwork.active_inequalities(i)) { + if (i >= qpmodel.n_in) { + // box constraints + qpwork.kkt(i - qpmodel.n_in, i - qpmodel.n_in) += + std::pow(qpwork.i_scaled(i - qpmodel.n_in), 2) * + qpresults.info.mu_in_inv; + } else { + // generic ineq constraint + qpwork.kkt.noalias() += qpwork.C_scaled.row(i).transpose() * + qpwork.C_scaled.row(i) * + qpresults.info.mu_in_inv; + } + } + } + qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); + } break; + case DenseBackend::Automatic: + break; + } + + qpwork.constraints_changed = false; +} +/*! + * Derives the residual of the iterative refinement algorithm used for solving + * associated linear systems of solvers algorithms. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param inner_pb_dim dimension of the linear system. + */ +template +void +iterative_residual(const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const isize n_constraints, + isize inner_pb_dim, + const HessianType& hessian_type) +{ + auto& Hdx = qpwork.Hdx; + auto& Adx = qpwork.Adx; + auto& ATdy = qpwork.CTz; + qpwork.err.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); + switch (hessian_type) { + case HessianType::Zero: + break; + case HessianType::Dense: + Hdx.noalias() = qpwork.H_scaled.template selfadjointView() * + qpwork.dw_aug.head(qpmodel.dim); + qpwork.err.head(qpmodel.dim).noalias() -= Hdx; + break; + case HessianType::Diagonal: +#ifndef NDEBUG + PROXSUITE_THROW_PRETTY(!qpwork.H_scaled.isDiagonal(), + std::invalid_argument, + "H is not diagonal."); +#endif + Hdx.array() = qpwork.H_scaled.diagonal().array() * + qpwork.dw_aug.head(qpmodel.dim).array(); + qpwork.err.head(qpmodel.dim).noalias() -= Hdx; + break; + } + qpwork.err.head(qpmodel.dim) -= + qpresults.info.rho * qpwork.dw_aug.head(qpmodel.dim); + + // PERF: fuse {A, C}_scaled multiplication operations + ATdy.noalias() = qpwork.A_scaled.transpose() * + qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq); + qpwork.err.head(qpmodel.dim).noalias() -= ATdy; + if (n_constraints > qpmodel.n_in) { + // there are box constraints + qpwork.active_part_z.tail(qpmodel.dim) = qpwork.dw_aug.head(qpmodel.dim); + qpwork.active_part_z.tail(qpmodel.dim).array() *= qpwork.i_scaled.array(); + // ruiz.unscale_primal_in_place(VectorViewMut{from_eigen,qpwork.active_part_z.tail(qpmodel.dim)}); + } + for (isize i = 0; i < n_constraints; i++) { + isize j = qpwork.current_bijection_map(i); + if (j < qpwork.n_c) { + if (i >= qpmodel.n_in) { + // I_scaled * dz_box_scaled = unscale_primally(dz_box) + qpwork.err(i - qpmodel.n_in) -= + // qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * + // ruiz.delta(i-qpmodel.n_in); + qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * + qpwork.i_scaled(i - qpmodel.n_in); + // I_scaled * dx_scaled = dx_unscaled + qpwork.err(qpmodel.dim + qpmodel.n_eq + j) -= + (qpwork.active_part_z[i] - + qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * + qpresults.info.mu_in); + } else { + qpwork.err.head(qpmodel.dim).noalias() -= + qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * + qpwork.C_scaled.row(i); + qpwork.err(qpmodel.dim + qpmodel.n_eq + j) -= + (qpwork.C_scaled.row(i).dot(qpwork.dw_aug.head(qpmodel.dim)) - + qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * + qpresults.info.mu_in); + } + } + } + Adx.noalias() = qpwork.A_scaled * qpwork.dw_aug.head(qpmodel.dim); + qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).noalias() -= Adx; + qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) += + qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq; +} + +template +void +solve_linear_system(Vec& dw, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const isize n_constraints, + const DenseBackend& dense_backend, + isize inner_pb_dim, + proxsuite::linalg::veg::dynstack::DynStackMut& stack) +{ + + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: + qpwork.ldl.solve_in_place(dw.head(inner_pb_dim), stack); + break; + case DenseBackend::PrimalLDLT: + // find dx + dw.head(qpmodel.dim).noalias() += qpresults.info.mu_eq_inv * + qpwork.A_scaled.transpose() * + dw.segment(qpmodel.dim, qpmodel.n_eq); + for (isize i = 0; i < n_constraints; i++) { + isize j = qpwork.current_bijection_map(i); + if (j < qpwork.n_c) { + if (i >= qpmodel.n_in) { + // box constraints + dw(i - qpmodel.n_in) += dw(j + qpmodel.dim + qpmodel.n_eq) * + qpwork.i_scaled(i - qpmodel.n_in); + } else { + // ineq constraints + dw.head(qpmodel.dim) += + dw(j + qpmodel.dim + qpmodel.n_eq) * qpwork.C_scaled.row(i); + } + } + } + qpwork.ldl.solve_in_place(dw.head(qpmodel.dim), stack); + // find dy + dw.segment(qpmodel.dim, qpmodel.n_eq) -= + qpresults.info.mu_eq_inv * dw.segment(qpmodel.dim, qpmodel.n_eq); + dw.segment(qpmodel.dim, qpmodel.n_eq).noalias() += + qpresults.info.mu_eq_inv * + (qpwork.A_scaled * + dw.head( + qpmodel.dim)); //- qpwork.rhs.segment(qpmodel.dim,qpmodel.n_eq)); + // find dz_J + for (isize i = 0; i < n_constraints; i++) { + isize j = qpwork.current_bijection_map(i); + if (j < qpwork.n_c) { + if (i >= qpmodel.n_in) { + // box constraints + dw(j + qpmodel.dim + qpmodel.n_eq) -= + qpresults.info.mu_in_inv * (dw(j + qpmodel.dim + qpmodel.n_eq)); + dw(j + qpmodel.dim + qpmodel.n_eq) += + qpresults.info.mu_in_inv * + (dw( + i - + qpmodel.n_in)); //- qpwork.rhs(j + qpmodel.dim + qpmodel.n_eq)); + } else { + // ineq constraints + dw(j + qpmodel.dim + qpmodel.n_eq) -= + qpresults.info.mu_in_inv * (dw(j + qpmodel.dim + qpmodel.n_eq)); + dw(j + qpmodel.dim + qpmodel.n_eq) += + qpresults.info.mu_in_inv * + (qpwork.C_scaled.row(i).dot(dw.head( + qpmodel.dim))); //- qpwork.rhs(j + qpmodel.dim + qpmodel.n_eq)); + } + } + } + break; + case DenseBackend::Automatic: + break; + } +} + +/*! + * Performs iterative refinement for solving associated linear systems of + * solvers algorithms. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + * @param qpresults solver results. + * @param eps accuracy required for pursuing or not the iterative refinement. + * @param inner_pb_dim dimension of the linear system. + */ +template +void +iterative_solve_with_permut_fact( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const isize n_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + T eps, + isize inner_pb_dim) +{ + + qpwork.err.setZero(); + i32 it = 0; + i32 it_stability = 0; + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + qpwork.dw_aug.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); + solve_linear_system(qpwork.dw_aug, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + inner_pb_dim, + stack); + iterative_residual( + qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); + + ++it; + T preverr = infty_norm(qpwork.err.head(inner_pb_dim)); + while (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps) { + + if (it >= qpsettings.nb_iterative_refinement) { + break; + } + ++it; + solve_linear_system(qpwork.err, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + inner_pb_dim, + stack); + // qpwork.ldl.solve_in_place(qpwork.err.head(inner_pb_dim), stack); + qpwork.dw_aug.head(inner_pb_dim) += qpwork.err.head(inner_pb_dim); + + qpwork.err.head(inner_pb_dim).setZero(); + iterative_residual( + qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); + + if (infty_norm(qpwork.err.head(inner_pb_dim)) > preverr) { + it_stability += 1; + + } else { + it_stability = 0; + } + if (it_stability == 2) { + break; + } + preverr = infty_norm(qpwork.err.head(inner_pb_dim)); + } + + if (infty_norm(qpwork.err.head(inner_pb_dim)) >= + std::max(eps, qpsettings.eps_refact)) { + refactorize(qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + qpresults.info.rho); + it = 0; + it_stability = 0; + + qpwork.dw_aug.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); + solve_linear_system(qpwork.dw_aug, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + inner_pb_dim, + stack); + // qpwork.ldl.solve_in_place(qpwork.dw_aug.head(inner_pb_dim), stack); + + iterative_residual( + qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); + + preverr = infty_norm(qpwork.err.head(inner_pb_dim)); + ++it; + while (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps) { + + if (it >= qpsettings.nb_iterative_refinement) { + break; + } + ++it; + solve_linear_system(qpwork.err, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + inner_pb_dim, + stack); + // qpwork.ldl.solve_in_place(qpwork.err.head(inner_pb_dim), stack); + qpwork.dw_aug.head(inner_pb_dim) += qpwork.err.head(inner_pb_dim); + + qpwork.err.head(inner_pb_dim).setZero(); + iterative_residual( + qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); + + if (infty_norm(qpwork.err.head(inner_pb_dim)) > preverr) { + it_stability += 1; + } else { + it_stability = 0; + } + if (it_stability == 2) { + break; + } + preverr = infty_norm(qpwork.err.head(inner_pb_dim)); + } + } + if (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps && qpsettings.verbose) { + // std::cout << "after refact err " << err << std::endl; + std::cout << "refact err " << infty_norm(qpwork.err.head(inner_pb_dim)) + << std::endl; + } + qpresults.info.iterative_residual = infty_norm(qpwork.err.head(inner_pb_dim)); + + qpwork.rhs.head(inner_pb_dim).setZero(); +} + +} // namespace dense +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_DENSE_ITERATIVE_SOLVE_HPP */ diff --git a/include/proxsuite/proxqp/dense/model.hpp b/include/proxsuite/common/dense/model.hpp similarity index 94% rename from include/proxsuite/proxqp/dense/model.hpp rename to include/proxsuite/common/dense/model.hpp index 60cfe3c6a..17b67dcb1 100644 --- a/include/proxsuite/proxqp/dense/model.hpp +++ b/include/proxsuite/common/dense/model.hpp @@ -2,17 +2,19 @@ // Copyright (c) 2022 INRIA // /** \file */ -#ifndef PROXSUITE_PROXQP_DENSE_MODEL_HPP -#define PROXSUITE_PROXQP_DENSE_MODEL_HPP +#ifndef PROXSUITE_COMMON_DENSE_MODEL_HPP +#define PROXSUITE_COMMON_DENSE_MODEL_HPP #include #include "proxsuite/linalg/veg/type_traits/core.hpp" -#include "proxsuite/proxqp/dense/fwd.hpp" +#include "proxsuite/common/dense/fwd.hpp" +#include "proxsuite/common/dense/backward_data.hpp" #include "proxsuite/proxqp/sparse/model.hpp" -#include "proxsuite/proxqp/dense/backward_data.hpp" + namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { + /// /// @brief This class stores the model of the QP problem. /// @@ -170,7 +172,7 @@ operator!=(const Model& model1, const Model& model2) } } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_MODEL_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_MODEL_HPP */ diff --git a/include/proxsuite/proxqp/dense/preconditioner/identity.hpp b/include/proxsuite/common/dense/preconditioner/identity.hpp similarity index 88% rename from include/proxsuite/proxqp/dense/preconditioner/identity.hpp rename to include/proxsuite/common/dense/preconditioner/identity.hpp index efbb26eef..28b2add42 100644 --- a/include/proxsuite/proxqp/dense/preconditioner/identity.hpp +++ b/include/proxsuite/common/dense/preconditioner/identity.hpp @@ -1,18 +1,19 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2025 INRIA // /** * @file identity.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_PRECOND_IDENTITY_HPP -#define PROXSUITE_PROXQP_DENSE_PRECOND_IDENTITY_HPP +#ifndef PROXSUITE_COMMON_DENSE_PRECOND_IDENTITY_HPP +#define PROXSUITE_COMMON_DENSE_PRECOND_IDENTITY_HPP -#include "proxsuite/proxqp/dense/views.hpp" +#include "proxsuite/common/dense/views.hpp" namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { namespace preconditioner { + struct IdentityPrecond { @@ -106,8 +107,8 @@ struct IdentityPrecond }; } // namespace preconditioner } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_PRECOND_IDENTITY_HPP \ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_PRECOND_IDENTITY_HPP \ */ diff --git a/include/proxsuite/proxqp/dense/preconditioner/ruiz.hpp b/include/proxsuite/common/dense/preconditioner/ruiz.hpp similarity index 97% rename from include/proxsuite/proxqp/dense/preconditioner/ruiz.hpp rename to include/proxsuite/common/dense/preconditioner/ruiz.hpp index 17fb64801..e4545e437 100644 --- a/include/proxsuite/proxqp/dense/preconditioner/ruiz.hpp +++ b/include/proxsuite/common/dense/preconditioner/ruiz.hpp @@ -1,22 +1,22 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2025 INRIA // /** * @file ruiz.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP -#define PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP +#ifndef PROXSUITE_COMMON_DENSE_PRECOND_RUIZ_HPP +#define PROXSUITE_COMMON_DENSE_PRECOND_RUIZ_HPP -#include "proxsuite/proxqp/dense/views.hpp" -#include "proxsuite/proxqp/dense/fwd.hpp" +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/dense/fwd.hpp" +#include #include -#include #include #include #include namespace proxsuite { -namespace proxqp { +namespace common { enum struct Symmetry { general, @@ -259,7 +259,7 @@ ruiz_scale_qp_in_place( // // upper triangular part T tmp = T(0); for (isize j = 0; j < n; ++j) { - tmp += proxqp::dense::infty_norm(H.row(j).tail(n - j)); + tmp += infty_norm(H.row(j).tail(n - j)); } gamma = 1 / std::max(tmp / T(n), T(1)); break; @@ -268,7 +268,7 @@ ruiz_scale_qp_in_place( // // lower triangular part T tmp = T(0); for (isize j = 0; j < n; ++j) { - tmp += proxqp::dense::infty_norm(H.col(j).tail(n - j)); + tmp += infty_norm(H.col(j).tail(n - j)); } gamma = 1 / std::max(tmp / T(n), T(1)); break; @@ -412,7 +412,7 @@ struct RuizEquilibration if (execute_preconditioner) { delta.setOnes(); c = - detail::ruiz_scale_qp_in_place({ proxqp::from_eigen, delta }, + detail::ruiz_scale_qp_in_place({ from_eigen, delta }, logger_ptr, qp, epsilon, @@ -719,7 +719,7 @@ operator!=(const RuizEquilibration& ruiz1, const RuizEquilibration& ruiz2) } // namespace preconditioner } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_PRECOND_RUIZ_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_PRECOND_RUIZ_HPP */ diff --git a/include/proxsuite/common/dense/prints.hpp b/include/proxsuite/common/dense/prints.hpp new file mode 100644 index 000000000..231e7f2bd --- /dev/null +++ b/include/proxsuite/common/dense/prints.hpp @@ -0,0 +1,326 @@ +// +// Copyright (c) 2022-2025 INRIA +// +/** \file */ +#ifndef PROXSUITE_COMMON_DENSE_PRINTS_HPP +#define PROXSUITE_COMMON_DENSE_PRINTS_HPP + +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/utils/prints.hpp" +#include "proxsuite/common/dense/model.hpp" +#include "proxsuite/common/dense/workspace.hpp" +#include "proxsuite/common/dense/preconditioner/ruiz.hpp" +#include + +namespace proxsuite { +namespace common { +namespace dense { + +template +void +print_setup_header(const Settings& qpsettings, + const Results& qpresults, + const Model& qpmodel, + const bool box_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + const QPSolver solver) +{ + + print_preambule(solver); + + // Print variables and constraints + std::cout << "problem: " << std::noshowpos << std::endl; + std::cout << " variables n = " << qpmodel.dim + << ", equality constraints n_eq = " << qpmodel.n_eq << ",\n" + << " inequality constraints n_in = " << qpmodel.n_in + << std::endl; + + // Print Settings + std::cout << "settings: " << std::endl; + std::cout << " backend = dense," << std::endl; + std::cout << " eps_abs = " << qpsettings.eps_abs + << " eps_rel = " << qpsettings.eps_rel << std::endl; + std::cout << " eps_prim_inf = " << qpsettings.eps_primal_inf + << ", eps_dual_inf = " << qpsettings.eps_dual_inf << "," + << std::endl; + + std::cout << " rho = " << qpresults.info.rho + << ", mu_eq = " << qpresults.info.mu_eq + << ", mu_in = " << qpresults.info.mu_in << "," << std::endl; + std::cout << " max_iter = " << qpsettings.max_iter + << ", max_iter_in = " << qpsettings.max_iter_in << "," << std::endl; + if (box_constraints) { + std::cout << " box constraints: on, " << std::endl; + } else { + std::cout << " box constraints: off, " << std::endl; + } + switch (qpsettings.check_solved_option) { + case CheckSolvedStatus::ITERATION_BASED: { + std::cout << " check_solved_option: iteration based, " + << std::endl + << " frequence_infeasibility_check = " + << qpsettings.frequence_infeasibility_check << std::endl; + break; + } + case CheckSolvedStatus::INTERVAL_BASED: { + std::cout << " check_solved_option: interval based, " + << std::endl + << " check_termination = " + << qpsettings.check_termination << std::endl; + break; + } + } + switch (dense_backend) { + case DenseBackend::PrimalDualLDLT: + std::cout << " dense backend: PrimalDualLDLT, " << std::endl; + break; + case DenseBackend::PrimalLDLT: + std::cout << " dense backend: PrimalLDLT, " << std::endl; + break; + case DenseBackend::Automatic: + break; + } + switch (hessian_type) { + case HessianType::Dense: + std::cout << " problem type: Quadratic Program, " << std::endl; + break; + case HessianType::Zero: + std::cout << " problem type: Linear Program, " << std::endl; + break; + case HessianType::Diagonal: + std::cout + << " problem type: Quadratic Program with diagonal Hessian, " + << std::endl; + break; + } + if (qpsettings.compute_preconditioner) { + std::cout << " scaling: on, " << std::endl; + } else { + std::cout << " scaling: off, " << std::endl; + } + if (qpsettings.compute_timings) { + std::cout << " timings: on, " << std::endl; + } else { + std::cout << " timings: off, " << std::endl; + } + switch (qpsettings.initial_guess) { + case InitialGuessStatus::WARM_START: + std::cout << " initial guess: warm start. \n" << std::endl; + break; + case InitialGuessStatus::NO_INITIAL_GUESS: + std::cout << " initial guess: no initial guess. \n" << std::endl; + break; + case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: + std::cout + << " initial guess: warm start with previous result. \n" + << std::endl; + break; + case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: + std::cout + << " initial guess: cold start with previous result. \n" + << std::endl; + break; + case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: + std::cout + << " initial guess: equality constrained initial guess. \n" + << std::endl; + } + switch (solver) { + case QPSolver::PROXQP: { + break; + } + case QPSolver::OSQP: { + if (qpsettings.adaptive_mu) { + std::cout << " adaptive_mu: on, " << std::endl; + std::cout << " adaptive_mu_interval: " + << qpsettings.adaptive_mu_interval << ", " << std::endl; + std::cout << " adaptive_mu_tolerance: " + << qpsettings.adaptive_mu_tolerance << ". \n" + << std::endl; + } else { + std::cout << " adaptive_mu: off. \n" << std::endl; + } + if (qpsettings.polish) { + std::cout << " polish: on, " << std::endl; + std::cout << " delta: " << qpsettings.delta << ", " + << std::endl; + std::cout << " polish_refine_iter: " + << qpsettings.polish_refine_iter << ". \n" + << std::endl; + } else { + std::cout << " polish: off. \n" << std::endl; + } + break; + } + } +} + +template +void +print_iteration_line( // + Results& qpresults, + const Model& qpmodel, + const bool box_constraints, + preconditioner::RuizEquilibration& ruiz, + const QPSolver solver, + const isize iter) +{ + ruiz.unscale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); + ruiz.unscale_dual_in_place_eq(VectorViewMut{ from_eigen, qpresults.y }); + ruiz.unscale_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.unscale_box_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + { + // EigenAllowAlloc _{}; + qpresults.info.objValue = 0; + for (Eigen::Index j = 0; j < qpmodel.dim; ++j) { + qpresults.info.objValue += + 0.5 * (qpresults.x(j) * qpresults.x(j)) * qpmodel.H(j, j); + qpresults.info.objValue += + qpresults.x(j) * T(qpmodel.H.col(j) + .tail(qpmodel.dim - j - 1) + .dot(qpresults.x.tail(qpmodel.dim - j - 1))); + } + qpresults.info.objValue += (qpmodel.g).dot(qpresults.x); + } + switch (solver) { + case QPSolver::PROXQP: { + std::cout << "\033[1;32m[outer iteration " << iter + 1 << "]\033[0m" + << std::endl; + std::cout << std::scientific << std::setw(2) << std::setprecision(2) + << "| primal residual=" << qpresults.info.pri_res + << " | dual residual=" << qpresults.info.dua_res + << " | duality gap=" << qpresults.info.duality_gap + << " | mu_in=" << qpresults.info.mu_in + << " | rho=" << qpresults.info.rho << std::endl; + break; + } + case QPSolver::OSQP: { + std::cout << "\033[1;32m[iteration " << iter + 1 << "]\033[0m" + << std::endl; + std::cout << std::scientific << std::setw(2) << std::setprecision(2) + << "| primal residual=" << qpresults.info.pri_res + << " | dual residual=" << qpresults.info.dua_res + << " | duality gap=" << qpresults.info.duality_gap + << " | mu_eq=" << qpresults.info.mu_eq + << " | mu_in=" << qpresults.info.mu_in << std::endl; + break; + } + } + ruiz.scale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); + ruiz.scale_dual_in_place_eq(VectorViewMut{ from_eigen, qpresults.y }); + ruiz.scale_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.scale_box_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); + } +} + +template +void +print_solver_statistics( // + const Settings& qpsettings, + const Results& qpresults, + const QPSolver solver) +{ + std::cout << "-------------------SOLVER STATISTICS-------------------" + << std::endl; + switch (solver) { + case QPSolver::PROXQP: { + std::cout << "outer iter: " << qpresults.info.iter_ext << std::endl; + std::cout << "total iter: " << qpresults.info.iter << std::endl; + std::cout << "mu updates: " << qpresults.info.mu_updates << std::endl; + std::cout << "rho updates: " << qpresults.info.rho_updates + << std::endl; + std::cout << "objective: " << qpresults.info.objValue << std::endl; + break; + } + case QPSolver::OSQP: { + std::cout << "total iter: " << qpresults.info.iter << std::endl; + std::cout << "mu updates: " << qpresults.info.mu_updates << std::endl; + std::cout << "objective: " << qpresults.info.objValue << std::endl; + break; + } + } + switch (qpresults.info.status) { + case QPSolverOutput::QPSOLVER_SOLVED: { + std::cout << "status: " + << "Solved" << std::endl; + break; + } + case QPSolverOutput::QPSOLVER_MAX_ITER_REACHED: { + std::cout << "status: " + << "Maximum number of iterations reached" << std::endl; + break; + } + case QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE: { + std::cout << "status: " + << "Primal infeasible" << std::endl; + break; + } + case QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE: { + std::cout << "status: " + << "Dual infeasible" << std::endl; + break; + } + case QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { + std::cout << "status: " + << "Solved closest primal feasible" << std::endl; + break; + } + case QPSolverOutput::QPSOLVER_NOT_RUN: { + std::cout << "status: " + << "Solver not run" << std::endl; + break; + } + } + switch (solver) { + case QPSolver::PROXQP: { + break; + } + case QPSolver::OSQP: { + if (qpsettings.polish == true) { + switch (qpresults.info.status_polish) { + case PolishOutput::POLISH_SUCCEEDED: { + std::cout << "status_polish: " + << "Success" << std::endl; + break; + } + case PolishOutput::POLISH_FAILED: { + std::cout << "status_polish: " + << "Failed" << std::endl; + break; + } + case PolishOutput::POLISH_NO_ACTIVE_SET_FOUND: { + std::cout << "status_polish: " + << "No active set found" << std::endl; + break; + } + case PolishOutput::POLISH_NOT_RUN: { + std::cout << "status_polish: " + << "Not" << std::endl; + break; + } + } + } + break; + } + } + if (qpsettings.compute_timings) + std::cout << "run time [μs]: " << qpresults.info.solve_time << std::endl; + std::cout << "--------------------------------------------------------" + << std::endl; +} + +} // namespace dense +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_DENSE_PRINTS_HPP */ diff --git a/include/proxsuite/proxqp/dense/utils.hpp b/include/proxsuite/common/dense/utils.hpp similarity index 65% rename from include/proxsuite/proxqp/dense/utils.hpp rename to include/proxsuite/common/dense/utils.hpp index 6d9c62857..a6b66291e 100644 --- a/include/proxsuite/proxqp/dense/utils.hpp +++ b/include/proxsuite/common/dense/utils.hpp @@ -4,145 +4,29 @@ /** * @file utils.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_UTILS_HPP -#define PROXSUITE_PROXQP_DENSE_UTILS_HPP +#ifndef PROXSUITE_COMMON_DENSE_UTILS_HPP +#define PROXSUITE_COMMON_DENSE_UTILS_HPP #include -#include #include #include #include "proxsuite/helpers/common.hpp" -#include "proxsuite/proxqp/dense/views.hpp" -#include "proxsuite/proxqp/dense/workspace.hpp" -#include -#include -#include -#include -#include +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/dense/workspace.hpp" +#include +#include +#include +#include // #include // #include namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { -template -void -print_setup_header(const Settings& settings, - const Results& results, - const Model& model, - const bool box_constraints, - const DenseBackend& dense_backend, - const HessianType& hessian_type) -{ - - proxsuite::proxqp::print_preambule(); - - // Print variables and constraints - std::cout << "problem: " << std::noshowpos << std::endl; - std::cout << " variables n = " << model.dim - << ", equality constraints n_eq = " << model.n_eq << ",\n" - << " inequality constraints n_in = " << model.n_in - << std::endl; - - // Print Settings - std::cout << "settings: " << std::endl; - std::cout << " backend = dense," << std::endl; - std::cout << " eps_abs = " << settings.eps_abs - << " eps_rel = " << settings.eps_rel << std::endl; - std::cout << " eps_prim_inf = " << settings.eps_primal_inf - << ", eps_dual_inf = " << settings.eps_dual_inf << "," << std::endl; - - std::cout << " rho = " << results.info.rho - << ", mu_eq = " << results.info.mu_eq - << ", mu_in = " << results.info.mu_in << "," << std::endl; - std::cout << " max_iter = " << settings.max_iter - << ", max_iter_in = " << settings.max_iter_in << "," << std::endl; - if (box_constraints) { - std::cout << " box constraints: on, " << std::endl; - } else { - std::cout << " box constraints: off, " << std::endl; - } - switch (dense_backend) { - case DenseBackend::PrimalDualLDLT: - std::cout << " dense backend: PrimalDualLDLT, " << std::endl; - break; - case DenseBackend::PrimalLDLT: - std::cout << " dense backend: PrimalLDLT, " << std::endl; - break; - case DenseBackend::Automatic: - break; - } - switch (hessian_type) { - case HessianType::Dense: - std::cout << " problem type: Quadratic Program, " << std::endl; - break; - case HessianType::Zero: - std::cout << " problem type: Linear Program, " << std::endl; - break; - case HessianType::Diagonal: - std::cout - << " problem type: Quadratic Program with diagonal Hessian, " - << std::endl; - break; - } - if (settings.compute_preconditioner) { - std::cout << " scaling: on, " << std::endl; - } else { - std::cout << " scaling: off, " << std::endl; - } - if (settings.compute_timings) { - std::cout << " timings: on, " << std::endl; - } else { - std::cout << " timings: off, " << std::endl; - } - switch (settings.initial_guess) { - case InitialGuessStatus::WARM_START: - std::cout << " initial guess: warm start. \n" << std::endl; - break; - case InitialGuessStatus::NO_INITIAL_GUESS: - std::cout << " initial guess: no initial guess. \n" << std::endl; - break; - case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: - std::cout - << " initial guess: warm start with previous result. \n" - << std::endl; - break; - case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: - std::cout - << " initial guess: cold start with previous result. \n" - << std::endl; - break; - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: - std::cout - << " initial guess: equality constrained initial guess. \n" - << std::endl; - } -} - -/*! - * Save a matrix into a CSV format. Used for debug purposes. - * - * @param filename filename name for the CSV. - * @param mat matrix to save into CSV format. - */ -template -void -save_data(const std::string& filename, const ::Eigen::MatrixBase& mat) -{ - // https://eigen.tuxfamily.org/dox/structEigen_1_1IOFormat.html - const static Eigen::IOFormat CSVFormat( - Eigen::FullPrecision, Eigen::DontAlignCols, ", ", "\n"); - - std::ofstream file(filename); - if (file.is_open()) { - file << mat.format(CSVFormat); - file.close(); - } -} - /*! * Derives the global primal residual of the QP problem. * @@ -239,7 +123,7 @@ global_primal_residual(const Model& qpmodel, primal_feasibility_lhs = std::max(primal_feasibility_eq_lhs, primal_feasibility_in_lhs); if (qpsettings.primal_infeasibility_solving && - qpresults.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { + qpresults.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { qpwork.rhs.head(qpmodel.dim).noalias() = qpmodel.A.transpose() * qpresults.se; qpwork.rhs.head(qpmodel.dim).noalias() += @@ -586,8 +470,268 @@ global_dual_residual(Results& qpresults, } } +/*! + * Compute timings at the end of qp_solve function. + * + * @param qpwork solver workspace. + * @param qpresults solver results. + */ +template +void +compute_timings(Results& qpresults, Workspace& qpwork) +{ + qpresults.info.solve_time = qpwork.timer.elapsed().user; // in microseconds + qpresults.info.run_time = + qpresults.info.solve_time + qpresults.info.setup_time; +} + +/*! + * Computes the objective function. + * + * @param qpmodel solver model. + * @param qpresults solver results. + */ +template +void +compute_objective(Results& qpresults, const Model& qpmodel) +{ + qpresults.info.objValue = 0; + for (Eigen::Index j = 0; j < qpmodel.dim; ++j) { + qpresults.info.objValue += + 0.5 * (qpresults.x(j) * qpresults.x(j)) * qpmodel.H(j, j); + qpresults.info.objValue += + qpresults.x(j) * T(qpmodel.H.col(j) + .tail(qpmodel.dim - j - 1) + .dot(qpresults.x.tail(qpmodel.dim - j - 1))); + } + qpresults.info.objValue += (qpmodel.g).dot(qpresults.x); +} + +/*! + * Computes the unscaled primal and dual residuals. + * + * @param qpwork solver workspace. + * @param qpresults solver results. + * @param ruiz ruiz preconditioner. + */ +template +void +compute_residuals(const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const HessianType& hessian_type, + preconditioner::RuizEquilibration& ruiz, + T& primal_feasibility_lhs, + T& primal_feasibility_eq_rhs_0, + T& primal_feasibility_in_rhs_0, + T& primal_feasibility_eq_lhs, + T& primal_feasibility_in_lhs, + T& dual_feasibility_lhs, + T& dual_feasibility_rhs_0, + T& dual_feasibility_rhs_1, + T& dual_feasibility_rhs_3, + T& rhs_duality_gap, + T& duality_gap) +{ + // PERF: fuse matrix product computations in global_{primal, dual}_residual + global_primal_residual(qpmodel, + qpresults, + qpsettings, + qpwork, + ruiz, + box_constraints, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs); + + global_dual_residual(qpresults, + qpwork, + qpmodel, + box_constraints, + ruiz, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap, + hessian_type); + + qpresults.info.pri_res = primal_feasibility_lhs; + qpresults.info.dua_res = dual_feasibility_lhs; + qpresults.info.duality_gap = duality_gap; +} + +/*! + * Checks if the problem if solved. + * + * @param qpwork solver workspace. + * @param qpresults solver results. + */ +template +bool +is_solved(const Settings& qpsettings, + Results& qpresults, + const Workspace& qpwork, + T scaled_eps, + T primal_feasibility_lhs, + T primal_feasibility_eq_rhs_0, + T primal_feasibility_in_rhs_0, + T dual_feasibility_lhs, + T dual_feasibility_rhs_0, + T dual_feasibility_rhs_1, + T dual_feasibility_rhs_3, + T rhs_duality_gap) +{ + bool is_solved = false; + + T rhs_pri(scaled_eps); + if (qpsettings.eps_rel != 0) { + rhs_pri += qpsettings.eps_rel * std::max(primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0); + } + bool is_primal_feasible = primal_feasibility_lhs <= rhs_pri; + + T rhs_dua(qpsettings.eps_abs); + if (qpsettings.eps_rel != 0) { + rhs_dua += + qpsettings.eps_rel * + std::max(std::max(dual_feasibility_rhs_3, dual_feasibility_rhs_0), + std::max(dual_feasibility_rhs_1, qpwork.dual_feasibility_rhs_2)); + } + bool is_dual_feasible = dual_feasibility_lhs <= rhs_dua; + + if (is_primal_feasible && is_dual_feasible) { + if (qpsettings.check_duality_gap) { + if (std::fabs(qpresults.info.duality_gap) <= + qpsettings.eps_duality_gap_abs + + qpsettings.eps_duality_gap_rel * rhs_duality_gap) { + if (qpsettings.primal_infeasibility_solving && + qpresults.info.status == + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { + qpresults.info.status = + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + } else { + qpresults.info.status = QPSolverOutput::QPSOLVER_SOLVED; + } + is_solved = true; + } + } else { + qpresults.info.status = QPSolverOutput::QPSOLVER_SOLVED; + is_solved = true; + } + } + + return is_solved; +} + +/*! + * Checks if the problem if solved or closest solved. + * Used at the end of the loop to update the solver status + * in case of last iter. + * + * @param qpwork solver workspace. + * @param qpresults solver results. + */ +template +void +is_solved_or_closest_solved(const Settings& qpsettings, + Results& qpresults, + const Workspace& qpwork, + T scaled_eps, + T primal_feasibility_lhs_new, + T primal_feasibility_eq_rhs_0, + T primal_feasibility_in_rhs_0, + T dual_feasibility_lhs_new, + T dual_feasibility_rhs_0, + T dual_feasibility_rhs_1, + T dual_feasibility_rhs_3, + T rhs_duality_gap) +{ + bool is_primal_feasible = + primal_feasibility_lhs_new <= + (scaled_eps + qpsettings.eps_rel * std::max(primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0)); + + if (is_primal_feasible) { + bool is_dual_feasible = + dual_feasibility_lhs_new <= + (qpsettings.eps_abs + + qpsettings.eps_rel * + std::max( + std::max(dual_feasibility_rhs_3, dual_feasibility_rhs_0), + std::max(dual_feasibility_rhs_1, qpwork.dual_feasibility_rhs_2))); + + if (is_dual_feasible) { + if (qpsettings.check_duality_gap) { + if (std::fabs(qpresults.info.duality_gap) <= + qpsettings.eps_duality_gap_abs + + qpsettings.eps_duality_gap_rel * rhs_duality_gap) { + if (qpsettings.primal_infeasibility_solving && + qpresults.info.status == + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { + qpresults.info.status = + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + } else { + qpresults.info.status = QPSolverOutput::QPSOLVER_SOLVED; + } + } + } else { + if (qpsettings.primal_infeasibility_solving && + qpresults.info.status == + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { + qpresults.info.status = + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + } else { + qpresults.info.status = QPSolverOutput::QPSOLVER_SOLVED; + } + } + } + } +} + +/*! + * Unscales solver at the end of function qp_solve. + * + * @param qpwork solver workspace. + * @param qpresults solver results. + */ +template +void +unscale_solver(const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + const bool box_constraints, + preconditioner::RuizEquilibration& ruiz) +{ + ruiz.unscale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); + ruiz.unscale_dual_in_place_eq(VectorViewMut{ from_eigen, qpresults.y }); + ruiz.unscale_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.unscale_box_dual_in_place_in( + VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); + } + if (qpsettings.primal_infeasibility_solving && + qpresults.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { + ruiz.unscale_primal_residual_in_place_eq( + VectorViewMut{ from_eigen, qpresults.se }); + ruiz.unscale_primal_residual_in_place_in( + VectorViewMut{ from_eigen, qpresults.si.head(qpmodel.n_in) }); + if (box_constraints) { + ruiz.unscale_box_primal_residual_in_place_in( + VectorViewMut{ from_eigen, qpresults.si.tail(qpmodel.dim) }); + } + } +} + } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_UTILS_HPP */ +#endif /* end of include guard \ + PROXSUITE_COMMON_DENSE_UTILS_HPP */ diff --git a/include/proxsuite/proxqp/dense/views.hpp b/include/proxsuite/common/dense/views.hpp similarity index 98% rename from include/proxsuite/proxqp/dense/views.hpp rename to include/proxsuite/common/dense/views.hpp index c43c8cae9..ba6f7be1c 100644 --- a/include/proxsuite/proxqp/dense/views.hpp +++ b/include/proxsuite/common/dense/views.hpp @@ -4,8 +4,8 @@ /** * @file views.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_VIEWS_HPP -#define PROXSUITE_PROXQP_DENSE_VIEWS_HPP +#ifndef PROXSUITE_COMMON_DENSE_VIEWS_HPP +#define PROXSUITE_COMMON_DENSE_VIEWS_HPP #include #include @@ -14,12 +14,12 @@ #include #define LDLT_CONCEPT(...) \ - VEG_CONCEPT_MACRO(::proxsuite::proxqp::concepts, __VA_ARGS__) + VEG_CONCEPT_MACRO(::proxsuite::common::concepts, __VA_ARGS__) #define LDLT_CHECK_CONCEPT(...) \ - VEG_CHECK_CONCEPT_MACRO(::proxqp::concepts, __VA_ARGS__) + VEG_CHECK_CONCEPT_MACRO(::common::concepts, __VA_ARGS__) namespace proxsuite { -namespace proxqp { +namespace common { using usize = decltype(sizeof(0)); namespace detail { @@ -35,7 +35,7 @@ struct FnInfoRet_> } // namespace detail #define LDLT_IMPL_GET_PARAM(Fn, Idx) \ - typename ::proxsuite::proxqp::detail::FnInfo< \ + typename ::proxsuite::common::detail::FnInfo< \ decltype Fn /* NOLINT */>::template Arg<(Idx)>, #define LDLT_IMPL_GET_PARAMS_0(NParams, ...) \ @@ -52,9 +52,9 @@ struct FnInfoRet_> #define LDLT_EXPLICIT_TPL_DEF(NParams, ...) \ template auto __VA_ARGS__( \ LDLT_IMPL_GET_PARAMS(NParams, __VA_ARGS__) \ - typename ::proxsuite::proxqp::detail::FnInfo< \ + typename ::proxsuite::common::detail::FnInfo< \ decltype(__VA_ARGS__)>::template Arg<(NParams) - 1>) -> \ - typename ::proxsuite::proxqp::detail::FnInfo::Ret + typename ::proxsuite::common::detail::FnInfo::Ret #define LDLT_EXPLICIT_TPL_DECL(NParams, ...) \ extern LDLT_EXPLICIT_TPL_DEF(NParams, __VA_ARGS__) @@ -894,7 +894,7 @@ struct MatrixView return trans().col(r); } VEG_INLINE auto trans() const noexcept - -> MatrixView + -> MatrixView { return { from_ptr_rows_cols_stride, data, cols, rows, outer_stride, @@ -1000,7 +1000,7 @@ struct MatrixViewMut return trans().col(r); } VEG_INLINE auto trans() const noexcept - -> MatrixViewMut + -> MatrixViewMut { return { from_ptr_rows_cols_stride, data, cols, rows, outer_stride, @@ -1300,11 +1300,11 @@ noalias_mul_sub_tr_lo(MatrixViewMut out, } } // namespace detail -} // namespace proxqp +} // namespace common } // namespace proxsuite namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { @@ -1457,7 +1457,7 @@ VEG_NIEBLOID(sqrt); VEG_NIEBLOID(pow); VEG_NIEBLOID(infty_norm); } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_VIEWS_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_VIEWS_HPP */ diff --git a/include/proxsuite/proxqp/dense/workspace.hpp b/include/proxsuite/common/dense/workspace.hpp similarity index 65% rename from include/proxsuite/proxqp/dense/workspace.hpp rename to include/proxsuite/common/dense/workspace.hpp index 2a4d77b61..e7111315f 100644 --- a/include/proxsuite/proxqp/dense/workspace.hpp +++ b/include/proxsuite/common/dense/workspace.hpp @@ -4,17 +4,20 @@ /** * @file workspace.hpp */ -#ifndef PROXSUITE_PROXQP_DENSE_WORKSPACE_HPP -#define PROXSUITE_PROXQP_DENSE_WORKSPACE_HPP +#ifndef PROXSUITE_COMMON_DENSE_WORKSPACE_HPP +#define PROXSUITE_COMMON_DENSE_WORKSPACE_HPP #include #include -#include #include -#include +#include +#include +#include + namespace proxsuite { -namespace proxqp { +namespace common { namespace dense { + /// /// @brief This class defines the workspace of the dense solver. /// @@ -94,7 +97,24 @@ struct Workspace bool proximal_parameter_update; bool is_initialized; - sparse::isize n_c; // final number of active inequalities + isize n_c; // final number of active inequalities + + // OSQP + Vec x_tilde; + Vec nu_eq; + Vec nu_in; + Vec zeta_tilde_eq; + Vec zeta_tilde_in; + Vec zeta_eq_next; + Vec zeta_in_next; + + Mat C_scaled_low; + Mat C_scaled_upper; + Timer timer_polish; + + proxsuite::linalg::dense::Ldlt ldl_polish{}; + proxsuite::linalg::veg::Vec ldl_polish_stack; + /*! * Default constructor. * @param dim primal variable dimension. @@ -125,6 +145,11 @@ struct Workspace , refactorize(false) , proximal_parameter_update(false) , is_initialized(false) + , x_tilde(dim) + , nu_eq(n_eq) + , zeta_tilde_eq(n_eq) + , zeta_eq_next(n_eq) + , ldl_polish{} { if (box_constraints) { @@ -166,6 +191,33 @@ struct Workspace n_in + dim)) // TODO optimize here .alloc_req()); + + ldl_polish.reserve_uninit(dim + n_eq + n_in + dim); + ldl_polish_stack.resize_for_overwrite( + proxsuite::linalg::veg::dynstack::StackReq( + // optimize here + proxsuite::linalg::dense::Ldlt::factorize_req(dim + n_eq + + n_in + dim) | + + (proxsuite::linalg::dense::temp_vec_req( + proxsuite::linalg::veg::Tag{}, n_eq + n_in + dim) & + proxsuite::linalg::veg::dynstack::StackReq{ + isize{ sizeof(isize) } * (n_eq + n_in + dim), + alignof(isize) } & + proxsuite::linalg::dense::Ldlt::diagonal_update_req( + dim + n_eq + n_in + dim, n_eq + n_in + dim)) | + + (proxsuite::linalg::dense::temp_mat_req( + proxsuite::linalg::veg::Tag{}, + dim + n_eq + n_in + dim, + n_in + dim) & + proxsuite::linalg::dense::Ldlt::insert_block_at_req( + dim + n_eq + n_in + dim, n_in + dim)) | + + proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim + n_eq + + n_in + dim)) + // TODO optimize here + .alloc_req()); break; case DenseBackend::PrimalLDLT: kkt.resize(dim, dim); @@ -193,6 +245,31 @@ struct Workspace proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim)) .alloc_req()); + + ldl_polish.reserve_uninit(dim); + ldl_polish_stack.resize_for_overwrite( + proxsuite::linalg::veg::dynstack::StackReq( + + proxsuite::linalg::dense::Ldlt::factorize_req(dim) | + // check simplification possible + (proxsuite::linalg::dense::temp_vec_req( + proxsuite::linalg::veg::Tag{}, n_eq + n_in + dim) & + proxsuite::linalg::veg::dynstack::StackReq{ + isize{ sizeof(isize) } * (n_eq + n_in + dim), + alignof(isize) } & + proxsuite::linalg::dense::Ldlt::diagonal_update_req( + dim + n_eq + n_in + dim, n_eq + n_in + dim)) | + + (proxsuite::linalg::dense::temp_mat_req( + proxsuite::linalg::veg::Tag{}, + dim + n_eq + n_in + dim, + n_in + dim) & + proxsuite::linalg::dense::Ldlt::insert_block_at_req( + dim + n_eq + n_in + dim, n_in + dim)) | + // end check + proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim)) + + .alloc_req()); break; case DenseBackend::Automatic: break; @@ -215,6 +292,9 @@ struct Workspace primal_residual_in_scaled_low_plus_alphaCdx.resize(dim + n_in); Cdx.resize(n_in + dim); alphas.reserve(2 * n_in + 2 * dim); + nu_in.resize(n_in + dim); + zeta_tilde_in.resize(n_in + dim); + zeta_in_next.resize(n_in + dim); } else { z_prev.resize(n_in); @@ -244,6 +324,30 @@ struct Workspace n_in)) // end todo optimize here .alloc_req()); + + ldl_polish.reserve_uninit(dim + n_eq + n_in); + ldl_polish_stack.resize_for_overwrite( + proxsuite::linalg::veg::dynstack::StackReq( + // todo optimize here + proxsuite::linalg::dense::Ldlt::factorize_req(dim + n_eq + + n_in) | + + (proxsuite::linalg::dense::temp_vec_req( + proxsuite::linalg::veg::Tag{}, n_eq + n_in) & + proxsuite::linalg::veg::dynstack::StackReq{ + isize{ sizeof(isize) } * (n_eq + n_in), alignof(isize) } & + proxsuite::linalg::dense::Ldlt::diagonal_update_req( + dim + n_eq + n_in, n_eq + n_in)) | + + (proxsuite::linalg::dense::temp_mat_req( + proxsuite::linalg::veg::Tag{}, dim + n_eq + n_in, n_in) & + proxsuite::linalg::dense::Ldlt::insert_block_at_req( + dim + n_eq + n_in, n_in)) | + + proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim + n_eq + + n_in)) + // end todo optimize here + .alloc_req()); break; case DenseBackend::PrimalLDLT: kkt.resize(dim, dim); @@ -267,6 +371,27 @@ struct Workspace proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim)) .alloc_req()); + + ldl_polish.reserve_uninit(dim); + ldl_polish_stack.resize_for_overwrite( + proxsuite::linalg::veg::dynstack::StackReq( + + proxsuite::linalg::dense::Ldlt::factorize_req(dim) | + // check if it can be more simplified + (proxsuite::linalg::dense::temp_vec_req( + proxsuite::linalg::veg::Tag{}, n_eq + n_in) & + proxsuite::linalg::veg::dynstack::StackReq{ + isize{ sizeof(isize) } * (n_eq + n_in), alignof(isize) } & + proxsuite::linalg::dense::Ldlt::diagonal_update_req( + dim + n_eq + n_in, n_eq + n_in)) | + (proxsuite::linalg::dense::temp_mat_req( + proxsuite::linalg::veg::Tag{}, dim + n_eq + n_in, n_in) & + proxsuite::linalg::dense::Ldlt::insert_block_at_req( + dim + n_eq + n_in, n_in)) | + // end check + proxsuite::linalg::dense::Ldlt::solve_in_place_req(dim)) + + .alloc_req()); break; case DenseBackend::Automatic: break; @@ -290,6 +415,9 @@ struct Workspace primal_residual_in_scaled_low_plus_alphaCdx.resize(n_in); Cdx.resize(n_in); alphas.reserve(2 * n_in); + nu_in.resize(n_in); + zeta_tilde_in.resize(n_in); + zeta_in_next.resize(n_in); } H_scaled.setZero(); @@ -323,6 +451,14 @@ struct Workspace primal_residual_in_scaled_low_plus_alphaCdx.setZero(); CTz.setZero(); n_c = 0; + + x_tilde.setZero(); + nu_eq.setZero(); + nu_in.setZero(); + zeta_tilde_eq.setZero(); + zeta_tilde_in.setZero(); + zeta_eq_next.setZero(); + zeta_in_next.setZero(); } /*! * Clean-ups solver's workspace. @@ -374,10 +510,18 @@ struct Workspace proximal_parameter_update = false; is_initialized = false; n_c = 0; + + x_tilde.setZero(); + nu_eq.setZero(); + nu_in.setZero(); + zeta_tilde_eq.setZero(); + zeta_tilde_in.setZero(); + zeta_eq_next.setZero(); + zeta_in_next.setZero(); } }; } // namespace dense -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_WORKSPACE_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_DENSE_WORKSPACE_HPP */ diff --git a/include/proxsuite/common/dense/wrapper.hpp b/include/proxsuite/common/dense/wrapper.hpp new file mode 100644 index 000000000..033acc285 --- /dev/null +++ b/include/proxsuite/common/dense/wrapper.hpp @@ -0,0 +1,1015 @@ +// +// Copyright (c) 2022-2025 INRIA +// +/** + * @file wrapper.hpp + */ + +#ifndef PROXSUITE_COMMON_DENSE_WRAPPER_HPP +#define PROXSUITE_COMMON_DENSE_WRAPPER_HPP + +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/dense/model.hpp" +#include "proxsuite/common/dense/workspace.hpp" +#include +#include +#include + +namespace proxsuite { +namespace common { +namespace dense { + +///// Dense backend choice +template +DenseBackend +dense_backend_choice(DenseBackend _dense_backend, + isize dim, + isize n_eq, + isize n_in, + bool box_constraints) +{ + if (_dense_backend == DenseBackend::Automatic) { + isize n_constraints(n_in); + if (box_constraints) { + n_constraints += dim; + } + T threshold(1.5); + T frequence(0.2); + T PrimalDualLDLTCost = + 0.5 * std::pow(T(n_eq) / T(dim), 2) + + 0.17 * (std::pow(T(n_eq) / T(dim), 3) + + std::pow(T(n_constraints) / T(dim), 3)) + + frequence * std::pow(T(n_eq + n_constraints) / T(dim), 2) / T(dim); + T PrimalLDLTCost = + threshold * + ((0.5 * T(n_eq) + T(n_constraints)) / T(dim) + frequence / T(dim)); + bool choice = PrimalDualLDLTCost > PrimalLDLTCost; + if (choice) { + return DenseBackend::PrimalLDLT; + } else { + return DenseBackend::PrimalDualLDLT; + } + } else { + return _dense_backend; + } +} + +/// +/// @brief This class defines the base API of the solvers with dense backend. +/// +/*! + * Base CRTP class for QP solvers with dense backend. + */ +template +struct QPBase +{ +private: + Derived& derived() { return static_cast(*this); } + const Derived& derived() const { return static_cast(*this); } + +protected: + DenseBackend dense_backend; + bool box_constraints; + HessianType hessian_type; + +public: + Results results; + Settings settings; + Model model; + Workspace work; + preconditioner::RuizEquilibration ruiz; + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QPBase(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + HessianType _hessian_type, + DenseBackend _dense_backend) + : dense_backend(dense_backend_choice(_dense_backend, + _dim, + _n_eq, + _n_in, + _box_constraints)) + , box_constraints(_box_constraints) + , hessian_type(_hessian_type) + , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, _box_constraints) + , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, + _n_eq, + _n_in, + _box_constraints }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QPBase(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + DenseBackend _dense_backend, + HessianType _hessian_type) + : dense_backend(dense_backend_choice(_dense_backend, + _dim, + _n_eq, + _n_in, + _box_constraints)) + , box_constraints(_box_constraints) + , hessian_type(_hessian_type) + , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, _box_constraints) + , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, + _n_eq, + _n_in, + _box_constraints }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + */ + QPBase(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + HessianType _hessian_type) + : dense_backend(dense_backend_choice(DenseBackend::Automatic, + _dim, + _n_eq, + _n_in, + _box_constraints)) + , box_constraints(_box_constraints) + , hessian_type(_hessian_type) + , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, _box_constraints) + , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, + _n_eq, + _n_in, + _box_constraints }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QPBase(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + DenseBackend _dense_backend) + : dense_backend(dense_backend_choice(_dense_backend, + _dim, + _n_eq, + _n_in, + _box_constraints)) + , box_constraints(_box_constraints) + , hessian_type(HessianType::Dense) + , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, _box_constraints) + , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, + _n_eq, + _n_in, + _box_constraints }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _box_constraints specify that there are (or not) box constraints. + */ + QPBase(isize _dim, isize _n_eq, isize _n_in, bool _box_constraints) + : dense_backend(dense_backend_choice(DenseBackend::Automatic, + _dim, + _n_eq, + _n_in, + _box_constraints)) + , box_constraints(_box_constraints) + , hessian_type(HessianType::Dense) + , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, _box_constraints) + , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, + _n_eq, + _n_in, + _box_constraints }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type specify that there are (or not) box constraints. + */ + QPBase(isize _dim, isize _n_eq, isize _n_in, HessianType _hessian_type) + : dense_backend(dense_backend_choice(DenseBackend::Automatic, + _dim, + _n_eq, + _n_in, + false)) + , box_constraints(false) + , hessian_type(_hessian_type) + , results(_dim, _n_eq, _n_in, false, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, false) + , work(_dim, _n_eq, _n_in, false, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, _n_eq, _n_in, false }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + */ + QPBase(isize _dim, isize _n_eq, isize _n_in) + : dense_backend(dense_backend_choice(DenseBackend::Automatic, + _dim, + _n_eq, + _n_in, + false)) + , box_constraints(false) + , hessian_type(HessianType::Dense) + , results(_dim, _n_eq, _n_in, false, dense_backend) + , settings(dense_backend) + , model(_dim, _n_eq, _n_in, false) + , work(_dim, _n_eq, _n_in, false, dense_backend) + , ruiz(preconditioner::RuizEquilibration{ _dim, _n_eq, _n_in, false }) + { + work.timer.stop(); + derived().init_derived_settings(); + derived().init_derived_results(); + } + + // Accessors + bool is_box_constrained() const { return box_constraints; }; + DenseBackend which_dense_backend() const { return dense_backend; }; + HessianType which_hessian_type() const { return hessian_type; }; + + /*! + * Setups the QP model (with dense matrix format) and equilibrates it if + * specified by the user. + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param compute_preconditioner boolean parameter for executing or not the + * preconditioner. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + */ + void init(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + bool compute_preconditioner = true, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional manual_minimal_H_eigenvalue = nullopt) + { + PROXSUITE_THROW_PRETTY( + box_constraints == true, + std::invalid_argument, + "wrong model setup: the QP object is designed with box " + "constraints, but is initialized without lower or upper box " + "inequalities."); + // dense case + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + settings.compute_preconditioner = compute_preconditioner; + // check the model is valid + if (g != nullopt && g.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + g.value().size(), + model.dim, + "the dimension wrt the primal variable x variable for initializing g " + "is not valid."); + } else { + g.reset(); + } + if (b != nullopt && b.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + b.value().size(), + model.n_eq, + "the dimension wrt equality constrained variables for initializing b " + "is not valid."); + } else { + b.reset(); + } + if (u != nullopt && u.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + u.value().size(), + model.n_in, + "the dimension wrt inequality constrained variables for initializing u " + "is not valid."); + } else { + u.reset(); + } + if (l != nullopt && l.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + l.value().size(), + model.n_in, + "the dimension wrt inequality constrained variables for initializing l " + "is not valid."); + } else { + l.reset(); + } + if (H != nullopt && H.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().rows(), + model.dim, + "the row dimension for initializing H is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().cols(), + model.dim, + "the column dimension for initializing H is not valid."); + } else { + H.reset(); + } + if (A != nullopt && A.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().rows(), + model.n_eq, + "the row dimension for initializing A is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().cols(), + model.dim, + "the column dimension for initializing A is not valid."); + } else { + A.reset(); + } + if (C != nullopt && C.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().rows(), + model.n_in, + "the row dimension for initializing C is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().cols(), + model.dim, + "the column dimension for initializing C is not valid."); + } else { + C.reset(); + } + if (settings.initial_guess == + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { + work.refactorize = + true; // necessary for the first solve (then refactorize only if there + // is an update of the matrices) + } else { + work.refactorize = false; + } + work.proximal_parameter_update = false; + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + PreconditionerStatus preconditioner_status; + if (compute_preconditioner) { + preconditioner_status = PreconditionerStatus::EXECUTE; + } else { + preconditioner_status = PreconditionerStatus::IDENTITY; + } + common::dense::update_proximal_parameters( + settings, results, work, rho, mu_eq, mu_in); + common::dense::update_default_rho_with_minimal_Hessian_eigen_value( + manual_minimal_H_eigenvalue, results, settings); + typedef optional> optional_VecRef; + common::dense::setup(H, + g, + A, + b, + C, + l, + u, + optional_VecRef(nullopt), + optional_VecRef(nullopt), + settings, + model, + work, + results, + box_constraints, + ruiz, + preconditioner_status, + hessian_type); + work.is_initialized = true; + if (settings.compute_timings) { + results.info.setup_time = work.timer.elapsed().user; // in microseconds + } + }; + + /*! + * Setups the QP model (with dense matrix format) and equilibrates it if + * specified by the user. + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param l_box lower box inequality constraint vector input defining the QP + * model. + * @param u_box uppper box inequality constraint vector input defining the QP + * model. + * @param compute_preconditioner boolean parameter for executing or not the + * preconditioner. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + */ + void init(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + bool compute_preconditioner = true, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional manual_minimal_H_eigenvalue = nullopt) + { + + // dense case + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + settings.compute_preconditioner = compute_preconditioner; + PROXSUITE_THROW_PRETTY( + box_constraints == false && (l_box != nullopt || u_box != nullopt), + std::invalid_argument, + "wrong model setup: the QP object is designed without box " + "constraints, but is initialized with lower or upper box inequalities."); + if (l_box != nullopt && l_box.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE(l_box.value().size(), + model.dim, + "the dimension wrt the primal variable x " + "variable for initializing l_box " + "is not valid."); + } else { + l_box.reset(); + } + if (u_box != nullopt && u_box.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE(u_box.value().size(), + model.dim, + "the dimension wrt the primal variable x " + "variable for initializing u_box " + "is not valid."); + } else { + l_box.reset(); + } + // check the model is valid + if (g != nullopt && g.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + g.value().size(), + model.dim, + "the dimension wrt the primal variable x variable for initializing g " + "is not valid."); + } else { + g.reset(); + } + if (b != nullopt && b.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + b.value().size(), + model.n_eq, + "the dimension wrt equality constrained variables for initializing b " + "is not valid."); + } else { + b.reset(); + } + if (u != nullopt && u.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + u.value().size(), + model.n_in, + "the dimension wrt inequality constrained variables for initializing u " + "is not valid."); + } else { + u.reset(); + } + if (u_box != nullopt && u_box.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + u_box.value().size(), + model.dim, + "the dimension wrt box inequality constrained variables for " + "initializing u_box " + "is not valid."); + } else { + u_box.reset(); + } + if (l != nullopt && l.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + l.value().size(), + model.n_in, + "the dimension wrt inequality constrained variables for initializing l " + "is not valid."); + } else { + l.reset(); + } + if (l_box != nullopt && l_box.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + l_box.value().size(), + model.dim, + "the dimension wrt box inequality constrained variables for " + "initializing l_box " + "is not valid."); + } else { + l_box.reset(); + } + if (H != nullopt && H.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().rows(), + model.dim, + "the row dimension for initializing H is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + H.value().cols(), + model.dim, + "the column dimension for initializing H is not valid."); + } else { + H.reset(); + } + if (A != nullopt && A.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().rows(), + model.n_eq, + "the row dimension for initializing A is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + A.value().cols(), + model.dim, + "the column dimension for initializing A is not valid."); + } else { + A.reset(); + } + if (C != nullopt && C.value().size() != 0) { + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().rows(), + model.n_in, + "the row dimension for initializing C is not valid."); + PROXSUITE_CHECK_ARGUMENT_SIZE( + C.value().cols(), + model.dim, + "the column dimension for initializing C is not valid."); + } else { + C.reset(); + } + if (settings.initial_guess == + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { + work.refactorize = + true; // necessary for the first solve (then refactorize only if there + // is an update of the matrices) + } else { + work.refactorize = false; + } + work.proximal_parameter_update = false; + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + PreconditionerStatus preconditioner_status; + if (compute_preconditioner) { + preconditioner_status = PreconditionerStatus::EXECUTE; + } else { + preconditioner_status = PreconditionerStatus::IDENTITY; + } + common::dense::update_proximal_parameters( + settings, results, work, rho, mu_eq, mu_in); + common::dense::update_default_rho_with_minimal_Hessian_eigen_value( + manual_minimal_H_eigenvalue, results, settings); + common::dense::setup(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + settings, + model, + work, + results, + box_constraints, + ruiz, + preconditioner_status, + hessian_type); + work.is_initialized = true; + if (settings.compute_timings) { + results.info.setup_time = work.timer.elapsed().user; // in microseconds + } + }; + + /*! + * Updates the QP model (with dense matrix format) and re-equilibrates it if + * specified by the user. + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param update_preconditioner bool parameter for updating or not the + * preconditioner and the associated scaled model. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + * @note The init method should be called before update. If it has not been + * done before, init is called depending on the is_initialized flag. + */ + void update(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + bool update_preconditioner = false, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional manual_minimal_H_eigenvalue = nullopt) + { + PROXSUITE_THROW_PRETTY( + box_constraints == true, + std::invalid_argument, + "wrong model setup: the QP object is designed without box " + "constraints, but the update does not include lower or upper box " + "inequalities."); + settings.update_preconditioner = update_preconditioner; + if (!work.is_initialized) { + init(H, g, A, b, C, l, u, update_preconditioner, rho, mu_eq, mu_in); + return; + } + // dense case + work.refactorize = false; + work.proximal_parameter_update = false; + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + PreconditionerStatus preconditioner_status; + if (update_preconditioner) { + preconditioner_status = PreconditionerStatus::EXECUTE; + } else { + preconditioner_status = PreconditionerStatus::KEEP; + } + const bool matrix_update = + !(H == nullopt && g == nullopt && A == nullopt && b == nullopt && + C == nullopt && u == nullopt && l == nullopt); + if (matrix_update) { + typedef optional> optional_VecRef; + common::dense::update(H, + g, + A, + b, + C, + l, + u, + optional_VecRef(nullopt), + optional_VecRef(nullopt), + model, + work, + box_constraints); + } + common::dense::update_proximal_parameters( + settings, results, work, rho, mu_eq, mu_in); + common::dense::update_default_rho_with_minimal_Hessian_eigen_value( + manual_minimal_H_eigenvalue, results, settings); + typedef optional> optional_MatRef; + typedef optional> optional_VecRef; + common::dense::setup(/* avoid double assignation */ + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + settings, + model, + work, + results, + box_constraints, + ruiz, + preconditioner_status, + hessian_type); + + if (settings.compute_timings) { + results.info.setup_time = work.timer.elapsed().user; // in microseconds + } + }; + + /*! + * Updates the QP model (with dense matrix format) and re-equilibrates it if + * specified by the user. + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param l_box lower inequality constraint vector input defining the QP + * model. + * @param u_box upper inequality constraint vector input defining the QP + * model. + * @param update_preconditioner bool parameter for updating or not the + * preconditioner and the associated scaled model. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + * @note The init method should be called before update. If it has not been + * done before, init is called depending on the is_initialized flag. + */ + void update(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + bool update_preconditioner = false, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional manual_minimal_H_eigenvalue = nullopt) + { + PROXSUITE_THROW_PRETTY( + box_constraints == false && (l_box != nullopt || u_box != nullopt), + std::invalid_argument, + "wrong model setup: the QP object is designed without box " + "constraints, but the update includes lower or upper box inequalities."); + settings.update_preconditioner = update_preconditioner; + if (!work.is_initialized) { + init(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + update_preconditioner, + rho, + mu_eq, + mu_in); + return; + } + // dense case + work.refactorize = false; + work.proximal_parameter_update = false; + if (settings.compute_timings) { + work.timer.stop(); + work.timer.start(); + } + PreconditionerStatus preconditioner_status; + if (update_preconditioner) { + preconditioner_status = PreconditionerStatus::EXECUTE; + } else { + preconditioner_status = PreconditionerStatus::KEEP; + } + const bool matrix_update = + !(H == nullopt && g == nullopt && A == nullopt && b == nullopt && + C == nullopt && u == nullopt && l == nullopt && u_box == nullopt && + l_box == nullopt); + if (matrix_update) { + common::dense::update( + H, g, A, b, C, l, u, l_box, u_box, model, work, box_constraints); + } + common::dense::update_proximal_parameters( + settings, results, work, rho, mu_eq, mu_in); + common::dense::update_default_rho_with_minimal_Hessian_eigen_value( + manual_minimal_H_eigenvalue, results, settings); + typedef optional> optional_MatRef; + typedef optional> optional_VecRef; + common::dense::setup(/* avoid double assignation */ + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_MatRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + optional_VecRef(nullopt), + settings, + model, + work, + results, + box_constraints, + ruiz, + preconditioner_status, + hessian_type); + + if (settings.compute_timings) { + results.info.setup_time = work.timer.elapsed().user; // in microseconds + } + }; + + /*! + * Solves the QP problem using Derived algorithm. + */ + void solve() { derived().solve_implem(); }; + /*! + * Solves the QP problem using solvers algorithm using a warm start. + * @param x primal warm start. + * @param y dual equality warm start. + * @param z dual inequality warm start. + */ + void solve(optional> x, + optional> y, + optional> z) + { + common::dense::warm_start(x, y, z, results, settings, model); + derived().solve_implem(); + }; + /*! + * Clean-ups solver's results and workspace. + */ + void cleanup() + { + results.cleanup(settings); + work.cleanup(box_constraints); + } +}; + +/*! + * Solves the QP problem using Derived algorithm without the need to define a QP + * object, with matrices defined by Dense Eigen matrices. It is possible to set + * up some of the solver parameters (warm start, initial guess option, proximal + * step sizes, absolute and relative accuracies, maximum number of iterations, + * preconditioner execution). There are no box constraints in the model. + * Templates QPDerived and ConfigDerived for solvers specialization. + */ +template +Results +solve_base(const ConfigDerived& config, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt) +{ + isize n(0); + isize n_eq(0); + isize n_in(0); + if (H != nullopt) { + n = H.value().rows(); + } + if (A != nullopt) { + n_eq = A.value().rows(); + } + if (C != nullopt) { + n_in = C.value().rows(); + } + + QPDerived Qp(n, n_eq, n_in, false, DenseBackend::PrimalDualLDLT); + + config.init_derived_settings(Qp.settings); + config.init_qp(Qp, H, g, A, b, C, l, u); + + Qp.solve(x, y, z); + + return Qp.results; +} + +/*! + * Solves the QP problem using Derived algorithm without the need to define a QP + * object, with matrices defined by Dense Eigen matrices. It is possible to set + * up some of the solver parameters (warm start, initial guess option, proximal + * step sizes, absolute and relative accuracies, maximum number of iterations, + * preconditioner execution). + * Templates QPDerived and ConfigDerived for solvers specialization. + */ +template +Results +solve_base_box(const ConfigDerived& config, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt) +{ + isize n(0); + isize n_eq(0); + isize n_in(0); + if (H != nullopt) { + n = H.value().rows(); + } + if (A != nullopt) { + n_eq = A.value().rows(); + } + if (C != nullopt) { + n_in = C.value().rows(); + } + + QPDerived Qp(n, n_eq, n_in, true, DenseBackend::PrimalDualLDLT); + + config.init_derived_settings(Qp.settings); + config.init_qp_box(Qp, H, g, A, b, C, l, u, l_box, u_box); + + Qp.solve(x, y, z); + + return Qp.results; +} + +} // namespace dense +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_DENSE_WRAPPER_HPP */ diff --git a/include/proxsuite/proxqp/results.hpp b/include/proxsuite/common/results.hpp similarity index 72% rename from include/proxsuite/proxqp/results.hpp rename to include/proxsuite/common/results.hpp index 987d694e7..8f6b59f71 100644 --- a/include/proxsuite/proxqp/results.hpp +++ b/include/proxsuite/common/results.hpp @@ -4,21 +4,22 @@ /** * @file results.hpp */ -#ifndef PROXSUITE_PROXQP_RESULTS_HPP -#define PROXSUITE_PROXQP_RESULTS_HPP +#ifndef PROXSUITE_COMMON_RESULTS_HPP +#define PROXSUITE_COMMON_RESULTS_HPP #include #include #include #include -#include -#include "proxsuite/proxqp/status.hpp" -#include "proxsuite/proxqp/sparse/fwd.hpp" +#include +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/dense/fwd.hpp" namespace proxsuite { -namespace proxqp { +namespace common { + /// -/// @brief This class stores the results statistics of PROXQP solvers with +/// @brief This class stores the results statistics of the solvers with /// sparse and dense backends. /// /*! @@ -36,16 +37,17 @@ struct Info T nu; ///// iteration count - sparse::isize iter; - sparse::isize iter_ext; - sparse::isize mu_updates; - sparse::isize rho_updates; + isize iter; + isize iter_ext; + isize mu_updates; + isize rho_updates; QPSolverOutput status; //// timings T setup_time; T solve_time; T run_time; + T objValue; T pri_res; T dua_res; @@ -55,9 +57,15 @@ struct Info SparseBackend sparse_backend; //// quadratic cost minimal eigenvalue estimate T minimal_H_eigenvalue_estimate; + + // OSQP + T rho_osqp_estimate; + + T polish_time; + PolishOutput status_polish; }; /// -/// @brief This class stores all the results of PROXQP solvers with sparse and +/// @brief This class stores all the results of the solvers with sparse and /// dense backends. /// /*! @@ -67,19 +75,29 @@ template struct Results { + // Note code factorization + // Default values (e.g. mu_eq, mu_in, etc) come form ProxQP, as + // it was the first solver in ProxSuite. + // As this header is shared with others, like OSQP, the default + // values are systematically initialized in the corresponding wrappers. + ///// SOLUTION STORAGE - sparse::Vec x; - sparse::Vec y; - sparse::Vec z; - sparse::Vec se; // optimal shift to the closest feasible problem wrt - // equality constraints - sparse::Vec si; // optimal shift to the closest feasible problem wrt - // inequality constraints + dense::Vec x; + dense::Vec y; + dense::Vec z; + dense::Vec se; // optimal shift to the closest feasible problem wrt + // equality constraints + dense::Vec si; // optimal shift to the closest feasible problem wrt + // inequality constraints proxsuite::linalg::veg::Vec active_constraints; Info info; + // OSQP + dense::Vec zeta_eq; + dense::Vec zeta_in; + ////// SOLUTION STATUS /*! * Default constructor. @@ -97,19 +115,25 @@ struct Results , z(n_in) , se(n_eq) , si(n_in) + , zeta_eq(n_eq) + , zeta_in(n_in) { if (box_constraints) { z.resize(dim + n_in); si.resize(dim + n_in); + zeta_in.resize(dim + n_in); } else { z.resize(n_in); si.resize(n_in); + zeta_in.resize(n_in); } x.setZero(); y.setZero(); z.setZero(); se.setZero(); si.setZero(); + zeta_eq.setZero(); + zeta_in.setZero(); switch (dense_backend) { case DenseBackend::PrimalDualLDLT: info.rho = 1e-6; @@ -133,14 +157,17 @@ struct Results info.run_time = 0; info.setup_time = 0; info.solve_time = 0; + info.polish_time = 0.; info.objValue = 0.; info.pri_res = 0.; info.dua_res = 0.; info.duality_gap = 0.; info.iterative_residual = 0.; - info.status = QPSolverOutput::PROXQP_NOT_RUN; + info.status = QPSolverOutput::QPSOLVER_NOT_RUN; info.sparse_backend = SparseBackend::Automatic; info.minimal_H_eigenvalue_estimate = 0.; + info.status_polish = PolishOutput::POLISH_NOT_RUN; + info.rho_osqp_estimate = 1e-1; } /*! * cleanups the Result variables and set the info variables to their initial @@ -153,6 +180,8 @@ struct Results z.setZero(); se.setZero(); si.setZero(); + zeta_eq.setZero(); + zeta_in.setZero(); cold_start(settings); } void cleanup_statistics() @@ -160,6 +189,7 @@ struct Results info.run_time = 0; info.setup_time = 0; info.solve_time = 0; + info.polish_time = 0.; info.objValue = 0.; info.iter = 0; info.iter_ext = 0; @@ -169,8 +199,9 @@ struct Results info.dua_res = 0.; info.duality_gap = 0.; info.iterative_residual = 0.; - info.status = QPSolverOutput::PROXQP_MAX_ITER_REACHED; + info.status = QPSolverOutput::QPSOLVER_MAX_ITER_REACHED; info.sparse_backend = SparseBackend::Automatic; + info.status_polish = PolishOutput::POLISH_NOT_RUN; } void cold_start(optional> settings = nullopt) { @@ -181,6 +212,7 @@ struct Results info.mu_in = 1e-1; info.nu = 1.; info.minimal_H_eigenvalue_estimate = 0.; + info.rho_osqp_estimate = 1e-1; if (settings != nullopt) { info.rho = settings.value().default_rho; info.mu_eq = settings.value().default_mu_eq; @@ -199,6 +231,8 @@ struct Results z.setZero(); se.setZero(); si.setZero(); + zeta_eq.setZero(); + zeta_in.setZero(); cleanup_statistics(); } }; @@ -216,10 +250,14 @@ operator==(const Info& info1, const Info& info2) info1.rho_updates == info2.rho_updates && info1.status == info2.status && info1.setup_time == info2.setup_time && info1.solve_time == info2.solve_time && info1.run_time == info2.run_time && + info1.polish_time == info2.polish_time && + info1.status_polish == info2.status_polish && info1.objValue == info2.objValue && info1.pri_res == info2.pri_res && info1.dua_res == info2.dua_res && info1.duality_gap == info2.duality_gap && info1.duality_gap == info2.duality_gap && - info1.minimal_H_eigenvalue_estimate == info2.minimal_H_eigenvalue_estimate; + info1.minimal_H_eigenvalue_estimate == + info2.minimal_H_eigenvalue_estimate && + info1.rho_osqp_estimate == info2.rho_osqp_estimate; return value; } @@ -246,7 +284,7 @@ operator!=(const Results& results1, const Results& results2) return !(results1 == results2); } -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_RESULTS_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_RESULTS_HPP */ diff --git a/include/proxsuite/proxqp/settings.hpp b/include/proxsuite/common/settings.hpp similarity index 78% rename from include/proxsuite/proxqp/settings.hpp rename to include/proxsuite/common/settings.hpp index f455238b4..d0e0b5f1b 100644 --- a/include/proxsuite/proxqp/settings.hpp +++ b/include/proxsuite/common/settings.hpp @@ -4,16 +4,15 @@ /** * @file settings.hpp */ -#ifndef PROXSUITE_PROXQP_SETTINGS_HPP -#define PROXSUITE_PROXQP_SETTINGS_HPP +#ifndef PROXSUITE_COMMON_SETTINGS_HPP +#define PROXSUITE_COMMON_SETTINGS_HPP #include -#include -#include -#include +#include +#include namespace proxsuite { -namespace proxqp { +namespace common { // Sparse backend specifications enum struct SparseBackend @@ -77,7 +76,7 @@ operator<<(std::ostream& os, const DenseBackend& dense_backend) } /// -/// @brief This class defines the settings of PROXQP solvers with sparse and +/// @brief This class defines the settings of the solvers with sparse and /// dense backends. /// /*! @@ -88,6 +87,12 @@ template struct Settings { + // Note code factorization + // Default values (e.g. default_mu_eq, default_mu_in, etc) come form ProxQP, + // as it was the first solver in ProxSuite. As this header is shared with + // others, like OSQP, the default values are systematically initialized in the + // corresponding wrappers. + T default_rho; T default_mu_eq; T default_mu_in; @@ -137,10 +142,30 @@ struct Settings MeritFunctionType merit_function_type; T alpha_gpdal; - SparseBackend sparse_backend; + CheckSolvedStatus check_solved_option; + isize check_termination; bool primal_infeasibility_solving; isize frequence_infeasibility_check; + + SparseBackend sparse_backend; T default_H_eigenvalue_estimate; + + // OSQP + T alpha; + + T mu_max_eq; + T mu_max_in; + T mu_min_eq_inv; + T mu_min_in_inv; + + bool adaptive_mu; + isize adaptive_mu_interval; + T adaptive_mu_tolerance; + + bool polish; + T delta; + isize polish_refine_iter; + /*! * Default constructor. * @param default_rho default rho parameter of result class @@ -198,16 +223,36 @@ struct Settings * @param bcl_update if set to true, BCL strategy is used for calibrating * mu_eq and mu_in. If set to false, a strategy developped by Martinez & al is * used. - * @param sparse_backend Default automatic. User can choose between sparse - * cholesky or iterative matrix free sparse backend. + * @param check_solved_option: decide whether we check at each + * iteration (then use frequence_inefasibility_check to control the + * infeasibility check) or given an interval of iterations (then + * check_termination applies to solve and infeasibility checking) + * @param check_termination check termination interval for solved + * or infeasibility detected * @param primal_infeasibility_solving solves the closest primal feasible * problem if activated * @param frequence_infeasibility_check frequence at which infeasibility is * checked + * @param sparse_backend Default automatic. User can choose between sparse + * cholesky or iterative matrix free sparse backend. * @param find_H_minimal_eigenvalue track the minimal eigen value of the * quadratic cost H * @param default_H_eigenvalue_estimate default H eigenvalue estimate (i.e., * if we make a model update and H does not change this one is used) + * @param alpha (OSQP): alpha parameter in ADMM + * @param mu_max_eq (OSQP): maximum value for mu_eq + * @param mu_max_in (OSQP): maximum value for mu_in + * @param mu_min_eq_inv (OSQP): minimum value for mu_eq_inv + * @param mu_min_in_inv (OSQP): minimum value for mu_in_inv + * @param adaptive_mu (OSQP): if set to true, performs mu udpates + * @param adaptive_mu_interval (OSQP): minimum number of iterations before + * updating mu + * @param adaptive_mu_tolerance (OSQP): minimum ratio between old and new mu + * @param polish (OSQP): if set to true, polish the solution obtained from + * ADMM + * @param delta (OSQP): delta parameter in solution polishing + * @param polish_refine_iter (OSQP): number of iterative refinements in + * solution polishing */ Settings( @@ -254,10 +299,23 @@ struct Settings bool bcl_update = true, MeritFunctionType merit_function_type = MeritFunctionType::GPDAL, T alpha_gpdal = 0.95, - SparseBackend sparse_backend = SparseBackend::Automatic, + CheckSolvedStatus check_solved_option = CheckSolvedStatus::ITERATION_BASED, + isize check_termination = 25, bool primal_infeasibility_solving = false, isize frequence_infeasibility_check = 1, - T default_H_eigenvalue_estimate = 0.) + SparseBackend sparse_backend = SparseBackend::Automatic, + T default_H_eigenvalue_estimate = 0., + T alpha = 1.6, + T mu_max_eq = 1e3, + T mu_max_in = 1e6, + T mu_min_eq_inv = 1e-3, + T mu_min_in_inv = 1e-6, + bool adaptive_mu = true, + isize adaptive_mu_interval = 50, + T adaptive_mu_tolerance = 5., + bool polish = false, + T delta = 1e-6, + isize polish_refine_iter = 3) : default_mu_eq(default_mu_eq) , default_mu_in(default_mu_in) , alpha_bcl(alpha_bcl) @@ -296,10 +354,23 @@ struct Settings , bcl_update(bcl_update) , merit_function_type(merit_function_type) , alpha_gpdal(alpha_gpdal) - , sparse_backend(sparse_backend) + , check_solved_option(check_solved_option) + , check_termination(check_termination) , primal_infeasibility_solving(primal_infeasibility_solving) , frequence_infeasibility_check(frequence_infeasibility_check) + , sparse_backend(sparse_backend) , default_H_eigenvalue_estimate(default_H_eigenvalue_estimate) + , alpha(alpha) + , mu_max_eq(mu_max_eq) + , mu_max_in(mu_max_in) + , mu_min_eq_inv(mu_min_eq_inv) + , mu_min_in_inv(mu_min_in_inv) + , adaptive_mu(adaptive_mu) + , adaptive_mu_interval(adaptive_mu_interval) + , adaptive_mu_tolerance(adaptive_mu_tolerance) + , polish(polish) + , delta(delta) + , polish_refine_iter(polish_refine_iter) { switch (dense_backend) { case DenseBackend::PrimalDualLDLT: @@ -360,13 +431,26 @@ operator==(const Settings& settings1, const Settings& settings2) settings1.bcl_update == settings2.bcl_update && settings1.merit_function_type == settings2.merit_function_type && settings1.alpha_gpdal == settings2.alpha_gpdal && - settings1.sparse_backend == settings2.sparse_backend && + settings1.check_solved_option == settings2.check_solved_option && + settings1.check_termination == settings2.check_termination && settings1.primal_infeasibility_solving == settings2.primal_infeasibility_solving && settings1.frequence_infeasibility_check == settings2.frequence_infeasibility_check && + settings1.sparse_backend == settings2.sparse_backend && settings1.default_H_eigenvalue_estimate == - settings2.default_H_eigenvalue_estimate; + settings2.default_H_eigenvalue_estimate && + settings1.alpha == settings2.alpha && + settings1.mu_max_eq == settings2.mu_max_eq && + settings1.mu_max_in == settings2.mu_max_in && + settings1.mu_min_eq_inv == settings2.mu_min_eq_inv && + settings1.mu_min_in_inv == settings2.mu_min_in_inv && + settings1.adaptive_mu == settings2.adaptive_mu && + settings1.adaptive_mu_interval == settings2.adaptive_mu_interval && + settings1.adaptive_mu_tolerance == settings2.adaptive_mu_tolerance && + settings1.polish == settings2.polish && + settings1.delta == settings2.delta && + settings1.polish_refine_iter == settings2.polish_refine_iter; return value; } @@ -377,7 +461,7 @@ operator!=(const Settings& settings1, const Settings& settings2) return !(settings1 == settings2); } -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_SETTINGS_HPP */ +#endif /* end of include guard PROXSUITE_COMMON_SETTINGS_HPP */ diff --git a/include/proxsuite/common/solvers.hpp b/include/proxsuite/common/solvers.hpp new file mode 100644 index 000000000..214291e58 --- /dev/null +++ b/include/proxsuite/common/solvers.hpp @@ -0,0 +1,23 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file solvers.hpp + */ +#ifndef PROXSUITE_COMMON_SOLVERS_HPP +#define PROXSUITE_COMMON_SOLVERS_HPP + +namespace proxsuite { +namespace common { + +// SOLVERS IN PROXSUITE +enum struct QPSolver +{ + PROXQP, + OSQP +}; + +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_SOLVERS_HPP */ diff --git a/include/proxsuite/common/status.hpp b/include/proxsuite/common/status.hpp new file mode 100644 index 000000000..85ca784f1 --- /dev/null +++ b/include/proxsuite/common/status.hpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2022-2025 INRIA +// +/** + * @file constants.hpp + */ +#ifndef PROXSUITE_COMMON_STATUS_HPP +#define PROXSUITE_COMMON_STATUS_HPP + +namespace proxsuite { +namespace common { + +// SOLVER STATUS +enum struct QPSolverOutput +{ + QPSOLVER_SOLVED, // the problem is solved. + QPSOLVER_MAX_ITER_REACHED, // the maximum number of iterations has been + // reached. + QPSOLVER_PRIMAL_INFEASIBLE, // the problem is primal infeasible. + QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE, // the closest (in L2 sense) feasible + // problem is solved. + QPSOLVER_DUAL_INFEASIBLE, // the problem is dual infeasible. + QPSOLVER_NOT_RUN // the solver has not been run yet. +}; +// INITIAL GUESS STATUS +enum struct InitialGuessStatus +{ + NO_INITIAL_GUESS, + EQUALITY_CONSTRAINED_INITIAL_GUESS, + WARM_START_WITH_PREVIOUS_RESULT, + WARM_START, + COLD_START_WITH_PREVIOUS_RESULT +}; +// PRECONDITIONER STATUS +enum struct PreconditionerStatus +{ + EXECUTE, // initialize or update with qp in entry + KEEP, // keep previous preconditioner (for update method) + IDENTITY // do not execute, hence use identity preconditioner (for init + // method) +}; +// CHECK IF SOLVED OPTION +enum struct CheckSolvedStatus +{ + ITERATION_BASED, // use of settings.frequence_infeasibility_check only. + INTERVAL_BASED, // use of settings.check_termination only. +}; +// POLISH (OSQP) STATUS +enum struct PolishOutput +{ + POLISH_FAILED, // polish failed. + POLISH_NOT_RUN, // polish have not been run yet. + POLISH_SUCCEEDED, // residuals are reduced. + POLISH_NO_ACTIVE_SET_FOUND // no active set detected, polish skipped. +}; + +} // namespace common +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_STATUS_HPP */ diff --git a/include/proxsuite/proxqp/timings.hpp b/include/proxsuite/common/timings.hpp similarity index 91% rename from include/proxsuite/proxqp/timings.hpp rename to include/proxsuite/common/timings.hpp index 7d1e7a88a..524d3fb27 100644 --- a/include/proxsuite/proxqp/timings.hpp +++ b/include/proxsuite/common/timings.hpp @@ -2,13 +2,13 @@ // Copyright (c) 2022 INRIA // -#ifndef PROXSUITE_PROXQP_TIMINGS_HPP -#define PROXSUITE_PROXQP_TIMINGS_HPP +#ifndef PROXSUITE_COMMON_TIMINGS_HPP +#define PROXSUITE_COMMON_TIMINGS_HPP #include namespace proxsuite { -namespace proxqp { +namespace common { struct CPUTimes { @@ -95,7 +95,7 @@ struct Timer std::chrono::time_point m_start, m_end; }; -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif // ifndef PROXSUITE_PROXQP_TIMINGS_HPP +#endif // ifndef PROXSUITE_COMMON_TIMINGS_HPP diff --git a/include/proxsuite/common/utils/prints.hpp b/include/proxsuite/common/utils/prints.hpp new file mode 100644 index 000000000..24fe369e9 --- /dev/null +++ b/include/proxsuite/common/utils/prints.hpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2022-2025 INRIA +// +/** \file */ + +#ifndef PROXSUITE_COMMON_UTILS_PRINTS_HPP +#define PROXSUITE_COMMON_UTILS_PRINTS_HPP + +#include "proxsuite/common/solvers.hpp" +#include + +namespace proxsuite { +namespace common { + +inline void +print_line() +{ + std::string the_line = "-----------------------------------------------------" + "--------------------------------------------\0"; + std::cout << the_line << "\n" << std::endl; +} + +inline void +print_preambule(const QPSolver solver) +{ + print_line(); + switch (solver) { + case QPSolver::PROXQP: { + std::cout + << " ProxQP - Primal-Dual Proximal QP " + "Solver\n" + << " (c) Antoine Bambade, Sarah El Kazdadi, Fabian Schramm, Adrien " + "Taylor, and " + "Justin Carpentier\n" + << " Inria Paris 2022 \n" + << std::endl; + break; + } + case QPSolver::OSQP: { + std::cout + << "OSQP - An operator splitting algorithm for QP programs\n" + << "(c) Paper - Bartolomeo Stellato, Goran Banjac, Paul Goulart, " + "Alberto Bemporad and Stephen Boyd\n" + << "(c) Implementation - Lucas Haubert\n" + << "Inria Paris 2025\n" + << std::endl; + break; + } + } + + print_line(); +} + +} // end namespace common +} // end namespace proxsuite + +#endif /* end of include guard PROXSUITE_COMMON_UTILS_PRINTS_HPP */ diff --git a/include/proxsuite/proxqp/utils/random_qp_problems.hpp b/include/proxsuite/common/utils/random_qp_problems.hpp similarity index 83% rename from include/proxsuite/proxqp/utils/random_qp_problems.hpp rename to include/proxsuite/common/utils/random_qp_problems.hpp index e08641d47..423cd7643 100644 --- a/include/proxsuite/proxqp/utils/random_qp_problems.hpp +++ b/include/proxsuite/common/utils/random_qp_problems.hpp @@ -1,5 +1,10 @@ -#ifndef PROXSUITE_PROXQP_UTILS_RANDOM_QP_PROBLEMS_HPP -#define PROXSUITE_PROXQP_UTILS_RANDOM_QP_PROBLEMS_HPP +// +// Copyright (c) 2022-2025 INRIA +// +/** \file */ + +#ifndef PROXSUITE_COMMON_UTILS_RANDOM_QP_PROBLEMS_HPP +#define PROXSUITE_COMMON_UTILS_RANDOM_QP_PROBLEMS_HPP #include #include @@ -7,34 +12,30 @@ #include #include #include -#include -#include +#include +#include #include #include #include namespace proxsuite { -namespace proxqp { +namespace common { namespace utils { using c_int = long long; using c_float = double; -namespace proxqp = proxsuite::proxqp; - -template -using Mat = - Eigen::Matrix; +template +using Mat = Eigen::Matrix; template using Vec = Eigen::Matrix; template using SparseMat = Eigen::SparseMatrix; -using namespace proxqp; namespace eigen { template void @@ -64,8 +65,8 @@ LDLT_EXPLICIT_TPL_DECL(2, ldlt_compute>); } // namespace eigen namespace rand { -using proxqp::u32; -using proxqp::u64; +using common::u32; +using common::u64; #ifdef _MSC_VER /* Using the MSCV compiler on Windows causes problems because the type uint128 @@ -368,7 +369,7 @@ sparse_matrix_rand_not_compressed(isize nrows, isize ncols, Scalar p) } } // namespace rand -using proxqp::usize; +using common::usize; namespace osqp { auto @@ -380,31 +381,31 @@ to_sparse_sym(Mat const& mat) -> SparseMat; template auto matmul_impl( // - Mat const& lhs, - Mat const& rhs) -> Mat + Mat const& lhs, + Mat const& rhs) -> Mat { return lhs.operator*(rhs); } template auto -mat_cast(Mat const& from) -> Mat +mat_cast(Mat const& from) -> Mat { return from.template cast(); } LDLT_EXPLICIT_TPL_DECL(2, matmul_impl); -LDLT_EXPLICIT_TPL_DECL(1, mat_cast); -LDLT_EXPLICIT_TPL_DECL(1, mat_cast); +LDLT_EXPLICIT_TPL_DECL(1, mat_cast); +LDLT_EXPLICIT_TPL_DECL(1, mat_cast); template auto -matmul(MatLhs const& a, MatRhs const& b) -> Mat +matmul(MatLhs const& a, MatRhs const& b) -> Mat { using Upscaled = typename std:: conditional::value, long double, T>::type; - return mat_cast(matmul_impl( - Mat(a).template cast(), - Mat(b).template cast())); + return mat_cast( + matmul_impl(Mat(a).template cast(), + Mat(b).template cast())); } template auto -matmul3(MatLhs const& a, MatMid const& b, MatRhs const& c) - -> Mat +matmul3(MatLhs const& a, MatMid const& b, MatRhs const& c) -> Mat { return matmul(matmul(a, b), c); } @@ -436,8 +436,8 @@ struct EigenNoAlloc }; template -proxsuite::proxqp::dense::Model -dense_unconstrained_qp(proxqp::isize dim, +dense::Model +dense_unconstrained_qp(isize dim, Scalar sparsity_factor, Scalar strong_convexity_factor = Scalar(1e-2)) { @@ -453,17 +453,17 @@ dense_unconstrained_qp(proxqp::isize dim, rand::sparse_matrix_rand_not_compressed(0, dim, sparsity_factor); Vec u = rand::vector_rand(0); Vec l = rand::vector_rand(0); - proxsuite::proxqp::dense::Model model(dim, 0, 0); + dense::Model model(dim, 0, 0); model.H = H; model.g = g; return model; } template -proxsuite::proxqp::dense::Model -dense_strongly_convex_qp(proxqp::isize dim, - proxqp::isize n_eq, - proxqp::isize n_in, +dense::Model +dense_strongly_convex_qp(isize dim, + isize n_eq, + isize n_in, Scalar sparsity_factor, Scalar strong_convexity_factor = Scalar(1e-2)) { @@ -480,7 +480,7 @@ dense_strongly_convex_qp(proxqp::isize dim, Vec x_sol = rand::vector_rand(dim); auto delta = Vec(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { + for (isize i = 0; i < n_in; ++i) { delta(i) = rand::uniform_rand(); } @@ -490,7 +490,7 @@ dense_strongly_convex_qp(proxqp::isize dim, l.setZero(); l.array() -= 1.e20; - proxsuite::proxqp::dense::Model model(dim, n_eq, n_in); + dense::Model model(dim, n_eq, n_in); model.H = H; model.g = g; model.A = A; @@ -502,10 +502,10 @@ dense_strongly_convex_qp(proxqp::isize dim, } template -proxsuite::proxqp::dense::Model -dense_not_strongly_convex_qp(proxqp::isize dim, - proxqp::isize n_eq, - proxqp::isize n_in, +dense::Model +dense_not_strongly_convex_qp(isize dim, + isize n_eq, + isize n_in, Scalar sparsity_factor) { @@ -522,7 +522,7 @@ dense_not_strongly_convex_qp(proxqp::isize dim, Vec z_sol = rand::vector_rand(n_in); auto delta = Vec(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { + for (isize i = 0; i < n_in; ++i) { delta(i) = rand::uniform_rand(); } auto Cx = C * x_sol; @@ -531,7 +531,7 @@ dense_not_strongly_convex_qp(proxqp::isize dim, Vec l = Cx - delta; Vec g = -(H * x_sol + C.transpose() * z_sol + A.transpose() * y_sol); - proxsuite::proxqp::dense::Model model(dim, n_eq, n_in); + dense::Model model(dim, n_eq, n_in); model.H = H; model.g = g; model.A = A; @@ -543,10 +543,10 @@ dense_not_strongly_convex_qp(proxqp::isize dim, } template -proxsuite::proxqp::dense::Model -dense_degenerate_qp(proxqp::isize dim, - proxqp::isize n_eq, - proxqp::isize n_in, +dense::Model +dense_degenerate_qp(isize dim, + isize n_eq, + isize n_in, Scalar sparsity_factor, Scalar strong_convexity_factor = Scalar(1e-2)) { @@ -557,12 +557,12 @@ dense_degenerate_qp(proxqp::isize dim, Vec g = rand::vector_rand(dim); Mat A = rand::sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor); - Mat C = Mat(2 * n_in, dim); + Mat C = Mat(2 * n_in, dim); Vec x_sol = rand::vector_rand(dim); auto delta = Vec(2 * n_in); - for (proxqp::isize i = 0; i < 2 * n_in; ++i) { + for (isize i = 0; i < 2 * n_in; ++i) { delta(i) = rand::uniform_rand(); } Vec b = A * x_sol; @@ -577,7 +577,7 @@ dense_degenerate_qp(proxqp::isize dim, l.setZero(); l.array() -= 1.e20; - proxsuite::proxqp::dense::Model model(dim, n_eq, n_in); + dense::Model model(dim, n_eq, n_in); model.H = H; model.g = g; model.A = A; @@ -589,10 +589,10 @@ dense_degenerate_qp(proxqp::isize dim, } template -proxsuite::proxqp::dense::Model -dense_box_constrained_qp(proxqp::isize dim, - proxqp::isize n_eq, - proxqp::isize n_in, +dense::Model +dense_box_constrained_qp(isize dim, + isize n_eq, + isize n_in, Scalar sparsity_factor, Scalar strong_convexity_factor = Scalar(1e-2)) { @@ -603,12 +603,12 @@ dense_box_constrained_qp(proxqp::isize dim, Vec g = rand::vector_rand(dim); Mat A = rand::sparse_matrix_rand_not_compressed(n_eq, dim, sparsity_factor); - Mat C = Mat(n_in, dim); + Mat C = Mat(n_in, dim); Vec x_sol = rand::vector_rand(dim); auto delta = Vec(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { + for (isize i = 0; i < n_in; ++i) { delta(i) = rand::uniform_rand(); } Vec b = A * x_sol; @@ -616,7 +616,7 @@ dense_box_constrained_qp(proxqp::isize dim, C.diagonal().array() += 1; Vec u = x_sol + delta; Vec l = x_sol - delta; - proxsuite::proxqp::dense::Model model(dim, n_eq, n_in); + dense::Model model(dim, n_eq, n_in); model.H = H; model.g = g; model.A = A; @@ -629,9 +629,9 @@ dense_box_constrained_qp(proxqp::isize dim, template proxsuite::proxqp::sparse::SparseModel -sparse_strongly_convex_qp(proxqp::isize dim, - proxqp::isize n_eq, - proxqp::isize n_in, +sparse_strongly_convex_qp(isize dim, + isize n_eq, + isize n_in, Scalar sparsity_factor, Scalar strong_convexity_factor = Scalar(1e-2)) { @@ -647,7 +647,7 @@ sparse_strongly_convex_qp(proxqp::isize dim, Vec x_sol = rand::vector_rand(dim); auto delta = Vec(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { + for (isize i = 0; i < n_in; ++i) { delta(i) = rand::uniform_rand(); } @@ -662,8 +662,8 @@ sparse_strongly_convex_qp(proxqp::isize dim, } } // namespace utils -} // namespace proxqp +} // namespace common } // namespace proxsuite -#endif /* end of include guard PROXSUITE_PROXQP_UTILS_RANDOM_QP_PROBLEMS_HPP \ +#endif /* end of include guard PROXSUITE_COMMON_UTILS_RANDOM_QP_PROBLEMS_HPP \ */ diff --git a/include/proxsuite/osqp/dense/aliases.hpp b/include/proxsuite/osqp/dense/aliases.hpp new file mode 100644 index 000000000..bf24f06e7 --- /dev/null +++ b/include/proxsuite/osqp/dense/aliases.hpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file aliases.hpp + */ + +#ifndef PROXSUITE_OSQP_DENSE_ALIASES_HPP +#define PROXSUITE_OSQP_DENSE_ALIASES_HPP + +#include "proxsuite/common/solvers.hpp" +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/dense/model.hpp" +#include "proxsuite/common/dense/workspace.hpp" + +namespace proxsuite { +namespace osqp { +namespace dense { + +using proxsuite::common::from_eigen; +using proxsuite::common::i32; +using proxsuite::common::i64; +using proxsuite::common::isize; +using proxsuite::common::dense::infty_norm; + +using proxsuite::common::CheckSolvedStatus; +using proxsuite::common::DenseBackend; +using proxsuite::common::HessianType; +using proxsuite::common::InitialGuessStatus; +using proxsuite::common::MeritFunctionType; +using proxsuite::common::PolishOutput; +using proxsuite::common::PreconditionerStatus; +using proxsuite::common::QPSolver; +using proxsuite::common::QPSolverOutput; +using proxsuite::common::SparseBackend; +using proxsuite::common::Timer; + +using proxsuite::common::Results; +using proxsuite::common::Settings; +using proxsuite::common::dense::Model; +using proxsuite::common::dense::Workspace; + +using proxsuite::common::VectorView; +using proxsuite::common::VectorViewMut; +using proxsuite::common::dense::Mat; +using proxsuite::common::dense::MatRef; +using proxsuite::common::dense::Vec; +using proxsuite::common::dense::VecRef; + +} // namespace dense +} // namespace osqp +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_OSQP_DENSE_ALIASES_HPP */ diff --git a/include/proxsuite/osqp/dense/dense.hpp b/include/proxsuite/osqp/dense/dense.hpp new file mode 100644 index 000000000..f9bfda676 --- /dev/null +++ b/include/proxsuite/osqp/dense/dense.hpp @@ -0,0 +1,13 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file dense.hpp + */ + +#ifndef PROXSUITE_OSQP_DENSE_DENSE_HPP +#define PROXSUITE_OSQP_DENSE_DENSE_HPP + +#include "proxsuite/osqp/dense/wrapper.hpp" // includes everything + +#endif /* end of include guard PROXSUITE_OSQP_DENSE_DENSE_HPP */ diff --git a/include/proxsuite/osqp/dense/solver.hpp b/include/proxsuite/osqp/dense/solver.hpp new file mode 100644 index 000000000..d2574c294 --- /dev/null +++ b/include/proxsuite/osqp/dense/solver.hpp @@ -0,0 +1,1272 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file solver.hpp + */ + +#ifndef PROXSUITE_OSQP_DENSE_SOLVER_HPP +#define PROXSUITE_OSQP_DENSE_SOLVER_HPP + +#include "proxsuite/fwd.hpp" +#include "proxsuite/osqp/dense/aliases.hpp" +#include "proxsuite/common/dense/helpers.hpp" +#include "proxsuite/common/dense/utils.hpp" +#include "proxsuite/common/dense/prints.hpp" + +#include +#include + +namespace proxsuite { +namespace osqp { +namespace dense { + +/*! + * One iteration of the ADMM algorithm adapted in OSQP. + * + * Solves the linear system (KKT), then update the primal and dual variables. + * + * @param qpsettings solver settings. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param qpwork solver workspace. + */ +template +void +admm_step(const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const isize n_constraints, + const DenseBackend dense_backend) +{ + // Solve the linear system + qpwork.x_tilde.setZero(); + qpwork.nu_eq.setZero(); + qpwork.nu_in.setZero(); + qpwork.zeta_tilde_eq.setZero(); + qpwork.zeta_tilde_in.setZero(); + qpwork.zeta_in_next.setZero(); + + qpwork.rhs.setZero(); + qpwork.rhs.head(qpmodel.dim) = + qpresults.info.rho * qpresults.x - qpwork.g_scaled; + qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) = + qpresults.zeta_eq - qpresults.info.mu_eq * qpresults.y; + qpwork.rhs.tail(n_constraints) = + qpresults.zeta_in - qpresults.info.mu_in * qpresults.z; + + isize inner_pb_dim = qpmodel.dim + qpmodel.n_eq + n_constraints; + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + common::dense::solve_linear_system(qpwork.rhs, + qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + inner_pb_dim, + stack); + qpwork.x_tilde = qpwork.rhs.head(qpmodel.dim); + qpwork.nu_eq = qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq); + qpwork.nu_in = qpwork.rhs.tail(n_constraints); + + // Update the variables + qpwork.zeta_tilde_eq = + qpresults.zeta_eq + qpresults.info.mu_eq * (qpwork.nu_eq - qpresults.y); + qpwork.zeta_tilde_in = + qpresults.zeta_in + qpresults.info.mu_in * (qpwork.nu_in - qpresults.z); + + qpresults.x = + qpsettings.alpha * qpwork.x_tilde + (1 - qpsettings.alpha) * qpresults.x; + + qpwork.zeta_eq_next = qpwork.b_scaled; // projection in [b, b] + qpwork.zeta_in_next = qpsettings.alpha * qpwork.zeta_tilde_in + + (1 - qpsettings.alpha) * qpresults.zeta_in + + qpresults.info.mu_in * qpresults.z; + if (box_constraints) { + qpwork.zeta_in_next.head(qpmodel.n_in) = qpwork.l_scaled.cwiseMax( + qpwork.zeta_in_next.head(qpmodel.n_in).cwiseMin(qpwork.u_scaled)); + qpwork.zeta_in_next.tail(qpmodel.dim) = qpwork.l_box_scaled.cwiseMax( + qpwork.zeta_in_next.tail(qpmodel.dim).cwiseMin(qpwork.u_box_scaled)); + } else { + qpwork.zeta_in_next = + qpwork.l_scaled.cwiseMax(qpwork.zeta_in_next.cwiseMin(qpwork.u_scaled)); + } + + qpresults.y = qpresults.y + qpresults.info.mu_eq_inv * + (qpsettings.alpha * qpwork.zeta_tilde_eq + + (1 - qpsettings.alpha) * qpresults.zeta_eq - + qpwork.zeta_eq_next); + qpresults.z = qpresults.z + qpresults.info.mu_in_inv * + (qpsettings.alpha * qpwork.zeta_tilde_in + + (1 - qpsettings.alpha) * qpresults.zeta_in - + qpwork.zeta_in_next); + + qpresults.zeta_eq = qpwork.zeta_eq_next; + qpresults.zeta_in = qpwork.zeta_in_next; +} + +/*! + * Derives the scaled global primal residual of the QP problem. + * Computed as OSQP source code does to compute the ratio for mu update. + * + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param qpwork solver workspace. + * @param scaled_primal_feasibility_lhs primal infeasibility. + * @param scaled_primal_feasibility_eq_rhs_0 norm(scaled Ax) + * @param scaled_primal_feasibility_in_rhs_0 norm(scaled Cx) + * @param scaled_primal_feasibility_eq_lhs norm(scaled Ax - zeta_eq) + * @param scaled_primal_feasibility_in_lhs norm(scaled Cx - zeta_in) + */ +template +void +scaled_global_primal_residual(const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + T& scaled_primal_feasibility_lhs, + T& scaled_primal_feasibility_eq_rhs_0, + T& scaled_primal_feasibility_in_rhs_0, + T& scaled_primal_feasibility_eq_lhs, + T& scaled_primal_feasibility_in_lhs) +{ + qpresults.se.noalias() = qpwork.A_scaled * qpresults.x; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in).noalias() = + qpwork.C_scaled * qpresults.x; + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) = qpresults.x; + } + + scaled_primal_feasibility_eq_rhs_0 = infty_norm(qpresults.se); + scaled_primal_feasibility_in_rhs_0 = + infty_norm(qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in)); + + qpresults.si.head(qpmodel.n_in) = + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) - + qpresults.zeta_in.head(qpmodel.n_in); + if (box_constraints) { + qpresults.si.tail(qpmodel.dim) = + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) - + qpresults.zeta_in.tail(qpmodel.dim); + + qpwork.active_part_z.tail(qpmodel.dim) = + qpresults.x - qpresults.si.tail(qpmodel.dim); + scaled_primal_feasibility_in_rhs_0 = + std::max(scaled_primal_feasibility_in_rhs_0, + infty_norm(qpwork.active_part_z.tail(qpmodel.dim))); + scaled_primal_feasibility_in_rhs_0 = + std::max(scaled_primal_feasibility_in_rhs_0, infty_norm(qpresults.x)); + } + qpresults.se -= qpwork.b_scaled; + + scaled_primal_feasibility_in_lhs = infty_norm(qpresults.si); + scaled_primal_feasibility_eq_lhs = infty_norm(qpresults.se); + scaled_primal_feasibility_lhs = std::max(scaled_primal_feasibility_eq_lhs, + scaled_primal_feasibility_in_lhs); +} + +/*! + * Derives the scaled global dual residual of the QP problem. + * Computed as OSQP source code does to compute the ratio for mu update. + * + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpwork solver workspace. + * @param qpresults solver results. + * @param dual_feasibility_lhs primal infeasibility. + * @param scaled_dual_feasibility_eq_rhs_0 scalar variable used when using a + * relative stopping criterion. + * @param scaled_dual_feasibility_rhs_0 scalar variable used when using a + * relative stopping criterion. + * @param scaled_dual_feasibility_rhs_1 scalar variable used when using a + * relative stopping criterion. + * @param scaled_dual_feasibility_rhs_3 scalar variable used when using a + * relative stopping criterion. + */ +template +void +scaled_global_dual_residual( + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + T& scaled_dual_feasibility_lhs, // norm(scaled dual residual) + T& scaled_dual_feasibility_rhs_0, // norm(Hx) + T& scaled_dual_feasibility_rhs_1, // norm(ATy) + T& scaled_dual_feasibility_rhs_3, // norm(CTz) + const HessianType& hessian_type) +{ + qpwork.dual_residual_scaled = qpwork.g_scaled; + + switch (hessian_type) { + case HessianType::Zero: + scaled_dual_feasibility_rhs_0 = 0; + break; + case HessianType::Dense: + qpwork.CTz.noalias() = + qpwork.H_scaled.template selfadjointView() * qpresults.x; + qpwork.dual_residual_scaled += qpwork.CTz; + scaled_dual_feasibility_rhs_0 = infty_norm(qpwork.CTz); + break; + case HessianType::Diagonal: + qpwork.CTz.array() = + qpwork.H_scaled.diagonal().array() * qpresults.x.array(); + qpwork.dual_residual_scaled += qpwork.CTz; + scaled_dual_feasibility_rhs_0 = infty_norm(qpwork.CTz); + break; + } + + qpwork.CTz.noalias() = qpwork.A_scaled.transpose() * qpresults.y; + qpwork.dual_residual_scaled += qpwork.CTz; + scaled_dual_feasibility_rhs_1 = infty_norm(qpwork.CTz); + + qpwork.CTz.noalias() = + qpwork.C_scaled.transpose() * qpresults.z.head(qpmodel.n_in); + qpwork.dual_residual_scaled += qpwork.CTz; + scaled_dual_feasibility_rhs_3 = infty_norm(qpwork.CTz); + if (box_constraints) { + qpwork.CTz.noalias() = qpresults.z.tail(qpmodel.dim); + qpwork.CTz.array() *= qpwork.i_scaled.array(); + + qpwork.dual_residual_scaled += qpwork.CTz; + scaled_dual_feasibility_rhs_3 = + std::max(infty_norm(qpwork.CTz), scaled_dual_feasibility_rhs_3); + } + + scaled_dual_feasibility_lhs = infty_norm(qpwork.dual_residual_scaled); +} + +/*! + * Finds the active sets of constraints for the polishing step. + * + * Equality constraints are considred as lower inequality constraints. + * + * @param qpsettings solver settings. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param qpwork solver workspace. + */ +template +void +find_active_sets(const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + isize& numactive_inequalities, + isize& numactive_lower_inequalities, + isize& numactive_upper_inequalities, + isize& inner_pb_dim) +{ + qpwork.primal_residual_in_scaled_up = qpresults.zeta_in + qpwork.z_prev; + qpresults.si = qpwork.primal_residual_in_scaled_up; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) -= qpwork.u_scaled; + qpresults.si.head(qpmodel.n_in) -= qpwork.l_scaled; + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) -= + qpwork.u_box_scaled; + qpresults.si.tail(qpmodel.dim) -= qpwork.l_box_scaled; + } + + qpwork.active_set_up.array() = + (qpwork.primal_residual_in_scaled_up.array() > 0); + qpwork.active_set_low.array() = (qpresults.si.array() < 0); + + qpwork.active_inequalities = qpwork.active_set_up || qpwork.active_set_low; + numactive_lower_inequalities = qpwork.active_set_low.count(); + numactive_upper_inequalities = qpwork.active_set_up.count(); + numactive_inequalities = qpwork.active_inequalities.count(); + inner_pb_dim = qpmodel.dim + qpmodel.n_eq + numactive_inequalities; +} + +/*! + * Build the reduced matrix of inequality constraints in polishing. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + * @param qpresults solver results. + */ +template +void +build_reduced_inequality_constraints_matrices( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const isize n_constraints, + Mat& C_low, + Mat& C_up) +{ + isize low_index = 0; + isize up_index = 0; + + Vec tmp_low(qpmodel.dim); + Vec tmp_up(qpmodel.dim); + tmp_low.setZero(); + tmp_up.setZero(); + for (isize i = 0; i < n_constraints; ++i) { + if (qpwork.active_set_low(i)) { + if (i < qpmodel.n_in) { + C_low.row(low_index) = qpwork.C_scaled.row(i); + } else { + tmp_low(i - qpmodel.n_in) = qpwork.i_scaled(i - qpmodel.n_in); + C_low.row(low_index) = tmp_low; + tmp_low(i - qpmodel.n_in) = 0.; + } + ++low_index; + } + if (qpwork.active_set_up(i)) { + if (i < qpmodel.n_in) { + C_up.row(up_index) = qpwork.C_scaled.row(i); + } else { + tmp_up(i - qpmodel.n_in) = qpwork.i_scaled(i - qpmodel.n_in); + C_up.row(up_index) = tmp_up; + tmp_up(i - qpmodel.n_in) = 0.; + } + ++up_index; + } + } +} + +/*! + * Build the matrices K and K + Delta_K in polishing. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + */ +template +void +build_kkt_matrices_polishing( // + const Settings& qpsettings, + const Model& qpmodel, + Workspace& qpwork, + const HessianType hessian_type, + Mat& k_polish, + Mat& k_plus_delta_k_polish, + Mat C_low, + Mat C_up, + isize numactive_lower_inequalities, + isize numactive_upper_inequalities, + isize numactive_inequalities) +{ + // Construction of K + isize row; + isize col; + + switch (hessian_type) { + case HessianType::Dense: + k_polish.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; + break; + case HessianType::Zero: + k_polish.topLeftCorner(qpmodel.dim, qpmodel.dim).setZero(); + break; + case HessianType::Diagonal: + k_polish.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; + break; + } + + col = qpmodel.dim; + k_polish.block(0, col, qpmodel.dim, qpmodel.n_eq) = + qpwork.A_scaled.transpose(); + + col += qpmodel.n_eq; + k_polish.block(0, col, qpmodel.dim, numactive_lower_inequalities) = + C_low.transpose(); + + col += numactive_lower_inequalities; + k_polish.block(0, col, qpmodel.dim, numactive_upper_inequalities) = + C_up.transpose(); + + row = qpmodel.dim; + k_polish.block(row, 0, qpmodel.n_eq, qpmodel.dim) = qpwork.A_scaled; + + row += qpmodel.n_eq; + k_polish.block(row, 0, numactive_lower_inequalities, qpmodel.dim) = C_low; + + row += numactive_lower_inequalities; + k_polish.block(row, 0, numactive_upper_inequalities, qpmodel.dim) = C_up; + + k_polish + .bottomRightCorner(qpmodel.n_eq + numactive_inequalities, + qpmodel.n_eq + numactive_inequalities) + .setZero(); + + // Construction of K + Delta_K + k_plus_delta_k_polish = k_polish; + k_plus_delta_k_polish.topLeftCorner(qpmodel.dim, qpmodel.dim) + .diagonal() + .array() += qpsettings.delta; + k_plus_delta_k_polish + .bottomRightCorner(qpmodel.n_eq + numactive_inequalities, + qpmodel.n_eq + numactive_inequalities) + .diagonal() + .array() -= qpsettings.delta; +} + +/*! + * Build the right hand side (-g, b, l_L, u_U) in polishing. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpsettings solver settings. + */ +template +void +build_rhs_polishing( // + const Settings& qpsettings, + const Model& qpmodel, + Workspace& qpwork, + const HessianType hessian_type, + const isize n_constraints, + Vec& rhs_polish, + isize numactive_lower_inequalities, + isize numactive_upper_inequalities) +{ + isize low_index = 0; + isize up_index = 0; + + Vec l_low(numactive_lower_inequalities); + Vec u_up(numactive_upper_inequalities); + for (isize i = 0; i < n_constraints; ++i) { + if (qpwork.active_set_low(i)) { + if (i < qpmodel.n_in) { + l_low(low_index) = qpwork.l_scaled(i); + } else { + l_low(low_index) = qpwork.l_box_scaled(i - qpmodel.n_in); + } + ++low_index; + } + if (qpwork.active_set_up(i)) { + if (i < qpmodel.n_in) { + u_up(up_index) = qpwork.u_scaled(i); + } else { + u_up(up_index) = qpwork.u_box_scaled(i - qpmodel.n_in); + } + ++up_index; + } + } + + isize row; + + row = qpmodel.dim; + rhs_polish.head(row) = -qpwork.g_scaled; + rhs_polish.segment(row, qpmodel.n_eq) = qpwork.b_scaled; + + row += qpmodel.n_eq; + rhs_polish.segment(row, numactive_lower_inequalities) = l_low; + + row += numactive_lower_inequalities; + rhs_polish.segment(row, numactive_upper_inequalities) = u_up; +} + +/*! + * Update primal and dual variables in polishing. + * + * @param qpwork solver workspace. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + */ +template +void +update_variables_polishing( // + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const isize n_constraints, + Vec hat_t, + isize numactive_lower_inequalities) +{ + // Get (x, y, z) from hat_t + qpresults.x = hat_t.head(qpmodel.dim); + qpresults.y = hat_t.segment(qpmodel.dim, qpmodel.n_eq); + + isize low_index = 0; + isize up_index = 0; + + for (isize i = 0; i < n_constraints; ++i) { + if (qpwork.active_set_low(i)) { + qpresults.z(i) = hat_t(qpmodel.dim + qpmodel.n_eq + low_index); + ++low_index; + } + if (qpwork.active_set_up(i)) { + qpresults.z(i) = hat_t(qpmodel.dim + qpmodel.n_eq + + numactive_lower_inequalities + up_index); + ++up_index; + } + } + + // Projection of the dual solution into the normal cone N_[l, u](zeta) + // by doing: z <- z + zeta; zeta <- proj_[l, u](z); z <- z - zeta + qpresults.z += qpresults.zeta_in; + if (box_constraints) { + qpresults.zeta_in.head(qpmodel.n_in) = qpwork.l_scaled.cwiseMax( + qpresults.z.head(qpmodel.n_in).cwiseMin(qpwork.u_scaled)); + qpresults.zeta_in.tail(qpmodel.dim) = qpwork.l_box_scaled.cwiseMax( + qpresults.z.tail(qpmodel.dim).cwiseMin(qpwork.u_box_scaled)); + } else { + qpresults.zeta_in = + qpwork.l_scaled.cwiseMax(qpresults.z.cwiseMin(qpwork.u_scaled)); + } + qpresults.z -= qpresults.zeta_in; +} + +/*! + * Print polishing line after the ADMM iterations. + * + * @param qpresults solver results. + * @param qpsettings solver settings. + */ +template +void +print_polishing_line( // + const Settings& qpsettings, + Results& qpresults) +{ + switch (qpresults.info.status_polish) { + case PolishOutput::POLISH_SUCCEEDED: { + std::cout << "\033[1;34m[polishing]\033[0m" << std::endl; + std::cout << std::scientific << std::setw(2) << std::setprecision(2) + << "| primal residual=" << qpresults.info.pri_res + << " | dual residual=" << qpresults.info.dua_res + << " | duality gap=" << qpresults.info.duality_gap + << " | delta=" << qpsettings.delta << std::endl; + std::cout << "\033[1;34m[polishing: succeed]\033[0m" << std::endl; + break; + } + case PolishOutput::POLISH_FAILED: { + std::cout << "\033[1;34m[polishing]\033[0m" << std::endl; + std::cout << std::scientific << std::setw(2) << std::setprecision(2) + << "| primal residual=" << qpresults.info.pri_res + << " | dual residual=" << qpresults.info.dua_res + << " | duality gap=" << qpresults.info.duality_gap + << " | delta=" << qpsettings.delta << std::endl; + std::cout << "\033[1;34m[polishing: failed]\033[0m" << std::endl; + break; + } + case PolishOutput::POLISH_NO_ACTIVE_SET_FOUND: { + std::cout << "\033[1;34m[polishing: no active set found]\033[0m" + << std::endl; + break; + } + case PolishOutput::POLISH_NOT_RUN: { + std::cout << "\033[1;34m[polishing: not run]\033[0m" << std::endl; + break; + } + } +} + +/*! + * Executes the OSQP algorithm. + * + * @param qpsettings solver settings. + * @param qpmodel QP problem model as defined by the user (without any scaling + * performed). + * @param qpresults solver results. + * @param qpwork solver workspace. + * @param ruiz ruiz preconditioner. + */ +template +void +qp_solve( // + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + common::dense::preconditioner::RuizEquilibration& ruiz) +{ + PROXSUITE_EIGEN_MALLOC_NOT_ALLOWED(); + + isize n_constraints(qpmodel.n_in); + if (box_constraints) { + n_constraints += qpmodel.dim; + } + if (qpsettings.compute_timings) { + qpwork.timer.stop(); + qpwork.timer.start(); + } + + // Setup header + /////////////////////// + + if (qpsettings.verbose) { + common::dense::print_setup_header(qpsettings, + qpresults, + qpmodel, + box_constraints, + dense_backend, + hessian_type, + QPSolver::OSQP); + } + + // Ruiz equilibration and factorization + /////////////////////// + + common::dense::init_qp_solve(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + dense_backend, + hessian_type, + ruiz, + n_constraints, + QPSolver::OSQP); + + // Active sets for duality gap computation + /////////////////////// + + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in).noalias() = + qpwork.C_scaled * qpresults.x; // contains now scaled(Cx) + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) = qpresults.x; + // contains now scaled(Cx) + } + + qpwork.primal_residual_in_scaled_up += + qpwork.z_prev * + qpresults.info.mu_in; // contains now scaled(Cx+z_prev*mu_in) + + qpresults.si = qpwork.primal_residual_in_scaled_up; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) -= + qpwork.u_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.head(qpmodel.n_in) -= + qpwork.l_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) -= + qpwork.u_box_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.tail(qpmodel.dim) -= + qpwork.l_box_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + } + + qpwork.active_set_up.array() = + (qpwork.primal_residual_in_scaled_up.array() >= 0); + qpwork.active_set_low.array() = (qpresults.si.array() <= 0); + + // Tmp variables + /////////////////////// + + T primal_feasibility_eq_rhs_0(0); + T primal_feasibility_in_rhs_0(0); + T dual_feasibility_rhs_0(0); + T dual_feasibility_rhs_1(0); + T dual_feasibility_rhs_3(0); + T primal_feasibility_lhs(0); + T primal_feasibility_eq_lhs(0); + T primal_feasibility_in_lhs(0); + T dual_feasibility_lhs(0); + + T scaled_primal_feasibility_lhs(0); + T scaled_primal_feasibility_eq_rhs_0(0); + T scaled_primal_feasibility_in_rhs_0(0); + T scaled_primal_feasibility_eq_lhs(0); + T scaled_primal_feasibility_in_lhs(0); + T scaled_dual_feasibility_lhs(0); + T scaled_dual_feasibility_rhs_0(0); + T scaled_dual_feasibility_rhs_1(0); + T scaled_dual_feasibility_rhs_3(0); + + T zeta_norms(0); + T pri_res_norms(0); + T pri_res_update(0); + T objective_norms(0); + T constraints_norms(0); + T dua_res_update(0); + T mu_update_ratio(0); + + T new_mu_eq(qpresults.info.mu_eq); + T new_mu_in(qpresults.info.mu_in); + T new_mu_eq_inv(qpresults.info.mu_eq_inv); + T new_mu_in_inv(qpresults.info.mu_in_inv); + + T duality_gap(0); + T rhs_duality_gap(0); + T scaled_eps(qpsettings.eps_abs); + + // ADMM loop + /////////////////////// + + for (i64 iter = 0; iter < qpsettings.max_iter; ++iter) { + + common::dense::compute_residuals(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + hessian_type, + ruiz, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap); + + // Print iteration + /////////////////////// + + if (qpsettings.verbose) { + common::dense::print_iteration_line( + qpresults, qpmodel, box_constraints, ruiz, QPSolver::OSQP, iter); + } + + // Check if solved + /////////////////////// + + bool can_check_termination; + switch (qpsettings.check_solved_option) { + case CheckSolvedStatus::ITERATION_BASED: { + can_check_termination = true; + break; + } + case CheckSolvedStatus::INTERVAL_BASED: { + can_check_termination = qpsettings.check_termination != 0 && + iter % qpsettings.check_termination == 0; + break; + } + } + + if (can_check_termination) { + bool stop_solved = common::dense::is_solved(qpsettings, + qpresults, + qpwork, + scaled_eps, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap); + + if (stop_solved) { + break; + } + } + + // Set iteration and variables + /////////////////////// + + qpresults.info.iter_ext += 1; + + qpwork.x_prev = qpresults.x; + qpwork.y_prev = qpresults.y; + qpwork.z_prev = qpresults.z; + + // Active sets for duality gap computation + /////////////////////// + + ruiz.scale_primal_residual_in_place_in( + VectorViewMut{ from_eigen, + qpwork.primal_residual_in_scaled_up.head( + qpmodel.n_in) }); // contains now scaled(Cx) + if (box_constraints) { + ruiz.scale_box_primal_residual_in_place_in( + VectorViewMut{ from_eigen, + qpwork.primal_residual_in_scaled_up.tail( + qpmodel.dim) }); // contains now scaled(x) + } + + qpwork.primal_residual_in_scaled_up += + qpwork.z_prev * + qpresults.info.mu_in; // contains now scaled(Cx+z_prev*mu_in) + + qpresults.si = qpwork.primal_residual_in_scaled_up; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) -= + qpwork.u_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.head(qpmodel.n_in) -= + qpwork.l_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) -= + qpwork.u_box_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.tail(qpmodel.dim) -= + qpwork.l_box_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + } + + qpwork.active_set_up.array() = + (qpwork.primal_residual_in_scaled_up.array() >= 0); + qpwork.active_set_low.array() = (qpresults.si.array() <= 0); + + // ADMM step of variable updates + /////////////////////// + + admm_step(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + n_constraints, + dense_backend); + + // Check infeasibility + /////////////////////// + + switch (qpsettings.check_solved_option) { + case CheckSolvedStatus::ITERATION_BASED: { + can_check_termination = + iter % qpsettings.frequence_infeasibility_check == 0 || + qpsettings.primal_infeasibility_solving; + break; + } + case CheckSolvedStatus::INTERVAL_BASED: { + can_check_termination = qpsettings.check_termination != 0 && + iter % qpsettings.check_termination == 0; + break; + } + } + + if (can_check_termination) { + Vec dx = qpresults.x - qpwork.x_prev; + Vec dy = qpresults.y - qpwork.y_prev; + Vec dz = qpresults.z - qpwork.z_prev; + + auto& Hdx = qpwork.Hdx; + auto& Adx = qpwork.Adx; + auto& Cdx = qpwork.Cdx; + auto& ATdy = qpwork.CTz; + + switch (hessian_type) { + case HessianType::Zero: + break; + case HessianType::Dense: + Hdx.noalias() = + qpwork.H_scaled.template selfadjointView() * dx; + break; + case HessianType::Diagonal: +#ifndef NDEBUG + PROXSUITE_THROW_PRETTY(!qpwork.H_scaled.isDiagonal(), + std::invalid_argument, + "H is not diagonal."); +#endif + Hdx.array() = qpwork.H_scaled.diagonal().array() * dx.array(); + break; + } + + Adx.noalias() = qpwork.A_scaled * dx; + ATdy.noalias() = qpwork.A_scaled.transpose() * dy; + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() + }; + LDLT_TEMP_VEC(T, CTdz, qpmodel.dim, stack); + if (qpmodel.n_in > 0) { + Cdx.head(qpmodel.n_in).noalias() = qpwork.C_scaled * dx; + CTdz.noalias() = qpwork.C_scaled.transpose() * dz.head(qpmodel.n_in); + } + if (box_constraints) { + // use active_part_z as tmp variable in order to unscale primarilly dz + qpwork.active_part_z.tail(qpmodel.dim) = dz.tail(qpmodel.dim); + qpwork.active_part_z.tail(qpmodel.dim).array() *= + qpwork.i_scaled.array(); + CTdz.noalias() += qpwork.active_part_z.tail(qpmodel.dim); + + Cdx.tail(qpmodel.dim) = dx; + Cdx.tail(qpmodel.dim).array() *= qpwork.i_scaled.array(); + } + + // compute primal and dual infeasibility criteria + bool is_primal_infeasible = + common::dense::global_primal_residual_infeasibility( + VectorViewMut{ from_eigen, ATdy }, + VectorViewMut{ from_eigen, CTdz }, + VectorViewMut{ from_eigen, dy }, + VectorViewMut{ from_eigen, dz }, + qpwork, + qpmodel, + qpsettings, + box_constraints, + ruiz); + + bool is_dual_infeasible = + common::dense::global_dual_residual_infeasibility( + VectorViewMut{ from_eigen, Adx }, + VectorViewMut{ from_eigen, Cdx }, + VectorViewMut{ from_eigen, Hdx }, + VectorViewMut{ from_eigen, dx }, + qpwork, + qpsettings, + qpmodel, + box_constraints, + ruiz); + + if (is_primal_infeasible) { + qpresults.info.status = QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE; + break; + } else if (is_dual_infeasible) { + qpresults.info.status = QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE; + break; + } + } + + // Update solver status + /////////////////////// + + T primal_feasibility_lhs_new(primal_feasibility_lhs); + T dual_feasibility_lhs_new(dual_feasibility_lhs); + + common::dense::compute_residuals(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + hessian_type, + ruiz, + primal_feasibility_lhs_new, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs, + dual_feasibility_lhs_new, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap); + + common::dense::is_solved_or_closest_solved(qpsettings, + qpresults, + qpwork, + scaled_eps, + primal_feasibility_lhs_new, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + dual_feasibility_lhs_new, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap); + + // Update of proximal parameter mu + /////////////////////// + + if (qpsettings.adaptive_mu) { + bool iteration_condition = + (iter + 1) % qpsettings.adaptive_mu_interval == 0; + + if (iteration_condition) { + scaled_global_primal_residual( + qpmodel, + qpresults, + qpwork, + box_constraints, + scaled_primal_feasibility_lhs, // norm(scaled pri res) + scaled_primal_feasibility_eq_rhs_0, // norm(scaled Ax) + scaled_primal_feasibility_in_rhs_0, // norm(scaled Cx) + scaled_primal_feasibility_eq_lhs, // norm(scaled Ax - zeta_eq) + scaled_primal_feasibility_in_lhs); // norm(scaled Cx - zeta_in) + + scaled_global_dual_residual( + qpmodel, + qpresults, + qpwork, + box_constraints, + scaled_dual_feasibility_lhs, // norm(scaled dua res) + scaled_dual_feasibility_rhs_0, // norm(Hx) + scaled_dual_feasibility_rhs_1, // norm(ATy) + scaled_dual_feasibility_rhs_3, // norm(CTz) + hessian_type); + + zeta_norms = std::max(infty_norm(qpresults.zeta_eq), + infty_norm(qpresults.zeta_in)); + pri_res_norms = std::max(scaled_primal_feasibility_eq_rhs_0, + scaled_primal_feasibility_in_rhs_0); + pri_res_update = scaled_primal_feasibility_lhs / + (std::max(zeta_norms, pri_res_norms) + 1e-30); + + objective_norms = + std::max(scaled_dual_feasibility_rhs_0, infty_norm(qpwork.g_scaled)); + constraints_norms = std::max(scaled_dual_feasibility_rhs_1, + scaled_dual_feasibility_rhs_3); + dua_res_update = scaled_dual_feasibility_lhs / + (std::max(objective_norms, constraints_norms) + 1e-30); + + mu_update_ratio = std::sqrt(pri_res_update / dua_res_update); + + qpresults.info.rho_osqp_estimate = + qpresults.info.mu_in_inv * mu_update_ratio; + qpresults.info.rho_osqp_estimate = std::min( + std::max(qpresults.info.rho_osqp_estimate, qpsettings.mu_min_in_inv), + qpsettings.mu_max_in_inv); + + bool tolerance_condition = + (qpresults.info.rho_osqp_estimate > + qpresults.info.mu_in_inv * qpsettings.adaptive_mu_tolerance || + qpresults.info.rho_osqp_estimate < + qpresults.info.mu_in_inv / qpsettings.adaptive_mu_tolerance); + + if (tolerance_condition) { + { + ++qpresults.info.mu_updates; + + new_mu_eq = 1e-3 / qpresults.info.rho_osqp_estimate; + new_mu_in = 1.0 / qpresults.info.rho_osqp_estimate; + new_mu_eq_inv = 1e3 * qpresults.info.rho_osqp_estimate; + new_mu_in_inv = qpresults.info.rho_osqp_estimate; + } + mu_update(qpmodel, + qpresults, + qpwork, + n_constraints, + dense_backend, + new_mu_eq, + new_mu_in); + + qpresults.info.mu_eq = new_mu_eq; + qpresults.info.mu_in = new_mu_in; + qpresults.info.mu_eq_inv = new_mu_eq_inv; + qpresults.info.mu_in_inv = new_mu_in_inv; + } + } + } + } // End of ADMM loop + + // Solution polishing + /////////////////////// + + if (qpsettings.polish && + qpresults.info.status == QPSolverOutput::QPSOLVER_SOLVED) { + + // Timing polishing + qpwork.timer_polish.stop(); + qpwork.timer_polish.start(); + + // ADMM solution + dense::Vec x_admm = qpresults.x; + dense::Vec y_admm = qpresults.y; + dense::Vec z_admm = qpresults.z; + dense::Vec zeta_in_admm = qpresults.zeta_in; + + T pri_res_admm = qpresults.info.pri_res; + T dua_res_admm = qpresults.info.dua_res; + T duality_gap_admm = qpresults.info.duality_gap; + + // Find active inequality constraints (equality are considered lower-active) + isize numactive_lower_inequalities; + isize numactive_upper_inequalities; + isize numactive_inequalities; + isize inner_pb_dim; + + find_active_sets(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + numactive_inequalities, + numactive_lower_inequalities, + numactive_upper_inequalities, + inner_pb_dim); + + // Build the reduced KKT matrix + Mat C_low(numactive_lower_inequalities, qpmodel.dim); + Mat C_up(numactive_upper_inequalities, qpmodel.dim); + + build_reduced_inequality_constraints_matrices( + qpsettings, qpmodel, qpresults, qpwork, n_constraints, C_low, C_up); + + Mat k_polish(inner_pb_dim, inner_pb_dim); + Mat k_plus_delta_k_polish(inner_pb_dim, inner_pb_dim); + + build_kkt_matrices_polishing(qpsettings, + qpmodel, + qpwork, + hessian_type, + k_polish, + k_plus_delta_k_polish, + C_low, + C_up, + numactive_lower_inequalities, + numactive_upper_inequalities, + numactive_inequalities); + + proxsuite::linalg::veg::dynstack::DynStackMut stack{ + proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_polish_stack.as_mut() + }; + qpwork.ldl_polish.factorize(k_plus_delta_k_polish.transpose(), stack); + + // Build the reduced rhs + Vec rhs_polish(inner_pb_dim); + + build_rhs_polishing(qpsettings, + qpmodel, + qpwork, + hessian_type, + n_constraints, + rhs_polish, + numactive_lower_inequalities, + numactive_upper_inequalities); + + // Solve K t = rhs before iterative refinement + Vec hat_t = rhs_polish; + + qpwork.ldl_polish.solve_in_place(hat_t.head(inner_pb_dim), stack); + + // Iterative refinement + Vec rhs_polish_refine(inner_pb_dim); + Vec delta_hat_t(inner_pb_dim); + + for (i64 iter = 0; iter < qpsettings.polish_refine_iter; ++iter) { + rhs_polish_refine = rhs_polish - k_polish * hat_t; + delta_hat_t = rhs_polish_refine; + + qpwork.ldl_polish.solve_in_place(delta_hat_t.head(inner_pb_dim), stack); + + hat_t = hat_t + delta_hat_t; + } + + // Update variables + update_variables_polishing(qpmodel, + qpresults, + qpwork, + box_constraints, + n_constraints, + hat_t, + numactive_lower_inequalities); + + // Check if solution polishing succeeded + common::dense::global_primal_residual(qpmodel, + qpresults, + qpsettings, + qpwork, + ruiz, + box_constraints, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs); + + ruiz.scale_primal_residual_in_place_in( + VectorViewMut{ from_eigen, + qpwork.primal_residual_in_scaled_up.head( + qpmodel.n_in) }); // contains now scaled(Cx) + if (box_constraints) { + ruiz.scale_box_primal_residual_in_place_in( + VectorViewMut{ from_eigen, + qpwork.primal_residual_in_scaled_up.tail( + qpmodel.dim) }); // contains now scaled(x) + } + + qpwork.primal_residual_in_scaled_up += + qpwork.z_prev * + qpresults.info.mu_in; // contains now scaled(Cx+z_prev*mu_in) + + qpresults.si = qpwork.primal_residual_in_scaled_up; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) -= + qpwork.u_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.head(qpmodel.n_in) -= + qpwork.l_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) -= + qpwork.u_box_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.tail(qpmodel.dim) -= + qpwork.l_box_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + } + + qpwork.active_set_up.array() = + (qpwork.primal_residual_in_scaled_up.array() >= 0); + qpwork.active_set_low.array() = (qpresults.si.array() <= 0); + + common::dense::global_dual_residual(qpresults, + qpwork, + qpmodel, + box_constraints, + ruiz, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap, + hessian_type); + + qpresults.info.pri_res = primal_feasibility_lhs; + qpresults.info.dua_res = dual_feasibility_lhs; + qpresults.info.duality_gap = duality_gap; + + bool polish_succeeded = + (qpresults.info.pri_res < pri_res_admm && + qpresults.info.dua_res < dua_res_admm) || + (qpresults.info.pri_res < pri_res_admm && dua_res_admm < 1e-10) || + (qpresults.info.dua_res < dua_res_admm && pri_res_admm < 1e-10); + + if (polish_succeeded) { + qpresults.info.status_polish = PolishOutput::POLISH_SUCCEEDED; + } else { + qpresults.x = x_admm; + qpresults.y = y_admm; + qpresults.z = z_admm; + qpresults.zeta_in = zeta_in_admm; + + qpresults.info.pri_res = pri_res_admm; + qpresults.info.dua_res = dua_res_admm; + qpresults.info.duality_gap = duality_gap_admm; + + qpresults.info.status_polish = PolishOutput::POLISH_FAILED; + } + + // Timing polishing + qpresults.info.polish_time = qpwork.timer_polish.elapsed().user; + + // Print polishing info + if (qpsettings.verbose) { + print_polishing_line(qpsettings, qpresults); + } + } + + // End of qp_solve + /////////////////////// + + common::dense::unscale_solver( + qpsettings, qpmodel, qpresults, box_constraints, ruiz); + + common::dense::compute_objective(qpresults, qpmodel); + + if (qpsettings.compute_timings) { + common::dense::compute_timings(qpresults, qpwork); + } + + if (qpsettings.verbose) { + common::dense::print_solver_statistics( + qpsettings, qpresults, QPSolver::OSQP); + } + + qpwork.dirty = true; + qpwork.is_initialized = true; // necessary because we call workspace cleanup + + assert(!std::isnan(qpresults.info.pri_res)); + assert(!std::isnan(qpresults.info.dua_res)); + assert(!std::isnan(qpresults.info.duality_gap)); + + PROXSUITE_EIGEN_MALLOC_ALLOWED(); +} + +} // namespace dense +} // namespace osqp +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_OSQP_DENSE_SOLVER_HPP */ diff --git a/include/proxsuite/osqp/dense/wrapper.hpp b/include/proxsuite/osqp/dense/wrapper.hpp new file mode 100644 index 000000000..015d71bbc --- /dev/null +++ b/include/proxsuite/osqp/dense/wrapper.hpp @@ -0,0 +1,691 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file wrapper.hpp + */ + +#ifndef PROXSUITE_OSQP_DENSE_WRAPPER_HPP +#define PROXSUITE_OSQP_DENSE_WRAPPER_HPP + +#include "proxsuite/osqp/dense/aliases.hpp" +#include +#include + +namespace proxsuite { +namespace osqp { +namespace dense { + +/// +/// @brief This class defines the API of OSQP solver with dense backend. +/// +template +struct QP : common::dense::QPBase, T> +{ +private: + using Base = common::dense::QPBase, T>; + +public: + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QP(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + HessianType _hessian_type, + DenseBackend _dense_backend) + : Base(_dim, _n_eq, _n_in, _box_constraints, _hessian_type, _dense_backend) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QP(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + DenseBackend _dense_backend, + HessianType _hessian_type) + : Base(_dim, _n_eq, _n_in, _box_constraints, _dense_backend, _hessian_type) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + */ + QP(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + HessianType _hessian_type) + : Base(_dim, + _n_eq, + _n_in, + _box_constraints, + _hessian_type, + DenseBackend::PrimalDualLDLT) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type problem type (QP, LP, DIAGONAL) + * @param _box_constraints specify that there are (or not) box constraints. + * @param _dense_backend specify which factorization is used. + */ + QP(isize _dim, + isize _n_eq, + isize _n_in, + bool _box_constraints, + DenseBackend _dense_backend) + : Base(_dim, _n_eq, _n_in, _box_constraints, _dense_backend) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _box_constraints specify that there are (or not) box constraints. + */ + QP(isize _dim, isize _n_eq, isize _n_in, bool _box_constraints) + : Base(_dim, + _n_eq, + _n_in, + _box_constraints, + HessianType::Dense, + DenseBackend::PrimalDualLDLT) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + * @param _hessian_type specify that there are (or not) box constraints. + */ + QP(isize _dim, isize _n_eq, isize _n_in, HessianType _hessian_type) + : Base(_dim, + _n_eq, + _n_in, + false, + _hessian_type, + DenseBackend::PrimalDualLDLT) + { + } + /*! + * Default constructor using QP model dimensions. + * @param _dim primal variable dimension. + * @param _n_eq number of equality constraints. + * @param _n_in number of inequality constraints. + */ + QP(isize _dim, isize _n_eq, isize _n_in) + : Base(_dim, + _n_eq, + _n_in, + false, + HessianType::Dense, + DenseBackend::PrimalDualLDLT) + { + } + /*! + * Initialize OSQP-specific settings. + */ + void init_derived_settings() + { + this->settings.default_mu_eq = 1.E-2; + this->settings.default_mu_in = 1.E1; + + this->settings.alpha_bcl = 0.1; + this->settings.beta_bcl = 0.9; + this->settings.refactor_dual_feasibility_threshold = 1e-2; + this->settings.refactor_rho_threshold = 1E-7; + + this->settings.mu_min_eq = 1E-9; + this->settings.mu_min_in = 1E-6; + this->settings.mu_max_eq_inv = 1E9; + this->settings.mu_max_in_inv = 1E6; + + this->settings.mu_update_factor = 0.1; + this->settings.mu_update_inv_factor = 10; + this->settings.cold_reset_mu_eq = 1. / 1.1; + this->settings.cold_reset_mu_in = 1. / 1.1; + this->settings.cold_reset_mu_eq_inv = 1.1; + this->settings.cold_reset_mu_in_inv = 1.1; + + this->settings.eps_abs = 1.E-3; + this->settings.eps_rel = 1.E-3; + this->settings.max_iter = 4000; + this->settings.max_iter_in = 1500; + this->settings.safe_guard = 1.E4; + this->settings.nb_iterative_refinement = 10; + this->settings.eps_refact = 1.E-6; + + this->settings.verbose = false; + this->settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + this->settings.update_preconditioner = false; + this->settings.compute_preconditioner = true; + this->settings.compute_timings = false; + + this->settings.check_duality_gap = false; + this->settings.eps_duality_gap_abs = 1.E-3; + this->settings.eps_duality_gap_rel = 1.E-3; + + this->settings.preconditioner_max_iter = 10; + this->settings.preconditioner_accuracy = 1.E-3; + this->settings.primal_infeasibility_solving = false; + this->settings.eps_primal_inf = 1.E-4; + this->settings.eps_dual_inf = 1.E-4; + this->settings.bcl_update = true; + this->settings.merit_function_type = MeritFunctionType::GPDAL; + this->settings.alpha_gpdal = 0.95; + + this->settings.check_solved_option = CheckSolvedStatus::ITERATION_BASED; + this->settings.check_termination = 25; + this->settings.frequence_infeasibility_check = 1; + + this->settings.sparse_backend = SparseBackend::Automatic; + this->settings.default_H_eigenvalue_estimate = 0.; + + this->settings.alpha = 1.6; + this->settings.mu_max_eq = 1E3; + this->settings.mu_max_in = 1E6; + this->settings.mu_min_eq_inv = 1E-3; + this->settings.mu_min_in_inv = 1E-6; + this->settings.adaptive_mu = true; + this->settings.adaptive_mu_interval = 50; + this->settings.adaptive_mu_tolerance = 5.; + this->settings.polish = false; + this->settings.delta = 1E-6; + this->settings.polish_refine_iter = 3; + } + /*! + * Initialize OSQP-specific results. + */ + void init_derived_results() + { + this->results.info.mu_eq = 1E-2; + this->results.info.mu_in = 1E1; + this->results.info.mu_eq_inv = 1E2; + this->results.info.mu_in_inv = 1E-1; + } + /*! + * OSQP-specific solve implementation. + * Calls the OSQP algorithm. + */ + void solve_implem() + { + qp_solve( // + this->settings, + this->model, + this->results, + this->work, + this->is_box_constrained(), + this->which_dense_backend(), + this->which_hessian_type(), + this->ruiz); + } +}; + +/// +/// @brief This class defines the OSQP default parameter +/// configuration of the function osqp::dense::solve. +/// +template +struct OSQPConfig +{ + optional eps_abs; + optional eps_rel; + optional rho; + optional mu_eq; + optional mu_in; + optional verbose; + bool compute_preconditioner; + bool compute_timings; + optional max_iter; + InitialGuessStatus initial_guess; + bool check_duality_gap; + optional eps_duality_gap_abs; + optional eps_duality_gap_rel; + bool primal_infeasibility_solving; + optional manual_minimal_H_eigenvalue; + bool adaptive_mu; + optional adaptive_mu_interval; + optional adaptive_mu_tolerance; + bool polish; + optional delta; + optional polish_refine_iter; + + OSQPConfig( + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt, + bool adaptive_mu = true, + optional adaptive_mu_interval = nullopt, + optional adaptive_mu_tolerance = nullopt, + bool polish = false, + optional delta = nullopt, + optional polish_refine_iter = nullopt) + : eps_abs(eps_abs) + , eps_rel(eps_rel) + , rho(rho) + , mu_eq(mu_eq) + , mu_in(mu_in) + , verbose(verbose) + , compute_preconditioner(compute_preconditioner) + , compute_timings(compute_timings) + , max_iter(max_iter) + , initial_guess(initial_guess) + , check_duality_gap(check_duality_gap) + , eps_duality_gap_abs(eps_duality_gap_abs) + , eps_duality_gap_rel(eps_duality_gap_rel) + , primal_infeasibility_solving(primal_infeasibility_solving) + , manual_minimal_H_eigenvalue(manual_minimal_H_eigenvalue) + , adaptive_mu(adaptive_mu) + , adaptive_mu_interval(adaptive_mu_interval) + , adaptive_mu_tolerance(adaptive_mu_tolerance) + , polish(polish) + , delta(delta) + , polish_refine_iter(polish_refine_iter) + { + } + /*! + * OSQP settings initialization. + */ + void init_derived_settings(Settings& settings) const + { + settings.initial_guess = initial_guess; + settings.check_duality_gap = check_duality_gap; + settings.compute_timings = compute_timings; + settings.primal_infeasibility_solving = primal_infeasibility_solving; + settings.adaptive_mu = adaptive_mu; + settings.polish = polish; + + if (eps_abs != nullopt) { + settings.eps_abs = eps_abs.value(); + } + if (eps_rel != nullopt) { + settings.eps_rel = eps_rel.value(); + } + if (verbose != nullopt) { + settings.verbose = verbose.value(); + } + if (max_iter != nullopt) { + settings.max_iter = max_iter.value(); + } + if (eps_duality_gap_abs != nullopt) { + settings.eps_duality_gap_abs = eps_duality_gap_abs.value(); + } + if (eps_duality_gap_rel != nullopt) { + settings.eps_duality_gap_rel = eps_duality_gap_rel.value(); + } + if (adaptive_mu_interval != nullopt) { + settings.adaptive_mu_interval = adaptive_mu_interval.value(); + } + if (adaptive_mu_tolerance != nullopt) { + settings.adaptive_mu_tolerance = adaptive_mu_tolerance.value(); + } + if (delta != nullopt) { + settings.delta = delta.value(); + } + if (polish_refine_iter != nullopt) { + settings.polish_refine_iter = polish_refine_iter.value(); + } + } + /*! + * Call to init() from QPBase without box constraints. + */ + void init_qp(QP& qp, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u) const + { + if (manual_minimal_H_eigenvalue != nullopt) { + qp.init(H, + g, + A, + b, + C, + l, + u, + compute_preconditioner, + rho, + mu_eq, + mu_in, + manual_minimal_H_eigenvalue.value()); + } else { + qp.init(H, + g, + A, + b, + C, + l, + u, + compute_preconditioner, + rho, + mu_eq, + mu_in, + nullopt); + } + } + /*! + * Call to QPBase init() without box constraints. + */ + void init_qp_box(QP& qp, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box) const + { + if (manual_minimal_H_eigenvalue != nullopt) { + qp.init(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + compute_preconditioner, + rho, + mu_eq, + mu_in, + manual_minimal_H_eigenvalue.value()); + } else { + qp.init(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + compute_preconditioner, + rho, + mu_eq, + mu_in, + nullopt); + } + } +}; + +/*! + * Solves the QP problem using OSQP algorithm without the need to define a QP + * object, with matrices defined by Dense Eigen matrices. It is possible to + * set up some of the solver parameters (warm start, initial guess option, + * proximal step sizes, absolute and relative accuracies, maximum number of + * iterations, preconditioner execution). There are no box constraints in the + * model. + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param x primal warm start. + * @param y dual equality constraint warm start. + * @param z dual inequality constraint warm start. + * @param verbose if set to true, the solver prints more information about + * each iteration. + * @param compute_preconditioner bool parameter for executing or not the + * preconditioner. + * @param compute_timings boolean parameter for computing the solver timings. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param eps_abs absolute accuracy threshold. + * @param eps_rel relative accuracy threshold. + * @param max_iter maximum number of iteration. + * @param initial_guess initial guess option for warm starting or not the + * initial iterate values. + * @param check_duality_gap If set to true, include the duality gap in + * absolute and relative stopping criteria. + * @param eps_duality_gap_abs absolute accuracy threshold for the duality-gap + * criterion. + * @param eps_duality_gap_rel relative accuracy threshold for the duality-gap + * criterion. + * @param adaptive_mu if set to true, perform updates of mu. + * @param adaptive_mu_interval minimum interval between to mu update iterations. + * @param adaptive_mu_tolerance tolerance on the ratio of residuals in mu + * update. + * @param polish if set to true, perform solution polishing. + * @param delta regularisation parameter in solution polishing. + * @param polish_refine_iter number of iterations in polishing refinement + * procedure. + */ +template +Results +solve(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt, + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt, + bool adaptive_mu = true, + optional adaptive_mu_interval = nullopt, + optional adaptive_mu_tolerance = nullopt, + bool polish = false, + optional delta = nullopt, + optional polish_refine_iter = nullopt) +{ + OSQPConfig config(eps_abs, + eps_rel, + rho, + mu_eq, + mu_in, + verbose, + compute_preconditioner, + compute_timings, + max_iter, + initial_guess, + check_duality_gap, + eps_duality_gap_abs, + eps_duality_gap_rel, + primal_infeasibility_solving, + manual_minimal_H_eigenvalue, + adaptive_mu, + adaptive_mu_interval, + adaptive_mu_tolerance, + polish, + delta, + polish_refine_iter); + + return common::dense::solve_base, OSQPConfig, T>( + config, H, g, A, b, C, l, u, x, y, z); +} +/*! + * Solves the QP problem using OSQP algorithm without the need to define a QP + * object, with matrices defined by Dense Eigen matrices. It is possible to + * set up some of the solver parameters (warm start, initial guess option, + * proximal step sizes, absolute and relative accuracies, maximum number of + * iterations, preconditioner execution). + * @param H quadratic cost input defining the QP model. + * @param g linear cost input defining the QP model. + * @param A equality constraint matrix input defining the QP model. + * @param b equality constraint vector input defining the QP model. + * @param C inequality constraint matrix input defining the QP model. + * @param l lower inequality constraint vector input defining the QP model. + * @param u upper inequality constraint vector input defining the QP model. + * @param l_box lower box inequality constraint vector input defining the QP + * model. + * @param u_box upper box inequality constraint vector input defining the QP + * model. + * @param x primal warm start. + * @param y dual equality constraint warm start. + * @param z dual inequality constraint warm start. The upper part must contain + * a warm start for inequality constraints wrt C matrix, whereas the latter + * wrt the box inequalities. + * @param verbose if set to true, the solver prints more information about + * each iteration. + * @param compute_preconditioner bool parameter for executing or not the + * preconditioner. + * @param compute_timings boolean parameter for computing the solver timings. + * @param rho proximal step size wrt primal variable. + * @param mu_eq proximal step size wrt equality constrained multiplier. + * @param mu_in proximal step size wrt inequality constrained multiplier. + * @param eps_abs absolute accuracy threshold. + * @param eps_rel relative accuracy threshold. + * @param max_iter maximum number of iteration. + * @param initial_guess initial guess option for warm starting or not the + * initial iterate values. + * @param check_duality_gap If set to true, include the duality gap in + * absolute and relative stopping criteria. + * @param eps_duality_gap_abs absolute accuracy threshold for the duality-gap + * criterion. + * @param eps_duality_gap_rel relative accuracy threshold for the duality-gap + * criterion. + * @param adaptive_mu if set to true, perform updates of mu. + * @param adaptive_mu_interval minimum interval between to mu update iterations. + * @param adaptive_mu_tolerance tolerance on the ratio of residuals in mu + * update. + * @param polish if set to true, perform solution polishing. + * @param delta regularisation parameter in solution polishing. + * @param polish_refine_iter number of iterations in polishing refinement + * procedure. + */ +template +Results +solve(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt, + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt, + bool adaptive_mu = true, + optional adaptive_mu_interval = nullopt, + optional adaptive_mu_tolerance = nullopt, + bool polish = false, + optional delta = nullopt, + optional polish_refine_iter = nullopt) +{ + OSQPConfig config(eps_abs, + eps_rel, + rho, + mu_eq, + mu_in, + verbose, + compute_preconditioner, + compute_timings, + max_iter, + initial_guess, + check_duality_gap, + eps_duality_gap_abs, + eps_duality_gap_rel, + primal_infeasibility_solving, + manual_minimal_H_eigenvalue, + adaptive_mu, + adaptive_mu_interval, + adaptive_mu_tolerance, + polish, + delta, + polish_refine_iter); + + return common::dense::solve_base_box, OSQPConfig, T>( + config, H, g, A, b, C, l, u, l_box, u_box, x, y, z); +} + +template +bool +operator==(const QP& qp1, const QP& qp2) +{ + bool value = qp1.model == qp2.model && qp1.settings == qp2.settings && + qp1.results == qp2.results && + qp1.is_box_constrained() == qp2.is_box_constrained(); + return value; +} + +template +bool +operator!=(const QP& qp1, const QP& qp2) +{ + return !(qp1 == qp2); +} + +} // namespace dense +} // namespace osqp +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_OSQP_DENSE_WRAPPER_HPP */ diff --git a/include/proxsuite/proxqp/dense/aliases.hpp b/include/proxsuite/proxqp/dense/aliases.hpp new file mode 100644 index 000000000..3accf31dd --- /dev/null +++ b/include/proxsuite/proxqp/dense/aliases.hpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file aliases.hpp + */ + +#ifndef PROXSUITE_PROXQP_DENSE_ALIASES_HPP +#define PROXSUITE_PROXQP_DENSE_ALIASES_HPP + +#include "proxsuite/common/solvers.hpp" +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/dense/model.hpp" +#include "proxsuite/common/dense/workspace.hpp" + +namespace proxsuite { +namespace proxqp { +namespace dense { + +using proxsuite::common::from_eigen; +using proxsuite::common::i32; +using proxsuite::common::i64; +using proxsuite::common::isize; +using proxsuite::common::dense::infty_norm; + +using proxsuite::common::CheckSolvedStatus; +using proxsuite::common::DenseBackend; +using proxsuite::common::HessianType; +using proxsuite::common::InitialGuessStatus; +using proxsuite::common::MeritFunctionType; +using proxsuite::common::PolishOutput; +using proxsuite::common::PreconditionerStatus; +using proxsuite::common::QPSolver; +using proxsuite::common::QPSolverOutput; +using proxsuite::common::SparseBackend; +using proxsuite::common::Timer; + +using proxsuite::common::Results; +using proxsuite::common::Settings; +using proxsuite::common::dense::Model; +using proxsuite::common::dense::Workspace; + +using proxsuite::common::VectorView; +using proxsuite::common::VectorViewMut; +using proxsuite::common::dense::Mat; +using proxsuite::common::dense::MatRef; +using proxsuite::common::dense::Vec; +using proxsuite::common::dense::VecRef; + +} // namespace dense +} // namespace proxqp +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_PROXQP_DENSE_ALIASES_HPP */ diff --git a/include/proxsuite/proxqp/dense/compute_ECJ.hpp b/include/proxsuite/proxqp/dense/compute_ECJ.hpp index 0413758e3..e8222c1db 100644 --- a/include/proxsuite/proxqp/dense/compute_ECJ.hpp +++ b/include/proxsuite/proxqp/dense/compute_ECJ.hpp @@ -35,7 +35,7 @@ compute_backward(dense::QP& solved_qp, T mu_new = 1.E-6) { bool check = - solved_qp.results.info.status == QPSolverOutput::PROXQP_DUAL_INFEASIBLE; + solved_qp.results.info.status == QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE; if (check) { PROXSUITE_THROW_PRETTY( true, @@ -69,12 +69,11 @@ compute_backward(dense::QP& solved_qp, // so in order to avoid to much refactorization later in the // iterative refinement, a factorization from scratch is directly // performed with new mu and rho as well to enable more stability - proxsuite::proxqp::dense::setup_factorization( - solved_qp.work, - solved_qp.model, - solved_qp.results, - solved_qp.which_dense_backend(), - solved_qp.which_hessian_type()); + common::dense::setup_factorization(solved_qp.work, + solved_qp.model, + solved_qp.results, + solved_qp.which_dense_backend(), + solved_qp.which_hessian_type()); solved_qp.work.n_c = 0; for (isize i = 0; i < solved_qp.model.n_in; i++) { solved_qp.work.current_bijection_map(i) = i; @@ -110,7 +109,7 @@ compute_backward(dense::QP& solved_qp, from_eigen, solved_qp.work.rhs.tail(solved_qp.model.n_in) }); } } - iterative_solve_with_permut_fact( // + common::dense::iterative_solve_with_permut_fact( // solved_qp.settings, solved_qp.model, solved_qp.results, diff --git a/include/proxsuite/proxqp/dense/dense.hpp b/include/proxsuite/proxqp/dense/dense.hpp old mode 100755 new mode 100644 index 7a0f6d767..e6939d4a5 --- a/include/proxsuite/proxqp/dense/dense.hpp +++ b/include/proxsuite/proxqp/dense/dense.hpp @@ -1,7 +1,10 @@ // // Copyright (c) 2022 INRIA // -/** \file */ +/** + * @file dense.hpp + */ + #ifndef PROXSUITE_PROXQP_DENSE_DENSE_HPP #define PROXSUITE_PROXQP_DENSE_DENSE_HPP diff --git a/include/proxsuite/proxqp/dense/helpers.hpp b/include/proxsuite/proxqp/dense/helpers.hpp deleted file mode 100644 index b41b649a8..000000000 --- a/include/proxsuite/proxqp/dense/helpers.hpp +++ /dev/null @@ -1,768 +0,0 @@ -// -// Copyright (c) 2022-2023 INRIA -// -/** - * @file helpers.hpp - */ - -#ifndef PROXSUITE_PROXQP_DENSE_HELPERS_HPP -#define PROXSUITE_PROXQP_DENSE_HELPERS_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace proxsuite { -namespace proxqp { -namespace dense { - -template -T -power_iteration(const Eigen::MatrixBase& H, - const Eigen::MatrixBase& dw, - const Eigen::MatrixBase& rhs, - const Eigen::MatrixBase& err_v, - T power_iteration_accuracy, - isize nb_power_iteration) -{ - auto& dw_cc = dw.const_cast_derived(); - auto& rhs_cc = rhs.const_cast_derived(); - auto& err_v_cc = err_v.const_cast_derived(); - // computes maximal eigen value of the bottom right matrix of the LDLT - isize dim = H.rows(); - rhs_cc.setZero(); - // stores eigenvector - rhs_cc.array() += 1. / std::sqrt(dim); - // stores Hx - dw_cc.noalias() = H.template selfadjointView() * rhs_cc; // Hx - T eig = 0; - for (isize i = 0; i < nb_power_iteration; i++) { - - rhs_cc = dw_cc / dw_cc.norm(); - dw_cc.noalias() = (H.template selfadjointView() * rhs_cc); - // calculate associated eigenvalue - eig = rhs.dot(dw_cc); - // calculate associated error - err_v_cc = dw_cc - eig * rhs_cc; - T err = proxsuite::proxqp::dense::infty_norm(err_v_cc); - // std::cout << "power iteration max: i " << i << " err " << err << - // std::endl; - if (err <= power_iteration_accuracy) { - break; - } - } - return eig; -} -template -T -min_eigen_value_via_modified_power_iteration( - const Eigen::MatrixBase& H, - const Eigen::MatrixBase& dw, - const Eigen::MatrixBase& rhs, - const Eigen::MatrixBase& err_v, - T max_eigen_value, - T power_iteration_accuracy, - isize nb_power_iteration) -{ - // performs power iteration on the matrix: max_eigen_value I - H - // estimates then the minimal eigenvalue with: minimal_eigenvalue = - // max_eigen_value - eig - auto& dw_cc = dw.const_cast_derived(); - auto& rhs_cc = rhs.const_cast_derived(); - auto& err_v_cc = err_v.const_cast_derived(); - isize dim = H.rows(); - rhs_cc.setZero(); - // stores eigenvector - rhs_cc.array() += 1. / std::sqrt(dim); - // stores Hx - dw_cc.noalias() = - -(H.template selfadjointView() * rhs_cc); // Hx - dw_cc += max_eigen_value * rhs_cc; - T eig = 0; - for (isize i = 0; i < nb_power_iteration; i++) { - - rhs_cc = dw_cc / dw_cc.norm(); - dw_cc.noalias() = -(H.template selfadjointView() * rhs_cc); - dw_cc += max_eigen_value * rhs_cc; - // calculate associated eigenvalue - eig = rhs_cc.dot(dw_cc); - // calculate associated error - err_v_cc = dw_cc - eig * rhs_cc; - T err = proxsuite::proxqp::dense::infty_norm(err_v_cc); - // std::cout << "power iteration min: i " << i << " err " << err << - // std::endl; - if (err <= power_iteration_accuracy) { - break; - } - } - T minimal_eigenvalue = max_eigen_value - eig; - return minimal_eigenvalue; -} -/////// SETUP //////// -/*! - * Estimate minimal eigenvalue of a symmetric Matrix - * @param H symmetric matrix. - * @param EigenValueEstimateMethodOption - * @param power_iteration_accuracy power iteration algorithm accuracy tracked - * @param nb_power_iteration maximal number of power iteration executed - * - */ -template -T -estimate_minimal_eigen_value_of_symmetric_matrix( - const Eigen::MatrixBase& H, - EigenValueEstimateMethodOption estimate_method_option, - T power_iteration_accuracy, - isize nb_power_iteration) -{ - PROXSUITE_THROW_PRETTY( - (!H.isApprox(H.transpose(), std::numeric_limits::epsilon())), - std::invalid_argument, - "H is not symmetric."); - if (H.size()) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.rows(), - H.cols(), - "H has a number of rows different of the number of columns."); - } - isize dim = H.rows(); - T res(0.); - switch (estimate_method_option) { - case EigenValueEstimateMethodOption::PowerIteration: { - Vec dw(dim); - Vec rhs(dim); - Vec err(dim); - T dominant_eigen_value = power_iteration( - H, dw, rhs, err, power_iteration_accuracy, nb_power_iteration); - T min_eigenvalue = - min_eigen_value_via_modified_power_iteration(H, - dw, - rhs, - err, - dominant_eigen_value, - power_iteration_accuracy, - nb_power_iteration); - res = std::min(min_eigenvalue, dominant_eigen_value); - } break; - case EigenValueEstimateMethodOption::ExactMethod: { - Eigen::SelfAdjointEigenSolver> es(H, Eigen::EigenvaluesOnly); - res = T(es.eigenvalues()[0]); - } break; - } - return res; -} -/////// SETUP //////// -/*! - * Estimate H minimal eigenvalue - * @param settings solver settings - * @param results solver results. - * @param manual_minimal_H_eigenvalue minimal H eigenvalue estimate. - */ -template -void -update_default_rho_with_minimal_Hessian_eigen_value( - optional manual_minimal_H_eigenvalue, - Results& results, - Settings& settings) -{ - if (manual_minimal_H_eigenvalue != nullopt) { - settings.default_H_eigenvalue_estimate = - manual_minimal_H_eigenvalue.value(); - results.info.minimal_H_eigenvalue_estimate = - settings.default_H_eigenvalue_estimate; - } - settings.default_rho += std::abs(results.info.minimal_H_eigenvalue_estimate); - results.info.rho = settings.default_rho; -} -/*! - * Computes the equality constrained initial guess of a QP problem. - * - * @param qpwork workspace of the solver. - * @param qpsettings settings of the solver. - * @param qpmodel QP problem as defined by the user (without any scaling - * performed). - * @param qpresults solution results. - */ -template -void -compute_equality_constrained_initial_guess(Workspace& qpwork, - const Settings& qpsettings, - const Model& qpmodel, - const isize n_constraints, - const DenseBackend& dense_backend, - const HessianType& hessian_type, - Results& qpresults) -{ - - qpwork.rhs.setZero(); - qpwork.rhs.head(qpmodel.dim) = -qpwork.g_scaled; - qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq) = qpwork.b_scaled; - iterative_solve_with_permut_fact( // - qpsettings, - qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - hessian_type, - T(1), - qpmodel.dim + qpmodel.n_eq); - - qpresults.x = qpwork.dw_aug.head(qpmodel.dim); - qpresults.y = qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq); - qpwork.dw_aug.setZero(); - qpwork.rhs.setZero(); -} - -/*! - * Setups and performs the first factorization of the regularized KKT matrix of - * the problem. - * - * @param qpwork workspace of the solver. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpresults solution results. - */ -template -void -setup_factorization(Workspace& qpwork, - const Model& qpmodel, - Results& qpresults, - const DenseBackend& dense_backend, - const HessianType& hessian_type) -{ - - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, - qpwork.ldl_stack.as_mut(), - }; - switch (hessian_type) { - case HessianType::Dense: - qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; - break; - case HessianType::Zero: - qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim).setZero(); - break; - case HessianType::Diagonal: - qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim) = qpwork.H_scaled; - break; - } - qpwork.kkt.topLeftCorner(qpmodel.dim, qpmodel.dim).diagonal().array() += - qpresults.info.rho; - switch (dense_backend) { - case DenseBackend::PrimalDualLDLT: - qpwork.kkt.block(0, qpmodel.dim, qpmodel.dim, qpmodel.n_eq) = - qpwork.A_scaled.transpose(); - qpwork.kkt.block(qpmodel.dim, 0, qpmodel.n_eq, qpmodel.dim) = - qpwork.A_scaled; - qpwork.kkt.bottomRightCorner(qpmodel.n_eq, qpmodel.n_eq).setZero(); - qpwork.kkt.diagonal() - .segment(qpmodel.dim, qpmodel.n_eq) - .setConstant(-qpresults.info.mu_eq); - qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); - break; - case DenseBackend::PrimalLDLT: - qpwork.kkt.noalias() += qpresults.info.mu_eq_inv * - (qpwork.A_scaled.transpose() * qpwork.A_scaled); - qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); - break; - case DenseBackend::Automatic: - break; - } -} -/*! - * Performs the equilibration of the QP problem for reducing its - * ill-conditionness. - * - * @param qpwork workspace of the solver. - * @param qpsettings settings of the solver. - * @param ruiz ruiz preconditioner. - * @param execute_preconditioner boolean variable for executing or not the ruiz - * preconditioner. If set to False, it uses the previous preconditioning - * variables (initialized to the identity preconditioner if it is the first - * scaling performed). - */ -template -void -setup_equilibration(Workspace& qpwork, - const Settings& qpsettings, - const bool box_constraints, - const HessianType hessian_type, - preconditioner::RuizEquilibration& ruiz, - bool execute_preconditioner) -{ - - QpViewBoxMut qp_scaled{ - { from_eigen, qpwork.H_scaled }, { from_eigen, qpwork.g_scaled }, - { from_eigen, qpwork.A_scaled }, { from_eigen, qpwork.b_scaled }, - { from_eigen, qpwork.C_scaled }, { from_eigen, qpwork.u_scaled }, - { from_eigen, qpwork.l_scaled }, { from_eigen, qpwork.i_scaled }, - { from_eigen, qpwork.l_box_scaled }, { from_eigen, qpwork.u_box_scaled }, - }; - - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, - qpwork.ldl_stack.as_mut(), - }; - ruiz.scale_qp_in_place(qp_scaled, - execute_preconditioner, - qpsettings.primal_infeasibility_solving, - qpsettings.preconditioner_max_iter, - qpsettings.preconditioner_accuracy, - hessian_type, - box_constraints, - stack); - qpwork.correction_guess_rhs_g = infty_norm(qpwork.g_scaled); -} - -/*! - * Setups the solver initial guess. - * - * @param qpwork solver workspace. - * @param qpsettings solver settings. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpresults solver results. - */ -template -void -initial_guess(Workspace& qpwork, - Settings& qpsettings, - Model& qpmodel, - Results& qpresults) -{ - - switch (qpsettings.initial_guess) { - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { - compute_equality_constrained_initial_guess( - qpwork, qpsettings, qpmodel, qpresults); - break; - } - } -} -/*! - * Updates the QP solver model. - * - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param qpwork solver workspace. - * @param qpsettings solver settings. - * @param qpmodel solver model. - * @param qpresults solver result. - */ - -template -void -update(optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> l_box, - optional> u_box, - Model& model, - Workspace& work, - const bool box_constraints) -{ - // check the model is valid - if (g != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE(g.value().size(), - model.dim, - "the dimension wrt the primal variable x " - "variable for updating g is not valid."); - } - if (b != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE(b.value().size(), - model.n_eq, - "the dimension wrt equality constrained " - "variables for updating b is not valid."); - } - if (u != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE(u.value().size(), - model.n_in, - "the dimension wrt inequality constrained " - "variables for updating u is not valid."); - } - if (l != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE(l.value().size(), - model.n_in, - "the dimension wrt inequality constrained " - "variables for updating l is not valid."); - } - if (H != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().rows(), - model.dim, - "the row dimension for updating H is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().cols(), - model.dim, - "the column dimension for updating H is not valid."); - } - if (A != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().rows(), - model.n_eq, - "the row dimension for updating A is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().cols(), - model.dim, - "the column dimension for updating A is not valid."); - } - if (C != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().rows(), - model.n_in, - "the row dimension for updating C is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().cols(), - model.dim, - "the column dimension for updating C is not valid."); - } - - // update the model - if (g != nullopt) { - model.g = g.value().eval(); - } - if (b != nullopt) { - model.b = b.value().eval(); - } - if (u != nullopt) { - model.u = u.value().eval(); - } - if (l != nullopt) { - model.l = l.value().eval(); - } - if (u_box != nullopt && box_constraints) { - model.u_box = u_box.value(); - } // else qpmodel.u_box remains initialized to a matrix with zero elements or - // zero shape - - if (l_box != nullopt && box_constraints) { - model.l_box = l_box.value(); - } // else qpmodel.l_box remains initialized to a matrix with zero elements or - // zero shape - - if (H != nullopt || A != nullopt || C != nullopt) { - work.refactorize = true; - } - - if (H != nullopt) { - model.H = H.value(); - } - if (A != nullopt) { - model.A = A.value(); - } - if (C != nullopt) { - model.C = C.value(); - } - assert(model.is_valid(box_constraints)); -} -/*! - * Setups the QP solver model. - * - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param qpwork solver workspace. - * @param qpsettings solver settings. - * @param qpmodel solver model. - * @param qpresults solver result. - * @param ruiz ruiz preconditioner. - * @param preconditioner_status bool variable for deciding whether executing the - * preconditioning algorithm, or keeping previous preconditioning variables, or - * using the identity preconditioner (i.e., no preconditioner). - */ -template -void -setup( // - optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> l_box, - optional> u_box, - Settings& qpsettings, - Model& qpmodel, - Workspace& qpwork, - Results& qpresults, - const bool box_constraints, - preconditioner::RuizEquilibration& ruiz, - PreconditionerStatus preconditioner_status, - const HessianType hessian_type) -{ - - switch (qpsettings.initial_guess) { - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { - if (qpwork.proximal_parameter_update) { - qpresults.cleanup_all_except_prox_parameters(); - } else { - qpresults.cleanup(qpsettings); - } - qpwork.cleanup(box_constraints); - break; - } - case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { - // keep solutions but restart workspace and results - if (qpwork.proximal_parameter_update) { - qpresults.cleanup_statistics(); - } else { - qpresults.cold_start(qpsettings); - } - qpwork.cleanup(box_constraints); - break; - } - case InitialGuessStatus::NO_INITIAL_GUESS: { - if (qpwork.proximal_parameter_update) { - qpresults.cleanup_all_except_prox_parameters(); - } else { - qpresults.cleanup(qpsettings); - } - qpwork.cleanup(box_constraints); - break; - } - case InitialGuessStatus::WARM_START: { - if (qpwork.proximal_parameter_update) { - qpresults - .cleanup_all_except_prox_parameters(); // the warm start is given at - // the solve function - } else { - qpresults.cleanup(qpsettings); - } - qpwork.cleanup(box_constraints); - break; - } - case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { - if (qpwork.refactorize || qpwork.proximal_parameter_update) { - qpwork.cleanup(box_constraints); // meaningful for when there is an - // upate of the model and one wants to - // warm start with previous result - qpwork.refactorize = true; - } - qpresults.cleanup_statistics(); - break; - } - } - if (H != nullopt) { - qpmodel.H = H.value(); - } // else qpmodel.H remains initialzed to a matrix with zero elements - if (g != nullopt) { - qpmodel.g = g.value(); - } - - if (A != nullopt) { - qpmodel.A = A.value(); - } // else qpmodel.A remains initialized to a matrix with zero elements or zero - // shape - - if (b != nullopt) { - qpmodel.b = b.value(); - } // else qpmodel.b remains initialized to a matrix with zero elements or zero - // shape - - if (C != nullopt) { - qpmodel.C = C.value(); - } // else qpmodel.C remains initialized to a matrix with zero elements or zero - // shape - - if (u != nullopt) { - qpmodel.u = u.value(); - } // else qpmodel.u remains initialized to a matrix with zero elements or zero - // shape - - if (l != nullopt) { - qpmodel.l = l.value(); - } // else qpmodel.l remains initialized to a matrix with zero elements or zero - // shape - if (u_box != nullopt) { - qpmodel.u_box = u_box.value(); - } // else qpmodel.u_box remains initialized to a matrix with zero elements or - // zero shape - - if (l_box != nullopt) { - qpmodel.l_box = l_box.value(); - } // else qpmodel.l_box remains initialized to a matrix with zero elements or - // zero shape - assert(qpmodel.is_valid(box_constraints)); - switch (hessian_type) { - case HessianType::Zero: - break; - case HessianType::Dense: - qpwork.H_scaled = qpmodel.H; - break; - case HessianType::Diagonal: - qpwork.H_scaled = qpmodel.H; - break; - } - qpwork.g_scaled = qpmodel.g; - qpwork.A_scaled = qpmodel.A; - qpwork.b_scaled = qpmodel.b; - qpwork.C_scaled = qpmodel.C; - qpwork.u_scaled = - (qpmodel.u.array() <= T(1.E20)) - .select(qpmodel.u, - Eigen::Matrix::Zero(qpmodel.n_in).array() + - T(1.E20)); - qpwork.l_scaled = - (qpmodel.l.array() >= T(-1.E20)) - .select(qpmodel.l, - Eigen::Matrix::Zero(qpmodel.n_in).array() - - T(1.E20)); - if (box_constraints) { - qpwork.u_box_scaled = - (qpmodel.u_box.array() <= T(1.E20)) - .select(qpmodel.u_box, - Eigen::Matrix::Zero(qpmodel.dim).array() + - T(1.E20)); - qpwork.l_box_scaled = - (qpmodel.l_box.array() >= T(-1.E20)) - .select(qpmodel.l_box, - Eigen::Matrix::Zero(qpmodel.dim).array() - - T(1.E20)); - } - - qpwork.dual_feasibility_rhs_2 = infty_norm(qpmodel.g); - switch (preconditioner_status) { - case PreconditionerStatus::EXECUTE: - setup_equilibration( - qpwork, qpsettings, box_constraints, hessian_type, ruiz, true); - break; - case PreconditionerStatus::IDENTITY: - setup_equilibration( - qpwork, qpsettings, box_constraints, hessian_type, ruiz, false); - break; - case PreconditionerStatus::KEEP: - // keep previous one - setup_equilibration( - qpwork, qpsettings, box_constraints, hessian_type, ruiz, false); - break; - } -} -////// UPDATES /////// - -/*! - * Update the proximal parameters of the results object. - * - * @param rho_new primal proximal parameter. - * @param mu_eq_new dual equality proximal parameter. - * @param mu_in_new dual inequality proximal parameter. - * @param results solver results. - */ -template -void -update_proximal_parameters(Settings& settings, - Results& results, - Workspace& work, - optional rho_new, - optional mu_eq_new, - optional mu_in_new) -{ - - if (rho_new != nullopt) { - settings.default_rho = rho_new.value(); - results.info.rho = rho_new.value(); - work.proximal_parameter_update = true; - } - if (mu_eq_new != nullopt) { - settings.default_mu_eq = mu_eq_new.value(); - results.info.mu_eq = mu_eq_new.value(); - results.info.mu_eq_inv = T(1) / results.info.mu_eq; - work.proximal_parameter_update = true; - } - if (mu_in_new != nullopt) { - settings.default_mu_in = mu_in_new.value(); - results.info.mu_in = mu_in_new.value(); - results.info.mu_in_inv = T(1) / results.info.mu_in; - work.proximal_parameter_update = true; - } -} -/*! - * Warm start the primal and dual variables. - * - * @param x_wm primal warm start. - * @param y_wm dual equality warm start. - * @param z_wm dual inequality warm start. - * @param results solver result. - * @param settings solver settings. - */ -template -void -warm_start(optional> x_wm, - optional> y_wm, - optional> z_wm, - Results& results, - Settings& settings, - Model& model) -{ - if (x_wm == nullopt && y_wm == nullopt && z_wm == nullopt) - return; - - settings.initial_guess = InitialGuessStatus::WARM_START; - - // first check problem dimensions - if (x_wm != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - x_wm.value().rows(), - model.dim, - "the dimension wrt primal variable x for warm start is not valid."); - } - - if (y_wm != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE(y_wm.value().rows(), - model.n_eq, - "the dimension wrt equality constrained " - "variables for warm start is not valid."); - } - - if (z_wm != nullopt) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - z_wm.value().rows(), - model.n_in, - "the dimension wrt inequality constrained variables for warm start " - "is not valid."); - } - - if (x_wm != nullopt) { - results.x = x_wm.value().eval(); - } - - if (y_wm != nullopt) { - results.y = y_wm.value().eval(); - } - - if (z_wm != nullopt) { - results.z = z_wm.value().eval(); - } -} -} // namespace dense -} // namespace proxqp -} // namespace proxsuite - -#endif /* end of include guard PROXSUITE_PROXQP_DENSE_HELPERS_HPP */ diff --git a/include/proxsuite/proxqp/dense/linesearch.hpp b/include/proxsuite/proxqp/dense/linesearch.hpp index dcc7b525d..3de2c3a63 100644 --- a/include/proxsuite/proxqp/dense/linesearch.hpp +++ b/include/proxsuite/proxqp/dense/linesearch.hpp @@ -5,16 +5,14 @@ #ifndef PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP #define PROXSUITE_PROXQP_DENSE_LINESEARCH_HPP -#include "proxsuite/proxqp/dense/views.hpp" -#include "proxsuite/proxqp/dense/model.hpp" -#include "proxsuite/proxqp/results.hpp" -#include "proxsuite/proxqp/dense/workspace.hpp" -#include "proxsuite/proxqp/settings.hpp" +#include "proxsuite/proxqp/dense/aliases.hpp" #include + namespace proxsuite { namespace proxqp { namespace dense { namespace linesearch { + /// /// @brief This class stores the results of the primal-dual line-search. /// diff --git a/include/proxsuite/proxqp/dense/solver.hpp b/include/proxsuite/proxqp/dense/solver.hpp index 6d428ae64..ac4beeab8 100644 --- a/include/proxsuite/proxqp/dense/solver.hpp +++ b/include/proxsuite/proxqp/dense/solver.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022-2024 INRIA +// Copyright (c) 2022-2025 INRIA // /** * @file solver.hpp @@ -9,536 +9,19 @@ #define PROXSUITE_PROXQP_DENSE_SOLVER_HPP #include "proxsuite/fwd.hpp" -#include "proxsuite/proxqp/dense/views.hpp" +#include "proxsuite/proxqp/dense/aliases.hpp" +#include "proxsuite/common/dense/helpers.hpp" +#include "proxsuite/common/dense/utils.hpp" +#include "proxsuite/common/dense/prints.hpp" +#include "proxsuite/common/dense/iterative_solve.hpp" #include "proxsuite/proxqp/dense/linesearch.hpp" -#include "proxsuite/proxqp/dense/helpers.hpp" -#include "proxsuite/proxqp/dense/utils.hpp" -#include -#include #include -#include -#include -#include -#include #include namespace proxsuite { namespace proxqp { namespace dense { -/*! - * Performs a refactorization of the KKT matrix used by the solver. - * - * @param qpwork solver workspace. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpresults solver results. - * @param rho_new new primal proximal parameter used for the refactorization. - */ -template -void -refactorize(const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - const isize n_constraints, - const DenseBackend& dense_backend, - T rho_new) -{ - - if (!qpwork.constraints_changed && rho_new == qpresults.info.rho) { - return; - } - - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() - }; - switch (dense_backend) { - case DenseBackend::PrimalDualLDLT: { - qpwork.kkt.diagonal().head(qpmodel.dim).array() += - rho_new - qpresults.info.rho; - qpwork.kkt.diagonal().segment(qpmodel.dim, qpmodel.n_eq).array() = - -qpresults.info.mu_eq; - qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); - - isize n = qpmodel.dim; - isize n_eq = qpmodel.n_eq; - isize n_in = qpmodel.n_in; - isize n_c = qpwork.n_c; - - LDLT_TEMP_MAT(T, new_cols, n + n_eq + n_c, n_c, stack); - T mu_in_neg(-qpresults.info.mu_in); - for (isize i = 0; i < n_constraints; ++i) { - isize j = qpwork.current_bijection_map[i]; - if (j < n_c) { - auto col = new_cols.col(j); - if (i >= n_in) { - // I_scaled = D which is the diagonal matrix - // scaling x - // col(i-n_in) = ruiz.delta[i-qpmodel.n_in]; - col(i - n_in) = qpwork.i_scaled[i - qpmodel.n_in]; - } else { - col.head(n) = qpwork.C_scaled.row(i); - } - col.segment(n, n_eq + n_c).setZero(); - col(n + n_eq + j) = mu_in_neg; - } - } - qpwork.ldl.insert_block_at(n + n_eq, new_cols, stack); - } break; - case DenseBackend::PrimalLDLT: { - qpwork.kkt.noalias() = - qpwork.H_scaled + (qpwork.A_scaled.transpose() * qpwork.A_scaled) * - qpresults.info.mu_eq_inv; - qpwork.kkt.diagonal().array() += qpresults.info.rho; - for (isize i = 0; i < n_constraints; i++) { - if (qpwork.active_inequalities(i)) { - if (i >= qpmodel.n_in) { - // box constraints - qpwork.kkt(i - qpmodel.n_in, i - qpmodel.n_in) += - std::pow(qpwork.i_scaled(i - qpmodel.n_in), 2) * - qpresults.info.mu_in_inv; - } else { - // generic ineq constraint - qpwork.kkt.noalias() += qpwork.C_scaled.row(i).transpose() * - qpwork.C_scaled.row(i) * - qpresults.info.mu_in_inv; - } - } - } - qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); - } break; - case DenseBackend::Automatic: - break; - } - - qpwork.constraints_changed = false; -} - -/*! - * Updates the dual proximal parameters of the solver (i.e., penalization - * parameters of the primal-dual merit function). - * - * @param qpwork solver workspace. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpresults solver results. - * @param mu_eq_new new dual equality constrained proximal parameter. - * @param mu_in_new new dual inequality constrained proximal parameter. - */ -template -void -mu_update(const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - isize n_constraints, - const DenseBackend& dense_backend, - T mu_eq_new, - T mu_in_new) -{ - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() - }; - - isize n = qpmodel.dim; - isize n_eq = qpmodel.n_eq; - isize n_c = qpwork.n_c; - - if ((n_eq + n_c) == 0) { - return; - } - switch (dense_backend) { - case DenseBackend::PrimalDualLDLT: { - LDLT_TEMP_VEC_UNINIT(T, rank_update_alpha, n_eq + n_c, stack); - - rank_update_alpha.head(n_eq).setConstant(qpresults.info.mu_eq - - mu_eq_new); - rank_update_alpha.tail(n_c).setConstant(qpresults.info.mu_in - mu_in_new); - - { - auto _indices = stack.make_new_for_overwrite( - proxsuite::linalg::veg::Tag{}, n_eq + n_c); - isize* indices = _indices.ptr_mut(); - for (isize k = 0; k < n_eq; ++k) { - indices[k] = n + k; - } - for (isize k = 0; k < n_c; ++k) { - indices[n_eq + k] = n + n_eq + k; - } - qpwork.ldl.diagonal_update_clobber_indices( - indices, n_eq + n_c, rank_update_alpha, stack); - } - } break; - case DenseBackend::PrimalLDLT: { - // we refactorize there for the moment - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, - qpwork.ldl_stack.as_mut(), - }; - // qpwork.kkt.noalias() = qpwork.H_scaled + (qpwork.A_scaled.transpose() * - // qpwork.A_scaled) / mu_eq_new; qpwork.kkt.diagonal().array() += - // qpresults.info.rho; for (isize i = 0; i < n_constraints; i++){ - // if (qpwork.active_inequalities(i)){ - // if (i >=qpmodel.n_in){ - // // box constraints - // qpwork.kkt(i-qpmodel.n_in,i-qpmodel.n_in) += - // std::pow(qpwork.i_scaled(i-qpmodel.n_in),2) / mu_in_new ; - // } else{ - // // generic ineq constraint - // qpwork.kkt.noalias() += qpwork.C_scaled.row(i).transpose() * - // qpwork.C_scaled.row(i) / mu_in_new ; - // } - // } - // } - // qpwork.ldl.factorize(qpwork.kkt.transpose(), stack); - - // mu update for C_J - { - LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpwork.n_c, stack); - qpwork.dw_aug.head(qpmodel.dim).setOnes(); - T delta_mu(T(1) / mu_in_new - qpresults.info.mu_in_inv); - qpwork.dw_aug.head(qpmodel.dim).array() *= delta_mu; - for (isize i = 0; i < n_constraints; ++i) { - isize j = qpwork.current_bijection_map[i]; - if (j < n_c) { - auto col = new_cols.col(j); - if (i >= qpmodel.n_in) { - // box constraint - col.setZero(); - col[i - qpmodel.n_in] = qpwork.i_scaled[i - qpmodel.n_in]; - } else { - // generic ineq constraints - col = qpwork.C_scaled.row(i); - } - } - } - qpwork.ldl.rank_r_update( - new_cols, qpwork.dw_aug.head(qpwork.n_c), stack); - } - // mu update for A - { - LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpmodel.n_eq, stack); - qpwork.dw_aug.head(qpmodel.n_eq).setOnes(); - T delta_mu(1 / mu_eq_new - qpresults.info.mu_eq_inv); - qpwork.dw_aug.head(qpmodel.n_eq).array() *= delta_mu; - new_cols = qpwork.A_scaled.transpose(); - qpwork.ldl.rank_r_update( - new_cols, qpwork.dw_aug.head(qpmodel.n_eq), stack); - } - } break; - case DenseBackend::Automatic: - break; - } - qpwork.constraints_changed = true; -} -/*! - * Derives the residual of the iterative refinement algorithm used for solving - * associated linear systems of PROXQP algorithm. - * - * @param qpwork solver workspace. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpresults solver results. - * @param inner_pb_dim dimension of the linear system. - */ -template -void -iterative_residual(const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - const isize n_constraints, - isize inner_pb_dim, - const HessianType& hessian_type) -{ - auto& Hdx = qpwork.Hdx; - auto& Adx = qpwork.Adx; - auto& ATdy = qpwork.CTz; - qpwork.err.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); - switch (hessian_type) { - case HessianType::Zero: - break; - case HessianType::Dense: - Hdx.noalias() = qpwork.H_scaled.template selfadjointView() * - qpwork.dw_aug.head(qpmodel.dim); - qpwork.err.head(qpmodel.dim).noalias() -= Hdx; - break; - case HessianType::Diagonal: -#ifndef NDEBUG - PROXSUITE_THROW_PRETTY(!qpwork.H_scaled.isDiagonal(), - std::invalid_argument, - "H is not diagonal."); -#endif - Hdx.array() = qpwork.H_scaled.diagonal().array() * - qpwork.dw_aug.head(qpmodel.dim).array(); - qpwork.err.head(qpmodel.dim).noalias() -= Hdx; - break; - } - qpwork.err.head(qpmodel.dim) -= - qpresults.info.rho * qpwork.dw_aug.head(qpmodel.dim); - - // PERF: fuse {A, C}_scaled multiplication operations - ATdy.noalias() = qpwork.A_scaled.transpose() * - qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq); - qpwork.err.head(qpmodel.dim).noalias() -= ATdy; - if (n_constraints > qpmodel.n_in) { - // there are box constraints - qpwork.active_part_z.tail(qpmodel.dim) = qpwork.dw_aug.head(qpmodel.dim); - qpwork.active_part_z.tail(qpmodel.dim).array() *= qpwork.i_scaled.array(); - // ruiz.unscale_primal_in_place(VectorViewMut{from_eigen,qpwork.active_part_z.tail(qpmodel.dim)}); - } - for (isize i = 0; i < n_constraints; i++) { - isize j = qpwork.current_bijection_map(i); - if (j < qpwork.n_c) { - if (i >= qpmodel.n_in) { - // I_scaled * dz_box_scaled = unscale_primally(dz_box) - qpwork.err(i - qpmodel.n_in) -= - // qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * - // ruiz.delta(i-qpmodel.n_in); - qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * - qpwork.i_scaled(i - qpmodel.n_in); - // I_scaled * dx_scaled = dx_unscaled - qpwork.err(qpmodel.dim + qpmodel.n_eq + j) -= - (qpwork.active_part_z[i] - - qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * - qpresults.info.mu_in); - } else { - qpwork.err.head(qpmodel.dim).noalias() -= - qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * - qpwork.C_scaled.row(i); - qpwork.err(qpmodel.dim + qpmodel.n_eq + j) -= - (qpwork.C_scaled.row(i).dot(qpwork.dw_aug.head(qpmodel.dim)) - - qpwork.dw_aug(qpmodel.dim + qpmodel.n_eq + j) * - qpresults.info.mu_in); - } - } - } - Adx.noalias() = qpwork.A_scaled * qpwork.dw_aug.head(qpmodel.dim); - qpwork.err.segment(qpmodel.dim, qpmodel.n_eq).noalias() -= Adx; - qpwork.err.segment(qpmodel.dim, qpmodel.n_eq) += - qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq) * qpresults.info.mu_eq; -} - -template -void -solve_linear_system(proxsuite::proxqp::dense::Vec& dw, - const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - const isize n_constraints, - const DenseBackend& dense_backend, - isize inner_pb_dim, - proxsuite::linalg::veg::dynstack::DynStackMut& stack) -{ - - switch (dense_backend) { - case DenseBackend::PrimalDualLDLT: - qpwork.ldl.solve_in_place(dw.head(inner_pb_dim), stack); - break; - case DenseBackend::PrimalLDLT: - // find dx - dw.head(qpmodel.dim).noalias() += qpresults.info.mu_eq_inv * - qpwork.A_scaled.transpose() * - dw.segment(qpmodel.dim, qpmodel.n_eq); - for (isize i = 0; i < n_constraints; i++) { - isize j = qpwork.current_bijection_map(i); - if (j < qpwork.n_c) { - if (i >= qpmodel.n_in) { - // box constraints - dw(i - qpmodel.n_in) += dw(j + qpmodel.dim + qpmodel.n_eq) * - qpwork.i_scaled(i - qpmodel.n_in); - } else { - // ineq constraints - dw.head(qpmodel.dim) += - dw(j + qpmodel.dim + qpmodel.n_eq) * qpwork.C_scaled.row(i); - } - } - } - qpwork.ldl.solve_in_place(dw.head(qpmodel.dim), stack); - // find dy - dw.segment(qpmodel.dim, qpmodel.n_eq) -= - qpresults.info.mu_eq_inv * dw.segment(qpmodel.dim, qpmodel.n_eq); - dw.segment(qpmodel.dim, qpmodel.n_eq).noalias() += - qpresults.info.mu_eq_inv * - (qpwork.A_scaled * - dw.head( - qpmodel.dim)); //- qpwork.rhs.segment(qpmodel.dim,qpmodel.n_eq)); - // find dz_J - for (isize i = 0; i < n_constraints; i++) { - isize j = qpwork.current_bijection_map(i); - if (j < qpwork.n_c) { - if (i >= qpmodel.n_in) { - // box constraints - dw(j + qpmodel.dim + qpmodel.n_eq) -= - qpresults.info.mu_in_inv * (dw(j + qpmodel.dim + qpmodel.n_eq)); - dw(j + qpmodel.dim + qpmodel.n_eq) += - qpresults.info.mu_in_inv * - (dw( - i - - qpmodel.n_in)); //- qpwork.rhs(j + qpmodel.dim + qpmodel.n_eq)); - } else { - // ineq constraints - dw(j + qpmodel.dim + qpmodel.n_eq) -= - qpresults.info.mu_in_inv * (dw(j + qpmodel.dim + qpmodel.n_eq)); - dw(j + qpmodel.dim + qpmodel.n_eq) += - qpresults.info.mu_in_inv * - (qpwork.C_scaled.row(i).dot(dw.head( - qpmodel.dim))); //- qpwork.rhs(j + qpmodel.dim + qpmodel.n_eq)); - } - } - } - break; - case DenseBackend::Automatic: - break; - } -} - -/*! - * Performs iterative refinement for solving associated linear systems of PROXQP - * algorithm. - * - * @param qpwork solver workspace. - * @param qpmodel QP problem model as defined by the user (without any scaling - * performed). - * @param qpsettings solver settings. - * @param qpresults solver results. - * @param eps accuracy required for pursuing or not the iterative refinement. - * @param inner_pb_dim dimension of the linear system. - */ -template -void -iterative_solve_with_permut_fact( // - const Settings& qpsettings, - const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - const isize n_constraints, - const DenseBackend& dense_backend, - const HessianType& hessian_type, - T eps, - isize inner_pb_dim) -{ - - qpwork.err.setZero(); - i32 it = 0; - i32 it_stability = 0; - - proxsuite::linalg::veg::dynstack::DynStackMut stack{ - proxsuite::linalg::veg::from_slice_mut, qpwork.ldl_stack.as_mut() - }; - qpwork.dw_aug.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); - solve_linear_system(qpwork.dw_aug, - qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - inner_pb_dim, - stack); - iterative_residual( - qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); - - ++it; - T preverr = infty_norm(qpwork.err.head(inner_pb_dim)); - while (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps) { - - if (it >= qpsettings.nb_iterative_refinement) { - break; - } - ++it; - solve_linear_system(qpwork.err, - qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - inner_pb_dim, - stack); - // qpwork.ldl.solve_in_place(qpwork.err.head(inner_pb_dim), stack); - qpwork.dw_aug.head(inner_pb_dim) += qpwork.err.head(inner_pb_dim); - - qpwork.err.head(inner_pb_dim).setZero(); - iterative_residual( - qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); - - if (infty_norm(qpwork.err.head(inner_pb_dim)) > preverr) { - it_stability += 1; - - } else { - it_stability = 0; - } - if (it_stability == 2) { - break; - } - preverr = infty_norm(qpwork.err.head(inner_pb_dim)); - } - - if (infty_norm(qpwork.err.head(inner_pb_dim)) >= - std::max(eps, qpsettings.eps_refact)) { - refactorize(qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - qpresults.info.rho); - it = 0; - it_stability = 0; - - qpwork.dw_aug.head(inner_pb_dim) = qpwork.rhs.head(inner_pb_dim); - solve_linear_system(qpwork.dw_aug, - qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - inner_pb_dim, - stack); - // qpwork.ldl.solve_in_place(qpwork.dw_aug.head(inner_pb_dim), stack); - - iterative_residual( - qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); - - preverr = infty_norm(qpwork.err.head(inner_pb_dim)); - ++it; - while (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps) { - - if (it >= qpsettings.nb_iterative_refinement) { - break; - } - ++it; - solve_linear_system(qpwork.err, - qpmodel, - qpresults, - qpwork, - n_constraints, - dense_backend, - inner_pb_dim, - stack); - // qpwork.ldl.solve_in_place(qpwork.err.head(inner_pb_dim), stack); - qpwork.dw_aug.head(inner_pb_dim) += qpwork.err.head(inner_pb_dim); - - qpwork.err.head(inner_pb_dim).setZero(); - iterative_residual( - qpmodel, qpresults, qpwork, n_constraints, inner_pb_dim, hessian_type); - - if (infty_norm(qpwork.err.head(inner_pb_dim)) > preverr) { - it_stability += 1; - } else { - it_stability = 0; - } - if (it_stability == 2) { - break; - } - preverr = infty_norm(qpwork.err.head(inner_pb_dim)); - } - } - if (infty_norm(qpwork.err.head(inner_pb_dim)) >= eps && qpsettings.verbose) { - // std::cout << "after refact err " << err << std::endl; - std::cout << "refact err " << infty_norm(qpwork.err.head(inner_pb_dim)) - << std::endl; - } - qpresults.info.iterative_residual = infty_norm(qpwork.err.head(inner_pb_dim)); - - qpwork.rhs.head(inner_pb_dim).setZero(); -} /*! * BCL rule for updating penalization parameters and accuracy variables. * @@ -845,7 +328,7 @@ primal_dual_semi_smooth_newton_step(const Settings& qpsettings, } break; } - iterative_solve_with_permut_fact( // + common::dense::iterative_solve_with_permut_fact( // qpsettings, qpmodel, qpresults, @@ -881,16 +364,17 @@ primal_dual_semi_smooth_newton_step(const Settings& qpsettings, */ template void -primal_dual_newton_semi_smooth(const Settings& qpsettings, - const Model& qpmodel, - Results& qpresults, - Workspace& qpwork, - const bool box_constraints, - const isize n_constraints, - preconditioner::RuizEquilibration& ruiz, - const DenseBackend& dense_backend, - const HessianType& hessian_type, - T eps_int) +primal_dual_newton_semi_smooth( + const Settings& qpsettings, + const Model& qpmodel, + Results& qpresults, + Workspace& qpwork, + const bool box_constraints, + const isize n_constraints, + common::dense::preconditioner::RuizEquilibration& ruiz, + const DenseBackend& dense_backend, + const HessianType& hessian_type, + T eps_int) { /* MUST CONTAIN IN ENTRY WITH x = x_prev ; y = y_prev ; z = z_prev @@ -1028,35 +512,37 @@ primal_dual_newton_semi_smooth(const Settings& qpsettings, if (iter % qpsettings.frequence_infeasibility_check == 0 || qpsettings.primal_infeasibility_solving) { // compute primal and dual infeasibility criteria - bool is_primal_infeasible = global_primal_residual_infeasibility( - VectorViewMut{ from_eigen, ATdy }, - VectorViewMut{ from_eigen, CTdz }, - VectorViewMut{ from_eigen, dy }, - VectorViewMut{ from_eigen, dz }, - qpwork, - qpmodel, - qpsettings, - box_constraints, - ruiz); + bool is_primal_infeasible = + common::dense::global_primal_residual_infeasibility( + VectorViewMut{ from_eigen, ATdy }, + VectorViewMut{ from_eigen, CTdz }, + VectorViewMut{ from_eigen, dy }, + VectorViewMut{ from_eigen, dz }, + qpwork, + qpmodel, + qpsettings, + box_constraints, + ruiz); bool is_dual_infeasible = - global_dual_residual_infeasibility(VectorViewMut{ from_eigen, Adx }, - VectorViewMut{ from_eigen, Cdx }, - VectorViewMut{ from_eigen, Hdx }, - VectorViewMut{ from_eigen, dx }, - qpwork, - qpsettings, - qpmodel, - box_constraints, - ruiz); + common::dense::global_dual_residual_infeasibility( + VectorViewMut{ from_eigen, Adx }, + VectorViewMut{ from_eigen, Cdx }, + VectorViewMut{ from_eigen, Hdx }, + VectorViewMut{ from_eigen, dx }, + qpwork, + qpsettings, + qpmodel, + box_constraints, + ruiz); if (is_primal_infeasible) { - qpresults.info.status = QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE; + qpresults.info.status = QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE; if (!qpsettings.primal_infeasibility_solving) { qpresults.info.iter += iter + 1; break; } } else if (is_dual_infeasible) { - qpresults.info.status = QPSolverOutput::PROXQP_DUAL_INFEASIBLE; + qpresults.info.status = QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE; qpresults.info.iter += iter + 1; break; } @@ -1095,7 +581,7 @@ qp_solve( // const bool box_constraints, const DenseBackend& dense_backend, const HessianType& hessian_type, - preconditioner::RuizEquilibration& ruiz) + common::dense::preconditioner::RuizEquilibration& ruiz) { /*** TEST WITH MATRIX FULL OF NAN FOR DEBUG static constexpr Layout layout = rowmajor; @@ -1113,268 +599,77 @@ qp_solve( // qpwork.timer.stop(); qpwork.timer.start(); } + + // Setup header + /////////////////////// + if (qpsettings.verbose) { - dense::print_setup_header(qpsettings, - qpresults, - qpmodel, - box_constraints, - dense_backend, - hessian_type); + common::dense::print_setup_header(qpsettings, + qpresults, + qpmodel, + box_constraints, + dense_backend, + hessian_type, + QPSolver::PROXQP); } - // std::cout << "qpwork.dirty " << qpwork.dirty << std::endl; - if (qpwork.dirty) { // the following is used when a solve has already been - // executed (and without any intermediary model update) - switch (qpsettings.initial_guess) { - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { - qpwork.cleanup(box_constraints); - qpresults.cleanup(qpsettings); - break; - } - case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { - // keep solutions but restart workspace and results - qpwork.cleanup(box_constraints); - qpresults.cold_start(qpsettings); - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, qpresults.x }); - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - break; - } - case InitialGuessStatus::NO_INITIAL_GUESS: { - qpwork.cleanup(box_constraints); - qpresults.cleanup(qpsettings); - break; - } - case InitialGuessStatus::WARM_START: { - qpwork.cleanup(box_constraints); - qpresults.cold_start( - qpsettings); // because there was already a solve, - // precond was already computed if set so - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, - qpresults - .x }); // it contains the value given in entry for warm start - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - break; - } - case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { - // keep workspace and results solutions except statistics - // std::cout << "i keep previous solution" << std::endl; - qpresults.cleanup_statistics(); - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, qpresults.x }); - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - break; - } - } - if (qpsettings.initial_guess != - InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { - switch (hessian_type) { - case HessianType::Zero: - break; - case HessianType::Dense: - qpwork.H_scaled = qpmodel.H; - break; - case HessianType::Diagonal: - qpwork.H_scaled = qpmodel.H; - break; - } - qpwork.g_scaled = qpmodel.g; - qpwork.A_scaled = qpmodel.A; - qpwork.b_scaled = qpmodel.b; - qpwork.C_scaled = qpmodel.C; - qpwork.u_scaled = qpmodel.u; - qpwork.l_scaled = qpmodel.l; - proxsuite::proxqp::dense::setup_equilibration( - qpwork, - qpsettings, - box_constraints, - hessian_type, - ruiz, - false); // reuse previous equilibration - proxsuite::proxqp::dense::setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - } - switch (qpsettings.initial_guess) { - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { - compute_equality_constrained_initial_guess(qpwork, - qpsettings, - qpmodel, - n_constraints, - dense_backend, - hessian_type, - qpresults); - break; - } - case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { - //!\ TODO in a quicker way - qpwork.n_c = 0; - for (isize i = 0; i < n_constraints; i++) { - if (qpresults.z[i] != 0) { - qpwork.active_inequalities[i] = true; - } else { - qpwork.active_inequalities[i] = false; - } - } - linesearch::active_set_change( - qpmodel, qpresults, dense_backend, n_constraints, qpwork); - break; - } - case InitialGuessStatus::NO_INITIAL_GUESS: { - break; - } - case InitialGuessStatus::WARM_START: { - //!\ TODO in a quicker way - qpwork.n_c = 0; - for (isize i = 0; i < n_constraints; i++) { - if (qpresults.z[i] != 0) { - qpwork.active_inequalities[i] = true; - } else { - qpwork.active_inequalities[i] = false; - } - } - linesearch::active_set_change( - qpmodel, qpresults, dense_backend, n_constraints, qpwork); - break; - } - case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { - // keep workspace and results solutions except statistics - // std::cout << "i use previous solution" << std::endl; - // meaningful for when one wants to warm start with previous result with - // the same QP model - break; - } - } - } else { // the following is used for a first solve after initializing or - // updating the Qp object - switch (qpsettings.initial_guess) { - case InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS: { - proxsuite::proxqp::dense::setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - compute_equality_constrained_initial_guess(qpwork, - qpsettings, - qpmodel, - n_constraints, - dense_backend, - hessian_type, - qpresults); - break; - } - case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { - //!\ TODO in a quicker way - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, - qpresults - .x }); // meaningful for when there is an upate of the model and - // one wants to warm start with previous result - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - qpwork.n_c = 0; - for (isize i = 0; i < n_constraints; i++) { - if (qpresults.z[i] != 0) { - qpwork.active_inequalities[i] = true; - } else { - qpwork.active_inequalities[i] = false; - } - } - linesearch::active_set_change( - qpmodel, qpresults, dense_backend, n_constraints, qpwork); - break; - } - case InitialGuessStatus::NO_INITIAL_GUESS: { - setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - break; - } - case InitialGuessStatus::WARM_START: { - //!\ TODO in a quicker way - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, qpresults.x }); - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - qpwork.n_c = 0; - for (isize i = 0; i < n_constraints; i++) { - if (qpresults.z[i] != 0) { - qpwork.active_inequalities[i] = true; - } else { - qpwork.active_inequalities[i] = false; - } - } - linesearch::active_set_change( - qpmodel, qpresults, dense_backend, n_constraints, qpwork); - break; - } - case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { - // std::cout << "i refactorize from previous solution" << std::endl; - ruiz.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, - qpresults - .x }); // meaningful for when there is an upate of the model and - // one wants to warm start with previous result - ruiz.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - { proxsuite::proxqp::from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - if (qpwork.refactorize) { // refactorization only when one of the - // matrices has changed or one proximal - // parameter has changed - setup_factorization( - qpwork, qpmodel, qpresults, dense_backend, hessian_type); - qpwork.n_c = 0; - for (isize i = 0; i < n_constraints; i++) { - if (qpresults.z[i] != 0) { - qpwork.active_inequalities[i] = true; - } else { - qpwork.active_inequalities[i] = false; - } - } - linesearch::active_set_change( - qpmodel, qpresults, dense_backend, n_constraints, qpwork); - break; - } - } - } + + // Ruiz equilibration and factorization + /////////////////////// + + common::dense::init_qp_solve(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + dense_backend, + hessian_type, + ruiz, + n_constraints, + QPSolver::PROXQP); + + // Active sets for duality gap computation + /////////////////////// + + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in).noalias() = + qpwork.C_scaled * qpresults.x; // contains now scaled(Cx) + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) = qpresults.x; + // contains now scaled(Cx) } + + qpwork.primal_residual_in_scaled_up += + qpwork.z_prev * + qpresults.info.mu_in; // contains now scaled(Cx+z_prev*mu_in) + + switch (qpsettings.merit_function_type) { + case MeritFunctionType::GPDAL: + qpwork.primal_residual_in_scaled_up += + (qpsettings.alpha_gpdal - 1.) * qpresults.info.mu_in * qpresults.z; + break; + case MeritFunctionType::PDAL: + break; + } + + qpresults.si = qpwork.primal_residual_in_scaled_up; + qpwork.primal_residual_in_scaled_up.head(qpmodel.n_in) -= + qpwork.u_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.head(qpmodel.n_in) -= + qpwork.l_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + + if (box_constraints) { + qpwork.primal_residual_in_scaled_up.tail(qpmodel.dim) -= + qpwork.u_box_scaled; // contains now scaled(Cx-u+z_prev*mu_in) + qpresults.si.tail(qpmodel.dim) -= + qpwork.l_box_scaled; // contains now scaled(Cx-l+z_prev*mu_in) + } + + qpwork.active_set_up.array() = + (qpwork.primal_residual_in_scaled_up.array() >= 0); + qpwork.active_set_low.array() = (qpresults.si.array() <= 0); + + // Tmp variables + /////////////////////// + T bcl_eta_ext_init = pow(T(0.1), qpsettings.alpha_bcl); T bcl_eta_ext = bcl_eta_ext_init; T bcl_eta_in(1); @@ -1394,131 +689,74 @@ qp_solve( // T rhs_duality_gap(0); T scaled_eps(qpsettings.eps_abs); - for (i64 iter = 0; iter < qpsettings.max_iter; ++iter) { + // Solver iterations + /////////////////////// - // compute primal residual - - // PERF: fuse matrix product computations in global_{primal, dual}_residual - global_primal_residual(qpmodel, - qpresults, - qpsettings, - qpwork, - ruiz, - box_constraints, - primal_feasibility_lhs, - primal_feasibility_eq_rhs_0, - primal_feasibility_in_rhs_0, - primal_feasibility_eq_lhs, - primal_feasibility_in_lhs); - - global_dual_residual(qpresults, - qpwork, - qpmodel, - box_constraints, - ruiz, - dual_feasibility_lhs, - dual_feasibility_rhs_0, - dual_feasibility_rhs_1, - dual_feasibility_rhs_3, - rhs_duality_gap, - duality_gap, - hessian_type); - - qpresults.info.pri_res = primal_feasibility_lhs; - qpresults.info.dua_res = dual_feasibility_lhs; - qpresults.info.duality_gap = duality_gap; + for (i64 iter = 0; iter < qpsettings.max_iter; ++iter) { T new_bcl_mu_in(qpresults.info.mu_in); T new_bcl_mu_eq(qpresults.info.mu_eq); T new_bcl_mu_in_inv(qpresults.info.mu_in_inv); T new_bcl_mu_eq_inv(qpresults.info.mu_eq_inv); - T rhs_pri(scaled_eps); - if (qpsettings.eps_rel != 0) { - rhs_pri += qpsettings.eps_rel * std::max(primal_feasibility_eq_rhs_0, - primal_feasibility_in_rhs_0); - } - bool is_primal_feasible = primal_feasibility_lhs <= rhs_pri; - - T rhs_dua(qpsettings.eps_abs); - if (qpsettings.eps_rel != 0) { - rhs_dua += - qpsettings.eps_rel * - std::max( - std::max(dual_feasibility_rhs_3, dual_feasibility_rhs_0), - std::max(dual_feasibility_rhs_1, qpwork.dual_feasibility_rhs_2)); - } - - bool is_dual_feasible = dual_feasibility_lhs <= rhs_dua; + common::dense::compute_residuals(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + hessian_type, + ruiz, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap); + + // Print iteration + /////////////////////// if (qpsettings.verbose) { - - ruiz.unscale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); - ruiz.unscale_dual_in_place_eq( - VectorViewMut{ from_eigen, qpresults.y }); - ruiz.unscale_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.unscale_box_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - { - // EigenAllowAlloc _{}; - qpresults.info.objValue = 0; - for (Eigen::Index j = 0; j < qpmodel.dim; ++j) { - qpresults.info.objValue += - 0.5 * (qpresults.x(j) * qpresults.x(j)) * qpmodel.H(j, j); - qpresults.info.objValue += - qpresults.x(j) * T(qpmodel.H.col(j) - .tail(qpmodel.dim - j - 1) - .dot(qpresults.x.tail(qpmodel.dim - j - 1))); - } - qpresults.info.objValue += (qpmodel.g).dot(qpresults.x); - } - std::cout << "\033[1;32m[outer iteration " << iter + 1 << "]\033[0m" - << std::endl; - std::cout << std::scientific << std::setw(2) << std::setprecision(2) - << "| primal residual=" << qpresults.info.pri_res - << " | dual residual=" << qpresults.info.dua_res - << " | duality gap=" << qpresults.info.duality_gap - << " | mu_in=" << qpresults.info.mu_in - << " | rho=" << qpresults.info.rho << std::endl; - ruiz.scale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); - ruiz.scale_dual_in_place_eq(VectorViewMut{ from_eigen, qpresults.y }); - ruiz.scale_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.scale_box_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - } - if (is_primal_feasible && is_dual_feasible) { - if (qpsettings.check_duality_gap) { - if (std::fabs(qpresults.info.duality_gap) <= - qpsettings.eps_duality_gap_abs + - qpsettings.eps_duality_gap_rel * rhs_duality_gap) { - if (qpsettings.primal_infeasibility_solving && - qpresults.info.status == - QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { - qpresults.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; - } else { - qpresults.info.status = QPSolverOutput::PROXQP_SOLVED; - } - break; - } - } else { - qpresults.info.status = QPSolverOutput::PROXQP_SOLVED; - break; - } + common::dense::print_iteration_line( + qpresults, qpmodel, box_constraints, ruiz, QPSolver::PROXQP, iter); + } + + // Check if solved + /////////////////////// + + bool stop_solved = common::dense::is_solved(qpsettings, + qpresults, + qpwork, + scaled_eps, + primal_feasibility_lhs, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + dual_feasibility_lhs, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap); + + if (stop_solved) { + break; } + + // Set iteration and variables + /////////////////////// + qpresults.info.iter_ext += 1; // We start a new external loop update qpwork.x_prev = qpresults.x; qpwork.y_prev = qpresults.y; qpwork.z_prev = qpresults.z; - // primal dual version from gill and robinson + // Primal dual version from Gill and Robinson + /////////////////////// ruiz.scale_primal_residual_in_place_in( VectorViewMut{ from_eigen, @@ -1569,9 +807,9 @@ qp_solve( // hessian_type, bcl_eta_in); - if ((qpresults.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE && + if ((qpresults.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE && !qpsettings.primal_infeasibility_solving) || - qpresults.info.status == QPSolverOutput::PROXQP_DUAL_INFEASIBLE) { + qpresults.info.status == QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE) { // certificate of infeasibility qpresults.x = qpwork.dw_aug.head(qpmodel.dim); qpresults.y = qpwork.dw_aug.segment(qpmodel.dim, qpmodel.n_eq); @@ -1580,7 +818,7 @@ qp_solve( // } if (scaled_eps == qpsettings.eps_abs && qpsettings.primal_infeasibility_solving && - qpresults.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { + qpresults.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { qpwork.rhs.segment(qpmodel.dim, qpmodel.n_eq + qpmodel.n_in) .setConstant(T(1)); qpwork.rhs.head(qpmodel.dim).noalias() = @@ -1593,77 +831,48 @@ qp_solve( // scaled_eps = infty_norm(qpwork.rhs.head(qpmodel.dim)) * qpsettings.eps_abs; } + + // Update solver status + /////////////////////// + T primal_feasibility_lhs_new(primal_feasibility_lhs); + T dual_feasibility_lhs_new(dual_feasibility_lhs); + + common::dense::compute_residuals(qpsettings, + qpmodel, + qpresults, + qpwork, + box_constraints, + hessian_type, + ruiz, + primal_feasibility_lhs_new, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + primal_feasibility_eq_lhs, + primal_feasibility_in_lhs, + dual_feasibility_lhs_new, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap); + + common::dense::is_solved_or_closest_solved(qpsettings, + qpresults, + qpwork, + scaled_eps, + primal_feasibility_lhs_new, + primal_feasibility_eq_rhs_0, + primal_feasibility_in_rhs_0, + dual_feasibility_lhs_new, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap); + + // Update of proximal parameter mu + /////////////////////// - global_primal_residual(qpmodel, - qpresults, - qpsettings, - qpwork, - ruiz, - box_constraints, - primal_feasibility_lhs_new, - primal_feasibility_eq_rhs_0, - primal_feasibility_in_rhs_0, - primal_feasibility_eq_lhs, - primal_feasibility_in_lhs); - - is_primal_feasible = - primal_feasibility_lhs_new <= - (scaled_eps + qpsettings.eps_rel * std::max(primal_feasibility_eq_rhs_0, - primal_feasibility_in_rhs_0)); - qpresults.info.pri_res = primal_feasibility_lhs_new; - if (is_primal_feasible) { - T dual_feasibility_lhs_new(dual_feasibility_lhs); - - global_dual_residual(qpresults, - qpwork, - qpmodel, - box_constraints, - ruiz, - dual_feasibility_lhs_new, - dual_feasibility_rhs_0, - dual_feasibility_rhs_1, - dual_feasibility_rhs_3, - rhs_duality_gap, - duality_gap, - hessian_type); - qpresults.info.dua_res = dual_feasibility_lhs_new; - qpresults.info.duality_gap = duality_gap; - - is_dual_feasible = - dual_feasibility_lhs_new <= - (qpsettings.eps_abs + - qpsettings.eps_rel * - std::max( - std::max(dual_feasibility_rhs_3, dual_feasibility_rhs_0), - std::max(dual_feasibility_rhs_1, qpwork.dual_feasibility_rhs_2))); - - if (is_dual_feasible) { - if (qpsettings.check_duality_gap) { - if (std::fabs(qpresults.info.duality_gap) <= - qpsettings.eps_duality_gap_abs + - qpsettings.eps_duality_gap_rel * rhs_duality_gap) { - if (qpsettings.primal_infeasibility_solving && - qpresults.info.status == - QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { - qpresults.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; - } else { - qpresults.info.status = QPSolverOutput::PROXQP_SOLVED; - } - } - } else { - if (qpsettings.primal_infeasibility_solving && - qpresults.info.status == - QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { - qpresults.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; - } else { - qpresults.info.status = QPSolverOutput::PROXQP_SOLVED; - } - } - } - } if (qpsettings.bcl_update) { bcl_update(qpsettings, qpresults, @@ -1692,20 +901,20 @@ qp_solve( // } // COLD RESTART - T dual_feasibility_lhs_new(dual_feasibility_lhs); - - global_dual_residual(qpresults, - qpwork, - qpmodel, - box_constraints, - ruiz, - dual_feasibility_lhs_new, - dual_feasibility_rhs_0, - dual_feasibility_rhs_1, - dual_feasibility_rhs_3, - rhs_duality_gap, - duality_gap, - hessian_type); + dual_feasibility_lhs_new = dual_feasibility_lhs; + + common::dense::global_dual_residual(qpresults, + qpwork, + qpmodel, + box_constraints, + ruiz, + dual_feasibility_lhs_new, + dual_feasibility_rhs_0, + dual_feasibility_rhs_1, + dual_feasibility_rhs_3, + rhs_duality_gap, + duality_gap, + hessian_type); qpresults.info.dua_res = dual_feasibility_lhs_new; qpresults.info.duality_gap = duality_gap; @@ -1746,92 +955,23 @@ qp_solve( // qpresults.info.mu_in_inv = new_bcl_mu_in_inv; } - ruiz.unscale_primal_in_place(VectorViewMut{ from_eigen, qpresults.x }); - ruiz.unscale_dual_in_place_eq(VectorViewMut{ from_eigen, qpresults.y }); - ruiz.unscale_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.unscale_box_dual_in_place_in( - VectorViewMut{ from_eigen, qpresults.z.tail(qpmodel.dim) }); - } - if (qpsettings.primal_infeasibility_solving && - qpresults.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { - ruiz.unscale_primal_residual_in_place_eq( - VectorViewMut{ from_eigen, qpresults.se }); - ruiz.unscale_primal_residual_in_place_in( - VectorViewMut{ from_eigen, qpresults.si.head(qpmodel.n_in) }); - if (box_constraints) { - ruiz.unscale_box_primal_residual_in_place_in( - VectorViewMut{ from_eigen, qpresults.si.tail(qpmodel.dim) }); - } - } + // End of qp_solve + /////////////////////// - { - // EigenAllowAlloc _{}; - qpresults.info.objValue = 0; - for (Eigen::Index j = 0; j < qpmodel.dim; ++j) { - qpresults.info.objValue += - 0.5 * (qpresults.x(j) * qpresults.x(j)) * qpmodel.H(j, j); - qpresults.info.objValue += - qpresults.x(j) * T(qpmodel.H.col(j) - .tail(qpmodel.dim - j - 1) - .dot(qpresults.x.tail(qpmodel.dim - j - 1))); - } - qpresults.info.objValue += (qpmodel.g).dot(qpresults.x); - } + common::dense::unscale_solver( + qpsettings, qpmodel, qpresults, box_constraints, ruiz); + + common::dense::compute_objective(qpresults, qpmodel); if (qpsettings.compute_timings) { - qpresults.info.solve_time = qpwork.timer.elapsed().user; // in microseconds - qpresults.info.run_time = - qpresults.info.solve_time + qpresults.info.setup_time; + common::dense::compute_timings(qpresults, qpwork); } if (qpsettings.verbose) { - std::cout << "-------------------SOLVER STATISTICS-------------------" - << std::endl; - std::cout << "outer iter: " << qpresults.info.iter_ext << std::endl; - std::cout << "total iter: " << qpresults.info.iter << std::endl; - std::cout << "mu updates: " << qpresults.info.mu_updates << std::endl; - std::cout << "rho updates: " << qpresults.info.rho_updates << std::endl; - std::cout << "objective: " << qpresults.info.objValue << std::endl; - switch (qpresults.info.status) { - case QPSolverOutput::PROXQP_SOLVED: { - std::cout << "status: " - << "Solved" << std::endl; - break; - } - case QPSolverOutput::PROXQP_MAX_ITER_REACHED: { - std::cout << "status: " - << "Maximum number of iterations reached" << std::endl; - break; - } - case QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE: { - std::cout << "status: " - << "Primal infeasible" << std::endl; - break; - } - case QPSolverOutput::PROXQP_DUAL_INFEASIBLE: { - std::cout << "status: " - << "Dual infeasible" << std::endl; - break; - } - case QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { - std::cout << "status: " - << "Solved closest primal feasible" << std::endl; - break; - } - case QPSolverOutput::PROXQP_NOT_RUN: { - std::cout << "status: " - << "Solver not run" << std::endl; - break; - } - } - - if (qpsettings.compute_timings) - std::cout << "run time [μs]: " << qpresults.info.solve_time << std::endl; - std::cout << "--------------------------------------------------------" - << std::endl; + common::dense::print_solver_statistics( + qpsettings, qpresults, QPSolver::PROXQP); } + qpwork.dirty = true; qpwork.is_initialized = true; // necessary because we call workspace cleanup diff --git a/include/proxsuite/proxqp/dense/wrapper.hpp b/include/proxsuite/proxqp/dense/wrapper.hpp index 6e0c1a066..79cf22414 100644 --- a/include/proxsuite/proxqp/dense/wrapper.hpp +++ b/include/proxsuite/proxqp/dense/wrapper.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2025 INRIA // /** * @file wrapper.hpp @@ -7,15 +7,16 @@ #ifndef PROXSUITE_PROXQP_DENSE_WRAPPER_HPP #define PROXSUITE_PROXQP_DENSE_WRAPPER_HPP -#include + +#include "proxsuite/common/status.hpp" +#include #include -#include -#include -#include +#include namespace proxsuite { namespace proxqp { namespace dense { + /// /// @brief This class defines the API of PROXQP solver with dense backend. /// @@ -36,10 +37,10 @@ auto main() -> int { // Generate a random QP problem with primal variable dimension of size dim; n_eq equality constraints and n_in inequality constraints - ::proxsuite::proxqp::test::rand::set_seed(1); - proxqp::isize dim = 10; - proxqp::isize n_eq(dim / 4); - proxqp::isize n_in(dim / 4); + ::proxsuite::common::test::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); T sparsity_factor = 0.15; // controls the sparsity of each matrix of the problem generated T eps_abs = T(1e-9); Qp qp{ @@ -77,57 +78,14 @@ Qp.results.y + qp.C.transpose() * Qp.results.z) .lpNorm(); } * ``` */ -///// QP object -template -DenseBackend -dense_backend_choice(DenseBackend _dense_backend, - isize dim, - isize n_eq, - isize n_in, - bool box_constraints) -{ - if (_dense_backend == DenseBackend::Automatic) { - isize n_constraints(n_in); - if (box_constraints) { - n_constraints += dim; - } - T threshold(1.5); - T frequence(0.2); - T PrimalDualLDLTCost = - 0.5 * std::pow(T(n_eq) / T(dim), 2) + - 0.17 * (std::pow(T(n_eq) / T(dim), 3) + - std::pow(T(n_constraints) / T(dim), 3)) + - frequence * std::pow(T(n_eq + n_constraints) / T(dim), 2) / T(dim); - T PrimalLDLTCost = - threshold * - ((0.5 * T(n_eq) + T(n_constraints)) / T(dim) + frequence / T(dim)); - bool choice = PrimalDualLDLTCost > PrimalLDLTCost; - if (choice) { - return DenseBackend::PrimalLDLT; - } else { - return DenseBackend::PrimalDualLDLT; - } - } else { - return _dense_backend; - } -} + template -struct QP +struct QP : public common::dense::QPBase, T> { private: - // structure of the problem - // not supposed to change - DenseBackend dense_backend; - bool box_constraints; - HessianType hessian_type; + using Base = common::dense::QPBase, T>; public: - Results results; - Settings settings; - Model model; - Workspace work; - preconditioner::RuizEquilibration ruiz; - /*! * Default constructor using QP model dimensions. * @param _dim primal variable dimension. @@ -141,25 +99,10 @@ struct QP isize _n_eq, isize _n_in, bool _box_constraints, - proxsuite::proxqp::HessianType _hessian_type, + HessianType _hessian_type, DenseBackend _dense_backend) - : dense_backend(dense_backend_choice(_dense_backend, - _dim, - _n_eq, - _n_in, - _box_constraints)) - , box_constraints(_box_constraints) - , hessian_type(_hessian_type) - , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, _box_constraints) - , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, - _n_eq, - _n_in, - _box_constraints }) + : Base(_dim, _n_eq, _n_in, _box_constraints, _hessian_type, _dense_backend) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -175,24 +118,9 @@ struct QP isize _n_in, bool _box_constraints, DenseBackend _dense_backend, - proxsuite::proxqp::HessianType _hessian_type) - : dense_backend(dense_backend_choice(_dense_backend, - _dim, - _n_eq, - _n_in, - _box_constraints)) - , box_constraints(_box_constraints) - , hessian_type(_hessian_type) - , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, _box_constraints) - , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, - _n_eq, - _n_in, - _box_constraints }) + HessianType _hessian_type) + : Base(_dim, _n_eq, _n_in, _box_constraints, _dense_backend, _hessian_type) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -206,24 +134,9 @@ struct QP isize _n_eq, isize _n_in, bool _box_constraints, - proxsuite::proxqp::HessianType _hessian_type) - : dense_backend(dense_backend_choice(DenseBackend::Automatic, - _dim, - _n_eq, - _n_in, - _box_constraints)) - , box_constraints(_box_constraints) - , hessian_type(_hessian_type) - , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, _box_constraints) - , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, - _n_eq, - _n_in, - _box_constraints }) + HessianType _hessian_type) + : Base(_dim, _n_eq, _n_in, _box_constraints, _hessian_type) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -239,23 +152,8 @@ struct QP isize _n_in, bool _box_constraints, DenseBackend _dense_backend) - : dense_backend(dense_backend_choice(_dense_backend, - _dim, - _n_eq, - _n_in, - _box_constraints)) - , box_constraints(_box_constraints) - , hessian_type(HessianType::Dense) - , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, _box_constraints) - , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, - _n_eq, - _n_in, - _box_constraints }) + : Base(_dim, _n_eq, _n_in, _box_constraints, _dense_backend) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -265,23 +163,8 @@ struct QP * @param _box_constraints specify that there are (or not) box constraints. */ QP(isize _dim, isize _n_eq, isize _n_in, bool _box_constraints) - : dense_backend(dense_backend_choice(DenseBackend::Automatic, - _dim, - _n_eq, - _n_in, - _box_constraints)) - , box_constraints(_box_constraints) - , hessian_type(proxsuite::proxqp::HessianType::Dense) - , results(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, _box_constraints) - , work(_dim, _n_eq, _n_in, _box_constraints, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, - _n_eq, - _n_in, - _box_constraints }) + : Base(_dim, _n_eq, _n_in, _box_constraints) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -290,24 +173,9 @@ struct QP * @param _n_in number of inequality constraints. * @param _hessian_type specify that there are (or not) box constraints. */ - QP(isize _dim, - isize _n_eq, - isize _n_in, - proxsuite::proxqp::HessianType _hessian_type) - : dense_backend(dense_backend_choice(DenseBackend::Automatic, - _dim, - _n_eq, - _n_in, - false)) - , box_constraints(false) - , hessian_type(_hessian_type) - , results(_dim, _n_eq, _n_in, false, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, false) - , work(_dim, _n_eq, _n_in, false, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, _n_eq, _n_in, false }) + QP(isize _dim, isize _n_eq, isize _n_in, HessianType _hessian_type) + : Base(_dim, _n_eq, _n_in, _hessian_type) { - work.timer.stop(); } /*! * Default constructor using QP model dimensions. @@ -316,657 +184,289 @@ struct QP * @param _n_in number of inequality constraints. */ QP(isize _dim, isize _n_eq, isize _n_in) - : dense_backend(dense_backend_choice(DenseBackend::Automatic, - _dim, - _n_eq, - _n_in, - false)) - , box_constraints(false) - , hessian_type(proxsuite::proxqp::HessianType::Dense) - , results(_dim, _n_eq, _n_in, false, dense_backend) - , settings(dense_backend) - , model(_dim, _n_eq, _n_in, false) - , work(_dim, _n_eq, _n_in, false, dense_backend) - , ruiz(preconditioner::RuizEquilibration{ _dim, _n_eq, _n_in, false }) + : Base(_dim, _n_eq, _n_in) { - work.timer.stop(); } - bool is_box_constrained() const { return box_constraints; }; - DenseBackend which_dense_backend() const { return dense_backend; }; - HessianType which_hessian_type() const { return hessian_type; }; /*! - * Setups the QP model (with dense matrix format) and equilibrates it if - * specified by the user. - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param compute_preconditioner boolean parameter for executing or not the - * preconditioner. - * @param rho proximal step size wrt primal variable. - * @param mu_eq proximal step size wrt equality constrained multiplier. - * @param mu_in proximal step size wrt inequality constrained multiplier. - * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + * Initialize ProxQP-specific settings. */ - void init(optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - bool compute_preconditioner = true, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional manual_minimal_H_eigenvalue = nullopt) + void init_derived_settings() { - PROXSUITE_THROW_PRETTY( - box_constraints == true, - std::invalid_argument, - "wrong model setup: the QP object is designed with box " - "constraints, but is initialized without lower or upper box " - "inequalities."); - // dense case - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); - } - settings.compute_preconditioner = compute_preconditioner; - // check the model is valid - if (g != nullopt && g.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - g.value().size(), - model.dim, - "the dimension wrt the primal variable x variable for initializing g " - "is not valid."); - } else { - g.reset(); - } - if (b != nullopt && b.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - b.value().size(), - model.n_eq, - "the dimension wrt equality constrained variables for initializing b " - "is not valid."); - } else { - b.reset(); - } - if (u != nullopt && u.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - u.value().size(), - model.n_in, - "the dimension wrt inequality constrained variables for initializing u " - "is not valid."); - } else { - u.reset(); - } - if (l != nullopt && l.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - l.value().size(), - model.n_in, - "the dimension wrt inequality constrained variables for initializing l " - "is not valid."); - } else { - l.reset(); - } - if (H != nullopt && H.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().rows(), - model.dim, - "the row dimension for initializing H is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().cols(), - model.dim, - "the column dimension for initializing H is not valid."); - } else { - H.reset(); - } - if (A != nullopt && A.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().rows(), - model.n_eq, - "the row dimension for initializing A is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().cols(), - model.dim, - "the column dimension for initializing A is not valid."); - } else { - A.reset(); - } - if (C != nullopt && C.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().rows(), - model.n_in, - "the row dimension for initializing C is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().cols(), - model.dim, - "the column dimension for initializing C is not valid."); - } else { - C.reset(); - } - if (settings.initial_guess == - InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { - work.refactorize = - true; // necessary for the first solve (then refactorize only if there - // is an update of the matrices) - } else { - work.refactorize = false; - } - work.proximal_parameter_update = false; - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); - } - PreconditionerStatus preconditioner_status; - if (compute_preconditioner) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; - } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::IDENTITY; - } - proxsuite::proxqp::dense::update_proximal_parameters( - settings, results, work, rho, mu_eq, mu_in); - proxsuite::proxqp::dense:: - update_default_rho_with_minimal_Hessian_eigen_value( - manual_minimal_H_eigenvalue, results, settings); - typedef optional> optional_VecRef; - proxsuite::proxqp::dense::setup(H, - g, - A, - b, - C, - l, - u, - optional_VecRef(nullopt), - optional_VecRef(nullopt), - settings, - model, - work, - results, - box_constraints, - ruiz, - preconditioner_status, - hessian_type); - work.is_initialized = true; - if (settings.compute_timings) { - results.info.setup_time = work.timer.elapsed().user; // in microseconds - } - }; + this->settings.default_mu_eq = 1.E-3; + this->settings.default_mu_in = 1.E-1; + + this->settings.alpha_bcl = 0.1; + this->settings.beta_bcl = 0.9; + this->settings.refactor_dual_feasibility_threshold = 1E-2; + this->settings.refactor_rho_threshold = 1E-7; + + this->settings.mu_min_eq = 1E-9; + this->settings.mu_min_in = 1E-8; + this->settings.mu_max_eq_inv = 1E9; + this->settings.mu_max_in_inv = 1E8; + + this->settings.mu_update_factor = 0.1; + this->settings.mu_update_inv_factor = 10; + this->settings.cold_reset_mu_eq = 1. / 1.1; + this->settings.cold_reset_mu_in = 1. / 1.1; + this->settings.cold_reset_mu_eq_inv = 1.1; + this->settings.cold_reset_mu_in_inv = 1.1; + + this->settings.eps_abs = 1.E-5; + this->settings.eps_rel = 0; + this->settings.max_iter = 10000; + this->settings.max_iter_in = 1500; + this->settings.safe_guard = 1.E4; + this->settings.nb_iterative_refinement = 10; + this->settings.eps_refact = 1.E-6; + + this->settings.verbose = false; + this->settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + this->settings.update_preconditioner = false; + this->settings.compute_preconditioner = true; + this->settings.compute_timings = false; + + this->settings.check_duality_gap = false; + this->settings.eps_duality_gap_abs = 1.E-4; + this->settings.eps_duality_gap_rel = 0; + + this->settings.preconditioner_max_iter = 10; + this->settings.preconditioner_accuracy = 1.E-3; + this->settings.primal_infeasibility_solving = false; + this->settings.eps_primal_inf = 1.E-4; + this->settings.eps_dual_inf = 1.E-4; + this->settings.bcl_update = true; + this->settings.merit_function_type = MeritFunctionType::GPDAL; + this->settings.alpha_gpdal = 0.95; + + this->settings.check_solved_option = CheckSolvedStatus::ITERATION_BASED; + this->settings.check_termination = 25; + this->settings.frequence_infeasibility_check = 1; + + this->settings.sparse_backend = SparseBackend::Automatic; + this->settings.default_H_eigenvalue_estimate = 0.; + + this->settings.alpha = 1.6; + this->settings.mu_max_eq = 1E3; + this->settings.mu_max_in = 1E6; + this->settings.mu_min_eq_inv = 1E-3; + this->settings.mu_min_in_inv = 1E-6; + this->settings.adaptive_mu = true; + this->settings.adaptive_mu_interval = 50; + this->settings.adaptive_mu_tolerance = 5.; + this->settings.polish = false; + this->settings.delta = 1E-6; + this->settings.polish_refine_iter = 3; + } /*! - * Setups the QP model (with dense matrix format) and equilibrates it if - * specified by the user. - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param l_box lower box inequality constraint vector input defining the QP - * model. - * @param u_box uppper box inequality constraint vector input defining the QP - * model. - * @param compute_preconditioner boolean parameter for executing or not the - * preconditioner. - * @param rho proximal step size wrt primal variable. - * @param mu_eq proximal step size wrt equality constrained multiplier. - * @param mu_in proximal step size wrt inequality constrained multiplier. - * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H + * Initialize ProxQP-specific results. */ - void init(optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> l_box, - optional> u_box, - bool compute_preconditioner = true, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional manual_minimal_H_eigenvalue = nullopt) + void init_derived_results() { - - // dense case - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); - } - settings.compute_preconditioner = compute_preconditioner; - PROXSUITE_THROW_PRETTY( - box_constraints == false && (l_box != nullopt || u_box != nullopt), - std::invalid_argument, - "wrong model setup: the QP object is designed without box " - "constraints, but is initialized with lower or upper box inequalities."); - if (l_box != nullopt && l_box.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE(l_box.value().size(), - model.dim, - "the dimension wrt the primal variable x " - "variable for initializing l_box " - "is not valid."); - } else { - l_box.reset(); - } - if (u_box != nullopt && u_box.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE(u_box.value().size(), - model.dim, - "the dimension wrt the primal variable x " - "variable for initializing u_box " - "is not valid."); - } else { - l_box.reset(); - } - // check the model is valid - if (g != nullopt && g.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - g.value().size(), - model.dim, - "the dimension wrt the primal variable x variable for initializing g " - "is not valid."); - } else { - g.reset(); - } - if (b != nullopt && b.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - b.value().size(), - model.n_eq, - "the dimension wrt equality constrained variables for initializing b " - "is not valid."); - } else { - b.reset(); - } - if (u != nullopt && u.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - u.value().size(), - model.n_in, - "the dimension wrt inequality constrained variables for initializing u " - "is not valid."); - } else { - u.reset(); - } - if (u_box != nullopt && u_box.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - u_box.value().size(), - model.dim, - "the dimension wrt box inequality constrained variables for " - "initializing u_box " - "is not valid."); - } else { - u_box.reset(); - } - if (l != nullopt && l.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - l.value().size(), - model.n_in, - "the dimension wrt inequality constrained variables for initializing l " - "is not valid."); - } else { - l.reset(); - } - if (l_box != nullopt && l_box.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - l_box.value().size(), - model.dim, - "the dimension wrt box inequality constrained variables for " - "initializing l_box " - "is not valid."); - } else { - l_box.reset(); - } - if (H != nullopt && H.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().rows(), - model.dim, - "the row dimension for initializing H is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - H.value().cols(), - model.dim, - "the column dimension for initializing H is not valid."); - } else { - H.reset(); - } - if (A != nullopt && A.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().rows(), - model.n_eq, - "the row dimension for initializing A is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - A.value().cols(), - model.dim, - "the column dimension for initializing A is not valid."); - } else { - A.reset(); - } - if (C != nullopt && C.value().size() != 0) { - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().rows(), - model.n_in, - "the row dimension for initializing C is not valid."); - PROXSUITE_CHECK_ARGUMENT_SIZE( - C.value().cols(), - model.dim, - "the column dimension for initializing C is not valid."); - } else { - C.reset(); - } - if (settings.initial_guess == - InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) { - work.refactorize = - true; // necessary for the first solve (then refactorize only if there - // is an update of the matrices) - } else { - work.refactorize = false; - } - work.proximal_parameter_update = false; - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); - } - PreconditionerStatus preconditioner_status; - if (compute_preconditioner) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; - } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::IDENTITY; - } - proxsuite::proxqp::dense::update_proximal_parameters( - settings, results, work, rho, mu_eq, mu_in); - proxsuite::proxqp::dense:: - update_default_rho_with_minimal_Hessian_eigen_value( - manual_minimal_H_eigenvalue, results, settings); - proxsuite::proxqp::dense::setup(H, - g, - A, - b, - C, - l, - u, - l_box, - u_box, - settings, - model, - work, - results, - box_constraints, - ruiz, - preconditioner_status, - hessian_type); - work.is_initialized = true; - if (settings.compute_timings) { - results.info.setup_time = work.timer.elapsed().user; // in microseconds - } - }; + this->results.info.mu_eq = 1E-3; + this->results.info.mu_in = 1E-1; + this->results.info.mu_eq_inv = 1E3; + this->results.info.mu_in_inv = 1E1; + } /*! - * Updates the QP model (with dense matrix format) and re-equilibrates it if - * specified by the user. - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param update_preconditioner bool parameter for updating or not the - * preconditioner and the associated scaled model. - * @param rho proximal step size wrt primal variable. - * @param mu_eq proximal step size wrt equality constrained multiplier. - * @param mu_in proximal step size wrt inequality constrained multiplier. - * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H - * @note The init method should be called before update. If it has not been - * done before, init is called depending on the is_initialized flag. + * ProxQP-specific solve implementation. + * Calls the ProxQP algorithm. */ - void update(optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - bool update_preconditioner = false, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional manual_minimal_H_eigenvalue = nullopt) + void solve_implem() { - PROXSUITE_THROW_PRETTY( - box_constraints == true, - std::invalid_argument, - "wrong model setup: the QP object is designed without box " - "constraints, but the update does not include lower or upper box " - "inequalities."); - settings.update_preconditioner = update_preconditioner; - if (!work.is_initialized) { - init(H, g, A, b, C, l, u, update_preconditioner, rho, mu_eq, mu_in); - return; - } - // dense case - work.refactorize = false; - work.proximal_parameter_update = false; - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); - } - PreconditionerStatus preconditioner_status; - if (update_preconditioner) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; - } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::KEEP; - } - const bool matrix_update = - !(H == nullopt && g == nullopt && A == nullopt && b == nullopt && - C == nullopt && u == nullopt && l == nullopt); - if (matrix_update) { - typedef optional> optional_VecRef; - proxsuite::proxqp::dense::update(H, - g, - A, - b, - C, - l, - u, - optional_VecRef(nullopt), - optional_VecRef(nullopt), - model, - work, - box_constraints); - } - proxsuite::proxqp::dense::update_proximal_parameters( - settings, results, work, rho, mu_eq, mu_in); - proxsuite::proxqp::dense:: - update_default_rho_with_minimal_Hessian_eigen_value( - manual_minimal_H_eigenvalue, results, settings); - typedef optional> optional_MatRef; - typedef optional> optional_VecRef; - proxsuite::proxqp::dense::setup(/* avoid double assignation */ - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - settings, - model, - work, - results, - box_constraints, - ruiz, - preconditioner_status, - hessian_type); + qp_solve( // + this->settings, + this->model, + this->results, + this->work, + this->is_box_constrained(), + this->which_dense_backend(), + this->which_hessian_type(), + this->ruiz); + } +}; - if (settings.compute_timings) { - results.info.setup_time = work.timer.elapsed().user; // in microseconds - } - }; +/// +/// @brief This class defines the ProxQP default parameter +/// configuration of the function proxqp::dense::solve. +/// +template +struct ProxQPConfig +{ + optional eps_abs; + optional eps_rel; + optional rho; + optional mu_eq; + optional mu_in; + optional verbose; + bool compute_preconditioner; + bool compute_timings; + optional max_iter; + InitialGuessStatus initial_guess; + bool check_duality_gap; + optional eps_duality_gap_abs; + optional eps_duality_gap_rel; + bool primal_infeasibility_solving; + optional manual_minimal_H_eigenvalue; + + ProxQPConfig(optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt) + : eps_abs(eps_abs) + , eps_rel(eps_rel) + , rho(rho) + , mu_eq(mu_eq) + , mu_in(mu_in) + , verbose(verbose) + , compute_preconditioner(compute_preconditioner) + , compute_timings(compute_timings) + , max_iter(max_iter) + , initial_guess(initial_guess) + , check_duality_gap(check_duality_gap) + , eps_duality_gap_abs(eps_duality_gap_abs) + , eps_duality_gap_rel(eps_duality_gap_rel) + , primal_infeasibility_solving(primal_infeasibility_solving) + , manual_minimal_H_eigenvalue(manual_minimal_H_eigenvalue) + { + } /*! - * Updates the QP model (with dense matrix format) and re-equilibrates it if - * specified by the user. - * @param H quadratic cost input defining the QP model. - * @param g linear cost input defining the QP model. - * @param A equality constraint matrix input defining the QP model. - * @param b equality constraint vector input defining the QP model. - * @param C inequality constraint matrix input defining the QP model. - * @param l lower inequality constraint vector input defining the QP model. - * @param u upper inequality constraint vector input defining the QP model. - * @param l_box lower inequality constraint vector input defining the QP - * model. - * @param u_box upper inequality constraint vector input defining the QP - * model. - * @param update_preconditioner bool parameter for updating or not the - * preconditioner and the associated scaled model. - * @param rho proximal step size wrt primal variable. - * @param mu_eq proximal step size wrt equality constrained multiplier. - * @param mu_in proximal step size wrt inequality constrained multiplier. - * @param manual_minimal_H_eigenvalue manual minimal eigenvalue proposed for H - * @note The init method should be called before update. If it has not been - * done before, init is called depending on the is_initialized flag. + * ProxQP settings initialization. */ - void update(optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> l_box, - optional> u_box, - bool update_preconditioner = false, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional manual_minimal_H_eigenvalue = nullopt) + void init_derived_settings(Settings& settings) const { - PROXSUITE_THROW_PRETTY( - box_constraints == false && (l_box != nullopt || u_box != nullopt), - std::invalid_argument, - "wrong model setup: the QP object is designed without box " - "constraints, but the update includes lower or upper box inequalities."); - settings.update_preconditioner = update_preconditioner; - if (!work.is_initialized) { - init(H, - g, - A, - b, - C, - l, - u, - l_box, - u_box, - update_preconditioner, - rho, - mu_eq, - mu_in); - return; + settings.initial_guess = initial_guess; + settings.check_duality_gap = check_duality_gap; + settings.compute_timings = compute_timings; + settings.primal_infeasibility_solving = primal_infeasibility_solving; + + if (eps_abs != nullopt) { + settings.eps_abs = eps_abs.value(); } - // dense case - work.refactorize = false; - work.proximal_parameter_update = false; - if (settings.compute_timings) { - work.timer.stop(); - work.timer.start(); + if (eps_rel != nullopt) { + settings.eps_rel = eps_rel.value(); } - PreconditionerStatus preconditioner_status; - if (update_preconditioner) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; - } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::KEEP; + if (verbose != nullopt) { + settings.verbose = verbose.value(); } - const bool matrix_update = - !(H == nullopt && g == nullopt && A == nullopt && b == nullopt && - C == nullopt && u == nullopt && l == nullopt && u_box == nullopt && - l_box == nullopt); - if (matrix_update) { - proxsuite::proxqp::dense::update( - H, g, A, b, C, l, u, l_box, u_box, model, work, box_constraints); + if (max_iter != nullopt) { + settings.max_iter = max_iter.value(); } - proxsuite::proxqp::dense::update_proximal_parameters( - settings, results, work, rho, mu_eq, mu_in); - proxsuite::proxqp::dense:: - update_default_rho_with_minimal_Hessian_eigen_value( - manual_minimal_H_eigenvalue, results, settings); - typedef optional> optional_MatRef; - typedef optional> optional_VecRef; - proxsuite::proxqp::dense::setup(/* avoid double assignation */ - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_MatRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - optional_VecRef(nullopt), - settings, - model, - work, - results, - box_constraints, - ruiz, - preconditioner_status, - hessian_type); - - if (settings.compute_timings) { - results.info.setup_time = work.timer.elapsed().user; // in microseconds + if (eps_duality_gap_abs != nullopt) { + settings.eps_duality_gap_abs = eps_duality_gap_abs.value(); } - }; - /*! - * Solves the QP problem using PRXOQP algorithm. - */ - void solve() - { - qp_solve( // - settings, - model, - results, - work, - box_constraints, - dense_backend, - hessian_type, - ruiz); - }; + if (eps_duality_gap_rel != nullopt) { + settings.eps_duality_gap_rel = eps_duality_gap_rel.value(); + } + } /*! - * Solves the QP problem using PROXQP algorithm using a warm start. - * @param x primal warm start. - * @param y dual equality warm start. - * @param z dual inequality warm start. + * Call to init() from QPBase without box constraints. */ - void solve(optional> x, - optional> y, - optional> z) + void init_qp(QP& qp, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u) const { - proxsuite::proxqp::dense::warm_start(x, y, z, results, settings, model); - qp_solve( // - settings, - model, - results, - work, - box_constraints, - dense_backend, - hessian_type, - ruiz); - }; + if (manual_minimal_H_eigenvalue != nullopt) { + qp.init(H, + g, + A, + b, + C, + l, + u, + compute_preconditioner, + rho, + mu_eq, + mu_in, + manual_minimal_H_eigenvalue.value()); + } else { + qp.init(H, + g, + A, + b, + C, + l, + u, + compute_preconditioner, + rho, + mu_eq, + mu_in, + nullopt); + } + } /*! - * Clean-ups solver's results and workspace. + * Call to QPBase init() without box constraints. */ - void cleanup() + void init_qp_box(QP& qp, + optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box) const { - results.cleanup(settings); - work.cleanup(box_constraints); + if (manual_minimal_H_eigenvalue != nullopt) { + qp.init(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + compute_preconditioner, + rho, + mu_eq, + mu_in, + manual_minimal_H_eigenvalue.value()); + } else { + qp.init(H, + g, + A, + b, + C, + l, + u, + l_box, + u_box, + compute_preconditioner, + rho, + mu_eq, + mu_in, + nullopt); + } } }; + /*! * Solves the QP problem using PROXQP algorithm without the need to define a QP - * object, with matrices defined by Dense Eigen matrices. It is possible to set - * up some of the solver parameters (warm start, initial guess option, proximal - * step sizes, absolute and relative accuracies, maximum number of iterations, - * preconditioner execution). There are no box constraints in the model. + * object, with matrices defined by Dense Eigen matrices. It is possible to + * set up some of the solver parameters (warm start, initial guess option, + * proximal step sizes, absolute and relative accuracies, maximum number of + * iterations, preconditioner execution). There are no box constraints in the + * model. * @param H quadratic cost input defining the QP model. * @param g linear cost input defining the QP model. * @param A equality constraint matrix input defining the QP model. @@ -977,8 +477,8 @@ struct QP * @param x primal warm start. * @param y dual equality constraint warm start. * @param z dual inequality constraint warm start. - * @param verbose if set to true, the solver prints more information about each - * iteration. + * @param verbose if set to true, the solver prints more information about + * each iteration. * @param compute_preconditioner bool parameter for executing or not the * preconditioner. * @param compute_timings boolean parameter for computing the solver timings. @@ -990,107 +490,67 @@ struct QP * @param max_iter maximum number of iteration. * @param initial_guess initial guess option for warm starting or not the * initial iterate values. - * @param check_duality_gap If set to true, include the duality gap in absolute - * and relative stopping criteria. + * @param check_duality_gap If set to true, include the duality gap in + * absolute and relative stopping criteria. * @param eps_duality_gap_abs absolute accuracy threshold for the duality-gap * criterion. * @param eps_duality_gap_rel relative accuracy threshold for the duality-gap * criterion. */ template -proxqp::Results -solve( - optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> x = nullopt, - optional> y = nullopt, - optional> z = nullopt, - optional eps_abs = nullopt, - optional eps_rel = nullopt, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional verbose = nullopt, - bool compute_preconditioner = true, - bool compute_timings = false, - optional max_iter = nullopt, - proxsuite::proxqp::InitialGuessStatus initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, - bool check_duality_gap = false, - optional eps_duality_gap_abs = nullopt, - optional eps_duality_gap_rel = nullopt, - bool primal_infeasibility_solving = false, - optional manual_minimal_H_eigenvalue = nullopt) +Results +solve(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt, + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt) { - isize n(0); - isize n_eq(0); - isize n_in(0); - if (H != nullopt) { - n = H.value().rows(); - } - if (A != nullopt) { - n_eq = A.value().rows(); - } - if (C != nullopt) { - n_in = C.value().rows(); - } - - QP Qp(n, n_eq, n_in, false, DenseBackend::PrimalDualLDLT); - Qp.settings.initial_guess = initial_guess; - Qp.settings.check_duality_gap = check_duality_gap; - - if (eps_abs != nullopt) { - Qp.settings.eps_abs = eps_abs.value(); - } - if (eps_rel != nullopt) { - Qp.settings.eps_rel = eps_rel.value(); - } - if (verbose != nullopt) { - Qp.settings.verbose = verbose.value(); - } - if (max_iter != nullopt) { - Qp.settings.max_iter = max_iter.value(); - } - if (eps_duality_gap_abs != nullopt) { - Qp.settings.eps_duality_gap_abs = eps_duality_gap_abs.value(); - } - if (eps_duality_gap_rel != nullopt) { - Qp.settings.eps_duality_gap_rel = eps_duality_gap_rel.value(); - } - Qp.settings.compute_timings = compute_timings; - Qp.settings.primal_infeasibility_solving = primal_infeasibility_solving; - if (manual_minimal_H_eigenvalue != nullopt) { - Qp.init(H, - g, - A, - b, - C, - l, - u, - compute_preconditioner, - rho, - mu_eq, - mu_in, - manual_minimal_H_eigenvalue.value()); - } else { - Qp.init( - H, g, A, b, C, l, u, compute_preconditioner, rho, mu_eq, mu_in, nullopt); - } - Qp.solve(x, y, z); + ProxQPConfig config(eps_abs, + eps_rel, + rho, + mu_eq, + mu_in, + verbose, + compute_preconditioner, + compute_timings, + max_iter, + initial_guess, + check_duality_gap, + eps_duality_gap_abs, + eps_duality_gap_rel, + primal_infeasibility_solving, + manual_minimal_H_eigenvalue); - return Qp.results; + return common::dense::solve_base, ProxQPConfig, T>( + config, H, g, A, b, C, l, u, x, y, z); } /*! * Solves the QP problem using PROXQP algorithm without the need to define a QP - * object, with matrices defined by Dense Eigen matrices. It is possible to set - * up some of the solver parameters (warm start, initial guess option, proximal - * step sizes, absolute and relative accuracies, maximum number of iterations, - * preconditioner execution). + * object, with matrices defined by Dense Eigen matrices. It is possible to + * set up some of the solver parameters (warm start, initial guess option, + * proximal step sizes, absolute and relative accuracies, maximum number of + * iterations, preconditioner execution). * @param H quadratic cost input defining the QP model. * @param g linear cost input defining the QP model. * @param A equality constraint matrix input defining the QP model. @@ -1104,11 +564,11 @@ solve( * model. * @param x primal warm start. * @param y dual equality constraint warm start. - * @param z dual inequality constraint warm start. The upper part must contain a - * warm start for inequality constraints wrt C matrix, whereas the latter wrt - * the box inequalities. - * @param verbose if set to true, the solver prints more information about each - * iteration. + * @param z dual inequality constraint warm start. The upper part must contain + * a warm start for inequality constraints wrt C matrix, whereas the latter + * wrt the box inequalities. + * @param verbose if set to true, the solver prints more information about + * each iteration. * @param compute_preconditioner bool parameter for executing or not the * preconditioner. * @param compute_timings boolean parameter for computing the solver timings. @@ -1120,116 +580,62 @@ solve( * @param max_iter maximum number of iteration. * @param initial_guess initial guess option for warm starting or not the * initial iterate values. - * @param check_duality_gap If set to true, include the duality gap in absolute - * and relative stopping criteria. + * @param check_duality_gap If set to true, include the duality gap in + * absolute and relative stopping criteria. * @param eps_duality_gap_abs absolute accuracy threshold for the duality-gap * criterion. * @param eps_duality_gap_rel relative accuracy threshold for the duality-gap * criterion. */ template -proxqp::Results -solve( - optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> l_box, - optional> u_box, - optional> x = nullopt, - optional> y = nullopt, - optional> z = nullopt, - optional eps_abs = nullopt, - optional eps_rel = nullopt, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional verbose = nullopt, - bool compute_preconditioner = true, - bool compute_timings = false, - optional max_iter = nullopt, - proxsuite::proxqp::InitialGuessStatus initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, - bool check_duality_gap = false, - optional eps_duality_gap_abs = nullopt, - optional eps_duality_gap_rel = nullopt, - bool primal_infeasibility_solving = false, - optional manual_minimal_H_eigenvalue = nullopt) +Results +solve(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> l_box, + optional> u_box, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt, + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt) { - isize n(0); - isize n_eq(0); - isize n_in(0); - if (H != nullopt) { - n = H.value().rows(); - } - if (A != nullopt) { - n_eq = A.value().rows(); - } - if (C != nullopt) { - n_in = C.value().rows(); - } - - QP Qp(n, n_eq, n_in, true, DenseBackend::PrimalDualLDLT); - Qp.settings.initial_guess = initial_guess; - Qp.settings.check_duality_gap = check_duality_gap; - - if (eps_abs != nullopt) { - Qp.settings.eps_abs = eps_abs.value(); - } - if (eps_rel != nullopt) { - Qp.settings.eps_rel = eps_rel.value(); - } - if (verbose != nullopt) { - Qp.settings.verbose = verbose.value(); - } - if (max_iter != nullopt) { - Qp.settings.max_iter = max_iter.value(); - } - if (eps_duality_gap_abs != nullopt) { - Qp.settings.eps_duality_gap_abs = eps_duality_gap_abs.value(); - } - if (eps_duality_gap_rel != nullopt) { - Qp.settings.eps_duality_gap_rel = eps_duality_gap_rel.value(); - } - Qp.settings.compute_timings = compute_timings; - Qp.settings.primal_infeasibility_solving = primal_infeasibility_solving; - if (manual_minimal_H_eigenvalue != nullopt) { - Qp.init(H, - g, - A, - b, - C, - l, - u, - l_box, - u_box, - compute_preconditioner, - rho, - mu_eq, - mu_in, - manual_minimal_H_eigenvalue.value()); - } else { - Qp.init(H, - g, - A, - b, - C, - l, - u, - l_box, - u_box, - compute_preconditioner, - rho, - mu_eq, - mu_in, - nullopt); - } - Qp.solve(x, y, z); + ProxQPConfig config(eps_abs, + eps_rel, + rho, + mu_eq, + mu_in, + verbose, + compute_preconditioner, + compute_timings, + max_iter, + initial_guess, + check_duality_gap, + eps_duality_gap_abs, + eps_duality_gap_rel, + primal_infeasibility_solving, + manual_minimal_H_eigenvalue); - return Qp.results; + return common::dense::solve_base_box, ProxQPConfig, T>( + config, H, g, A, b, C, l, u, l_box, u_box, x, y, z); } template diff --git a/include/proxsuite/proxqp/parallel/qp_solve.hpp b/include/proxsuite/proxqp/parallel/qp_solve.hpp index ff37efc08..35c58d80b 100644 --- a/include/proxsuite/proxqp/parallel/qp_solve.hpp +++ b/include/proxsuite/proxqp/parallel/qp_solve.hpp @@ -86,7 +86,7 @@ void qp_solve_backward_in_parallel( optional num_threads, std::vector>& qps, - std::vector>& loss_derivatives, + std::vector>& loss_derivatives, T eps = 1.E-4, T rho_new = 1.E-6, T mu_new = 1.E-6) @@ -114,7 +114,7 @@ void qp_solve_backward_in_parallel( optional num_threads, proxqp::dense::BatchQP& qps, - std::vector>& loss_derivatives, + std::vector>& loss_derivatives, T eps = 1.E-4, T rho_new = 1.E-6, T mu_new = 1.E-6) diff --git a/include/proxsuite/proxqp/sparse/aliases.hpp b/include/proxsuite/proxqp/sparse/aliases.hpp new file mode 100644 index 000000000..ce3c3b4c7 --- /dev/null +++ b/include/proxsuite/proxqp/sparse/aliases.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2025 INRIA +// +/** + * @file aliases.hpp + */ + +#ifndef PROXSUITE_PROXQP_SPARSE_ALIASES_HPP +#define PROXSUITE_PROXQP_SPARSE_ALIASES_HPP + +#include "proxsuite/common/solvers.hpp" +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/settings.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/common/timings.hpp" + +namespace proxsuite { +namespace proxqp { +namespace sparse { + +using proxsuite::common::from_eigen; +using proxsuite::common::isize; + +using proxsuite::common::HessianType; +using proxsuite::common::InitialGuessStatus; +using proxsuite::common::MeritFunctionType; +using proxsuite::common::PreconditionerStatus; +using proxsuite::common::QPSolver; +using proxsuite::common::QPSolverOutput; +using proxsuite::common::SparseBackend; +using proxsuite::common::Timer; + +using proxsuite::common::Results; +using proxsuite::common::Settings; + +using proxsuite::common::VectorView; +using proxsuite::common::VectorViewMut; + +} // namespace sparse +} // namespace proxqp +} // namespace proxsuite + +#endif /* end of include guard PROXSUITE_PROXQP_SPARSE_ALIASES_HPP */ diff --git a/include/proxsuite/proxqp/sparse/fwd.hpp b/include/proxsuite/proxqp/sparse/fwd.hpp index 99b133d9e..e2641b7b2 100644 --- a/include/proxsuite/proxqp/sparse/fwd.hpp +++ b/include/proxsuite/proxqp/sparse/fwd.hpp @@ -7,14 +7,14 @@ #include #include "proxsuite/linalg/veg/vec.hpp" -#include "proxsuite/proxqp/dense/views.hpp" +#include "proxsuite/common/dense/views.hpp" #include "proxsuite/helpers/common.hpp" namespace proxsuite { namespace proxqp { namespace sparse { -using dense::infty_norm; +using common::dense::infty_norm; using proxsuite::linalg::veg::i64; using proxsuite::linalg::veg::isize; using proxsuite::linalg::veg::usize; diff --git a/include/proxsuite/proxqp/sparse/helpers.hpp b/include/proxsuite/proxqp/sparse/helpers.hpp index c162484e7..f99982fa9 100644 --- a/include/proxsuite/proxqp/sparse/helpers.hpp +++ b/include/proxsuite/proxqp/sparse/helpers.hpp @@ -5,10 +5,14 @@ #ifndef PROXSUITE_PROXQP_SPARSE_HELPERS_HPP #define PROXSUITE_PROXQP_SPARSE_HELPERS_HPP +#include "proxsuite/common/status.hpp" +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/settings.hpp" #include #include #include +#include #include #include namespace proxsuite { @@ -46,7 +50,7 @@ power_iteration(SparseMat& H, eig = rhs.dot(dw); // calculate associated error err_v = dw - eig * rhs; - T err = proxsuite::proxqp::dense::infty_norm(err_v); + T err = proxsuite::common::dense::infty_norm(err_v); // std::cout << "power iteration max: i " << i << " err " << err << // std::endl; if (err <= power_iteration_accuracy) { @@ -95,7 +99,7 @@ min_eigen_value_via_modified_power_iteration(SparseMat& H, eig = rhs.dot(dw); // calculate associated error err_v = dw - eig * rhs; - T err = proxsuite::proxqp::dense::infty_norm(err_v); + T err = proxsuite::common::dense::infty_norm(err_v); // std::cout << "power iteration min: i " << i << " err " << err << // std::endl; if (err <= power_iteration_accuracy) { diff --git a/include/proxsuite/proxqp/sparse/model.hpp b/include/proxsuite/proxqp/sparse/model.hpp index 110bc986c..c4e14e99e 100644 --- a/include/proxsuite/proxqp/sparse/model.hpp +++ b/include/proxsuite/proxqp/sparse/model.hpp @@ -12,6 +12,9 @@ namespace proxsuite { namespace proxqp { namespace sparse { + +; + /// /// @brief This class stores the model of the QP problem. /// @@ -189,34 +192,26 @@ struct SparseModel { } - auto as_view() -> proxqp::dense::QpView + auto as_view() -> common::dense::QpView { return { - { proxqp::from_eigen, H }, - { proxqp::from_eigen, g }, - { proxqp::from_eigen, A }, - { proxqp::from_eigen, b }, - { proxqp::from_ptr_rows_cols_stride, - nullptr, - 0, - proxqp::isize(H.rows()), - 0 }, - { proxqp::from_ptr_size, nullptr, 0 }, + { common::from_eigen, H }, + { common::from_eigen, g }, + { common::from_eigen, A }, + { common::from_eigen, b }, + { common::from_ptr_rows_cols_stride, nullptr, 0, isize(H.rows()), 0 }, + { common::from_ptr_size, nullptr, 0 }, }; } - auto as_mut() -> proxqp::dense::QpViewMut + auto as_mut() -> common::dense::QpViewMut { return { - { proxqp::from_eigen, H }, - { proxqp::from_eigen, g }, - { proxqp::from_eigen, A }, - { proxqp::from_eigen, b }, - { proxqp::from_ptr_rows_cols_stride, - nullptr, - 0, - proxqp::isize(H.rows()), - 0 }, - { proxqp::from_ptr_size, nullptr, 0 }, + { common::from_eigen, H }, + { common::from_eigen, g }, + { common::from_eigen, A }, + { common::from_eigen, b }, + { common::from_ptr_rows_cols_stride, nullptr, 0, isize(H.rows()), 0 }, + { common::from_ptr_size, nullptr, 0 }, }; } }; diff --git a/include/proxsuite/proxqp/sparse/preconditioner/identity.hpp b/include/proxsuite/proxqp/sparse/preconditioner/identity.hpp index 7bf99bd52..2f152e4b2 100644 --- a/include/proxsuite/proxqp/sparse/preconditioner/identity.hpp +++ b/include/proxsuite/proxqp/sparse/preconditioner/identity.hpp @@ -6,6 +6,9 @@ #ifndef PROXSUITE_PROXQP_SPARSE_PRECOND_IDENTITY_HPP #define PROXSUITE_PROXQP_SPARSE_PRECOND_IDENTITY_HPP +#include "proxsuite/common/dense/views.hpp" +#include "proxsuite/proxqp/sparse/aliases.hpp" + namespace proxsuite { namespace proxqp { namespace sparse { diff --git a/include/proxsuite/proxqp/sparse/preconditioner/ruiz.hpp b/include/proxsuite/proxqp/sparse/preconditioner/ruiz.hpp index 9a30d40c6..55223a4cb 100644 --- a/include/proxsuite/proxqp/sparse/preconditioner/ruiz.hpp +++ b/include/proxsuite/proxqp/sparse/preconditioner/ruiz.hpp @@ -7,6 +7,7 @@ #define PROXSUITE_PROXQP_SPARSE_PRECOND_RUIZ_HPP #include "proxsuite/proxqp/sparse/fwd.hpp" +#include "proxsuite/proxqp/sparse/aliases.hpp" namespace proxsuite { namespace proxqp { @@ -20,6 +21,7 @@ enum struct Symmetry }; namespace detail { + template void rowwise_infty_norm(T* row_norm, proxsuite::linalg::sparse::MatRef m) @@ -331,6 +333,8 @@ ruiz_scale_qp_in_place( // } } // namespace detail +; + template struct RuizEquilibration { @@ -380,7 +384,7 @@ struct RuizEquilibration if (execute_preconditioner) { delta.setOnes(); c = detail::ruiz_scale_qp_in_place( // - { proxqp::from_eigen, delta }, + { from_eigen, delta }, qp, epsilon, max_iter, diff --git a/include/proxsuite/proxqp/sparse/solver.hpp b/include/proxsuite/proxqp/sparse/solver.hpp index f486fd024..f24ebdf2b 100644 --- a/include/proxsuite/proxqp/sparse/solver.hpp +++ b/include/proxsuite/proxqp/sparse/solver.hpp @@ -14,10 +14,12 @@ #include #include #include -#include -#include #include -#include "proxsuite/proxqp/results.hpp" +#include +#include +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/status.hpp" +#include "proxsuite/proxqp/sparse/aliases.hpp" #include "proxsuite/proxqp/sparse/fwd.hpp" #include "proxsuite/proxqp/sparse/views.hpp" #include "proxsuite/proxqp/sparse/model.hpp" @@ -35,6 +37,8 @@ namespace proxsuite { namespace proxqp { namespace sparse { +; + template void ldl_solve(VectorViewMut sol, @@ -157,8 +161,8 @@ ldl_iter_solve_noalias( } prev_err_norm = err_norm; - ldl_solve({ proxqp::from_eigen, err }, - { proxqp::from_eigen, err }, + ldl_solve({ from_eigen, err }, + { from_eigen, err }, n_tot, ldl, iterative_solver, @@ -219,7 +223,7 @@ ldl_solve_in_place( proxsuite::linalg::veg::SliceMut active_constraints) { LDLT_TEMP_VEC_UNINIT(T, tmp, n_tot, stack); - ldl_iter_solve_noalias({ proxqp::from_eigen, tmp }, + ldl_iter_solve_noalias({ from_eigen, tmp }, rhs.as_const(), init_guess, results, @@ -393,12 +397,9 @@ qp_solve(Results& results, case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { // keep solutions but restart workspace and results results.cold_start(settings); - precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, results.x }); - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_primal_in_place({ from_eigen, results.x }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } case InitialGuessStatus::NO_INITIAL_GUESS: { @@ -409,23 +410,18 @@ qp_solve(Results& results, results.cold_start(settings); // because there was already a solve, // precond was already computed if set so precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, + { from_eigen, results.x }); // it contains the value given in entry for warm start - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { // keep workspace and results solutions except statistics results.cleanup_statistics(); - precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, results.x }); - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_primal_in_place({ from_eigen, results.x }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } } @@ -447,36 +443,29 @@ qp_solve(Results& results, } case InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT: { precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, + { from_eigen, results.x }); // meaningful for when there is an upate of the model // and one wants to warm start with previous result - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } case InitialGuessStatus::NO_INITIAL_GUESS: { break; } case InitialGuessStatus::WARM_START: { - precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, results.x }); - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_primal_in_place({ from_eigen, results.x }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } case InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT: { precond.scale_primal_in_place( - { proxsuite::proxqp::from_eigen, + { from_eigen, results.x }); // meaningful for when there is an upate of the model // and one wants to warm start with previous result - precond.scale_dual_in_place_eq( - { proxsuite::proxqp::from_eigen, results.y }); - precond.scale_dual_in_place_in( - { proxsuite::proxqp::from_eigen, results.z }); + precond.scale_dual_in_place_eq({ from_eigen, results.y }); + precond.scale_dual_in_place_in({ from_eigen, results.z }); break; } } @@ -496,9 +485,9 @@ qp_solve(Results& results, isize n_in = data.n_in; isize n_tot = n + n_eq + n_in; - VectorViewMut x{ proxqp::from_eigen, results.x }; - VectorViewMut y{ proxqp::from_eigen, results.y }; - VectorViewMut z{ proxqp::from_eigen, results.z }; + VectorViewMut x{ from_eigen, results.x }; + VectorViewMut y{ from_eigen, results.y }; + VectorViewMut z{ from_eigen, results.z }; proxsuite::linalg::sparse::MatMut kkt = data.kkt_mut(); @@ -696,8 +685,8 @@ qp_solve(Results& results, rhs.segment(n, n_eq) = b_scaled_e; rhs.segment(n + n_eq, n_in).setZero(); - ldl_solve_in_place({ proxqp::from_eigen, rhs }, - { proxqp::from_eigen, no_guess }, + ldl_solve_in_place({ from_eigen, rhs }, + { from_eigen, no_guess }, results, data, n_tot, @@ -828,11 +817,11 @@ qp_solve(Results& results, LDLT_TEMP_VEC_UNINIT(T, tmp, n, stack); tmp.setZero(); detail::noalias_symhiv_add(tmp, qp_scaled.H.to_eigen(), x_e); - precond.unscale_dual_residual_in_place({ proxqp::from_eigen, tmp }); + precond.unscale_dual_residual_in_place({ from_eigen, tmp }); - precond.unscale_primal_in_place({ proxqp::from_eigen, x_e }); - precond.unscale_dual_in_place_eq({ proxqp::from_eigen, y_e }); - precond.unscale_dual_in_place_in({ proxqp::from_eigen, z_e }); + precond.unscale_primal_in_place({ from_eigen, x_e }); + precond.unscale_dual_in_place_eq({ from_eigen, y_e }); + precond.unscale_dual_in_place_in({ from_eigen, z_e }); tmp *= 0.5; tmp += data.g; results.info.objValue = (tmp).dot(x_e); @@ -860,9 +849,9 @@ qp_solve(Results& results, results.info.dua_res = dual_feasibility_lhs; if (settings.primal_infeasibility_solving) { results.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; } else { - results.info.status = QPSolverOutput::PROXQP_SOLVED; + results.info.status = QPSolverOutput::QPSOLVER_SOLVED; } break; } @@ -871,9 +860,9 @@ qp_solve(Results& results, results.info.dua_res = dual_feasibility_lhs; if (settings.primal_infeasibility_solving) { results.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; } else { - results.info.status = QPSolverOutput::PROXQP_SOLVED; + results.info.status = QPSolverOutput::QPSOLVER_SOLVED; } break; } @@ -1044,10 +1033,9 @@ qp_solve(Results& results, // break; // } ldl_solve_in_place( - { proxqp::from_eigen, rhs }, - { proxqp::from_eigen, - dw_prev }, // todo: MAJ dw_prev avec dw pour avoir meilleur - // guess sur les solve in place + { from_eigen, rhs }, + { from_eigen, dw_prev }, // todo: MAJ dw_prev avec dw pour avoir + // meilleur guess sur les solve in place results, data, n_tot, @@ -1370,7 +1358,7 @@ qp_solve(Results& results, data, precond); if (is_primal_infeasible) { - results.info.status = QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE; + results.info.status = QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE; if (!settings.primal_infeasibility_solving) { results.info.iter += iter_inner + 1; dw_prev = dw; @@ -1378,7 +1366,7 @@ qp_solve(Results& results, } } else if (is_dual_infeasible) { if (!settings.primal_infeasibility_solving) { - results.info.status = QPSolverOutput::PROXQP_DUAL_INFEASIBLE; + results.info.status = QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE; results.info.iter += iter_inner + 1; dw_prev = dw; break; @@ -1394,9 +1382,9 @@ qp_solve(Results& results, // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ primal_dual_newton_semi_smooth(); - if ((results.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE && + if ((results.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE && !settings.primal_infeasibility_solving) || - results.info.status == QPSolverOutput::PROXQP_DUAL_INFEASIBLE) { + results.info.status == QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE) { // certificate of infeasibility results.x = dw_prev.head(data.dim); results.y = dw_prev.segment(data.dim, data.n_eq); @@ -1407,7 +1395,7 @@ qp_solve(Results& results, } if (scaled_eps == settings.eps_abs && settings.primal_infeasibility_solving && - results.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { + results.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { LDLT_TEMP_VEC(T, rhs_dim, n, stack); LDLT_TEMP_VEC(T, rhs_n_eq, n_eq, stack); LDLT_TEMP_VEC(T, rhs_n_in, n_in, stack); @@ -1454,11 +1442,11 @@ qp_solve(Results& results, results.info.dua_res = dual_feasibility_lhs_new; if (settings.primal_infeasibility_solving && results.info.status == - QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { results.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; } else { - results.info.status = QPSolverOutput::PROXQP_SOLVED; + results.info.status = QPSolverOutput::QPSOLVER_SOLVED; } break; } @@ -1466,11 +1454,12 @@ qp_solve(Results& results, results.info.pri_res = primal_feasibility_lhs_new; results.info.dua_res = dual_feasibility_lhs_new; if (settings.primal_infeasibility_solving && - results.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) { + results.info.status == + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE) { results.info.status = - QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE; + QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE; } else { - results.info.status = QPSolverOutput::PROXQP_SOLVED; + results.info.status = QPSolverOutput::QPSOLVER_SOLVED; } break; } @@ -1607,11 +1596,11 @@ qp_solve(Results& results, LDLT_TEMP_VEC_UNINIT(T, tmp, n, stack); tmp.setZero(); detail::noalias_symhiv_add(tmp, qp_scaled.H.to_eigen(), x_e); - precond.unscale_dual_residual_in_place({ proxqp::from_eigen, tmp }); + precond.unscale_dual_residual_in_place({ from_eigen, tmp }); - precond.unscale_primal_in_place({ proxqp::from_eigen, x_e }); - precond.unscale_dual_in_place_eq({ proxqp::from_eigen, y_e }); - precond.unscale_dual_in_place_in({ proxqp::from_eigen, z_e }); + precond.unscale_primal_in_place({ from_eigen, x_e }); + precond.unscale_dual_in_place_eq({ from_eigen, y_e }); + precond.unscale_dual_in_place_in({ from_eigen, z_e }); tmp *= 0.5; tmp += data.g; results.info.objValue = (tmp).dot(x_e); @@ -1629,32 +1618,32 @@ qp_solve(Results& results, std::cout << "rho updates: " << results.info.rho_updates << std::endl; std::cout << "objective: " << results.info.objValue << std::endl; switch (results.info.status) { - case QPSolverOutput::PROXQP_SOLVED: { + case QPSolverOutput::QPSOLVER_SOLVED: { std::cout << "status: " << "Solved" << std::endl; break; } - case QPSolverOutput::PROXQP_MAX_ITER_REACHED: { + case QPSolverOutput::QPSOLVER_MAX_ITER_REACHED: { std::cout << "status: " << "Maximum number of iterations reached" << std::endl; break; } - case QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE: { + case QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE: { std::cout << "status: " << "Primal infeasible" << std::endl; break; } - case QPSolverOutput::PROXQP_DUAL_INFEASIBLE: { + case QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE: { std::cout << "status: " << "Dual infeasible" << std::endl; break; } - case QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { + case QPSolverOutput::QPSOLVER_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { std::cout << "status: " << "Solved closest primal feasible" << std::endl; break; } - case QPSolverOutput::PROXQP_NOT_RUN: { + case QPSolverOutput::QPSOLVER_NOT_RUN: { std::cout << "status: " << "Solver not run" << std::endl; break; diff --git a/include/proxsuite/proxqp/sparse/utils.hpp b/include/proxsuite/proxqp/sparse/utils.hpp index fe1e4b275..4706b906c 100644 --- a/include/proxsuite/proxqp/sparse/utils.hpp +++ b/include/proxsuite/proxqp/sparse/utils.hpp @@ -13,15 +13,16 @@ #include "proxsuite/helpers/common.hpp" #include #include -#include "proxsuite/proxqp/sparse/workspace.hpp" #include #include #include -#include -#include #include -#include "proxsuite/proxqp/results.hpp" -#include "proxsuite/proxqp/utils/prints.hpp" +#include +#include +#include "proxsuite/common/results.hpp" +#include "proxsuite/common/utils/prints.hpp" +#include "proxsuite/proxqp/sparse/workspace.hpp" +#include "proxsuite/proxqp/sparse/aliases.hpp" #include "proxsuite/proxqp/sparse/views.hpp" #include "proxsuite/proxqp/sparse/model.hpp" #include "proxsuite/proxqp/sparse/preconditioner/ruiz.hpp" @@ -31,6 +32,8 @@ namespace proxsuite { namespace proxqp { namespace sparse { +; + template void print_setup_header(const Settings& settings, @@ -38,7 +41,7 @@ print_setup_header(const Settings& settings, const Model& model) { - proxsuite::proxqp::print_preambule(); + print_preambule(QPSolver::PROXQP); // Print variables and constraints std::cout << "problem: " << std::noshowpos << std::endl; @@ -266,11 +269,11 @@ noalias_gevmmv_add(OutL&& out_l, { // noalias general vector matrix matrix vector add noalias_gevmmv_add_impl( - { proxqp::from_eigen, out_l }, - { proxqp::from_eigen, out_r }, + { from_eigen, out_l }, + { from_eigen, out_r }, { proxsuite::linalg::sparse::from_eigen, a }, - { proxqp::from_eigen, in_l }, - { proxqp::from_eigen, in_r }); + { from_eigen, in_l }, + { from_eigen, in_r }); } template @@ -279,9 +282,9 @@ noalias_symhiv_add(Out&& out, A const& a, In const& in) { // noalias symmetric (hi) matrix vector add noalias_symhiv_add_impl( - { proxqp::from_eigen, out }, + { from_eigen, out }, { proxsuite::linalg::sparse::from_eigen, a }, - { proxqp::from_eigen, in }); + { from_eigen, in }); } template @@ -646,9 +649,9 @@ unscaled_primal_dual_residual( dual_residual_scaled += tmp; precond.unscale_dual_residual_in_place( - { proxqp::from_eigen, tmp }); // contains unscaled Hx + { from_eigen, tmp }); // contains unscaled Hx dual_feasibility_rhs_0 = infty_norm(tmp); - precond.unscale_primal_in_place({ proxqp::from_eigen, x_e }); + precond.unscale_primal_in_place({ from_eigen, x_e }); results.info.duality_gap = x_e.dot(data.g); // contains gTx rhs_duality_gap = std::fabs(results.info.duality_gap); @@ -656,15 +659,15 @@ unscaled_primal_dual_residual( results.info.duality_gap += xHx; rhs_duality_gap = std::max(rhs_duality_gap, std::abs(xHx)); tmp += data.g; // contains now Hx+g - precond.scale_primal_in_place({ proxqp::from_eigen, x_e }); + precond.scale_primal_in_place({ from_eigen, x_e }); - precond.unscale_dual_in_place_eq({ proxsuite::proxqp::from_eigen, y_e }); + precond.unscale_dual_in_place_eq({ from_eigen, y_e }); const T by = (data.b).dot(y_e); results.info.duality_gap += by; rhs_duality_gap = std::max(rhs_duality_gap, std::abs(by)); - precond.scale_dual_in_place_eq({ proxsuite::proxqp::from_eigen, y_e }); + precond.scale_dual_in_place_eq({ from_eigen, y_e }); - precond.unscale_dual_in_place_in({ proxsuite::proxqp::from_eigen, z_e }); + precond.unscale_dual_in_place_in({ from_eigen, z_e }); const T zl = helpers::select(work.active_set_low, results.z, 0) @@ -678,7 +681,7 @@ unscaled_primal_dual_residual( results.info.duality_gap += zu; rhs_duality_gap = std::max(rhs_duality_gap, std::abs(zu)); - precond.scale_dual_in_place_in({ proxsuite::proxqp::from_eigen, z_e }); + precond.scale_dual_in_place_in({ from_eigen, z_e }); } { @@ -691,7 +694,7 @@ unscaled_primal_dual_residual( dual_residual_scaled += ATy; - precond.unscale_dual_residual_in_place({ proxqp::from_eigen, ATy }); + precond.unscale_dual_residual_in_place({ from_eigen, ATy }); dual_feasibility_rhs_1 = infty_norm(ATy); } @@ -705,15 +708,15 @@ unscaled_primal_dual_residual( dual_residual_scaled += CTz; - precond.unscale_dual_residual_in_place({ proxqp::from_eigen, CTz }); + precond.unscale_dual_residual_in_place({ from_eigen, CTz }); dual_feasibility_rhs_3 = infty_norm(CTz); } precond.unscale_primal_residual_in_place_eq( - { proxqp::from_eigen, primal_residual_eq_scaled }); + { from_eigen, primal_residual_eq_scaled }); primal_feasibility_eq_rhs_0 = infty_norm(primal_residual_eq_scaled); precond.unscale_primal_residual_in_place_in( - { proxqp::from_eigen, primal_residual_in_scaled_up }); + { from_eigen, primal_residual_in_scaled_up }); primal_feasibility_in_rhs_0 = infty_norm(primal_residual_in_scaled_up); auto b = data.b; @@ -730,42 +733,38 @@ unscaled_primal_dual_residual( std::max(primal_feasibility_eq_lhs, primal_feasibility_in_lhs); if ((settings.primal_infeasibility_solving && - results.info.status == QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE)) { + results.info.status == QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE)) { tmp.setZero(); { results.se = primal_residual_eq_scaled; results.si = primal_residual_in_scaled_lo; precond.unscale_primal_residual_in_place_eq( - { proxqp::from_eigen, - primal_residual_eq_scaled }); // E^{-1}(unscaled Ax-b) + { from_eigen, primal_residual_eq_scaled }); // E^{-1}(unscaled Ax-b) tmp.noalias() = qp_scaled.AT.to_eigen() * primal_residual_eq_scaled; } { precond.unscale_primal_residual_in_place_in( - { proxqp::from_eigen, - primal_residual_in_scaled_lo }); // E^{-1}(unscaled Ax-b) + { from_eigen, primal_residual_in_scaled_lo }); // E^{-1}(unscaled Ax-b) tmp.noalias() += qp_scaled.CT.to_eigen() * primal_residual_in_scaled_lo; } - precond.unscale_dual_residual_in_place({ proxqp::from_eigen, tmp }); + precond.unscale_dual_residual_in_place({ from_eigen, tmp }); primal_feasibility_lhs = infty_norm(tmp); precond.scale_primal_residual_in_place_eq( - { proxqp::from_eigen, primal_residual_eq_scaled }); + { from_eigen, primal_residual_eq_scaled }); } // scaled Ax - b precond.scale_primal_residual_in_place_eq( - { proxqp::from_eigen, primal_residual_eq_scaled }); + { from_eigen, primal_residual_eq_scaled }); // scaled Cx precond.scale_primal_residual_in_place_in( - { proxqp::from_eigen, primal_residual_in_scaled_up }); + { from_eigen, primal_residual_in_scaled_up }); - precond.unscale_dual_residual_in_place( - { proxqp::from_eigen, dual_residual_scaled }); + precond.unscale_dual_residual_in_place({ from_eigen, dual_residual_scaled }); T dual_feasibility_lhs = infty_norm(dual_residual_scaled); - precond.scale_dual_residual_in_place( - { proxqp::from_eigen, dual_residual_scaled }); + precond.scale_dual_residual_in_place({ from_eigen, dual_residual_scaled }); return proxsuite::linalg::veg::tuplify(primal_feasibility_lhs, dual_feasibility_lhs); diff --git a/include/proxsuite/proxqp/sparse/views.hpp b/include/proxsuite/proxqp/sparse/views.hpp index 45f02b55a..af9965a2a 100644 --- a/include/proxsuite/proxqp/sparse/views.hpp +++ b/include/proxsuite/proxqp/sparse/views.hpp @@ -11,11 +11,11 @@ #include #include #include -#include -#include +#include +#include #include #include "proxsuite/proxqp/sparse/model.hpp" -#include "proxsuite/proxqp/results.hpp" +#include "proxsuite/common/results.hpp" #include #include diff --git a/include/proxsuite/proxqp/sparse/workspace.hpp b/include/proxsuite/proxqp/sparse/workspace.hpp index 5d763e3ad..0b1570ef6 100644 --- a/include/proxsuite/proxqp/sparse/workspace.hpp +++ b/include/proxsuite/proxqp/sparse/workspace.hpp @@ -11,13 +11,14 @@ #include #include #include -#include -#include -#include #include +#include +#include +#include "proxsuite/common/results.hpp" +#include +#include "proxsuite/proxqp/sparse/aliases.hpp" #include "proxsuite/proxqp/sparse/views.hpp" #include "proxsuite/proxqp/sparse/model.hpp" -#include "proxsuite/proxqp/results.hpp" #include "proxsuite/proxqp/sparse/utils.hpp" #include diff --git a/include/proxsuite/proxqp/sparse/wrapper.hpp b/include/proxsuite/proxqp/sparse/wrapper.hpp index 6cfa48641..a8f61622a 100644 --- a/include/proxsuite/proxqp/sparse/wrapper.hpp +++ b/include/proxsuite/proxqp/sparse/wrapper.hpp @@ -7,14 +7,17 @@ #ifndef PROXSUITE_PROXQP_SPARSE_WRAPPER_HPP #define PROXSUITE_PROXQP_SPARSE_WRAPPER_HPP -#include -#include +#include +#include #include #include namespace proxsuite { namespace proxqp { namespace sparse { + +; + /// /// @brief This class defines the API of PROXQP solver with sparse backend. /// @@ -37,9 +40,9 @@ auto main() -> int { // Generate a random QP problem with primal variable dimension of size dim; n_eq equality constraints and n_in inequality constraints ::proxsuite::proxqp::test::rand::set_seed(1); - proxqp::isize dim = 10; - proxqp::isize n_eq(dim / 4); - proxqp::isize n_in(dim / 4); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); T sparsity_factor = 0.15; // controls the sparsity of each matrix of the problem generated T eps_abs = T(1e-9); double p = 1.0; T conditioning(10.0); @@ -259,9 +262,9 @@ struct QP work.internal.proximal_parameter_update = false; PreconditionerStatus preconditioner_status; if (compute_preconditioner_) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; + preconditioner_status = PreconditionerStatus::EXECUTE; } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::IDENTITY; + preconditioner_status = PreconditionerStatus::IDENTITY; } proxsuite::proxqp::sparse::update_proximal_parameters( settings, results, work, rho, mu_eq, mu_in); @@ -379,9 +382,9 @@ struct QP work.internal.proximal_parameter_update = false; PreconditionerStatus preconditioner_status; if (update_preconditioner) { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::EXECUTE; + preconditioner_status = PreconditionerStatus::EXECUTE; } else { - preconditioner_status = proxsuite::proxqp::PreconditionerStatus::KEEP; + preconditioner_status = PreconditionerStatus::KEEP; } isize n = model.dim; isize n_eq = model.n_eq; @@ -708,36 +711,34 @@ struct QP * criterion. */ template -proxqp::Results -solve( - optional> H, - optional> g, - optional> A, - optional> b, - optional> C, - optional> l, - optional> u, - optional> x = nullopt, - optional> y = nullopt, - optional> z = nullopt, - optional eps_abs = nullopt, - optional eps_rel = nullopt, - optional rho = nullopt, - optional mu_eq = nullopt, - optional mu_in = nullopt, - optional verbose = nullopt, - bool compute_preconditioner = true, - bool compute_timings = false, - optional max_iter = nullopt, - proxsuite::proxqp::InitialGuessStatus initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, - proxsuite::proxqp::SparseBackend sparse_backend = - proxsuite::proxqp::SparseBackend::Automatic, - bool check_duality_gap = false, - optional eps_duality_gap_abs = nullopt, - optional eps_duality_gap_rel = nullopt, - bool primal_infeasibility_solving = false, - optional manual_minimal_H_eigenvalue = nullopt) +Results +solve(optional> H, + optional> g, + optional> A, + optional> b, + optional> C, + optional> l, + optional> u, + optional> x = nullopt, + optional> y = nullopt, + optional> z = nullopt, + optional eps_abs = nullopt, + optional eps_rel = nullopt, + optional rho = nullopt, + optional mu_eq = nullopt, + optional mu_in = nullopt, + optional verbose = nullopt, + bool compute_preconditioner = true, + bool compute_timings = false, + optional max_iter = nullopt, + InitialGuessStatus initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + SparseBackend sparse_backend = SparseBackend::Automatic, + bool check_duality_gap = false, + optional eps_duality_gap_abs = nullopt, + optional eps_duality_gap_rel = nullopt, + bool primal_infeasibility_solving = false, + optional manual_minimal_H_eigenvalue = nullopt) { isize n(0); diff --git a/include/proxsuite/proxqp/status.hpp b/include/proxsuite/proxqp/status.hpp deleted file mode 100644 index 55c5c8389..000000000 --- a/include/proxsuite/proxqp/status.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2022 INRIA -// -/** - * @file constants.hpp - */ -#ifndef PROXSUITE_PROXQP_CONSTANTS_HPP -#define PROXSUITE_PROXQP_CONSTANTS_HPP - -#include -#include "proxsuite/proxqp/sparse/fwd.hpp" - -namespace proxsuite { -namespace proxqp { - -// SOLVER STATUS -enum struct QPSolverOutput -{ - PROXQP_SOLVED, // the problem is solved. - PROXQP_MAX_ITER_REACHED, // the maximum number of iterations has been reached. - PROXQP_PRIMAL_INFEASIBLE, // the problem is primal infeasible. - PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE, // the closest (in L2 sense) feasible - // problem is solved. - PROXQP_DUAL_INFEASIBLE, // the problem is dual infeasible. - PROXQP_NOT_RUN // the solver has not been run yet. -}; -// INITIAL GUESS STATUS -enum struct InitialGuessStatus -{ - NO_INITIAL_GUESS, - EQUALITY_CONSTRAINED_INITIAL_GUESS, - WARM_START_WITH_PREVIOUS_RESULT, - WARM_START, - COLD_START_WITH_PREVIOUS_RESULT -}; -// PRECONDITIONER STATUS -enum struct PreconditionerStatus -{ - EXECUTE, // initialize or update with qp in entry - KEEP, // keep previous preconditioner (for update method) - IDENTITY // do not execute, hence use identity preconditioner (for init - // method) -}; - -} // namespace proxqp -} // namespace proxsuite - -#endif /* end of include guard PROXSUITE_PROXQP_CONSTANTS_HPP */ diff --git a/include/proxsuite/proxqp/utils/prints.hpp b/include/proxsuite/proxqp/utils/prints.hpp deleted file mode 100644 index 77984e531..000000000 --- a/include/proxsuite/proxqp/utils/prints.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) 2022 INRIA -// -/** \file */ - -#ifndef PROXSUITE_PROXQP_UTILS_PRINTS_HPP -#define PROXSUITE_PROXQP_UTILS_PRINTS_HPP - -#include - -namespace proxsuite { -namespace proxqp { - -inline void -print_line() -{ - std::string the_line = "-----------------------------------------------------" - "--------------------------------------------\0"; - std::cout << the_line << "\n" << std::endl; -} - -inline void -print_header() -{ - std::cout << "iter objective pri res dua res mu_in \n" - << std::endl; -} - -inline void -print_preambule() -{ - print_line(); - std::cout - << " ProxQP - Primal-Dual Proximal QP " - "Solver\n" - << " (c) Antoine Bambade, Sarah El Kazdadi, Fabian Schramm, Adrien " - "Taylor, and " - "Justin Carpentier\n" - << " Inria Paris 2022 \n" - << std::endl; - print_line(); -} - -} // end namespace proxqp -} // end namespace proxsuite - -#endif /* end of include guard PROXSUITE_PROXQP_UTILS_PRINTS_HPP */ diff --git a/include/proxsuite/serialization/model.hpp b/include/proxsuite/serialization/model.hpp index c6caf6cf2..fb057f641 100644 --- a/include/proxsuite/serialization/model.hpp +++ b/include/proxsuite/serialization/model.hpp @@ -9,13 +9,13 @@ #define PROXSUITE_SERIALIZATION_MODEL_HPP #include -#include +#include namespace cereal { template void -serialize(Archive& archive, proxsuite::proxqp::dense::Model& model) +serialize(Archive& archive, proxsuite::common::dense::Model& model) { archive(CEREAL_NVP(model.dim), CEREAL_NVP(model.n_eq), diff --git a/include/proxsuite/serialization/results.hpp b/include/proxsuite/serialization/results.hpp index 6b69ccc5e..7bfe5a7dc 100644 --- a/include/proxsuite/serialization/results.hpp +++ b/include/proxsuite/serialization/results.hpp @@ -9,13 +9,13 @@ #define PROXSUITE_SERIALIZATION_RESULTS_HPP #include -#include +#include namespace cereal { template void -serialize(Archive& archive, proxsuite::proxqp::Info& info) +serialize(Archive& archive, proxsuite::common::Info& info) { archive(CEREAL_NVP(info.mu_eq), CEREAL_NVP(info.mu_eq_inv), @@ -41,7 +41,7 @@ serialize(Archive& archive, proxsuite::proxqp::Info& info) template void -serialize(Archive& archive, proxsuite::proxqp::Results& results) +serialize(Archive& archive, proxsuite::common::Results& results) { archive(CEREAL_NVP(results.x), CEREAL_NVP(results.y), diff --git a/include/proxsuite/serialization/ruiz.hpp b/include/proxsuite/serialization/ruiz.hpp index c1a0791c8..58e42ead6 100644 --- a/include/proxsuite/serialization/ruiz.hpp +++ b/include/proxsuite/serialization/ruiz.hpp @@ -9,14 +9,14 @@ #define PROXSUITE_SERIALIZATION_RUIZ_HPP #include -#include +#include namespace cereal { template void serialize(Archive& archive, - proxsuite::proxqp::dense::preconditioner::RuizEquilibration& ruiz) + proxsuite::common::dense::preconditioner::RuizEquilibration& ruiz) { archive( // CEREAL_NVP(ruiz.delta), diff --git a/include/proxsuite/serialization/settings.hpp b/include/proxsuite/serialization/settings.hpp index 3535dd3d9..fc2b36af4 100644 --- a/include/proxsuite/serialization/settings.hpp +++ b/include/proxsuite/serialization/settings.hpp @@ -9,13 +9,13 @@ #define PROXSUITE_SERIALIZATION_SETTINGS_HPP #include -#include +#include namespace cereal { template void -serialize(Archive& archive, proxsuite::proxqp::Settings& settings) +serialize(Archive& archive, proxsuite::common::Settings& settings) { archive(CEREAL_NVP(settings.default_rho), CEREAL_NVP(settings.default_mu_eq), diff --git a/include/proxsuite/serialization/workspace.hpp b/include/proxsuite/serialization/workspace.hpp index 456fc3e8d..b10fdcb7e 100644 --- a/include/proxsuite/serialization/workspace.hpp +++ b/include/proxsuite/serialization/workspace.hpp @@ -9,13 +9,13 @@ #define PROXSUITE_SERIALIZATION_WORKSPACE_HPP #include -#include +#include namespace cereal { template void -serialize(Archive& archive, proxsuite::proxqp::dense::Workspace& work) +serialize(Archive& archive, proxsuite::common::dense::Workspace& work) { archive( // CEREAL_NVP(work.ldl), diff --git a/include/proxsuite/serialization/wrapper.hpp b/include/proxsuite/serialization/wrapper.hpp index d2bf7a0ea..e4c3a1e31 100644 --- a/include/proxsuite/serialization/wrapper.hpp +++ b/include/proxsuite/serialization/wrapper.hpp @@ -10,6 +10,7 @@ #include #include +#include namespace cereal { @@ -20,5 +21,14 @@ serialize(Archive& archive, proxsuite::proxqp::dense::QP& qp) archive( CEREAL_NVP(qp.model), CEREAL_NVP(qp.results), CEREAL_NVP(qp.settings)); } // CEREAL_NVP(qp.ruiz), ,CEREAL_NVP(qp.ruiz) + +template +void +serialize(Archive& archive, proxsuite::osqp::dense::QP& qp) +{ + archive( + CEREAL_NVP(qp.model), CEREAL_NVP(qp.results), CEREAL_NVP(qp.settings)); +} // CEREAL_NVP(qp.ruiz), ,CEREAL_NVP(qp.ruiz) + } // namespace cereal #endif /* end of include guard PROXSUITE_SERIALIZATION_WRAPPER_HPP */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dbea3694d..ad7e13789 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,24 +39,32 @@ macro(proxsuite_test name path) add_dependencies(build_tests ${target_name}) endmacro() -proxsuite_test(dense_ruiz_equilibration src/dense_ruiz_equilibration.cpp) -proxsuite_test(dense_qp_eq src/dense_qp_eq.cpp) -proxsuite_test(dense_qp_with_eq_and_in src/dense_qp_with_eq_and_in.cpp) -proxsuite_test(dense_qp_unconstrained src/dense_unconstrained_qp.cpp) -proxsuite_test(dense_backward src/dense_backward.cpp) -proxsuite_test(dense_qp_wrapper src/dense_qp_wrapper.cpp) -proxsuite_test(dense_qp_solve src/dense_qp_solve.cpp) -proxsuite_test(sparse_ruiz_equilibration src/sparse_ruiz_equilibration.cpp) -proxsuite_test(sparse_qp src/sparse_qp.cpp) -proxsuite_test(sparse_qp_wrapper src/sparse_qp_wrapper.cpp) -proxsuite_test(sparse_qp_solve src/sparse_qp_solve.cpp) -proxsuite_test(sparse_factorization src/sparse_factorization.cpp) -proxsuite_test(cvxpy src/cvxpy.cpp) +proxsuite_test(proxqp-dense_ruiz_equilibration src/proxqp/dense_ruiz_equilibration.cpp) +proxsuite_test(proxqp-dense_qp_eq src/proxqp/dense_qp_eq.cpp) +proxsuite_test(proxqp-dense_qp_with_eq_and_in src/proxqp/dense_qp_with_eq_and_in.cpp) +proxsuite_test(proxqp-dense_qp_unconstrained src/proxqp/dense_unconstrained_qp.cpp) +proxsuite_test(proxqp-dense_backward src/proxqp/dense_backward.cpp) +proxsuite_test(proxqp-dense_qp_wrapper src/proxqp/dense_qp_wrapper.cpp) +proxsuite_test(proxqp-dense_qp_solve src/proxqp/dense_qp_solve.cpp) +proxsuite_test(proxqp-sparse_ruiz_equilibration src/proxqp/sparse_ruiz_equilibration.cpp) +proxsuite_test(proxqp-sparse_qp src/proxqp/sparse_qp.cpp) +proxsuite_test(proxqp-sparse_qp_wrapper src/proxqp/sparse_qp_wrapper.cpp) +proxsuite_test(proxqp-sparse_qp_solve src/proxqp/sparse_qp_solve.cpp) +proxsuite_test(proxqp-sparse_factorization src/proxqp/sparse_factorization.cpp) +proxsuite_test(proxqp-cvxpy src/proxqp/cvxpy.cpp) + +proxsuite_test(osqp-cvxpy src/osqp/cvxpy.cpp) +proxsuite_test(osqp-dense_qp_eq src/osqp/dense_qp_eq.cpp) +proxsuite_test(osqp-dense_qp_solve src/osqp/dense_qp_solve.cpp) +proxsuite_test(osqp-dense_qp_with_eq_and_in src/osqp/dense_qp_with_eq_and_in.cpp) +proxsuite_test(osqp-dense_qp_wrapper src/osqp/dense_qp_wrapper.cpp) +proxsuite_test(osqp-dense_ruiz_equilibration src/osqp/dense_ruiz_equilibration.cpp) +proxsuite_test(osqp-dense_qp_unconstrained src/osqp/dense_unconstrained_qp.cpp) if(BUILD_WITH_OPENMP_SUPPORT) - proxsuite_test(parallel src/parallel_qp_solve.cpp) + proxsuite_test(proxqp-parallel src/proxqp/parallel_qp_solve.cpp) target_link_libraries( - ${PROJECT_NAME}-test-cpp-parallel + ${PROJECT_NAME}-test-cpp-proxqp-parallel PRIVATE OpenMP::OpenMP_CXX ) endif() @@ -67,27 +75,29 @@ macro(ADD_TEST_CFLAGS target flag) endmacro() make_directory("${CMAKE_CURRENT_BINARY_DIR}/serialization-data") -proxsuite_test(serialization src/serialization.cpp) +proxsuite_test(proxqp-serialization src/proxqp/serialization.cpp) add_test_cflags( - ${PROJECT_NAME}-test-cpp-serialization + ${PROJECT_NAME}-test-cpp-proxqp-serialization "-DTEST_SERIALIZATION_FOLDER=\\\\\"${CMAKE_CURRENT_BINARY_DIR}/serialization-data\\\\\"" ) if(cereal_FOUND) target_link_libraries( - ${PROJECT_NAME}-test-cpp-serialization + ${PROJECT_NAME}-test-cpp-proxqp-serialization PRIVATE cereal::cereal ) else() target_include_directories( - ${PROJECT_NAME}-test-cpp-serialization + ${PROJECT_NAME}-test-cpp-proxqp-serialization SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/external/cereal/include ) endif() if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT MSVC) - proxsuite_test(dense_maros_meszaros src/dense_maros_meszaros.cpp) - proxsuite_test(sparse_maros_meszaros src/sparse_maros_meszaros.cpp) + proxsuite_test(proxqp-dense_maros_meszaros src/proxqp/dense_maros_meszaros.cpp) + proxsuite_test(proxqp-sparse_maros_meszaros src/proxqp/sparse_maros_meszaros.cpp) + + proxsuite_test(osqp-dense_maros_meszaros src/osqp/dense_maros_meszaros.cpp) endif() if(BUILD_PYTHON_INTERFACE) @@ -106,10 +116,21 @@ if(BUILD_PYTHON_INTERFACE) foreach(TEST_FILE ${${PROJECT_NAME}_PYTHON_UNITTEST}) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) - string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/" "" TEST_FILE ${TEST_FILE}) + string(REPLACE "${PROJECT_SOURCE_DIR}/" "" TEST_FILE_REL ${TEST_FILE}) + string(REGEX REPLACE "^test/src/" "" TEST_SUBPATH ${TEST_FILE_REL}) + get_filename_component(TEST_DIR ${TEST_SUBPATH} DIRECTORY) + if(TEST_DIR STREQUAL "") + set(PYTHON_TEST_NAME "${PROJECT_NAME}-test-py-${TEST_NAME}") + else() + string(REPLACE "/" "-" TEST_DIR_CLEAN ${TEST_DIR}) + set( + PYTHON_TEST_NAME + "${PROJECT_NAME}-test-py-${TEST_DIR_CLEAN}-${TEST_NAME}" + ) + endif() ADD_PYTHON_UNIT_TEST( - "${PROJECT_NAME}-test-py-${TEST_NAME}" - "${TEST_FILE}" + "${PYTHON_TEST_NAME}" + "${TEST_FILE_REL}" "bindings/python" ) endforeach() diff --git a/test/include/util_f32.hpp b/test/include/util_f32.hpp index c8415220c..c3ad6198c 100644 --- a/test/include/util_f32.hpp +++ b/test/include/util_f32.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include namespace proxsuite { -namespace proxqp { +namespace common { namespace utils { namespace eigen { @@ -22,8 +22,8 @@ LDLT_EXPLICIT_TPL_DECL(3, sparse_positive_definite_rand); } // namespace rand LDLT_EXPLICIT_TPL_DECL(2, matmul_impl); -LDLT_EXPLICIT_TPL_DECL(1, mat_cast); +LDLT_EXPLICIT_TPL_DECL(1, mat_cast); } // namespace utils -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/test/include/util_f64.hpp b/test/include/util_f64.hpp index 591e7e466..30fe9f144 100644 --- a/test/include/util_f64.hpp +++ b/test/include/util_f64.hpp @@ -1,9 +1,9 @@ #pragma once -#include +#include namespace proxsuite { -namespace proxqp { +namespace common { namespace utils { namespace eigen { @@ -25,5 +25,5 @@ LDLT_EXPLICIT_TPL_DECL(3, sparse_positive_definite_rand); LDLT_EXPLICIT_TPL_DECL(1, mat_cast); } // namespace utils -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/test/packaging/src/run-proxqp.cpp b/test/packaging/src/run-proxqp.cpp index a1fd4a215..56255810a 100644 --- a/test/packaging/src/run-proxqp.cpp +++ b/test/packaging/src/run-proxqp.cpp @@ -1,29 +1,29 @@ #include // load the dense solver backend -#include // used for generating a random convex Qp +#include // used for generating a random convex Qp -using namespace proxsuite::proxqp; +using namespace proxsuite; using T = double; int main() { - isize dim = 10; - isize n_eq(dim / 4); - isize n_in(dim / 4); + common::isize dim = 10; + common::isize n_eq(dim / 4); + common::isize n_in(dim / 4); // generate a random qp T sparsity_factor(0.15); T strong_convexity_factor(1.e-2); - dense::Model qp = utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP Qp(dim, n_eq, n_in); // create the QP object + proxqp::dense::QP Qp(dim, n_eq, n_in); // create the QP object Qp.init(qp.H, qp.g, qp.A, qp.b, qp.C, qp.l, qp.u); // initialize the model Qp.solve(); // solve the problem without warm start - auto x_wm = utils::rand::vector_rand(dim); - auto y_wm = utils::rand::vector_rand(n_eq); - auto z_wm = utils::rand::vector_rand(n_in); + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); Qp.solve(x_wm, y_wm, z_wm); // if you have a warm start, put it here diff --git a/test/src/osqp/cvxpy.cpp b/test/src/osqp/cvxpy.cpp new file mode 100644 index 000000000..b03c1baaf --- /dev/null +++ b/test/src/osqp/cvxpy.cpp @@ -0,0 +1,166 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +template +using Mat = + Eigen::Matrix; +template +using Vec = Eigen::Matrix; + +DOCTEST_TEST_CASE("OSQP: 3 dim test case from cvxpy, check feasibility") +{ + + std::cout << "---OSQP: 3 dim test case from cvxpy, check feasibility " + << std::endl; + T eps_abs = T(1e-5); // OSQP unit test + isize dim = 3; + + Mat H = Mat(dim, dim); + H << 13.0, 12.0, -2.0, 12.0, 17.0, 6.0, -2.0, 6.0, 12.0; + + Vec g = Vec(dim); + g << -22.0, -14.5, 13.0; + + Mat C = Mat(dim, dim); + C << 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0; + + Vec l = Vec(dim); + l << -1.0, -1.0, -1.0; + + Vec u = Vec(dim); + u << 1.0, 1.0, 1.0; + common::Results results = osqp::dense::solve( + H, g, nullopt, nullopt, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); + + T pri_res = (helpers::positive_part(C * results.x - u) + + helpers::negative_part(C * results.x - l)) + .lpNorm(); + T dua_res = + (H * results.x + g + C.transpose() * results.z).lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: simple test case from cvxpy, check feasibility") +{ + + std::cout << "---OSQP: simple test case from cvxpy, check feasibility " + << std::endl; + T eps_abs = T(1e-5); // OSQP unit test + isize dim = 1; + + Mat H = Mat(dim, dim); + H << 20.0; + + Vec g = Vec(dim); + g << -10.0; + + Mat C = Mat(dim, dim); + C << 1.0; + + Vec l = Vec(dim); + l << 0.0; + + Vec u = Vec(dim); + u << 1.0; + common::Results results = osqp::dense::solve( + H, g, nullopt, nullopt, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); + + T pri_res = (helpers::positive_part(C * results.x - u) + + helpers::negative_part(C * results.x - l)) + .lpNorm(); + T dua_res = + (H * results.x + g + C.transpose() * results.z).lpNorm(); + T x_sol = 0.5; + + DOCTEST_CHECK((x_sol - results.x.coeff(0, 0)) <= eps_abs); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: simple test case from cvxpy, init with solution, check that " + "solver stays there") +{ + + std::cout + << "---OSQP: simple test case from cvxpy, init with solution, check that " + "solver stays there" + << std::endl; + T eps_abs = T(1e-4); + isize dim = 1; + + Mat H = Mat(dim, dim); + H << 20.0; + + Vec g = Vec(dim); + g << -10.0; + + Mat C = Mat(dim, dim); + C << 1.0; + + Vec l = Vec(dim); + l << 0.0; + + Vec u = Vec(dim); + u << 1.0; + + T x_sol = 0.5; + + isize n_in(1); + isize n_eq(0); + osqp::dense::QP qp{ dim, n_eq, n_in }; + qp.settings.eps_abs = eps_abs; + + qp.init(H, g, nullopt, nullopt, C, u, l); + + common::dense::Vec x = common::dense::Vec(dim); + common::dense::Vec z = common::dense::Vec(n_in); + x << 0.5; + z << 0.0; + qp.solve(x, nullopt, z); + + T pri_res = (helpers::positive_part(C * qp.results.x - u) + + helpers::negative_part(C * qp.results.x - l)) + .lpNorm(); + T dua_res = (H * qp.results.x + g + C.transpose() * qp.results.z) + .lpNorm(); + + DOCTEST_CHECK(qp.results.info.iter_ext <= 0); + DOCTEST_CHECK((x_sol - qp.results.x.coeff(0, 0)) <= eps_abs); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + std::cout << "setup timing " << qp.results.info.setup_time << " solve time " + << qp.results.info.solve_time << std::endl; +} diff --git a/test/src/osqp/cvxpy.py b/test/src/osqp/cvxpy.py new file mode 100644 index 000000000..6012c64ac --- /dev/null +++ b/test/src/osqp/cvxpy.py @@ -0,0 +1,80 @@ +# +# Copyright (c) 2025, INRIA +# + +import proxsuite +import numpy as np +import unittest + + +def normInf(x): + if x.shape[0] == 0: + return 0.0 + else: + return np.linalg.norm(x, np.inf) + + +class CvxpyTest(unittest.TestCase): + def test_trigger_infeasibility_with_exact_solution_known(self): + print( + "------------------------ OSQP: test if infeasibility is triggered even though exact solution known" + ) + + n = 3 + H = np.array([[13.0, 12.0, -2.0], [12.0, 17.0, 6.0], [-2.0, 6.0, 12.0]]) + g = np.array([-22.0, -14.5, 13.0]) + A = None + b = None + C = np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]) + l = -np.ones((n)) + u = np.ones(n) + + qp = proxsuite.osqp.dense.QP(n, 0, n) + qp.init(H, g, A, b, C, l, u) + qp.settings.verbose = True + qp.settings.eps_abs = 1e-9 + qp.settings.eps_abs = 1e-9 + qp.solve() + x_sol = np.array([1, 0.5, -1]) + + dua_res = normInf(H @ qp.results.x + g + C.transpose() @ qp.results.z) + pri_res = normInf( + np.maximum(C @ qp.results.x - u, 0) + np.minimum(C @ qp.results.x - l, 0) + ) + assert qp.results.info.status.name == "QPSOLVER_SOLVED" + + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + assert normInf(x_sol - qp.results.x) <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, 0, n)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_one_dim_with_exact_solution_known(self): + print("------------------------ OSQP: test_one_dim_with_exact_solution_known") + n = 1 + H = np.array([[20.0]]) + g = np.array([-10.0]) + A = None + b = None + C = np.array([[1.0]]) + l = 0 * np.ones((n)) + u = np.ones(n) + + qp = proxsuite.osqp.dense.QP(n, 0, n) + qp.init(H, g, A, b, C, l, u) + qp.settings.verbose = True + qp.settings.eps_abs = 1e-8 + qp.solve() + + x_sol = 0.5 + assert (x_sol - qp.results.x) <= 1e-4 + + +if __name__ == "__main__": + unittest.main() diff --git a/test/src/osqp/dense_maros_meszaros.cpp b/test/src/osqp/dense_maros_meszaros.cpp new file mode 100644 index 000000000..953d6bc83 --- /dev/null +++ b/test/src/osqp/dense_maros_meszaros.cpp @@ -0,0 +1,252 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include + +using namespace proxsuite; +using namespace proxsuite::common; + +#define MAROS_MESZAROS_DIR PROBLEM_PATH "/data/maros_meszaros_data/" + +// Pass or fail with the settings: +// eps_abs = 1e-5, eps_rel = 0. +// adaptive_mu_update = true, adaptive_mu_interval = 50 +// polish = false + +// More details in /examples/python/osqp_calibration_dense_maros_meszaros.py +// Commented problems fail in both OSQP Proxsuite and source code + +char const* files[] = { + MAROS_MESZAROS_DIR "AUG2D.mat", // Skip + MAROS_MESZAROS_DIR "AUG2DC.mat", // Skip + MAROS_MESZAROS_DIR "AUG2DCQP.mat", // Skip + MAROS_MESZAROS_DIR "AUG2DQP.mat", // Skip + MAROS_MESZAROS_DIR "AUG3D.mat", // Skip + MAROS_MESZAROS_DIR "AUG3DC.mat", // Skip + MAROS_MESZAROS_DIR "AUG3DCQP.mat", // Skip + MAROS_MESZAROS_DIR "AUG3DQP.mat", // Skip + MAROS_MESZAROS_DIR "BOYD1.mat", // Skip + MAROS_MESZAROS_DIR "BOYD2.mat", // Skip + MAROS_MESZAROS_DIR "CONT-050.mat", // Skip + MAROS_MESZAROS_DIR "CONT-100.mat", // Skip + MAROS_MESZAROS_DIR "CONT-101.mat", // Skip + MAROS_MESZAROS_DIR "CONT-200.mat", // Skip + MAROS_MESZAROS_DIR "CONT-201.mat", // Skip + MAROS_MESZAROS_DIR "CONT-300.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP1_L.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP1_M.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP1_S.mat", // ----- Pass + MAROS_MESZAROS_DIR "CVXQP2_L.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP2_M.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP2_S.mat", // ----- Pass + MAROS_MESZAROS_DIR "CVXQP3_L.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP3_M.mat", // Skip + MAROS_MESZAROS_DIR "CVXQP3_S.mat", // ----- Pass + MAROS_MESZAROS_DIR "DPKLO1.mat", // ----- Pass + MAROS_MESZAROS_DIR "DTOC3.mat", // Skip + MAROS_MESZAROS_DIR "DUAL1.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUAL2.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUAL3.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUAL4.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUALC1.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUALC2.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUALC5.mat", // ----- Pass + MAROS_MESZAROS_DIR "DUALC8.mat", // ----- Pass + MAROS_MESZAROS_DIR "EXDATA.mat", // Skip + MAROS_MESZAROS_DIR "GENHS28.mat", // ----- Pass + MAROS_MESZAROS_DIR "GOULDQP2.mat", // Skip + MAROS_MESZAROS_DIR "GOULDQP3.mat", // Skip + MAROS_MESZAROS_DIR "HS118.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS21.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS268.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS35.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS35MOD.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS51.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS52.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS53.mat", // ----- Pass + MAROS_MESZAROS_DIR "HS76.mat", // ----- Pass + MAROS_MESZAROS_DIR "HUES-MOD.mat", // Skip + MAROS_MESZAROS_DIR "HUESTIS.mat", // Skip + MAROS_MESZAROS_DIR "KSIP.mat", // Skip + MAROS_MESZAROS_DIR "LASER.mat", // Skip + MAROS_MESZAROS_DIR "LISWET1.mat", // Skip + MAROS_MESZAROS_DIR "LISWET10.mat", // Skip + MAROS_MESZAROS_DIR "LISWET11.mat", // Skip + MAROS_MESZAROS_DIR "LISWET12.mat", // Skip + MAROS_MESZAROS_DIR "LISWET2.mat", // Skip + MAROS_MESZAROS_DIR "LISWET3.mat", // Skip + MAROS_MESZAROS_DIR "LISWET4.mat", // Skip + MAROS_MESZAROS_DIR "LISWET5.mat", // Skip + MAROS_MESZAROS_DIR "LISWET6.mat", // Skip + MAROS_MESZAROS_DIR "LISWET7.mat", // Skip + MAROS_MESZAROS_DIR "LISWET8.mat", // Skip + MAROS_MESZAROS_DIR "LISWET9.mat", // Skip + MAROS_MESZAROS_DIR "LOTSCHD.mat", // ----- Pass + MAROS_MESZAROS_DIR "MOSARQP1.mat", // Skip + MAROS_MESZAROS_DIR "MOSARQP2.mat", // Skip + MAROS_MESZAROS_DIR "POWELL20.mat", // Skip + MAROS_MESZAROS_DIR "PRIMAL1.mat", // ----- Pass + MAROS_MESZAROS_DIR "PRIMAL2.mat", // ----- Pass + MAROS_MESZAROS_DIR "PRIMAL3.mat", // ----- Pass + MAROS_MESZAROS_DIR "PRIMAL4.mat", // Skip + // MAROS_MESZAROS_DIR "PRIMALC1.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "PRIMALC2.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "PRIMALC5.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "PRIMALC8.mat", // ------------- Fail + MAROS_MESZAROS_DIR "Q25FV47.mat", // Skip + MAROS_MESZAROS_DIR "QADLITTL.mat", // ----- Pass + MAROS_MESZAROS_DIR "QAFIRO.mat", // ----- Pass + // MAROS_MESZAROS_DIR "QBANDM.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QBEACONF.mat", // ----- Pass + // MAROS_MESZAROS_DIR "QBORE3D.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QBRANDY.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QCAPRI.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QE226.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QETAMACR.mat", // Skip + MAROS_MESZAROS_DIR "QFFFFF80.mat", // Skip + // MAROS_MESZAROS_DIR "QFORPLAN.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QGFRDXPN.mat", // Skip + // MAROS_MESZAROS_DIR "QFORPLAN.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QGROW22.mat", // Skip + // MAROS_MESZAROS_DIR "QGROW7.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QISRAEL.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QPCBLEND.mat", // ----- Pass + // MAROS_MESZAROS_DIR "QPCBOEI1.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QPCBOEI2.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QPCSTAIR.mat", // ----- Pass + MAROS_MESZAROS_DIR "QPILOTNO.mat", // Skip + MAROS_MESZAROS_DIR "QPTEST.mat", // Skip + MAROS_MESZAROS_DIR "QRECIPE.mat", // Skip + MAROS_MESZAROS_DIR "QSC205.mat", // Skip + // MAROS_MESZAROS_DIR "QSCAGR25.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QSCAGR7.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QSCFXM1.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QSCFXM2.mat", // Skip + MAROS_MESZAROS_DIR "QSCFXM3.mat", // Skip + // MAROS_MESZAROS_DIR "QSCORPIO.mat", // --------------Fail + MAROS_MESZAROS_DIR "QSCRS8.mat", // Skip + MAROS_MESZAROS_DIR "QSCSD1.mat", // ----- Pass + MAROS_MESZAROS_DIR "QSCSD6.mat", // Skip + MAROS_MESZAROS_DIR "QSCSD8.mat", // Skip + // MAROS_MESZAROS_DIR "QSCTAP1.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QSCTAP2.mat", // Skip + MAROS_MESZAROS_DIR "QSCTAP3.mat", // Skip + MAROS_MESZAROS_DIR "QSEBA.mat", // Skip + // MAROS_MESZAROS_DIR "QSHARE1B.mat", // ------------- Fail + // MAROS_MESZAROS_DIR "QSHARE2B.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QSHELL.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP04L.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP04S.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP08L.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP08S.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP12L.mat", // Skip + MAROS_MESZAROS_DIR "QSHIP12S.mat", // Skip + MAROS_MESZAROS_DIR "QSIERRA.mat", // Skip + // MAROS_MESZAROS_DIR "QSTAIR.mat", // ------------- Fail + MAROS_MESZAROS_DIR "QSTANDAT.mat", // Skip + MAROS_MESZAROS_DIR "S268.mat", // ----- Pass + MAROS_MESZAROS_DIR "STADAT1.mat", // Skip + MAROS_MESZAROS_DIR "STADAT2.mat", // Skip + MAROS_MESZAROS_DIR "STADAT3.mat", // Skip + MAROS_MESZAROS_DIR "STCQP1.mat", // Skip + MAROS_MESZAROS_DIR "STCQP2.mat", // Skip + MAROS_MESZAROS_DIR "TAME.mat", // ----- Pass + MAROS_MESZAROS_DIR "UBH1.mat", // Skip + MAROS_MESZAROS_DIR "VALUES.mat", // ----- Pass + MAROS_MESZAROS_DIR "YAO.mat", // Skip + MAROS_MESZAROS_DIR "ZECEVIC2.mat", // ----- Pass +}; + +TEST_CASE("OSQP: dense maros meszaros using the api") +{ + using T = double; + using isize = dense::isize; + Timer timer; + T elapsed_time = 0.0; + + for (auto const* file : files) { + SUBCASE(file) + { + auto qp = load_qp(file); + isize n = qp.P.rows(); + isize n_eq_in = qp.A.rows(); + + const bool skip = n > 1000 || n_eq_in > 1000; + if (skip) { + std::cout << " path: " << qp.filename << " n: " << n + << " n_eq+n_in: " << n_eq_in << " - skipping" << std::endl; + } else { + std::cout << " path: " << qp.filename << " n: " << n + << " n_eq+n_in: " << n_eq_in << std::endl; + } + + if (!skip) { + + auto preprocessed = preprocess_qp(qp); + auto& H = preprocessed.H; + auto& A = preprocessed.A; + auto& C = preprocessed.C; + auto& g = preprocessed.g; + auto& b = preprocessed.b; + auto& u = preprocessed.u; + auto& l = preprocessed.l; + + isize dim = H.rows(); + isize n_eq = A.rows(); + isize n_in = C.rows(); + timer.stop(); + timer.start(); + osqp::dense::QP qp{ + dim, n_eq, n_in, false, DenseBackend::PrimalDualLDLT + }; // creating QP object + // TODO: Automatic when PrimalLDLT is implemented + qp.init(H, g, A, b, C, l, u); + qp.settings.verbose = false; + + qp.settings.eps_abs = 1e-5; // OSQP unit test + qp.settings.eps_rel = 0; + qp.settings.eps_primal_inf = 1e-12; + qp.settings.eps_dual_inf = 1e-12; + auto& eps = qp.settings.eps_abs; + + for (size_t it = 0; it < 2; ++it) { + if (it > 0) + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + + qp.solve(); + const auto& x = qp.results.x; + const auto& y = qp.results.y; + const auto& z = qp.results.z; + + T prim_eq = common::dense::infty_norm(A * x - b); + T prim_in = + common::dense::infty_norm(helpers::positive_part(C * x - u) + + helpers::negative_part(C * x - l)); + std::cout << "primal residual " << std::max(prim_eq, prim_in) + << std::endl; + std::cout << "dual residual " + << common::dense::infty_norm(H * x + g + A.transpose() * y + + C.transpose() * z) + << std::endl; + std::cout << "iter " << qp.results.info.iter_ext << std::endl; + CHECK(common::dense::infty_norm(H * x + g + A.transpose() * y + + C.transpose() * z) < 2 * eps); + CHECK(common::dense::infty_norm(A * x - b) > -eps); + CHECK((C * x - l).minCoeff() > -eps); + CHECK((C * x - u).maxCoeff() < eps); + + if (it > 0) { + CHECK(qp.results.info.iter_ext == 0); + } + } + timer.stop(); + elapsed_time += timer.elapsed().user; + } + } + } + std::cout << "timings total : \t" << elapsed_time * 1e-3 << "ms" << std::endl; +} diff --git a/test/src/osqp/dense_qp_eq.cpp b/test/src/osqp/dense_qp_eq.cpp new file mode 100644 index 000000000..fa4d60d62 --- /dev/null +++ b/test/src/osqp/dense_qp_eq.cpp @@ -0,0 +1,266 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +DOCTEST_TEST_CASE("OSQP: qp: start from solution using the wrapper framework") +{ + isize dim = 30; + isize n_eq = 6; + isize n_in = 0; + T sparsity_factor = 0.15; + T strong_convexity_factor(1.e-2); + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality " + "constraints and starting at the solution using the wrapper " + "framework---" + << std::endl; + common::utils::rand::set_seed(1); + auto H = ::utils::rand::sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor); + auto A = ::utils::rand::sparse_matrix_rand_not_compressed( + n_eq, dim, sparsity_factor); + auto solution = ::utils::rand::vector_rand(dim + n_eq); + auto primal_solution = solution.topRows(dim); + auto dual_solution = solution.bottomRows(n_eq); + auto b = A * primal_solution; + auto g = -H * primal_solution - A.transpose() * dual_solution; + auto C = ::utils::rand::sparse_matrix_rand_not_compressed( + 0, dim, sparsity_factor); + Eigen::Matrix dual_init_in(n_in); + Eigen::Matrix u(0); + Eigen::Matrix l(0); + dual_init_in.setZero(); + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_abs = eps_rel; + qp.settings.initial_guess = InitialGuessStatus::WARM_START; + qp.init(H, g, A, b, C, l, u); + qp.solve(primal_solution, dual_solution, dual_init_in); + + DOCTEST_CHECK((A * qp.results.x - b).lpNorm() <= eps_abs); + DOCTEST_CHECK((H * qp.results.x + g + A.transpose() * qp.results.y) + .lpNorm() <= eps_abs); +} +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality constraints " + "and increasing dimension with the wrapper API") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality " + "constraints and increasing dimension with the wrapper API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(dim / 2); + isize n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.u, + qp_random.l); + qp.solve(); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using wrapper API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} +DOCTEST_TEST_CASE( + "OSQP: linear problem with equality with equality constraints and " + "linar cost and increasing dimension using wrapper API") +{ + + std::cout << "---OSQP: testing linear problem with equality constraints and " + "increasing dimension using wrapper API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(dim / 2); + isize n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + qp_random.H.setZero(); + auto y_sol = common::utils::rand::vector_rand( + n_eq); // make sure the LP is bounded within the feasible set + qp_random.g = -qp_random.A.transpose() * y_sol; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using wrapper API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE( + "OSQP: linear problem with equality with equality constraints and " + "linear cost and increasing dimension using wrapper API and " + "the dedicated LP interface") +{ + + std::cout + << "---OSQP: testing LP interface for solving linear problem with " + "equality constraints and increasing dimension using wrapper API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(dim / 2); + isize n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + qp_random.H.setZero(); + auto y_sol = common::utils::rand::vector_rand( + n_eq); // make sure the LP is bounded within the feasible set + qp_random.g = -qp_random.A.transpose() * y_sol; + + osqp::dense::QP qp{ + dim, n_eq, n_in, common::HessianType::Zero + }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using wrapper API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE("OSQP: infeasible qp") +{ + // (x1- 9)^2 + (x2-6)^2 + // s.t. + // x1 <= 10 + // x2 <= 10 + // x1 >= 20 + Eigen::Matrix H; + H << 1.0, 0.0, 0.0, 1.0; + H = 2 * H; + + Eigen::Matrix g; + g << -18.0, -12.0; + + Eigen::Matrix C; + C << 1, 0, // x1 <= 10 + 0, 1, // x2 <= 10 + -1, 0; // x1 >= 20 + + Eigen::Matrix u; + u << 10, 10, -20; + + int n = H.rows(); + int n_in = C.rows(); + int n_eq = 0; + + Eigen::Matrix l = + Eigen::Matrix::Constant( + n_in, -std::numeric_limits::infinity()); + + proxsuite::osqp::dense::QP qp(n, n_eq, n_in); + qp.init(H, g, nullopt, nullopt, C, l, u); + qp.settings.eps_rel = 0.; + qp.settings.eps_abs = 1e-5; // OSQP unit test + + qp.solve(); + + DOCTEST_CHECK(qp.results.info.status == + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE); +} \ No newline at end of file diff --git a/test/src/osqp/dense_qp_solve.cpp b/test/src/osqp/dense_qp_solve.cpp new file mode 100644 index 000000000..666953adc --- /dev/null +++ b/test/src/osqp/dense_qp_solve.cpp @@ -0,0 +1,400 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +DOCTEST_TEST_CASE("OSQP: osqp::dense: test init with fixed sizes matrices") +{ + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(5), n_in(2); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + Eigen::Matrix H = qp.H; + Eigen::Matrix g = qp.g; + Eigen::Matrix A = qp.A; + Eigen::Matrix b = qp.b; + Eigen::Matrix C = qp.C; + Eigen::Matrix l = qp.l; + Eigen::Matrix u = qp.u; + + { + common::Results results = osqp::dense::solve( + H, g, A, b, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); + + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; + } + + { + osqp::dense::QP qp_problem(dim, n_eq, 0); + qp_problem.init(H, g, A, b, nullopt, nullopt, nullopt); + qp_problem.settings.eps_abs = eps_abs; + qp_problem.settings.eps_rel = eps_rel; + qp_problem.solve(); + + const common::Results& results = qp_problem.results; + + T pri_res = (qp.A * results.x - qp.b).lpNorm(); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; + } +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test solve function") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve function---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + common::Results results = osqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0); + + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different rho value") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different rho value---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + common::Results results = osqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + T(1.E-7)); + DOCTEST_CHECK(results.info.rho == T(1.E-7)); + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different mu_eq and mu_in values") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different mu_eq and " + "mu_in values---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + common::Results results = osqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + T(1.E-1), + T(1.E0)); + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); + common::Results results = osqp::dense::solve( + qp.H, qp.g, qp.A, qp.b, qp.C, qp.l, qp.u, x_wm, y_wm, z_wm, eps_abs, 0); + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test verbose = true") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test verbose = true ---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + bool verbose = true; + common::Results results = osqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + nullopt, + nullopt, + verbose); + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test no initial guess") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test no initial guess ---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + common::Results results = osqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + nullopt, + nullopt, + nullopt, + true, + true, + nullopt, + initial_guess); + T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), + (helpers::positive_part(qp.C * results.x - qp.u) + + helpers::negative_part(qp.C * results.x - qp.l)) + .lpNorm()); + T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y + + qp.C.transpose() * results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << results.info.iter_ext + << std::endl; + std::cout << "setup timing " << results.info.setup_time << " solve time " + << results.info.solve_time << std::endl; +} diff --git a/test/src/osqp/dense_qp_solve.py b/test/src/osqp/dense_qp_solve.py new file mode 100644 index 000000000..5a59fe71b --- /dev/null +++ b/test/src/osqp/dense_qp_solve.py @@ -0,0 +1,432 @@ +# +# Copyright (c) 2025 INRIA +# +import os +import proxsuite +import numpy as np +import scipy.sparse as spa +import scipy.io as spio +import unittest + + +def normInf(x): + if x.shape[0] == 0: + return 0.0 + else: + return np.linalg.norm(x, np.inf) + + +def generate_mixed_qp(n, seed=1): + """ + Generate sparse problem in dense QP format + """ + np.random.seed(seed) + + m = int(n / 4) + int(n / 4) + # m = n + n_eq = int(n / 4) + n_in = int(n / 4) + + P = spa.random( + n, n, density=0.075, data_rvs=np.random.randn, format="csc" + ).toarray() + P = (P + P.T) / 2.0 + + s = max(np.absolute(np.linalg.eigvals(P))) + P += (abs(s) + 1e-02) * spa.eye(n) + P = spa.coo_matrix(P) + # print("sparsity of P : {}".format((P.nnz) / (n**2))) + q = np.random.randn(n) + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) # row-major + v = np.random.randn(n) # Fictitious solution + u = A @ v + l = -1.0e20 * np.ones(m) + + return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] + + +class DenseQpWrapper(unittest.TestCase): + # TESTS DENSE SOLVE FUNCTION + + def test_case_basic_solve(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test basic solve" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_case_different_rho_value(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test different rho values" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + rho=1.0e-7, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert results.info.rho == 1e-7 + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_case_different_mu_values(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test different mu_eq and mu_in values" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + mu_eq=1.0e-1, + mu_in=1.0e0, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_case_different_warm_starting(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test warm starting" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + x_wm = np.random.randn(n) + y_wm = np.random.randn(n_eq) + z_wm = np.random.randn(n_in) + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + x=x_wm, + y=y_wm, + z=z_wm, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_case_different_verbose_true(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test verbose = true" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + verbose=True, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_case_different_no_initial_guess(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test no initial guess" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + eps_abs = 1e-5 # OSQP unit test + results = proxsuite.osqp.dense.solve( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + eps_abs=eps_abs, + eps_rel=0, + initial_guess=proxsuite.osqp.NO_INITIAL_GUESS, + ) + dua_res = normInf( + H @ results.x + g + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_sparse_problem_with_exact_solution_known(self): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints and exact solution known" + ) + + n = 150 + M = spa.lil_matrix(spa.eye(n)) + for i in range(1, n - 1): + M[i, i + 1] = -1 + M[i, i - 1] = 1 + + H = spa.csc_matrix(M.dot(M.transpose())).toarray() + H = np.ascontiguousarray(H) + g = -np.ones((n,)) + A = None + b = None + C = spa.csc_matrix(spa.eye(n)).toarray() + C = np.ascontiguousarray(C) + l = 2.0 * np.ones((n,)) + u = np.full(l.shape, +np.inf) + + results = proxsuite.osqp.dense.solve( + H, g, A, b, C, l, u, eps_rel=0 + ) # test refers to osqp one with eps_rel = 0 + x_theoretically_optimal = np.array([2.0] * 149 + [3.0]) + + dua_res = normInf(H @ results.x + g + C.transpose() @ results.z) + pri_res = normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ) + + assert dua_res <= 1e-3 # default precision of the solver + assert pri_res <= 1e-3 + assert normInf(x_theoretically_optimal - results.x) <= 2e-3 # OSQP + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, 0, n)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_initializing_with_None(self): + print("------------------------OSQP: test initialization with Nones") + + H = np.array([[65.0, -22.0, -16.0], [-22.0, 14.0, 7.0], [-16.0, 7.0, 5.0]]) + g = np.array([-13.0, 15.0, 7.0]) + A = None + b = None + C = None + _u = None + _l = None + + results = proxsuite.osqp.dense.solve( + H, + g, + A, + b, + C, + eps_rel=0, # test refers to osqp one with eps_rel = 0 + ) + print("optimal x: {}".format(results.x)) + + dua_res = normInf(H @ results.x + g) + + assert dua_res <= 1e-3 # default precision of the solver + print("--n = {} ; n_eq = {} ; n_in = {}".format(3, 0, 0)) + print("dual residual = {} ".format(dua_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + def test_solve_qpsolvers_problem(self): + print( + "------------------------OSQP: test case from qpsolvers with equality constraint and upper bound inequality constraints" + ) + file_path = os.path.dirname(os.path.realpath(__file__)) + data_path = os.path.join(file_path, "..", "..", "data") + m = spio.loadmat( + os.path.join(data_path, "simple_qp_with_inifinity_lower_bound.mat"), + squeeze_me=True, + ) + P = np.ascontiguousarray(m["P"].astype(float)) + q = m["q"].astype(float) + A = np.ascontiguousarray(m["A"].astype(float).reshape((1, 3))) + b = np.array([m["b"]]).reshape((1,)) + C = np.ascontiguousarray(m["C"].astype(float)) + l = m["l"].astype(float) + u = m["u"].astype(float) + + eps_abs = 1e-5 # OSQP unit test + + results = proxsuite.osqp.dense.solve( + P, q, A, b, C, l, u, verbose=False, eps_abs=eps_abs, eps_rel=0 + ) + print("optimal x: {}".format(results.x)) + + dua_res = normInf( + P @ results.x + q + A.transpose() @ results.y + C.transpose() @ results.z + ) + pri_res = max( + normInf(A @ results.x - b), + normInf( + np.maximum(C @ results.x - u, 0) + np.minimum(C @ results.x - l, 0) + ), + ) + assert dua_res <= eps_abs + assert pri_res <= eps_abs + + print("--n = {} ; n_eq = {} ; n_in = {}".format(3, 1, 3)) + print("dual residual = {} ".format(dua_res)) + print("total number of iteration: {}".format(results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + results.info.setup_time, results.info.solve_time + ) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/src/osqp/dense_qp_with_eq_and_in.cpp b/test/src/osqp/dense_qp_with_eq_and_in.cpp new file mode 100644 index 000000000..77c3a6642 --- /dev/null +++ b/test/src/osqp/dense_qp_with_eq_and_in.cpp @@ -0,0 +1,386 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with equality and " + "inequality constraints " + "and increasing dimension using wrapper API") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints and increasing dimension using wrapper API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex qp with box inequality " + "constraints and increasing dimension using the API") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with box inequality " + "constraints and increasing dimension using the API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(0); + isize n_in(dim); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_box_constrained_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE("OSQP: sparse random not strongly convex qp with inequality " + "constraints and increasing dimension using the API") +{ + + std::cout + << "---OSQP: testing sparse random not strongly convex qp with inequality " + "constraints and increasing dimension using the API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize n_in(dim / 2); + isize n_eq(0); + common::dense::Model qp_random = + common::utils::dense_not_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with degenerate inequality " + "constraints and increasing dimension using the API") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with degenerate " + "inequality constraints and increasing dimension using the API---" + << std::endl; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + T eps_primal_inf = T(1e-15); + T eps_dual_inf = T(1e-15); + T sparsity_factor = 0.45; + T strong_convexity_factor(1e-2); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize m(dim / 4); + isize n_in(2 * m); + isize n_eq(0); + common::dense::Model qp_random = common::utils::dense_degenerate_qp( + dim, + n_eq, + m, // it n_in = 2 * m, it doubles the inequality constraints + sparsity_factor, + strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.settings.eps_primal_inf = eps_primal_inf; + qp.settings.eps_dual_inf = eps_dual_inf; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + DOCTEST_CHECK(qp.results.info.status == + common::QPSolverOutput::QPSOLVER_SOLVED); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } + // Note: + // eps_primal_inf and eps_dual_inf are set to 1e-15 to reproduce the + // benchmark setting on OSQP in the benchmark repository: + // https://github.com/Simple-Robotics/proxqp_benchmark +} + +DOCTEST_TEST_CASE( + "OSQP: linear problem with equality inequality constraints and " + "increasing dimension using the API") +{ + srand(1); + std::cout + << "---OSQP: testing linear problem with inequality constraints and " + "increasing dimension using the API---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize n_in(dim / 2); + isize n_eq(0); + common::dense::Model qp_random = + common::utils::dense_not_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor); + qp_random.H.setZero(); + auto z_sol = common::utils::rand::vector_rand(n_in); + qp_random.g = -qp_random.C.transpose() * + z_sol; // make sure the LP is bounded within the feasible set + // std::cout << "g : " << qp.g << " C " << qp.C << " u " << qp.u << " l " + // << qp.l << std::endl; + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.settings.verbose = false; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and inequality " + "constraints " + "and increasing dimension using wrapper API to test different settings " + "on solution polishing.") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with equality and " + "inequality constraints and increasing dimension using wrapper API " + "to test different settings on solution polishing---" + << std::endl; + T sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = T(0); + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + // Trivial test + osqp::dense::QP qp{ dim, n_eq, n_in }; + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.settings.polish = true; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + + DOCTEST_CHECK(qp.results.info.status_polish == + common::PolishOutput::POLISH_NOT_RUN); + + qp.solve(); + + DOCTEST_CHECK(qp.results.info.status_polish != + common::PolishOutput::POLISH_NO_ACTIVE_SET_FOUND); + + // Polishing not run because problem is not solved as + // algorithm is stopped early + osqp::dense::QP qp2{ dim, n_eq, n_in }; + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = eps_rel; + qp2.settings.polish = true; + qp2.settings.max_iter = 1; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + + DOCTEST_CHECK(qp2.results.info.status_polish == + common::PolishOutput::POLISH_NOT_RUN); + + qp2.solve(); + + DOCTEST_CHECK(qp2.results.info.status_polish == + common::PolishOutput::POLISH_NOT_RUN); + + // Polish succeeds as the problem is not hard (compared + // to some Maros Meszaros ones, see OSQP benchmarks) + osqp::dense::QP qp3{ dim, n_eq, n_in }; + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = eps_rel; + qp3.settings.polish = true; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + + DOCTEST_CHECK(qp3.results.info.status_polish == + common::PolishOutput::POLISH_NOT_RUN); + + qp3.solve(); + + DOCTEST_CHECK(qp3.results.info.status_polish == + common::PolishOutput::POLISH_SUCCEEDED); + } +} diff --git a/test/src/osqp/dense_qp_wrapper.cpp b/test/src/osqp/dense_qp_wrapper.cpp new file mode 100644 index 000000000..f61850ebd --- /dev/null +++ b/test/src/osqp/dense_qp_wrapper.cpp @@ -0,0 +1,8047 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; +using namespace proxsuite::common; + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with inequality constraints" + "and empty equality constraints") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // inequality " + // "constraints " + // "and empty equality constraints---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(0); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + + // Testing with empty but properly sized matrix A of size (0, 10) + // std::cout << "Solving QP with" << std::endl; + // std::cout << "dim: " << dim << std::endl; + // std::cout << "n_eq: " << n_eq << std::endl; + // std::cout << "n_in: " << n_in << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "A.cols() : " << qp_random.A.cols() << std::endl; + // std::cout << "A.rows() : " << qp_random.A.rows() << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + qp.init(qp_random.H, + qp_random.g, + nullopt, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm(); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // Testing with empty matrix A of size (0, 0) + qp_random.A = Eigen::MatrixXd(); + qp_random.b = Eigen::VectorXd(); + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + + // std::cout << "Solving QP with" << std::endl; + // std::cout << "dim: " << dim << std::endl; + // std::cout << "n_eq: " << n_eq << std::endl; + // std::cout << "n_in: " << n_in << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "A.cols() : " << qp_random.A.cols() << std::endl; + // std::cout << "A.rows() : " << qp_random.A.rows() << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm(); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // Testing with nullopt + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + + qp3.init(qp_random.H, + qp_random.g, + nullopt, + nullopt, + qp_random.C, + qp_random.l, + qp_random.u); + qp3.solve(); + + pri_res = (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm(); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update H") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update H---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating H" << std::endl; + qp_random.H.setIdentity(); + qp.update(qp_random.H, nullopt, nullopt, nullopt, nullopt, nullopt, nullopt); + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update A") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update A---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating A" << std::endl; + qp_random.A = common::utils::rand::sparse_matrix_rand_not_compressed( + n_eq, dim, sparsity_factor); + qp.update(nullopt, nullopt, qp_random.A, nullopt, nullopt, nullopt, nullopt); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update C") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update C---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating C" << std::endl; + qp_random.C = common::utils::rand::sparse_matrix_rand_not_compressed( + n_in, dim, sparsity_factor); + qp.update(nullopt, nullopt, nullopt, nullopt, qp_random.C, nullopt, nullopt); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update b") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update b---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating b" << std::endl; + auto x_sol = common::utils::rand::vector_rand(dim); + qp_random.b = qp_random.A * x_sol; + qp.update(nullopt, nullopt, nullopt, qp_random.b, nullopt, nullopt, nullopt); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update u") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update u---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating b" << std::endl; + auto x_sol = common::utils::rand::vector_rand(dim); + auto delta = common::utils::Vec(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); + } + + qp_random.u = qp_random.C * x_sol + delta; + qp.update(nullopt, nullopt, nullopt, nullopt, nullopt, nullopt, qp_random.u); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update g") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update g---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating g" << std::endl; + auto g = common::utils::rand::vector_rand(dim); + + qp_random.g = g; + qp.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and inequality " + "constraints: test update H and A and b and u and l") +{ + + // std::cout + // << "---OSQP: testing sparse random strongly convex qp with equality and " + // "inequality constraints: test update H and A and b and u and l---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "H : " << qp_random.H << std::endl; + // std::cout << "g : " << qp_random.g << std::endl; + // std::cout << "A : " << qp_random.A << std::endl; + // std::cout << "b : " << qp_random.b << std::endl; + // std::cout << "C : " << qp_random.C << std::endl; + // std::cout << "u : " << qp_random.u << std::endl; + // std::cout << "l : " << qp_random.l << std::endl; + + // std::cout << "testing updating b" << std::endl; + qp_random.H = + common::utils::rand::sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor); + qp_random.A = common::utils::rand::sparse_matrix_rand_not_compressed( + n_eq, dim, sparsity_factor); + auto x_sol = common::utils::rand::vector_rand(dim); + auto delta = common::utils::Vec(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); + } + qp_random.b = qp_random.A * x_sol; + qp_random.u = qp_random.C * x_sol + delta; + qp_random.l = qp_random.C * x_sol - delta; + qp.update(qp_random.H, + nullopt, + qp_random.A, + qp_random.b, + nullopt, + qp_random.l, + qp_random.u); + + // std::cout << "after upating" << std::endl; + // std::cout << "H : " << qp.model.H << std::endl; + // std::cout << "g : " << qp.model.g << std::endl; + // std::cout << "A : " << qp.model.A << std::endl; + // std::cout << "b : " << qp.model.b << std::endl; + // std::cout << "C : " << qp.model.C << std::endl; + // std::cout << "u : " << qp.model.u << std::endl; + // std::cout << "l : " << qp.model.l << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update rho") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update rho---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "rho : " << qp.results.info.rho << std::endl; + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.e-7), + nullopt, + nullopt); // restart the problem with default options + // std::cout << "after upating" << std::endl; + // std::cout << "rho : " << qp.results.info.rho << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.e-7), + nullopt, + nullopt); + // std::cout << "rho : " << qp2.results.info.rho << std::endl; + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update mu_eq and mu_in") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test update mu_eq and mu_in---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "before upating" << std::endl; + // std::cout << "mu_in : " << qp.results.info.mu_in << std::endl; + // std::cout << "mu_eq : " << qp.results.info.mu_eq << std::endl; + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + nullopt, + T(1.e-1), // x 10 + T(1.e-1)); // /100 + + // std::cout << "after upating" << std::endl; + // std::cout << "mu_in : " << qp.results.info.mu_in << std::endl; + // std::cout << "mu_eq : " << qp.results.info.mu_eq << std::endl; + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + T(1.e-1), // x 10 + T(1.e-1)); // /100 + qp2.solve(); + // std::cout << "mu_in : " << qp2.results.info.mu_in << std::endl; + // std::cout << "mu_eq : " << qp2.results.info.mu_eq << std::endl; + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test warm starting---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); + // std::cout << "proposed warm start" << std::endl; + // std::cout << "x_wm : " << x_wm << std::endl; + // std::cout << "y_wm : " << y_wm << std::endl; + // std::cout << "z_wm : " << z_wm << std::endl; + qp.settings.initial_guess = InitialGuessStatus::WARM_START; + qp.solve(x_wm, y_wm, z_wm); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------using API solving qp with dim after updating: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = InitialGuessStatus::WARM_START; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(x_wm, y_wm, z_wm); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // std::cout << "------ conter factual check with another QP object starting + // at " + // "the updated model : " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test dense init") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test dense init---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init( + Eigen::Matrix( + qp_random.H), + qp_random.g, + Eigen::Matrix( + qp_random.A), + qp_random.b, + Eigen::Matrix( + qp_random.C), + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test with no initial guess") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test with no initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp2: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test with equality constrained initial guess") +{ + + // std::cout + // << "---OSQP: testing sparse random strongly convex qp with equality and " + // "inequality constraints: test with equality constrained initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp2: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test with warm start with previous result") +{ + + // // std::cout + // // << "---OSQP: testing sparse random strongly convex qp with equality + // and " + // // "inequality constraints: test with warm start with previous result---" + // // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = InitialGuessStatus::WARM_START; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + + auto x = qp.results.x; + auto y = qp.results.y; + auto z = qp.results.z; + // // std::cout << "after scaling x " << x << " qp.results.x " << + // qp.results.x + // << std::endl; + qp2.ruiz.scale_primal_in_place({ common::from_eigen, x }); + qp2.ruiz.scale_dual_in_place_eq({ common::from_eigen, y }); + qp2.ruiz.scale_dual_in_place_in({ common::from_eigen, z }); + // // std::cout << "after scaling x " << x << " qp.results.x " << + // qp.results.x + // << std::endl; + qp2.solve(x, y, z); + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp.update( + nullopt, nullopt, nullopt, nullopt, nullopt, nullopt, nullopt, false); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + // std::cout << "------using API solving qp with dim with qp after warm start + // " + // "with previous result: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp2: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test with cold start option") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test with cold start option---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp: " << dim + // << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = InitialGuessStatus::WARM_START; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + + auto x = qp.results.x; + auto y = qp.results.y; + auto z = qp.results.z; + // // std::cout << "after scaling x " << x << " qp.results.x " << + // qp.results.x + // << std::endl; + qp2.ruiz.scale_primal_in_place({ common::from_eigen, x }); + qp2.ruiz.scale_dual_in_place_eq({ common::from_eigen, y }); + qp2.ruiz.scale_dual_in_place_in({ common::from_eigen, z }); + // // std::cout << "after scaling x " << x << " qp.results.x " << + // qp.results.x + // << std::endl; + qp2.solve(x, y, z); + + qp.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + qp.update( + nullopt, nullopt, nullopt, nullopt, nullopt, nullopt, nullopt, true); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + // std::cout << "------using API solving qp with dim with qp after warm start + // " + // "with cold start option: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with cold start option: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test equilibration options at initialization") +{ + + // std::cout + // << "---OSQP: testing sparse random strongly convex qp with equality and " + // "inequality constraints: test equilibration options at initialization---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp with " + // "preconditioner derived: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "ruiz vector : " << qp.ruiz.delta << " ruiz scalar factor " + // << qp.ruiz.c << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + false); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp without preconditioner derivation: + // " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "ruiz vector : " << qp2.ruiz.delta << " ruiz scalar factor " + // << qp2.ruiz.c << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test equilibration options at update") +{ + + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test equilibration options at update---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + qp.solve(); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with dim with qp with " + // "preconditioner derived: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true); // rederive preconditioner with previous options, i.e., redo + // exact same derivations + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp with preconditioner re derived " + // "after an update (should get exact same results): " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + // std::cout << "------using API solving qp with preconditioner derivation and + // " + // "another object QP: " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; + + qp2.update( + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + false); // use previous preconditioner: should get same result as well + qp2.solve(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // std::cout << "------using API solving qp without preconditioner derivation: + // " + // << dim << " neq: " << n_eq << " nin: " << n_in << std::endl; + // std::cout << "primal residual: " << pri_res << std::endl; + // std::cout << "dual residual: " << dua_res << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "ruiz vector : " << qp2.ruiz.delta << " ruiz scalar factor " + // << qp2.ruiz.c << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +///// TESTS ALL INITIAL GUESS OPTIONS FOR MULTIPLE SOLVES AT ONCE +TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with no initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with no initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with equality " + "constrained initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with equality constrained initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with equality " + "constrained initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with warm start with previous result and first solve + // with " + // "equality constrained initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with no initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with warm start with previous result and first solve + // with " + // "no initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with cold start " + "initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with cold start with previous result and first solve + // with " + // "equality constrained initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with warm start") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with warm start and first solve with no initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = InitialGuessStatus::WARM_START; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: warm start test from init") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with warm start and first solve with no initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp2.settings.initial_guess = InitialGuessStatus::WARM_START; + // std::cout << "dirty workspace for qp2 : " << qp2.work.dirty << std::endl; + qp2.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve with new QP object" << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +/// TESTS WITH UPDATE + INITIAL GUESS OPTIONS + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update and multiple solve at once with " + "no initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with no initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + bool update_preconditioner = true; + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + // std::cout << "dirty workspace after update : " << qp.work.dirty << + // std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update + multiple solve at once with " + "equality constrained initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with equality constrained initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + bool update_preconditioner = true; + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + // std::cout << "dirty workspace after update : " << qp.work.dirty << + // std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test update + multiple solve at once with equality " + "constrained initial guess and then warm start with previous results") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with warm start with previous result and first solve + // with " + // "equality constrained initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + bool update_preconditioner = true; + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + // std::cout << "dirty workspace after update : " << qp.work.dirty << + // std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test multiple solve at once with no initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with warm start with previous result and first solve + // with " + // "no initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + bool update_preconditioner = true; + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update + multiple solve at once with " + "cold start initial guess and then cold start option") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + + // std::cout << "Test with cold start with previous result and first solve + // with " + // "equality constrained initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + bool update_preconditioner = true; + qp.update(qp_random.H, + qp_random.g, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + update_preconditioner); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test update + multiple solve at once with " + "warm start") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test with warm start and first solve with no initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + qp.settings.initial_guess = InitialGuessStatus::WARM_START; + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + auto x_wm = qp.results.x; // keep previous result + auto y_wm = qp.results.y; + auto z_wm = qp.results.z; + bool update_preconditioner = true; + // test with a false update (the warm start should give the exact solution) + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + // std::cout << "dirty workspace after update: " << qp.work.dirty << + // std::endl; + qp.solve(x_wm, y_wm, z_wm); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + // std::cout << "Second solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + x_wm = qp.results.x; // keep previous result + y_wm = qp.results.y; + z_wm = qp.results.z; + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + // try now with a real update + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + update_preconditioner); + // std::cout << "dirty workspace after update: " << qp.work.dirty << + // std::endl; + qp.solve(x_wm, y_wm, z_wm); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Third solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fourth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // std::cout << "dirty workspace : " << qp.work.dirty << std::endl; + qp.solve(qp.results.x, qp.results.y, qp.results.z); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "Fifth solve " << std::endl; + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; +} + +TEST_CASE( + "OSQP: :dense: Test initializaton with rho for different initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test initializaton with rho for different initial guess" + // << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.E-7)); + qp.solve(); + CHECK(qp.results.info.rho == T(1.E-7)); + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.E-7)); + qp2.solve(); + CHECK(qp2.results.info.rho == T(1.E-7)); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; + + osqp::dense::QP qp3(dim, n_eq, n_in); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.E-7)); + qp3.solve(); + CHECK(qp3.results.info.rho == T(1.E-7)); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp3.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp3.results.info.setup_time << " solve time + // " + // << qp3.results.info.solve_time << std::endl; + + osqp::dense::QP qp4(dim, n_eq, n_in); + qp4.settings.eps_abs = eps_abs; + qp4.settings.eps_rel = 0; + qp4.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + qp4.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.E-7)); + qp4.solve(); + CHECK(qp4.results.info.rho == T(1.E-7)); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp4.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp4.results.info.setup_time << " solve time + // " + // << qp4.results.info.solve_time << std::endl; + + osqp::dense::QP qp5(dim, n_eq, n_in); + qp5.settings.eps_abs = eps_abs; + qp5.settings.eps_rel = 0; + qp5.settings.initial_guess = InitialGuessStatus::WARM_START; + qp5.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + T(1.E-7)); + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); + CHECK(qp5.results.info.rho == T(1.E-7)); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp5.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp5.results.info.setup_time << " solve time + // " + // << qp5.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: Test g update for different initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test g update for different initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + auto old_g = qp_random.g; + qp_random.g = common::utils::rand::vector_rand(dim); + qp.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK((qp.model.g - qp_random.g).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp2.init(qp_random.H, + old_g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + old_g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp2.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK((qp2.model.g - qp_random.g).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; + + osqp::dense::QP qp3(dim, n_eq, n_in); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp3.init(qp_random.H, + old_g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + old_g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp3.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK((qp3.model.g - qp_random.g).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp3.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp3.results.info.setup_time << " solve time + // " + // << qp3.results.info.solve_time << std::endl; + + osqp::dense::QP qp4(dim, n_eq, n_in); + qp4.settings.eps_abs = eps_abs; + qp4.settings.eps_rel = 0; + qp4.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + qp4.init(qp_random.H, + old_g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp4.solve(); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + old_g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp4.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp4.solve(); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK((qp4.model.g - qp_random.g).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp4.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp4.results.info.setup_time << " solve time + // " + // << qp4.results.info.solve_time << std::endl; + + osqp::dense::QP qp5(dim, n_eq, n_in); + qp5.settings.eps_abs = eps_abs; + qp5.settings.eps_rel = 0; + qp5.settings.initial_guess = InitialGuessStatus::WARM_START; + qp5.init(qp_random.H, + old_g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + old_g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp5.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp5.solve(); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK((qp5.model.g - qp_random.g).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp5.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp5.results.info.setup_time << " solve time + // " + // << qp5.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: Test A update for different initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test A update for different initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + auto new_A = common::utils::rand::sparse_matrix_rand_not_compressed( + n_eq, dim, sparsity_factor); + qp.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); + qp.solve(); + pri_res = + std::max((new_A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = + (qp_random.H * qp.results.x + qp_random.g + + new_A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK((qp.model.A - new_A).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp2.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); + qp2.solve(); + pri_res = std::max( + (new_A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + new_A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK((qp2.model.A - new_A).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; + + osqp::dense::QP qp3(dim, n_eq, n_in); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp3.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); + qp3.solve(); + pri_res = std::max( + (new_A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + new_A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK((qp3.model.A - new_A).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp3.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp3.results.info.setup_time << " solve time + // " + // << qp3.results.info.solve_time << std::endl; + + osqp::dense::QP qp4(dim, n_eq, n_in); + qp4.settings.eps_abs = eps_abs; + qp4.settings.eps_rel = 0; + qp4.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + qp4.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp4.solve(); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp4.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); + qp4.solve(); + pri_res = std::max( + (new_A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + new_A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK((qp4.model.A - new_A).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp4.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp4.results.info.setup_time << " solve time + // " + // << qp4.results.info.solve_time << std::endl; + + osqp::dense::QP qp5(dim, n_eq, n_in); + qp5.settings.eps_abs = eps_abs; + qp5.settings.eps_rel = 0; + qp5.settings.initial_guess = InitialGuessStatus::WARM_START; + qp5.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp5.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); + qp5.solve(); + pri_res = std::max( + (new_A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + new_A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK((qp5.model.A - new_A).lpNorm() <= eps_abs); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp5.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp5.results.info.setup_time << " solve time + // " + // << qp5.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: Test rho update for different initial guess") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // std::cout << "Test rho update for different initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.E-7)); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(qp.results.info.rho == T(1.E-7)); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp2.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.E-7)); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(qp2.results.info.rho == T(1.e-7)); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; + + osqp::dense::QP qp3(dim, n_eq, n_in); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.E-7)); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + CHECK(qp3.results.info.rho == T(1.e-7)); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp3.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp3.results.info.setup_time << " solve time + // " + // << qp3.results.info.solve_time << std::endl; + + osqp::dense::QP qp4(dim, n_eq, n_in); + qp4.settings.eps_abs = eps_abs; + qp4.settings.eps_rel = 0; + qp4.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + qp4.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp4.solve(); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp4.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.E-7)); + qp4.solve(); + pri_res = std::max( + (qp_random.A * qp4.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp4.results.x + qp_random.g + + qp_random.A.transpose() * qp4.results.y + + qp_random.C.transpose() * qp4.results.z) + .lpNorm(); + CHECK(qp4.results.info.rho == T(1.e-7)); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp4.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp4.results.info.setup_time << " solve time + // " + // << qp4.results.info.solve_time << std::endl; + + osqp::dense::QP qp5(dim, n_eq, n_in); + qp5.settings.eps_abs = eps_abs; + qp5.settings.eps_rel = 0; + qp5.settings.initial_guess = InitialGuessStatus::WARM_START; + qp5.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + qp5.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + T(1.E-7)); + qp5.solve(); + pri_res = std::max( + (qp_random.A * qp5.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp5.results.x + qp_random.g + + qp_random.A.transpose() * qp5.results.y + + qp_random.C.transpose() * qp5.results.z) + .lpNorm(); + CHECK(qp5.results.info.rho == T(1.e-7)); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp5.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp5.results.info.setup_time << " solve time + // " + // << qp5.results.info.solve_time << std::endl; +} + +TEST_CASE("OSQP: :dense: Test g update for different warm start with previous " + "result option") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + + // std::cout << "Test rho update for different initial guess" << std::endl; + // std::cout << "dirty workspace before any solving: " << qp.work.dirty + // << std::endl; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + // a new linear cost slightly modified + auto g = qp_random.g * 0.95; + + qp.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); + qp.solve(); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = + (qp_random.H * qp.results.x + g + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp.results.info.setup_time << " solve time + // " + // << qp.results.info.solve_time << std::endl; + + osqp::dense::QP qp2(dim, n_eq, n_in); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + qp2.init(qp_random.H, + g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp2.solve(); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = + (qp_random.H * qp2.results.x + g + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // std::cout << "--n = " << dim << " n_eq " << n_eq << " n_in " << n_in + // << std::endl; + // std::cout << "; dual residual " << dua_res << "; primal residual " << + // pri_res + // << std::endl; + // std::cout << "total number of iteration: " << qp2.results.info.iter_ext + // << std::endl; + // std::cout << "setup timing " << qp2.results.info.setup_time << " solve time + // " + // << qp2.results.info.solve_time << std::endl; +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after updates using warm start with previous results") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "updates using warm start with previous results---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-3); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + qp3.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after updates using cold start with previous results") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "updates using cold start with previous results---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after updates using equality constrained initial guess") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "updates using equality constrained initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after updates using no initial guess") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "updates using no initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after several solves using warm start with previous results") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "several solves using warm start with previous results---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + qp2.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + for (isize iter = 0; iter < 10; ++iter) { + // warm start with previous result used, hence if the qp is small and + // simple, the parameters should not changed during first solve, and also + // after as we start at the solution + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.settings.verbose = false; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + + for (isize iter = 0; iter < 10; ++iter) { + // warm start with previous result used, hence if the qp is small and + // simple, the parameters should not changed during first solve, and also + // after as we start at the solution + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + for (isize iter = 0; iter < 10; ++iter) { + // warm start with previous result used, hence if the qp is small and + // simple, the parameters should not changed during first solve, and also + // after as we start at the solution + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after several solves using cold start with previous results") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "several solves using cold start with previous results---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after several solves " + "using equality constrained initial guess") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "several solves using equality constrained initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } +} + +DOCTEST_TEST_CASE( + "OSQP: :dense: sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings " + "after several solves using no initial guess") +{ + // std::cout << "---OSQP: testing sparse random strongly convex qp with + // equality and + // " + // "inequality constraints: test changing default settings after " + // "several solves using no initial guess---" + // << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + T rho(1.e-7); + T mu_eq(1.e-4); + bool compute_preconditioner = true; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) <= 1.E-9); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6); + for (isize iter = 0; iter < 10; ++iter) { + qp.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp2.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp2.settings.eps_abs = eps_abs; + qp2.settings.eps_rel = 0; + qp2.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + nullopt, + mu_eq); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + qp2.solve(); + DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp2.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp2.results.x + qp_random.g + + qp_random.A.transpose() * qp2.results.y + + qp_random.C.transpose() * qp2.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + // conter factual check with another QP object starting at the updated model + osqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + DOCTEST_CHECK(qp3.settings.initial_guess == + InitialGuessStatus::NO_INITIAL_GUESS); + qp3.settings.eps_abs = eps_abs; + qp3.settings.eps_rel = 0; + qp3.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + compute_preconditioner, + rho, + mu_eq); + + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(mu_eq - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } + + qp3.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + compute_preconditioner, + 1.e-6, + 1.e-3); + for (isize iter = 0; iter < 10; ++iter) { + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + qp3.solve(); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e-3 - qp3.results.info.mu_eq) <= 1.E-9); + DOCTEST_CHECK(std::abs(1.e3 - qp3.results.info.mu_eq_inv) <= 1.E-9); + pri_res = std::max( + (qp_random.A * qp3.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp3.results.x + qp_random.g + + qp_random.A.transpose() * qp3.results.y + + qp_random.C.transpose() * qp3.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + } +} + +TEST_CASE("OSQP: :dense: init must be called before update") +{ + + double sparsity_factor = 0.15; + T eps_abs = T(1e-3); // OSQP unit test + common::utils::rand::set_seed(1); + isize dim = 10; + + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + osqp::dense::QP qp(dim, n_eq, n_in); + + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + // call update without init, update calls init internally + qp.update(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true); + + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + + qp_random.H *= 2.; + qp_random.g = common::utils::rand::vector_rand(dim); + qp.update(qp_random.H, + qp_random.g, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true); + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); +} + +// test of the box constraints interface +TEST_CASE("OSQP: :dense: check ordering of z when there are box constraints") +{ + isize n_test(1000); + double sparsity_factor = 1.; + T eps_abs = T(1e-3); // OSQP unit test + isize dim = 15; + + // mixing ineq and box constraints + for (isize i = 0; i < n_test; i++) { + common::utils::rand::set_seed(i); + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + // ineq and boxes + Eigen::Matrix x_sol = + common::utils::rand::vector_rand(dim); + Eigen::Matrix delta(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); + } + qp_random.u = qp_random.C * x_sol + delta; + qp_random.b = qp_random.A * x_sol; + Eigen::Matrix u_box(dim); + u_box.setZero(); + Eigen::Matrix l_box(dim); + l_box.setZero(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); + u_box(i) = x_sol(i) + 2 * shift; // OSQP unit test + l_box(i) = x_sol(i) - 2 * shift; // OSQP unit test + } + /////////////////// for debuging + // using Mat = + // Eigen::Matrix; + // Mat C_enlarged(dim+n_in,dim); + // C_enlarged.setZero(); + // C_enlarged.topLeftCorner(n_in,dim) = qp_random.C; + // C_enlarged.bottomLeftCorner(dim,dim).diagonal().array() += 1.; + // Eigen::Matrix u_enlarged(n_in+dim); + // Eigen::Matrix l_enlarged(n_in+dim); + // u_enlarged.head(n_in) = qp_random.u; + // u_enlarged.tail(dim) = u_box; + // l_enlarged.head(n_in) = qp_random.l; + // l_enlarged.tail(dim) = l_box; + // // std::cout << "n " << dim << " n_eq " << n_eq << " n_in "<< n_in << + // std::endl; // std::cout << "=================qp compare" << std::endl; + // osqp::dense::QP qp_compare{ dim, n_eq, dim + n_in, false}; + // qp_compare.settings.eps_abs = eps_abs; + // qp_compare.settings.eps_rel = 0; + // qp_compare.settings.max_iter = 10; + // qp_compare.settings.max_iter_in = 10; + // qp_compare.settings.verbose = true; + // qp_compare.settings.initial_guess = + // InitialGuessStatus::NO_INITIAL_GUESS; + // qp_compare.init(qp_random.H, + // qp_random.g, + // qp_random.A, + // qp_random.b, + // C_enlarged, + // l_enlarged, + // u_enlarged, + // true); + // qp_compare.solve(); + // // std::cout << "=================qp compare end" << std::endl; + //////////////// + + osqp::dense::QP qp(dim, n_eq, n_in, true); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + l_box, + u_box, + true); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp.results.x - u_box) + + helpers::negative_part(qp.results.x - l_box)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z.head(n_in) + + qp.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + } + // idem but without ineq constraints + for (isize i = 0; i < n_test; i++) { + common::utils::rand::set_seed(i); + isize n_eq(dim / 4); + isize n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + // ineq and boxes + Eigen::Matrix x_sol = + common::utils::rand::vector_rand(dim); + Eigen::Matrix delta(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); + } + qp_random.u = qp_random.C * x_sol + delta; + qp_random.b = qp_random.A * x_sol; + Eigen::Matrix u_box(dim); + u_box.setZero(); + Eigen::Matrix l_box(dim); + l_box.setZero(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); + u_box(i) = x_sol(i) + 2 * shift; // OSQP unit test + l_box(i) = x_sol(i) - 2 * shift; // OSQP unit test + } + + osqp::dense::QP qp(dim, n_eq, n_in, true); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + if (i == 294 || i == 715 || i == 782) { + qp.settings.verbose = true; + } + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + l_box, + u_box); + + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp.results.x - u_box) + + helpers::negative_part(qp.results.x - l_box)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z.head(n_in) + + qp.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + if (pri_res > eps_abs) { + std::cout << "pri_res: " << pri_res << std::endl; + std::cout << "i of failed pri_res: " << i << std::endl; + std::cout << "iter_ext at i: " << qp.results.info.iter_ext << std::endl; + std::cout << "Status: " + << (qp.results.info.status == + common::QPSolverOutput::QPSOLVER_SOLVED + ? "Success" + : qp.results.info.status == + common::QPSolverOutput::QPSOLVER_MAX_ITER_REACHED + ? "Max iterations (success)" + : qp.results.info.status == + common::QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE + ? "Primal infeasible" + : qp.results.info.status == + common::QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE + ? "Dual infeasible" + : qp.results.info.status == + common::QPSolverOutput::QPSOLVER_NOT_RUN + ? "Not run" + : "Unknown") + << std::endl; + } + } + // idem but without ineq and without eq constraints + for (isize i = 0; i < n_test; i++) { + isize n_eq(0); + isize n_in(0); + T strong_convexity_factor(1.e-2); + using Mat = + Eigen::Matrix; + Mat eye(dim, dim); + eye.setZero(); + eye.diagonal().array() += 1.; + + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + // ineq and boxes + Eigen::Matrix x_sol = + common::utils::rand::vector_rand(dim); + Eigen::Matrix delta(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); + } + qp_random.u = qp_random.C * x_sol + delta; + qp_random.b = qp_random.A * x_sol; + Eigen::Matrix u_box(dim); + u_box.setZero(); + Eigen::Matrix l_box(dim); + l_box.setZero(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); + u_box(i) = x_sol(i) + 2 * shift; // OSQP unit test + l_box(i) = x_sol(i) - 2 * shift; // OSQP unit test + } + // make a qp to compare + osqp::dense::QP qp_compare(dim, n_eq, dim, false); + qp_compare.settings.eps_abs = eps_abs; + qp_compare.settings.eps_rel = 0; + qp_compare.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp_compare.settings.compute_preconditioner = true; + qp_compare.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + eye, + l_box, + u_box, + true); + + qp_compare.solve(); + + T pri_res = std::max( + (qp_random.A * qp_compare.results.x - qp_random.b) + .lpNorm(), + (helpers::positive_part(qp_random.C * qp_compare.results.x - + qp_random.u) + + helpers::negative_part(qp_random.C * qp_compare.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp_compare.results.x - u_box) + + helpers::negative_part(qp_compare.results.x - l_box)) + .lpNorm()); + T dua_res = (qp_random.H * qp_compare.results.x + qp_random.g + + qp_random.C.transpose() * qp_compare.results.z.head(n_in) + + qp_random.A.transpose() * qp_compare.results.y + + qp_compare.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + // ineq and boxes + osqp::dense::QP qp(dim, n_eq, n_in, true); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.compute_preconditioner = true; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + l_box, + u_box, + true); + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp.results.x - u_box) + + helpers::negative_part(qp.results.x - l_box)) + .lpNorm()); + dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z.head(n_in) + + qp.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + } +} +TEST_CASE("OSQP: :dense: check updates work when there are box constraints") +{ + + double sparsity_factor = 1.; + T eps_abs = T(1e-3); // OSQP unit test + isize dim = 50; + isize n_eq(dim / 4); + isize n_in(dim / 4); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + // ineq and boxes + osqp::dense::QP qp(dim, n_eq, n_in, true); + Eigen::Matrix u_box(dim); + u_box.setZero(); + u_box.array() += 1.E2; + Eigen::Matrix l_box(dim); + l_box.setZero(); + l_box.array() -= 1.E2; + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + l_box, + u_box); + + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp.results.x - u_box) + + helpers::negative_part(qp.results.x - l_box)) + .lpNorm()); + T dua_res = + (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z.head(n_in) + qp.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); + + u_box.array() += 1.E1; + l_box.array() -= 1.E1; + + qp.update(nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + qp_random.l, + qp_random.u, + l_box, + u_box); + + qp.solve(); + + pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + pri_res = std::max(pri_res, + (helpers::positive_part(qp.results.x - u_box) + + helpers::negative_part(qp.results.x - l_box)) + .lpNorm()); + dua_res = + (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z.head(n_in) + qp.results.z.tail(dim)) + .lpNorm(); + CHECK(dua_res <= eps_abs); + CHECK(pri_res <= eps_abs); +} + +// Option primal_infeasibility_solving +// TEST_CASE("OSQP: :dense: test primal infeasibility solving") +// { +// double sparsity_factor = 0.15; +// T eps_abs = T(1e-3); +// common::utils::rand::set_seed(1); +// isize dim = 20; + +// isize n_eq(dim / 4); +// isize n_in(dim / 4); +// T strong_convexity_factor(1.e-2); +// for (isize i = 0; i < 20; ++i) { +// ::utils::rand::set_seed(i); +// common::dense::Model qp_random = +// common::utils::dense_strongly_convex_qp( +// dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + +// osqp::dense::QP qp(dim, n_eq, n_in); +// qp.settings.eps_abs = eps_abs; +// qp.settings.eps_rel = 0; +// // create infeasible problem +// qp_random.b.array() += T(10.); +// qp_random.u.array() -= T(100.); +// qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; +// qp.settings.primal_infeasibility_solving = true; +// qp.settings.eps_primal_inf = T(1.E-4); +// qp.settings.eps_dual_inf = T(1.E-4); +// qp.settings.verbose = false; +// qp.init(qp_random.H, +// qp_random.g, +// qp_random.A, +// qp_random.b, +// qp_random.C, +// qp_random.l, +// qp_random.u); +// qp.solve(); + +// proxsuite::OSQP: :OSQP: :utils::Vec rhs_dim(dim); +// proxsuite::OSQP: :OSQP: :utils::Vec rhs_n_eq(n_eq); +// rhs_n_eq.setOnes(); +// proxsuite::OSQP: :OSQP: :utils::Vec rhs_n_in(n_in); +// rhs_n_in.setOnes(); +// rhs_dim.noalias() = +// qp_random.A.transpose() * rhs_n_eq + qp_random.C.transpose() * +// rhs_n_in; +// T scaled_eps = (rhs_dim).lpNorm() * eps_abs; + +// T pri_res = +// (qp_random.A.transpose() * (qp_random.A * qp.results.x - qp_random.b) + +// qp_random.C.transpose() * +// (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + +// helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))) +// .lpNorm(); +// T dua_res = (qp_random.H.selfadjointView() * qp.results.x + +// qp_random.g + qp_random.A.transpose() * qp.results.y + +// qp_random.C.transpose() * qp.results.z) +// .lpNorm(); +// DOCTEST_CHECK(pri_res <= scaled_eps); +// DOCTEST_CHECK(dua_res <= eps_abs); +// if (pri_res > scaled_eps) { +// std::cout << "pri_res: " << pri_res << std::endl; +// std::cout << "i of failed pri_res: " << i << std::endl; +// std::cout << "iter_ext at i: " << qp.results.info.iter_ext << +// std::endl; std::cout << "Status: " << +// (qp.results.info.status == common::QPSolverOutput::QPSOLVER_SOLVED ? +// "Success" : +// qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_MAX_ITER_REACHED ? "Max iterations +// (success)" : qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE ? "Primal infeasible" +// : qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE ? "Dual infeasible" : +// qp.results.info.status == common::QPSolverOutput::QPSOLVER_NOT_RUN ? +// "Not run" : +// "Unknown +// status") +// << std::endl; +// } +// if (dua_res > scaled_eps) { +// std::cout << "dua_res: " << dua_res << std::endl; +// std::cout << "i of failed dua_res: " << i << std::endl; +// std::cout << "iter_ext at i: " << qp.results.info.iter_ext << +// std::endl; std::cout << "Status: " << +// (qp.results.info.status == common::QPSolverOutput::QPSOLVER_SOLVED ? +// "Success" : +// qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_MAX_ITER_REACHED ? "Max iterations +// (success)" : qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE ? "Primal infeasible" +// : qp.results.info.status == +// common::QPSolverOutput::QPSOLVER_DUAL_INFEASIBLE ? "Dual infeasible" : +// qp.results.info.status == common::QPSolverOutput::QPSOLVER_NOT_RUN ? +// "Not run" : +// "Unknown +// status") +// << std::endl; +// } +// // Note: Values are way larger than expected +// // i = 1: pri_res 0.700136 >= 0.00231401 / iter 52 / Primal infeasible +// // i = 5: dua_res 63.0452 >= 0.001 / iter 66 / Primal infeasible +// // i = 5: dua_res 0.0694577 >= 0.001 / iter 57 / Primal infeasible +// // i = 9: pri_res 1.53315 and dua_res 0.0178529 / iter 35 / Primal +// infeasible +// // i = 10: dua_res 3.77981 / iter 56 / Primal infeasible +// // i = 14: dua_res 0.424509 / iter 4000 / Max iterations +// // i = 15: pri_res 0.896644 and dua_res 3.88932 / iter 78 / Primal +// infeasible +// // i = 18: pri_res 0.369713 and dua_res 12.2003 / iter 55 / Primal +// infeasible +// } +// } + +TEST_CASE("OSQP: :dense: estimate of minimal eigenvalues using Eigen") +{ + double sparsity_factor = 1.; + T tol = T(1e-6); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); + T strong_convexity_factor(1.e-2); + for (isize i = 0; i < 1; ++i) { + // trivial test + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + qp_random.H.diagonal().setOnes(); + qp_random.H.diagonal().tail(1).setConstant(-1.); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate + 1) <= + tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += random_diag.array(); + T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += 100 * random_diag.array(); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); + T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } +} + +TEST_CASE( + "OSQP: :dense: test estimate of minimal eigenvalue using manual choice") +{ + double sparsity_factor = 1.; + T tol = T(1e-6); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); + T strong_convexity_factor(1.e-2); + for (isize i = 0; i < 1; ++i) { + // trivial test + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + qp_random.H.diagonal().setOnes(); + qp_random.H.diagonal().tail(1).setConstant(-1.); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + -1); + + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate + 1) <= + tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += random_diag.array(); + T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + minimal_eigenvalue); + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += 100 * random_diag.array(); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); + T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + minimal_eigenvalue); + + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } +} + +TEST_CASE( + "OSQP: :dense: test estimate of minimal eigenvalue using power iteration") +{ + double sparsity_factor = 1.; + T tol = T(1e-3); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); + T strong_convexity_factor(1.e-2); + for (isize i = 0; i < 1; ++i) { + // trivial test + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + qp_random.H.diagonal().setOnes(); + qp_random.H.diagonal().tail(1).setConstant(-0.5); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::PowerIteration, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + + DOCTEST_CHECK( + std::abs(qp.results.info.minimal_H_eigenvalue_estimate + 0.5) <= tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += random_diag.array(); + T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::PowerIteration, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } + dim = 50; + n_eq = dim; + n_in = dim; + for (isize i = 0; i < 20; ++i) { + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); + qp_random.H.diagonal().array() += + 100 * random_diag.array(); // add some random values to dense matrix + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); + T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); + + T estimate_minimal_eigen_value = + dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::PowerIteration, + 1.E-6, + 10000); + + osqp::dense::QP qp(dim, n_eq, n_in); + qp.settings.max_iter = 1; + qp.settings.max_iter_in = 1; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u, + true, + nullopt, + nullopt, + nullopt, + estimate_minimal_eigen_value); + + DOCTEST_CHECK(std::abs(qp.results.info.minimal_H_eigenvalue_estimate - + minimal_eigenvalue) <= tol); + } +} + +DOCTEST_TEST_CASE( + "OSQP: check that model.is_valid function for symmetric matrices " + "works for epsilon precision") +{ + Eigen::Matrix matrix = Eigen::Matrix::Random(); + Eigen::Matrix symmetric_mat = matrix + matrix.transpose(); + + symmetric_mat(0, 1) = + symmetric_mat(1, 0) + std::numeric_limits::epsilon(); + + // compare the two checks for symmetry with and without tolerance + bool is_symmetric_without_tolerance = + symmetric_mat.isApprox(symmetric_mat.transpose(), 0.0); + bool is_symmetric_with_tolerance = symmetric_mat.isApprox( + symmetric_mat.transpose(), + std::numeric_limits::epsilon()); + DOCTEST_CHECK(is_symmetric_without_tolerance == false); + DOCTEST_CHECK(is_symmetric_with_tolerance == true); + + // initialize a model with a symmetric matrix as Hessian, this runs + // model.is_valid() that performs the check above + osqp::dense::QP qp(3, 0, 0); + qp.init(symmetric_mat, nullopt, nullopt, nullopt, nullopt, nullopt, nullopt); +} + +TEST_CASE("OSQP: :dense: test memory allocation when estimating biggest " + "eigenvalue with power iteration") +{ + double sparsity_factor = 1.; + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); + T strong_convexity_factor(1.e-2); + Eigen::Matrix H; + Eigen::VectorXd dw(2), rhs(2), err_v(2); + // trivial test + ::utils::rand::set_seed(1234); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + qp_random.H.setZero(); + qp_random.H.diagonal().setOnes(); + qp_random.H.diagonal().tail(1).setConstant(-0.5); + H = qp_random.H; + PROXSUITE_EIGEN_MALLOC_NOT_ALLOWED(); + dense::power_iteration(H, dw, rhs, err_v, 1.E-6, 10000); + PROXSUITE_EIGEN_MALLOC_ALLOWED(); +} + +// TODO: Test when PrimalLDLT is implemented +// TEST_CASE("OSQP: :dense: sparse random strongly convex qp with" +// "inequality constraints: test PrimalLDLT backend mu update") +// { + +// // std::cout << "---OSQP: testing sparse random strongly convex qp with" +// "inequality constraints: test PrimalLDLT backend mu update---" +// // << std::endl; +// double sparsity_factor = 1; +// common::utils::rand::set_seed(1); +// isize dim = 3; +// isize n_eq(0); +// isize n_in(9); +// T strong_convexity_factor(1.e-2); +// common::dense::Model qp_random = +// common::utils::dense_strongly_convex_qp( +// dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); +// osqp::dense::QP qp{ +// dim, +// n_eq, +// n_in, +// false, +// HessianType::Dense, +// DenseBackend::PrimalLDLT +// }; // creating QP object +// T eps_abs = T(1e-7); +// qp.settings.eps_abs = eps_abs; +// qp.settings.eps_rel = 0; +// qp.settings.compute_timings = true; +// qp.settings.verbose = true; +// qp.init(qp_random.H, +// qp_random.g, +// nullopt, +// nullopt, +// qp_random.C, +// nullopt, +// qp_random.u); +// qp.solve(); + +// DOCTEST_CHECK(qp.results.info.mu_updates > 0); + +// T pri_res = (helpers::negative_part(qp_random.C * qp.results.x - +// qp_random.l)) +// .lpNorm(); +// T dua_res = (qp_random.H * qp.results.x + qp_random.g + +// qp_random.C.transpose() * qp.results.z) +// .lpNorm(); +// DOCTEST_CHECK(pri_res <= eps_abs); +// DOCTEST_CHECK(dua_res <= eps_abs); + +// // std::cout << "------using API solving qp with dim: " << dim +// // << " neq: " << n_eq << " nin: " << n_in << std::endl; +// // std::cout << "primal residual: " << pri_res << std::endl; +// // std::cout << "dual residual: " << dua_res << std::endl; +// // std::cout << "total number of iteration: " << qp.results.info.iter_ext +// // << std::endl; +// // std::cout << "setup timing " << qp.results.info.setup_time << " solve +// time " +// // << qp.results.info.solve_time << std::endl; +// } diff --git a/test/src/osqp/dense_qp_wrapper.py b/test/src/osqp/dense_qp_wrapper.py new file mode 100644 index 000000000..9a37c01ab --- /dev/null +++ b/test/src/osqp/dense_qp_wrapper.py @@ -0,0 +1,5010 @@ +# +# Copyright (c) 2022-2025, INRIA +# +import proxsuite +import numpy as np +import scipy.sparse as spa +import unittest + +np.printoptions(precision=16) + + +def normInf(x): + if x.shape[0] == 0: + return 0.0 + else: + return np.linalg.norm(x, np.inf) + + +def generate_mixed_qp(n, seed=1, reg=0.01): + """ + Generate sparse problem in dense QP format + """ + np.random.seed(seed) + + m = int(n / 4) + int(n / 4) + # m = n + n_eq = int(n / 4) + n_in = int(n / 4) + + P = spa.random( + n, n, density=0.075, data_rvs=np.random.randn, format="csc" + ).toarray() + P = (P + P.T) / 2.0 + + s = max(np.absolute(np.linalg.eigvals(P))) + P += (abs(s) + reg) * spa.eye(n) + P = spa.coo_matrix(P) + # print("sparsity of P : {}".format((P.nnz) / (n**2))) + q = np.random.randn(n) + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) + v = np.random.randn(n) # Fictitious solution + _delta = np.random.rand(m) # To get inequality + u = A @ v + l = -1.0e20 * np.ones(m) + + return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] + + +def generate_mixed_qp_with_box(n, seed=1): + """ + Generate sparse problem in dense QP format + """ + np.random.seed(seed) + + m = int(n / 4) + int(n / 4) + # m = n + n_eq = int(n / 4) + n_in = int(n / 4) + + P = spa.random( + n, n, density=0.075, data_rvs=np.random.randn, format="csc" + ).toarray() + P = (P + P.T) / 2.0 + + s = max(np.absolute(np.linalg.eigvals(P))) + P += (abs(s) + 1e-02) * spa.eye(n) + P = spa.coo_matrix(P) + # print("sparsity of P : {}".format((P.nnz) / (n**2))) + q = np.random.randn(n) + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) + v = np.random.randn(n) # Fictitious solution + u = A @ v + l = -1.0e20 * np.ones(m) + delta_box = np.random.rand(n) + u_box = v + delta_box + l_box = v - delta_box + + return ( + P.toarray(), + q, + A[:n_eq, :], + u[:n_eq], + A[n_in:, :], + u[n_in:], + l[n_in:], + u_box, + l_box, + ) + + +class DenseqpWrapper(unittest.TestCase): + # TESTS OF GENERAL METHODS OF THE API + def test_case_deterministic_behavior(self): + print("------------------------OSQP: test the result is deterministic") + n = 100 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + ) + qp.solve() + x_prev = np.copy(qp.results.x) + y_prev = np.copy(qp.results.y) + z_prev = np.copy(qp.results.z) + for i in range(20): + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + ) + qp.solve() + + print(f"{normInf(x_prev - qp.results.x)=}") + print(f"{normInf(y_prev - qp.results.y)=}") + print(f"{normInf(z_prev - qp.results.z)=}") + + assert normInf(x_prev - qp.results.x) <= 1e-9 + assert normInf(y_prev - qp.results.y) <= 1e-9 + assert normInf(z_prev - qp.results.z) <= 1e-9 + + def test_case_update_rho(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test update rho" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H=H, + g=np.asfortranarray(g), + A=A, + b=np.asfortranarray(b), + C=C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + rho=1.0e-7, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_update_mu(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test update mus" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + l=np.asfortranarray(l), + u=np.asfortranarray(u), + mu_eq=1.0e-2, + mu_in=1.0e-3, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_no_equilibration_at_initialization(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test with no equilibration at initialization" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + False, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_with_equilibration_at_initialization(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test with equilibration at initialization" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_no_initial_guess(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_no_initial_guess_and_update(self): + print( + "------------------------OSQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + g = np.random.randn(n) + H *= 2.0 # too keep same sparsity structure + qp.update( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_starting(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + x_wm = np.random.randn(n) + y_wm = np.random.randn(n_eq) + z_wm = np.random.randn(n_in) + qp.solve(x_wm, y_wm, z_wm) + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_previous_result(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start with previous result---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert pri_res <= 1e-3 + assert dua_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.verbose = False + qp2.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + + x = qp.results.x + y = qp.results.y + z = qp.results.z + qp2.solve(x, y, z) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp2".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_cold_start_with_previous_result(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test with cold start with previous result---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.verbose = False + qp2.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + ) + x = qp.results.x + y = qp.results.y + z = qp.results.z + qp2.solve(x, y, z) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp2".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_equilibration_option(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option---" + ) + + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + False, + ) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} after warm starting with qp2".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_equilibration_option_at_update(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option at update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print("--n = {} ; n_eq = {} ; n_in = {} with qp".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.update(update_preconditioner=True) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} with qp after update".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + False, + ) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print("--n = {} ; n_eq = {} ; n_in = {} with qp2".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2.update(update_preconditioner=False) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print( + "--n = {} ; n_eq = {} ; n_in = {} with qp2 after update".format( + n, n_eq, n_in + ) + ) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_other_initialization(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test warm start with other initialization---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve(np.random.randn(n), np.random.randn(n_eq), np.random.randn(n_in)) + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert pri_res <= 1.0e-3 + assert dua_res <= 1.0e-3 + print("--n = {} ; n_eq = {} ; n_in = {} with qp".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + # TESTS ALL INITIAL GUESS OPTIONS FOR MULTIPLE SOLVES AT ONCE + + def test_case_multiple_solve_with_no_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_multiple_solve_with_equality_constrained_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_previous_result_starting_with_equality_constraints_initial_guess( + self, + ): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_previous_result_starting_with_no_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_cold_start_with_previous_result_starting_with_no_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_no_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + qp.init(H, g, A, b, C, l, u) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_no_initial_guess_and_different_init(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test solve from warm start and no initial guess with other initialization---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + qp.init(H, g, A, b, C, l, u) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.init(H, g, A, b, C, l, u) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + qp2.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve with new QP object") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp2.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp2.results.info.setup_time, qp2.results.info.solve_time + ) + ) + + # TESTS WITH UPDATE + INITIAL GUESS OPTIONS + + def test_case_multiple_solve_with_no_initial_guess_and_update(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + H *= 2.0 # keep same sparsity structure + g = np.random.randn(n) + update_preconditioner = True + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + update_preconditioner, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_multiple_solve_with_equality_constrained_initial_guess_and_update( + self, + ): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + H *= 2.0 # keep same sparsity structure + g = np.random.randn(n) + update_preconditioner = True + qp.update( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + update_preconditioner, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_previous_result_starting_with_equality_constraints_initial_guess_and_update( + self, + ): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + + H *= 2.0 # keep same sparsity structure + g = np.random.randn(n) + update_preconditioner = True + qp.update( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + update_preconditioner, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_previous_result_starting_with_no_initial_guess_and_update( + self, + ): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + + H *= 2.0 # keep same sparsity structure + g = np.random.randn(n) + update_preconditioner = True + qp.update( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + update_preconditioner, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_cold_start_with_previous_result_starting_with_no_initial_guess_and_update( + self, + ): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + + H *= 2 # keep same sparsity structure + g = np.random.randn(n) + qp.update(H=H, g=np.asfortranarray(g), update_preconditioner=True) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_warm_start_with_no_initial_guess_and_update(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess and update---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.WARM_START + + H *= 2.0 # keep same sparsity structure + g = np.random.randn(n) + update_preconditioner = True + qp.update( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + update_preconditioner, + ) + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Second solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Third solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp.solve(qp.results.x, qp.results.y, qp.results.z) + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("Fourth solve ") + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_case_initialization_with_rho_for_different_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test initializaton with rho for different initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=1.0e-7, + ) + qp.solve() + assert qp.results.info.rho == 1.0e-7 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=1.0e-7, + ) + qp2.solve() + assert qp2.results.info.rho == 1.0e-7 + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp2.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp2.results.info.setup_time, qp2.results.info.solve_time + ) + ) + + qp3 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp3.settings.eps_abs = 1.0e-3 # OSQP unit test + qp3.settings.eps_rel = 0 + qp3.settings.verbose = False + qp3.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp3.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=1.0e-7, + ) + qp3.solve() + assert qp.results.info.rho == 1.0e-7 + dua_res = normInf( + H @ qp3.results.x + + g + + A.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A @ qp3.results.x - b), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp3.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp3.results.info.setup_time, qp3.results.info.solve_time + ) + ) + + qp4 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp4.settings.eps_abs = 1.0e-3 # OSQP unit test + qp4.settings.eps_rel = 0 + qp4.settings.verbose = False + qp4.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp4.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=1.0e-7, + ) + qp4.solve() + assert qp4.results.info.rho == 1.0e-7 + dua_res = normInf( + H @ qp4.results.x + + g + + A.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A @ qp4.results.x - b), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp4.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp4.results.info.setup_time, qp4.results.info.solve_time + ) + ) + + qp5 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp5.settings.eps_abs = 1.0e-3 # OSQP unit test + qp5.settings.eps_rel = 0 + qp5.settings.verbose = False + qp5.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp5.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=1.0e-7, + ) + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z) + assert qp.results.info.rho == 1.0e-7 + dua_res = normInf( + H @ qp5.results.x + + g + + A.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A @ qp5.results.x - b), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp5.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp5.results.info.setup_time, qp5.results.info.solve_time + ) + ) + + def test_case_update_g_for_different_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test update g for different initial guess---" + ) + n = 10 + H, g_old, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g_old), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + g = np.random.randn(n) + dua_res = normInf( + H @ qp.results.x + + g_old + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(g=g) + assert normInf(qp.model.g - g) <= 1.0e-3 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp2.init( + H, + np.asfortranarray(g_old), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g_old + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp2.update(g=g) + assert normInf(qp.model.g - g) <= 1.0e-3 + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp2.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp2.results.info.setup_time, qp2.results.info.solve_time + ) + ) + + qp3 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp3.settings.eps_abs = 1.0e-3 # OSQP unit test + qp3.settings.eps_rel = 0 + qp3.settings.verbose = False + qp3.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp3.init( + H, + np.asfortranarray(g_old), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g_old + + A.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A @ qp3.results.x - b), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp3.update(g=g) + assert normInf(qp.model.g - g) <= 1.0e-3 + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g + + A.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A @ qp3.results.x - b), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp3.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp3.results.info.setup_time, qp3.results.info.solve_time + ) + ) + + qp4 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp4.settings.eps_abs = 1.0e-3 # OSQP unit test + qp4.settings.eps_rel = 0 + qp4.settings.verbose = False + qp4.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp4.init( + H, + np.asfortranarray(g_old), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g_old + + A.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A @ qp4.results.x - b), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp4.update(g=g) + assert normInf(qp.model.g - g) <= 1.0e-3 + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g + + A.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A @ qp4.results.x - b), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp4.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp4.results.info.setup_time, qp4.results.info.solve_time + ) + ) + + qp5 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp5.settings.eps_abs = 1.0e-3 # OSQP unit test + qp5.settings.eps_rel = 0 + qp5.settings.verbose = False + qp5.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp5.init( + H, + np.asfortranarray(g_old), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z) + dua_res = normInf( + H @ qp5.results.x + + g_old + + A.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A @ qp5.results.x - b), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp5.update(g=g) + assert normInf(qp.model.g - g) <= 1.0e-3 + qp5.solve() + dua_res = normInf( + H @ qp5.results.x + + g + + A.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A @ qp5.results.x - b), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp5.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp5.results.info.setup_time, qp5.results.info.solve_time + ) + ) + + def test_case_update_A_for_different_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test update A for different initial guess---" + ) + n = 10 + H, g, A_old, b_old, C, u, l = generate_mixed_qp(n) + n_eq = A_old.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A_old, + np.asfortranarray(b_old), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + _, _, A_new, b_new, _, _, _ = generate_mixed_qp(n, seed=2) + dua_res = normInf( + H @ qp.results.x + + g + + A_old.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A_old @ qp.results.x - b_old), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(A=A_new, b=b_new) + assert normInf(qp.model.A - A_new) <= 1.0e-3 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A_new.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A_new @ qp.results.x - b_new), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp2.init( + H, + np.asfortranarray(g), + A_old, + np.asfortranarray(b_old), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A_old.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A_old @ qp2.results.x - b_old), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp2.update(A=A_new, b=b_new) + assert normInf(qp.model.A - A_new) <= 1.0e-3 + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A_new.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A_new @ qp2.results.x - b_new), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp2.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp2.results.info.setup_time, qp2.results.info.solve_time + ) + ) + + qp3 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp3.settings.eps_abs = 1.0e-3 # OSQP unit test + qp3.settings.eps_rel = 0 + qp3.settings.verbose = False + qp3.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp3.init( + H, + np.asfortranarray(g), + A_old, + np.asfortranarray(b_old), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g + + A_old.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A_old @ qp3.results.x - b_old), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp3.update(A=A_new, b=b_new) + assert normInf(qp.model.A - A_new) <= 1.0e-3 + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g + + A_new.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A_new @ qp3.results.x - b_new), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp3.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp3.results.info.setup_time, qp3.results.info.solve_time + ) + ) + + qp4 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp4.settings.eps_abs = 1.0e-3 # OSQP unit test + qp4.settings.eps_rel = 0 + qp4.settings.verbose = False + qp4.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp4.init( + H, + np.asfortranarray(g), + A_old, + np.asfortranarray(b_old), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g + + A_old.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A_old @ qp4.results.x - b_old), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp4.update(A=A_new, b=b_new) + assert normInf(qp.model.A - A_new) <= 1.0e-3 + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g + + A_new.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A_new @ qp4.results.x - b_new), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp4.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp4.results.info.setup_time, qp4.results.info.solve_time + ) + ) + + qp5 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp5.settings.eps_abs = 1.0e-3 # OSQP unit test + qp5.settings.eps_rel = 0 + qp5.settings.verbose = False + qp5.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp5.init( + H, + np.asfortranarray(g), + A_old, + np.asfortranarray(b_old), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z) + dua_res = normInf( + H @ qp5.results.x + + g + + A_old.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A_old @ qp5.results.x - b_old), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp5.update(A=A_new, b=b_new) + assert normInf(qp.model.A - A_new) <= 1.0e-3 + qp5.solve() + dua_res = normInf( + H @ qp5.results.x + + g + + A_new.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A_new @ qp5.results.x - b_new), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp5.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp5.results.info.setup_time, qp5.results.info.solve_time + ) + ) + + def test_case_update_rho_update_for_different_initial_guess(self): + print( + "---OSQP: testing sparse random strongly convex qp with equality and inequality constraints: test update rho for different initial guess---" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(rho=1.0e-7) + assert qp.results.info.rho == 1.0e-7 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + qp2 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp2.settings.eps_abs = 1.0e-3 # OSQP unit test + qp2.settings.eps_rel = 0 + qp2.settings.verbose = False + qp2.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp2.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp2.update(rho=1.0e-7) + assert qp2.results.info.rho == 1.0e-7 + qp2.solve() + dua_res = normInf( + H @ qp2.results.x + + g + + A.transpose() @ qp2.results.y + + C.transpose() @ qp2.results.z + ) + pri_res = max( + normInf(A @ qp2.results.x - b), + normInf( + np.maximum(C @ qp2.results.x - u, 0) + + np.minimum(C @ qp2.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp2.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp2.results.info.setup_time, qp2.results.info.solve_time + ) + ) + + qp3 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp3.settings.eps_abs = 1.0e-3 # OSQP unit test + qp3.settings.eps_rel = 0 + qp3.settings.verbose = False + qp3.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp3.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g + + A.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A @ qp3.results.x - b), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp3.update(rho=1.0e-7) + assert qp3.results.info.rho == 1.0e-7 + qp3.solve() + dua_res = normInf( + H @ qp3.results.x + + g + + A.transpose() @ qp3.results.y + + C.transpose() @ qp3.results.z + ) + pri_res = max( + normInf(A @ qp3.results.x - b), + normInf( + np.maximum(C @ qp3.results.x - u, 0) + + np.minimum(C @ qp3.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp3.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp3.results.info.setup_time, qp3.results.info.solve_time + ) + ) + + qp4 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp4.settings.eps_abs = 1.0e-3 # OSQP unit test + qp4.settings.eps_rel = 0 + qp4.settings.verbose = False + qp4.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp4.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g + + A.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A @ qp4.results.x - b), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp4.update(rho=1.0e-7) + assert qp4.results.info.rho == 1.0e-7 + qp4.solve() + dua_res = normInf( + H @ qp4.results.x + + g + + A.transpose() @ qp4.results.y + + C.transpose() @ qp4.results.z + ) + pri_res = max( + normInf(A @ qp4.results.x - b), + normInf( + np.maximum(C @ qp4.results.x - u, 0) + + np.minimum(C @ qp4.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp4.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp4.results.info.setup_time, qp4.results.info.solve_time + ) + ) + + qp5 = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp5.settings.eps_abs = 1.0e-3 # OSQP unit test + qp5.settings.eps_rel = 0 + qp5.settings.verbose = False + qp5.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp5.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + ) + qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z) + dua_res = normInf( + H @ qp5.results.x + + g + + A.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A @ qp5.results.x - b), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp5.update(rho=1.0e-7) + assert qp5.results.info.rho == 1.0e-7 + qp5.solve() + dua_res = normInf( + H @ qp5.results.x + + g + + A.transpose() @ qp5.results.y + + C.transpose() @ qp5.results.z + ) + pri_res = max( + normInf(A @ qp5.results.x - b), + normInf( + np.maximum(C @ qp5.results.x - u, 0) + + np.minimum(C @ qp5.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, n_eq, n_in)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp5.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp5.results.info.setup_time, qp5.results.info.solve_time + ) + ) + + def test_sparse_problem_with_exact_solution_known(self): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints and exact solution known" + ) + + n = 150 + M = spa.lil_matrix(spa.eye(n)) + for i in range(1, n - 1): + M[i, i + 1] = -1 + M[i, i - 1] = 1 + + H = spa.csc_matrix(M.dot(M.transpose())).toarray(order="C") + g = -np.ones((n,)) + A = None + b = None + C = spa.csc_matrix(spa.eye(n)).toarray(order="C") + l = 2.0 * np.ones((n,)) + u = np.full(l.shape, +np.inf) + + qp = proxsuite.osqp.dense.QP(n, 0, n) + qp.settings.eps_abs = 1e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.init(H, g, A, b, C, l, u) + qp.solve() + x_theoretically_optimal = np.array([2.0] * 149 + [3.0]) + + dua_res = normInf(H @ qp.results.x + g + C.transpose() @ qp.results.z) + pri_res = normInf( + np.maximum(C @ qp.results.x - u, 0) + np.minimum(C @ qp.results.x - l, 0) + ) + + assert dua_res <= 1e-3 # default precision of the solver + assert pri_res <= 1e-3 + assert normInf(x_theoretically_optimal - qp.results.x) <= 2e-3 # OSQP + print("--n = {} ; n_eq = {} ; n_in = {}".format(n, 0, n)) + print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_no_initial_guess( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, no initial guess, multiple solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + for i in range(10): + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_EQUALITY_CONSTRAINED_INITIAL_GUESS( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, multiple solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + for i in range(10): + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_COLD_START_WITH_PREVIOUS_RESULT( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + for i in range(10): + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_WARM_START_WITH_PREVIOUS_RESULT( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + for i in range(10): + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_WARM_START_WITH_PREVIOUS_RESULT( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.WARM_START_WITH_PREVIOUS_RESULT + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(mu_eq=1.0e-3, rho=1.0e-6) + assert np.abs(1.0e-6 - qp.settings.default_rho) < 1.0e-9 + assert np.abs(1.0e-6 - qp.results.info.rho) < 1.0e-9 + assert np.abs(1.0e-3 - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_COLD_START_WITH_PREVIOUS_RESULT( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.COLD_START_WITH_PREVIOUS_RESULT + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(mu_eq=1.0e-3, rho=1.0e-6) + assert np.abs(1.0e-6 - qp.settings.default_rho) < 1.0e-9 + assert np.abs(1.0e-6 - qp.results.info.rho) < 1.0e-9 + assert np.abs(1.0e-3 - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_EQUALITY_CONSTRAINED_INITIAL_GUESS( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, update + solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = ( + proxsuite.osqp.InitialGuess.EQUALITY_CONSTRAINED_INITIAL_GUESS + ) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(mu_eq=1.0e-3, rho=1.0e-6) + assert np.abs(1.0e-6 - qp.settings.default_rho) < 1.0e-9 + assert np.abs(1.0e-6 - qp.results.info.rho) < 1.0e-9 + assert np.abs(1.0e-3 - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_NO_INITIAL_GUESS( + self, + ): + print( + "------------------------OSQP: sparse random strongly convex qp with inequality constraints, NO_INITIAL_GUESS, update + solve and default rho and mu_eq" + ) + n = 10 + H, g, A, b, C, u, l = generate_mixed_qp(n) + n_eq = A.shape[0] + n_in = C.shape[0] + rho = 1.0e-7 + mu_eq = 1.0e-4 + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.eps_abs = 1.0e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + True, + rho=rho, + mu_eq=mu_eq, + ) + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + assert np.abs(rho - qp.settings.default_rho) < 1.0e-9 + assert np.abs(rho - qp.results.info.rho) < 1.0e-9 + assert np.abs(mu_eq - qp.settings.default_mu_eq) < 1.0e-9 + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + qp.update(mu_eq=1.0e-3, rho=1.0e-6) + assert np.abs(1.0e-6 - qp.settings.default_rho) < 1.0e-9 + assert np.abs(1.0e-6 - qp.results.info.rho) < 1.0e-9 + assert np.abs(1.0e-3 - qp.settings.default_mu_eq) < 1.0e-9 + qp.solve() + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + ) + assert dua_res <= 1e-3 + assert pri_res <= 1e-3 + + def test_initializing_with_None(self): + print("------------------------OSQP: test initialization with Nones") + + H = np.array([[65.0, -22.0, -16.0], [-22.0, 14.0, 7.0], [-16.0, 7.0, 5.0]]) + g = np.array([-13.0, 15.0, 7.0]) + A = None + b = None + C = None + u = None + l = None + + qp = proxsuite.osqp.dense.QP(3, 0, 0) + qp.settings.eps_abs = 1e-3 # OSQP unit test + qp.settings.eps_rel = 0 + qp.init(H, g, A, b, C, l, u) + qp.solve() + print("optimal x: {}".format(qp.results.x)) + + dua_res = normInf(H @ qp.results.x + g) + + assert dua_res <= 1e-3 # default precision of the solver + print("--n = {} ; n_eq = {} ; n_in = {}".format(3, 0, 0)) + print("dual residual = {} ".format(dua_res)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) + print( + "setup timing = {} ; solve time = {}".format( + qp.results.info.setup_time, qp.results.info.solve_time + ) + ) + + # Fail + def test_z_ordering_with_box_constraints_interface(self): + print( + "------------------------OSQP: test check ordering of z when there are box constraints" + ) + + n = 50 + n_test = 1000 + eps = 1.0e-3 # OSQP unit test + # inequality and box constraints case + for i in range(n_test): + H, g, A, b, C, u, l, u_box, l_box = generate_mixed_qp_with_box(n, i) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.solve() + + # if infeasibility is detected, we relax the tolerance and solve again + if ( + qp.results.info.status == proxsuite.osqp.QPSOLVER_DUAL_INFEASIBLE + or qp.results.info.status == proxsuite.osqp.QPSOLVER_PRIMAL_INFEASIBLE + ): + print(f"[{i}] {qp.results.info.status=}, solve again.") + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.settings.eps_primal_inf = 1e-12 + qp.settings.eps_dual_inf = 1e-12 + qp.solve() + + assert qp.results.info.status == proxsuite.osqp.QPSOLVER_SOLVED + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z[:n_in] + + qp.results.z[n_in:] + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + normInf( + np.maximum(qp.results.x - u_box, 0) + + np.minimum(qp.results.x - l_box, 0) + ), + ) + assert dua_res <= eps + assert pri_res <= eps + # no inequality and box constraints case + for i in range(n_test): + H, g, A, b, C, u, l, u_box, l_box = generate_mixed_qp_with_box(n, i) + n_eq = A.shape[0] + n_in = 0 + C = np.zeros((n_in, n)) + u = np.zeros(n_in) + l = np.zeros(n_in) + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.solve() + + # if infeasibility is detected, we relax the tolerance and solve again + if ( + qp.results.info.status == proxsuite.osqp.QPSOLVER_DUAL_INFEASIBLE + or qp.results.info.status == proxsuite.osqp.QPSOLVER_PRIMAL_INFEASIBLE + ): + print(f"[{i}] {qp.results.info.status=}, solve again.") + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.settings.eps_primal_inf = 1e-12 + qp.settings.eps_dual_inf = 1e-12 + qp.solve() + + # assert qp.results.info.status == proxsuite.osqp.QPSOLVER_SOLVED + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z[:n_in] + + qp.results.z[n_in:] + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + normInf( + np.maximum(qp.results.x - u_box, 0) + + np.minimum(qp.results.x - l_box, 0) + ), + ) + # assert dua_res <= eps + # assert pri_res <= eps + # Note: 1 fail (over 1000) at i = 291 + + # # no inequality, no equalities and box constraints case + for i in range(n_test): + H, g, A, b, C, u, l, u_box, l_box = generate_mixed_qp_with_box(n, i) + n_eq = 0 + n_in = 0 + C = np.zeros((n_in, n)) + u = np.zeros(n_in) + l = np.zeros(n_in) + A = np.zeros((n_eq, n)) + b = np.zeros(n_eq) + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z[:n_in] + + qp.results.z[n_in:] + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + normInf( + np.maximum(qp.results.x - u_box, 0) + + np.minimum(qp.results.x - l_box, 0) + ), + ) + assert dua_res <= eps + assert pri_res <= eps + """ + """ + + def test_updates_with_box_constraints_interface(self): + print( + "------------------------OSQP: test check updates work when there are box constraints" + ) + n = 50 + H, g, A, b, C, u, l = generate_mixed_qp(n) + eps = 1.0e-3 # OSQP unit test + n_eq = A.shape[0] + n_in = C.shape[0] + u_box = np.ones(n) * 100 + l_box = -np.ones(n) * 100 + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in, True) + qp.init(H, g, A, b, C, l, u, l_box, u_box) + qp.settings.eps_abs = eps + qp.settings.eps_rel = 0 + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z[:n_in] + + qp.results.z[n_in:] + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + normInf( + np.maximum(qp.results.x - u_box, 0) + + np.minimum(qp.results.x - l_box, 0) + ), + ) + assert dua_res <= eps + assert pri_res <= eps + u_box += 10 + l_box -= 10 + + qp.update(l=l, u=u, u_box=u_box, l_box=l_box) + qp.solve() + + dua_res = normInf( + H @ qp.results.x + + g + + A.transpose() @ qp.results.y + + C.transpose() @ qp.results.z[:n_in] + + qp.results.z[n_in:] + ) + pri_res = max( + normInf(A @ qp.results.x - b), + normInf( + np.maximum(C @ qp.results.x - u, 0) + + np.minimum(C @ qp.results.x - l, 0) + ), + normInf( + np.maximum(qp.results.x - u_box, 0) + + np.minimum(qp.results.x - l_box, 0) + ), + ) + assert dua_res <= eps + assert pri_res <= eps + + # Option primal_infeasibility_solving + # def test_dense_infeasibility_solving( + # self, + # ): + # print( + # "------------------------dense random strongly convex qp with inequality constraints, test infeasibility solving" + # ) + # n = 20 + # for i in range(20): + # H, g, A, b, C, u, l = generate_mixed_qp(n, i) + # b += 10.0 ## create infeasible pbls + # u -= 100.0 + # n_eq = A.shape[0] + # n_in = C.shape[0] + # qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + # qp.settings.eps_abs = 1.0e-3 + # qp.settings.eps_rel = 0 + # qp.settings.eps_primal_inf = 1.0e-4 + # qp.settings.verbose = False + # qp.settings.primal_infeasibility_solving = True + # qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + # qp.init( + # H, + # np.asfortranarray(g), + # A, + # np.asfortranarray(b), + # C, + # np.asfortranarray(l), + # np.asfortranarray(u), + # ) + # qp.solve() + # dua_res = normInf( + # H @ qp.results.x + # + g + # + A.transpose() @ qp.results.y + # + C.transpose() @ qp.results.z + # ) + # ones = A.T @ np.ones(n_eq) + C.T @ np.ones(n_in) + + # scaled_eps = normInf(ones) * qp.settings.eps_abs + # pri_res = normInf( + # A.T @ (A @ qp.results.x - b) + # + C.T + # @ ( + # np.maximum(C @ qp.results.x - u, 0) + # + np.minimum(C @ qp.results.x - l, 0) + # ) + # ) + # assert dua_res <= qp.settings.eps_abs + # assert pri_res <= scaled_eps + + def test_minimal_eigenvalue_estimation_nonconvex_eigen_option( + self, + ): + print( + "------------------------dense non convex qp with inequality constraints, estimate minimal eigenvalue with eigen method" + ) + n = 50 + tol = 1.0e-3 + for i in range(50): + H, g, A, b, C, u, l = generate_mixed_qp(n, i, -0.01) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + estimate_minimal_eigen_value = ( + proxsuite.osqp.dense.estimate_minimal_eigen_value_of_symmetric_matrix( + H, + proxsuite.osqp.EigenValueEstimateMethodOption.ExactMethod, + 1.0e-6, + 10000, + ) + ) + vals, _ = spa.linalg.eigs(H, which="SR") + min_eigenvalue = float(np.min(vals)) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + manual_minimal_H_eigenvalue=estimate_minimal_eigen_value, + ) + assert ( + np.abs(min_eigenvalue - qp.results.info.minimal_H_eigenvalue_estimate) + <= tol + ) + + def test_minimal_eigenvalue_estimation_nonconvex_manual_option( + self, + ): + print( + "------------------------dense non convex qp with inequality constraints, estimate minimal eigenvalue with manual option" + ) + n = 50 + tol = 1.0e-3 + for i in range(50): + H, g, A, b, C, u, l = generate_mixed_qp(n, i, -0.01) + n_eq = A.shape[0] + n_in = C.shape[0] + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + vals, _ = spa.linalg.eigs(H, which="SR") + min_eigenvalue = float(np.min(vals)) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + manual_minimal_H_eigenvalue=min_eigenvalue, + ) + assert ( + np.abs(min_eigenvalue - qp.results.info.minimal_H_eigenvalue_estimate) + <= tol + ) + + def test_minimal_eigenvalue_estimation_nonconvex_power_iter_option( + self, + ): + print( + "------------------------dense non convex qp with inequality constraints, estimate minimal eigenvalue with power iter option" + ) + n = 50 + tol = 1.0e-3 + for i in range(50): + H, g, A, b, C, u, l = generate_mixed_qp(n, i, -0.01) + n_eq = A.shape[0] + n_in = C.shape[0] + + qp = proxsuite.osqp.dense.QP(n, n_eq, n_in) + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.osqp.InitialGuess.NO_INITIAL_GUESS + estimate_minimal_eigen_value = ( + proxsuite.osqp.dense.estimate_minimal_eigen_value_of_symmetric_matrix( + H, + proxsuite.osqp.EigenValueEstimateMethodOption.PowerIteration, + 1.0e-6, + 10000, + ) + ) + vals, _ = spa.linalg.eigs(H, which="SR") + min_eigenvalue = float(np.min(vals)) + qp.init( + H, + np.asfortranarray(g), + A, + np.asfortranarray(b), + C, + np.asfortranarray(l), + np.asfortranarray(u), + manual_minimal_H_eigenvalue=estimate_minimal_eigen_value, + ) + assert ( + np.abs(min_eigenvalue - qp.results.info.minimal_H_eigenvalue_estimate) + <= tol + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/src/osqp/dense_ruiz_equilibration.cpp b/test/src/osqp/dense_ruiz_equilibration.cpp new file mode 100644 index 000000000..e8d127567 --- /dev/null +++ b/test/src/osqp/dense_ruiz_equilibration.cpp @@ -0,0 +1,72 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using namespace proxsuite; +using Scalar = double; + +DOCTEST_TEST_CASE("OSQP: ruiz preconditioner") +{ + int dim = 5; + int n_eq = 6; + int n_in = 0; + auto sym = common::Symmetry::general; // 0 : upper triangular (by default), + // 1: + // auto sym = common::Symmetry::lower; // 0 : upper triangular (by default), + // 1: lower triangular ; else full matrix + + Scalar sparsity_factor(0.75); + Scalar strong_convexity_factor(0.01); + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + + switch (sym) { + case common::Symmetry::upper: { + qp_random.H = qp_random.H.triangularView(); + break; + } + case common::Symmetry::lower: { + qp_random.H = qp_random.H.triangularView(); + break; + } + default: { + } + } + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + + auto head = Eigen::Matrix( + qp.ruiz.delta.head(dim).asDiagonal()); + auto tail = Eigen::Matrix( + qp.ruiz.delta.tail(n_eq).asDiagonal()); + auto c = qp.ruiz.c; + + auto const& H = qp_random.H; + auto const& g = qp_random.g; + auto const& A = qp_random.A; + auto const& b = qp_random.b; + + auto H_new = (c * head * H * head).eval(); + auto g_new = (c * head * g).eval(); + auto A_new = (tail * A * head).eval(); + auto b_new = (tail * b).eval(); + + DOCTEST_CHECK((H_new - qp.work.H_scaled).norm() <= Scalar(1e-10)); + DOCTEST_CHECK((g_new - qp.work.g_scaled).norm() <= Scalar(1e-10)); + DOCTEST_CHECK((A_new - qp.work.A_scaled).norm() <= Scalar(1e-10)); + DOCTEST_CHECK((b_new - qp.work.b_scaled).norm() <= Scalar(1e-10)); +} diff --git a/test/src/osqp/dense_unconstrained_qp.cpp b/test/src/osqp/dense_unconstrained_qp.cpp new file mode 100644 index 000000000..48029b3fb --- /dev/null +++ b/test/src/osqp/dense_unconstrained_qp.cpp @@ -0,0 +1,267 @@ +// +// Copyright (c) 2025 INRIA +// +#include +#include +#include +#include +#include +#include +#include + +using T = double; +using namespace proxsuite; + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex unconstrained qp and " + "increasing dimension") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with increasing " + "dimension---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = 0; + for (int dim = 10; dim < 1000; dim += 100) { + + int n_eq(0); + int n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE( + "OSQP: sparse random not strongly convex unconstrained qp and " + "increasing dimension") +{ + + std::cout + << "---OSQP: testing sparse random not strongly convex unconstrained qp " + "with increasing dimension---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = 0; + for (int dim = 10; dim < 1000; dim += 100) { + + int n_eq(0); + int n_in(0); + T strong_convexity_factor(0); + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor); + auto x_sol = common::utils::rand::vector_rand(dim); + qp_random.g = + -qp_random.H * + x_sol; // to be dually feasible g must be in the image space of H + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; + } +} + +DOCTEST_TEST_CASE("OSQP: unconstrained qp with H = Id and g random") +{ + + std::cout << "---OSQP: unconstrained qp with H = Id and g random---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = 0; + + int dim(100); + int n_eq(0); + int n_in(0); + T strong_convexity_factor(1.E-2); + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor); + qp_random.H.setZero(); + qp_random.H.diagonal().array() += 1; + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: unconstrained qp with H = Id and g = 0") +{ + + std::cout << "---OSQP: unconstrained qp with H = Id and g = 0---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = 0; + + int dim(100); + int n_eq(0); + int n_in(0); + T strong_convexity_factor(1.E-2); + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor); + qp_random.H.setZero(); + qp_random.H.diagonal().array() += 1; + qp_random.g.setZero(); + + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + qp.solve(); + + T pri_res = std::max( + (qp_random.A * qp.results.x - qp_random.b).lpNorm(), + (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm()); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.A.transpose() * qp.results.y + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------solving qp with dim: " << dim << " neq: " << n_eq + << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter_ext + << std::endl; +} + +DOCTEST_TEST_CASE("OSQP: sparse random strongly convex unconstrained qp and " + "increasing dimension " + "with solution poslihing") +{ + + std::cout + << "---OSQP: testing sparse random strongly convex qp with increasing " + "dimension with solution poslihing to check that no active set " + "is found---" + << std::endl; + double sparsity_factor = 0.15; + T eps_abs = T(1e-5); // OSQP unit test + T eps_rel = 0; + for (int dim = 10; dim < 1000; dim += 100) { + + int n_eq(0); + int n_in(0); + T strong_convexity_factor(1.e-2); + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( + dim, sparsity_factor, strong_convexity_factor); + osqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = eps_rel; + qp.settings.polish = true; + qp.init(qp_random.H, + qp_random.g, + qp_random.A, + qp_random.b, + qp_random.C, + qp_random.l, + qp_random.u); + + DOCTEST_CHECK(qp.results.info.status_polish == + common::PolishOutput::POLISH_NOT_RUN); // not run before solve + + qp.solve(); + + DOCTEST_CHECK(qp.results.info.status_polish == + common::PolishOutput::POLISH_SUCCEEDED); + // Note: Choice in the implemntation: Perform solution polishing on Hx = -g + // on unconstrained problems to make the dual residual vanish. + // It is done in the osqp wrapper from conda. + } +} \ No newline at end of file diff --git a/test/src/backward.cpp b/test/src/proxqp/backward.cpp similarity index 100% rename from test/src/backward.cpp rename to test/src/proxqp/backward.cpp diff --git a/test/src/cvxpy.cpp b/test/src/proxqp/cvxpy.cpp similarity index 77% rename from test/src/cvxpy.cpp rename to test/src/proxqp/cvxpy.cpp index dfdb6df2e..7bf41d88f 100644 --- a/test/src/cvxpy.cpp +++ b/test/src/proxqp/cvxpy.cpp @@ -8,23 +8,23 @@ using T = double; using namespace proxsuite; -using namespace proxsuite::proxqp; - -template -using Mat = - Eigen::Matrix; +using namespace proxsuite::common; + +template +using Mat = Eigen::Matrix; template using Vec = Eigen::Matrix; -DOCTEST_TEST_CASE("3 dim test case from cvxpy, check feasibility") +DOCTEST_TEST_CASE("ProxQP: 3 dim test case from cvxpy, check feasibility") { - std::cout << "---3 dim test case from cvxpy, check feasibility " << std::endl; + std::cout << "---ProxQP: 3 dim test case from cvxpy, check feasibility " + << std::endl; T eps_abs = T(1e-9); - dense::isize dim = 3; + isize dim = 3; Mat H = Mat(dim, dim); H << 13.0, 12.0, -2.0, 12.0, 17.0, 6.0, -2.0, 6.0, 12.0; @@ -40,7 +40,7 @@ DOCTEST_TEST_CASE("3 dim test case from cvxpy, check feasibility") Vec u = Vec(dim); u << 1.0, 1.0, 1.0; - Results results = dense::solve( + common::Results results = proxqp::dense::solve( H, g, nullopt, nullopt, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); T pri_res = (helpers::positive_part(C * results.x - u) + @@ -58,13 +58,13 @@ DOCTEST_TEST_CASE("3 dim test case from cvxpy, check feasibility") << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("simple test case from cvxpy, check feasibility") +DOCTEST_TEST_CASE("ProxQP: simple test case from cvxpy, check feasibility") { - std::cout << "---simple test case from cvxpy, check feasibility " + std::cout << "---ProxQP: simple test case from cvxpy, check feasibility " << std::endl; T eps_abs = T(1e-8); - dense::isize dim = 1; + isize dim = 1; Mat H = Mat(dim, dim); H << 20.0; @@ -80,7 +80,7 @@ DOCTEST_TEST_CASE("simple test case from cvxpy, check feasibility") Vec u = Vec(dim); u << 1.0; - Results results = dense::solve( + common::Results results = proxqp::dense::solve( H, g, nullopt, nullopt, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); T pri_res = (helpers::positive_part(C * results.x - u) + @@ -101,15 +101,17 @@ DOCTEST_TEST_CASE("simple test case from cvxpy, check feasibility") << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("simple test case from cvxpy, init with solution, check that " - "solver stays there") +DOCTEST_TEST_CASE( + "ProxQP: simple test case from cvxpy, init with solution, check that " + "solver stays there") { - std::cout << "---simple test case from cvxpy, init with solution, check that " - "solver stays there" - << std::endl; + std::cout + << "---ProxQP: simple test case from cvxpy, init with solution, check that " + "solver stays there" + << std::endl; T eps_abs = T(1e-4); - dense::isize dim = 1; + isize dim = 1; Mat H = Mat(dim, dim); H << 20.0; @@ -128,15 +130,15 @@ DOCTEST_TEST_CASE("simple test case from cvxpy, init with solution, check that " T x_sol = 0.5; - proxqp::isize n_in(1); - proxqp::isize n_eq(0); + isize n_in(1); + isize n_eq(0); proxqp::dense::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.init(H, g, nullopt, nullopt, C, u, l); - dense::Vec x = dense::Vec(dim); - dense::Vec z = dense::Vec(n_in); + common::dense::Vec x = common::dense::Vec(dim); + common::dense::Vec z = common::dense::Vec(n_in); x << 0.5; z << 0.0; qp.solve(x, nullopt, z); diff --git a/test/src/cvxpy.py b/test/src/proxqp/cvxpy.py similarity index 85% rename from test/src/cvxpy.py rename to test/src/proxqp/cvxpy.py index cd45496cf..2da878277 100644 --- a/test/src/cvxpy.py +++ b/test/src/proxqp/cvxpy.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2022, INRIA +# Copyright (c) 2025, INRIA # import proxsuite @@ -17,7 +17,7 @@ def normInf(x): class CvxpyTest(unittest.TestCase): def test_trigger_infeasibility_with_exact_solution_known(self): print( - "------------------------ test if infeasibility is triggered even though exact solution known" + "------------------------ ProxQP: test if infeasibility is triggered even though exact solution known" ) n = 3 @@ -39,14 +39,14 @@ def test_trigger_infeasibility_with_exact_solution_known(self): pri_res = normInf( np.maximum(C @ qp.results.x - u, 0) + np.minimum(C @ qp.results.x - l, 0) ) - assert qp.results.info.status.name == "PROXQP_SOLVED" + assert qp.results.info.status.name == "QPSOLVER_SOLVED" assert dua_res <= 1e-3 # default precision of the solver assert pri_res <= 1e-3 assert normInf(x_sol - qp.results.x) <= 1e-3 print("--n = {} ; n_eq = {} ; n_in = {}".format(n, 0, n)) print("dual residual = {} ; primal residual = {}".format(dua_res, pri_res)) - print("total number of iteration: {}".format(qp.results.info.iter)) + print("total number of iteration: {}".format(qp.results.info.iter_ext)) print( "setup timing = {} ; solve time = {}".format( qp.results.info.setup_time, qp.results.info.solve_time @@ -54,7 +54,9 @@ def test_trigger_infeasibility_with_exact_solution_known(self): ) def test_one_dim_with_exact_solution_known(self): - print("------------------------ test_one_dim_with_exact_solution_known") + print( + "------------------------ ProxQP: test_one_dim_with_exact_solution_known" + ) n = 1 H = np.array([[20.0]]) g = np.array([-10.0]) diff --git a/test/src/dense_backward.cpp b/test/src/proxqp/dense_backward.cpp similarity index 82% rename from test/src/dense_backward.cpp rename to test/src/proxqp/dense_backward.cpp index 346275161..a4d3cb9b5 100644 --- a/test/src/dense_backward.cpp +++ b/test/src/proxqp/dense_backward.cpp @@ -6,23 +6,24 @@ #include #include #include -#include +#include #include using T = double; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; -DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (feasible QP)") +DOCTEST_TEST_CASE( + "ProxQP: proxqp::dense: test compute backward for g (feasible QP)") { double sparsity_factor = 0.85; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(5), n_in(0); + isize n_eq(5), n_in(0); T strong_convexity_factor(1.e-1); - proxqp::dense::Model random_qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model random_qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix H = random_qp.H; @@ -33,7 +34,7 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (feasible QP)") // Eigen::Matrix l = random_qp.l; // Eigen::Matrix u = random_qp.u; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -45,7 +46,8 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (feasible QP)") Eigen::MatrixXd dx_dg = Eigen::MatrixXd::Zero(dim, dim); for (int i = 0; i < dim; i++) { loss_derivative(i) = T(1); - dense::compute_backward(qp, loss_derivative, 1e-5, 1e-7, 1e-7); + proxqp::dense::compute_backward( + qp, loss_derivative, 1e-5, 1e-7, 1e-7); dx_dg.row(i) = qp.model.backward_data.dL_dg; loss_derivative(i) = T(0); } @@ -79,16 +81,17 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (feasible QP)") } } -DOCTEST_TEST_CASE("proxqp::dense: test compute backward for b (feasible QP)") +DOCTEST_TEST_CASE( + "ProxQP: proxqp::dense: test compute backward for b (feasible QP)") { double sparsity_factor = 0.85; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(5), n_in(0); + isize n_eq(5), n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model random_qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model random_qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix H = random_qp.H; @@ -99,7 +102,7 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for b (feasible QP)") // Eigen::Matrix l = random_qp.l; // Eigen::Matrix u = random_qp.u; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -111,7 +114,8 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for b (feasible QP)") Eigen::MatrixXd dx_db = Eigen::MatrixXd::Zero(dim, n_eq); for (int i = 0; i < dim; i++) { loss_derivative(i) = 1; - dense::compute_backward(qp, loss_derivative, 1e-5, 1e-7, 1e-7); + proxqp::dense::compute_backward( + qp, loss_derivative, 1e-5, 1e-7, 1e-7); dx_db.row(i) = qp.model.backward_data.dL_db; loss_derivative(i) = 0; } @@ -145,17 +149,17 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for b (feasible QP)") } } -DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (QP with " +DOCTEST_TEST_CASE("ProxQP: proxqp::dense: test compute backward for g (QP with " "saturating inequality constraints)") { double sparsity_factor = 0.85; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 6; + common::utils::rand::set_seed(1); + isize dim = 6; - dense::isize n_eq(0), n_in(12); + isize n_eq(0), n_in(12); T strong_convexity_factor(1.e-1); - proxqp::dense::Model random_qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model random_qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); std::cout << "creating random qp " << std::endl; @@ -172,7 +176,7 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (QP with " l(9) = 1e3; // Eigen::Matrix u = random_qp.u; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -187,7 +191,8 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for g (QP with " Eigen::MatrixXd dx_dg = Eigen::MatrixXd::Zero(dim, dim); for (int i = 0; i < dim; i++) { loss_derivative(i) = T(1); - dense::compute_backward(qp, loss_derivative, 1e-5, 1e-7, 1e-7); + proxqp::dense::compute_backward( + qp, loss_derivative, 1e-5, 1e-7, 1e-7); dx_dg.row(i) = qp.model.backward_data.dL_dg; loss_derivative(i) = T(0); } diff --git a/test/src/dense_maros_meszaros.cpp b/test/src/proxqp/dense_maros_meszaros.cpp similarity index 91% rename from test/src/dense_maros_meszaros.cpp rename to test/src/proxqp/dense_maros_meszaros.cpp index 0e1340f08..46f8bb678 100644 --- a/test/src/dense_maros_meszaros.cpp +++ b/test/src/proxqp/dense_maros_meszaros.cpp @@ -3,10 +3,11 @@ // #include #include -#include +#include #include using namespace proxsuite; +using namespace proxsuite::common; #define MAROS_MESZAROS_DIR PROBLEM_PATH "/data/maros_meszaros_data/" @@ -82,11 +83,11 @@ char const* files[] = { MAROS_MESZAROS_DIR "YAO.mat", MAROS_MESZAROS_DIR "ZECEVIC2.mat", }; -TEST_CASE("dense maros meszaros using the api") +TEST_CASE("ProxQP: dense maros meszaros using the api") { using T = double; - using isize = proxqp::utils::isize; - proxsuite::proxqp::Timer timer; + using isize = dense::isize; + Timer timer; T elapsed_time = 0.0; for (auto const* file : files) { @@ -120,7 +121,7 @@ TEST_CASE("dense maros meszaros using the api") timer.stop(); timer.start(); proxqp::dense::QP qp{ - dim, n_eq, n_in, false, proxsuite::proxqp::DenseBackend::Automatic + dim, n_eq, n_in, false, DenseBackend::Automatic }; // creating QP object qp.init(H, g, A, b, C, l, u); @@ -132,28 +133,28 @@ TEST_CASE("dense maros meszaros using the api") for (size_t it = 0; it < 2; ++it) { if (it > 0) - qp.settings.initial_guess = proxsuite::proxqp::InitialGuessStatus:: - WARM_START_WITH_PREVIOUS_RESULT; + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp.solve(); const auto& x = qp.results.x; const auto& y = qp.results.y; const auto& z = qp.results.z; - T prim_eq = proxqp::dense::infty_norm(A * x - b); + T prim_eq = common::dense::infty_norm(A * x - b); T prim_in = - proxqp::dense::infty_norm(helpers::positive_part(C * x - u) + + common::dense::infty_norm(helpers::positive_part(C * x - u) + helpers::negative_part(C * x - l)); std::cout << "primal residual " << std::max(prim_eq, prim_in) << std::endl; std::cout << "dual residual " - << proxqp::dense::infty_norm(H * x + g + A.transpose() * y + + << common::dense::infty_norm(H * x + g + A.transpose() * y + C.transpose() * z) << std::endl; std::cout << "iter " << qp.results.info.iter << std::endl; - CHECK(proxqp::dense::infty_norm(H * x + g + A.transpose() * y + + CHECK(common::dense::infty_norm(H * x + g + A.transpose() * y + C.transpose() * z) < 2 * eps); - CHECK(proxqp::dense::infty_norm(A * x - b) > -eps); + CHECK(common::dense::infty_norm(A * x - b) > -eps); CHECK((C * x - l).minCoeff() > -eps); CHECK((C * x - u).maxCoeff() < eps); diff --git a/test/src/dense_qp_eq.cpp b/test/src/proxqp/dense_qp_eq.cpp similarity index 70% rename from test/src/dense_qp_eq.cpp rename to test/src/proxqp/dense_qp_eq.cpp index 2a6aa2541..6bb625556 100644 --- a/test/src/dense_qp_eq.cpp +++ b/test/src/proxqp/dense_qp_eq.cpp @@ -7,36 +7,36 @@ #include #include #include -#include +#include using T = double; using namespace proxsuite; -DOCTEST_TEST_CASE("qp: start from solution using the wrapper framework") +using namespace proxsuite::common; + +DOCTEST_TEST_CASE("ProxQP: qp: start from solution using the wrapper framework") { - proxqp::isize dim = 30; - proxqp::isize n_eq = 6; - proxqp::isize n_in = 0; + isize dim = 30; + isize n_eq = 6; + isize n_in = 0; T sparsity_factor = 0.15; T strong_convexity_factor(1.e-2); - std::cout << "---testing sparse random strongly convex qp with equality " - "constraints and starting at the solution using the wrapper " - "framework---" - << std::endl; - proxqp::utils::rand::set_seed(1); - auto H = ::proxsuite::proxqp::utils::rand:: - sparse_positive_definite_rand_not_compressed( - dim, strong_convexity_factor, sparsity_factor); - auto A = - ::proxsuite::proxqp::utils::rand::sparse_matrix_rand_not_compressed( - n_eq, dim, sparsity_factor); - auto solution = ::proxsuite::proxqp::utils::rand::vector_rand(dim + n_eq); + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality " + "constraints and starting at the solution using the wrapper " + "framework---" + << std::endl; + common::utils::rand::set_seed(1); + auto H = ::utils::rand::sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor); + auto A = ::utils::rand::sparse_matrix_rand_not_compressed( + n_eq, dim, sparsity_factor); + auto solution = ::utils::rand::vector_rand(dim + n_eq); auto primal_solution = solution.topRows(dim); auto dual_solution = solution.bottomRows(n_eq); auto b = A * primal_solution; auto g = -H * primal_solution - A.transpose() * dual_solution; - auto C = - ::proxsuite::proxqp::utils::rand::sparse_matrix_rand_not_compressed( - 0, dim, sparsity_factor); + auto C = ::utils::rand::sparse_matrix_rand_not_compressed( + 0, dim, sparsity_factor); Eigen::Matrix dual_init_in(n_in); Eigen::Matrix u(0); Eigen::Matrix l(0); @@ -45,7 +45,7 @@ DOCTEST_TEST_CASE("qp: start from solution using the wrapper framework") proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; - qp.settings.initial_guess = proxsuite::proxqp::InitialGuessStatus::WARM_START; + qp.settings.initial_guess = InitialGuessStatus::WARM_START; qp.init(H, g, A, b, C, l, u); qp.solve(primal_solution, dual_solution, dual_init_in); @@ -53,22 +53,24 @@ DOCTEST_TEST_CASE("qp: start from solution using the wrapper framework") DOCTEST_CHECK((H * qp.results.x + g + A.transpose() * qp.results.y) .lpNorm() <= eps_abs); } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality constraints " - "and increasing dimension with the wrapper API") +DOCTEST_TEST_CASE( + "ProxQP: sparse random strongly convex qp with equality constraints " + "and increasing dimension with the wrapper API") { - std::cout << "---testing sparse random strongly convex qp with equality " - "constraints and increasing dimension with the wrapper API---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality " + "constraints and increasing dimension with the wrapper API---" + << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(0); + isize n_eq(dim / 2); + isize n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; @@ -100,25 +102,27 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality constraints " << std::endl; } } -DOCTEST_TEST_CASE("linear problem with equality with equality constraints and " - "linar cost and increasing dimension using wrapper API") +DOCTEST_TEST_CASE( + "ProxQP: linear problem with equality with equality constraints and " + "linar cost and increasing dimension using wrapper API") { - std::cout << "---testing linear problem with equality constraints and " - "increasing dimension using wrapper API---" - << std::endl; + std::cout + << "---ProxQP: testing linear problem with equality constraints and " + "increasing dimension using wrapper API---" + << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(0); + isize n_eq(dim / 2); + isize n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - auto y_sol = proxqp::utils::rand::vector_rand( + auto y_sol = common::utils::rand::vector_rand( n_eq); // make sure the LP is bounded within the feasible set qp_random.g = -qp_random.A.transpose() * y_sol; @@ -155,32 +159,33 @@ DOCTEST_TEST_CASE("linear problem with equality with equality constraints and " } } -DOCTEST_TEST_CASE("linear problem with equality with equality constraints and " - "linear cost and increasing dimension using wrapper API and " - "the dedicated LP interface") +DOCTEST_TEST_CASE( + "ProxQP: linear problem with equality with equality constraints and " + "linear cost and increasing dimension using wrapper API and " + "the dedicated LP interface") { std::cout - << "---testing LP interface for solving linear problem with " + << "---ProxQP: testing LP interface for solving linear problem with " "equality constraints and increasing dimension using wrapper API---" << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_eq(dim / 2); - proxqp::isize n_in(0); + isize n_eq(dim / 2); + isize n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - auto y_sol = proxqp::utils::rand::vector_rand( + auto y_sol = common::utils::rand::vector_rand( n_eq); // make sure the LP is bounded within the feasible set qp_random.g = -qp_random.A.transpose() * y_sol; proxqp::dense::QP qp{ - dim, n_eq, n_in, proxqp::HessianType::Zero + dim, n_eq, n_in, common::HessianType::Zero }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -214,7 +219,7 @@ DOCTEST_TEST_CASE("linear problem with equality with equality constraints and " } } -DOCTEST_TEST_CASE("infeasible qp") +DOCTEST_TEST_CASE("ProxQP: infeasible qp") { // (x1- 9)^2 + (x2-6)^2 // s.t. @@ -252,5 +257,5 @@ DOCTEST_TEST_CASE("infeasible qp") qp.solve(); DOCTEST_CHECK(qp.results.info.status == - proxsuite::proxqp::QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE); + QPSolverOutput::QPSOLVER_PRIMAL_INFEASIBLE); } \ No newline at end of file diff --git a/test/src/dense_qp_solve.cpp b/test/src/proxqp/dense_qp_solve.cpp similarity index 56% rename from test/src/dense_qp_solve.cpp rename to test/src/proxqp/dense_qp_solve.cpp index 012bcb4c5..474ecec81 100644 --- a/test/src/dense_qp_solve.cpp +++ b/test/src/proxqp/dense_qp_solve.cpp @@ -7,22 +7,22 @@ #include #include #include -#include +#include using T = double; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; -DOCTEST_TEST_CASE("proxqp::dense: test init with fixed sizes matrices") +DOCTEST_TEST_CASE("ProxQP: proxqp::dense: test init with fixed sizes matrices") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(5), n_in(2); + isize n_eq(5), n_in(2); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); Eigen::Matrix H = qp.H; @@ -34,7 +34,7 @@ DOCTEST_TEST_CASE("proxqp::dense: test init with fixed sizes matrices") Eigen::Matrix u = qp.u; { - Results results = dense::solve( + common::Results results = proxqp::dense::solve( H, g, A, b, C, l, u, nullopt, nullopt, nullopt, eps_abs, 0); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), @@ -58,12 +58,12 @@ DOCTEST_TEST_CASE("proxqp::dense: test init with fixed sizes matrices") } { - dense::QP qp_problem(dim, n_eq, 0); + proxqp::dense::QP qp_problem(dim, n_eq, 0); qp_problem.init(H, g, A, b, nullopt, nullopt, nullopt); qp_problem.settings.eps_abs = eps_abs; qp_problem.solve(); - const Results& results = qp_problem.results; + const common::Results& results = qp_problem.results; T pri_res = (qp.A * results.x - qp.b).lpNorm(); T dua_res = (qp.H * results.x + qp.g + qp.A.transpose() * results.y) @@ -82,35 +82,36 @@ DOCTEST_TEST_CASE("proxqp::dense: test init with fixed sizes matrices") } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve function") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve function---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve function---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - Results results = dense::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - 0); + common::Results results = proxqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + @@ -131,36 +132,37 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve with different rho value") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve with different rho value---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different rho value---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - Results results = dense::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - 0, - T(1.E-7)); + common::Results results = proxqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + T(1.E-7)); DOCTEST_CHECK(results.info.rho == T(1.E-7)); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + @@ -182,39 +184,40 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve with different mu_eq and mu_in values") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve with different mu_eq and " - "mu_in values---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different mu_eq and " + "mu_in values---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - Results results = dense::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - 0, - nullopt, - T(1.E-2), - T(1.E-2)); + common::Results results = proxqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + T(1.E-2), + T(1.E-2)); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l)) @@ -234,27 +237,28 @@ DOCTEST_TEST_CASE( << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test warm starting") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test warm starting---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - auto x_wm = utils::rand::vector_rand(dim); - auto y_wm = utils::rand::vector_rand(n_eq); - auto z_wm = utils::rand::vector_rand(n_in); - Results results = dense::solve( + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); + common::Results results = proxqp::dense::solve( qp.H, qp.g, qp.A, qp.b, qp.C, qp.l, qp.u, x_wm, y_wm, z_wm, eps_abs, 0); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + @@ -275,40 +279,41 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test verbose = true") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test verbose = true ---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test verbose = true ---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); bool verbose = true; - Results results = dense::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - 0, - nullopt, - nullopt, - nullopt, - verbose); + common::Results results = proxqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + nullopt, + nullopt, + verbose); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l)) @@ -328,44 +333,45 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " << results.info.solve_time << std::endl; } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test no initial guess ---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test no initial guess ---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; - Results results = dense::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - 0, - nullopt, - nullopt, - nullopt, - nullopt, - true, - true, - nullopt, - initial_guess); + common::Results results = proxqp::dense::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + 0, + nullopt, + nullopt, + nullopt, + nullopt, + true, + true, + nullopt, + initial_guess); T pri_res = std::max((qp.A * results.x - qp.b).lpNorm(), (helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l)) diff --git a/test/src/dense_qp_solve.py b/test/src/proxqp/dense_qp_solve.py similarity index 91% rename from test/src/dense_qp_solve.py rename to test/src/proxqp/dense_qp_solve.py index ebc5dd19f..d091f2985 100644 --- a/test/src/dense_qp_solve.py +++ b/test/src/proxqp/dense_qp_solve.py @@ -52,7 +52,7 @@ class DenseQpWrapper(unittest.TestCase): def test_case_basic_solve(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test basic solve" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test basic solve" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -92,7 +92,7 @@ def test_case_basic_solve(self): def test_case_different_rho_value(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test different rho values" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test different rho values" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -134,7 +134,7 @@ def test_case_different_rho_value(self): def test_case_different_mu_values(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test different mu_eq and mu_in values" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test different mu_eq and mu_in values" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -176,7 +176,7 @@ def test_case_different_mu_values(self): def test_case_different_warm_starting(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test warm starting" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test warm starting" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -221,7 +221,7 @@ def test_case_different_warm_starting(self): def test_case_different_verbose_true(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test verbose = true" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test verbose = true" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -261,7 +261,7 @@ def test_case_different_verbose_true(self): def test_case_different_no_initial_guess(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test no initial guess" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -301,7 +301,7 @@ def test_case_different_no_initial_guess(self): def test_sparse_problem_with_exact_solution_known(self): print( - "------------------------sparse random strongly convex qp with inequality constraints and exact solution known" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints and exact solution known" ) n = 150 @@ -341,7 +341,7 @@ def test_sparse_problem_with_exact_solution_known(self): ) def test_initializing_with_None(self): - print("------------------------test initialization with Nones") + print("------------------------ProxQP: test initialization with Nones") H = np.array([[65.0, -22.0, -16.0], [-22.0, 14.0, 7.0], [-16.0, 7.0, 5.0]]) g = np.array([-13.0, 15.0, 7.0]) @@ -374,10 +374,10 @@ def test_initializing_with_None(self): def test_solve_qpsolvers_problem(self): print( - "------------------------test case from qpsolvers with equality constraint and upper bound inequality constraints" + "------------------------ProxQP: test case from qpsolvers with equality constraint and upper bound inequality constraints" ) file_path = os.path.dirname(os.path.realpath(__file__)) - data_path = os.path.join(file_path, "..", "data") + data_path = os.path.join(file_path, "..", "..", "data") m = spio.loadmat( os.path.join(data_path, "simple_qp_with_inifinity_lower_bound.mat"), squeeze_me=True, diff --git a/test/src/dense_qp_with_eq_and_in.cpp b/test/src/proxqp/dense_qp_with_eq_and_in.cpp similarity index 75% rename from test/src/dense_qp_with_eq_and_in.cpp rename to test/src/proxqp/dense_qp_with_eq_and_in.cpp index cb7e1d808..c802133cc 100644 --- a/test/src/dense_qp_with_eq_and_in.cpp +++ b/test/src/proxqp/dense_qp_with_eq_and_in.cpp @@ -7,28 +7,30 @@ #include #include #include -#include +#include + using T = double; using namespace proxsuite; +using namespace proxsuite::common; -DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and inequality constraints " - "and increasing dimension using wrapper API") +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " + "inequality constraints " + "and increasing dimension using wrapper API") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints and increasing dimension using wrapper API---" << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_eq(dim / 4); - proxqp::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object @@ -64,23 +66,24 @@ DOCTEST_TEST_CASE( } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with box inequality " - "constraints and increasing dimension using the API") +DOCTEST_TEST_CASE( + "ProxQP: sparse random strongly convex qp with box inequality " + "constraints and increasing dimension using the API") { - std::cout - << "---testing sparse random strongly convex qp with box inequality " - "constraints and increasing dimension using the API---" - << std::endl; + std::cout << "---ProxQP: testing sparse random strongly convex qp with box " + "inequality " + "constraints and increasing dimension using the API---" + << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_eq(0); - proxqp::isize n_in(dim); + isize n_eq(0); + isize n_in(dim); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_box_constrained_qp( + common::dense::Model qp_random = common::utils::dense_box_constrained_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; @@ -114,22 +117,23 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with box inequality " } } -DOCTEST_TEST_CASE("sparse random not strongly convex qp with inequality " - "constraints and increasing dimension using the API") +DOCTEST_TEST_CASE( + "ProxQP: sparse random not strongly convex qp with inequality " + "constraints and increasing dimension using the API") { - std::cout - << "---testing sparse random not strongly convex qp with inequality " - "constraints and increasing dimension using the API---" - << std::endl; + std::cout << "---ProxQP: testing sparse random not strongly convex qp with " + "inequality " + "constraints and increasing dimension using the API---" + << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_in(dim / 2); - proxqp::isize n_eq(0); - proxqp::dense::Model qp_random = - proxqp::utils::dense_not_strongly_convex_qp( + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize n_in(dim / 2); + isize n_eq(0); + common::dense::Model qp_random = + common::utils::dense_not_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor); proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object @@ -164,23 +168,24 @@ DOCTEST_TEST_CASE("sparse random not strongly convex qp with inequality " } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with degenerate inequality " - "constraints and increasing dimension using the API") +DOCTEST_TEST_CASE( + "ProxQP: sparse random strongly convex qp with degenerate inequality " + "constraints and increasing dimension using the API") { std::cout - << "---testing sparse random strongly convex qp with degenerate " + << "---ProxQP: testing sparse random strongly convex qp with degenerate " "inequality constraints and increasing dimension using the API---" << std::endl; T sparsity_factor = 0.45; T eps_abs = T(1e-9); T strong_convexity_factor(1e-2); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize m(dim / 4); - proxqp::isize n_in(2 * m); - proxqp::isize n_eq(0); - proxqp::dense::Model qp_random = proxqp::utils::dense_degenerate_qp( + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize m(dim / 4); + isize n_in(2 * m); + isize n_eq(0); + common::dense::Model qp_random = common::utils::dense_degenerate_qp( dim, n_eq, m, // it n_in = 2 * m, it doubles the inequality constraints @@ -198,7 +203,7 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with degenerate inequality " qp_random.u); qp.solve(); DOCTEST_CHECK(qp.results.info.status == - proxqp::QPSolverOutput::PROXQP_SOLVED); + common::QPSolverOutput::QPSOLVER_SOLVED); T pri_res = std::max( (qp_random.A * qp.results.x - qp_random.b).lpNorm(), (helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + @@ -220,24 +225,26 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with degenerate inequality " } } -DOCTEST_TEST_CASE("linear problem with equality inequality constraints and " - "increasing dimension using the API") +DOCTEST_TEST_CASE( + "ProxQP: linear problem with equality inequality constraints and " + "increasing dimension using the API") { srand(1); - std::cout << "---testing linear problem with inequality constraints and " - "increasing dimension using the API---" - << std::endl; + std::cout + << "---ProxQP: testing linear problem with inequality constraints and " + "increasing dimension using the API---" + << std::endl; T sparsity_factor = 0.15; T eps_abs = T(1e-9); - proxqp::utils::rand::set_seed(1); - for (proxqp::isize dim = 10; dim < 1000; dim += 100) { - proxqp::isize n_in(dim / 2); - proxqp::isize n_eq(0); - proxqp::dense::Model qp_random = - proxqp::utils::dense_not_strongly_convex_qp( + common::utils::rand::set_seed(1); + for (isize dim = 10; dim < 1000; dim += 100) { + isize n_in(dim / 2); + isize n_eq(0); + common::dense::Model qp_random = + common::utils::dense_not_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor); qp_random.H.setZero(); - auto z_sol = proxqp::utils::rand::vector_rand(n_in); + auto z_sol = common::utils::rand::vector_rand(n_in); qp_random.g = -qp_random.C.transpose() * z_sol; // make sure the LP is bounded within the feasible set // std::cout << "g : " << qp.g << " C " << qp.C << " u " << qp.u << " l " diff --git a/test/src/dense_qp_wrapper.cpp b/test/src/proxqp/dense_qp_wrapper.cpp similarity index 90% rename from test/src/dense_qp_wrapper.cpp rename to test/src/proxqp/dense_qp_wrapper.cpp index 353fb7dce..b96e863ce 100644 --- a/test/src/dense_qp_wrapper.cpp +++ b/test/src/proxqp/dense_qp_wrapper.cpp @@ -7,32 +7,33 @@ #include #include #include -#include +#include using T = double; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with inequality constraints" + "ProxQP: :dense: sparse random strongly convex qp with inequality constraints" "and empty equality constraints") { - std::cout << "---testing sparse random strongly convex qp with inequality " - "constraints " - "and empty equality constraints---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with inequality " + "constraints " + "and empty equality constraints---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(0); - dense::isize n_in(dim / 4); + isize n_eq(0); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -83,7 +84,7 @@ DOCTEST_TEST_CASE( qp_random.A = Eigen::MatrixXd(); qp_random.b = Eigen::VectorXd(); - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; @@ -129,7 +130,7 @@ DOCTEST_TEST_CASE( << qp.results.info.solve_time << std::endl; // Testing with nullopt - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; @@ -161,24 +162,25 @@ DOCTEST_TEST_CASE( << qp.results.info.solve_time << std::endl; } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update H") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update H---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update H---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -256,7 +258,7 @@ DOCTEST_TEST_CASE( << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -292,25 +294,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update A") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update A---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update A---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -353,7 +356,7 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating A" << std::endl; - qp_random.A = utils::rand::sparse_matrix_rand_not_compressed( + qp_random.A = common::utils::rand::sparse_matrix_rand_not_compressed( n_eq, dim, sparsity_factor); qp.update(nullopt, nullopt, qp_random.A, nullopt, nullopt, nullopt, nullopt); @@ -389,7 +392,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -425,25 +428,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update C") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update C---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update C---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -486,7 +490,7 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating C" << std::endl; - qp_random.C = utils::rand::sparse_matrix_rand_not_compressed( + qp_random.C = common::utils::rand::sparse_matrix_rand_not_compressed( n_in, dim, sparsity_factor); qp.update(nullopt, nullopt, nullopt, nullopt, qp_random.C, nullopt, nullopt); @@ -522,7 +526,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -558,25 +562,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update b") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update b---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update b---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -619,7 +624,7 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating b" << std::endl; - auto x_sol = utils::rand::vector_rand(dim); + auto x_sol = common::utils::rand::vector_rand(dim); qp_random.b = qp_random.A * x_sol; qp.update(nullopt, nullopt, nullopt, qp_random.b, nullopt, nullopt, nullopt); @@ -655,7 +660,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -691,23 +696,24 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update u") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update u---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update u---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -750,10 +756,10 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating b" << std::endl; - auto x_sol = utils::rand::vector_rand(dim); - auto delta = utils::Vec(n_in); + auto x_sol = common::utils::rand::vector_rand(dim); + auto delta = common::utils::Vec(n_in); for (isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; @@ -791,7 +797,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -827,23 +833,24 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update g") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update g---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update g---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -886,7 +893,7 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating g" << std::endl; - auto g = utils::rand::vector_rand(dim); + auto g = common::utils::rand::vector_rand(dim); qp_random.g = g; qp.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); @@ -923,7 +930,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -959,24 +966,24 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and inequality " + "ProxQP: sparse random strongly convex qp with equality and inequality " "constraints: test update H and A and b and u and l") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test update H and A and b and u and l---" << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -1019,14 +1026,15 @@ DOCTEST_TEST_CASE( std::cout << "l : " << qp_random.l << std::endl; std::cout << "testing updating b" << std::endl; - qp_random.H = utils::rand::sparse_positive_definite_rand_not_compressed( - dim, strong_convexity_factor, sparsity_factor); - qp_random.A = utils::rand::sparse_matrix_rand_not_compressed( + qp_random.H = + common::utils::rand::sparse_positive_definite_rand_not_compressed( + dim, strong_convexity_factor, sparsity_factor); + qp_random.A = common::utils::rand::sparse_matrix_rand_not_compressed( n_eq, dim, sparsity_factor); - auto x_sol = utils::rand::vector_rand(dim); - auto delta = utils::Vec(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + auto x_sol = common::utils::rand::vector_rand(dim); + auto delta = common::utils::Vec(n_in); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.b = qp_random.A * x_sol; qp_random.u = qp_random.C * x_sol + delta; @@ -1071,7 +1079,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -1107,23 +1115,24 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update rho") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update rho---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update rho---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -1196,7 +1205,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -1237,23 +1246,24 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update mu_eq and mu_in") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test update mu_eq and mu_in---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test update mu_eq and mu_in---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -1329,7 +1339,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -1370,24 +1380,25 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test warm starting") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test warm starting---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 10; isize n_eq(dim / 4); isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -1420,9 +1431,9 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - auto x_wm = utils::rand::vector_rand(dim); - auto y_wm = utils::rand::vector_rand(n_eq); - auto z_wm = utils::rand::vector_rand(n_in); + auto x_wm = common::utils::rand::vector_rand(dim); + auto y_wm = common::utils::rand::vector_rand(n_eq); + auto z_wm = common::utils::rand::vector_rand(n_in); std::cout << "proposed warm start" << std::endl; std::cout << "x_wm : " << x_wm << std::endl; std::cout << "y_wm : " << y_wm << std::endl; @@ -1452,7 +1463,7 @@ DOCTEST_TEST_CASE( << qp.results.info.solve_time << std::endl; // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -1489,25 +1500,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test dense init") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test dense init---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test dense init---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init( @@ -1537,25 +1549,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test with no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test with no initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test with no initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -1588,7 +1601,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -1623,26 +1636,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test with equality constrained initial guess") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test with equality constrained initial guess---" << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -1676,7 +1689,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -1712,26 +1725,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test with warm start with previous result") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test with warm start with previous result---" << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -1765,7 +1778,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -1783,9 +1796,9 @@ DOCTEST_TEST_CASE( auto z = qp.results.z; // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; - qp2.ruiz.scale_primal_in_place({ from_eigen, x }); - qp2.ruiz.scale_dual_in_place_eq({ from_eigen, y }); - qp2.ruiz.scale_dual_in_place_in({ from_eigen, z }); + qp2.ruiz.scale_primal_in_place({ common::from_eigen, x }); + qp2.ruiz.scale_dual_in_place_eq({ common::from_eigen, y }); + qp2.ruiz.scale_dual_in_place_in({ common::from_eigen, z }); // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; qp2.solve(x, y, z); @@ -1835,25 +1848,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test with cold start option") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test with cold start option---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test with cold start option---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -1887,7 +1901,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -1905,9 +1919,9 @@ DOCTEST_TEST_CASE( auto z = qp.results.z; // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; - qp2.ruiz.scale_primal_in_place({ from_eigen, x }); - qp2.ruiz.scale_dual_in_place_eq({ from_eigen, y }); - qp2.ruiz.scale_dual_in_place_in({ from_eigen, z }); + qp2.ruiz.scale_primal_in_place({ common::from_eigen, x }); + qp2.ruiz.scale_dual_in_place_eq({ common::from_eigen, y }); + qp2.ruiz.scale_dual_in_place_in({ common::from_eigen, z }); // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; qp2.solve(x, y, z); @@ -1957,26 +1971,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test equilibration options at initialization") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test equilibration options at initialization---" << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -2014,7 +2028,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -2052,25 +2066,26 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test equilibration options at update") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test equilibration options at update---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test equilibration options at update---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -2136,7 +2151,7 @@ DOCTEST_TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -2195,22 +2210,22 @@ DOCTEST_TEST_CASE( ///// TESTS ALL INITIAL GUESS OPTIONS FOR MULTIPLE SOLVES AT ONCE TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -2318,23 +2333,23 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with equality " "constrained initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -2443,23 +2458,23 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with equality " "constrained initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = @@ -2573,22 +2588,22 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -2701,23 +2716,23 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with cold start " "initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -2831,22 +2846,22 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with warm start") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -2957,22 +2972,22 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: warm start test from init") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3012,7 +3027,7 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -3049,23 +3064,23 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " /// TESTS WITH UPDATE + INITIAL GUESS OPTIONS -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update and multiple solve at once with " "no initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3106,7 +3121,7 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.dirty << std::endl; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); bool update_preconditioner = true; qp.update(qp_random.H, qp_random.g, @@ -3186,23 +3201,23 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "equality constrained initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3244,7 +3259,7 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.dirty << std::endl; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); bool update_preconditioner = true; qp.update(qp_random.H, qp_random.g, @@ -3325,23 +3340,23 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with equality " "constrained initial guess and then warm start with previous results") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3387,7 +3402,7 @@ TEST_CASE( InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.dirty << std::endl; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); bool update_preconditioner = true; qp.update(qp_random.H, qp_random.g, @@ -3468,22 +3483,22 @@ TEST_CASE( } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3528,7 +3543,7 @@ TEST_CASE( InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.dirty << std::endl; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); bool update_preconditioner = true; qp.update(qp_random.H, qp_random.g, @@ -3607,23 +3622,23 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "cold start initial guess and then cold start option") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3669,7 +3684,7 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.dirty << std::endl; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); bool update_preconditioner = true; qp.update(qp_random.H, qp_random.g, @@ -3748,23 +3763,23 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "warm start") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3845,7 +3860,7 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " y_wm = qp.results.y; z_wm = qp.results.z; qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); // try now with a real update qp.update(qp_random.H, qp_random.g, @@ -3926,21 +3941,21 @@ TEST_CASE("ProxQP::dense: sparse random strongly convex qp with equality and " } TEST_CASE( - "ProxQP::dense: Test initializaton with rho for different initial guess") + "ProxQP: :dense: Test initializaton with rho for different initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -3982,7 +3997,7 @@ TEST_CASE( std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -4018,7 +4033,7 @@ TEST_CASE( std::cout << "setup timing " << qp2.results.info.setup_time << " solve time " << qp2.results.info.solve_time << std::endl; - dense::QP qp3(dim, n_eq, n_in); + proxqp::dense::QP qp3(dim, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.settings.initial_guess = @@ -4054,7 +4069,7 @@ TEST_CASE( std::cout << "setup timing " << qp3.results.info.setup_time << " solve time " << qp3.results.info.solve_time << std::endl; - dense::QP qp4(dim, n_eq, n_in); + proxqp::dense::QP qp4(dim, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.eps_rel = 0; qp4.settings.initial_guess = @@ -4090,7 +4105,7 @@ TEST_CASE( std::cout << "setup timing " << qp4.results.info.setup_time << " solve time " << qp4.results.info.solve_time << std::endl; - dense::QP qp5(dim, n_eq, n_in); + proxqp::dense::QP qp5(dim, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.eps_rel = 0; qp5.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -4126,21 +4141,21 @@ TEST_CASE( << qp5.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: Test g update for different initial guess") +TEST_CASE("ProxQP: :dense: Test g update for different initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -4171,7 +4186,7 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); auto old_g = qp_random.g; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); qp.update(nullopt, qp_random.g, nullopt, nullopt, nullopt, nullopt, nullopt); qp.solve(); pri_res = std::max( @@ -4195,7 +4210,7 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -4242,7 +4257,7 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") std::cout << "setup timing " << qp2.results.info.setup_time << " solve time " << qp2.results.info.solve_time << std::endl; - dense::QP qp3(dim, n_eq, n_in); + proxqp::dense::QP qp3(dim, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.settings.initial_guess = @@ -4289,7 +4304,7 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") std::cout << "setup timing " << qp3.results.info.setup_time << " solve time " << qp3.results.info.solve_time << std::endl; - dense::QP qp4(dim, n_eq, n_in); + proxqp::dense::QP qp4(dim, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.eps_rel = 0; qp4.settings.initial_guess = @@ -4336,7 +4351,7 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") std::cout << "setup timing " << qp4.results.info.setup_time << " solve time " << qp4.results.info.solve_time << std::endl; - dense::QP qp5(dim, n_eq, n_in); + proxqp::dense::QP qp5(dim, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.eps_rel = 0; qp5.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -4383,21 +4398,21 @@ TEST_CASE("ProxQP::dense: Test g update for different initial guess") << qp5.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: Test A update for different initial guess") +TEST_CASE("ProxQP: :dense: Test A update for different initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -4427,7 +4442,7 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") .lpNorm(); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); - auto new_A = utils::rand::sparse_matrix_rand_not_compressed( + auto new_A = common::utils::rand::sparse_matrix_rand_not_compressed( n_eq, dim, sparsity_factor); qp.update(nullopt, nullopt, new_A, nullopt, nullopt, nullopt, nullopt); qp.solve(); @@ -4452,7 +4467,7 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -4499,7 +4514,7 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") std::cout << "setup timing " << qp2.results.info.setup_time << " solve time " << qp2.results.info.solve_time << std::endl; - dense::QP qp3(dim, n_eq, n_in); + proxqp::dense::QP qp3(dim, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.settings.initial_guess = @@ -4546,7 +4561,7 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") std::cout << "setup timing " << qp3.results.info.setup_time << " solve time " << qp3.results.info.solve_time << std::endl; - dense::QP qp4(dim, n_eq, n_in); + proxqp::dense::QP qp4(dim, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.eps_rel = 0; qp4.settings.initial_guess = @@ -4593,7 +4608,7 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") std::cout << "setup timing " << qp4.results.info.setup_time << " solve time " << qp4.results.info.solve_time << std::endl; - dense::QP qp5(dim, n_eq, n_in); + proxqp::dense::QP qp5(dim, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.eps_rel = 0; qp5.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -4640,21 +4655,21 @@ TEST_CASE("ProxQP::dense: Test A update for different initial guess") << qp5.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: Test rho update for different initial guess") +TEST_CASE("ProxQP: :dense: Test rho update for different initial guess") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -4715,7 +4730,7 @@ TEST_CASE("ProxQP::dense: Test rho update for different initial guess") std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -4770,7 +4785,7 @@ TEST_CASE("ProxQP::dense: Test rho update for different initial guess") std::cout << "setup timing " << qp2.results.info.setup_time << " solve time " << qp2.results.info.solve_time << std::endl; - dense::QP qp3(dim, n_eq, n_in); + proxqp::dense::QP qp3(dim, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.settings.initial_guess = @@ -4825,7 +4840,7 @@ TEST_CASE("ProxQP::dense: Test rho update for different initial guess") std::cout << "setup timing " << qp3.results.info.setup_time << " solve time " << qp3.results.info.solve_time << std::endl; - dense::QP qp4(dim, n_eq, n_in); + proxqp::dense::QP qp4(dim, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.eps_rel = 0; qp4.settings.initial_guess = @@ -4880,7 +4895,7 @@ TEST_CASE("ProxQP::dense: Test rho update for different initial guess") std::cout << "setup timing " << qp4.results.info.setup_time << " solve time " << qp4.results.info.solve_time << std::endl; - dense::QP qp5(dim, n_eq, n_in); + proxqp::dense::QP qp5(dim, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.eps_rel = 0; qp5.settings.initial_guess = InitialGuessStatus::WARM_START; @@ -4935,22 +4950,23 @@ TEST_CASE("ProxQP::dense: Test rho update for different initial guess") << qp5.results.info.solve_time << std::endl; } -TEST_CASE("ProxQP::dense: Test g update for different warm start with previous " - "result option") +TEST_CASE( + "ProxQP: :dense: Test g update for different warm start with previous " + "result option") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -5015,7 +5031,7 @@ TEST_CASE("ProxQP::dense: Test g update for different warm start with previous " std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - dense::QP qp2(dim, n_eq, n_in); + proxqp::dense::QP qp2(dim, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.settings.initial_guess = @@ -5050,32 +5066,33 @@ TEST_CASE("ProxQP::dense: Test g update for different warm start with previous " } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using warm start with previous results") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using warm start with previous results---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using warm start with previous results---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -5115,7 +5132,7 @@ DOCTEST_TEST_CASE( compute_preconditioner, 1.e-6); qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) <= 1.E-9); @@ -5135,9 +5152,9 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5171,9 +5188,9 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5220,7 +5237,7 @@ DOCTEST_TEST_CASE( 1.e-6, 1.e-3); qp3.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5241,34 +5258,35 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using cold start with previous results") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using cold start with previous results---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using cold start with previous results---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -5325,11 +5343,11 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5363,11 +5381,11 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object qp3.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5433,34 +5451,35 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using equality constrained initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using equality constrained initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using equality constrained initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -5517,11 +5536,11 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5555,11 +5574,11 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object qp3.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5625,33 +5644,34 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using no initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using no initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object - qp.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -5708,10 +5728,10 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object - qp2.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5745,10 +5765,10 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object - qp3.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5814,32 +5834,33 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using warm start with previous results") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using warm start with previous results---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using warm start with previous results---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -5870,7 +5891,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; for (isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); @@ -5915,9 +5936,9 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5939,7 +5960,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; for (isize iter = 0; iter < 10; ++iter) { // warm start with previous result used, hence if the qp is small and // simple, the parameters should not changed during first solve, and also @@ -5965,9 +5986,9 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.settings.verbose = true; @@ -6050,34 +6071,35 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using cold start with previous results") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using cold start with previous results---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using cold start with previous results---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -6151,11 +6173,11 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -6198,11 +6220,11 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object qp3.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -6278,34 +6300,35 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings after several solves " "using equality constrained initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using equality constrained initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using equality constrained initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -6379,11 +6402,11 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object qp2.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -6426,11 +6449,11 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object qp3.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -6506,33 +6529,34 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::dense: sparse random strongly convex qp with equality and " + "ProxQP: :dense: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using no initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using no initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); T rho(1.e-7); T mu_eq(1.e-4); bool compute_preconditioner = true; - dense::QP qp{ dim, n_eq, n_in }; // creating QP object - qp.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -6606,10 +6630,10 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp2{ dim, n_eq, n_in }; // creating QP object - qp2.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp2{ dim, n_eq, n_in }; // creating QP object + qp2.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -6652,10 +6676,10 @@ DOCTEST_TEST_CASE( } // conter factual check with another QP object starting at the updated model - dense::QP qp3{ dim, n_eq, n_in }; // creating QP object - qp3.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxqp::dense::QP qp3{ dim, n_eq, n_in }; // creating QP object + qp3.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + InitialGuessStatus::NO_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -6730,21 +6754,21 @@ DOCTEST_TEST_CASE( } } -TEST_CASE("ProxQP::dense: init must be called before update") +TEST_CASE("ProxQP: :dense: init must be called before update") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp(dim, n_eq, n_in); + proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; @@ -6775,7 +6799,7 @@ TEST_CASE("ProxQP::dense: init must be called before update") CHECK(pri_res <= eps_abs); qp_random.H *= 2.; - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); qp.update(qp_random.H, qp_random.g, nullopt, @@ -6800,27 +6824,27 @@ TEST_CASE("ProxQP::dense: init must be called before update") CHECK(pri_res <= eps_abs); } // test of the box constraints interface -TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") +TEST_CASE("ProxQP: :dense: check ordering of z when there are box constraints") { - dense::isize n_test(1000); + isize n_test(1000); double sparsity_factor = 1.; T eps_abs = T(1e-9); - dense::isize dim = 15; + isize dim = 15; // mixing ineq and box constraints for (isize i = 0; i < n_test; i++) { - utils::rand::set_seed(i); - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::utils::rand::set_seed(i); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // ineq and boxes Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -6828,8 +6852,8 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } @@ -6856,7 +6880,8 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") // qp_compare.settings.max_iter = 10; // qp_compare.settings.max_iter_in = 10; // qp_compare.settings.verbose = true; - // qp_compare.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + // qp_compare.settings.initial_guess = + // InitialGuessStatus::NO_INITIAL_GUESS; // qp_compare.init(qp_random.H, // qp_random.g, // qp_random.A, @@ -6869,7 +6894,7 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") // std::cout << "=================qp compare end" << std::endl; //////////////// - dense::QP qp(dim, n_eq, n_in, true); + proxqp::dense::QP qp(dim, n_eq, n_in, true); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -6904,18 +6929,18 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") } // idem but without ineq constraints for (isize i = 0; i < n_test; i++) { - utils::rand::set_seed(i); - dense::isize n_eq(dim / 4); - dense::isize n_in(0); + common::utils::rand::set_seed(i); + isize n_eq(dim / 4); + isize n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // ineq and boxes Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -6923,13 +6948,13 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } - dense::QP qp(dim, n_eq, n_in, true); + proxqp::dense::QP qp(dim, n_eq, n_in, true); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -6965,8 +6990,8 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") } // idem but without ineq and without eq constraints for (isize i = 0; i < n_test; i++) { - dense::isize n_eq(0); - dense::isize n_in(0); + isize n_eq(0); + isize n_in(0); T strong_convexity_factor(1.e-2); using Mat = Eigen::Matrix; @@ -6974,14 +6999,14 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") eye.setZero(); eye.diagonal().array() += 1.; - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // ineq and boxes Eigen::Matrix x_sol = - utils::rand::vector_rand(dim); + common::utils::rand::vector_rand(dim); Eigen::Matrix delta(n_in); - for (proxqp::isize i = 0; i < n_in; ++i) { - delta(i) = utils::rand::uniform_rand(); + for (isize i = 0; i < n_in; ++i) { + delta(i) = common::utils::rand::uniform_rand(); } qp_random.u = qp_random.C * x_sol + delta; qp_random.b = qp_random.A * x_sol; @@ -6989,13 +7014,13 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") u_box.setZero(); Eigen::Matrix l_box(dim); l_box.setZero(); - for (proxqp::isize i = 0; i < dim; ++i) { - T shift = utils::rand::uniform_rand(); + for (isize i = 0; i < dim; ++i) { + T shift = common::utils::rand::uniform_rand(); u_box(i) = x_sol(i) + shift; l_box(i) = x_sol(i) - shift; } // make a qp to compare - dense::QP qp_compare(dim, n_eq, dim, false); + proxqp::dense::QP qp_compare(dim, n_eq, dim, false); qp_compare.settings.eps_abs = eps_abs; qp_compare.settings.eps_rel = 0; qp_compare.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -7030,7 +7055,7 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); // ineq and boxes - dense::QP qp(dim, n_eq, n_in, true); + proxqp::dense::QP qp(dim, n_eq, n_in, true); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -7066,19 +7091,19 @@ TEST_CASE("ProxQP::dense: check ordering of z when there are box constraints") CHECK(pri_res <= eps_abs); } } -TEST_CASE("ProxQP::dense: check updates work when there are box constraints") +TEST_CASE("ProxQP: :dense: check updates work when there are box constraints") { double sparsity_factor = 1.; T eps_abs = T(1e-9); - dense::isize dim = 50; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize dim = 50; + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // ineq and boxes - dense::QP qp(dim, n_eq, n_in, true); + proxqp::dense::QP qp(dim, n_eq, n_in, true); Eigen::Matrix u_box(dim); u_box.setZero(); u_box.array() += 1.E2; @@ -7150,19 +7175,19 @@ TEST_CASE("ProxQP::dense: check updates work when there are box constraints") CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); } -TEST_CASE("ProxQP::dense: test primal infeasibility solving") +TEST_CASE("ProxQP: :dense: test primal infeasibility solving") { double sparsity_factor = 0.15; T eps_abs = T(1e-5); - utils::rand::set_seed(1); - dense::isize dim = 20; + common::utils::rand::set_seed(1); + isize dim = 20; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + isize n_eq(dim / 4); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::dense::QP qp(dim, n_eq, n_in); @@ -7185,10 +7210,10 @@ TEST_CASE("ProxQP::dense: test primal infeasibility solving") qp_random.u); qp.solve(); - proxsuite::proxqp::utils::Vec rhs_dim(dim); - proxsuite::proxqp::utils::Vec rhs_n_eq(n_eq); + utils::Vec rhs_dim(dim); + utils::Vec rhs_n_eq(n_eq); rhs_n_eq.setOnes(); - proxsuite::proxqp::utils::Vec rhs_n_in(n_in); + utils::Vec rhs_n_in(n_in); rhs_n_in.setOnes(); rhs_dim.noalias() = qp_random.A.transpose() * rhs_n_eq + qp_random.C.transpose() * rhs_n_in; @@ -7209,19 +7234,19 @@ TEST_CASE("ProxQP::dense: test primal infeasibility solving") } } -TEST_CASE("ProxQP::dense: estimate of minimal eigenvalues using Eigen") +TEST_CASE("ProxQP: :dense: estimate of minimal eigenvalues using Eigen") { double sparsity_factor = 1.; T tol = T(1e-6); - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); T strong_convexity_factor(1.e-2); for (isize i = 0; i < 1; ++i) { // trivial test - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); @@ -7229,8 +7254,11 @@ TEST_CASE("ProxQP::dense: estimate of minimal eigenvalues using Eigen") qp_random.H.diagonal().tail(1).setConstant(-1.); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( - qp_random.H, EigenValueEstimateMethodOption::ExactMethod, 1.E-6, 10000); + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; @@ -7256,18 +7284,22 @@ TEST_CASE("ProxQP::dense: estimate of minimal eigenvalues using Eigen") n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += random_diag.array(); T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( - qp_random.H, EigenValueEstimateMethodOption::ExactMethod, 1.E-6, 10000); + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; @@ -7292,19 +7324,23 @@ TEST_CASE("ProxQP::dense: estimate of minimal eigenvalues using Eigen") n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += 100 * random_diag.array(); - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( - qp_random.H, EigenValueEstimateMethodOption::ExactMethod, 1.E-6, 10000); + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( + qp_random.H, + common::EigenValueEstimateMethodOption::ExactMethod, + 1.E-6, + 10000); proxqp::dense::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; @@ -7329,19 +7365,19 @@ TEST_CASE("ProxQP::dense: estimate of minimal eigenvalues using Eigen") } TEST_CASE( - "ProxQP::dense: test estimate of minimal eigenvalue using manual choice") + "ProxQP: :dense: test estimate of minimal eigenvalue using manual choice") { double sparsity_factor = 1.; T tol = T(1e-6); - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); T strong_convexity_factor(1.e-2); for (isize i = 0; i < 1; ++i) { // trivial test - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); @@ -7372,12 +7408,13 @@ TEST_CASE( n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += random_diag.array(); T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); @@ -7404,14 +7441,15 @@ TEST_CASE( n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += 100 * random_diag.array(); - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); proxqp::dense::QP qp(dim, n_eq, n_in); @@ -7437,19 +7475,19 @@ TEST_CASE( } TEST_CASE( - "ProxQP::dense: test estimate of minimal eigenvalue using power iteration") + "ProxQP: :dense: test estimate of minimal eigenvalue using power iteration") { double sparsity_factor = 1.; T tol = T(1e-3); - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); T strong_convexity_factor(1.e-2); for (isize i = 0; i < 1; ++i) { // trivial test - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); @@ -7457,9 +7495,9 @@ TEST_CASE( qp_random.H.diagonal().tail(1).setConstant(-0.5); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( qp_random.H, - EigenValueEstimateMethodOption::PowerIteration, + common::EigenValueEstimateMethodOption::PowerIteration, 1.E-6, 10000); @@ -7487,19 +7525,20 @@ TEST_CASE( n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += random_diag.array(); T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( qp_random.H, - EigenValueEstimateMethodOption::PowerIteration, + common::EigenValueEstimateMethodOption::PowerIteration, 1.E-6, 10000); @@ -7526,21 +7565,22 @@ TEST_CASE( n_eq = dim; n_in = dim; for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += 100 * random_diag.array(); // add some random values to dense matrix - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); T estimate_minimal_eigen_value = - dense::estimate_minimal_eigen_value_of_symmetric_matrix( + common::dense::estimate_minimal_eigen_value_of_symmetric_matrix( qp_random.H, - EigenValueEstimateMethodOption::PowerIteration, + common::EigenValueEstimateMethodOption::PowerIteration, 1.E-6, 10000); @@ -7566,8 +7606,9 @@ TEST_CASE( } } -DOCTEST_TEST_CASE("check that model.is_valid function for symmetric matrices " - "works for epsilon precision") +DOCTEST_TEST_CASE( + "ProxQP: check that model.is_valid function for symmetric matrices " + "works for epsilon precision") { Eigen::Matrix matrix = Eigen::Matrix::Random(); Eigen::Matrix symmetric_mat = matrix + matrix.transpose(); @@ -7590,20 +7631,20 @@ DOCTEST_TEST_CASE("check that model.is_valid function for symmetric matrices " qp.init(symmetric_mat, nullopt, nullopt, nullopt, nullopt, nullopt, nullopt); } -TEST_CASE("ProxQP::dense: test memory allocation when estimating biggest " +TEST_CASE("ProxQP: :dense: test memory allocation when estimating biggest " "eigenvalue with power iteration") { double sparsity_factor = 1.; - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + isize dim = 2; + isize n_eq(dim); + isize n_in(dim); T strong_convexity_factor(1.e-2); Eigen::Matrix H; Eigen::VectorXd dw(2), rhs(2), err_v(2); // trivial test - ::proxsuite::proxqp::utils::rand::set_seed(1234); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + ::utils::rand::set_seed(1234); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); @@ -7611,32 +7652,27 @@ TEST_CASE("ProxQP::dense: test memory allocation when estimating biggest " qp_random.H.diagonal().tail(1).setConstant(-0.5); H = qp_random.H; PROXSUITE_EIGEN_MALLOC_NOT_ALLOWED(); - dense::power_iteration(H, dw, rhs, err_v, 1.E-6, 10000); + common::dense::power_iteration(H, dw, rhs, err_v, 1.E-6, 10000); PROXSUITE_EIGEN_MALLOC_ALLOWED(); } -TEST_CASE("ProxQP::dense: sparse random strongly convex qp with" +TEST_CASE("ProxQP: :dense: sparse random strongly convex qp with" "inequality constraints: test PrimalLDLT backend mu update") { - std::cout << "---testing sparse random strongly convex qp with" + std::cout << "---ProxQP: testing sparse random strongly convex qp with" "inequality constraints: test PrimalLDLT backend mu update---" << std::endl; double sparsity_factor = 1; - utils::rand::set_seed(1); + common::utils::rand::set_seed(1); isize dim = 3; isize n_eq(0); isize n_in(9); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ - dim, - n_eq, - n_in, - false, - proxsuite::proxqp::HessianType::Dense, - proxsuite::proxqp::DenseBackend::PrimalLDLT + proxqp::dense::QP qp{ + dim, n_eq, n_in, false, HessianType::Dense, DenseBackend::PrimalLDLT }; // creating QP object T eps_abs = T(1e-7); qp.settings.eps_abs = eps_abs; diff --git a/test/src/dense_qp_wrapper.py b/test/src/proxqp/dense_qp_wrapper.py similarity index 95% rename from test/src/dense_qp_wrapper.py rename to test/src/proxqp/dense_qp_wrapper.py index 844224009..ceeee4484 100644 --- a/test/src/dense_qp_wrapper.py +++ b/test/src/proxqp/dense_qp_wrapper.py @@ -95,7 +95,7 @@ def generate_mixed_qp_with_box(n, seed=1): class DenseqpWrapper(unittest.TestCase): # TESTS OF GENERAL METHODS OF THE API def test_case_deterministic_behavior(self): - print("------------------------test the result is deterministic") + print("------------------------ProxQP: test the result is deterministic") n = 100 H, g, A, b, C, u, l = generate_mixed_qp(n) n_eq = A.shape[0] @@ -142,7 +142,7 @@ def test_case_deterministic_behavior(self): def test_case_update_rho(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test update rho" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test update rho" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -189,7 +189,7 @@ def test_case_update_rho(self): def test_case_update_mu(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test update mus" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test update mus" ) n = 10 @@ -239,7 +239,7 @@ def test_case_update_mu(self): def test_case_no_equilibration_at_initialization(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no equilibration at initialization" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no equilibration at initialization" ) n = 10 @@ -288,7 +288,7 @@ def test_case_no_equilibration_at_initialization(self): def test_case_with_equilibration_at_initialization(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with equilibration at initialization" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with equilibration at initialization" ) n = 10 @@ -337,7 +337,7 @@ def test_case_with_equilibration_at_initialization(self): def test_case_no_initial_guess(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" ) n = 10 @@ -386,7 +386,7 @@ def test_case_no_initial_guess(self): def test_case_no_initial_guess_and_update(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" ) n = 10 @@ -474,7 +474,7 @@ def test_case_no_initial_guess_and_update(self): def test_case_warm_starting(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with warm start---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -525,7 +525,7 @@ def test_case_warm_starting(self): def test_case_warm_start_with_previous_result(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with warm start with previous result---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start with previous result---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -650,7 +650,7 @@ def test_case_warm_start_with_previous_result(self): def test_case_cold_start_with_previous_result(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with cold start with previous result---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with cold start with previous result---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -777,7 +777,7 @@ def test_case_cold_start_with_previous_result(self): def test_case_equilibration_option(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option---" ) n = 10 @@ -876,7 +876,7 @@ def test_case_equilibration_option(self): def test_case_equilibration_option_at_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option at update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option at update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1040,7 +1040,7 @@ def test_case_equilibration_option_at_update(self): def test_case_warm_start_with_other_initialization(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test warm start with other initialization---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test warm start with other initialization---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1090,7 +1090,7 @@ def test_case_warm_start_with_other_initialization(self): def test_case_multiple_solve_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1216,7 +1216,7 @@ def test_case_multiple_solve_with_no_initial_guess(self): def test_case_multiple_solve_with_equality_constrained_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1346,7 +1346,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1478,7 +1478,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints def test_case_warm_start_with_previous_result_starting_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1608,7 +1608,7 @@ def test_case_warm_start_with_previous_result_starting_with_no_initial_guess(sel def test_case_cold_start_with_previous_result_starting_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1738,7 +1738,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess(sel def test_case_warm_start_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1868,7 +1868,7 @@ def test_case_warm_start_with_no_initial_guess(self): def test_case_warm_start_with_no_initial_guess_and_different_init(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test solve from warm start and no initial guess with other initialization---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test solve from warm start and no initial guess with other initialization---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1951,7 +1951,7 @@ def test_case_warm_start_with_no_initial_guess_and_different_init(self): def test_case_multiple_solve_with_no_initial_guess_and_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2092,7 +2092,7 @@ def test_case_multiple_solve_with_equality_constrained_initial_guess_and_update( self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2235,7 +2235,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2382,7 +2382,7 @@ def test_case_warm_start_with_previous_result_starting_with_no_initial_guess_and self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2527,7 +2527,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess_and self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2659,7 +2659,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess_and def test_case_warm_start_with_no_initial_guess_and_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2800,7 +2800,7 @@ def test_case_warm_start_with_no_initial_guess_and_update(self): def test_case_initialization_with_rho_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test initializaton with rho for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test initializaton with rho for different initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -3019,7 +3019,7 @@ def test_case_initialization_with_rho_for_different_initial_guess(self): def test_case_update_g_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update g for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update g for different initial guess---" ) n = 10 H, g_old, A, b, C, u, l = generate_mixed_qp(n) @@ -3319,7 +3319,7 @@ def test_case_update_g_for_different_initial_guess(self): def test_case_update_A_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update A for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update A for different initial guess---" ) n = 10 H, g, A_old, b_old, C, u, l = generate_mixed_qp(n) @@ -3619,7 +3619,7 @@ def test_case_update_A_for_different_initial_guess(self): def test_case_update_rho_update_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update rho for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update rho for different initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -3918,7 +3918,7 @@ def test_case_update_rho_update_for_different_initial_guess(self): def test_sparse_problem_with_exact_solution_known(self): print( - "------------------------sparse random strongly convex qp with inequality constraints and exact solution known" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints and exact solution known" ) n = 150 @@ -3961,7 +3961,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_no_initial_gue self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, no initial guess, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, no initial guess, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4032,7 +4032,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_EQUALITY_CONST self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4105,7 +4105,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_COLD_START_WIT self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4178,7 +4178,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_WARM_START_WIT self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4251,7 +4251,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_WARM_START_W self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4324,7 +4324,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_COLD_START_W self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4397,7 +4397,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_EQUALITY_CON self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4470,7 +4470,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_NO_INITIAL_G self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, NO_INITIAL_GUESS, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, NO_INITIAL_GUESS, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4586,8 +4586,8 @@ def test_z_ordering_with_box_constraints_interface(self): # if infeasibility is detected, we relax the tolerance and solve again if ( - qp.results.info.status == proxsuite.proxqp.PROXQP_DUAL_INFEASIBLE - or qp.results.info.status == proxsuite.proxqp.PROXQP_PRIMAL_INFEASIBLE + qp.results.info.status == proxsuite.proxqp.QPSOLVER_DUAL_INFEASIBLE + or qp.results.info.status == proxsuite.proxqp.QPSOLVER_PRIMAL_INFEASIBLE ): print(f"[{i}] {qp.results.info.status=}, solve again.") qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in, True) @@ -4597,7 +4597,7 @@ def test_z_ordering_with_box_constraints_interface(self): qp.settings.eps_dual_inf = 1e-12 qp.solve() - assert qp.results.info.status == proxsuite.proxqp.PROXQP_SOLVED + assert qp.results.info.status == proxsuite.proxqp.QPSOLVER_SOLVED dua_res = normInf( H @ qp.results.x @@ -4635,8 +4635,8 @@ def test_z_ordering_with_box_constraints_interface(self): # if infeasibility is detected, we relax the tolerance and solve again if ( - qp.results.info.status == proxsuite.proxqp.PROXQP_DUAL_INFEASIBLE - or qp.results.info.status == proxsuite.proxqp.PROXQP_PRIMAL_INFEASIBLE + qp.results.info.status == proxsuite.proxqp.QPSOLVER_DUAL_INFEASIBLE + or qp.results.info.status == proxsuite.proxqp.QPSOLVER_PRIMAL_INFEASIBLE ): print(f"[{i}] {qp.results.info.status=}, solve again.") qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in, True) @@ -4646,7 +4646,7 @@ def test_z_ordering_with_box_constraints_interface(self): qp.settings.eps_dual_inf = 1e-12 qp.solve() - assert qp.results.info.status == proxsuite.proxqp.PROXQP_SOLVED + assert qp.results.info.status == proxsuite.proxqp.QPSOLVER_SOLVED dua_res = normInf( H @ qp.results.x diff --git a/test/src/dense_ruiz_equilibration.cpp b/test/src/proxqp/dense_ruiz_equilibration.cpp similarity index 82% rename from test/src/dense_ruiz_equilibration.cpp rename to test/src/proxqp/dense_ruiz_equilibration.cpp index d1bff6479..4641028f7 100644 --- a/test/src/dense_ruiz_equilibration.cpp +++ b/test/src/proxqp/dense_ruiz_equilibration.cpp @@ -7,33 +7,33 @@ #include #include #include -#include +#include using namespace proxsuite; using Scalar = double; -DOCTEST_TEST_CASE("ruiz preconditioner") +DOCTEST_TEST_CASE("ProxQP: ruiz preconditioner") { int dim = 5; int n_eq = 6; int n_in = 0; - auto sym = proxqp::Symmetry::general; // 0 : upper triangular (by default), + auto sym = common::Symmetry::general; // 0 : upper triangular (by default), // 1: - // auto sym = proxqp::Symmetry::lower; // 0 : upper triangular (by default), + // auto sym = common::Symmetry::lower; // 0 : upper triangular (by default), // 1: lower triangular ; else full matrix Scalar sparsity_factor(0.75); Scalar strong_convexity_factor(0.01); - proxqp::dense::Model qp_random = - proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = + common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); switch (sym) { - case proxqp::Symmetry::upper: { + case common::Symmetry::upper: { qp_random.H = qp_random.H.triangularView(); break; } - case proxqp::Symmetry::lower: { + case common::Symmetry::lower: { qp_random.H = qp_random.H.triangularView(); break; } diff --git a/test/src/dense_unconstrained_qp.cpp b/test/src/proxqp/dense_unconstrained_qp.cpp similarity index 87% rename from test/src/dense_unconstrained_qp.cpp rename to test/src/proxqp/dense_unconstrained_qp.cpp index eed71aecc..59ea6dcf1 100644 --- a/test/src/dense_unconstrained_qp.cpp +++ b/test/src/proxqp/dense_unconstrained_qp.cpp @@ -7,18 +7,19 @@ #include #include #include -#include -using namespace proxsuite; +#include using T = double; +using namespace proxsuite; -DOCTEST_TEST_CASE( - "sparse random strongly convex unconstrained qp and increasing dimension") +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex unconstrained qp and " + "increasing dimension") { - std::cout << "---testing sparse random strongly convex qp with increasing " - "dimension---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with increasing " + "dimension---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); for (int dim = 10; dim < 1000; dim += 100) { @@ -26,7 +27,7 @@ DOCTEST_TEST_CASE( int n_eq(0); int n_in(0); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_unconstrained_qp( + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( dim, sparsity_factor, strong_convexity_factor); proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.settings.eps_abs = eps_abs; @@ -60,13 +61,15 @@ DOCTEST_TEST_CASE( } } -DOCTEST_TEST_CASE("sparse random not strongly convex unconstrained qp and " - "increasing dimension") +DOCTEST_TEST_CASE( + "ProxQP: sparse random not strongly convex unconstrained qp and " + "increasing dimension") { - std::cout << "---testing sparse random not strongly convex unconstrained qp " - "with increasing dimension---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random not strongly convex unconstrained qp " + "with increasing dimension---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); for (int dim = 10; dim < 1000; dim += 100) { @@ -74,9 +77,9 @@ DOCTEST_TEST_CASE("sparse random not strongly convex unconstrained qp and " int n_eq(0); int n_in(0); T strong_convexity_factor(0); - proxqp::dense::Model qp_random = proxqp::utils::dense_unconstrained_qp( + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( dim, sparsity_factor, strong_convexity_factor); - auto x_sol = proxqp::utils::rand::vector_rand(dim); + auto x_sol = common::utils::rand::vector_rand(dim); qp_random.g = -qp_random.H * x_sol; // to be dually feasible g must be in the image space of H @@ -113,10 +116,11 @@ DOCTEST_TEST_CASE("sparse random not strongly convex unconstrained qp and " } } -DOCTEST_TEST_CASE("unconstrained qp with H = Id and g random") +DOCTEST_TEST_CASE("ProxQP: unconstrained qp with H = Id and g random") { - std::cout << "---unconstrained qp with H = Id and g random---" << std::endl; + std::cout << "---ProxQP: unconstrained qp with H = Id and g random---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); @@ -124,7 +128,7 @@ DOCTEST_TEST_CASE("unconstrained qp with H = Id and g random") int n_eq(0); int n_in(0); T strong_convexity_factor(1.E-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_unconstrained_qp( + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( dim, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); qp_random.H.diagonal().array() += 1; @@ -171,7 +175,7 @@ DOCTEST_TEST_CASE("unconstrained qp with H = Id and g = 0") int n_eq(0); int n_in(0); T strong_convexity_factor(1.E-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_unconstrained_qp( + common::dense::Model qp_random = common::utils::dense_unconstrained_qp( dim, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); qp_random.H.diagonal().array() += 1; diff --git a/test/src/parallel_qp_solve.cpp b/test/src/proxqp/parallel_qp_solve.cpp similarity index 83% rename from test/src/parallel_qp_solve.cpp rename to test/src/proxqp/parallel_qp_solve.cpp index b60d25f53..bd41e130d 100644 --- a/test/src/parallel_qp_solve.cpp +++ b/test/src/proxqp/parallel_qp_solve.cpp @@ -6,17 +6,16 @@ #include #include #include -#include +#include #include using namespace proxsuite; -using namespace proxsuite::proxqp; -using namespace proxsuite::proxqp::utils; +using namespace proxsuite::common; using T = double; -using I = c_int; +using I = utils::c_int; using namespace proxsuite::linalg::sparse::tags; -DOCTEST_TEST_CASE("test parallel qp_solve for dense qps") +DOCTEST_TEST_CASE("ProxQP: test parallel qp_solve for dense qps") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); @@ -33,10 +32,10 @@ DOCTEST_TEST_CASE("test parallel qp_solve for dense qps") // Generate two lists with identical QPs for (int i = 0; i < num_qps; i++) { utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; + proxqp::dense::QP qp{ dim, n_eq, n_in }; qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0.0; qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -49,7 +48,7 @@ DOCTEST_TEST_CASE("test parallel qp_solve for dense qps") qp_random.u); qps.push_back(qp); - dense::QP qp_compare{ dim, n_eq, n_in }; + proxqp::dense::QP qp_compare{ dim, n_eq, n_in }; qp_compare.settings.eps_abs = eps_abs; qp_compare.settings.eps_rel = 0.0; qp_compare.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; @@ -76,7 +75,7 @@ DOCTEST_TEST_CASE("test parallel qp_solve for dense qps") } } -DOCTEST_TEST_CASE("test dense BatchQP and optional NUM_THREADS") +DOCTEST_TEST_CASE("ProxQP: test dense BatchQP and optional NUM_THREADS") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); @@ -88,12 +87,12 @@ DOCTEST_TEST_CASE("test dense BatchQP and optional NUM_THREADS") T strong_convexity_factor(1.e-2); int num_qps = 64; std::vector> qps_compare; - dense::BatchQP qps_vector = dense::BatchQP(num_qps); + proxqp::dense::BatchQP qps_vector = proxqp::dense::BatchQP(num_qps); for (int i = 0; i < num_qps; i++) { auto& qp = qps_vector.init_qp_in_place(dim, n_eq, n_in); utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0.0; @@ -132,24 +131,24 @@ DOCTEST_TEST_CASE("test dense BatchQP and optional NUM_THREADS") } } -DOCTEST_TEST_CASE("test parallel qp_solve for sparse qps") +DOCTEST_TEST_CASE("ProxQP: test parallel qp_solve for sparse qps") { - sparse::isize dim = 500; - sparse::isize n_eq(10); - sparse::isize n_in(10); + proxqp::sparse::isize dim = 500; + proxqp::sparse::isize n_eq(10); + proxqp::sparse::isize n_in(10); T eps_abs = T(1e-9); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; int num_qps = 64; - std::vector> qps; - std::vector> qps_compare; + std::vector> qps; + std::vector> qps_compare; // Generate two lists with identical QPs for (int i = 0; i < num_qps; i++) { utils::rand::set_seed(i); - sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( + proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qps.emplace_back(dim, n_eq, n_in); @@ -193,26 +192,27 @@ DOCTEST_TEST_CASE("test parallel qp_solve for sparse qps") } } -DOCTEST_TEST_CASE("test sparse BatchQP") +DOCTEST_TEST_CASE("ProxQP: test sparse BatchQP") { - sparse::isize dim = 500; - sparse::isize n_eq(10); - sparse::isize n_in(10); + proxqp::sparse::isize dim = 500; + proxqp::sparse::isize n_eq(10); + proxqp::sparse::isize n_in(10); T eps_abs = T(1e-9); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; int num_qps = 64; - std::vector> qps_compare; + std::vector> qps_compare; - sparse::BatchQP qps_vector = sparse::BatchQP(num_qps); + proxqp::sparse::BatchQP qps_vector = + proxqp::sparse::BatchQP(num_qps); // qps_vector.init_qp_in_place(dim, n_eq, n_in); // Generate two lists with identical QPs for (int i = 0; i < num_qps; i++) { auto& qp = qps_vector.init_qp_in_place(dim, n_eq, n_in); utils::rand::set_seed(i); - sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( + proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0.0; diff --git a/test/src/parallel_qp_solve.py b/test/src/proxqp/parallel_qp_solve.py similarity index 96% rename from test/src/parallel_qp_solve.py rename to test/src/proxqp/parallel_qp_solve.py index 5a6f39a41..b2428d430 100644 --- a/test/src/parallel_qp_solve.py +++ b/test/src/proxqp/parallel_qp_solve.py @@ -56,6 +56,7 @@ class ParallelWrapper(unittest.TestCase): # TESTS OF GENERAL METHODS OF THE API def test_dense_parallel(self): + print("------------------------ ProxQP: dense_parallel") n = 10 # dimension batch_size = 4 qps = [] @@ -106,6 +107,7 @@ def test_dense_parallel(self): assert np.allclose(qps[i].results.x, qps_compare[i].results.x, rtol=1e-8) def test_dense_parallel_custom_BatchQP(self): + print("------------------------ ProxQP: dense_parallel_custom_BatchQP") n = 10 # dimension batch_size = 4 qps = [] @@ -155,6 +157,7 @@ def test_dense_parallel_custom_BatchQP(self): assert np.allclose(qps[i].results.x, qp_vector.get(i).results.x, rtol=1e-8) def test_sparse_parallel_custom_BatchQP(self): + print("------------------------ ProxQP: sparse_parallel_custom_BatchQP") n = 10 # dimension batch_size = 4 qps = [] diff --git a/test/src/serialization.cpp b/test/src/proxqp/serialization.cpp similarity index 78% rename from test/src/serialization.cpp rename to test/src/proxqp/serialization.cpp index d9fe67693..8c16c1978 100644 --- a/test/src/serialization.cpp +++ b/test/src/proxqp/serialization.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -17,9 +17,9 @@ template struct init; template -struct init> +struct init> { - typedef proxsuite::proxqp::dense::Model Model; + typedef proxsuite::common::dense::Model Model; static Model run() { @@ -29,9 +29,9 @@ struct init> }; template -struct init> +struct init> { - typedef proxsuite::proxqp::Results Results; + typedef proxsuite::common::Results Results; static Results run() { @@ -41,9 +41,9 @@ struct init> }; template -struct init> +struct init> { - typedef proxsuite::proxqp::Settings Settings; + typedef proxsuite::common::Settings Settings; static Settings run() { @@ -108,22 +108,23 @@ generic_test(const T& object, const std::string& filename) using T = double; using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; -DOCTEST_TEST_CASE("test serialization of qp model, results and settings") +DOCTEST_TEST_CASE( + "ProxQP: test serialization of qp model, results and settings") { - std::cout << "--- serialization ---" << std::endl; + std::cout << "--- ProxQP: serialization ---" << std::endl; double sparsity_factor = 0.15; - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + isize dim = 10; - dense::isize n_eq(0); - dense::isize n_in(dim / 4); + isize n_eq(0); + isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::QP qp{ dim, n_eq, n_in }; // creating QP object + proxqp::dense::QP qp{ dim, n_eq, n_in }; // creating QP object qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -141,7 +142,7 @@ DOCTEST_TEST_CASE("test serialization of qp model, results and settings") } DOCTEST_TEST_CASE( - "test serialization of eigen matrices with different storage orders") + "ProxQP: test serialization of eigen matrices with different storage orders") { Eigen::Matrix row_matrix; Eigen::Matrix row_matrix_loaded; diff --git a/test/src/serialization.py b/test/src/proxqp/serialization.py similarity index 97% rename from test/src/serialization.py rename to test/src/proxqp/serialization.py index 4c03725e9..45d910fc8 100644 --- a/test/src/serialization.py +++ b/test/src/proxqp/serialization.py @@ -64,7 +64,7 @@ def generic_test(object, filename): class DenseqpWrapperSerialization(unittest.TestCase): def test_pickle(self): - print("------------------------test pickle") + print("------------------------ProxQP: test pickle") n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) n_eq = A.shape[0] diff --git a/test/src/sparse_factorization.cpp b/test/src/proxqp/sparse_factorization.cpp similarity index 99% rename from test/src/sparse_factorization.cpp rename to test/src/proxqp/sparse_factorization.cpp index d60e00fba..480612ee1 100644 --- a/test/src/sparse_factorization.cpp +++ b/test/src/proxqp/sparse_factorization.cpp @@ -106,7 +106,7 @@ ldlt_with_perm(proxsuite::linalg::veg::Slice perm_inv, using namespace proxsuite::linalg::sparse; using namespace proxsuite::linalg::veg; -TEST_CASE("ldlt: factorize compressed") +TEST_CASE("ProxQP: ldlt: factorize compressed") { using I = int; using T = long double; @@ -301,7 +301,7 @@ TEST_CASE("ldlt: factorize compressed") } } -TEST_CASE("ldlt: factorize uncompressed, rank update") +TEST_CASE("ProxQP: ldlt: factorize uncompressed, rank update") { using I = isize; using T = double; @@ -469,7 +469,7 @@ TEST_CASE("ldlt: factorize uncompressed, rank update") .norm() < T(1e-10)); } -TEST_CASE("ldlt: row mod") +TEST_CASE("ProxQP: ldlt: row mod") { using I = isize; using T = double; diff --git a/test/src/sparse_maros_meszaros.cpp b/test/src/proxqp/sparse_maros_meszaros.cpp similarity index 86% rename from test/src/sparse_maros_meszaros.cpp rename to test/src/proxqp/sparse_maros_meszaros.cpp index 732583eee..06eb8baf8 100644 --- a/test/src/sparse_maros_meszaros.cpp +++ b/test/src/proxqp/sparse_maros_meszaros.cpp @@ -8,30 +8,30 @@ #include using namespace proxsuite; +using namespace proxsuite::common; #define MAROS_MESZAROS_DIR PROBLEM_PATH "/data/maros_meszaros_data/" template void compute_primal_dual_feasibility(const PreprocessedQpSparse& preprocessed, - const proxqp::Results& results, + const common::Results& results, T& primal_feasibility, T& dual_feasibility) { - dual_feasibility = proxsuite::proxqp::dense::infty_norm( + dual_feasibility = dense::infty_norm( preprocessed.H.selfadjointView() * results.x + preprocessed.g + preprocessed.AT * results.y + preprocessed.CT * results.z); - T prim_eq = proxsuite::proxqp::dense::infty_norm( - preprocessed.AT.transpose() * results.x - preprocessed.b); - T prim_in = - std::max(proxsuite::proxqp::dense::infty_norm( - preprocessed.AT.transpose() * results.x - preprocessed.b), - proxsuite::proxqp::dense::infty_norm( - helpers::positive_part(preprocessed.CT.transpose() * results.x - - preprocessed.u) + - helpers::negative_part(preprocessed.CT.transpose() * results.x - - preprocessed.l))); + T prim_eq = + dense::infty_norm(preprocessed.AT.transpose() * results.x - preprocessed.b); + T prim_in = std::max( + dense::infty_norm(preprocessed.AT.transpose() * results.x - preprocessed.b), + dense::infty_norm( + helpers::positive_part(preprocessed.CT.transpose() * results.x - + preprocessed.u) + + helpers::negative_part(preprocessed.CT.transpose() * results.x - + preprocessed.l))); primal_feasibility = std::max(prim_eq, prim_in); } @@ -107,7 +107,7 @@ char const* files[] = { MAROS_MESZAROS_DIR "YAO.mat", MAROS_MESZAROS_DIR "ZECEVIC2.mat", }; -TEST_CASE("sparse maros meszaros using the API") +TEST_CASE("ProxQP: sparse maros meszaros using the API") { using isize = proxsuite::proxqp::sparse::isize; using T = double; @@ -153,8 +153,8 @@ TEST_CASE("sparse maros meszaros using the API") for (isize iter = 0; iter < 2; ++iter) { if (iter > 0) - qp.settings.initial_guess = proxsuite::proxqp::InitialGuessStatus:: - WARM_START_WITH_PREVIOUS_RESULT; + qp.settings.initial_guess = + InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp.solve(); T primal_feasibility, dual_feasibility; @@ -173,12 +173,12 @@ TEST_CASE("sparse maros meszaros using the API") { qp.solve(); - CHECK(proxsuite::proxqp::dense::infty_norm( - H.selfadjointView() * qp.results.x + g + - AT * qp.results.y + CT * qp.results.z) <= - 2 * eps_abs_no_duality_gap); - CHECK(proxsuite::proxqp::dense::infty_norm( - AT.transpose() * qp.results.x - b) <= eps_abs_no_duality_gap); + CHECK( + dense::infty_norm(H.selfadjointView() * qp.results.x + + g + AT * qp.results.y + CT * qp.results.z) <= + 2 * eps_abs_no_duality_gap); + CHECK(dense::infty_norm(AT.transpose() * qp.results.x - b) <= + eps_abs_no_duality_gap); if (n_in > 0) { CHECK((CT.transpose() * qp.results.x - l).minCoeff() > -eps_abs_no_duality_gap); @@ -196,8 +196,7 @@ TEST_CASE("sparse maros meszaros using the API") } { - qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; qp.settings.check_duality_gap = true; qp.settings.eps_abs = eps_abs_with_duality_gap; qp.settings.eps_duality_gap_abs = eps_abs_with_duality_gap; diff --git a/test/src/sparse_qp.cpp b/test/src/proxqp/sparse_qp.cpp similarity index 75% rename from test/src/sparse_qp.cpp rename to test/src/proxqp/sparse_qp.cpp index 6aec4a872..21d46e2fb 100644 --- a/test/src/sparse_qp.cpp +++ b/test/src/proxqp/sparse_qp.cpp @@ -3,13 +3,13 @@ // #include #include -#include +#include #include #include using namespace proxsuite; using T = double; -using I = proxqp::utils::c_int; +using I = common::utils::c_int; using namespace linalg::sparse::tags; /* TEST_CASE("random ruiz") { @@ -26,12 +26,12 @@ TEST_CASE("random ruiz") { double p = 1.0; - auto H = proxqp::utils::rand::sparse_positive_definite_rand(n, -T(10.0), p); auto g = proxqp::utils::rand::vector_rand(n); auto AT = -proxqp::utils::rand::sparse_matrix_rand(n, n_eq, p); auto b = -proxqp::utils::rand::vector_rand(n_eq); auto CT = -proxqp::utils::rand::sparse_matrix_rand(n, n_in, p); auto l = -proxqp::utils::rand::vector_rand(n_in); auto u = (l.array() + + auto H = common::utils::rand::sparse_positive_definite_rand(n, +T(10.0), p); auto g = common::utils::rand::vector_rand(n); auto AT = +common::utils::rand::sparse_matrix_rand(n, n_eq, p); auto b = +common::utils::rand::vector_rand(n_eq); auto CT = +common::utils::rand::sparse_matrix_rand(n, n_in, p); auto l = +common::utils::rand::vector_rand(n_in); auto u = (l.array() + 1).matrix().eval(); { @@ -66,10 +66,10 @@ ruiz{ n, n_eq + n_in, 1e-3, 10, proxqp::sparse::preconditioner::Symmetry::UPPER, sparse::qp_solve(results, data, settings, work, ruiz); CHECK( - proxqp::dense::infty_norm( + common::dense::infty_norm( H.selfadjointView() * x + g + AT * y + CT * z) <= 1e-9); - CHECK(proxqp::dense::infty_norm(AT.transpose() * x - b) + CHECK(common::dense::infty_norm(AT.transpose() * x - b) <= 1e-9); if (n_in > 0) { CHECK((CT.transpose() * x - l).minCoeff() > -1e-9); CHECK((CT.transpose() * x - u).maxCoeff() < 1e-9); @@ -92,12 +92,12 @@ TEST_CASE("random ruiz using the API") { double p = 1.0; - auto H = proxqp::utils::rand::sparse_positive_definite_rand(n, -T(10.0), p); auto g = proxqp::utils::rand::vector_rand(n); auto A = -proxqp::utils::rand::sparse_matrix_rand(n_eq,n, p); auto b = -proxqp::utils::rand::vector_rand(n_eq); auto C = -proxqp::utils::rand::sparse_matrix_rand(n_in,n, p); auto l = -proxqp::utils::rand::vector_rand(n_in); auto u = (l.array() + + auto H = common::utils::rand::sparse_positive_definite_rand(n, +T(10.0), p); auto g = common::utils::rand::vector_rand(n); auto A = +common::utils::rand::sparse_matrix_rand(n_eq,n, p); auto b = +common::utils::rand::vector_rand(n_eq); auto C = +common::utils::rand::sparse_matrix_rand(n_in,n, p); auto l = +common::utils::rand::vector_rand(n_in); auto u = (l.array() + 1).matrix().eval(); { @@ -107,10 +107,10 @@ proxqp::utils::rand::vector_rand(n_in); auto u = (l.array() + qp.setup_sparse_matrices(H,g,A,b,C,u,l); qp.solve(); CHECK( - proxqp::dense::infty_norm( + common::dense::infty_norm( H.selfadjointView() * qp.results.x + g + A.transpose() * qp.results.y + C.transpose() * -qp.results.z) <= 1e-9); CHECK(proxqp::dense::infty_norm(A * qp.results.x - b) <= +qp.results.z) <= 1e-9); CHECK(common::dense::infty_norm(A * qp.results.x - b) <= 1e-9); if (n_in > 0) { CHECK((C * qp.results.x - l).minCoeff() > -1e-9); CHECK((C * qp.results.x - u).maxCoeff() < 1e-9); } @@ -132,12 +132,12 @@ TEST_CASE("random id") { double p = 1.0; - auto H = proxqp::utils::rand::sparse_positive_definite_rand(n, -T(10.0), p); auto g = proxqp::utils::rand::vector_rand(n); auto AT = -proxqp::utils::rand::sparse_matrix_rand(n, n_eq, p); auto b = -proxqp::utils::rand::vector_rand(n_eq); auto CT = -proxqp::utils::rand::sparse_matrix_rand(n, n_in, p); auto l = -proxqp::utils::rand::vector_rand(n_in); auto u = (l.array() + + auto H = common::utils::rand::sparse_positive_definite_rand(n, +T(10.0), p); auto g = common::utils::rand::vector_rand(n); auto AT = +common::utils::rand::sparse_matrix_rand(n, n_eq, p); auto b = +common::utils::rand::vector_rand(n_eq); auto CT = +common::utils::rand::sparse_matrix_rand(n, n_in, p); auto l = +common::utils::rand::vector_rand(n_in); auto u = (l.array() + 1).matrix().eval(); { @@ -170,10 +170,10 @@ u}, sparse::qp_solve(results, data, settings, work, id); CHECK( - proxqp::dense::infty_norm( + common::dense::infty_norm( H.selfadjointView() * x + g + AT * y + CT * z) <= 1e-9); - CHECK(proxqp::dense::infty_norm(AT.transpose() * x - b) + CHECK(common::dense::infty_norm(AT.transpose() * x - b) <= 1e-9); if (n_in > 0) { CHECK((CT.transpose() * x - l).minCoeff() > -1e-9); CHECK((CT.transpose() * x - u).maxCoeff() < 1e-9); @@ -182,7 +182,7 @@ u}, } } */ -TEST_CASE("random id using the API") +TEST_CASE("ProxQP: random id using the API") { for (auto const& dims : { @@ -196,12 +196,12 @@ TEST_CASE("random id using the API") double p = 1.0; - auto H = proxqp::utils::rand::sparse_positive_definite_rand(n, T(10.0), p); - auto g = proxqp::utils::rand::vector_rand(n); - auto A = proxqp::utils::rand::sparse_matrix_rand(n_eq, n, p); - auto b = proxqp::utils::rand::vector_rand(n_eq); - auto C = proxqp::utils::rand::sparse_matrix_rand(n_in, n, p); - auto l = proxqp::utils::rand::vector_rand(n_in); + auto H = common::utils::rand::sparse_positive_definite_rand(n, T(10.0), p); + auto g = common::utils::rand::vector_rand(n); + auto A = common::utils::rand::sparse_matrix_rand(n_eq, n, p); + auto b = common::utils::rand::vector_rand(n_eq); + auto C = common::utils::rand::sparse_matrix_rand(n_in, n, p); + auto l = common::utils::rand::vector_rand(n_in); auto u = (l.array() + 1).matrix().eval(); { @@ -211,11 +211,11 @@ TEST_CASE("random id using the API") qp.init(H, g, A, b, C, l, u); qp.solve(); - CHECK(proxqp::dense::infty_norm( + CHECK(common::dense::infty_norm( H.selfadjointView() * qp.results.x + g + A.transpose() * qp.results.y + C.transpose() * qp.results.z) <= 1e-9); - CHECK(proxqp::dense::infty_norm(A * qp.results.x - b) <= 1e-9); + CHECK(common::dense::infty_norm(A * qp.results.x - b) <= 1e-9); if (n_in > 0) { CHECK((C * qp.results.x - l).minCoeff() > -1e-9); CHECK((C * qp.results.x - u).maxCoeff() < 1e-9); diff --git a/test/src/sparse_qp_solve.cpp b/test/src/proxqp/sparse_qp_solve.cpp similarity index 50% rename from test/src/sparse_qp_solve.cpp rename to test/src/proxqp/sparse_qp_solve.cpp index 112fca202..30dec269d 100644 --- a/test/src/sparse_qp_solve.cpp +++ b/test/src/proxqp/sparse_qp_solve.cpp @@ -4,23 +4,24 @@ #include #include #include -#include +#include #include -using namespace proxsuite; -using namespace proxsuite::proxqp; -using namespace proxsuite::proxqp::utils; using T = double; -using I = c_int; +using namespace proxsuite; +using namespace proxsuite::common; + +using I = common::utils::c_int; using namespace proxsuite::linalg::sparse::tags; -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve function") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve function---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve function---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -32,16 +33,16 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " T eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::utils::rand::set_seed(1); /* - auto H = ::proxsuite::proxqp::utils::rand::sparse_positive_definite_rand( + auto H = ::utils::rand::sparse_positive_definite_rand( n, T(10.0), sparsity_factor); - auto g = ::proxsuite::proxqp::utils::rand::vector_rand(n); - auto A = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_eq, n, + auto g = ::utils::rand::vector_rand(n); + auto A = ::utils::rand::sparse_matrix_rand(n_eq, n, sparsity_factor); auto x_sol = - ::proxsuite::proxqp::utils::rand::vector_rand(n); auto b = A * x_sol; - auto C = ::proxsuite::proxqp::utils::rand::sparse_matrix_rand(n_in, n, + ::utils::rand::vector_rand(n); auto b = A * x_sol; + auto C = ::utils::rand::sparse_matrix_rand(n_in, n, sparsity_factor); auto l = C * x_sol; auto u = (l.array() + 10).matrix().eval(); @@ -59,27 +60,26 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " sparsity_factor, strong_convexity_factor); */ - proxqp::dense::Model qp_dense = utils::dense_strongly_convex_qp( + common::dense::Model qp_dense = common::utils::dense_strongly_convex_qp( n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::SparseModel qp = qp_dense.to_sparse(); - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs); - - T dua_res = proxqp::dense::infty_norm( + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs); + + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -96,13 +96,14 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve with different rho value") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve with different rho value---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different rho value---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -114,29 +115,29 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - nullopt, - T(1.E-7)); + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + nullopt, + T(1.E-7)); DOCTEST_CHECK(results.info.rho == T(1.E-7)); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -154,14 +155,15 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test solve with different mu_eq and mu_in values") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test solve with different mu_eq and " - "mu_in values---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test solve with different mu_eq and " + "mu_in values---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -173,30 +175,30 @@ DOCTEST_TEST_CASE( double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - nullopt, - nullopt, - T(1.E-2), - T(1.E-2)); - T dua_res = proxqp::dense::infty_norm( + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + nullopt, + nullopt, + T(1.E-2), + T(1.E-2)); + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -214,12 +216,12 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test setting specific sparse backend") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test setting specific sparse backend ---" << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -233,45 +235,44 @@ DOCTEST_TEST_CASE( double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - proxsuite::proxqp::InitialGuessStatus initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; - proxsuite::proxqp::SparseBackend sparse_backend = - proxsuite::proxqp::SparseBackend::MatrixFree; - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - nullopt, - nullopt, - nullopt, - nullopt, - nullopt, - true, - true, - nullopt, - initial_guess, - sparse_backend); - T dua_res = proxqp::dense::infty_norm( + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + SparseBackend sparse_backend = SparseBackend::MatrixFree; + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + true, + nullopt, + initial_guess, + sparse_backend); + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); - DOCTEST_CHECK(results.info.sparse_backend == SparseBackend::MatrixFree); + DOCTEST_CHECK(results.info.sparse_backend == + common::SparseBackend::MatrixFree); std::cout << "------using API solving qp with dim: " << n << " neq: " << n_eq << " nin: " << n_in << std::endl; @@ -284,13 +285,14 @@ DOCTEST_TEST_CASE( } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test warm starting") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test warm starting---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test warm starting---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -302,20 +304,20 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - auto x_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n); - auto y_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_eq); - auto z_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_in); - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve( - qp.H, qp.g, qp.A, qp.b, qp.C, qp.l, qp.u, x_wm, y_wm, z_wm, eps_abs); - T dua_res = proxqp::dense::infty_norm( + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + auto x_wm = ::utils::rand::vector_rand(n); + auto y_wm = ::utils::rand::vector_rand(n_eq); + auto z_wm = ::utils::rand::vector_rand(n_in); + Results results = proxsuite::proxqp::sparse::solve( + qp.H, qp.g, qp.A, qp.b, qp.C, qp.l, qp.u, x_wm, y_wm, z_wm, eps_abs); + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -332,13 +334,14 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test verbose = true") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test verbose = true ---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test verbose = true ---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -350,32 +353,32 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); bool verbose = true; - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - nullopt, - nullopt, - nullopt, - nullopt, - verbose); - T dua_res = proxqp::dense::infty_norm( + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + nullopt, + nullopt, + nullopt, + nullopt, + verbose); + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -392,13 +395,14 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " } } -DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " +DOCTEST_TEST_CASE("ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test no initial guess ---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test no initial guess ---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -410,37 +414,36 @@ DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and " double eps_abs = 1.e-9; T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); - proxsuite::proxqp::InitialGuessStatus initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; - proxsuite::proxqp::Results results = - proxsuite::proxqp::sparse::solve(qp.H, - qp.g, - qp.A, - qp.b, - qp.C, - qp.l, - qp.u, - nullopt, - nullopt, - nullopt, - eps_abs, - nullopt, - nullopt, - nullopt, - nullopt, - nullopt, - true, - true, - nullopt, - initial_guess); - T dua_res = proxqp::dense::infty_norm( + ::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + InitialGuessStatus initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + Results results = proxsuite::proxqp::sparse::solve(qp.H, + qp.g, + qp.A, + qp.b, + qp.C, + qp.l, + qp.u, + nullopt, + nullopt, + nullopt, + eps_abs, + nullopt, + nullopt, + nullopt, + nullopt, + nullopt, + true, + true, + nullopt, + initial_guess); + T dua_res = common::dense::infty_norm( qp.H.selfadjointView() * results.x + qp.g + qp.A.transpose() * results.y + qp.C.transpose() * results.z); - T pri_res = std::max(proxqp::dense::infty_norm(qp.A * results.x - qp.b), - proxqp::dense::infty_norm( + T pri_res = std::max(common::dense::infty_norm(qp.A * results.x - qp.b), + common::dense::infty_norm( helpers::positive_part(qp.C * results.x - qp.u) + helpers::negative_part(qp.C * results.x - qp.l))); DOCTEST_CHECK(pri_res <= eps_abs); diff --git a/test/src/sparse_qp_solve.py b/test/src/proxqp/sparse_qp_solve.py similarity index 91% rename from test/src/sparse_qp_solve.py rename to test/src/proxqp/sparse_qp_solve.py index c058a7973..2388e5a99 100644 --- a/test/src/sparse_qp_solve.py +++ b/test/src/proxqp/sparse_qp_solve.py @@ -49,7 +49,7 @@ class SparseQpWrapper(unittest.TestCase): def test_case_basic_solve(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test basic solve" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test basic solve" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -88,7 +88,7 @@ def test_case_basic_solve(self): def test_case_different_rho_value(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test different rho values" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test different rho values" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -129,7 +129,7 @@ def test_case_different_rho_value(self): def test_case_different_mu_values(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test different mu_eq and mu_in values" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test different mu_eq and mu_in values" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -170,7 +170,7 @@ def test_case_different_mu_values(self): def test_case_different_warm_starting(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test warm starting" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test warm starting" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -214,7 +214,7 @@ def test_case_different_warm_starting(self): def test_case_different_verbose_true(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test verbose = true" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test verbose = true" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -253,7 +253,7 @@ def test_case_different_verbose_true(self): def test_case_different_no_initial_guess(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test no initial guess" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -292,7 +292,7 @@ def test_case_different_no_initial_guess(self): def test_case_different_matrix_free_sparse_backend(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test setting matrix free backend" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test setting matrix free backend" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -332,7 +332,7 @@ def test_case_different_matrix_free_sparse_backend(self): def test_sparse_problem_with_exact_solution_known(self): print( - "------------------------sparse random strongly convex qp with inequality constraints and exact solution known" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints and exact solution known" ) n = 150 @@ -370,7 +370,7 @@ def test_sparse_problem_with_exact_solution_known(self): ) def test_initializing_with_None(self): - print("------------------------test initialization with Nones") + print("------------------------ProxQP: test initialization with Nones") H = np.array([[65.0, -22.0, -16.0], [-22.0, 14.0, 7.0], [-16.0, 7.0, 5.0]]) g = np.array([-13.0, 15.0, 7.0]) diff --git a/test/src/sparse_qp_wrapper.cpp b/test/src/proxqp/sparse_qp_wrapper.cpp similarity index 84% rename from test/src/sparse_qp_wrapper.cpp rename to test/src/proxqp/sparse_qp_wrapper.cpp index f8d07398a..ef757f778 100644 --- a/test/src/sparse_qp_wrapper.cpp +++ b/test/src/proxqp/sparse_qp_wrapper.cpp @@ -3,25 +3,26 @@ // #include #include -#include +#include #include #include using namespace proxsuite; using namespace proxsuite::proxqp; -using namespace proxsuite::proxqp::utils; +using namespace proxsuite::common::utils; using T = double; using I = c_int; using namespace proxsuite::linalg::sparse::tags; -DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with inequality constraints" - "and empty equality constraints") +DOCTEST_TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with " + "inequality constraints" + "and empty equality constraints") { - std::cout << "---testing sparse random strongly convex qp with inequality " - "constraints " - "and empty equality constraints---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with inequality " + "constraints " + "and empty equality constraints---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -32,9 +33,10 @@ DOCTEST_TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); // Testing with empty but properly sized matrix A of size (0, 10) std::cout << "Solving QP with" << std::endl; @@ -66,13 +68,13 @@ DOCTEST_TEST_CASE( std::cout << "after upating" << std::endl; std::cout << "rho : " << qp.results.info.rho << std::endl; qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -119,10 +121,10 @@ DOCTEST_TEST_CASE( std::cout << "after upating" << std::endl; std::cout << "rho : " << qp2.results.info.rho << std::endl; qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.C.transpose() * qp.results.z); - pri_res = proxqp::dense::infty_norm( + pri_res = common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)); CHECK(dua_res <= 1e-9); @@ -154,10 +156,10 @@ DOCTEST_TEST_CASE( std::cout << "after upating" << std::endl; std::cout << "rho : " << qp3.results.info.rho << std::endl; qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.C.transpose() * qp.results.z); - pri_res = proxqp::dense::infty_norm( + pri_res = common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)); CHECK(dua_res <= 1e-9); @@ -173,12 +175,13 @@ DOCTEST_TEST_CASE( } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update rho") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test update rho" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test update rho" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -189,9 +192,10 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; @@ -210,13 +214,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "after upating" << std::endl; std::cout << "rho : " << qp.results.info.rho << std::endl; qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -232,12 +236,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test setting SparseBackend") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test setting SparseBackend" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test setting SparseBackend" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -248,18 +253,19 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; qp.settings.verbose = true; CHECK(qp.settings.sparse_backend == - proxsuite::proxqp::SparseBackend::Automatic); - qp.settings.sparse_backend = proxsuite::proxqp::SparseBackend::MatrixFree; + proxsuite::common::SparseBackend::Automatic); + qp.settings.sparse_backend = proxsuite::common::SparseBackend::MatrixFree; CHECK(qp.settings.sparse_backend == - proxsuite::proxqp::SparseBackend::MatrixFree); + proxsuite::common::SparseBackend::MatrixFree); qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -272,19 +278,19 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " nullopt, nullopt); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); CHECK(pri_res <= 1E-9); CHECK(qp.results.info.sparse_backend == - proxsuite::proxqp::SparseBackend::MatrixFree); + proxsuite::common::SparseBackend::MatrixFree); std::cout << "--n = " << n << " n_eq " << n_eq << " n_in " << n_in << std::endl; std::cout << "dual residual " << dua_res << "; primal residual " << pri_res @@ -298,11 +304,11 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp2.settings.eps_abs = 1.E-9; qp2.settings.verbose = true; CHECK(qp2.settings.sparse_backend == - proxsuite::proxqp::SparseBackend::Automatic); + proxsuite::common::SparseBackend::Automatic); qp2.settings.sparse_backend = - proxsuite::proxqp::SparseBackend::SparseCholesky; + proxsuite::common::SparseBackend::SparseCholesky; CHECK(qp2.settings.sparse_backend == - proxsuite::proxqp::SparseBackend::SparseCholesky); + proxsuite::common::SparseBackend::SparseCholesky); qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -315,19 +321,19 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " nullopt, nullopt); qp2.solve(); - T dua_res2 = proxqp::dense::infty_norm( + T dua_res2 = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); T pri_res2 = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res2 <= 1e-9); CHECK(pri_res2 <= 1E-9); CHECK(qp2.results.info.sparse_backend == - proxsuite::proxqp::SparseBackend::SparseCholesky); + proxsuite::common::SparseBackend::SparseCholesky); std::cout << "--n = " << n << " n_eq " << n_eq << " n_in " << n_in << std::endl; std::cout << "dual residual " << dua_res2 << "; primal residual " @@ -340,13 +346,14 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update mus") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test update mus" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test update mus" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -357,9 +364,10 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; @@ -379,13 +387,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "mu_in : " << qp.results.info.mu_in << std::endl; qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -401,14 +409,15 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test with no equilibration at initialization") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test with no " - "equilibration at initialization" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test with no " + "equilibration at initialization" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -419,9 +428,10 @@ TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; @@ -435,13 +445,13 @@ TEST_CASE( false); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -457,12 +467,13 @@ TEST_CASE( } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test with equilibration at initialization") { std::cout - << "------------------------sparse random strongly convex qp with equality " + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality " "and inequality constraints: test with equilibration at initialization" << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -475,9 +486,10 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; @@ -491,13 +503,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " true); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -512,13 +524,14 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test with no initial guess") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test with no initial guess" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test with no initial guess" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -529,14 +542,15 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -546,13 +560,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -568,13 +582,14 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update g for unconstrained problem") { - std::cout << "------------------------sparse random strongly convex qp with " - "equality and inequality constraints: test with no initial guess" - << std::endl; + std::cout + << "------------------------ProxQP: sparse random strongly convex qp with " + "equality and inequality constraints: test with no initial guess" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), // proxsuite::linalg::veg::tuplify(10, 0, 10), @@ -585,14 +600,15 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -601,13 +617,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.l, qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -621,7 +637,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "setup timing " << qp.results.info.setup_time << " solve time " << qp.results.info.solve_time << std::endl; - auto g = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g = ::proxsuite::common::utils::rand::vector_rand(n); std::cout << "H before update " << qp_random.H << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure std::cout << "H generated " << H_new << std::endl; @@ -645,13 +661,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << " H_unscaled " << H_unscaled.to_eigen() << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -667,7 +683,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test warm starting") { @@ -681,14 +697,15 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -696,22 +713,22 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.C, qp_random.l, qp_random.u); - auto x_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n); - auto y_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_eq); - auto z_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_in); + auto x_wm = ::proxsuite::common::utils::rand::vector_rand(n); + auto y_wm = ::proxsuite::common::utils::rand::vector_rand(n_eq); + auto z_wm = ::proxsuite::common::utils::rand::vector_rand(n_in); std::cout << "proposed warm start" << std::endl; std::cout << "x_wm : " << x_wm << std::endl; std::cout << "y_wm : " << y_wm << std::endl; std::cout << "z_wm : " << z_wm << std::endl; qp.solve(x_wm, y_wm, z_wm); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -728,12 +745,12 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test with warm start with previous result") { std::cout - << "---testing sparse random strongly convex qp with equality and " + << "---ProxQP: testing sparse random strongly convex qp with equality and " "inequality constraints: test with warm start with previous result---" << std::endl; @@ -747,15 +764,16 @@ DOCTEST_TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); T eps_abs = 1.E-9; proxqp::sparse::QP qp(n, n_eq, n_in); // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -765,13 +783,13 @@ DOCTEST_TEST_CASE( qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); DOCTEST_CHECK(pri_res <= eps_abs); @@ -788,7 +806,7 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp2(n, n_eq, n_in); // creating QP object qp2.settings.eps_abs = 1.E-9; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -803,7 +821,7 @@ DOCTEST_TEST_CASE( auto z = qp.results.z; qp2.solve(x, y, z); qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp.solve(); pri_res = std::max( (qp_random.A * qp.results.x - qp_random.b).lpNorm(), @@ -846,13 +864,14 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test with cold start option") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test with cold start option---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test with cold start option---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), @@ -864,15 +883,16 @@ DOCTEST_TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); T eps_abs = 1.E-9; proxqp::sparse::QP qp(n, n_eq, n_in); // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -905,7 +925,7 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp2(n, n_eq, n_in); // creating QP object qp2.settings.eps_abs = 1.E-9; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -919,15 +939,15 @@ DOCTEST_TEST_CASE( auto z = qp.results.z; // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; - qp2.ruiz.scale_primal_in_place({ proxsuite::proxqp::from_eigen, x }); - qp2.ruiz.scale_dual_in_place_eq({ proxsuite::proxqp::from_eigen, y }); - qp2.ruiz.scale_dual_in_place_in({ proxsuite::proxqp::from_eigen, z }); + qp2.ruiz.scale_primal_in_place({ proxsuite::common::from_eigen, x }); + qp2.ruiz.scale_dual_in_place_eq({ proxsuite::common::from_eigen, y }); + qp2.ruiz.scale_dual_in_place_in({ proxsuite::common::from_eigen, z }); // std::cout << "after scaling x " << x << " qp.results.x " << qp.results.x // << std::endl; qp2.solve(x, y, z); qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; qp.solve(); pri_res = std::max( (qp_random.A * qp.results.x - qp_random.b).lpNorm(), @@ -970,13 +990,14 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test equilibration option") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test equilibration option---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test equilibration option---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), @@ -988,15 +1009,16 @@ DOCTEST_TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); T eps_abs = 1.E-9; proxqp::sparse::QP qp(n, n_eq, n_in); // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -1061,13 +1083,14 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test equilibration option at update") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test equilibration option at update---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test equilibration option at update---" + << std::endl; for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), // proxsuite::linalg::veg::tuplify(50, 25, 0), @@ -1079,15 +1102,16 @@ DOCTEST_TEST_CASE( T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); T eps_abs = 1.E-9; proxqp::sparse::QP qp(n, n_eq, n_in); // creating QP object qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -1195,7 +1219,7 @@ DOCTEST_TEST_CASE( } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test new init") { @@ -1209,14 +1233,15 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(n, n_eq, n_in); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp.init(qp_random.H, qp_random.g, qp_random.A, @@ -1224,22 +1249,22 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.C, qp_random.l, qp_random.u); - auto x_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n); - auto y_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_eq); - auto z_wm = ::proxsuite::proxqp::utils::rand::vector_rand(n_in); + auto x_wm = ::proxsuite::common::utils::rand::vector_rand(n); + auto y_wm = ::proxsuite::common::utils::rand::vector_rand(n_eq); + auto z_wm = ::proxsuite::common::utils::rand::vector_rand(n_in); std::cout << "proposed warm start" << std::endl; std::cout << "x_wm : " << x_wm << std::endl; std::cout << "y_wm : " << y_wm << std::endl; std::cout << "z_wm : " << z_wm << std::endl; qp.solve(x_wm, y_wm, z_wm); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1255,7 +1280,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test new init") { @@ -1269,9 +1294,10 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -1287,13 +1313,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1319,13 +1345,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1342,7 +1368,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } ///// TESTS ALL INITIAL GUESS OPTIONS FOR MULTIPLE SOLVES AT ONCE TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { @@ -1354,19 +1380,20 @@ TEST_CASE( proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with no initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -1380,13 +1407,13 @@ TEST_CASE( qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1402,13 +1429,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1425,13 +1452,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1448,13 +1475,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1471,7 +1498,7 @@ TEST_CASE( } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with equality " "constrained initial guess") { @@ -1484,19 +1511,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with equality constrained initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -1511,13 +1539,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1533,13 +1561,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1556,13 +1584,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1579,13 +1607,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1602,7 +1630,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with equality " "constrained initial guess") { @@ -1615,19 +1643,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with warm start with previous result and first solve " "with equality constrained initial guess" @@ -1644,13 +1673,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1665,16 +1694,16 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1691,13 +1720,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1714,13 +1743,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1737,7 +1766,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { @@ -1749,19 +1778,20 @@ TEST_CASE( proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with warm start with previous result and first solve " "with no initial guess" @@ -1778,13 +1808,13 @@ TEST_CASE( qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1799,16 +1829,16 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1825,13 +1855,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1848,13 +1878,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1870,7 +1900,7 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with cold start " "initial guess") { @@ -1883,19 +1913,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with cold start with previous result and first solve " "with equality constrained initial guess" @@ -1912,13 +1943,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1933,16 +1964,16 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1959,13 +1990,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -1982,13 +2013,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2005,7 +2036,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with warm start") { @@ -2017,19 +2048,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with warm start and first solve with no initial guess" << std::endl; @@ -2045,13 +2077,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2066,16 +2098,16 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2092,13 +2124,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2115,13 +2147,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2138,7 +2170,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: warm start test from init") { @@ -2150,19 +2182,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with warm start and first solve with no initial guess" << std::endl; @@ -2178,13 +2211,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2210,17 +2243,17 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp2.settings.eps_abs = 1.E-9; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; std::cout << "dirty workspace for qp2 : " << qp2.work.internal.dirty << std::endl; qp2.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2239,7 +2272,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " /// TESTS WITH UPDATE + INITIAL GUESS OPTIONS -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update and multiple solve at once with " "no initial guess") { @@ -2252,19 +2285,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with no initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -2279,13 +2313,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2301,7 +2335,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); bool update_preconditioner = true; qp.update(H_new, g_new, @@ -2314,13 +2348,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace after update : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2337,13 +2371,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2360,13 +2394,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2383,7 +2417,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "equality constrained initial guess") { @@ -2396,19 +2430,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with equality constrained initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -2423,13 +2458,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2445,7 +2480,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); bool update_preconditioner = true; qp.update(H_new, g_new, @@ -2458,13 +2493,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace after update : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2481,13 +2516,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2504,13 +2539,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2528,7 +2563,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with equality " "constrained initial guess and then warm start with previous results") { @@ -2541,19 +2576,20 @@ TEST_CASE( proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with warm start with previous result and first solve " "with equality constrained initial guess" @@ -2570,13 +2606,13 @@ TEST_CASE( qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2591,10 +2627,10 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); bool update_preconditioner = true; qp.update(H_new, g_new, @@ -2607,13 +2643,13 @@ TEST_CASE( std::cout << "dirty workspace after update : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2630,13 +2666,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2653,13 +2689,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2676,7 +2712,7 @@ TEST_CASE( } } TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test multiple solve at once with no initial guess") { @@ -2688,19 +2724,20 @@ TEST_CASE( proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with warm start with previous result and first solve " "with no initial guess" @@ -2717,13 +2754,13 @@ TEST_CASE( qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2738,10 +2775,10 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); bool update_preconditioner = true; qp.update(H_new, g_new, @@ -2752,13 +2789,13 @@ TEST_CASE( qp_random.u, update_preconditioner); qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2775,13 +2812,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2798,13 +2835,13 @@ TEST_CASE( std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2820,7 +2857,7 @@ TEST_CASE( << qp.results.info.solve_time << std::endl; } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "cold start initial guess and then cold start option") { @@ -2833,19 +2870,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test with cold start with previous result and first solve " "with equality constrained initial guess" @@ -2862,13 +2900,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2883,10 +2921,10 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); bool update_preconditioner = true; qp.update(H_new, g_new, @@ -2897,13 +2935,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " nullopt, update_preconditioner); qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2920,13 +2958,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2943,13 +2981,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -2966,7 +3004,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } } -TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " +TEST_CASE("ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test update + multiple solve at once with " "warm start") { @@ -2979,19 +3017,20 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " proxsuite::linalg::veg::tuplify(10, 2, 2) }) { VEG_BIND(auto const&, (n, n_eq, n_in), dims); - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = 1.E-9; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test with warm start and first solve with no initial guess" << std::endl; @@ -3007,13 +3046,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -3028,7 +3067,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " << qp.results.info.solve_time << std::endl; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; auto x_wm = qp.results.x; // keep previous result auto y_wm = qp.results.y; @@ -3046,13 +3085,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace after update: " << qp.work.internal.dirty << std::endl; qp.solve(x_wm, y_wm, z_wm); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -3072,7 +3111,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " y_wm = qp.results.y; z_wm = qp.results.z; auto H_new = 2. * qp_random.H; // keep same sparsity structure - auto g_new = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g_new = ::proxsuite::common::utils::rand::vector_rand(n); update_preconditioner = true; qp.update(H_new, g_new, @@ -3085,13 +3124,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace after update: " << qp.work.internal.dirty << std::endl; qp.solve(x_wm, y_wm, z_wm); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -3108,13 +3147,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -3131,13 +3170,13 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " std::cout << "dirty workspace : " << qp.work.internal.dirty << std::endl; qp.solve(qp.results.x, qp.results.y, qp.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( H_new.selfadjointView() * qp.results.x + g_new + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= 1e-9); @@ -3155,7 +3194,7 @@ TEST_CASE("ProxQP::sparse: sparse random strongly convex qp with equality and " } TEST_CASE( - "ProxQP::sparse: Test initializaton with rho for different initial guess") + "ProxQP: :sparse: Test initializaton with rho for different initial guess") { for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -3168,19 +3207,20 @@ TEST_CASE( double eps_abs = 1.e-9; - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test initializaton with rho for different initial guess" << std::endl; @@ -3198,13 +3238,13 @@ TEST_CASE( T(1.E-7)); qp.solve(); CHECK(qp.results.info.rho == T(1.E-7)); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3221,7 +3261,7 @@ TEST_CASE( proxqp::sparse::QP qp2(n, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -3233,13 +3273,13 @@ TEST_CASE( T(1.E-7)); qp2.solve(); CHECK(qp2.results.info.rho == T(1.E-7)); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3256,7 +3296,7 @@ TEST_CASE( proxqp::sparse::QP qp3(n, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp3.init(qp_random.H, qp_random.g, qp_random.A, @@ -3268,13 +3308,13 @@ TEST_CASE( T(1.E-7)); qp3.solve(); CHECK(qp3.results.info.rho == T(1.E-7)); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3291,7 +3331,7 @@ TEST_CASE( proxqp::sparse::QP qp4(n, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; qp4.init(qp_random.H, qp_random.g, qp_random.A, @@ -3303,13 +3343,13 @@ TEST_CASE( T(1.E-7)); qp4.solve(); CHECK(qp4.results.info.rho == T(1.E-7)); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3326,7 +3366,7 @@ TEST_CASE( proxqp::sparse::QP qp5(n, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp5.init(qp_random.H, qp_random.g, qp_random.A, @@ -3338,13 +3378,13 @@ TEST_CASE( T(1.E-7)); qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); CHECK(qp5.results.info.rho == T(1.E-7)); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3360,7 +3400,7 @@ TEST_CASE( } } -TEST_CASE("ProxQP::sparse: Test g update for different initial guess") +TEST_CASE("ProxQP: :sparse: Test g update for different initial guess") { for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -3373,19 +3413,20 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") double eps_abs = 1.e-9; - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::NO_INITIAL_GUESS; std::cout << "Test g update for different initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -3399,27 +3440,27 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") qp_random.l, qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); - auto g = ::proxsuite::proxqp::utils::rand::vector_rand(n); + auto g = ::proxsuite::common::utils::rand::vector_rand(n); qp.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK((qp.model.g - g).lpNorm() <= eps_abs); @@ -3437,7 +3478,7 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") proxqp::sparse::QP qp2(n, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -3446,26 +3487,26 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") qp_random.l, qp_random.u); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp2.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK((qp2.model.g - g).lpNorm() <= eps_abs); @@ -3483,7 +3524,7 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") proxqp::sparse::QP qp3(n, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp3.init(qp_random.H, qp_random.g, qp_random.A, @@ -3492,26 +3533,26 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") qp_random.l, qp_random.u); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp3.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK((qp3.model.g - g).lpNorm() <= eps_abs); @@ -3529,7 +3570,7 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") proxqp::sparse::QP qp4(n, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; qp4.init(qp_random.H, qp_random.g, qp_random.A, @@ -3538,26 +3579,26 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") qp_random.l, qp_random.u); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp4.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK((qp4.model.g - g).lpNorm() <= eps_abs); @@ -3575,7 +3616,7 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") proxqp::sparse::QP qp5(n, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp5.init(qp_random.H, qp_random.g, qp_random.A, @@ -3584,26 +3625,26 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") qp_random.l, qp_random.u); qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp5.update(nullopt, g, nullopt, nullopt, nullopt, nullopt, nullopt); qp5.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK((qp5.model.g - g).lpNorm() <= eps_abs); @@ -3620,7 +3661,7 @@ TEST_CASE("ProxQP::sparse: Test g update for different initial guess") } } -TEST_CASE("ProxQP::sparse: Test A update for different initial guess") +TEST_CASE("ProxQP: :sparse: Test A update for different initial guess") { for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -3633,12 +3674,13 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") double eps_abs = 1.e-9; - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -3646,7 +3688,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") // proxqp::sparse::QP qp(n,n_eq,n_in); qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test A update for different initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -3660,13 +3702,13 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp_random.l, qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3675,12 +3717,12 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp.update(nullopt, nullopt, A, nullopt, nullopt, nullopt, nullopt); qp.settings.verbose = false; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -3711,7 +3753,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") proxqp::sparse::QP qp2(n, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -3720,26 +3762,26 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp_random.l, qp_random.u); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp2.update(nullopt, nullopt, A, nullopt, nullopt, nullopt, nullopt); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); // get stored A from KKT matrix @@ -3768,7 +3810,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") proxqp::sparse::QP qp3(n, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp3.init(qp_random.H, qp_random.g, qp_random.A, @@ -3777,26 +3819,26 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp_random.l, qp_random.u); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp3.update(nullopt, nullopt, A, nullopt, nullopt, nullopt, nullopt); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); // get stored A from KKT matrix @@ -3825,7 +3867,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") proxqp::sparse::QP qp4(n, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; qp4.init(qp_random.H, qp_random.g, qp_random.A, @@ -3834,26 +3876,26 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp_random.l, qp_random.u); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp4.update(nullopt, nullopt, A, nullopt, nullopt, nullopt, nullopt); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); // get stored A from KKT matrix @@ -3882,7 +3924,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") proxqp::sparse::QP qp5(n, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp5.init(qp_random.H, qp_random.g, qp_random.A, @@ -3891,26 +3933,26 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") qp_random.l, qp_random.u); qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); CHECK(pri_res <= eps_abs); qp5.update(nullopt, nullopt, A, nullopt, nullopt, nullopt, nullopt); qp5.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); // get stored A from KKT matrix @@ -3938,7 +3980,7 @@ TEST_CASE("ProxQP::sparse: Test A update for different initial guess") } } -TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") +TEST_CASE("ProxQP: :sparse: Test rho update for different initial guess") { for (auto const& dims : { // proxsuite::linalg::veg::tuplify(50, 0, 0), @@ -3951,12 +3993,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") double eps_abs = 1.e-9; - ::proxsuite::proxqp::utils::rand::set_seed(1); + ::proxsuite::common::utils::rand::set_seed(1); T sparsity_factor = 0.15; T strong_convexity_factor = 0.01; - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - n, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + n, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -3964,7 +4007,7 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") // proxqp::sparse::QP qp(n,n_eq,n_in); qp.settings.eps_abs = eps_abs; qp.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; std::cout << "Test rho update for different initial guess" << std::endl; std::cout << "dirty workspace before any solving: " @@ -3978,13 +4021,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") qp_random.l, qp_random.u); qp.solve(); - T dua_res = proxqp::dense::infty_norm( + T dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); T pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4000,13 +4043,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") T(1.E-7)); qp.settings.verbose = false; qp.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp.results.x + qp_random.g + qp_random.A.transpose() * qp.results.y + qp_random.C.transpose() * qp.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4024,7 +4067,7 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") proxqp::sparse::QP qp2(n, n_eq, n_in); qp2.settings.eps_abs = eps_abs; qp2.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; qp2.init(qp_random.H, qp_random.g, qp_random.A, @@ -4033,13 +4076,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") qp_random.l, qp_random.u); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4054,13 +4097,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") true, T(1.E-7)); qp2.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp2.results.x + qp_random.g + qp_random.A.transpose() * qp2.results.y + qp_random.C.transpose() * qp2.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp2.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp2.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp2.results.x - qp_random.l))); CHECK(qp2.results.info.rho == T(1.E-7)); @@ -4078,7 +4121,7 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") proxqp::sparse::QP qp3(n, n_eq, n_in); qp3.settings.eps_abs = eps_abs; qp3.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + proxsuite::common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; qp3.init(qp_random.H, qp_random.g, qp_random.A, @@ -4087,13 +4130,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") qp_random.l, qp_random.u); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4108,13 +4151,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") true, T(1.E-7)); qp3.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp3.results.x + qp_random.g + qp_random.A.transpose() * qp3.results.y + qp_random.C.transpose() * qp3.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp3.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp3.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp3.results.x - qp_random.l))); CHECK(qp3.results.info.rho == T(1.E-7)); @@ -4132,7 +4175,7 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") proxqp::sparse::QP qp4(n, n_eq, n_in); qp4.settings.eps_abs = eps_abs; qp4.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + proxsuite::common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; qp4.init(qp_random.H, qp_random.g, qp_random.A, @@ -4141,13 +4184,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") qp_random.l, qp_random.u); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4162,13 +4205,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") true, T(1.E-7)); qp4.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp4.results.x + qp_random.g + qp_random.A.transpose() * qp4.results.y + qp_random.C.transpose() * qp4.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp4.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp4.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp4.results.x - qp_random.l))); CHECK(qp4.results.info.rho == T(1.E-7)); @@ -4186,7 +4229,7 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") proxqp::sparse::QP qp5(n, n_eq, n_in); qp5.settings.eps_abs = eps_abs; qp5.settings.initial_guess = - proxsuite::proxqp::InitialGuessStatus::WARM_START; + proxsuite::common::InitialGuessStatus::WARM_START; qp5.init(qp_random.H, qp_random.g, qp_random.A, @@ -4195,13 +4238,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") qp_random.l, qp_random.u); qp5.solve(qp3.results.x, qp3.results.y, qp3.results.z); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK(dua_res <= eps_abs); @@ -4216,13 +4259,13 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") true, T(1.E-7)); qp5.solve(); - dua_res = proxqp::dense::infty_norm( + dua_res = common::dense::infty_norm( qp_random.H.selfadjointView() * qp5.results.x + qp_random.g + qp_random.A.transpose() * qp5.results.y + qp_random.C.transpose() * qp5.results.z); pri_res = std::max( - proxqp::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), - proxqp::dense::infty_norm( + common::dense::infty_norm(qp_random.A * qp5.results.x - qp_random.b), + common::dense::infty_norm( helpers::positive_part(qp_random.C * qp5.results.x - qp_random.u) + helpers::negative_part(qp_random.C * qp5.results.x - qp_random.l))); CHECK(qp5.results.info.rho == T(1.E-7)); @@ -4240,25 +4283,27 @@ TEST_CASE("ProxQP::sparse: Test rho update for different initial guess") } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using no initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using no initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -4268,9 +4313,9 @@ DOCTEST_TEST_CASE( T mu_eq(1.e-4); bool compute_preconditioner = true; - qp.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -4330,9 +4375,9 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp2(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); - qp2.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp2.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; @@ -4370,9 +4415,9 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp3(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); - qp3.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp3.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -4437,25 +4482,27 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using EQUALITY_CONSTRAINED_INITIAL_GUESS") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using EQUALITY_CONSTRAINED_INITIAL_GUESS---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using EQUALITY_CONSTRAINED_INITIAL_GUESS---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -4466,9 +4513,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -4529,9 +4576,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -4566,9 +4613,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -4633,25 +4680,27 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(dua_res <= eps_abs); } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using COLD_START_WITH_PREVIOUS_RESULT") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using COLD_START_WITH_PREVIOUS_RESULT---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using COLD_START_WITH_PREVIOUS_RESULT---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -4662,9 +4711,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -4725,9 +4774,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; @@ -4766,9 +4815,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -4834,25 +4883,27 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after updates using WARM_START_WITH_PREVIOUS_RESULT") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "updates using WARM_START_WITH_PREVIOUS_RESULT---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "updates using WARM_START_WITH_PREVIOUS_RESULT---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -4863,9 +4914,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; qp.init(qp_random.H, @@ -4926,9 +4977,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; @@ -4967,9 +5018,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5035,26 +5086,28 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using no initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using no initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using no initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -5070,9 +5123,9 @@ DOCTEST_TEST_CASE( T mu_eq(1.e-4); bool compute_preconditioner = true; - qp.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -5103,7 +5156,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); @@ -5129,7 +5182,7 @@ DOCTEST_TEST_CASE( nullopt, compute_preconditioner, 1.e-6); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); @@ -5150,9 +5203,9 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp2(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); - qp2.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp2.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5171,7 +5224,7 @@ DOCTEST_TEST_CASE( qp2.solve(); DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); qp2.solve(); DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); @@ -5192,9 +5245,9 @@ DOCTEST_TEST_CASE( proxqp::sparse::QP qp3(qp_random.H.cast(), qp_random.A.cast(), qp_random.C.cast()); - qp3.settings.initial_guess = proxqp::InitialGuessStatus::NO_INITIAL_GUESS; + qp3.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::NO_INITIAL_GUESS); + common::InitialGuessStatus::NO_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5208,7 +5261,7 @@ DOCTEST_TEST_CASE( rho, mu_eq); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5239,7 +5292,7 @@ DOCTEST_TEST_CASE( compute_preconditioner, 1.e-6, 1.e-3); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5262,26 +5315,28 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "sparse random strongly convex qp with equality and " + "ProxQP: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings after several solves " "using equality constrained initial guess") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using equality constrained initial guess---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using equality constrained initial guess---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -5298,9 +5353,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -5331,7 +5386,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); @@ -5357,7 +5412,7 @@ DOCTEST_TEST_CASE( nullopt, compute_preconditioner, 1.e-6); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); @@ -5379,9 +5434,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5400,7 +5455,7 @@ DOCTEST_TEST_CASE( qp2.solve(); DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); qp2.solve(); DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); @@ -5422,9 +5477,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); + common::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5438,7 +5493,7 @@ DOCTEST_TEST_CASE( rho, mu_eq); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5468,7 +5523,7 @@ DOCTEST_TEST_CASE( compute_preconditioner, 1.e-6, 1.e-3); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5491,26 +5546,28 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using cold start with previous result") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using cold start with previous result---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using cold start with previous result---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -5527,9 +5584,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -5560,7 +5617,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); @@ -5586,7 +5643,7 @@ DOCTEST_TEST_CASE( nullopt, compute_preconditioner, 1.e-6); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); @@ -5608,9 +5665,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5631,7 +5688,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); @@ -5657,9 +5714,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5673,7 +5730,7 @@ DOCTEST_TEST_CASE( rho, mu_eq); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5708,7 +5765,7 @@ DOCTEST_TEST_CASE( compute_preconditioner, 1.e-6, 1.e-3); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5735,26 +5792,28 @@ DOCTEST_TEST_CASE( } DOCTEST_TEST_CASE( - "ProxQP::sparse: sparse random strongly convex qp with equality and " + "ProxQP: :sparse: sparse random strongly convex qp with equality and " "inequality constraints: test changing default settings " "after several solves using warm start with previous result") { - std::cout << "---testing sparse random strongly convex qp with equality and " - "inequality constraints: test changing default settings after " - "several solves using warm start with previous result---" - << std::endl; + std::cout + << "---ProxQP: testing sparse random strongly convex qp with equality and " + "inequality constraints: test changing default settings after " + "several solves using warm start with previous result---" + << std::endl; double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -5771,9 +5830,9 @@ DOCTEST_TEST_CASE( bool compute_preconditioner = true; qp.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; // qp.settings.verbose = true; @@ -5804,7 +5863,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(pri_res <= eps_abs); DOCTEST_CHECK(dua_res <= eps_abs); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(rho - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(rho - qp.results.info.rho) < 1.e-9); @@ -5830,7 +5889,7 @@ DOCTEST_TEST_CASE( nullopt, compute_preconditioner, 1.e-6); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { qp.solve(); DOCTEST_CHECK(std::abs(1.e-6 - qp.settings.default_rho) < 1.e-9); DOCTEST_CHECK(std::abs(1.e-6 - qp.results.info.rho) < 1.e-9); @@ -5852,9 +5911,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp2.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp2.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp2.settings.eps_abs = eps_abs; qp2.settings.eps_rel = 0; qp2.init(qp_random.H, @@ -5875,7 +5934,7 @@ DOCTEST_TEST_CASE( DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(mu_eq - qp2.settings.default_mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp2.results.info.mu_eq) <= 1.E-9); DOCTEST_CHECK(std::abs(T(1) / mu_eq - qp2.results.info.mu_eq_inv) <= 1.E-9); @@ -5901,9 +5960,9 @@ DOCTEST_TEST_CASE( qp_random.A.cast(), qp_random.C.cast()); qp3.settings.initial_guess = - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT; DOCTEST_CHECK(qp3.settings.initial_guess == - proxqp::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); + common::InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT); qp3.settings.eps_abs = eps_abs; qp3.settings.eps_rel = 0; qp3.init(qp_random.H, @@ -5917,7 +5976,7 @@ DOCTEST_TEST_CASE( rho, mu_eq); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(rho - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(rho - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(mu_eq - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5952,7 +6011,7 @@ DOCTEST_TEST_CASE( compute_preconditioner, 1.e-6, 1.e-3); - for (isize iter = 0; iter < 10; ++iter) { + for (common::dense::isize iter = 0; iter < 10; ++iter) { DOCTEST_CHECK(std::abs(1.e-6 - qp3.settings.default_rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-6 - qp3.results.info.rho) <= 1.E-9); DOCTEST_CHECK(std::abs(1.e-3 - qp3.settings.default_mu_eq) <= 1.E-9); @@ -5978,19 +6037,20 @@ DOCTEST_TEST_CASE( } } -TEST_CASE("ProxQP::sparse: init must be called before update") +TEST_CASE("ProxQP: :sparse: init must be called before update") { double sparsity_factor = 0.15; T eps_abs = T(1e-9); - utils::rand::set_seed(1); - dense::isize dim = 10; + common::utils::rand::set_seed(1); + common::dense::isize dim = 10; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - ::proxsuite::proxqp::utils::rand::set_seed(1); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + ::proxsuite::common::utils::rand::set_seed(1); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -5999,7 +6059,7 @@ TEST_CASE("ProxQP::sparse: init must be called before update") qp.settings.eps_abs = eps_abs; qp.settings.eps_rel = 0; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; // call update without init, update calls init internally qp.update(qp_random.H, @@ -6026,7 +6086,7 @@ TEST_CASE("ProxQP::sparse: init must be called before update") DOCTEST_CHECK(dua_res <= eps_abs); qp_random.H = 2 * qp_random.H; // keep same sparsity structure - qp_random.g = utils::rand::vector_rand(dim); + qp_random.g = common::utils::rand::vector_rand(dim); qp.update(qp_random.H, qp_random.g, nullopt, @@ -6051,20 +6111,21 @@ TEST_CASE("ProxQP::sparse: init must be called before update") DOCTEST_CHECK(dua_res <= eps_abs); } -TEST_CASE("ProxQP::sparse: test primal infeasibility solving") +TEST_CASE("ProxQP: :sparse: test primal infeasibility solving") { double sparsity_factor = 0.15; T eps_abs = T(1e-5); - utils::rand::set_seed(1); - dense::isize dim = 20; + common::utils::rand::set_seed(1); + common::dense::isize dim = 20; - dense::isize n_eq(dim / 4); - dense::isize n_in(dim / 4); + common::dense::isize n_eq(dim / 4); + common::dense::isize n_in(dim / 4); T strong_convexity_factor(1.e-2); - for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::sparse::SparseModel qp_random = utils::sparse_strongly_convex_qp( - dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + for (common::dense::isize i = 0; i < 20; ++i) { + ::proxsuite::common::utils::rand::set_seed(i); + proxqp::sparse::SparseModel qp_random = + common::utils::sparse_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); proxqp::sparse::QP qp(qp_random.H.cast(), qp_random.A.cast(), @@ -6074,7 +6135,7 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") // create infeasible problem qp_random.b.array() += T(10.); qp_random.u.array() -= T(100.); - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; qp.settings.primal_infeasibility_solving = true; qp.settings.eps_primal_inf = T(1.E-4); qp.settings.eps_dual_inf = T(1.E-4); @@ -6111,29 +6172,29 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") DOCTEST_CHECK(dua_res <= eps_abs); } } -// TEST_CASE("ProxQP::sparse: estimate of minimal eigenvalues using Eigen") +// TEST_CASE("ProxQP: :sparse: estimate of minimal eigenvalues using Eigen") // { // double sparsity_factor = 0.25; // T tol = T(1e-6); -// utils::rand::set_seed(1); -// dense::isize dim = 2; -// dense::isize n_eq(dim); -// dense::isize n_in(dim); +// common::utils::rand::set_seed(1); +// common::dense::isize dim = 2; +// common::dense::isize n_eq(dim); +// common::dense::isize n_in(dim); // T strong_convexity_factor(1.e-2); // dim = 50; // n_eq = dim; // n_in = dim; -// for (isize i = 0; i < 20; ++i) { -// ::proxsuite::proxqp::utils::rand::set_seed(i); -// proxqp::dense::Model qp_random = -// proxqp::utils::dense_strongly_convex_qp( +// for (common::dense::isize i = 0; i < 20; ++i) { +// ::proxsuite::common::utils::rand::set_seed(i); +// common::dense::Model qp_random = +// common::utils::dense_strongly_convex_qp( // dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // // proxqp::sparse::SparseModel qp_random = -// // utils::sparse_strongly_convex_qp( +// // common::utils::sparse_strongly_convex_qp( // // dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // qp_random.H.setZero(); -// dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); +// dense::Vec random_diag = common::utils::rand::vector_rand(dim); // qp_random.H.diagonal().array() += random_diag.array(); // T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); @@ -6142,7 +6203,7 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") // qp.settings.max_iter_in = 1; // qp.settings.estimate_method_option = // EigenValueEstimateMethodOption::EigenRegularization; -// qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; +// qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; // SparseMat H_sparse = qp_random.H.sparseView(); // SparseMat A_sparse = qp_random.A.sparseView(); // SparseMat C_sparse = qp_random.C.sparseView(); @@ -6159,13 +6220,13 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") // dim = 50; // n_eq = dim; // n_in = dim; -// for (isize i = 0; i < 20; ++i) { -// ::proxsuite::proxqp::utils::rand::set_seed(i); -// proxqp::dense::Model qp_random = -// proxqp::utils::dense_strongly_convex_qp( +// for (common::dense::isize i = 0; i < 20; ++i) { +// ::proxsuite::common::utils::rand::set_seed(i); +// common::dense::Model qp_random = +// common::utils::dense_strongly_convex_qp( // dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); -// dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); +// dense::Vec random_diag = common::utils::rand::vector_rand(dim); // qp_random.H.diagonal().array() += 100 * random_diag.array(); // proxqp::sparse::QP qp(dim, n_eq, n_in); @@ -6173,7 +6234,7 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") // qp.settings.max_iter_in = 1; // qp.settings.estimate_method_option = // EigenValueEstimateMethodOption::EigenRegularization; -// qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; +// qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; // SparseMat H_sparse = qp_random.H.sparseView(); // SparseMat A_sparse = qp_random.A.sparseView(); // SparseMat C_sparse = qp_random.C.sparseView(); @@ -6191,35 +6252,37 @@ TEST_CASE("ProxQP::sparse: test primal infeasibility solving") // minimal_eigenvalue) <= 1.); // } // } -TEST_CASE("ProxQP::sparse: estimate of minimal eigenvalues using manual choice") +TEST_CASE( + "ProxQP: :sparse: estimate of minimal eigenvalues using manual choice") { double sparsity_factor = 0.25; T tol = T(1e-6); - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + common::dense::isize dim = 2; + common::dense::isize n_eq(dim); + common::dense::isize n_in(dim); T strong_convexity_factor(1.e-2); dim = 50; n_eq = dim; n_in = dim; - for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + for (common::dense::isize i = 0; i < 20; ++i) { + ::proxsuite::common::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // proxqp::sparse::SparseModel qp_random = - // utils::sparse_strongly_convex_qp( + // common::utils::sparse_strongly_convex_qp( // dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += random_diag.array(); T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); proxqp::sparse::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; qp.settings.max_iter_in = 1; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; SparseMat H_sparse = qp_random.H.sparseView(); SparseMat A_sparse = qp_random.A.sparseView(); SparseMat C_sparse = qp_random.C.sparseView(); @@ -6241,23 +6304,24 @@ TEST_CASE("ProxQP::sparse: estimate of minimal eigenvalues using manual choice") dim = 50; n_eq = dim; n_in = dim; - for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + for (common::dense::isize i = 0; i < 20; ++i) { + ::proxsuite::common::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += 100 * random_diag.array(); proxqp::sparse::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; qp.settings.max_iter_in = 1; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; SparseMat H_sparse = qp_random.H.sparseView(); SparseMat A_sparse = qp_random.A.sparseView(); SparseMat C_sparse = qp_random.C.sparseView(); - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); qp.init(H_sparse, qp_random.g, @@ -6277,28 +6341,29 @@ TEST_CASE("ProxQP::sparse: estimate of minimal eigenvalues using manual choice") } TEST_CASE( - "ProxQP::sparse: estimate of minimal eigenvalues using Power iteration") + "ProxQP: :sparse: estimate of minimal eigenvalues using Power iteration") { double sparsity_factor = 0.25; T tol = T(1e-6); - utils::rand::set_seed(1); - dense::isize dim = 2; - dense::isize n_eq(dim); - dense::isize n_in(dim); + common::utils::rand::set_seed(1); + common::dense::isize dim = 2; + common::dense::isize n_eq(dim); + common::dense::isize n_in(dim); T strong_convexity_factor(1.e-2); dim = 50; n_eq = dim; n_in = dim; - for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + for (common::dense::isize i = 0; i < 20; ++i) { + ::proxsuite::common::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); // proxqp::sparse::SparseModel qp_random = - // utils::sparse_strongly_convex_qp( + // common::utils::sparse_strongly_convex_qp( // dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); qp_random.H.setZero(); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += random_diag.array(); T minimal_eigenvalue = qp_random.H.diagonal().minCoeff(); // std::cout << "qp_random.H" << std::endl; @@ -6307,7 +6372,7 @@ TEST_CASE( proxqp::sparse::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; qp.settings.max_iter_in = 1; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; SparseMat H_sparse = qp_random.H.sparseView(); SparseMat A_sparse = qp_random.A.sparseView(); SparseMat C_sparse = qp_random.C.sparseView(); @@ -6332,26 +6397,27 @@ TEST_CASE( dim = 50; n_eq = dim; n_in = dim; - for (isize i = 0; i < 20; ++i) { - ::proxsuite::proxqp::utils::rand::set_seed(i); - proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + for (common::dense::isize i = 0; i < 20; ++i) { + ::proxsuite::common::utils::rand::set_seed(i); + common::dense::Model qp_random = common::utils::dense_strongly_convex_qp( dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); - dense::Vec random_diag = proxqp::utils::rand::vector_rand(dim); + common::dense::Vec random_diag = + common::utils::rand::vector_rand(dim); qp_random.H.diagonal().array() += 100 * random_diag.array(); proxqp::sparse::QP qp(dim, n_eq, n_in); qp.settings.max_iter = 1; qp.settings.max_iter_in = 1; - qp.settings.initial_guess = InitialGuessStatus::NO_INITIAL_GUESS; + qp.settings.initial_guess = common::InitialGuessStatus::NO_INITIAL_GUESS; SparseMat H_sparse = qp_random.H.sparseView(); SparseMat A_sparse = qp_random.A.sparseView(); SparseMat C_sparse = qp_random.C.sparseView(); T estimate_minimal_eigen_value = sparse::estimate_minimal_eigen_value_of_symmetric_matrix( H_sparse, 1.E-6, 10000); - Eigen::SelfAdjointEigenSolver> es(qp_random.H, - Eigen::EigenvaluesOnly); + Eigen::SelfAdjointEigenSolver> es( + qp_random.H, Eigen::EigenvaluesOnly); const T minimal_eigenvalue = T(es.eigenvalues().minCoeff()); qp.init(H_sparse, qp_random.g, diff --git a/test/src/sparse_qp_wrapper.py b/test/src/proxqp/sparse_qp_wrapper.py similarity index 95% rename from test/src/sparse_qp_wrapper.py rename to test/src/proxqp/sparse_qp_wrapper.py index bd79834db..795c43f2b 100644 --- a/test/src/sparse_qp_wrapper.py +++ b/test/src/proxqp/sparse_qp_wrapper.py @@ -48,7 +48,7 @@ class SparseqpWrapper(unittest.TestCase): # TESTS OF GENERAL METHODS OF THE API def test_case_update_rho(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test update rho" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test update rho" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -89,7 +89,7 @@ def test_case_update_rho(self): def test_case_setting_SparseBackend(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test setting SparseBackend" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test setting SparseBackend" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -174,7 +174,7 @@ def test_case_setting_SparseBackend(self): def test_case_update_mu(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test update mus" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test update mus" ) n = 10 @@ -226,7 +226,7 @@ def test_case_update_mu(self): def test_case_no_equilibration_at_initialization(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no equilibration at initialization" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no equilibration at initialization" ) n = 10 @@ -278,7 +278,7 @@ def test_case_no_equilibration_at_initialization(self): def test_case_with_equilibration_at_initialization(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with equilibration at initialization" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with equilibration at initialization" ) n = 10 @@ -330,7 +330,7 @@ def test_case_with_equilibration_at_initialization(self): def test_case_no_initial_guess(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" ) n = 10 @@ -382,7 +382,7 @@ def test_case_no_initial_guess(self): def test_case_no_initial_guess_and_update(self): print( - "------------------------sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" + "------------------------ProxQP: sparse random strongly convex qp with equality and inequality constraints: test with no initial guess" ) n = 10 @@ -473,7 +473,7 @@ def test_case_no_initial_guess_and_update(self): def test_case_warm_starting(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with warm start---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -527,7 +527,7 @@ def test_case_warm_starting(self): def test_case_warm_start_with_previous_result(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with warm start with previous result---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with warm start with previous result---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -655,7 +655,7 @@ def test_case_warm_start_with_previous_result(self): def test_case_cold_start_with_previous_result(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test with cold start with previous result---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test with cold start with previous result---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -785,7 +785,7 @@ def test_case_cold_start_with_previous_result(self): def test_case_equilibration_option(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option---" ) n = 10 @@ -887,7 +887,7 @@ def test_case_equilibration_option(self): def test_case_equilibration_option_at_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option at update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test equilibration option at update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1054,7 +1054,7 @@ def test_case_equilibration_option_at_update(self): def test_case_warm_start_with_other_initialization(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test warm start with other initialization---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test warm start with other initialization---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1104,7 +1104,7 @@ def test_case_warm_start_with_other_initialization(self): def test_case_multiple_solve_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1230,7 +1230,7 @@ def test_case_multiple_solve_with_no_initial_guess(self): def test_case_multiple_solve_with_equality_constrained_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1360,7 +1360,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1492,7 +1492,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints def test_case_warm_start_with_previous_result_starting_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1622,7 +1622,7 @@ def test_case_warm_start_with_previous_result_starting_with_no_initial_guess(sel def test_case_cold_start_with_previous_result_starting_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1752,7 +1752,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess(sel def test_case_warm_start_with_no_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1883,7 +1883,7 @@ def test_case_warm_start_with_no_initial_guess(self): def test_case_warm_start_with_no_initial_guess_and_different_init(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test solve from warm start and no initial guess with other initialization---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test solve from warm start and no initial guess with other initialization---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -1966,7 +1966,7 @@ def test_case_warm_start_with_no_initial_guess_and_different_init(self): def test_case_multiple_solve_with_no_initial_guess_and_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with no inital guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2107,7 +2107,7 @@ def test_case_multiple_solve_with_equality_constrained_initial_guess_and_update( self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve with equality constrained initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2250,7 +2250,7 @@ def test_case_warm_start_with_previous_result_starting_with_equality_constraints self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and equality constrained inital guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2397,7 +2397,7 @@ def test_case_warm_start_with_previous_result_starting_with_no_initial_guess_and self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after warm starting with previous results and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2542,7 +2542,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess_and self, ): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve after cold starting with previous results and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2674,7 +2674,7 @@ def test_case_cold_start_with_previous_result_starting_with_no_initial_guess_and def test_case_warm_start_with_no_initial_guess_and_update(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess and update---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test multiple solve from warm start and no initial guess and update---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -2815,7 +2815,7 @@ def test_case_warm_start_with_no_initial_guess_and_update(self): def test_case_initialization_with_rho_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test initializaton with rho for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test initializaton with rho for different initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -3034,7 +3034,7 @@ def test_case_initialization_with_rho_for_different_initial_guess(self): def test_case_update_g_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update g for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update g for different initial guess---" ) n = 10 H, g_old, A, b, C, u, l = generate_mixed_qp(n) @@ -3334,7 +3334,7 @@ def test_case_update_g_for_different_initial_guess(self): def test_case_update_A_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update A for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update A for different initial guess---" ) n = 10 H, g, A_old, b, C, u, l = generate_mixed_qp(n) @@ -3629,7 +3629,7 @@ def test_case_update_A_for_different_initial_guess(self): def test_case_update_rho_update_for_different_initial_guess(self): print( - "---testing sparse random strongly convex qp with equality and inequality constraints: test update rho for different initial guess---" + "---ProxQP: testing sparse random strongly convex qp with equality and inequality constraints: test update rho for different initial guess---" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -3930,7 +3930,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_no_initial_gue self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, no initial guess, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, no initial guess, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4001,7 +4001,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_EQUALITY_CONST self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4074,7 +4074,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_COLD_START_WIT self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4147,7 +4147,7 @@ def test_sparse_problem_multiple_solve_with_default_rho_mu_eq_and_WARM_START_WIT self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, multiple solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4220,7 +4220,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_WARM_START_W self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, WARM_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4293,7 +4293,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_COLD_START_W self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, COLD_START_WITH_PREVIOUS_RESULT, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4366,7 +4366,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_EQUALITY_CON self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, EQUALITY_CONSTRAINED_INITIAL_GUESS, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4439,7 +4439,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_NO_INITIAL_G self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, NO_INITIAL_GUESS, update + solve and default rho and mu_eq" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, NO_INITIAL_GUESS, update + solve and default rho and mu_eq" ) n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) @@ -4508,7 +4508,7 @@ def test_sparse_problem_update_and_solve_with_default_rho_mu_eq_and_NO_INITIAL_G def test_sparse_problem_with_exact_solution_known(self): print( - "------------------------sparse random strongly convex qp with inequality constraints and exact solution known" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints and exact solution known" ) n = 150 @@ -4579,7 +4579,7 @@ def test_sparse_infeasibility_solving( self, ): print( - "------------------------sparse random strongly convex qp with inequality constraints, test infeasibility solving" + "------------------------ProxQP: sparse random strongly convex qp with inequality constraints, test infeasibility solving" ) n = 20 for i in range(20): @@ -4697,7 +4697,7 @@ def test_minimal_eigenvalue_estimation_nonconvex_power_iter_option( self, ): print( - "------------------------sparse non convex qp with inequality constraints, estimate minimal eigenvalue with power iter option" + "------------------------ProxQP: sparse non convex qp with inequality constraints, estimate minimal eigenvalue with power iter option" ) n = 50 tol = 1.0 diff --git a/test/src/sparse_ruiz_equilibration.cpp b/test/src/proxqp/sparse_ruiz_equilibration.cpp similarity index 76% rename from test/src/sparse_ruiz_equilibration.cpp rename to test/src/proxqp/sparse_ruiz_equilibration.cpp index 04f6b67f4..91fb3666a 100644 --- a/test/src/sparse_ruiz_equilibration.cpp +++ b/test/src/proxqp/sparse_ruiz_equilibration.cpp @@ -1,20 +1,22 @@ // // Copyright (c) 2022 INRIA // +#include "proxsuite/common/settings.hpp" #include #include -#include -#include +#include +#include #include #include using namespace proxsuite; -using namespace proxsuite::proxqp; +using namespace proxsuite::common; using T = double; -using I = utils::c_int; +using I = common::utils::c_int; + using namespace proxsuite::linalg::sparse::tags; -TEST_CASE("upper part") +TEST_CASE("ProxQP: upper part") { isize n = 10; isize n_eq = 6; @@ -48,7 +50,7 @@ TEST_CASE("upper part") proxqp::sparse::preconditioner::RuizEquilibration ruiz{ n, n_eq + n_in, 1e-3, 10, proxqp::sparse::preconditioner::Symmetry::UPPER, }; - proxqp::dense::preconditioner::RuizEquilibration ruiz_dense{ + common::dense::preconditioner::RuizEquilibration ruiz_dense{ n, n_eq, n_in, box_constraints, 1e-3, 10, Symmetry::upper, }; VEG_MAKE_STACK(stack, @@ -56,10 +58,10 @@ TEST_CASE("upper part") proxsuite::linalg::veg::Tag{}, n, n_eq, n_in)); bool execute_preconditioner = true; - proxsuite::proxqp::Settings settings; - proxqp::dense::Vec u_scaled_box(0); - proxqp::dense::Vec l_scaled_box(0); - proxqp::dense::Vec eye(0); + Settings settings; + common::dense::Vec u_scaled_box(0); + common::dense::Vec l_scaled_box(0); + common::dense::Vec eye(0); ruiz.scale_qp_in_place( { { proxsuite::linalg::sparse::from_eigen, H_scaled }, @@ -75,19 +77,19 @@ TEST_CASE("upper part") settings.preconditioner_max_iter, settings.preconditioner_accuracy, stack); - HessianType HessianType(proxsuite::proxqp::HessianType::Dense); + HessianType HessianType(HessianType::Dense); ruiz_dense.scale_qp_in_place( - proxqp::dense::QpViewBoxMut{ - { proxqp::from_eigen, H_scaled_dense }, - { proxqp::from_eigen, g_scaled_dense }, - { proxqp::from_eigen, A_scaled_dense }, - { proxqp::from_eigen, b_scaled_dense }, - { proxqp::from_eigen, C_scaled_dense }, - { proxqp::from_eigen, l_scaled_dense }, - { proxqp::from_eigen, u_scaled_dense }, - { proxqp::from_eigen, u_scaled_box }, - { proxqp::from_eigen, l_scaled_box }, - { proxqp::from_eigen, eye }, + common::dense::QpViewBoxMut{ + { common::from_eigen, H_scaled_dense }, + { common::from_eigen, g_scaled_dense }, + { common::from_eigen, A_scaled_dense }, + { common::from_eigen, b_scaled_dense }, + { common::from_eigen, C_scaled_dense }, + { common::from_eigen, l_scaled_dense }, + { common::from_eigen, u_scaled_dense }, + { common::from_eigen, u_scaled_box }, + { common::from_eigen, l_scaled_box }, + { common::from_eigen, eye }, }, execute_preconditioner, settings.primal_infeasibility_solving, @@ -106,7 +108,7 @@ TEST_CASE("upper part") CHECK(u_scaled.isApprox(u_scaled_dense)); } -TEST_CASE("lower part") +TEST_CASE("ProxQP: lower part") { isize n = 3; isize n_eq = 0; @@ -142,14 +144,14 @@ TEST_CASE("lower part") proxqp::sparse::preconditioner::RuizEquilibration ruiz{ n, n_eq + n_in, 1e-3, 10, proxqp::sparse::preconditioner::Symmetry::LOWER, }; - proxqp::dense::preconditioner::RuizEquilibration ruiz_dense{ + common::dense::preconditioner::RuizEquilibration ruiz_dense{ n, n_eq, n_in, box_constraints, 1e-3, 10, Symmetry::lower, }; VEG_MAKE_STACK(stack, ruiz.scale_qp_in_place_req( proxsuite::linalg::veg::Tag{}, n, n_eq, n_in)); bool execute_preconditioner = true; - proxsuite::proxqp::Settings settings; + Settings settings; ruiz.scale_qp_in_place( { { proxsuite::linalg::sparse::from_eigen, H_scaled }, @@ -165,22 +167,22 @@ TEST_CASE("lower part") settings.preconditioner_max_iter, settings.preconditioner_accuracy, stack); - proxqp::dense::Vec u_scaled_box(0); - proxqp::dense::Vec l_scaled_box(0); - proxqp::dense::Vec eye(0); + common::dense::Vec u_scaled_box(0); + common::dense::Vec l_scaled_box(0); + common::dense::Vec eye(0); HessianType HessianType(HessianType::Dense); ruiz_dense.scale_qp_in_place( - proxqp::dense::QpViewBoxMut{ - { proxqp::from_eigen, H_scaled_dense }, - { proxqp::from_eigen, g_scaled_dense }, - { proxqp::from_eigen, A_scaled_dense }, - { proxqp::from_eigen, b_scaled_dense }, - { proxqp::from_eigen, C_scaled_dense }, - { proxqp::from_eigen, l_scaled_dense }, - { proxqp::from_eigen, u_scaled_dense }, - { proxqp::from_eigen, u_scaled_box }, - { proxqp::from_eigen, l_scaled_box }, - { proxqp::from_eigen, eye }, + common::dense::QpViewBoxMut{ + { common::from_eigen, H_scaled_dense }, + { common::from_eigen, g_scaled_dense }, + { common::from_eigen, A_scaled_dense }, + { common::from_eigen, b_scaled_dense }, + { common::from_eigen, C_scaled_dense }, + { common::from_eigen, l_scaled_dense }, + { common::from_eigen, u_scaled_dense }, + { common::from_eigen, u_scaled_box }, + { common::from_eigen, l_scaled_box }, + { common::from_eigen, eye }, }, execute_preconditioner, settings.primal_infeasibility_solving, diff --git a/test/src/util_f32.cpp b/test/src/util_f32.cpp index 06afe7ffa..223081d02 100644 --- a/test/src/util_f32.cpp +++ b/test/src/util_f32.cpp @@ -1,7 +1,7 @@ #include "util_f64.hpp" namespace proxsuite { -namespace proxqp { +namespace common { namespace utils { namespace eigen { @@ -20,8 +20,8 @@ LDLT_EXPLICIT_TPL_DEF(3, sparse_positive_definite_rand); } // namespace rand LDLT_EXPLICIT_TPL_DEF(2, matmul_impl); -LDLT_EXPLICIT_TPL_DEF(1, mat_cast); +LDLT_EXPLICIT_TPL_DEF(1, mat_cast); } // namespace utils -} // namespace proxqp +} // namespace common } // namespace proxsuite diff --git a/test/src/util_f64.cpp b/test/src/util_f64.cpp index 0101ad398..71b8c5918 100644 --- a/test/src/util_f64.cpp +++ b/test/src/util_f64.cpp @@ -1,7 +1,7 @@ #include "util_f64.hpp" namespace proxsuite { -namespace proxqp { +namespace common { namespace utils { namespace eigen { @@ -23,5 +23,5 @@ LDLT_EXPLICIT_TPL_DEF(3, sparse_positive_definite_rand); LDLT_EXPLICIT_TPL_DEF(1, mat_cast); } // namespace utils -} // namespace proxqp +} // namespace common } // namespace proxsuite