From e6ff7153aa72e8a723f4700b0977d7c03f991b73 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 1 Jul 2019 14:13:15 +0200 Subject: [PATCH] polish new linear_solver non C++ API --- makefiles/Makefile.java.mk | 6 ++ ortools/linear_solver/java/linear_solver.i | 89 +++++++++++++++++-- ortools/linear_solver/python/linear_solver.i | 7 +- .../samples/LinearProgrammingExample.cs | 1 - 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/makefiles/Makefile.java.mk b/makefiles/Makefile.java.mk index e1168184f8..512cb5463c 100755 --- a/makefiles/Makefile.java.mk +++ b/makefiles/Makefile.java.mk @@ -263,6 +263,11 @@ $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.java: \ | $(GEN_DIR)/com/google/ortools/constraintsolver $(PROTOC) --proto_path=$(SRC_DIR) --java_out=$(GEN_PATH) $(SRC_DIR)$Sortools$Sconstraint_solver$Srouting_enums.proto +$(GEN_DIR)/com/google/ortools/linearsolver/MPModelProto.java: \ + $(SRC_DIR)/ortools/linear_solver/linear_solver.proto \ + | $(GEN_DIR)/com/google/ortools/linearsolver + $(PROTOC) --proto_path=$(SRC_DIR) --java_out=$(GEN_PATH) $(SRC_DIR)$Sortools$Slinear_solver$Slinear_solver.proto + $(GEN_DIR)/com/google/ortools/sat/CpModel.java: \ $(SRC_DIR)/ortools/sat/cp_model.proto \ | $(GEN_DIR)/com/google/ortools/sat @@ -285,6 +290,7 @@ $(JAVA_OR_TOOLS_LIBS): \ $(GEN_DIR)/com/google/ortools/constraintsolver/SearchLimitProtobuf.java \ $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingParameters.java \ $(GEN_DIR)/com/google/ortools/constraintsolver/RoutingEnums.java \ + $(GEN_DIR)/com/google/ortools/linearsolver/MPModelProto.java \ $(GEN_DIR)/com/google/ortools/sat/SatParameters.java \ $(GEN_DIR)/com/google/ortools/util/OptionalBoolean.java \ $(GEN_DIR)/com/google/ortools/sat/CpModel.java | \ diff --git a/ortools/linear_solver/java/linear_solver.i b/ortools/linear_solver/java/linear_solver.i index eeec1b1cb8..ebc0ad38a5 100644 --- a/ortools/linear_solver/java/linear_solver.i +++ b/ortools/linear_solver/java/linear_solver.i @@ -30,12 +30,12 @@ %include "ortools/base/base.i" -%include "ortools/util/java/proto.i" - // We prefer our in-house vector wrapper to std_vector.i, because it // converts to and from native java arrays. %import "ortools/util/java/vector.i" +%include "ortools/util/java/proto.i" + // We need to forward-declare the proto here, so that the PROTO_* macros // involving them work correctly. The order matters very much: this declaration // needs to be before the %{ #include ".../linear_solver.h" %}. @@ -50,17 +50,79 @@ class MPSolutionResponse; #include "ortools/linear_solver/model_exporter.h" %} +%typemap(javaimports) SWIGTYPE %{ +import java.lang.reflect.*; +%} + +// Conversion of array of MPVariable or MPConstraint from/to C++ vectors. CONVERT_VECTOR_WITH_CAST(operations_research::MPVariable, MPVariable, REINTERPRET_CAST, com/google/ortools/linearsolver); CONVERT_VECTOR_WITH_CAST(operations_research::MPConstraint, MPConstraint, REINTERPRET_CAST, com/google/ortools/linearsolver); -%typemap(javaimports) SWIGTYPE %{ -import java.lang.reflect.*; -%} +// Support the proto-based APIs. +// TODO(user): expose these in the open-source export as well. +PROTO_INPUT( + operations_research::MPModelProto, + com.google.ortools.linearsolver.MPModelProto, + input_model); +PROTO2_RETURN( + operations_research::MPModelProto, + com.google.ortools.linearsolver.MPModelProto); +PROTO_INPUT( + operations_research::MPModelRequest, + com.google.ortools.linearsolver.MPModelRequest, + model_request); +PROTO2_RETURN( + operations_research::MPSolutionResponse, + com.google.ortools.linearsolver.MPSolutionResponse); %extend operations_research::MPSolver { + // Replaces MPSolver::LoadModelFromProto. We simply return the error message, + // which will be empty iff the model is valid. + std::string loadModelFromProto(const operations_research::MPModelProto& input_model) { + std::string error_message; + $self->LoadModelFromProto(input_model, &error_message); + return error_message; + } + + // Replaces MPSolver::LoadModelFromProtoWithUniqueNamesOrDie + std::string loadModelFromProtoWithUniqueNamesOrDie( + const operations_research::MPModelProto& input_model) { + std::unordered_set names; + for (const auto var : input_model.variable()) { + if (!var.name().empty() && !names.insert(var.name()).second) { + LOG(FATAL) << "found duplicated variable names " + var.name(); + } + } + std::string error_message; + $self->LoadModelFromProtoWithUniqueNamesOrDie(input_model, &error_message); + return error_message; + } + + // Replaces MPSolver::ExportModelToProto + operations_research::MPModelProto exportModelToProto() { + operations_research::MPModelProto model; + $self->ExportModelToProto(&model); + return model; + } + + // Replaces MPSolver::FillSolutionResponseProto + operations_research::MPSolutionResponse createSolutionResponseProto() { + operations_research::MPSolutionResponse response; + $self->FillSolutionResponseProto(&response); + return response; + } + + // Replaces MPSolver::SolveWithProto + static operations_research::MPSolutionResponse solveWithProto( + const operations_research::MPModelRequest& model_request) { + operations_research::MPSolutionResponse response; + operations_research::MPSolver::SolveWithProto(model_request, &response); + return response; + } + std::string exportModelAsLpFormat( const operations_research::MPModelExportOptions& options = operations_research::MPModelExportOptions()) { @@ -77,7 +139,7 @@ import java.lang.reflect.*; return ExportModelAsMpsFormat(model, options).value_or(""); } - /// Set a hint for solution. + /// Sets a hint for solution. /// /// If a feasible or almost-feasible solution to the problem is already known, /// it may be helpful to pass it to the solver so that it can be used. A @@ -106,7 +168,7 @@ import java.lang.reflect.*; bool setNumThreads(int num_theads) { return $self->SetNumThreads(num_theads).ok(); } -} +} // Extend operations_research::MPSolver // Add java code on MPSolver. %typemap(javacode) operations_research::MPSolver %{ @@ -150,7 +212,7 @@ import java.lang.reflect.*; public MPVariable[] makeBoolVarArray(int count, String var_name) { return makeVarArray(count, 0.0, 1.0, true, var_name); } -%} +%} // %typemap(javacode) operations_research::MPSolver %ignoreall @@ -212,6 +274,17 @@ import java.lang.reflect.*; %rename (infinity) operations_research::MPSolver::infinity; %rename (setTimeLimit) operations_research::MPSolver::set_time_limit; // no test +// Proto-based API of the MPSolver. Use is encouraged. +// Note: the following proto-based methods aren't listed here, but are +// supported (that's because we re-implement them in java below): +// - loadModelFromProto +// - exportModelToProto +// - createSolutionResponseProto +// - solveWithProto +%unignore operations_research::MPSolver::LoadStatus; +%unignore operations_research::MPSolver::NO_ERROR; // no test +%unignore operations_research::MPSolver::UNKNOWN_VARIABLE_ID; // no test +%rename (loadSolutionFromProto) operations_research::MPSolver::LoadSolutionFromProto; // no test // Expose some of the more advanced MPSolver API. %rename (supportsProblemType) operations_research::MPSolver::SupportsProblemType; // no test diff --git a/ortools/linear_solver/python/linear_solver.i b/ortools/linear_solver/python/linear_solver.i index 50e12b292c..ca68534ac3 100644 --- a/ortools/linear_solver/python/linear_solver.i +++ b/ortools/linear_solver/python/linear_solver.i @@ -28,6 +28,7 @@ // // TODO(user): test all the APIs that are currently marked as 'untested'. +%include "std_string.i" %include "stdint.i" %include "ortools/base/base.i" @@ -184,6 +185,7 @@ from ortools.linear_solver.linear_solver_natural_api import VariableExpr } } + static double Infinity() { return operations_research::MPSolver::infinity(); } void SetTimeLimit(int64 x) { $self->set_time_limit(x); } int64 WallTime() const { return $self->wall_time(); } @@ -223,6 +225,7 @@ PY_PROTO_TYPEMAP(ortools.linear_solver.linear_solver_pb2, // Actual conversions. This also includes the conversion to std::vector. %define PY_CONVERT(Class) %{ +// Forcing SWIGTYPE_p_.... $descriptor( ) does not work outside of typemaps. template<> bool PyObjAs(PyObject *py_obj, operations_research::Class** b) { return SWIG_ConvertPtr(py_obj, reinterpret_cast(b), @@ -231,8 +234,8 @@ bool PyObjAs(PyObject *py_obj, operations_research::Class** b) { } PyObject* FromObject ## Class(operations_research::Class* obj) { - return SWIG_NewPointerObj(obj, - SWIGTYPE_p_operations_research__ ## Class, + return SWIG_NewPointerObj(obj, + SWIGTYPE_p_operations_research__ ## Class, SWIG_POINTER_NOSHADOW); } diff --git a/ortools/linear_solver/samples/LinearProgrammingExample.cs b/ortools/linear_solver/samples/LinearProgrammingExample.cs index 1f9d7b57ed..0d5be87643 100644 --- a/ortools/linear_solver/samples/LinearProgrammingExample.cs +++ b/ortools/linear_solver/samples/LinearProgrammingExample.cs @@ -11,7 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Linear optimization example. // [START program] using System; using Google.OrTools.LinearSolver;