44#include < iostream>
55#include < doctest.hpp>
66#include < Eigen/Core>
7+ #include < optional>
78#include < proxsuite/proxqp/dense/dense.hpp>
89#include < proxsuite/proxqp/utils/random_qp_problems.hpp>
910#include < proxsuite/proxqp/dense/compute_ECJ.hpp>
@@ -142,4 +143,75 @@ DOCTEST_TEST_CASE("proxqp::dense: test compute backward for b (feasible QP)")
142143 DOCTEST_CHECK (std::abs (dx_db_fd (i, j) - dx_db (i, j)) < 1e-5 );
143144 }
144145 }
146+ }
147+
148+ DOCTEST_TEST_CASE (" proxqp::dense: test compute backward for g (QP with saturating inequality constraints)" )
149+ {
150+ double sparsity_factor = 0.85 ;
151+ T eps_abs = T (1e-9 );
152+ utils::rand::set_seed (1 );
153+ dense::isize dim = 10 ;
154+
155+ dense::isize n_eq (0 ), n_in (2 );
156+ T strong_convexity_factor (1 .e -1 );
157+ proxqp::dense::Model<T> random_qp = proxqp::utils::dense_strongly_convex_qp (
158+ dim, n_eq, n_in, sparsity_factor, strong_convexity_factor);
159+
160+
161+ std::cout << " creating random qp " << std::endl;
162+ Eigen::Matrix<T, 10 , 10 > H = random_qp.H ;
163+ Eigen::Matrix<T, 10 , 1 > g = random_qp.g ;
164+ // Eigen::Matrix<T, 5, 10> A = random_qp.A;
165+ // Eigen::Matrix<T, 5, 1> b = random_qp.b;
166+ Eigen::Matrix<T, 2 , 10 > C = random_qp.C ;
167+ Eigen::Matrix<T, 2 , 1 > l = random_qp.l ;
168+ l (0 ) = 1e3 ;
169+ // Eigen::Matrix<T, 2, 1> u = random_qp.u;
170+
171+ dense::QP<T> qp{ dim, n_eq, n_in }; // creating QP object
172+ qp.settings .eps_abs = eps_abs;
173+ qp.settings .eps_rel = 0 ;
174+
175+ qp.init (H, g, nullopt , nullopt , C, l, nullopt );
176+ std::cout << " solving qp " << std::endl;
177+ qp.solve ();
178+ std::cout << " active ineq " << qp.work .active_inequalities .count () << std::endl;
179+
180+ // Compute dx_dg using backward function
181+ Eigen::VectorXd loss_derivative = Eigen::VectorXd::Zero (dim + n_eq + n_in);
182+ Eigen::MatrixXd dx_dg = Eigen::MatrixXd::Zero (dim, dim);
183+ for (int i = 0 ; i < dim; i++) {
184+ loss_derivative (i) = T (1 );
185+ dense::compute_backward<double >(qp, loss_derivative, 1e-5 , 1e-7 , 1e-7 );
186+ dx_dg.row (i) = qp.model .backward_data .dL_dg ;
187+ loss_derivative (i) = T (0 );
188+ }
189+ std::cout << " dx_dg: " << std::endl << dx_dg << std::endl;
190+
191+ // Compute dx_dg using finite differences
192+ Eigen::MatrixXd dx_dg_fd = Eigen::MatrixXd::Zero (dim, dim);
193+ Eigen::VectorXd g_fd = g;
194+ T eps = 1e-5 ;
195+ for (int i = 0 ; i < g.size (); i++) {
196+ g_fd (i) += eps;
197+ qp.init (H, g_fd, nullopt , nullopt , C, l, nullopt );
198+ qp.solve ();
199+ Eigen::VectorXd x_plus = qp.results .x ;
200+ g_fd (i) = g (i);
201+ g_fd (i) -= eps;
202+ qp.init (H, g_fd, nullopt , nullopt , C, l, nullopt );
203+ qp.solve ();
204+ Eigen::VectorXd x_minus = qp.results .x ;
205+
206+ g_fd (i) = T (0 );
207+ dx_dg_fd.col (i) = (x_plus - x_minus) / (T (2 ) * eps);
208+ }
209+ std::cout << " dx_dg_fd: " << std::endl << dx_dg_fd << std::endl;
210+
211+ // Compare dx_dg_fd with the result from the backward function
212+ for (int i = 0 ; i < dim; i++) {
213+ for (int j = 0 ; j < dim; j++) {
214+ DOCTEST_CHECK (std::abs (dx_dg_fd (i, j) - dx_dg (i, j)) < 1e-5 );
215+ }
216+ }
145217}
0 commit comments