diff --git a/ortools/linear_solver/java/LinearSolverTest.java b/ortools/linear_solver/java/LinearSolverTest.java index d1afd0683d8..92bd14201be 100644 --- a/ortools/linear_solver/java/LinearSolverTest.java +++ b/ortools/linear_solver/java/LinearSolverTest.java @@ -53,8 +53,6 @@ private void runBasicCtor(MPSolver.OptimizationProblemType solverType) { @Test public void testMPSolver_basicCtor() { runBasicCtor(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING); - runBasicCtor(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); - runBasicCtor(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); runBasicCtor(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); runBasicCtor(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); } @@ -129,10 +127,12 @@ private void runLinearSolver( public void testMPSolver_linearSolver() { runLinearSolver(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING, false); runLinearSolver(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING, false); - runLinearSolver(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING, false); + // TODO(b/312134897): Uncomment this. + // runLinearSolver(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING, false); runLinearSolver(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING, true); - runLinearSolver(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING, true); + // TODO(b/312134897): Uncomment this. + // runLinearSolver(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING, true); runLinearSolver(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING, true); } @@ -224,7 +224,8 @@ private void runFirstLinearExample(MPSolver.OptimizationProblemType problemType) public void testMPSolver_firstLinearExample() { runFirstLinearExample(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING); runFirstLinearExample(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); - runFirstLinearExample(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runFirstLinearExample(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); runFirstLinearExample(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING); } @@ -266,7 +267,8 @@ private void runFirstMIPExample(MPSolver.OptimizationProblemType problemType) { public void testMPSolver_firstMIPExample() { runFirstMIPExample(MPSolver.OptimizationProblemType.BOP_INTEGER_PROGRAMMING); runFirstMIPExample(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); - runFirstMIPExample(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runFirstMIPExample(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); runFirstMIPExample(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING); runFirstMIPExample(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING); runFirstMIPExample(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING); @@ -318,11 +320,13 @@ private void runSuccessiveObjectives(MPSolver.OptimizationProblemType problemTyp public void testMPSolver_successiveObjectives() { runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); - runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); - runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runSuccessiveObjectives(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING); runSuccessiveObjectives(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING); @@ -375,11 +379,13 @@ private void runObjectiveOffset(MPSolver.OptimizationProblemType problemType) { public void testMPSolver_objectiveOffset() { runObjectiveOffset(MPSolver.OptimizationProblemType.GLOP_LINEAR_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.CLP_LINEAR_PROGRAMMING); - runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_LINEAR_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.GUROBI_LINEAR_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING); - runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); + // TODO(b/312134897): Uncomment this. + // runObjectiveOffset(MPSolver.OptimizationProblemType.GLPK_MIXED_INTEGER_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.SCIP_MIXED_INTEGER_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.SAT_INTEGER_PROGRAMMING); runObjectiveOffset(MPSolver.OptimizationProblemType.GUROBI_MIXED_INTEGER_PROGRAMMING); diff --git a/ortools/linear_solver/python/py.typed b/ortools/linear_solver/python/py.typed new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ortools/pdlp/primal_dual_hybrid_gradient.cc b/ortools/pdlp/primal_dual_hybrid_gradient.cc index b872aebfd15..4f28078531d 100644 --- a/ortools/pdlp/primal_dual_hybrid_gradient.cc +++ b/ortools/pdlp/primal_dual_hybrid_gradient.cc @@ -1459,7 +1459,9 @@ PreprocessSolver::UpdateIterationStatsAndCheckTermination( POINT_TYPE_AVERAGE_ITERATE, last_primal_start_point, last_dual_start_point, stats); } - if (working_primal_delta != nullptr && working_dual_delta != nullptr) { + // Undoing presolve doesn't work for iterate differences. + if (!presolve_info_.has_value() && working_primal_delta != nullptr && + working_dual_delta != nullptr) { ComputeConvergenceAndInfeasibilityFromWorkingSolution( params, *working_primal_delta, *working_dual_delta, POINT_TYPE_ITERATE_DIFFERENCE, nullptr, @@ -1531,6 +1533,9 @@ void PreprocessSolver::ComputeConvergenceAndInfeasibilityFromWorkingSolution( EpsilonRatio(criteria.eps_optimal_dual_residual_absolute(), criteria.eps_optimal_dual_residual_relative()); if (presolve_info_.has_value()) { + // Undoing presolve doesn't make sense for iterate differences. + CHECK_NE(candidate_type, POINT_TYPE_ITERATE_DIFFERENCE); + PrimalAndDualSolution original = RecoverOriginalSolution( {.primal_solution = working_primal, .dual_solution = working_dual}); if (convergence_information != nullptr) { diff --git a/ortools/pdlp/primal_dual_hybrid_gradient_test.cc b/ortools/pdlp/primal_dual_hybrid_gradient_test.cc index 4e827e0a9be..2e949cf457d 100644 --- a/ortools/pdlp/primal_dual_hybrid_gradient_test.cc +++ b/ortools/pdlp/primal_dual_hybrid_gradient_test.cc @@ -1610,6 +1610,39 @@ TEST(PrimalDualHybridGradientTest, DetailedTerminationCriteria) { EigenArrayNear({0.0, 1.5, -3.5, 0.0}, 1.0e-4)); } +// Regression test for b/311455838. Note that this test only fails in debug +// mode, when an infeasible primal variable (from the iterate differences) +// violates presolve's assumptions and triggers a DCHECK() failure. +TEST(PrimalDualHybridGradientTest, IterateDifferenceBoundsInPresolve) { + // A trivial (but very badly scaled) LP found by fuzzing. + QuadraticProgram lp(2, 1); + lp.objective_offset = -3.0e+23; + lp.objective_vector = + VectorXd{{2.7369110631344083e-48, -3.0517578125211636e-05}}; + lp.constraint_lower_bounds = VectorXd{{-2.7369110631344083e-48}}; + lp.constraint_upper_bounds = VectorXd{{0}}; + lp.variable_lower_bounds = VectorXd{{1.8446744073709552e+21, -1.0}}; + lp.variable_upper_bounds = VectorXd{{kInfinity, 1.8446744073709552e+21}}; + lp.constraint_matrix.coeffRef(0, 0) = -2.7369110631344083e-48; + lp.constraint_matrix.coeffRef(0, 1) = 1.0; + lp.constraint_matrix.makeCompressed(); + + PrimalDualHybridGradientParams params; + params.mutable_termination_criteria()->set_iteration_limit(40); + auto& presolve_options = *params.mutable_presolve_options(); + presolve_options.set_use_glop(true); + presolve_options.mutable_glop_parameters()->set_solve_dual_problem( + operations_research::glop::GlopParameters::LET_SOLVER_DECIDE); + presolve_options.mutable_glop_parameters()->set_dualizer_threshold( + 1.3574141825331e-312); + params.set_infinite_constraint_bound_threshold(1.34785525461908e-312); + + SolverResult output = PrimalDualHybridGradient(lp, params); + EXPECT_THAT( + output.solve_log.termination_reason(), + AnyOf(TERMINATION_REASON_ITERATION_LIMIT, TERMINATION_REASON_OPTIMAL)); +} + // `FeasibilityPolishingTest` sets `params_` for feasibility polishing, and to // avoid PDLP features that disrupt a simple analysis of the performance with // and without feasibility polishing (primal weight adjustments, dynamic step diff --git a/ortools/sat/python/py.typed b/ortools/sat/python/py.typed new file mode 100644 index 00000000000..e69de29bb2d