Skip to content

Commit 78ba33b

Browse files
committed
test/maros: also check duality_gap for sparse Maros problems
1 parent 16f0839 commit 78ba33b

File tree

1 file changed

+77
-49
lines changed

1 file changed

+77
-49
lines changed

test/src/sparse_maros_meszaros.cpp

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
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+
1237
char 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

Comments
 (0)