diff --git a/src/linear_solver/glpk_interface.cc b/src/linear_solver/glpk_interface.cc index 2c5abe2944..4fa34cfb67 100644 --- a/src/linear_solver/glpk_interface.cc +++ b/src/linear_solver/glpk_interface.cc @@ -664,10 +664,10 @@ MPSolver::BasisStatus GLPKInterface::TransformGLPKBasisStatus( // ------ Query statistics on the solution and the solve ------ int64 GLPKInterface::iterations() const { -#if GLP_MINOR_VERSION < 49 +#if GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION < 49 if (!mip_ && CheckSolutionIsSynchronized()) return lpx_get_int_parm(lp_, LPX_K_ITCNT); -#elif GLP_MINOR_VERSION >= 53 +#elif GLP_MAJOR_VERSION == 4 && GLP_MINOR_VERSION >= 53 if (!mip_ && CheckSolutionIsSynchronized()) { return glp_get_it_cnt(lp_); } diff --git a/src/linear_solver/gurobi_interface.cc b/src/linear_solver/gurobi_interface.cc index 68673c581c..877ac5db3c 100644 --- a/src/linear_solver/gurobi_interface.cc +++ b/src/linear_solver/gurobi_interface.cc @@ -729,7 +729,7 @@ std::string GurobiInterface::ValidFileExtensionForParameterFile() const { return ".prm"; } -MPSolverInterface* BuildGurobiInterface(MPSolver* const solver, bool mip) { +MPSolverInterface* BuildGurobiInterface(bool mip, MPSolver* const solver) { return new GurobiInterface(solver, mip); } diff --git a/src/linear_solver/linear_solver.cc b/src/linear_solver/linear_solver.cc index 64d21ee67f..d4e5117887 100644 --- a/src/linear_solver/linear_solver.cc +++ b/src/linear_solver/linear_solver.cc @@ -335,8 +335,8 @@ extern MPSolverInterface* BuildSCIPInterface(MPSolver* const solver); extern MPSolverInterface* BuildSLMInterface(MPSolver* const solver, bool mip); #endif #if defined(USE_GUROBI) -extern MPSolverInterface* BuildGurobiInterface(MPSolver* const solver, - bool mip); +extern MPSolverInterface* BuildGurobiInterface(bool mip, + MPSolver* const solver); #endif @@ -370,9 +370,9 @@ MPSolverInterface* BuildSolverInterface(MPSolver* const solver) { #endif #if defined(USE_GUROBI) case MPSolver::GUROBI_LINEAR_PROGRAMMING: - return BuildGurobiInterface(solver, false); + return BuildGurobiInterface(false, solver); case MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING: - return BuildGurobiInterface(solver, true); + return BuildGurobiInterface(true, solver); #endif default: // TODO(user): Revert to the best *available* interface. diff --git a/src/linear_solver/linear_solver.swig b/src/linear_solver/linear_solver.swig index 345108ac47..44c7419127 100644 --- a/src/linear_solver/linear_solver.swig +++ b/src/linear_solver/linear_solver.swig @@ -63,6 +63,11 @@ %rename (Objective) MutableObjective; // Access to the underlying solver is available only in C++. %ignore underlying_solver; +// The ExportModelAs*Format methods return the string directly in the python +// API, using an empty string to indicate failures (which can also indicate an +// empty model). +%ignore ExportModelAsLpFormat(bool, string*); +%ignore ExportModelAsMpsFormat(bool, bool, string*); namespace operations_research { %pythoncode { @@ -535,7 +540,7 @@ namespace operations_research { %rename (clear) MPObjective::Clear; %rename (clear) MPSolver::Clear; %rename (computeExactConditionNumber) MPSolver::ComputeExactConditionNumber; -%rename (loadModel) MPSolver::LoadModel; +%rename (loadModelFromProto) MPSolver::LoadModelFromProto; %rename (lookupVariableOrNull) MPSolver::LookupVariableOrNull; %rename (lookupConstraintOrNull) MPSolver::LookupConstraintOrNull; %rename (makeBoolVar) MPSolver::MakeBoolVar; @@ -577,6 +582,9 @@ namespace operations_research { %ignore MPSolver::ExportModel; %ignore MPSolver::FillSolutionResponse; %ignore MPSolver::SolveWithProtocolBuffers; +// Ignore export to string methods. +%ignore operations_research::MPSolver::ExportModelAsLpFormat; +%ignore operations_research::MPSolver::ExportModelAsMpsFormat; // Access to the underlying solver is available only in C++. %ignore MPSolver::underlying_solver; @@ -646,6 +654,21 @@ Class c = Class.forName("com.google.ortools.linearsolver.MPSolver"); } %} +%extend MPSolver { + std::string exportModelAsLpFormat(bool obfuscated) { + std::string output; + if (!self->ExportModelAsLpFormat(obfuscated, &output)) return ""; + return output; + } + + std::string exportModelAsMpsFormat(bool fixed_format, bool obfuscated) { + std::string output; + if (!self->ExportModelAsMpsFormat(fixed_format, obfuscated, &output)) { + return ""; + } + return output; + } +} } // namespace operations_research #endif // SWIGJAVA @@ -689,6 +712,9 @@ namespace operations_research { // Ignore Objective(), use MutableObjective() instead. %ignore MPSolver::Objective; %rename (Objective) MPSolver::MutableObjective; +// Ignore export to string methods. +%ignore operations_research::MPSolver::ExportModelAsLpFormat; +%ignore operations_research::MPSolver::ExportModelAsMpsFormat; %typemap(cscode) MPVariable %{ public static LinearExpr operator+(Variable a, double v) @@ -1089,6 +1115,20 @@ namespace operations_research { Objective().SetMaximization(); Objective().SetCoefficient(var, 1.0); } + + std::string ExportModelAsLpFormat(bool obfuscated) { + std::string output; + if (!self->ExportModelAsLpFormat(obfuscated, &output)) return ""; + return output; + } + + std::string ExportModelAsMpsFormat(bool fixed_format, bool obfuscated) { + std::string output; + if (!self->ExportModelAsMpsFormat(fixed_format, obfuscated, &output)) { + return ""; + } + return output; + } %} } // namespace operations_research diff --git a/src/linear_solver/model_exporter.cc b/src/linear_solver/model_exporter.cc index 222d2fa633..393fa694cd 100644 --- a/src/linear_solver/model_exporter.cc +++ b/src/linear_solver/model_exporter.cc @@ -436,6 +436,28 @@ bool MPModelProtoExporter::CanUseFixedMpsFormat() const { return true; } +void MPModelProtoExporter::AppendMpsColumns(bool integrality, + const std::vector>>& transpose, std::string* output) { + current_mps_column_ = 0; + for (int var_index = 0; var_index < proto_.variable_size(); ++var_index) { + const MPVariableProto& var_proto = proto_.variable(var_index); + if (var_proto.is_integer() != integrality) continue; + const std::string var_name = GetVariableName(var_index); + current_mps_column_ = 0; + if (var_proto.objective_coefficient() != 0.0) { + AppendMpsTermWithContext(var_name, "COST", + var_proto.objective_coefficient(), + output); + } + for (const std::pair cst_index_and_coeff : transpose[var_index]) { + const std::string cst_name = GetConstraintName(cst_index_and_coeff.first); + AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second, + output); + } + AppendNewLineIfTwoColumns(output); + } +} + bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, bool obfuscated, std::string* output) { @@ -486,9 +508,9 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, } // As the information regarding a column needs to be contiguous, we create - // a map associating a variable to a the vector containing the indices of the + // a map associating a variable to a vector containing the indices of the // constraints where this variable appears. - std::vector > > transpose(proto_.variable_size()); + std::vector>> transpose(proto_.variable_size()); for (int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) { const MPConstraintProto& ct_proto = proto_.constraint(cst_index); for (int k = 0; k < ct_proto.var_index_size(); ++k) { @@ -506,35 +528,16 @@ bool MPModelProtoExporter::ExportModelAsMpsFormat(bool fixed_format, } // COLUMNS section. - current_mps_column_ = 0; std::string columns_section; - const char* const kIntMarkerFormat = " %-10s%-36s%-10s\n"; - for (int var_index = 0; var_index < proto_.variable_size(); ++var_index) { - const MPVariableProto& var_proto = proto_.variable(var_index); - const std::string var_name = GetVariableName(var_index); - current_mps_column_ = 0; - if (var_proto.is_integer()) { - StringAppendF(&columns_section, kIntMarkerFormat, "INTSTART", "'MARKER'", - "'INTORG'"); - } - if (var_proto.objective_coefficient() != 0.0) { - AppendMpsTermWithContext(var_name, "COST", - var_proto.objective_coefficient(), - &columns_section); - } - for (const std::pair cst_index_and_coeff : transpose[var_index]) { - const std::string cst_name = GetConstraintName(cst_index_and_coeff.first); - AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second, - &columns_section); - } - if (var_proto.is_integer()) { - columns_section += "\n"; - current_mps_column_ = 0; - StringAppendF(&columns_section, kIntMarkerFormat, "INTEND", "'MARKER'", - "'INTEND'"); - } - AppendNewLineIfTwoColumns(&columns_section); + AppendMpsColumns(/*integrality=*/true, transpose, &columns_section); + if (!columns_section.empty()) { + const char* const kIntMarkerFormat = " %-10s%-36s%-10s\n"; + columns_section = StringPrintf(kIntMarkerFormat, "INTSTART", + "'MARKER'", "'INTORG'") + columns_section; + StringAppendF(&columns_section, kIntMarkerFormat, + "INTEND", "'MARKER'", "'INTEND'"); } + AppendMpsColumns(/*integrality=*/false, transpose, &columns_section); if (!columns_section.empty()) { *output += "COLUMNS\n" + columns_section; } diff --git a/src/linear_solver/model_exporter.h b/src/linear_solver/model_exporter.h index 8591a0ab4d..132575a38b 100644 --- a/src/linear_solver/model_exporter.h +++ b/src/linear_solver/model_exporter.h @@ -151,6 +151,13 @@ class MPModelProtoExporter { // Used by and in complement to AppendMpsTermWithContext. void AppendNewLineIfTwoColumns(std::string* output); + // When 'integrality' is true, appends columns corresponding to integer + // variables. Appends the columns for non-integer variables otherwise. + // The sparse matrix must be passed as a vector of columns ('transpose'). + void AppendMpsColumns(bool integrality, + const std::vector>>& transpose, + std::string* output); + // Appends a line describing the bound of a variablenew-line if two columns // are already present on the MPS line. // Used by and in complement to AppendMpsTermWithContext.