export pdlp from google3
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "ortools/base/check.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/mathutil.h"
|
||||
#include "ortools/base/timer.h"
|
||||
@@ -175,8 +176,8 @@ std::string ToString(const IterationStats& iter_stats,
|
||||
if (convergence_information.has_value()) {
|
||||
const RelativeConvergenceInformation relative_information =
|
||||
ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(termination_criteria), bound_norms,
|
||||
*convergence_information);
|
||||
EffectiveOptimalityCriteria(termination_criteria),
|
||||
*convergence_information, bound_norms);
|
||||
return absl::StrCat(iteration_string, " | ",
|
||||
ToString(*convergence_information, relative_information,
|
||||
termination_criteria.optimality_norm()));
|
||||
@@ -200,8 +201,8 @@ std::string ToShortString(const IterationStats& iter_stats,
|
||||
if (convergence_information.has_value()) {
|
||||
const RelativeConvergenceInformation relative_information =
|
||||
ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(termination_criteria), bound_norms,
|
||||
*convergence_information);
|
||||
EffectiveOptimalityCriteria(termination_criteria),
|
||||
*convergence_information, bound_norms);
|
||||
return absl::StrCat(
|
||||
iteration_string, " | ",
|
||||
ToShortString(*convergence_information, relative_information,
|
||||
|
||||
@@ -150,7 +150,6 @@ TEST(SharderTest, UniformSharderExcessiveShards) {
|
||||
VerifySharder(sharder, 7, {1, 1, 1, 1, 1});
|
||||
}
|
||||
|
||||
// Regression test for b/225385276
|
||||
TEST(SharderTest, UniformSharderHugeNumShards) {
|
||||
Sharder sharder(/*num_elements=*/5, /*num_shards=*/1'000'000'000, nullptr);
|
||||
EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 1, 2, 3, 4, 5));
|
||||
|
||||
@@ -36,6 +36,7 @@ absl::Status CheckNonNegative(const double value,
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status ValidateTerminationCriteria(const TerminationCriteria& criteria) {
|
||||
if (criteria.optimality_norm() != OPTIMALITY_NORM_L_INF &&
|
||||
criteria.optimality_norm() != OPTIMALITY_NORM_L2) {
|
||||
@@ -100,11 +101,13 @@ absl::Status ValidateTerminationCriteria(const TerminationCriteria& criteria) {
|
||||
CheckNonNegative(criteria.eps_dual_infeasible(), "eps_dual_infeasible"));
|
||||
RETURN_IF_ERROR(
|
||||
CheckNonNegative(criteria.time_sec_limit(), "time_sec_limit"));
|
||||
|
||||
if (criteria.iteration_limit() < 0) {
|
||||
return InvalidArgumentError("iteration_limit must be non-negative");
|
||||
}
|
||||
RETURN_IF_ERROR(CheckNonNegative(criteria.kkt_matrix_pass_limit(),
|
||||
"kkt_matrix_pass_limit"));
|
||||
|
||||
return OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace operations_research::pdlp {
|
||||
namespace {
|
||||
|
||||
using ::google::protobuf::util::ParseTextOrDie;
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
TEST(ValidateTerminationCriteria, DefaultIsValid) {
|
||||
|
||||
@@ -24,18 +24,27 @@
|
||||
|
||||
namespace operations_research::pdlp {
|
||||
|
||||
namespace {
|
||||
|
||||
bool OptimalityCriteriaMet(
|
||||
const OptimalityNorm optimality_norm,
|
||||
bool ObjectiveGapMet(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const ConvergenceInformation& stats,
|
||||
const QuadraticProgramBoundNorms& bound_norms) {
|
||||
const ConvergenceInformation& stats) {
|
||||
if (std::isinf(optimality_criteria.eps_optimal_objective_gap_absolute()) ||
|
||||
std::isinf(optimality_criteria.eps_optimal_objective_gap_relative())) {
|
||||
return true;
|
||||
}
|
||||
const double abs_obj =
|
||||
std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
|
||||
const double gap =
|
||||
std::abs(stats.primal_objective() - stats.dual_objective());
|
||||
return std::isfinite(abs_obj) &&
|
||||
gap <= optimality_criteria.eps_optimal_objective_gap_absolute() +
|
||||
optimality_criteria.eps_optimal_objective_gap_relative() *
|
||||
abs_obj;
|
||||
}
|
||||
|
||||
bool OptimalityCriteriaMet(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const ConvergenceInformation& stats, const OptimalityNorm optimality_norm,
|
||||
const QuadraticProgramBoundNorms& bound_norms) {
|
||||
double primal_err;
|
||||
double primal_err_baseline;
|
||||
double dual_err;
|
||||
@@ -59,20 +68,25 @@ bool OptimalityCriteriaMet(
|
||||
<< OptimalityNorm_Name(optimality_norm);
|
||||
}
|
||||
|
||||
return dual_err <=
|
||||
optimality_criteria.eps_optimal_dual_residual_absolute() +
|
||||
optimality_criteria.eps_optimal_dual_residual_relative() *
|
||||
dual_err_baseline &&
|
||||
primal_err <=
|
||||
optimality_criteria.eps_optimal_primal_residual_absolute() +
|
||||
optimality_criteria.eps_optimal_primal_residual_relative() *
|
||||
primal_err_baseline &&
|
||||
std::isfinite(abs_obj) &&
|
||||
gap <= optimality_criteria.eps_optimal_objective_gap_absolute() +
|
||||
optimality_criteria.eps_optimal_objective_gap_relative() *
|
||||
abs_obj;
|
||||
const bool primal_err_ok =
|
||||
std::isinf(optimality_criteria.eps_optimal_primal_residual_absolute()) ||
|
||||
std::isinf(optimality_criteria.eps_optimal_primal_residual_relative()) ||
|
||||
primal_err <=
|
||||
optimality_criteria.eps_optimal_primal_residual_absolute() +
|
||||
optimality_criteria.eps_optimal_primal_residual_relative() *
|
||||
primal_err_baseline;
|
||||
const bool dual_err_ok =
|
||||
std::isinf(optimality_criteria.eps_optimal_dual_residual_absolute()) ||
|
||||
std::isinf(optimality_criteria.eps_optimal_dual_residual_relative()) ||
|
||||
dual_err <= optimality_criteria.eps_optimal_dual_residual_absolute() +
|
||||
optimality_criteria.eps_optimal_dual_residual_relative() *
|
||||
dual_err_baseline;
|
||||
return primal_err_ok && dual_err_ok &&
|
||||
ObjectiveGapMet(optimality_criteria, stats);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Checks if the criteria for primal infeasibility are approximately
|
||||
// satisfied; see https://developers.google.com/optimization/lp/pdlp_math for
|
||||
// more details.
|
||||
@@ -97,6 +111,7 @@ bool DualInfeasibilityCriteriaMet(double eps_dual_infeasible,
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TerminationCriteria::DetailedOptimalityCriteria EffectiveOptimalityCriteria(
|
||||
const TerminationCriteria& termination_criteria) {
|
||||
if (termination_criteria.has_detailed_optimality_criteria()) {
|
||||
@@ -165,8 +180,8 @@ std::optional<TerminationReasonAndPointType> CheckTerminationCriteria(
|
||||
TerminationCriteria::DetailedOptimalityCriteria optimality_criteria =
|
||||
EffectiveOptimalityCriteria(criteria);
|
||||
for (const auto& convergence_stats : stats.convergence_information()) {
|
||||
if (OptimalityCriteriaMet(criteria.optimality_norm(), optimality_criteria,
|
||||
convergence_stats, bound_norms)) {
|
||||
if (OptimalityCriteriaMet(optimality_criteria, convergence_stats,
|
||||
criteria.optimality_norm(), bound_norms)) {
|
||||
return TerminationReasonAndPointType{
|
||||
.reason = TERMINATION_REASON_OPTIMAL,
|
||||
.type = convergence_stats.candidate_type()};
|
||||
@@ -209,11 +224,14 @@ QuadraticProgramBoundNorms BoundNormsFromProblemStats(
|
||||
|
||||
RelativeConvergenceInformation ComputeRelativeResiduals(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const QuadraticProgramBoundNorms& norms,
|
||||
const ConvergenceInformation& stats) {
|
||||
const ConvergenceInformation& stats,
|
||||
const QuadraticProgramBoundNorms& bound_norms) {
|
||||
auto eps_ratio = [](const double absolute, const double relative) {
|
||||
return relative == 0.0 ? std::numeric_limits<double>::infinity()
|
||||
: absolute / relative;
|
||||
// The both-infinite case avoids a NAN.
|
||||
return (std::isinf(absolute) && std::isinf(relative))
|
||||
? 1.0
|
||||
: (relative == 0.0 ? std::numeric_limits<double>::infinity()
|
||||
: absolute / relative);
|
||||
};
|
||||
const double eps_ratio_primal =
|
||||
eps_ratio(optimality_criteria.eps_optimal_primal_residual_absolute(),
|
||||
@@ -227,16 +245,16 @@ RelativeConvergenceInformation ComputeRelativeResiduals(
|
||||
RelativeConvergenceInformation info;
|
||||
info.relative_l_inf_primal_residual =
|
||||
stats.l_inf_primal_residual() /
|
||||
(eps_ratio_primal + norms.l_inf_norm_constraint_bounds);
|
||||
(eps_ratio_primal + bound_norms.l_inf_norm_constraint_bounds);
|
||||
info.relative_l2_primal_residual =
|
||||
stats.l2_primal_residual() /
|
||||
(eps_ratio_primal + norms.l2_norm_constraint_bounds);
|
||||
(eps_ratio_primal + bound_norms.l2_norm_constraint_bounds);
|
||||
info.relative_l_inf_dual_residual =
|
||||
stats.l_inf_dual_residual() /
|
||||
(eps_ratio_dual + norms.l_inf_norm_primal_linear_objective);
|
||||
(eps_ratio_dual + bound_norms.l_inf_norm_primal_linear_objective);
|
||||
info.relative_l2_dual_residual =
|
||||
stats.l2_dual_residual() /
|
||||
(eps_ratio_dual + norms.l2_norm_primal_linear_objective);
|
||||
(eps_ratio_dual + bound_norms.l2_norm_primal_linear_objective);
|
||||
const double abs_obj =
|
||||
std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
|
||||
const double gap = stats.primal_objective() - stats.dual_objective();
|
||||
|
||||
@@ -44,6 +44,7 @@ TerminationCriteria::DetailedOptimalityCriteria EffectiveOptimalityCriteria(
|
||||
// unit tests where no TerminationCriteria is naturally available.
|
||||
TerminationCriteria::DetailedOptimalityCriteria EffectiveOptimalityCriteria(
|
||||
const TerminationCriteria::SimpleOptimalityCriteria& simple_criteria);
|
||||
|
||||
// Checks if any of the simple termination criteria are satisfied by `stats`,
|
||||
// and returns a termination reason if so, and nullopt otherwise. The "simple"
|
||||
// termination criteria are `time_sec_limit`, `iteration_limit`,
|
||||
@@ -108,9 +109,19 @@ struct RelativeConvergenceInformation {
|
||||
|
||||
RelativeConvergenceInformation ComputeRelativeResiduals(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const QuadraticProgramBoundNorms& norms,
|
||||
const ConvergenceInformation& stats,
|
||||
const QuadraticProgramBoundNorms& bound_norms);
|
||||
|
||||
bool ObjectiveGapMet(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const ConvergenceInformation& stats);
|
||||
|
||||
// Determines if the optimality criteria are met.
|
||||
bool OptimalityCriteriaMet(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
|
||||
const ConvergenceInformation& stats, const OptimalityNorm optimality_norm,
|
||||
const QuadraticProgramBoundNorms& bound_norms);
|
||||
|
||||
} // namespace operations_research::pdlp
|
||||
|
||||
#endif // PDLP_TERMINATION_H_
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
@@ -24,10 +25,8 @@
|
||||
#include "ortools/pdlp/solvers.pb.h"
|
||||
|
||||
namespace operations_research::pdlp {
|
||||
|
||||
bool operator==(
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& lhs,
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& rhs) {
|
||||
bool operator==(const TerminationCriteria::DetailedOptimalityCriteria& lhs,
|
||||
const TerminationCriteria::DetailedOptimalityCriteria& rhs) {
|
||||
if (lhs.eps_optimal_primal_residual_absolute() !=
|
||||
rhs.eps_optimal_primal_residual_absolute()) {
|
||||
return false;
|
||||
@@ -69,6 +68,13 @@ QuadraticProgramBoundNorms TestLpBoundNorms() {
|
||||
.l_inf_norm_constraint_bounds = 12.0};
|
||||
}
|
||||
|
||||
QuadraticProgramBoundNorms ZeroLpBoundNorms() {
|
||||
return {.l2_norm_primal_linear_objective = 0.0,
|
||||
.l2_norm_constraint_bounds = 0.0,
|
||||
.l_inf_norm_primal_linear_objective = 0.0,
|
||||
.l_inf_norm_constraint_bounds = 0.0};
|
||||
}
|
||||
|
||||
class SimpleTerminationTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
@@ -99,6 +105,7 @@ class TerminationTest : public testing::TestWithParam<OptimalityNorm> {
|
||||
|
||||
TerminationCriteria test_criteria_;
|
||||
};
|
||||
|
||||
class DetailedRelativeTerminationTest
|
||||
: public testing::TestWithParam<OptimalityNorm> {
|
||||
protected:
|
||||
@@ -143,15 +150,17 @@ TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaOverload) {
|
||||
const auto criteria =
|
||||
ParseTextOrDie<TerminationCriteria::SimpleOptimalityCriteria>(
|
||||
R"pb(eps_optimal_absolute: 1.0e-4 eps_optimal_relative: 2.0e-4)pb");
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaInput) {
|
||||
@@ -160,15 +169,17 @@ TEST(EffectiveOptimalityCriteriaTest, SimpleOptimalityCriteriaInput) {
|
||||
eps_optimal_absolute: 1.0e-4
|
||||
eps_optimal_relative: 2.0e-4
|
||||
})pb");
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, DetailedOptimalityCriteriaInput) {
|
||||
@@ -182,21 +193,23 @@ TEST(EffectiveOptimalityCriteriaTest, DetailedOptimalityCriteriaInput) {
|
||||
eps_optimal_objective_gap_relative: 6.0e-4
|
||||
})pb");
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
Eq(criteria.detailed_optimality_criteria()));
|
||||
Eq(criteria.detailed_optimality_criteria()));
|
||||
}
|
||||
|
||||
TEST(EffectiveOptimalityCriteriaTest, DeprecatedInput) {
|
||||
const auto criteria = ParseTextOrDie<TerminationCriteria>(
|
||||
R"pb(eps_optimal_absolute: 1.0e-4 eps_optimal_relative: 2.0e-4)pb");
|
||||
EXPECT_THAT(EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
EXPECT_THAT(
|
||||
EffectiveOptimalityCriteria(criteria),
|
||||
Eq(ParseTextOrDie<TerminationCriteria::DetailedOptimalityCriteria>(
|
||||
R"pb(
|
||||
eps_optimal_primal_residual_absolute: 1.0e-4
|
||||
eps_optimal_primal_residual_relative: 2.0e-4
|
||||
eps_optimal_dual_residual_absolute: 1.0e-4
|
||||
eps_optimal_dual_residual_relative: 2.0e-4
|
||||
eps_optimal_objective_gap_absolute: 1.0e-4
|
||||
eps_optimal_objective_gap_relative: 2.0e-4
|
||||
)pb")));
|
||||
}
|
||||
|
||||
TEST_P(DetailedRelativeTerminationTest, TerminationWithNearOptimal) {
|
||||
@@ -210,6 +223,12 @@ TEST_P(DetailedRelativeTerminationTest, TerminationWithNearOptimal) {
|
||||
l2_dual_residual: 6e-4
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_TRUE(ObjectiveGapMet(test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0)));
|
||||
EXPECT_TRUE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_THAT(
|
||||
CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
Optional(
|
||||
@@ -227,6 +246,12 @@ TEST_P(DetailedRelativeTerminationTest, NoTerminationWithExcessiveGap) {
|
||||
l2_dual_residual: 6e-4
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(ObjectiveGapMet(test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0)));
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -243,6 +268,10 @@ TEST_P(DetailedRelativeTerminationTest,
|
||||
l2_dual_residual: 6e-4
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -259,6 +288,10 @@ TEST_P(DetailedRelativeTerminationTest,
|
||||
l2_dual_residual: 7e-4
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -274,6 +307,12 @@ TEST_P(DetailedAbsoluteTerminationTest, TerminationWithNearOptimal) {
|
||||
l2_dual_residual: 9e-5
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_TRUE(ObjectiveGapMet(test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0)));
|
||||
EXPECT_TRUE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_THAT(
|
||||
CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
Optional(
|
||||
@@ -291,6 +330,12 @@ TEST_P(DetailedAbsoluteTerminationTest, NoTerminationWithExcessiveGap) {
|
||||
l2_dual_residual: 9e-5
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(ObjectiveGapMet(test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0)));
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -307,6 +352,10 @@ TEST_P(DetailedAbsoluteTerminationTest,
|
||||
l2_dual_residual: 9e-5
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -323,6 +372,10 @@ TEST_P(DetailedAbsoluteTerminationTest,
|
||||
l2_dual_residual: 11e-5
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
EXPECT_FALSE(OptimalityCriteriaMet(
|
||||
test_criteria_.detailed_optimality_criteria(),
|
||||
stats.convergence_information(0), test_criteria_.optimality_norm(),
|
||||
TestLpBoundNorms()));
|
||||
EXPECT_EQ(CheckTerminationCriteria(test_criteria_, stats, TestLpBoundNorms()),
|
||||
absl::nullopt);
|
||||
}
|
||||
@@ -540,6 +593,70 @@ TEST_P(TerminationTest, TerminationWithNearOptimal) {
|
||||
POINT_TYPE_CURRENT_ITERATE)));
|
||||
}
|
||||
|
||||
TEST_P(TerminationTest, TerminationWithNonOptimalInfiniteAbsoluteTolerances) {
|
||||
const auto stats = ParseTextOrDie<IterationStats>(R"pb(
|
||||
convergence_information {
|
||||
primal_objective: 1.0
|
||||
dual_objective: 1.0
|
||||
l_inf_primal_residual: 1.0
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_primal_residual: 1.0
|
||||
l2_dual_residual: 1.0
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_absolute(
|
||||
std::numeric_limits<double>::infinity());
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_relative(
|
||||
0);
|
||||
std::optional<TerminationReasonAndPointType> maybe_result =
|
||||
CheckTerminationCriteria(test_criteria_, stats, ZeroLpBoundNorms());
|
||||
EXPECT_THAT(maybe_result, Optional(FieldsAre(TERMINATION_REASON_OPTIMAL,
|
||||
POINT_TYPE_CURRENT_ITERATE)));
|
||||
}
|
||||
|
||||
TEST_P(TerminationTest, TerminationWithNonOptimalInfiniteRelativeTolerances) {
|
||||
const auto stats = ParseTextOrDie<IterationStats>(R"pb(
|
||||
convergence_information {
|
||||
primal_objective: 0.0
|
||||
dual_objective: 0.0
|
||||
l_inf_primal_residual: 1.0
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_primal_residual: 1.0
|
||||
l2_dual_residual: 1.0
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_absolute(
|
||||
0);
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_relative(
|
||||
std::numeric_limits<double>::infinity());
|
||||
std::optional<TerminationReasonAndPointType> maybe_result =
|
||||
CheckTerminationCriteria(test_criteria_, stats, ZeroLpBoundNorms());
|
||||
EXPECT_THAT(maybe_result, Optional(FieldsAre(TERMINATION_REASON_OPTIMAL,
|
||||
POINT_TYPE_CURRENT_ITERATE)));
|
||||
}
|
||||
|
||||
TEST_P(TerminationTest,
|
||||
TerminationWithNonOptimalInfiniteAbsoluteAndRelativeTolerances) {
|
||||
const auto stats = ParseTextOrDie<IterationStats>(R"pb(
|
||||
convergence_information {
|
||||
primal_objective: 1.0
|
||||
dual_objective: 1.0
|
||||
l_inf_primal_residual: 1.0
|
||||
l_inf_dual_residual: 1.0
|
||||
l2_primal_residual: 1.0
|
||||
l2_dual_residual: 1.0
|
||||
candidate_type: POINT_TYPE_CURRENT_ITERATE
|
||||
})pb");
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_absolute(
|
||||
std::numeric_limits<double>::infinity());
|
||||
test_criteria_.mutable_simple_optimality_criteria()->set_eps_optimal_relative(
|
||||
std::numeric_limits<double>::infinity());
|
||||
std::optional<TerminationReasonAndPointType> maybe_result =
|
||||
CheckTerminationCriteria(test_criteria_, stats, ZeroLpBoundNorms());
|
||||
EXPECT_THAT(maybe_result, Optional(FieldsAre(TERMINATION_REASON_OPTIMAL,
|
||||
POINT_TYPE_CURRENT_ITERATE)));
|
||||
}
|
||||
|
||||
TEST_P(TerminationTest, OptimalEvenWithNumericalError) {
|
||||
const auto stats = ParseTextOrDie<IterationStats>(R"pb(
|
||||
convergence_information {
|
||||
@@ -766,8 +883,8 @@ TEST(ComputeRelativeResiduals,
|
||||
termination_criteria.mutable_simple_optimality_criteria()
|
||||
->set_eps_optimal_relative(1.0e-6);
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(termination_criteria), TestLpBoundNorms(),
|
||||
stats);
|
||||
EffectiveOptimalityCriteria(termination_criteria), stats,
|
||||
TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 1.0 / 12.0);
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual, 1.0 / std::sqrt(210.0));
|
||||
@@ -797,7 +914,7 @@ TEST(ComputeRelativeResiduals,
|
||||
opt_criteria.set_eps_optimal_absolute(0.0);
|
||||
opt_criteria.set_eps_optimal_relative(0.0);
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(opt_criteria), TestLpBoundNorms(), stats);
|
||||
EffectiveOptimalityCriteria(opt_criteria), stats, TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 0.0);
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual, 0.0);
|
||||
@@ -822,7 +939,7 @@ TEST(ComputeRelativeResiduals,
|
||||
opt_criteria.set_eps_optimal_absolute(1.0e-6);
|
||||
opt_criteria.set_eps_optimal_relative(1.0e-6);
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(opt_criteria), TestLpBoundNorms(), stats);
|
||||
EffectiveOptimalityCriteria(opt_criteria), stats, TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 1.0 / (1.0 + 12.0));
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual,
|
||||
@@ -856,7 +973,7 @@ TEST(ComputeRelativeResiduals,
|
||||
opt_criteria.set_eps_optimal_objective_gap_absolute(3.0e-8);
|
||||
opt_criteria.set_eps_optimal_objective_gap_relative(3.0e-7);
|
||||
const RelativeConvergenceInformation relative_info =
|
||||
ComputeRelativeResiduals(opt_criteria, TestLpBoundNorms(), stats);
|
||||
ComputeRelativeResiduals(opt_criteria, stats, TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 1.0 / (0.01 + 12.0));
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual,
|
||||
@@ -871,5 +988,88 @@ TEST(ComputeRelativeResiduals,
|
||||
EXPECT_EQ(relative_info.relative_optimality_gap, 5.0 / (0.1 + 15.0));
|
||||
}
|
||||
|
||||
TEST(ComputeRelativeResiduals,
|
||||
ComputesCorrectRelativeResidualsForInfiniteAbsoluteTolerances) {
|
||||
ConvergenceInformation stats;
|
||||
stats.set_primal_objective(10.0);
|
||||
stats.set_dual_objective(5.0);
|
||||
stats.set_l_inf_primal_residual(1.0);
|
||||
stats.set_l2_primal_residual(1.0);
|
||||
stats.set_l_inf_dual_residual(1.0);
|
||||
stats.set_l2_dual_residual(1.0);
|
||||
TerminationCriteria::SimpleOptimalityCriteria opt_criteria;
|
||||
opt_criteria.set_eps_optimal_absolute(
|
||||
std::numeric_limits<double>::infinity());
|
||||
opt_criteria.set_eps_optimal_relative(1.0e-6);
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(opt_criteria), stats, TestLpBoundNorms());
|
||||
|
||||
// If absolute tolerance is infinite the relative residuals are zero.
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 0.0);
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual, 0.0);
|
||||
EXPECT_EQ(relative_info.relative_l_inf_dual_residual, 0.0);
|
||||
EXPECT_EQ(relative_info.relative_l2_dual_residual, 0.0);
|
||||
EXPECT_EQ(relative_info.relative_optimality_gap, 0.0);
|
||||
}
|
||||
|
||||
TEST(ComputeRelativeResiduals,
|
||||
ComputesCorrectRelativeResidualsForInfiniteRelativeTolerances) {
|
||||
ConvergenceInformation stats;
|
||||
stats.set_primal_objective(10.0);
|
||||
stats.set_dual_objective(5.0);
|
||||
stats.set_l_inf_primal_residual(1.0);
|
||||
stats.set_l2_primal_residual(1.0);
|
||||
stats.set_l_inf_dual_residual(1.0);
|
||||
stats.set_l2_dual_residual(1.0);
|
||||
TerminationCriteria::SimpleOptimalityCriteria opt_criteria;
|
||||
opt_criteria.set_eps_optimal_absolute(1.0e-6);
|
||||
opt_criteria.set_eps_optimal_relative(
|
||||
std::numeric_limits<double>::infinity());
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(opt_criteria), stats, TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 1.0 / 12.0);
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual, 1.0 / std::sqrt(210.0));
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_dual_residual, 1.0 / 5.5);
|
||||
EXPECT_EQ(relative_info.relative_l2_dual_residual, 1.0 / sqrt(36.25));
|
||||
|
||||
// The relative optimality gap should just be the objective difference divided
|
||||
// by the sum of absolute values.
|
||||
EXPECT_EQ(relative_info.relative_optimality_gap, 5.0 / 15.0);
|
||||
}
|
||||
|
||||
TEST(ComputeRelativeResiduals,
|
||||
ComputesCorrectRelativeResidualsForInfiniteAbsoluteAndRelativeTolerances) {
|
||||
ConvergenceInformation stats;
|
||||
// If the absolute error tolerance and relative error tolerance are both
|
||||
// infinity (and nonzero), the relative residuals are the absolute residuals
|
||||
// divided by 1.0 plus the corresponding norms.
|
||||
stats.set_primal_objective(10.0);
|
||||
stats.set_dual_objective(5.0);
|
||||
stats.set_l_inf_primal_residual(1.0);
|
||||
stats.set_l2_primal_residual(1.0);
|
||||
stats.set_l_inf_dual_residual(1.0);
|
||||
stats.set_l2_dual_residual(1.0);
|
||||
TerminationCriteria::SimpleOptimalityCriteria opt_criteria;
|
||||
opt_criteria.set_eps_optimal_absolute(
|
||||
std::numeric_limits<double>::infinity());
|
||||
opt_criteria.set_eps_optimal_relative(
|
||||
std::numeric_limits<double>::infinity());
|
||||
const RelativeConvergenceInformation relative_info = ComputeRelativeResiduals(
|
||||
EffectiveOptimalityCriteria(opt_criteria), stats, TestLpBoundNorms());
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_primal_residual, 1.0 / (1.0 + 12.0));
|
||||
EXPECT_EQ(relative_info.relative_l2_primal_residual,
|
||||
1.0 / (1.0 + std::sqrt(210.0)));
|
||||
|
||||
EXPECT_EQ(relative_info.relative_l_inf_dual_residual, 1.0 / (1.0 + 5.5));
|
||||
EXPECT_EQ(relative_info.relative_l2_dual_residual, 1.0 / (1.0 + sqrt(36.25)));
|
||||
|
||||
// The relative optimality gap should just be the objective difference divided
|
||||
// by 1.0 + the sum of absolute values.
|
||||
EXPECT_EQ(relative_info.relative_optimality_gap, 5.0 / (1.0 + 15.0));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace operations_research::pdlp
|
||||
|
||||
Reference in New Issue
Block a user