Skip to content

Commit 3ff2d25

Browse files
authored
Merge pull request #59 from fabinsch/topic/handle-empty-inputs
Handle empty inputs
2 parents 50b94fe + d55f6d9 commit 3ff2d25

File tree

5 files changed

+348
-16
lines changed

5 files changed

+348
-16
lines changed

include/proxsuite/linalg/veg/internal/macros.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
oss << "wrong argument size: expected " << expected_size << ", got " \
3131
<< size << "\n"; \
3232
oss << "hint: " << message << std::endl; \
33-
PROXSUITE_THROW_PRETTY(false, std::invalid_argument, oss.str()); \
33+
PROXSUITE_THROW_PRETTY(true, std::invalid_argument, oss.str()); \
3434
}
3535

3636
#if HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) || \

include/proxsuite/proxqp/dense/wrapper.hpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,35 +134,43 @@ struct QP
134134
work.timer.start();
135135
}
136136
// check the model is valid
137-
if (g != std::nullopt) {
137+
if (g != std::nullopt && g.value().size() != 0) {
138138
PROXSUITE_CHECK_ARGUMENT_SIZE(
139139
g.value().rows(),
140140
model.dim,
141141
"the dimension wrt the primal variable x variable for initializing g "
142142
"is not valid.");
143+
} else {
144+
g.reset();
143145
}
144-
if (b != std::nullopt) {
146+
if (b != std::nullopt && b.value().size() != 0) {
145147
PROXSUITE_CHECK_ARGUMENT_SIZE(
146148
b.value().rows(),
147149
model.n_eq,
148150
"the dimension wrt equality constrained variables for initializing b "
149151
"is not valid.");
152+
} else {
153+
b.reset();
150154
}
151-
if (u != std::nullopt) {
155+
if (u != std::nullopt && u.value().size() != 0) {
152156
PROXSUITE_CHECK_ARGUMENT_SIZE(
153157
u.value().rows(),
154158
model.n_in,
155159
"the dimension wrt inequality constrained variables for initializing u "
156160
"is not valid.");
161+
} else {
162+
u.reset();
157163
}
158-
if (l != std::nullopt) {
164+
if (l != std::nullopt && l.value().size() != 0) {
159165
PROXSUITE_CHECK_ARGUMENT_SIZE(
160166
l.value().rows(),
161167
model.n_in,
162168
"the dimension wrt inequality constrained variables for initializing l "
163169
"is not valid.");
170+
} else {
171+
l.reset();
164172
}
165-
if (H != std::nullopt) {
173+
if (H != std::nullopt && H.value().size() != 0) {
166174
PROXSUITE_CHECK_ARGUMENT_SIZE(
167175
H.value().rows(),
168176
model.dim,
@@ -171,8 +179,10 @@ struct QP
171179
H.value().cols(),
172180
model.dim,
173181
"the column dimension for initializing H is not valid.");
182+
} else {
183+
H.reset();
174184
}
175-
if (A != std::nullopt) {
185+
if (A != std::nullopt && A.value().size() != 0) {
176186
PROXSUITE_CHECK_ARGUMENT_SIZE(
177187
A.value().rows(),
178188
model.n_eq,
@@ -181,8 +191,10 @@ struct QP
181191
A.value().cols(),
182192
model.dim,
183193
"the column dimension for initializing A is not valid.");
194+
} else {
195+
A.reset();
184196
}
185-
if (C != std::nullopt) {
197+
if (C != std::nullopt && C.value().size() != 0) {
186198
PROXSUITE_CHECK_ARGUMENT_SIZE(
187199
C.value().rows(),
188200
model.n_in,
@@ -191,8 +203,9 @@ struct QP
191203
C.value().cols(),
192204
model.dim,
193205
"the column dimension for initializing C is not valid.");
206+
} else {
207+
C.reset();
194208
}
195-
196209
if (settings.initial_guess ==
197210
InitialGuessStatus::WARM_START_WITH_PREVIOUS_RESULT) {
198211
work.refactorize =

include/proxsuite/proxqp/sparse/wrapper.hpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,35 +178,43 @@ struct QP
178178
work.timer.stop();
179179
work.timer.start();
180180
}
181-
if (g != std::nullopt) {
181+
if (g != std::nullopt && g.value().size() != 0) {
182182
PROXSUITE_CHECK_ARGUMENT_SIZE(
183183
g.value().rows(),
184184
model.dim,
185185
"the dimension wrt the primal variable x variable for initializing g "
186186
"is not valid.");
187+
} else {
188+
g.reset();
187189
}
188-
if (b != std::nullopt) {
190+
if (b != std::nullopt && b.value().size() != 0) {
189191
PROXSUITE_CHECK_ARGUMENT_SIZE(
190192
b.value().rows(),
191193
model.n_eq,
192194
"the dimension wrt equality constrained variables for initializing b "
193195
"is not valid.");
196+
} else {
197+
b.reset();
194198
}
195-
if (u != std::nullopt) {
199+
if (u != std::nullopt && u.value().size() != 0) {
196200
PROXSUITE_CHECK_ARGUMENT_SIZE(
197201
u.value().rows(),
198202
model.n_in,
199203
"the dimension wrt inequality constrained variables for initializing u "
200204
"is not valid.");
205+
} else {
206+
u.reset();
201207
}
202-
if (l != std::nullopt) {
208+
if (l != std::nullopt && l.value().size() != 0) {
203209
PROXSUITE_CHECK_ARGUMENT_SIZE(
204210
l.value().rows(),
205211
model.n_in,
206212
"the dimension wrt inequality constrained variables for initializing l "
207213
"is not valid.");
214+
} else {
215+
l.reset();
208216
}
209-
if (H != std::nullopt) {
217+
if (H != std::nullopt && H.value().size() != 0) {
210218
PROXSUITE_CHECK_ARGUMENT_SIZE(
211219
H.value().rows(),
212220
model.dim,
@@ -215,8 +223,10 @@ struct QP
215223
H.value().cols(),
216224
model.dim,
217225
"the column dimension for initializing H is not valid.");
226+
} else {
227+
H.reset();
218228
}
219-
if (A != std::nullopt) {
229+
if (A != std::nullopt && A.value().size() != 0) {
220230
PROXSUITE_CHECK_ARGUMENT_SIZE(
221231
A.value().rows(),
222232
model.n_eq,
@@ -225,8 +235,10 @@ struct QP
225235
A.value().cols(),
226236
model.dim,
227237
"the column dimension for initializing A is not valid.");
238+
} else {
239+
A.reset();
228240
}
229-
if (C != std::nullopt) {
241+
if (C != std::nullopt && C.value().size() != 0) {
230242
PROXSUITE_CHECK_ARGUMENT_SIZE(
231243
C.value().rows(),
232244
model.n_in,
@@ -235,6 +247,8 @@ struct QP
235247
C.value().cols(),
236248
model.dim,
237249
"the column dimension for initializing C is not valid.");
250+
} else {
251+
C.reset();
238252
}
239253
work.internal.proximal_parameter_update = false;
240254
PreconditionerStatus preconditioner_status;

test/src/dense_qp_wrapper.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,152 @@ using T = double;
1212
using namespace proxsuite;
1313
using namespace proxsuite::proxqp;
1414

15+
DOCTEST_TEST_CASE("sparse random strongly convex qp with inequality constraints"
16+
"and empty equality constraints")
17+
{
18+
std::cout << "---testing sparse random strongly convex qp with inequality "
19+
"constraints "
20+
"and empty equality constraints---"
21+
<< std::endl;
22+
double sparsity_factor = 0.15;
23+
T eps_abs = T(1e-9);
24+
utils::rand::set_seed(1);
25+
dense::isize dim = 10;
26+
27+
dense::isize n_eq(0);
28+
dense::isize n_in(dim / 4);
29+
T strong_convexity_factor(1.e-2);
30+
proxqp::dense::Model<T> qp_random = proxqp::utils::dense_strongly_convex_qp(
31+
dim, n_eq, n_in, sparsity_factor, strong_convexity_factor);
32+
33+
dense::QP<T> qp{ dim, n_eq, n_in }; // creating QP object
34+
qp.settings.eps_abs = eps_abs;
35+
qp.settings.eps_rel = 0;
36+
37+
// Testing with empty but properly sized matrix A of size (0, 10)
38+
std::cout << "Solving QP with" << std::endl;
39+
std::cout << "dim: " << dim << std::endl;
40+
std::cout << "n_eq: " << n_eq << std::endl;
41+
std::cout << "n_in: " << n_in << std::endl;
42+
std::cout << "H : " << qp_random.H << std::endl;
43+
std::cout << "g : " << qp_random.g << std::endl;
44+
std::cout << "A : " << qp_random.A << std::endl;
45+
std::cout << "A.cols() : " << qp_random.A.cols() << std::endl;
46+
std::cout << "A.rows() : " << qp_random.A.rows() << std::endl;
47+
std::cout << "b : " << qp_random.b << std::endl;
48+
std::cout << "C : " << qp_random.C << std::endl;
49+
std::cout << "u : " << qp_random.u << std::endl;
50+
std::cout << "l : " << qp_random.l << std::endl;
51+
52+
qp.init(qp_random.H,
53+
qp_random.g,
54+
std::nullopt,
55+
qp_random.b,
56+
qp_random.C,
57+
qp_random.l,
58+
qp_random.u);
59+
qp.solve();
60+
61+
T pri_res = (dense::positive_part(qp_random.C * qp.results.x - qp_random.u) +
62+
dense::negative_part(qp_random.C * qp.results.x - qp_random.l))
63+
.lpNorm<Eigen::Infinity>();
64+
T dua_res = (qp_random.H * qp.results.x + qp_random.g +
65+
qp_random.C.transpose() * qp.results.z)
66+
.lpNorm<Eigen::Infinity>();
67+
DOCTEST_CHECK(pri_res <= eps_abs);
68+
DOCTEST_CHECK(dua_res <= eps_abs);
69+
70+
std::cout << "------using API solving qp with dim: " << dim
71+
<< " neq: " << n_eq << " nin: " << n_in << std::endl;
72+
std::cout << "primal residual: " << pri_res << std::endl;
73+
std::cout << "dual residual: " << dua_res << std::endl;
74+
std::cout << "total number of iteration: " << qp.results.info.iter
75+
<< std::endl;
76+
std::cout << "setup timing " << qp.results.info.setup_time << " solve time "
77+
<< qp.results.info.solve_time << std::endl;
78+
79+
// Testing with empty matrix A of size (0, 0)
80+
qp_random.A = Eigen::MatrixXd();
81+
qp_random.b = Eigen::VectorXd();
82+
83+
dense::QP<T> qp2{ dim, n_eq, n_in }; // creating QP object
84+
qp2.settings.eps_abs = eps_abs;
85+
qp2.settings.eps_rel = 0;
86+
87+
std::cout << "Solving QP with" << std::endl;
88+
std::cout << "dim: " << dim << std::endl;
89+
std::cout << "n_eq: " << n_eq << std::endl;
90+
std::cout << "n_in: " << n_in << std::endl;
91+
std::cout << "H : " << qp_random.H << std::endl;
92+
std::cout << "g : " << qp_random.g << std::endl;
93+
std::cout << "A : " << qp_random.A << std::endl;
94+
std::cout << "A.cols() : " << qp_random.A.cols() << std::endl;
95+
std::cout << "A.rows() : " << qp_random.A.rows() << std::endl;
96+
std::cout << "b : " << qp_random.b << std::endl;
97+
std::cout << "C : " << qp_random.C << std::endl;
98+
std::cout << "u : " << qp_random.u << std::endl;
99+
std::cout << "l : " << qp_random.l << std::endl;
100+
101+
qp2.init(qp_random.H,
102+
qp_random.g,
103+
qp_random.A,
104+
qp_random.b,
105+
qp_random.C,
106+
qp_random.l,
107+
qp_random.u);
108+
qp2.solve();
109+
110+
pri_res = (dense::positive_part(qp_random.C * qp.results.x - qp_random.u) +
111+
dense::negative_part(qp_random.C * qp.results.x - qp_random.l))
112+
.lpNorm<Eigen::Infinity>();
113+
dua_res = (qp_random.H * qp.results.x + qp_random.g +
114+
qp_random.C.transpose() * qp.results.z)
115+
.lpNorm<Eigen::Infinity>();
116+
DOCTEST_CHECK(pri_res <= eps_abs);
117+
DOCTEST_CHECK(dua_res <= eps_abs);
118+
119+
std::cout << "------using API solving qp with dim: " << dim
120+
<< " neq: " << n_eq << " nin: " << n_in << std::endl;
121+
std::cout << "primal residual: " << pri_res << std::endl;
122+
std::cout << "dual residual: " << dua_res << std::endl;
123+
std::cout << "total number of iteration: " << qp.results.info.iter
124+
<< std::endl;
125+
std::cout << "setup timing " << qp.results.info.setup_time << " solve time "
126+
<< qp.results.info.solve_time << std::endl;
127+
128+
// Testing with nullopt
129+
dense::QP<T> qp3{ dim, n_eq, n_in }; // creating QP object
130+
qp3.settings.eps_abs = eps_abs;
131+
qp3.settings.eps_rel = 0;
132+
133+
qp3.init(qp_random.H,
134+
qp_random.g,
135+
std::nullopt,
136+
std::nullopt,
137+
qp_random.C,
138+
qp_random.l,
139+
qp_random.u);
140+
qp3.solve();
141+
142+
pri_res = (dense::positive_part(qp_random.C * qp.results.x - qp_random.u) +
143+
dense::negative_part(qp_random.C * qp.results.x - qp_random.l))
144+
.lpNorm<Eigen::Infinity>();
145+
dua_res = (qp_random.H * qp.results.x + qp_random.g +
146+
qp_random.C.transpose() * qp.results.z)
147+
.lpNorm<Eigen::Infinity>();
148+
DOCTEST_CHECK(pri_res <= eps_abs);
149+
DOCTEST_CHECK(dua_res <= eps_abs);
150+
151+
std::cout << "------using API solving qp with dim: " << dim
152+
<< " neq: " << n_eq << " nin: " << n_in << std::endl;
153+
std::cout << "primal residual: " << pri_res << std::endl;
154+
std::cout << "dual residual: " << dua_res << std::endl;
155+
std::cout << "total number of iteration: " << qp.results.info.iter
156+
<< std::endl;
157+
std::cout << "setup timing " << qp.results.info.setup_time << " solve time "
158+
<< qp.results.info.solve_time << std::endl;
159+
}
160+
15161
DOCTEST_TEST_CASE("sparse random strongly convex qp with equality and "
16162
"inequality constraints: test update H")
17163
{

0 commit comments

Comments
 (0)