Change proto to/from file API to use absl::StatusOr; add reader/writer proto_file to model_builder

This commit is contained in:
Laurent Perron
2023-11-20 16:13:03 +01:00
parent dd57344ef2
commit 72f274f844
27 changed files with 229 additions and 524 deletions

View File

@@ -1,414 +1,61 @@
diff --git a/.gitignore b/.gitignore
index 4fbbc0e3b3..964b2d030d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,8 +83,6 @@ hooks/
localhooks/
# created when packaging, don't version control this
-src/scip/githash.c
-src/scip/buildflags.c
# settings
settings/
diff --git a/src/scip/benders_xyz.c b/src/scip/benders_xyz.c
index 0d812ba6bd..ffe1badee0 100644
--- a/src/scip/benders_xyz.c
+++ b/src/scip/benders_xyz.c
@@ -47,6 +47,7 @@
/** Benders' decomposition data */
struct SCIP_BendersData
{
+ void* ptr;
};
diff --git a/src/scip/benderscut_xyz.c b/src/scip/benderscut_xyz.c
index 0f05582fe4..7fb99f648a 100644
--- a/src/scip/benderscut_xyz.c
+++ b/src/scip/benderscut_xyz.c
@@ -41,6 +41,7 @@
/** Benders' decomposition cut data */
struct SCIP_BenderscutData
{
+ void* ptr;
};
diff --git a/src/scip/branch_xyz.c b/src/scip/branch_xyz.c
index 1f13ac7460..e8ded751cf 100644
--- a/src/scip/branch_xyz.c
+++ b/src/scip/branch_xyz.c
@@ -42,6 +42,7 @@
/** branching rule data */
struct SCIP_BranchruleData
{
+ void* ptr;
};
@@ -216,7 +217,7 @@ SCIP_RETCODE SCIPincludeBranchruleXyz(
/* use SCIPincludeBranchrule() if you want to set all callbacks explicitly and realize (by getting compiler errors) when
* new callbacks are added in future SCIP versions
*/
- SCIP_CALL( SCIPincludeBranchrule(scip, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY, BRANCHRULE_MAXDEPTH,
+ SCIP_CALL( SCIPincludeBranchrule(scip, BRANCHRULE_NAME, BRANCHRULE_DESC, BRANCHRULE_PRIORITY, BRANCHRULE_MAXDEPTH,
BRANCHRULE_MAXBOUNDDIST,
branchCopyXyz, branchFreeXyz, branchInitXyz, branchExitXyz, branchInitsolXyz, branchExitsolXyz,
branchExeclpXyz, branchExecextXyz, branchExecpsXyz,
diff --git a/src/scip/compr_xyz.c b/src/scip/compr_xyz.c
index 2f6b29e88c..a6142d7785 100644
--- a/src/scip/compr_xyz.c
+++ b/src/scip/compr_xyz.c
@@ -41,6 +41,7 @@
/** tree compression data */
struct SCIP_ComprData
{
+ void* ptr;
};
diff --git a/src/scip/cons_xyz.c b/src/scip/cons_xyz.c
index 8141039a2e..e3f5d2b94b 100644
--- a/src/scip/cons_xyz.c
+++ b/src/scip/cons_xyz.c
@@ -69,11 +69,13 @@
/** constraint data for xyz constraints */
struct SCIP_ConsData
{
+ void* ptr;
};
/** constraint handler data */
struct SCIP_ConshdlrData
{
+ void* ptr;
};
diff --git a/src/scip/cutsel_xyz.c b/src/scip/cutsel_xyz.c
index c660098bb0..92f3fb9d92 100644
--- a/src/scip/cutsel_xyz.c
+++ b/src/scip/cutsel_xyz.c
@@ -40,6 +40,7 @@
/** cut selector data */
struct SCIP_CutselData
{
+ void* ptr;
};
diff --git a/src/scip/dialog_xyz.c b/src/scip/dialog_xyz.c
index 1918057017..8506d526e6 100644
--- a/src/scip/dialog_xyz.c
+++ b/src/scip/dialog_xyz.c
@@ -28,7 +28,7 @@
#define DIALOG_NAME "xyz"
#define DIALOG_DESC "xyz user interface dialog"
-#define DIALOG_ISSUBMENU FALSE
+#define DIALOG_ISSUBMENU FALSE
@@ -42,6 +42,7 @@
/** dialog data */
struct SCIP_DialogData
{
+ void* ptr;
};
@@ -154,7 +155,7 @@ SCIP_RETCODE SCIPincludeDialogXyz(
/* create, include, and release dialog */
if( !SCIPdialogHasEntry(parentdialog, DIALOG_NAME) )
diff --git a/src/lpi/lpi_glop.cpp b/src/lpi/lpi_glop.cpp
index 2471778a8f..17fd1e8c34 100644
--- a/src/lpi/lpi_glop.cpp
+++ b/src/lpi/lpi_glop.cpp
@@ -3190,6 +3190,6 @@ SCIP_RETCODE SCIPlpiReadLP(
const std::string filespec(fname);
MPModelProto proto;
- if ( ! ReadFileToProto(filespec, &proto) )
+ if ( ! ReadFileToProto(filespec, &proto).ok() )
{
- SCIP_CALL( SCIPincludeDialog(scip, &dialog,
+ SCIP_CALL( SCIPincludeDialog(scip, &dialog,
dialogCopyXyz, dialogExecXyz, dialogDescXyz, dialogFreeXyz,
DIALOG_NAME, DIALOG_DESC, DIALOG_ISSUBMENU, dialogdata) );
SCIP_CALL( SCIPaddDialogEntry(scip, parentdialog, dialog) );
diff --git a/src/scip/disp_xyz.c b/src/scip/disp_xyz.c
index 6c6a776091..b00cf1e036 100644
--- a/src/scip/disp_xyz.c
+++ b/src/scip/disp_xyz.c
@@ -26,13 +26,13 @@
#include "scip/disp_xyz.h"
-#define DISP_NAME "xyz"
+#define DISP_NAME "xyz"
#define DISP_DESC "xyz display column"
-#define DISP_HEADER "xyz"
+#define DISP_HEADER "xyz"
#define DISP_WIDTH 14 /**< the width of the display column */
#define DISP_PRIORITY 110000 /**< the priority of the display column */
#define DISP_POSITION 30100 /**< the relative position of the display column */
-#define DISP_STRIPLINE TRUE /**< the default for whether the display column should be separated
+#define DISP_STRIPLINE TRUE /**< the default for whether the display column should be separated
* with a line from its right neighbor */
@@ -47,6 +47,7 @@
/** display column data */
struct SCIP_DispData
{
+ void* ptr;
};
@@ -188,10 +189,10 @@ SCIP_RETCODE SCIPincludeDispXyz(
/* TODO: (optional) create display column specific data here */
/* include display column */
- SCIP_CALL( SCIPincludeDisp(scip, DISP_NAME, DISP_DESC, DISP_HEADER, SCIP_DISPSTATUS_AUTO,
+ SCIP_CALL( SCIPincludeDisp(scip, DISP_NAME, DISP_DESC, DISP_HEADER, SCIP_DISPSTATUS_AUTO,
dispCopyXyz,
- dispFreeXyz, dispInitXyz, dispExitXyz,
- dispInitsolXyz, dispExitsolXyz, dispOutputXyz,
+ dispFreeXyz, dispInitXyz, dispExitXyz,
+ dispInitsolXyz, dispExitsolXyz, dispOutputXyz,
dispdata, DISP_WIDTH, DISP_PRIORITY, DISP_POSITION, DISP_STRIPLINE) );
/* add xyz display column parameters */
diff --git a/src/scip/event_xyz.c b/src/scip/event_xyz.c
index 31fd333f98..c793d69bc4 100644
--- a/src/scip/event_xyz.c
+++ b/src/scip/event_xyz.c
@@ -36,6 +36,7 @@
/** event handler data */
struct SCIP_EventhdlrData
{
+ void* ptr;
};
/*
@@ -179,7 +180,7 @@ SCIP_RETCODE SCIPincludeEventHdlrXyz(
*/
SCIP_CALL( SCIPincludeEventhdlr(scip, EVENTHDLR_NAME, EVENTHDLR_DESC,
eventCopyXyz,
- eventFreeXyz, eventInitXyz, eventExitXyz,
+ eventFreeXyz, eventInitXyz, eventExitXyz,
eventInitsolXyz, eventExitsolXyz, eventDeleteXyz, eventExecXyz,
eventhdlrdata) );
#else
diff --git a/src/scip/expr_xyz.c b/src/scip/expr_xyz.c
index 2eb7914e1d..4e924b03b5 100644
--- a/src/scip/expr_xyz.c
+++ b/src/scip/expr_xyz.c
@@ -38,11 +38,13 @@
/** expression handler data */
struct SCIP_ExprhdlrData
{
+ void* ptr;
};
/** expression data */
struct SCIP_ExprData
{
+ void* ptr;
};
/*
SCIPerrorMessage("Could not read <%s>\n", fname);
return SCIP_READERROR;
@@ -3214,7 +3214,7 @@ SCIP_RETCODE SCIPlpiWriteLP(
MPModelProto proto;
LinearProgramToMPModelProto(*lpi->linear_program, &proto);
const std::string filespec(fname);
- if ( ! WriteProtoToFile(filespec, proto, operations_research::ProtoWriteFormat::kProtoText, true) )
+ if ( ! WriteProtoToFile(filespec, proto, operations_research::ProtoWriteFormat::kProtoText, true).ok() )
{
SCIPerrorMessage("Could not write <%s>\n", fname);
return SCIP_READERROR;
diff --git a/src/symmetry/compute_symmetry_bliss.cpp b/src/symmetry/compute_symmetry_bliss.cpp
index 484627c4b9..27c2895165 100644
--- a/src/symmetry/compute_symmetry_bliss.cpp
+++ b/src/symmetry/compute_symmetry_bliss.cpp
@@ -25,5 +25,5 @@
#include "compute_symmetry.h"
/* include bliss graph */
-#include <bliss/defs.hh>
-#include <bliss/graph.hh>
+#include <bliss-0.73/defs.hh>
+#include <bliss-0.73/graph.hh>
#include <string.h>
#include <vector>
diff --git a/src/scip/githash.c b/src/scip/githash.c
new file mode 100644
index 0000000000..2891bc72de
--- /dev/null
+++ b/src/scip/githash.c
@@ -0,0 +1 @@
@@ -0,0 +1,1 @@
+#define SCIP_GITHASH "a740f0891e"
diff --git a/src/scip/heur_xyz.c b/src/scip/heur_xyz.c
index 9f7d804f4d..e33bb83b7c 100644
--- a/src/scip/heur_xyz.c
+++ b/src/scip/heur_xyz.c
@@ -46,6 +46,7 @@
/** primal heuristic data */
struct SCIP_HeurData
{
+ void* ptr;
};
diff --git a/src/scip/nlhdlr_xyz.c b/src/scip/nlhdlr_xyz.c
index bc90f3dafe..056af7b6d3 100644
--- a/src/scip/nlhdlr_xyz.c
+++ b/src/scip/nlhdlr_xyz.c
@@ -40,11 +40,13 @@
/** nonlinear handler data */
struct SCIP_NlhdlrData
{
+ void* ptr;
};
/** nonlinear handler expression data */
struct SCIP_NlhdlrExprData
{
+ void* ptr;
};
/*
diff --git a/src/scip/nlpi_xyz.c b/src/scip/nlpi_xyz.c
index 3509410b23..901433d2d4 100644
--- a/src/scip/nlpi_xyz.c
+++ b/src/scip/nlpi_xyz.c
@@ -43,12 +43,14 @@
struct SCIP_NlpiData
{
+ void* ptr;
};
/* TODO: fill in the necessary NLP problem instance data */
struct SCIP_NlpiProblem
{
+ void* ptr;
};
diff --git a/src/scip/nodesel_xyz.c b/src/scip/nodesel_xyz.c
index a5b6d9d7d6..0aacc3c8d2 100644
--- a/src/scip/nodesel_xyz.c
+++ b/src/scip/nodesel_xyz.c
@@ -41,6 +41,7 @@
/** node selector data */
struct SCIP_NodeselData
{
+ void* ptr;
};
diff --git a/src/scip/presol_xyz.c b/src/scip/presol_xyz.c
index 38ba9df72e..04fe8605f5 100644
--- a/src/scip/presol_xyz.c
+++ b/src/scip/presol_xyz.c
@@ -42,6 +42,7 @@
/** presolver data */
struct SCIP_PresolData
{
+ void* ptr;
};
diff --git a/src/scip/pricer_xyz.c b/src/scip/pricer_xyz.c
index 16c968b951..5090a91e35 100644
--- a/src/scip/pricer_xyz.c
+++ b/src/scip/pricer_xyz.c
@@ -43,6 +43,7 @@
/** variable pricer data */
struct SCIP_PricerData
{
+ void* ptr;
};
@@ -204,7 +205,7 @@ SCIP_RETCODE SCIPincludePricerXyz(
* new callbacks are added in future SCIP versions
*/
SCIP_CALL( SCIPincludePricer(scip, PRICER_NAME, PRICER_DESC, PRICER_PRIORITY, PRICER_DELAY,
- pricerCopyXyz, pricerFreeXyz, pricerInitXyz, pricerExitXyz,
+ pricerCopyXyz, pricerFreeXyz, pricerInitXyz, pricerExitXyz,
pricerInitsolXyz, pricerExitsolXyz, pricerRedcostXyz, pricerFarkasXyz,
pricerdata) );
#else
diff --git a/src/scip/prop_xyz.c b/src/scip/prop_xyz.c
index 431d8e909b..975564f12b 100644
--- a/src/scip/prop_xyz.c
+++ b/src/scip/prop_xyz.c
@@ -28,7 +28,7 @@
/* fundamental propagator properties */
#define PROP_NAME "xyz"
#define PROP_DESC "propagator template"
-#define PROP_PRIORITY 0 /**< propagator priority */
+#define PROP_PRIORITY 0 /**< propagator priority */
#define PROP_FREQ 10 /**< propagator frequency */
#define PROP_DELAY FALSE /**< should propagation method be delayed, if other propagators found reductions? */
#define PROP_TIMING SCIP_PROPTIMING_BEFORELP/**< propagation timing mask */
@@ -50,6 +50,7 @@
/** propagator data */
struct SCIP_PropData
{
+ void* ptr;
};
diff --git a/src/scip/reader_xyz.c b/src/scip/reader_xyz.c
index 08e5a6ff7b..c8fdd9d4f5 100644
--- a/src/scip/reader_xyz.c
+++ b/src/scip/reader_xyz.c
@@ -40,6 +40,7 @@
/** data for xyz reader */
struct SCIP_ReaderData
{
+ void* ptr;
};
diff --git a/src/scip/relax_xyz.c b/src/scip/relax_xyz.c
index 4d4acc80b3..74dfbda91f 100644
--- a/src/scip/relax_xyz.c
+++ b/src/scip/relax_xyz.c
@@ -43,6 +43,7 @@
/** relaxator data */
struct SCIP_RelaxData
{
+ void* ptr;
};
diff --git a/src/scip/scipbuildflags.c b/src/scip/scipbuildflags.c
index b54b9112cb..dc8e62b5e0 100644
--- a/src/scip/scipbuildflags.c
+++ b/src/scip/scipbuildflags.c
@@ -21,10 +21,9 @@
/*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
+#define SCIP_BUILDFLAGS " ARCH=x86_64\n COMP=gnu\n DEBUGSOL=false\n EXPRINT=none\n GAMS=false\n SYM=bliss\n GMP=false\n IPOPT=false\n IPOPTOPT=opt\n WORHP=false\n WORHPOPT=opt\n LPS=spx2\n LPSCHECK=false\n LPSOPT=opt\n NOBLKBUFMEM=false\n NOBLKMEM=false\n NOBUFMEM=false\n OPT=opt\n OSTYPE=linux\n PARASCIP=true\n READLINE=false\n SANITIZE=\n SHARED=false\n USRARFLAGS=\n USRCFLAGS=-fPIC\n USRCXXFLAGS=-fPIC\n USRDFLAGS=\n USRFLAGS=\n USRLDFLAGS=\n USROFLAGS=\n VERSION=7.0.1\n ZIMPL=false\n ZIMPLOPT=opt\n ZLIB=true"
+
#include "scip/scipbuildflags.h"
-#ifdef NO_CONFIG_HEADER
-#include "buildflags.c"
-#endif
/** returns the flags that were used to build SCIP */
const char* SCIPgetBuildFlags(
diff --git a/src/scip/sepa_xyz.c b/src/scip/sepa_xyz.c
index 40d3c1c5f7..f68658951d 100644
--- a/src/scip/sepa_xyz.c
+++ b/src/scip/sepa_xyz.c
@@ -44,6 +44,7 @@
/** separator data */
struct SCIP_SepaData
{
+ void* ptr;
};
diff --git a/src/scip/table_xyz.c b/src/scip/table_xyz.c
index 2c93a43235..4f1fd28de4 100644
--- a/src/scip/table_xyz.c
+++ b/src/scip/table_xyz.c
@@ -43,6 +43,7 @@
/** statistics table data */
struct SCIP_TableData
{
+ void* ptr;
};
diff --git a/src/symmetry/compute_symmetry_bliss.cpp b/src/symmetry/compute_symmetry_bliss.cpp
index 484627c4b9..27c2895165 100644
--- a/src/symmetry/compute_symmetry_bliss.cpp
+++ b/src/symmetry/compute_symmetry_bliss.cpp
@@ -25,8 +25,8 @@
#include "compute_symmetry.h"
/* include bliss graph */
-#include <bliss/defs.hh>
-#include <bliss/graph.hh>
+#include <bliss-0.73/defs.hh>
+#include <bliss-0.73/graph.hh>
#include <string.h>
#include <vector>

View File

@@ -247,7 +247,7 @@ if(BUILD_SCIP)
scip
GIT_REPOSITORY "https://github.com/scipopt/scip.git"
GIT_TAG "v804"
#PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/scip-v804.patch"
PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/scip-v804.patch"
)
FetchContent_MakeAvailable(scip)
set(LPI_GLOP_SRC ${scip_SOURCE_DIR}/src/lpi/lpi_glop.cpp PARENT_SCOPE)

View File

@@ -19,7 +19,7 @@
#include "ortools/base/logging.h"
#include "zlib.h"
bool GunzipString(const std::string& str, std::string* out) {
bool GunzipString(absl::string_view str, std::string* out) {
z_stream zs;
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;

View File

@@ -103,10 +103,8 @@ void DumpLinearProgramIfRequiredByFlags(const LinearProgram& linear_program,
const ProtoWriteFormat write_format = absl::GetFlag(FLAGS_lp_dump_binary_file)
? ProtoWriteFormat::kProtoBinary
: ProtoWriteFormat::kProtoText;
if (!WriteProtoToFile(filespec, proto, write_format,
absl::GetFlag(FLAGS_lp_dump_compressed_file))) {
LOG(DFATAL) << "Could not write " << filespec;
}
CHECK_OK(WriteProtoToFile(filespec, proto, write_format,
absl::GetFlag(FLAGS_lp_dump_compressed_file)));
#endif
}

View File

@@ -317,7 +317,17 @@ public final class ModelBuilder {
* @return true if the model was correctly written.
*/
public boolean exportToFile(String file) {
return helper.writeModelToFile(file);
return helper.writeModelToProtoFile(file);
}
/**
* import the model from protocol buffer 'file'.
*
* @param file file to read the model from.
* @return true if the model was correctly loaded.
*/
public boolean importFromFile(String file) {
return helper.readModelFromProtoFile(file);
}
public String exportToMpsString(boolean obfuscate) {

View File

@@ -376,7 +376,17 @@ public class Model
///@return true if the model was correctly written.
public bool ExportToFile(String file)
{
return helper_.WriteModelToFile(file);
return helper_.WriteModelToProtoFile(file);
}
/// <summary>
/// load the model as a protocol buffer from 'file'.
/// </summary>
/// @param file file to read the model from.
///@return true if the model was correctly loaded.
public bool ImportFromFile(String file)
{
return helper_.ReadModelFromProtoFile(file);
}
public String ExportToMpsString(bool obfuscate)

View File

@@ -116,7 +116,8 @@ VECTOR_AS_CSHARP_ARRAY(double, double, double, DoubleVector);
%rename (ConstraintsCount) operations_research::ModelBuilderHelper::num_constraints;
%rename (Name) operations_research::ModelBuilderHelper::name;
%unignore operations_research::ModelBuilderHelper::SetName;
%unignore operations_research::ModelBuilderHelper::WriteModelToFile;
%unignore operations_research::ModelBuilderHelper::ReadModelFromProtoFile;
%unignore operations_research::ModelBuilderHelper::WriteModelToProtoFile;
%unignore operations_research::ModelBuilderHelper::ImportFromMpsString;
%unignore operations_research::ModelBuilderHelper::ImportFromMpsFile;
%unignore operations_research::ModelBuilderHelper::ImportFromLpString;

View File

@@ -174,7 +174,8 @@ class GlobalRefGuard {
%rename (numConstraints) operations_research::ModelBuilderHelper::num_constraints;
%rename (getName) operations_research::ModelBuilderHelper::name;
%rename (setName) operations_research::ModelBuilderHelper::SetName;
%rename (writeModelToFile) operations_research::ModelBuilderHelper::WriteModelToFile;
%rename (readModelFromProtoFile) operations_research::ModelBuilderHelper::ReadModelFromProtoFile;
%rename (writeModelToProtoFile) operations_research::ModelBuilderHelper::WriteModelToProtoFile;
%rename (importFromMpsString) operations_research::ModelBuilderHelper::ImportFromMpsString;
%rename (importFromMpsFile) operations_research::ModelBuilderHelper::ImportFromMpsFile;
%rename (importFromLpString) operations_research::ModelBuilderHelper::ImportFromLpString;

View File

@@ -578,12 +578,19 @@ class MPSolver {
* from `MPSolver::Solve()` which by default sets the feasibility tolerance
* and the gap limit (as of 2020/02/11, to 1e-7 and 0.0001, respectively).
*/
#if !defined(SWIG)
ABSL_DEPRECATED("Prefer SolveMPModel() from solve_mp_model.h.")
#endif // !defined(SWIG)
static void SolveWithProto(const MPModelRequest& model_request,
MPSolutionResponse* response,
// `interrupt` is non-const because the internal
// solver may set it to true itself, in some cases.
std::atomic<bool>* interrupt = nullptr);
#if !defined(SWIG)
ABSL_DEPRECATED(
"Prefer SolverTypeSupportsInterruption() from solve_mp_model.h.")
#endif // !defined(SWIG)
static bool SolverTypeSupportsInterruption(
const MPModelRequest::SolverType solver) {
// Interruption requires that MPSolver::InterruptSolve is supported for the

View File

@@ -16,6 +16,7 @@
#include <cstdio>
#include <string>
#include "absl/log/check.h"
#include "absl/strings/match.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
@@ -51,12 +52,12 @@ int main(int argc, char** argv) {
operations_research::MPModelProto model_proto;
if (absl::GetFlag(FLAGS_input_is_mp_model_request)) {
operations_research::MPModelRequest request_proto;
CHECK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input),
&request_proto));
CHECK_OK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input),
&request_proto));
model_proto.Swap(request_proto.mutable_model());
} else {
CHECK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input),
&model_proto));
CHECK_OK(operations_research::ReadFileToProto(absl::GetFlag(FLAGS_input),
&model_proto));
}
std::string output_contents;
operations_research::MPModelExportOptions options;

View File

@@ -19,7 +19,6 @@
#include <string>
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/logging.h"
namespace operations_research {
@@ -29,7 +28,7 @@ namespace operations_research {
// If possible, std::move the request into this function call to avoid a copy.
//
// If you need to change the solver parameters, please use the
// EncodeSatParametersAsString() function below to set the request's
// EncodeParametersAsString() function to set the request's
// solver_specific_parameters field.
//
// The optional interrupt_solve can be used to interrupt the solve early. It

View File

@@ -1460,11 +1460,11 @@ class Model:
# Objective.
def minimize(self, linear_expr: LinearExprT) -> None:
"""Minimize the given objective."""
"""Minimizes the given objective."""
self.__optimize(linear_expr, False)
def maximize(self, linear_expr: LinearExprT) -> None:
"""Maximize the given objective."""
"""Maximizes the given objective."""
self.__optimize(linear_expr, True)
def __optimize(self, linear_expr: LinearExprT, maximize: bool) -> None:
@@ -1507,11 +1507,11 @@ class Model:
# Hints.
def clear_hints(self):
"""Clear all solution hints."""
"""Clears all solution hints."""
self.__helper.clear_hints()
def add_hint(self, var: Variable, value: NumberT) -> None:
"""Add var == value as a hint to the model.
"""Adds var == value as a hint to the model.
Args:
var: The variable of the hint
@@ -1537,28 +1537,28 @@ class Model:
return mbh.to_mpmodel_proto(self.__helper)
def import_from_mps_string(self, mps_string: str) -> bool:
"""Loads the a model from an MPS string."""
"""Reads a model from a MPS string."""
return self.__helper.import_from_mps_string(mps_string)
def import_from_mps_file(self, mps_file: str) -> bool:
"""Loads the a model from an MPS file."""
"""Reads a model from a .mps file."""
return self.__helper.import_from_mps_file(mps_file)
def import_from_lp_string(self, lp_string: str) -> bool:
"""Loads the a model from an LP string."""
"""Reads a model from a LP string."""
return self.__helper.import_from_lp_string(lp_string)
def import_from_lp_file(self, lp_file: str) -> bool:
"""Loads the a model from an LP file."""
"""Reads a model from a .lp file."""
return self.__helper.import_from_lp_file(lp_file)
def import_from_proto_file(self, proto_file: str) -> bool:
"""Loads the a model from an proto file."""
return self.__helper.load_model_from_file(proto_file)
"""Reads a model from a proto file."""
return self.__helper.read_model_from_proto_file(proto_file)
def export_to_proto_file(self, proto_file: str) -> bool:
"""Write a model to a proto file."""
return self.__helper.write_model_to_file(proto_file)
"""Writes a model to a proto file."""
return self.__helper.write_model_to_proto_file(proto_file)
# Model getters and Setters

View File

@@ -175,10 +175,10 @@ PYBIND11_MODULE(model_builder_helper, m) {
arg("options") = MPModelExportOptions())
.def("export_to_lp_string", &ModelBuilderHelper::ExportToLpString,
arg("options") = MPModelExportOptions())
.def("write_model_to_file", &ModelBuilderHelper::WriteModelToFile,
arg("filename"))
.def("load_model_from_file", &ModelBuilderHelper::LoadModelFromFile,
arg("filename"))
.def("read_model_from_proto_file",
&ModelBuilderHelper::ReadModelFromProtoFile, arg("filename"))
.def("write_model_to_proto_file",
&ModelBuilderHelper::WriteModelToProtoFile, arg("filename"))
.def("import_from_mps_string", &ModelBuilderHelper::ImportFromMpsString,
arg("mps_string"))
.def("import_from_mps_file", &ModelBuilderHelper::ImportFromMpsFile,

View File

@@ -32,7 +32,7 @@ def main(argv: Sequence[str]) -> None:
model = model_builder.ModelBuilder()
# Load MPS file.
# Load MPS or proto file.
if _INPUT.value.endswith(".mps"):
if not model.import_from_mps_file(_INPUT.value):
print(f"Cannot import MPS file: '{_INPUT.value}'")
@@ -41,7 +41,6 @@ def main(argv: Sequence[str]) -> None:
print(f"Cannot import Proto file: '{_INPUT.value}'")
return
# Create solver.
solver = model_builder.ModelSolver(_SOLVER.value)
if not solver.solver_is_supported():

View File

@@ -30,6 +30,7 @@ def create_data_model():
data["bins"] = data["items"]
data["bin_capacity"] = 100
return data
# [END data_model]

View File

@@ -36,6 +36,7 @@ def create_data_model():
data["num_vars"] = 5
data["num_constraints"] = 4
return data
# [END data_model]

View File

@@ -146,8 +146,8 @@ MPModelRequest ReadMipModel(const std::string& input) {
QCHECK_OK(glop::MPSReader().ParseFile(input, &model_proto))
<< "Error while parsing the mps file '" << input << "'.";
} else {
ReadFileToProto(input, &model_proto);
ReadFileToProto(input, &request_proto);
ReadFileToProto(input, &model_proto).IgnoreError();
ReadFileToProto(input, &request_proto).IgnoreError();
}
// If the input is a proto in binary format, both ReadFileToProto could
// return true. Instead use the actual number of variables found to test the
@@ -326,13 +326,13 @@ void Run() {
// If requested, save the model and/or request to file.
if (!absl::GetFlag(FLAGS_dump_model).empty()) {
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model),
request_proto.model(), write_format,
absl::GetFlag(FLAGS_dump_gzip)));
CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model),
request_proto.model(), write_format,
absl::GetFlag(FLAGS_dump_gzip)));
}
if (!absl::GetFlag(FLAGS_dump_request).empty()) {
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request_proto,
write_format, absl::GetFlag(FLAGS_dump_gzip)));
CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request_proto,
write_format, absl::GetFlag(FLAGS_dump_gzip)));
}
absl::PrintF(
@@ -377,8 +377,8 @@ void Run() {
file::Defaults()));
}
if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) {
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response,
write_format, absl::GetFlag(FLAGS_dump_gzip)));
CHECK_OK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response,
write_format, absl::GetFlag(FLAGS_dump_gzip)));
}
if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) {
std::string csv_file;

View File

@@ -24,6 +24,7 @@
#include "absl/log/check.h"
#include "absl/strings/match.h"
#include "ortools/base/helpers.h"
#include "ortools/base/logging.h"
#include "ortools/base/options.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/linear_solver.pb.h"
@@ -60,24 +61,28 @@ std::string ModelBuilderHelper::ExportToLpString(
.value_or("");
}
bool ModelBuilderHelper::WriteModelToFile(const std::string& filename) {
if (absl::EndsWith(filename, "txt") ||
absl::EndsWith(filename, ".textproto")) {
bool ModelBuilderHelper::ReadModelFromProtoFile(const std::string& filename) {
if (file::GetTextProto(filename, &model_, file::Defaults()).ok() ||
file::GetBinaryProto(filename, &model_, file::Defaults()).ok()) {
return true;
}
MPModelRequest request;
if (file::GetTextProto(filename, &request, file::Defaults()).ok() ||
file::GetBinaryProto(filename, &request, file::Defaults()).ok()) {
model_ = request.model();
return true;
}
return false;
}
bool ModelBuilderHelper::WriteModelToProtoFile(const std::string& filename) {
if (absl::EndsWith(filename, "txt")) {
return file::SetTextProto(filename, model_, file::Defaults()).ok();
} else {
return file::SetBinaryProto(filename, model_, file::Defaults()).ok();
}
}
bool ModelBuilderHelper::LoadModelFromFile(const std::string& filename) {
if (absl::EndsWith(filename, "txt") ||
absl::EndsWith(filename, ".textproto")) {
return file::GetTextProto(filename, &model_, file::Defaults()).ok();
} else {
return file::GetBinaryProto(filename, &model_, file::Defaults()).ok();
}
}
// See comment in the header file why we need to wrap absl::Status code with
// code having simpler APIs.
bool ModelBuilderHelper::ImportFromMpsString(const std::string& mps_string) {

View File

@@ -50,8 +50,8 @@ class ModelBuilderHelper {
options = MPModelExportOptions());
std::string ExportToLpString(const operations_research::MPModelExportOptions&
options = MPModelExportOptions());
bool WriteModelToFile(const std::string& filename);
bool LoadModelFromFile(const std::string& filename);
bool ReadModelFromProtoFile(const std::string& filename);
bool WriteModelToProtoFile(const std::string& filename);
bool ImportFromMpsString(const std::string& mps_string);
bool ImportFromMpsFile(const std::string& mps_file);

View File

@@ -29,8 +29,8 @@ bool LoadMPModelProtoFromModelOrRequest(const std::string& input_file_path,
MPModelProto* model) {
MPModelProto model_proto;
MPModelRequest request_proto;
ReadFileToProto(input_file_path, &model_proto);
ReadFileToProto(input_file_path, &request_proto);
ReadFileToProto(input_file_path, &model_proto).IgnoreError();
ReadFileToProto(input_file_path, &request_proto).IgnoreError();
// If the input proto is in binary format, both ReadFileToProto could return
// true. Instead use the actual number of variables found to test the
// correct format of the input.

View File

@@ -183,8 +183,5 @@ py_library(
name = "normalize",
srcs = ["normalize.py"],
visibility = ["//ortools/math_opt/python:__subpackages__"],
deps = [
# "@com_google_protobuf//protobuf:duration_py_pb2",
"@com_google_protobuf//:protobuf_python",
],
deps = ["@com_google_protobuf//:protobuf_python"],
)

View File

@@ -215,8 +215,8 @@ cc_library(
"//ortools/base:protoutil",
"//ortools/base:status_macros",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver/proto_solver:sat_proto_solver",
"//ortools/linear_solver/proto_solver:proto_utils",
"//ortools/linear_solver/proto_solver:sat_proto_solver",
"//ortools/math_opt:callback_cc_proto",
"//ortools/math_opt:infeasible_subsystem_cc_proto",
"//ortools/math_opt:model_cc_proto",

View File

@@ -69,7 +69,8 @@ QuadraticProgram ReadQuadraticProgramOrDie(const std::string& filename,
QuadraticProgram ReadMPModelProtoFileOrDie(
const std::string& mpmodel_proto_file, bool include_names) {
MPModelProto lp_proto;
QCHECK(ReadFileToProto(mpmodel_proto_file, &lp_proto)) << mpmodel_proto_file;
QCHECK_OK(ReadFileToProto(mpmodel_proto_file, &lp_proto))
<< mpmodel_proto_file;
auto result = QpFromMpModelProto(lp_proto, /*relax_integer_variables=*/true,
include_names);
QCHECK_OK(result);

View File

@@ -98,7 +98,7 @@ bool LoadProblem(const std::string& filename, CpModelProto* cp_model) {
}
} else {
LOG(INFO) << "Reading a CpModelProto.";
*cp_model = ReadFileToProtoOrDie<CpModelProto>(filename);
CHECK_OK(ReadFileToProto(filename, cp_model));
}
if (cp_model->name().empty()) {
cp_model->set_name(ExtractName(filename));

View File

@@ -295,6 +295,7 @@ cc_library(
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"//ortools/base",
"//ortools/base:dump_vars",
"//ortools/base:file",
"//ortools/base:hash",
"//ortools/base:recordio",

View File

@@ -16,8 +16,11 @@
#include <string>
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/io/tokenizer.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "google/protobuf/json/json.h"
#include "google/protobuf/message.h"
@@ -45,17 +48,23 @@ absl::StatusOr<std::string> ReadFileToString(absl::string_view filename) {
return contents;
}
bool ReadFileToProto(absl::string_view filename,
google::protobuf::Message* proto, bool allow_partial) {
absl::Status ReadFileToProto(absl::string_view filename,
google::protobuf::Message* proto,
bool allow_partial) {
std::string data;
CHECK_OK(file::GetContents(filename, &data, file::Defaults()));
RETURN_IF_ERROR(file::GetContents(filename, &data, file::Defaults()));
return util::StatusBuilder(StringToProto(data, proto, allow_partial))
<< " in file '" << filename << "'";
}
absl::Status StringToProto(absl::string_view data,
google::protobuf::Message* proto,
bool allow_partial) {
// Try decompressing it.
{
std::string uncompressed;
if (GunzipString(data, &uncompressed)) {
VLOG(1) << "ReadFileToProto(): input is gzipped";
data.swap(uncompressed);
}
std::string uncompressed;
if (GunzipString(data, &uncompressed)) {
VLOG(1) << "ReadFileToProto(): input is gzipped";
data = uncompressed;
}
// Try binary format first, then text format, then JSON, then proto3 JSON,
// then give up.
@@ -68,6 +77,7 @@ bool ReadFileToProto(absl::string_view filename,
// the case that the proto version changed and some fields are dropped.
// We just fail when the difference is too large.
constexpr double kMaxBinaryProtoParseShrinkFactor = 2;
std::string binary_format_error;
if (proto->ParsePartialFromString(data) &&
(allow_partial || proto->IsInitialized())) {
// NOTE(user): When using ParseFromString() from a generic
@@ -78,60 +88,76 @@ bool ReadFileToProto(absl::string_view filename,
proto->DiscardUnknownFields();
if (proto->ByteSizeLong() <
data.size() / kMaxBinaryProtoParseShrinkFactor) {
VLOG(1) << "ReadFileToProto(): input may be a binary proto, but of a "
"different proto";
binary_format_error =
"The input may be a binary protobuf payload, but it"
" probably is from a different proto class";
} else {
VLOG(1) << "ReadFileToProto(): input seems to be a binary proto";
return true;
VLOG(1) << "StringToProto(): input seems to be a binary proto";
return absl::OkStatus();
}
}
struct : public google::protobuf::io::ErrorCollector {
void RecordError(int line, google::protobuf::io::ColumnNumber column,
absl::string_view error_message) override {
if (!message.empty()) message += ", ";
absl::StrAppendFormat(&message, "%d:%d: %s",
// We convert the 0-indexed line to 1-indexed.
line + 1, column, error_message);
}
std::string message;
} text_format_error;
google::protobuf::TextFormat::Parser text_parser;
text_parser.RecordErrorsTo(&text_format_error);
text_parser.AllowPartialMessage(allow_partial);
if (text_parser.ParseFromString(data, proto)) {
VLOG(1) << "ReadFileToProto(): input is a text proto";
return true;
VLOG(1) << "StringToProto(): input is a text proto";
return absl::OkStatus();
}
// We use `auto` here since protobuf does not use absl::Status.
const auto status = JsonStringToMessage(data, proto, JsonParseOptions());
if (!status.ok()) {
VLOG(1) << status;
} else {
std::string json_error;
absl::Status json_status =
JsonStringToMessage(data, proto, JsonParseOptions());
if (json_status.ok()) {
// NOTE(user): We protect against the JSON proto3 parser being very lenient
// and easily accepting any JSON as a valid JSON for our proto: if the
// parsed proto's size is too small compared to the JSON, we probably parsed
// a JSON that wasn't representing a valid proto.
constexpr int kMaxJsonToBinaryShrinkFactor = 30;
if (proto->ByteSizeLong() < data.size() / kMaxJsonToBinaryShrinkFactor) {
VLOG(1) << "ReadFileToProto(): input is probably JSON, but probably not"
" of the right proto";
json_status = absl::InvalidArgumentError(
"The input looks like valid JSON, but probably not"
" of the right proto class");
} else {
VLOG(1) << "ReadFileToProto(): input is a proto JSON";
return true;
VLOG(1) << "StringToProto(): input is a proto JSON";
return absl::OkStatus();
}
}
LOG(WARNING) << "Could not parse protocol buffer";
return false;
return absl::InvalidArgumentError(absl::StrFormat(
"binary format error: '%s', text format error: '%s', json error: '%s'",
binary_format_error, text_format_error.message, json_status.message()));
}
bool WriteProtoToFile(absl::string_view filename,
const google::protobuf::Message& proto,
ProtoWriteFormat proto_write_format, bool gzipped,
bool append_extension_to_file_name) {
absl::Status WriteProtoToFile(absl::string_view filename,
const google::protobuf::Message& proto,
ProtoWriteFormat proto_write_format, bool gzipped,
bool append_extension_to_file_name) {
std::string file_type_suffix;
std::string output_string;
google::protobuf::io::StringOutputStream stream(&output_string);
auto make_error = [filename](absl::string_view error_message) {
return absl::InternalError(absl::StrFormat(
"WriteProtoToFile('%s') failed: %s", filename, error_message));
};
switch (proto_write_format) {
case ProtoWriteFormat::kProtoBinary:
if (!proto.SerializeToZeroCopyStream(&stream)) {
LOG(WARNING) << "Serialize to stream failed.";
return false;
return make_error("SerializeToZeroCopyStream()");
}
file_type_suffix = ".bin";
break;
case ProtoWriteFormat::kProtoText:
if (!google::protobuf::TextFormat::PrintToString(proto, &output_string)) {
LOG(WARNING) << "Printing to string failed.";
return false;
return make_error("TextFormat::PrintToString()");
}
break;
case ProtoWriteFormat::kJson: {
@@ -142,8 +168,7 @@ bool WriteProtoToFile(absl::string_view filename,
if (!google::protobuf::util::MessageToJsonString(proto, &output_string,
options)
.ok()) {
LOG(WARNING) << "Printing to stream failed.";
return false;
return make_error("Printing to stream failed.");
}
file_type_suffix = ".json";
break;
@@ -154,8 +179,7 @@ bool WriteProtoToFile(absl::string_view filename,
if (!google::protobuf::util::MessageToJsonString(proto, &output_string,
options)
.ok()) {
LOG(WARNING) << "Printing to stream failed.";
return false;
return make_error("Printing to stream failed.");
}
file_type_suffix = ".json";
break;
@@ -168,14 +192,9 @@ bool WriteProtoToFile(absl::string_view filename,
}
std::string output_filename(filename);
if (append_extension_to_file_name) output_filename += file_type_suffix;
VLOG(1) << "Writing " << output_string.size() << " bytes to "
<< output_filename;
if (!file::SetContents(output_filename, output_string, file::Defaults())
.ok()) {
LOG(WARNING) << "Writing to file failed.";
return false;
}
return true;
VLOG(1) << "Writing " << output_string.size() << " bytes to '"
<< output_filename << "'";
return file::SetContents(output_filename, output_string, file::Defaults());
}
} // namespace operations_research

View File

@@ -21,9 +21,11 @@
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/message.h"
#include "ortools/base/dump_vars.h"
#include "ortools/base/file.h"
#include "ortools/base/options.h"
#include "ortools/base/recordio.h"
#include "ortools/base/status_macros.h"
namespace operations_research {
@@ -31,23 +33,27 @@ namespace operations_research {
absl::StatusOr<std::string> ReadFileToString(absl::string_view filename);
// Reads a proto from a file. Supports the following formats: binary, text,
// JSON, all of those optionally gzipped. Crashes on filesystem failures, e.g.
// file unreadable. Returns false on format failures, e.g. the file could be
// read, but the contents couldn't be parsed -- or maybe it was a valid JSON,
// text proto, or binary proto, but not of the right proto message.
// Returns true on success.
bool ReadFileToProto(absl::string_view filename,
google::protobuf::Message* proto,
// If true, unset required fields don't cause errors. This
// boolean doesn't work for JSON inputs.
bool allow_partial = false);
// JSON, all of those optionally gzipped. Returns errors as expected: filesystem
// error, parsing errors, or type error: maybe it was a valid JSON, text proto,
// or binary proto, but not of the right proto message (this is not an exact
// science, but the heuristics used should work well in practice).
absl::Status ReadFileToProto(
absl::string_view filename, google::protobuf::Message* proto,
// If true, unset required fields don't cause errors. This
// boolean doesn't work for JSON inputs.
bool allow_partial = false);
// Exaclty like ReadFileToProto(), but directly from the contents.
absl::Status StringToProto(absl::string_view data,
google::protobuf::Message* proto,
bool allow_partial = false);
template <typename Proto>
Proto ReadFileToProtoOrDie(absl::string_view filename,
bool allow_partial = false) {
absl::StatusOr<Proto> ReadFileToProto(absl::string_view filename,
bool allow_partial = false) {
Proto proto;
CHECK(ReadFileToProto(filename, &proto, allow_partial))
<< "with file: '" << filename << "'";
RETURN_IF_ERROR(ReadFileToProto(filename, &proto, allow_partial))
<< DUMP_VARS(filename);
return proto;
}
@@ -56,14 +62,15 @@ Proto ReadFileToProtoOrDie(absl::string_view filename,
enum class ProtoWriteFormat { kProtoText, kProtoBinary, kJson, kCanonicalJson };
// Writes a proto to a file. Supports the following formats: binary, text, JSON,
// all of those optionally gzipped. Returns false on failure.
// all of those optionally gzipped.
// If 'proto_write_format' is kProtoBinary, ".bin" is appended to file_name. If
// 'proto_write_format' is kJson or kCanonicalJson, ".json" is appended to
// file_name. If 'gzipped' is true, ".gz" is appended to file_name.
bool WriteProtoToFile(absl::string_view filename,
const google::protobuf::Message& proto,
ProtoWriteFormat proto_write_format, bool gzipped = false,
bool append_extension_to_file_name = true);
absl::Status WriteProtoToFile(absl::string_view filename,
const google::protobuf::Message& proto,
ProtoWriteFormat proto_write_format,
bool gzipped = false,
bool append_extension_to_file_name = true);
namespace internal {
// General method to read expected_num_records from a file. If