diff --git a/ortools/pdlp/primal_dual_hybrid_gradient.cc b/ortools/pdlp/primal_dual_hybrid_gradient.cc index 28b51b5519..fc19657f6e 100644 --- a/ortools/pdlp/primal_dual_hybrid_gradient.cc +++ b/ortools/pdlp/primal_dual_hybrid_gradient.cc @@ -493,6 +493,7 @@ class PreprocessSolver { // A counter used to trigger writing iteration headers. int log_counter_ = 0; + absl::Time time_of_last_log_ = absl::InfinitePast(); IterationStatsCallback iteration_stats_callback_; }; @@ -1454,7 +1455,11 @@ PreprocessSolver::UpdateIterationStatsAndCheckTermination( last_dual_start_point, stats); } constexpr int kLogEvery = 15; - if (params.verbosity_level() >= 2) { + absl::Time logging_time = absl::Now(); + if (params.verbosity_level() >= 2 && + (params.log_interval_seconds() == 0.0 || + logging_time - time_of_last_log_ >= + absl::Seconds(params.log_interval_seconds()))) { if (log_counter_ == 0) { LogIterationStatsHeader(params.verbosity_level(), params.use_feasibility_polishing()); @@ -1475,9 +1480,10 @@ PreprocessSolver::UpdateIterationStatsAndCheckTermination( original_bound_norms_, POINT_TYPE_CURRENT_ITERATE); } } - } - if (++log_counter_ >= kLogEvery) { - log_counter_ = 0; + time_of_last_log_ = logging_time; + if (++log_counter_ >= kLogEvery) { + log_counter_ = 0; + } } if (iteration_stats_callback_ != nullptr) { iteration_stats_callback_( diff --git a/ortools/pdlp/primal_dual_hybrid_gradient_test.cc b/ortools/pdlp/primal_dual_hybrid_gradient_test.cc index af835cbffe..59e1642898 100644 --- a/ortools/pdlp/primal_dual_hybrid_gradient_test.cc +++ b/ortools/pdlp/primal_dual_hybrid_gradient_test.cc @@ -1917,30 +1917,6 @@ TEST_F(FeasibilityPolishingTest, IterationLimitIncludesFeasibilityPhases) { output.solve_log.iteration_count()); } -// Verifies that the primal and dual solution satisfy the bounds constraints. -// This function uses ASSERTS rather than EXPECTs because it's used with large -// QPs that would spam the logs otherwise. -void VerifyBoundConstraints(const QuadraticProgram& qp, - const VectorXd primal_solution, - const VectorXd dual_solution) { - for (int64_t i = 0; i < primal_solution.size(); ++i) { - ASSERT_TRUE(std::isfinite(primal_solution[i])) - << i << " " << primal_solution[i]; - ASSERT_GE(primal_solution[i], qp.variable_lower_bounds[i]); - ASSERT_LE(primal_solution[i], qp.variable_upper_bounds[i]); - } - for (int i = 0; i < dual_solution.size(); ++i) { - ASSERT_TRUE(std::isfinite(dual_solution[i])) - << i << " " << dual_solution[i]; - if (!std::isfinite(qp.constraint_lower_bounds[i])) { - ASSERT_LE(dual_solution[i], 0); - } - if (!std::isfinite(qp.constraint_upper_bounds[i])) { - ASSERT_GE(dual_solution[i], 0); - } - } -} - // This test doesn't attempt to check what is logged. Rather, it just verifies // that the code succeeds at each verbosity level. Other than having the // verbosity level as a parameter, and fixing the other parameters, it is the