Skip to content

Commit d8adc52

Browse files
authored
Adding missing tests for the C++ wrapper (#40)
* Adding missing tests for the C++ wrapper The tests are adapted directly from the rust tests. * Remove unnecessary spacing
1 parent ba7ec3e commit d8adc52

File tree

8 files changed

+668
-0
lines changed

8 files changed

+668
-0
lines changed

tests/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,16 @@ add_executable(clarabel_cpp_tests
2020
api_dimension_checks.cpp
2121
basic_lp.cpp
2222
basic_qp.cpp
23+
basic_eq_constrained.cpp
24+
basic_powcone.cpp
25+
basic_genpowcone.cpp
26+
basic_expcone.cpp
27+
basic_sdp.cpp
28+
basic_socp.cpp
2329
basic_unconstrained.cpp
2430
mixed_conic.cpp
2531
data_updating.cpp
32+
sdp_chordal.cpp
2633
)
2734
target_link_libraries(clarabel_cpp_tests
2835
libclarabel_c_shared

tests/basic_eq_constrained.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#include <Clarabel>
2+
#include <Eigen/Eigen>
3+
#include <cmath>
4+
#include <gtest/gtest.h>
5+
#include <iostream>
6+
#include <limits>
7+
#include <vector>
8+
9+
using namespace std;
10+
using namespace clarabel;
11+
using namespace Eigen;
12+
13+
SparseMatrix<double> eq_constrained_A1() {
14+
// A =
15+
//[ 0. 1. 1.;
16+
// 0. 1. -1.]
17+
int colptr[] = { 0, 0, 2, 4 }; // start index per column + 1 for last col
18+
int rowval[] = { 0, 1, 0, 1 }; // nonzero row indices
19+
double nzval[] = { 1., 1., 1., -1. };
20+
return SparseMatrix<double>::Map(
21+
2 /*rows*/, 3 /*cols*/, 4 /*nonzeros*/, colptr, rowval, nzval);
22+
}
23+
24+
SparseMatrix<double> eq_constrained_A2() {
25+
// A = [
26+
// 0 1.0 1.0;
27+
// 0 1.0 -1.0;
28+
//1.0 2.0 -1.0l
29+
//2.0 -1.0 3.0l
30+
//]
31+
int colptr[] = { 0, 2, 6, 10 }; // start index per column + 1 for last col
32+
int rowval[] = { 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; // nonzero row indices
33+
double nzval[] = {1., 2., 1., 1., 2., -1., 1., -1., -1., 3. };
34+
return SparseMatrix<double>::Map(
35+
4 /*rows*/, 3 /*cols*/, 10 /*nonzeros*/, colptr, rowval, nzval);
36+
}
37+
38+
TEST(BasicEqConstrainedTest, Feasible)
39+
{
40+
SparseMatrix<double> P = MatrixXd::Identity(3, 3).sparseView();
41+
P.makeCompressed();
42+
43+
Vector<double, 3> c = { 0., 0., 0. };
44+
45+
SparseMatrix<double> A = eq_constrained_A1();
46+
47+
Vector<double, 2> b = {2., 0.};
48+
49+
vector<SupportedConeT<double>> cones = {ZeroConeT<double>(2)};
50+
51+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
52+
53+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
54+
solver.solve();
55+
56+
DefaultSolution<double> solution = solver.solution();
57+
ASSERT_EQ(solution.status, SolverStatus::Solved);
58+
59+
// Compare the solution to the reference solution
60+
Vector3d ref_solution{ 0., 1., 1. };
61+
ASSERT_EQ(solution.x.size(), 3);
62+
ASSERT_TRUE(solution.x.isApprox(ref_solution, 1e-6));
63+
}
64+
65+
TEST(BasicEqConstrainedTest, PrimalInfeasible)
66+
{
67+
SparseMatrix<double> P = MatrixXd::Identity(3, 3).sparseView();
68+
P.makeCompressed();
69+
70+
Vector<double, 3> c = { 0., 0., 0. };
71+
72+
SparseMatrix<double> A = eq_constrained_A2();
73+
74+
Vector<double, 4> b = {1., 1., 1., 1.};
75+
76+
vector<SupportedConeT<double>> cones = {ZeroConeT<double>(4)};
77+
78+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
79+
80+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
81+
solver.solve();
82+
83+
DefaultSolution<double> solution = solver.solution();
84+
ASSERT_EQ(solution.status, SolverStatus::PrimalInfeasible);
85+
}
86+
87+
TEST(BasicEqConstrainedTest, DualInfeasible)
88+
{
89+
SparseMatrix<double> P = MatrixXd::Identity(3, 3).sparseView();
90+
P.makeCompressed();
91+
P.coeffRef(0,0) = 0.0;
92+
93+
Vector<double, 3> c = { 1., 1., 1. };
94+
95+
SparseMatrix<double> A = eq_constrained_A1();
96+
97+
Vector<double, 2> b = {2., 0.};
98+
99+
vector<SupportedConeT<double>> cones = {ZeroConeT<double>(2)};
100+
101+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
102+
103+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
104+
solver.solve();
105+
106+
DefaultSolution<double> solution = solver.solution();
107+
ASSERT_EQ(solution.status, SolverStatus::DualInfeasible);
108+
}

tests/basic_expcone.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include <Clarabel>
2+
#include <Eigen/Eigen>
3+
#include <cmath>
4+
#include <gtest/gtest.h>
5+
#include <iostream>
6+
#include <limits>
7+
#include <vector>
8+
9+
using namespace std;
10+
using namespace clarabel;
11+
using namespace Eigen;
12+
13+
class BasicExpConeTest : public testing::Test
14+
{
15+
// produces data for the following exponential cone problem
16+
// max x
17+
// s.t. y * exp(x / y) <= z
18+
// y == 1, z == exp(5)
19+
protected:
20+
SparseMatrix<double> P, A;
21+
Vector<double, 3> c = {-1., 0., 0.};
22+
Vector<double, 5> b = {0., 0., 0., 1.0, exp(5.)};
23+
vector<SupportedConeT<double>> cones = {
24+
ExponentialConeT<double>(),
25+
ZeroConeT<double>(2),
26+
};
27+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
28+
29+
BasicExpConeTest()
30+
{
31+
P = MatrixXd::Zero(3, 3).sparseView();
32+
P.makeCompressed();
33+
34+
MatrixXd A1 = -MatrixXd::Identity(3,3);
35+
MatrixXd A2(2,3);
36+
A2 << 0, 1, 0, // y = 1
37+
0, 0, 1; // z = exp(5)
38+
39+
MatrixXd A_dense = MatrixXd::Zero(5,3);
40+
A_dense << A1, A2;
41+
A = A_dense.sparseView();
42+
A.makeCompressed();
43+
}
44+
};
45+
46+
TEST_F(BasicExpConeTest, Feasible)
47+
{
48+
// solve the following exponential cone problem
49+
// max x
50+
// s.t. y * exp(x / y) <= z
51+
// y == 1, z == exp(5)
52+
//
53+
// This is just the default problem data above
54+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
55+
solver.solve();
56+
57+
DefaultSolution<double> solution = solver.solution();
58+
ASSERT_EQ(solution.status, SolverStatus::Solved);
59+
60+
// Check the solution
61+
Vector3d ref_solution{ 5.0, 1.0, exp(5.0) };
62+
ASSERT_EQ(solution.x.size(), 3);
63+
ASSERT_TRUE(solution.x.isApprox(ref_solution, 1e-6));
64+
65+
double ref_obj = -5.0;
66+
ASSERT_NEAR(solution.obj_val, ref_obj, 1e-6);
67+
ASSERT_NEAR(solution.obj_val_dual, ref_obj, 1e-6);
68+
}
69+
70+
TEST_F(BasicExpConeTest, PrimalInfeasible)
71+
{
72+
// solve the following exponential cone problem
73+
// max x
74+
// s.t. y * exp(x / y) <= z
75+
// y == 1, z == -1
76+
//
77+
// Same as default, but last element of b is different
78+
b[4] = -1.;
79+
80+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
81+
solver.solve();
82+
83+
DefaultSolution<double> solution = solver.solution();
84+
ASSERT_EQ(solution.status, SolverStatus::PrimalInfeasible);
85+
}
86+
87+
88+
TEST_F(BasicExpConeTest, DualInfeasible)
89+
{
90+
// solve the following exponential cone problem
91+
// max x
92+
// s.t. y * exp(x / y) <= z
93+
//
94+
// Same as default, but no equality constraint
95+
96+
Vector<double, 3> b = {0., 0., 0.};
97+
vector<SupportedConeT<double>> cones = {ExponentialConeT<double>()};
98+
MatrixXd A1 = -MatrixXd::Identity(3,3);
99+
SparseMatrix<double> A = A1.sparseView();
100+
A.makeCompressed();
101+
102+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
103+
solver.solve();
104+
105+
DefaultSolution<double> solution = solver.solution();
106+
ASSERT_EQ(solution.status, SolverStatus::DualInfeasible);
107+
}

tests/basic_genpowcone.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <Clarabel>
2+
#include <Eigen/Eigen>
3+
#include <cmath>
4+
#include <gtest/gtest.h>
5+
#include <iostream>
6+
#include <limits>
7+
#include <vector>
8+
9+
using namespace std;
10+
using namespace clarabel;
11+
using namespace Eigen;
12+
13+
TEST(BasicGenPowerConeTest, Feasible)
14+
{
15+
// solve the following power cone problem
16+
// max x1^0.6 y^0.4 + x2^0.1
17+
// s.t. x1, y, x2 >= 0
18+
// x1 + 2y + 3x2 == 3
19+
// which is equivalent to
20+
// max z1 + z2
21+
// s.t. (x1, y, z1) in K_pow(0.6)
22+
// (x2, 1, z2) in K_pow(0.1)
23+
// x1 + 2y + 3x2 == 3
24+
25+
// x = (x1, y, z1, x2, y2, z2)
26+
27+
SparseMatrix<double> P = MatrixXd::Zero(6, 6).sparseView();
28+
P.makeCompressed();
29+
30+
Vector<double, 6> c = { 0., 0., -1., 0., 0., -1. };
31+
32+
// Assembling A
33+
MatrixXd A1_dense = -MatrixXd::Identity(6, 6);
34+
MatrixXd A2_dense(2,6);
35+
A2_dense << 1., 2., 0., 3., 0., 0., //
36+
0., 0., 0., 0., 1., 0.; //
37+
38+
MatrixXd A_dense = MatrixXd::Zero(8,6);
39+
A_dense << A1_dense, A2_dense;
40+
SparseMatrix<double> A = A_dense.sparseView();
41+
A.makeCompressed();
42+
43+
// Assembling b
44+
Vector<double, 8> b = {0., 0., 0., 0., 0., 0., 3., 1.};
45+
// Assembling cones
46+
47+
Vector<double, 2> cone1 = {0.6, 0.4};
48+
Vector<double, 2> cone2 = {0.1, 0.9};
49+
vector<SupportedConeT<double>> cones = {GenPowerConeT<double>(cone1, 1),
50+
GenPowerConeT<double>(cone2, 1),
51+
ZeroConeT<double>(2)};
52+
53+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
54+
55+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
56+
solver.solve();
57+
58+
DefaultSolution<double> solution = solver.solution();
59+
ASSERT_EQ(solution.status, SolverStatus::Solved);
60+
61+
// Compare the solution to the reference solution
62+
double ref_obj = -1.8458;
63+
ASSERT_NEAR(solution.obj_val, ref_obj, 1e-3);
64+
}

tests/basic_powcone.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <Clarabel>
2+
#include <Eigen/Eigen>
3+
#include <cmath>
4+
#include <gtest/gtest.h>
5+
#include <iostream>
6+
#include <limits>
7+
#include <vector>
8+
9+
using namespace std;
10+
using namespace clarabel;
11+
using namespace Eigen;
12+
13+
TEST(BasicPowerConeTest, Feasible)
14+
{
15+
// solve the following power cone problem
16+
// max x1^0.6 y^0.4 + x2^0.1
17+
// s.t. x1, y, x2 >= 0
18+
// x1 + 2y + 3x2 == 3
19+
// which is equivalent to
20+
// max z1 + z2
21+
// s.t. (x1, y, z1) in K_pow(0.6)
22+
// (x2, 1, z2) in K_pow(0.1)
23+
// x1 + 2y + 3x2 == 3
24+
25+
// x = (x1, y, z1, x2, y2, z2)
26+
27+
SparseMatrix<double> P = MatrixXd::Zero(6, 6).sparseView();
28+
P.makeCompressed();
29+
30+
Vector<double, 6> c = { 0., 0., -1., 0., 0., -1. };
31+
32+
// Assembling A
33+
MatrixXd A1_dense = -MatrixXd::Identity(6, 6);
34+
MatrixXd A2_dense(2,6);
35+
A2_dense << 1., 2., 0., 3., 0., 0., //
36+
0., 0., 0., 0., 1., 0.; //
37+
38+
MatrixXd A_dense = MatrixXd::Zero(8,6);
39+
A_dense << A1_dense, A2_dense;
40+
SparseMatrix<double> A = A_dense.sparseView();
41+
A.makeCompressed();
42+
43+
// Assembling b
44+
Vector<double, 8> b = {0., 0., 0., 0., 0., 0., 3., 1.};
45+
// Assembling cones
46+
vector<SupportedConeT<double>> cones = {PowerConeT<double>(0.6),
47+
PowerConeT<double>(0.1),
48+
ZeroConeT<double>(2)};
49+
50+
DefaultSettings<double> settings = DefaultSettings<double>::default_settings();
51+
52+
DefaultSolver<double> solver(P, c, A, b, cones, settings);
53+
solver.solve();
54+
55+
DefaultSolution<double> solution = solver.solution();
56+
ASSERT_EQ(solution.status, SolverStatus::Solved);
57+
58+
// Compare the solution to the reference solution
59+
double ref_obj = -1.8458;
60+
ASSERT_NEAR(solution.obj_val, ref_obj, 1e-3);
61+
}

0 commit comments

Comments
 (0)