From 3765c971e57e95538c3a73d0c5ec025ef4db1f5e Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Tue, 19 Nov 2019 14:37:31 -0800 Subject: [PATCH] polish lp code --- ortools/linear_solver/linear_solver.cc | 18 +-- .../linear_solver/samples/mip_var_array.py | 137 +++++++++--------- ortools/linear_solver/sat_proto_solver.cc | 17 ++- ortools/linear_solver/sat_solver_utils.cc | 7 +- ortools/linear_solver/sat_solver_utils.h | 14 +- ortools/linear_solver/scip_interface.cc | 23 +-- ortools/port/file_nonport.cc | 22 ++- 7 files changed, 135 insertions(+), 103 deletions(-) diff --git a/ortools/linear_solver/linear_solver.cc b/ortools/linear_solver/linear_solver.cc index 747fcce64b1..4f73a8f3a9b 100644 --- a/ortools/linear_solver/linear_solver.cc +++ b/ortools/linear_solver/linear_solver.cc @@ -1675,21 +1675,9 @@ bool MPSolverInterface::SetSolverSpecificParametersAsString( if (parameters.empty()) return true; std::string extension = ValidFileExtensionForParameterFile(); -#if defined(__linux) - int32 tid = static_cast(pthread_self()); -#else // defined(__linux__) - int32 tid = 123; -#endif // defined(__linux__) -#if !defined(_MSC_VER) - int32 pid = static_cast(getpid()); -#else // _MSC_VER - int32 pid = 456; -#endif // _MSC_VER - int64 now = absl::GetCurrentTimeNanos(); - std::string filename = - absl::StrFormat("/tmp/parameters-tempfile-%x-%d-%llx%s", tid, pid, now, - extension.c_str()); - bool no_error_so_far = true; + std::string filename; + bool no_error_so_far = PortableTemporaryFile(nullptr, &filename); + filename += extension; if (no_error_so_far) { no_error_so_far = PortableFileSetContents(filename, parameters).ok(); } diff --git a/ortools/linear_solver/samples/mip_var_array.py b/ortools/linear_solver/samples/mip_var_array.py index fdd751c9659..2663f843bde 100644 --- a/ortools/linear_solver/samples/mip_var_array.py +++ b/ortools/linear_solver/samples/mip_var_array.py @@ -15,89 +15,90 @@ # [START import] from __future__ import print_function from ortools.linear_solver import pywraplp + # [END import] # [START data_model] def create_data_model(): - """Stores the data for the problem.""" - data = {} - # Locations in block units - data['constraint_coeffs'] = [ - [5, 7, 9, 2, 1], - [18, 4, -9, 10, 12], - [4, 7, 3, 8, 5], - [5, 13, 16, 3, -7], - ] - data['bounds'] = [250, 285, 211, 315] - data['obj_coeffs'] = [7, 8, 2, 9, 6] - data['num_vars'] = 5 - data['num_constraints'] = 4 - return data + """Stores the data for the problem.""" + data = {} + # Locations in block units + data['constraint_coeffs'] = [ + [5, 7, 9, 2, 1], + [18, 4, -9, 10, 12], + [4, 7, 3, 8, 5], + [5, 13, 16, 3, -7], + ] + data['bounds'] = [250, 285, 211, 315] + data['obj_coeffs'] = [7, 8, 2, 9, 6] + data['num_vars'] = 5 + data['num_constraints'] = 4 + return data + + # [END data_model] def main(): - # [START data] - data = create_data_model() - # [END data] - - # [START solver] - # MOE:begin_strip - # Create the mip solver with the CBC backend. - solver = pywraplp.Solver( - 'simple_mip_program', - pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING) - # [END solver] - # [START variables] - infinity = solver.infinity() - x = {} - for j in range(data['num_vars']): - x[j] = solver.IntVar(0, infinity, 'x[%i]' % j) - print('Number of variables =', solver.NumVariables()) - # [END variables] + # [START data] + data = create_data_model() + # [END data] - # [START constraints] - for i in range(data['num_constraints']): - constraint = solver.RowConstraint(0, data['bounds'][i], '') + # [START solver] + # Create the mip solver with the CBC backend. + solver = pywraplp.Solver('simple_mip_program', + pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING) + # [END solver] + # [START variables] + infinity = solver.infinity() + x = {} for j in range(data['num_vars']): - constraint.SetCoefficient(x[j], data['constraint_coeffs'][i][j]) - print('Number of constraints =', solver.NumConstraints()) - # In Python, you can also set the constraints as follows. - # for i in range(data['num_constraints']): - # constraint_expr = \ - # [data['constraint_coeffs'][i][j] * x[j] for j in range(data['num_vars'])] - # solver.Add(sum(constraint_expr) <= data['bounds'][i]) - # [END constraints] + x[j] = solver.IntVar(0, infinity, 'x[%i]' % j) + print('Number of variables =', solver.NumVariables()) + # [END variables] - # [START objective] - objective = solver.Objective() - for j in range(data['num_vars']): - objective.SetCoefficient(x[j], data['obj_coeffs'][j]) - objective.SetMaximization() - # In Python, you can also set the objective as follows. - # obj_expr = [data['obj_coeffs'][j] * x[j] for j in range(data['num_vars'])] - # solver.Maximize(solver.Sum(obj_expr)) - # [END objective] + # [START constraints] + for i in range(data['num_constraints']): + constraint = solver.RowConstraint(0, data['bounds'][i], '') + for j in range(data['num_vars']): + constraint.SetCoefficient(x[j], data['constraint_coeffs'][i][j]) + print('Number of constraints =', solver.NumConstraints()) + # In Python, you can also set the constraints as follows. + # for i in range(data['num_constraints']): + # constraint_expr = \ + # [data['constraint_coeffs'][i][j] * x[j] for j in range(data['num_vars'])] + # solver.Add(sum(constraint_expr) <= data['bounds'][i]) + # [END constraints] - # [START solve] - status = solver.Solve() - # [END solve] - - # [START print_solution] - if status == pywraplp.Solver.OPTIMAL: - print('Objective value =', solver.Objective().Value()) + # [START objective] + objective = solver.Objective() for j in range(data['num_vars']): - print(x[j].name(), ' = ', x[j].solution_value()) - print() - print('Problem solved in %f milliseconds' % solver.wall_time()) - print('Problem solved in %d iterations' % solver.iterations()) - print('Problem solved in %d branch-and-bound nodes' % solver.nodes()) - else: - print('The problem does not have an optimal solution.') - # [END print_solution] + objective.SetCoefficient(x[j], data['obj_coeffs'][j]) + objective.SetMaximization() + # In Python, you can also set the objective as follows. + # obj_expr = [data['obj_coeffs'][j] * x[j] for j in range(data['num_vars'])] + # solver.Maximize(solver.Sum(obj_expr)) + # [END objective] + + # [START solve] + status = solver.Solve() + # [END solve] + + # [START print_solution] + if status == pywraplp.Solver.OPTIMAL: + print('Objective value =', solver.Objective().Value()) + for j in range(data['num_vars']): + print(x[j].name(), ' = ', x[j].solution_value()) + print() + print('Problem solved in %f milliseconds' % solver.wall_time()) + print('Problem solved in %d iterations' % solver.iterations()) + print('Problem solved in %d branch-and-bound nodes' % solver.nodes()) + else: + print('The problem does not have an optimal solution.') + # [END print_solution] if __name__ == '__main__': - main() + main() # [END program] diff --git a/ortools/linear_solver/sat_proto_solver.cc b/ortools/linear_solver/sat_proto_solver.cc index 2576c2406d2..57e6af5f8f3 100644 --- a/ortools/linear_solver/sat_proto_solver.cc +++ b/ortools/linear_solver/sat_proto_solver.cc @@ -91,7 +91,22 @@ util::StatusOr SatSolveProto( } MPModelProto* const mp_model = request.mutable_model(); - auto shift_bounds_preprocessor = ApplyMipPresolveSteps(mp_model); + std::unique_ptr + shift_bounds_preprocessor; + + const auto status = + ApplyMipPresolveSteps(mp_model, &shift_bounds_preprocessor); + if (status == MPSolverResponseStatus::MPSOLVER_INFEASIBLE) { + if (params.log_search_progress()) { + // This is needed for our benchmark scripts. + sat::CpSolverResponse cp_response; + cp_response.set_status(sat::CpSolverStatus::INFEASIBLE); + LOG(INFO) << CpSolverResponseStats(cp_response); + } + response.set_status(MPSolverResponseStatus::MPSOLVER_INFEASIBLE); + response.set_status_str("Problem proven infeasible during MIP presolve"); + return response; + } const std::vector var_scaling = sat::ScaleContinuousVariables(params.mip_var_scaling(), mp_model); diff --git a/ortools/linear_solver/sat_solver_utils.cc b/ortools/linear_solver/sat_solver_utils.cc index 7f509040b2b..b4b83414044 100644 --- a/ortools/linear_solver/sat_solver_utils.cc +++ b/ortools/linear_solver/sat_solver_utils.cc @@ -20,9 +20,10 @@ namespace operations_research { -std::unique_ptr ApplyMipPresolveSteps( - MPModelProto* model) { - return nullptr; +MPSolverResponseStatus ApplyMipPresolveSteps( + MPModelProto* model, std::unique_ptr* + shift_bounds_preprocessor) { + return MPSolverResponseStatus::MPSOLVER_NOT_SOLVED; } } // namespace operations_research diff --git a/ortools/linear_solver/sat_solver_utils.h b/ortools/linear_solver/sat_solver_utils.h index 1044d120cd1..af20406064b 100644 --- a/ortools/linear_solver/sat_solver_utils.h +++ b/ortools/linear_solver/sat_solver_utils.h @@ -21,12 +21,18 @@ namespace operations_research { -// Apply presolve steps to improve the MIP -> IP imperfect conversion. The +// Applies presolve steps to improve the MIP -> IP imperfect conversion. The // stricter the domain of the variable, the more room we have for scaling the // constraint to integers and prevent overflow. -// If the bounds were shifted, returns the shifting preprocessor. -std::unique_ptr ApplyMipPresolveSteps( - MPModelProto* model); +// +// If the bounds were shifted, pass in the preprocessor for postsolve to the +// given unique_ptr. +// +// Returns the presolve status which is currently UNKNOWN for most cases but +// might be INFEASIBLE if there is some trivial infeasiblity in the model. +MPSolverResponseStatus ApplyMipPresolveSteps( + MPModelProto* model, std::unique_ptr* + shift_bounds_preprocessor); } // namespace operations_research #endif // OR_TOOLS_LINEAR_SOLVER_SAT_SOLVER_UTILS_H_ diff --git a/ortools/linear_solver/scip_interface.cc b/ortools/linear_solver/scip_interface.cc index 4f97db35fa8..6fd7d9b2e90 100644 --- a/ortools/linear_solver/scip_interface.cc +++ b/ortools/linear_solver/scip_interface.cc @@ -111,9 +111,9 @@ class SCIPInterface : public MPSolverInterface { void SetRelativeMipGap(double value) override; void SetPrimalTolerance(double value) override; void SetDualTolerance(double value) override; - void SetPresolveMode(int value) override; - void SetScalingMode(int value) override; - void SetLpAlgorithm(int value) override; + void SetPresolveMode(int presolve) override; + void SetScalingMode(int scaling) override; + void SetLpAlgorithm(int lp_algorithm) override; // SCIP parameters allow to lower and upper bound the number of threads used // (via "parallel/minnthreads" and "parallel/maxnthread", respectively). Here, @@ -712,7 +712,8 @@ MPSolver::ResultStatus SCIPInterface::Solve(const MPSolverParameters& param) { default: if (solution != nullptr) { result_status_ = MPSolver::FEASIBLE; - } else if (scip_status == SCIP_STATUS_TIMELIMIT) { + } else if (scip_status == SCIP_STATUS_TIMELIMIT || + scip_status == SCIP_STATUS_TOTALNODELIMIT) { result_status_ = MPSolver::NOT_SOLVED; } else { result_status_ = MPSolver::ABNORMAL; @@ -821,9 +822,9 @@ void SCIPInterface::SetDualTolerance(double value) { if (status_.ok()) status_ = status; } -void SCIPInterface::SetPresolveMode(int value) { +void SCIPInterface::SetPresolveMode(int presolve) { // See the NOTE on SetRelativeMipGap(). - switch (value) { + switch (presolve) { case MPSolverParameters::PRESOLVE_OFF: { const auto status = SCIP_TO_STATUS(SCIPsetIntParam(scip_, "presolving/maxrounds", 0)); @@ -837,22 +838,22 @@ void SCIPInterface::SetPresolveMode(int value) { return; } default: { - SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, value); + SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, presolve); return; } } } -void SCIPInterface::SetScalingMode(int value) { +void SCIPInterface::SetScalingMode(int scaling) { SetUnsupportedIntegerParam(MPSolverParameters::SCALING); } // Only the root LP algorithm is set as setting the node LP to a // non-default value rarely is beneficial. The node LP algorithm could // be set as well with "lp/resolvealgorithm". -void SCIPInterface::SetLpAlgorithm(int value) { +void SCIPInterface::SetLpAlgorithm(int lp_algorithm) { // See the NOTE on SetRelativeMipGap(). - switch (value) { + switch (lp_algorithm) { case MPSolverParameters::DUAL: { const auto status = SCIP_TO_STATUS(SCIPsetCharParam(scip_, "lp/initalgorithm", 'd')); @@ -874,7 +875,7 @@ void SCIPInterface::SetLpAlgorithm(int value) { } default: { SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM, - value); + lp_algorithm); return; } } diff --git a/ortools/port/file_nonport.cc b/ortools/port/file_nonport.cc index 2984ba86839..0b6257537fb 100644 --- a/ortools/port/file_nonport.cc +++ b/ortools/port/file_nonport.cc @@ -11,6 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. + +#if !defined(_MSC_VER) +#include +#endif + +#include "absl/strings/str_format.h" +#include "absl/time/clock.h" #include "ortools/base/file.h" #include "ortools/port/file.h" @@ -28,7 +35,20 @@ ::util::Status PortableFileGetContents(absl::string_view file_name, bool PortableTemporaryFile(const char* directory_prefix, std::string* filename_out) { - return false; +#if defined(__linux) + int32 tid = static_cast(pthread_self()); +#else // defined(__linux__) + int32 tid = 123; +#endif // defined(__linux__) +#if !defined(_MSC_VER) + int32 pid = static_cast(getpid()); +#else // _MSC_VER + int32 pid = 456; +#endif // _MSC_VER + int64 now = absl::GetCurrentTimeNanos(); + std::string filename = + absl::StrFormat("/tmp/parameters-tempfile-%x-%d-%llx", tid, pid, now); + return true; } ::util::Status PortableDeleteFile(absl::string_view file_name) {