21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
26 #include "ortools/pdlp/solve_log.pb.h"
27 #include "ortools/pdlp/solvers.pb.h"
35 using ::testing::AllOf;
36 using ::testing::Each;
37 using ::testing::ElementsAre;
42 using ::testing::SizeIs;
44 TEST(CorrectedDualTest, SimpleLpWithSuboptimalDual) {
45 const int num_threads = 2;
46 const int num_shards = 10;
47 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
49 Eigen::VectorXd primal_solution(4), dual_solution(4);
52 primal_solution << 0, 0, 6, 2.5;
53 dual_solution << -2, 0, 2.375, 1;
55 PrimalDualHybridGradientParams(), sharded_qp, primal_solution,
58 1.0, POINT_TYPE_CURRENT_ITERATE);
60 EXPECT_DOUBLE_EQ(stats.dual_objective(), -36.5);
61 EXPECT_DOUBLE_EQ(stats.corrected_dual_objective(), -36.5);
69 TEST(CorrectedDualTest, SimpleLpWithVariableFarFromBoundAsResiduals) {
70 const int num_threads = 2;
71 const int num_shards = 10;
72 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
74 Eigen::VectorXd primal_solution(4), dual_solution(4);
75 primal_solution << 0, 0, 2, 2.5;
76 dual_solution << -2, 0, 2.375, 1;
77 PrimalDualHybridGradientParams params;
78 params.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
true);
80 params, sharded_qp, primal_solution, dual_solution,
82 1.0, POINT_TYPE_CURRENT_ITERATE);
84 EXPECT_DOUBLE_EQ(stats.dual_objective(), -33.5);
85 EXPECT_DOUBLE_EQ(stats.corrected_dual_objective(), -36.5);
86 EXPECT_DOUBLE_EQ(stats.l_inf_dual_residual(), 0.5);
87 EXPECT_DOUBLE_EQ(stats.l2_dual_residual(), 0.5);
88 EXPECT_DOUBLE_EQ(stats.l_inf_componentwise_dual_residual(), 0.25);
91 TEST(CorrectedDualTest, SimpleLpWithVariableFarFromBoundAsReducedCosts) {
92 const int num_threads = 2;
93 const int num_shards = 10;
94 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
96 Eigen::VectorXd primal_solution(4), dual_solution(4);
97 primal_solution << 0, 0, 2, 2.5;
98 dual_solution << -2, 0, 2.375, 1;
99 PrimalDualHybridGradientParams params;
100 params.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
false);
102 params, sharded_qp, primal_solution, dual_solution,
104 1.0, POINT_TYPE_CURRENT_ITERATE);
106 EXPECT_DOUBLE_EQ(stats.dual_objective(), -36.5);
107 EXPECT_DOUBLE_EQ(stats.corrected_dual_objective(), -36.5);
108 EXPECT_DOUBLE_EQ(stats.l_inf_dual_residual(), 0.0);
109 EXPECT_DOUBLE_EQ(stats.l2_dual_residual(), 0.0);
110 EXPECT_DOUBLE_EQ(stats.l_inf_componentwise_dual_residual(), 0.0);
113 TEST(CorrectedDualObjective, QpSuboptimal) {
114 const int num_threads = 2;
115 const int num_shards = 10;
119 Eigen::VectorXd primal_solution(2), dual_solution(1);
121 primal_solution << -2.0, 2.0;
123 PrimalDualHybridGradientParams(), sharded_qp, primal_solution,
126 1.0, POINT_TYPE_CURRENT_ITERATE);
133 EXPECT_DOUBLE_EQ(stats.corrected_dual_objective(), -28.0);
136 TEST(RandomProjectionsTest, OneRandomProjectionsOfZeroVector) {
137 const int num_threads = 2;
138 const int num_shards = 10;
139 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
141 PointMetadata metadata;
145 EXPECT_THAT(metadata.random_primal_projections(), ElementsAre(0.0));
146 EXPECT_THAT(metadata.random_dual_projections(), ElementsAre(0.0));
149 TEST(RandomProjectionsTest, TwoRandomProjectionsOfVector) {
150 const int num_threads = 2;
151 const int num_shards = 10;
152 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
154 PointMetadata metadata;
158 EXPECT_THAT(metadata.random_primal_projections(), SizeIs(2));
159 EXPECT_THAT(metadata.random_dual_projections(), SizeIs(2));
162 EXPECT_THAT(metadata.random_primal_projections(),
163 Each(AllOf(Ge(-2.0), Le(2.0), Ne(0.0))));
164 EXPECT_THAT(metadata.random_dual_projections(), Each(Eq(0.0)));
168 const int num_threads = 2;
169 const int num_shards = 10;
170 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
172 Eigen::VectorXd primal_solution(4), dual_solution(4);
175 primal_solution << 0.0, -2.0, 6.0, 3.5;
176 dual_solution << 1.0, 0.0, 0.0, -2.0;
180 EXPECT_THAT(
ReducedCosts(PrimalDualHybridGradientParams(), sharded_qp,
181 primal_solution, dual_solution),
182 ElementsAre(0.0, 0.0, 0.0, -3.0));
183 EXPECT_THAT(
ReducedCosts(PrimalDualHybridGradientParams(), sharded_qp,
184 primal_solution, dual_solution,
186 ElementsAre(0.0, 0.0, 0.0, -4.0));
189 TEST(ReducedCostsTest, SimpleLpWithGapResiduals) {
190 const int num_threads = 2;
191 const int num_shards = 10;
192 ShardedQuadraticProgram sharded_qp(
TestLp(), num_threads, num_shards);
194 Eigen::VectorXd primal_solution(4), dual_solution(4);
196 dual_solution << 1.0, 0.0, 0.0, -1.0;
197 PrimalDualHybridGradientParams params_true, params_false;
198 params_true.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
200 params_false.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
210 ReducedCosts(params_true, sharded_qp, primal_solution, dual_solution),
211 ElementsAre(0.0, 0.0, 0.0, 0.0));
213 ReducedCosts(params_false, sharded_qp, primal_solution, dual_solution),
214 ElementsAre(0.0, 0.0, -0.5, -2.0));
218 primal_solution << 0.0, 0.0, 4.0, 3.0;
220 ReducedCosts(params_true, sharded_qp, primal_solution, dual_solution),
221 ElementsAre(0.0, 0.0, -0.5, -2.0));
223 ReducedCosts(params_false, sharded_qp, primal_solution, dual_solution),
224 ElementsAre(0.0, 0.0, -0.5, -2.0));
227 TEST(ReducedCostsTest, SimpleQp) {
228 const int num_threads = 2;
229 const int num_shards = 10;
233 Eigen::VectorXd primal_solution(2), dual_solution(1);
234 primal_solution << 1.0, 2.0;
235 dual_solution << 0.0;
236 PrimalDualHybridGradientParams params_true, params_false;
237 params_true.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
239 params_false.set_handle_some_primal_gradients_on_finite_bounds_as_residuals(
248 ReducedCosts(params_true, sharded_qp, primal_solution, dual_solution),
249 ElementsAre(3.0, 0.0));
251 ReducedCosts(params_false, sharded_qp, primal_solution, dual_solution),
252 ElementsAre(3.0, 1.0));
254 ReducedCosts(params_true, sharded_qp, primal_solution, dual_solution,
256 ElementsAre(0.0, 0.0));
258 ReducedCosts(params_false, sharded_qp, primal_solution, dual_solution,
260 ElementsAre(0.0, 0.0));
264 const auto test_stats = ParseTextOrDie<IterationStats>(R
"pb(
265 convergence_information {
266 candidate_type: POINT_TYPE_CURRENT_ITERATE
267 primal_objective: 1.0
269 convergence_information {
270 candidate_type: POINT_TYPE_AVERAGE_ITERATE
271 primal_objective: 2.0
274 const auto average_info =
276 ASSERT_TRUE(average_info.has_value());
277 EXPECT_EQ(average_info->candidate_type(), POINT_TYPE_AVERAGE_ITERATE);
278 EXPECT_EQ(average_info->primal_objective(), 2.0);
280 const auto current_info =
282 ASSERT_TRUE(current_info.has_value());
283 EXPECT_EQ(current_info->candidate_type(), POINT_TYPE_CURRENT_ITERATE);
284 EXPECT_EQ(current_info->primal_objective(), 1.0);
292 const auto test_stats = ParseTextOrDie<IterationStats>(R
"pb(
293 infeasibility_information {
294 candidate_type: POINT_TYPE_CURRENT_ITERATE
295 primal_ray_linear_objective: 1.0
297 infeasibility_information {
298 candidate_type: POINT_TYPE_AVERAGE_ITERATE
299 primal_ray_linear_objective: 2.0
302 const auto average_info =
304 ASSERT_TRUE(average_info.has_value());
305 EXPECT_EQ(average_info->candidate_type(), POINT_TYPE_AVERAGE_ITERATE);
306 EXPECT_EQ(average_info->primal_ray_linear_objective(), 2.0);
308 const auto current_info =
310 ASSERT_TRUE(current_info.has_value());
311 EXPECT_EQ(current_info->candidate_type(), POINT_TYPE_CURRENT_ITERATE);
312 EXPECT_EQ(current_info->primal_ray_linear_objective(), 1.0);
320 const auto test_stats = ParseTextOrDie<IterationStats>(R
"pb(
322 point_type: POINT_TYPE_CURRENT_ITERATE
323 active_primal_variable_count: 1
326 point_type: POINT_TYPE_AVERAGE_ITERATE
327 active_primal_variable_count: 2
330 const auto average_info =
332 ASSERT_TRUE(average_info.has_value());
333 EXPECT_EQ(average_info->point_type(), POINT_TYPE_AVERAGE_ITERATE);
334 EXPECT_EQ(average_info->active_primal_variable_count(), 2);
336 const auto current_info =
338 ASSERT_TRUE(current_info.has_value());
339 EXPECT_EQ(current_info->point_type(), POINT_TYPE_CURRENT_ITERATE);
340 EXPECT_EQ(current_info->active_primal_variable_count(), 1);
T ParseTextOrDie(const std::string &input)
VectorXd ReducedCosts(const PrimalDualHybridGradientParams ¶ms, const ShardedQuadraticProgram &sharded_qp, const VectorXd &primal_solution, const VectorXd &dual_solution, bool use_zero_primal_objective)
void SetRandomProjections(const ShardedQuadraticProgram &sharded_qp, const Eigen::VectorXd &primal_solution, const Eigen::VectorXd &dual_solution, const std::vector< int > &random_projection_seeds, PointMetadata &metadata)
std::optional< PointMetadata > GetPointMetadata(const IterationStats &stats, const PointType point_type)
ConvergenceInformation ComputeScaledConvergenceInformation(const PrimalDualHybridGradientParams ¶ms, const ShardedQuadraticProgram &sharded_qp, const VectorXd &primal_solution, const VectorXd &dual_solution, const double componentwise_primal_residual_offset, const double componentwise_dual_residual_offset, PointType candidate_type)
std::optional< InfeasibilityInformation > GetInfeasibilityInformation(const IterationStats &stats, PointType candidate_type)
std::optional< ConvergenceInformation > GetConvergenceInformation(const IterationStats &stats, PointType candidate_type)
QuadraticProgram TestLp()
QuadraticProgram TestDiagonalQp1()
TEST(LinearAssignmentTest, NullMatrix)
pdlp::QuadraticProgram SimpleLp()