11//
2- // Copyright (c) 2022 INRIA
2+ // Copyright (c) 2022-2023 INRIA
33//
4+
45#include < doctest.hpp>
56#include < maros_meszaros.hpp>
67#include < proxsuite/proxqp/sparse/wrapper.hpp>
@@ -9,6 +10,30 @@ using namespace proxsuite;
910
1011#define MAROS_MESZAROS_DIR PROBLEM_PATH " /data/maros_meszaros_data/"
1112
13+ template <typename T>
14+ void
15+ compute_primal_dual_feasibility (const PreprocessedQpSparse& preprocessed,
16+ const proxqp::Results<T>& results,
17+ T& primal_feasibility,
18+ T& dual_feasibility)
19+ {
20+ dual_feasibility = proxsuite::proxqp::dense::infty_norm (
21+ preprocessed.H .selfadjointView <Eigen::Upper>() * results.x +
22+ preprocessed.g + preprocessed.AT * results.y + preprocessed.CT * results.z );
23+
24+ T prim_eq = proxsuite::proxqp::dense::infty_norm (
25+ preprocessed.AT .transpose () * results.x - preprocessed.b );
26+ T prim_in =
27+ std::max (proxsuite::proxqp::dense::infty_norm (
28+ preprocessed.AT .transpose () * results.x - preprocessed.b ),
29+ proxsuite::proxqp::dense::infty_norm (
30+ helpers::positive_part (preprocessed.CT .transpose () * results.x -
31+ preprocessed.u ) +
32+ helpers::negative_part (preprocessed.CT .transpose () * results.x -
33+ preprocessed.l )));
34+ primal_feasibility = std::max (prim_eq, prim_in);
35+ }
36+
1237char const * files[] = {
1338
1439 MAROS_MESZAROS_DIR " AUG2D.mat" , MAROS_MESZAROS_DIR " AUG2DC.mat" ,
@@ -88,6 +113,9 @@ TEST_CASE("sparse maros meszaros using the API")
88113 using T = double ;
89114 using I = mat_int32_t ;
90115
116+ const T eps_abs_no_duality_gap = 2e-8 ;
117+ const T eps_abs_with_duality_gap = 1e-5 ;
118+
91119 for (auto const * file : files) {
92120 auto qp_raw = load_qp (file);
93121 isize n = qp_raw.P .rows ();
@@ -96,7 +124,7 @@ TEST_CASE("sparse maros meszaros using the API")
96124 bool skip = (n > 1000 || n_eq_in > 1000 );
97125 if (skip) {
98126 std::cout << " path: " << qp_raw.filename << " n: " << n
99- << " n_eq+n_in: " << n_eq_in << " skipping " << std::endl;
127+ << " n_eq+n_in: " << n_eq_in << " - SKIPPING " << std::endl;
100128 } else {
101129 std::cout << " path: " << qp_raw.filename << " n: " << n
102130 << " n_eq+n_in: " << n_eq_in << std::endl;
@@ -120,71 +148,71 @@ TEST_CASE("sparse maros meszaros using the API")
120148 qp.settings .max_iter = 1 .E6 ;
121149 qp.settings .verbose = false ;
122150
123- qp.settings .eps_abs = 2e-8 ;
151+ qp.settings .eps_abs = eps_abs_no_duality_gap ;
124152 qp.settings .eps_rel = 0 ;
125- auto & eps = qp.settings .eps_abs ;
126153 qp.init (H, g, AT.transpose (), b, CT.transpose (), l, u);
127- T prim_eq (0 );
128- T prim_in (0 );
129154
130155 for (isize iter = 0 ; iter < 10 ; ++iter) {
131156 if (iter > 0 )
132157 qp.settings .initial_guess = proxsuite::proxqp::InitialGuessStatus::
133158 WARM_START_WITH_PREVIOUS_RESULT;
134159 qp.solve ();
135160
161+ T primal_feasibility, dual_feasibility;
162+ compute_primal_dual_feasibility (
163+ preprocessed, qp.results , primal_feasibility, dual_feasibility);
164+
165+ CHECK (primal_feasibility < qp.settings .eps_abs );
166+ CHECK (dual_feasibility < qp.settings .eps_abs );
167+ CHECK (qp.results .info .pri_res < eps_abs_with_duality_gap);
168+ CHECK (qp.results .info .dua_res < eps_abs_with_duality_gap);
169+
170+ std::cout << " dual feasibility: " << dual_feasibility << std::endl;
171+
172+ std::cout << " primal residual " << primal_feasibility << std::endl;
173+ }
174+
175+ {
176+ qp.solve ();
177+
136178 CHECK (proxsuite::proxqp::dense::infty_norm (
137179 H.selfadjointView <Eigen::Upper>() * qp.results .x + g +
138- AT * qp.results .y + CT * qp.results .z ) <= eps);
180+ AT * qp.results .y + CT * qp.results .z ) <=
181+ eps_abs_no_duality_gap);
139182 CHECK (proxsuite::proxqp::dense::infty_norm (
140- AT.transpose () * qp.results .x - b) <= eps );
183+ AT.transpose () * qp.results .x - b) <= eps_abs_no_duality_gap );
141184 if (n_in > 0 ) {
142- CHECK ((CT.transpose () * qp.results .x - l).minCoeff () > -eps);
143- CHECK ((CT.transpose () * qp.results .x - u).maxCoeff () < eps);
185+ CHECK ((CT.transpose () * qp.results .x - l).minCoeff () >
186+ -eps_abs_no_duality_gap);
187+ CHECK ((CT.transpose () * qp.results .x - u).maxCoeff () <
188+ eps_abs_no_duality_gap);
144189 }
145- std::cout << " dual residual "
146- << proxsuite::proxqp::dense::infty_norm (
147- H.selfadjointView <Eigen::Upper>() * qp.results .x + g +
148- AT * qp.results .y + CT * qp.results .z )
149- << std::endl;
150- T prim_eq = proxsuite::proxqp::dense::infty_norm (
151- AT.transpose () * qp.results .x - b);
152- T prim_in = std::max (
153- proxsuite::proxqp::dense::infty_norm (AT.transpose () * qp.results .x -
154- b),
155- proxsuite::proxqp::dense::infty_norm (
156- helpers::positive_part (CT.transpose () * qp.results .x - u) +
157- helpers::negative_part (CT.transpose () * qp.results .x - l)));
158- std::cout << " primal residual " << std::max (prim_eq, prim_in)
159- << std::endl;
190+
191+ T primal_feasibility, dual_feasibility;
192+ compute_primal_dual_feasibility (
193+ preprocessed, qp.results , primal_feasibility, dual_feasibility);
194+
195+ std::cout << " dual residual " << dual_feasibility << std::endl;
196+
197+ std::cout << " primal residual " << primal_feasibility << std::endl;
160198 }
161199
162- qp.solve ();
200+ {
201+ qp.settings .initial_guess =
202+ proxsuite::proxqp::InitialGuessStatus::NO_INITIAL_GUESS;
203+ qp.settings .check_duality_gap = true ;
204+ qp.settings .eps_abs = eps_abs_with_duality_gap;
205+
206+ qp.solve ();
207+
208+ T primal_feasibility, dual_feasibility;
209+ compute_primal_dual_feasibility (
210+ preprocessed, qp.results , primal_feasibility, dual_feasibility);
163211
164- CHECK (proxsuite::proxqp::dense::infty_norm (
165- H.selfadjointView <Eigen::Upper>() * qp.results .x + g +
166- AT * qp.results .y + CT * qp.results .z ) <= eps);
167- CHECK (proxsuite::proxqp::dense::infty_norm (AT.transpose () * qp.results .x -
168- b) <= eps);
169- if (n_in > 0 ) {
170- CHECK ((CT.transpose () * qp.results .x - l).minCoeff () > -eps);
171- CHECK ((CT.transpose () * qp.results .x - u).maxCoeff () < eps);
212+ CHECK (primal_feasibility < eps_abs_with_duality_gap);
213+ CHECK (dual_feasibility < eps_abs_with_duality_gap);
214+ CHECK (qp.results .info .duality_gap < eps_abs_with_duality_gap);
172215 }
173- std::cout << " dual residual "
174- << proxsuite::proxqp::dense::infty_norm (
175- H.selfadjointView <Eigen::Upper>() * qp.results .x + g +
176- AT * qp.results .y + CT * qp.results .z )
177- << std::endl;
178-
179- prim_eq =
180- proxsuite::proxqp::dense::infty_norm (AT.transpose () * qp.results .x - b);
181- prim_in = std::max (
182- proxsuite::proxqp::dense::infty_norm (AT.transpose () * qp.results .x - b),
183- proxsuite::proxqp::dense::infty_norm (
184- helpers::positive_part (CT.transpose () * qp.results .x - u) +
185- helpers::negative_part (CT.transpose () * qp.results .x - l)));
186- std::cout << " primal residual " << std::max (prim_eq, prim_in)
187- << std::endl;
188216 }
189217 }
190218}
0 commit comments