From 5880a2776d3d631b30eed7a99807c41b4c8f7162 Mon Sep 17 00:00:00 2001 From: "lperron@google.com" Date: Wed, 13 Jul 2011 23:59:27 +0000 Subject: [PATCH] MPSolver can accept protocol buffers as input and as output, examples in examples/linear_solver_example_with_protocol_buffers.cc --- Makefile.build.cpp | 9 +++- linear_solver/linear_solver.cc | 4 +- linear_solver/linear_solver.h | 3 -- linear_solver/linear_solver.proto | 2 + linear_solver/linear_solver.swig | 23 ++++++++- util/data.swig | 82 +++++++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 7 deletions(-) diff --git a/Makefile.build.cpp b/Makefile.build.cpp index eda031c870..9a1d14e22a 100644 --- a/Makefile.build.cpp +++ b/Makefile.build.cpp @@ -48,7 +48,8 @@ cpexe: $(CPBINARIES) LPBINARIES = \ integer_solver_example$E \ - linear_solver_example$E + linear_solver_example$E \ + linear_solver_example_with_protocol_buffers$E lpexe: $(LPBINARIES) @@ -492,6 +493,12 @@ objs/linear_solver_example.$O: examples/linear_solver_example.cc linear_solver_example$E: $(LP_LIBS) $(BASE_LIBS) objs/linear_solver_example.$O $(CCC) $(CFLAGS) $(LDFLAGS) objs/linear_solver_example.$O $(LP_LIBS) $(BASE_LIBS) $(LDLPDEPS) $(EXEOUT)linear_solver_example$E +objs/linear_solver_example_with_protocol_buffers.$O: examples/linear_solver_example_with_protocol_buffers.cc + $(CCC) $(CFLAGS) -c examples/linear_solver_example_with_protocol_buffers.cc $(OBJOUT)objs/linear_solver_example_with_protocol_buffers.$O + +linear_solver_example_with_protocol_buffers$E: $(LP_LIBS) $(BASE_LIBS) objs/linear_solver_example_with_protocol_buffers.$O + $(CCC) $(CFLAGS) $(LDFLAGS) objs/linear_solver_example_with_protocol_buffers.$O $(LP_LIBS) $(BASE_LIBS) $(LDLPDEPS) $(EXEOUT)linear_solver_example_with_protocol_buffers$E + objs/integer_solver_example.$O: examples/integer_solver_example.cc $(CCC) $(CFLAGS) -c examples/integer_solver_example.cc $(OBJOUT)objs/integer_solver_example.$O diff --git a/linear_solver/linear_solver.cc b/linear_solver/linear_solver.cc index 27937720a0..695b826bb4 100644 --- a/linear_solver/linear_solver.cc +++ b/linear_solver/linear_solver.cc @@ -28,6 +28,7 @@ #include "base/map-util.h" #include "base/stl_util.h" #include "base/hash.h" + #include "linear_solver/linear_solver.pb.h" DEFINE_string(solver_write_model, "", "path of the file to write the model to"); @@ -503,7 +504,8 @@ void MPSolver::ExportModel(MPModelProto* model) const { // Encode current solution in a solution response protocol buffer. void MPSolver::FillSolutionResponse(MPSolutionResponse* response) const { CHECK_NOTNULL(response); - if (response->has_result_status() || + if ((response->has_result_status() && + response->result_status() != MPSolutionResponse::NOT_SOLVED) || response->has_objective_value() || response->solution_values_size() > 0) { LOG(WARNING) << "The solution response is not empty, " diff --git a/linear_solver/linear_solver.h b/linear_solver/linear_solver.h index 44f7970dea..c58e782b2f 100644 --- a/linear_solver/linear_solver.h +++ b/linear_solver/linear_solver.h @@ -286,8 +286,6 @@ class MPSolver { virtual ~MPSolver(); // ----- Methods using protocol buffers ----- -#ifndef SWIG - // TODO(user): fix swig support. // Loads model from protocol buffer. LoadStatus LoadModel(const MPModelProto& model); @@ -304,7 +302,6 @@ class MPSolver { // of MPSolver, MPModelRequest.OptimizationProblemType is ignored. void SolveWithProtocolBuffers(const MPModelRequest& model_request, MPSolutionResponse* response); -#endif // ----- Init and Clear ----- void Init() {} // To remove. diff --git a/linear_solver/linear_solver.proto b/linear_solver/linear_solver.proto index fe7427234e..4996c45ae1 100644 --- a/linear_solver/linear_solver.proto +++ b/linear_solver/linear_solver.proto @@ -14,6 +14,8 @@ // Linear solver Stubby services syntax = "proto2"; +option java_package = "com.google.ortools.linearsolver"; +option java_multiple_files = true; package operations_research; diff --git a/linear_solver/linear_solver.swig b/linear_solver/linear_solver.swig index 2d2c0cbfc6..c02081ec51 100644 --- a/linear_solver/linear_solver.swig +++ b/linear_solver/linear_solver.swig @@ -11,11 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -%include "base/base.swig" +%include base/base.swig +%include util/data.swig // Include the file we want to wrap a first time. %{ #include "linear_solver/linear_solver.h" +#include "linear_solver/linear_solver.pb.h" %} #ifdef SWIGPYTHON @@ -32,6 +34,12 @@ %ignore MakeNumVarArray; %ignore MakeIntVarArray; %ignore MakeBoolVarArray; +// The following 3 methods that use protocol buffers as output +// arguments are ignored as they are too difficult to wrap and there +// is no immediate use case. +%ignore ExportModel; +%ignore SolveWithProtocolBuffers; +%ignore FillSolutionResponse; namespace operations_research { %pythoncode { @@ -347,6 +355,7 @@ class LinearConstraint(object): #endif #ifdef SWIGJAVA + %module(directors="1") operations_research; namespace operations_research { @@ -392,6 +401,7 @@ namespace operations_research { %rename (clearObjective) MPSolver::ClearObjective; %rename (init) MPSolver::Init; %rename (isLpFormat) MPSolver::IsLPFormat; +%rename (loadModel) MPSolver::LoadModel; %rename (makeBoolVar) MPSolver::MakeBoolVar; %rename (makeIntVar) MPSolver::MakeIntVar; %rename (makeNumVar) MPSolver::MakeNumVar; @@ -415,11 +425,19 @@ namespace operations_research { %rename (timeLimit) MPSolver::time_limit; %rename (wallTime) MPSolver::wall_time; %rename (writeModelFilename) MPSolver::write_model_filename; -// Ignore code on MPSolver. +// Ignore code on MPSolver, see replacement java code below. %ignore MPSolver::MakeVarArray; %ignore MPSolver::MakeNumVarArray; %ignore MPSolver::MakeIntVarArray; %ignore MPSolver::MakeBoolVarArray; +// The following 3 methods that use protocol buffers as output +// arguments are replaced by methods that return a protocol buffer, +// see code below. +%ignore MPSolver::ExportModel; +%ignore MPSolver::FillSolutionResponse; +%ignore MPSolver::SolveWithProtocolBuffers; + + // Add java code on MPSolver. %typemap(javacode) MPSolver %{ public MPVariable[] makeVarArray(int count, @@ -475,6 +493,7 @@ namespace operations_research { return makeVarArray(count, 0.0, 1.0, true, var_name); } %} + } // namespace operations_research #endif // SWIGJAVA diff --git a/util/data.swig b/util/data.swig index 9fa8cda701..d872cda5be 100644 --- a/util/data.swig +++ b/util/data.swig @@ -256,6 +256,12 @@ static bool PyCallbackBool(PyObject* pyfunc) { #endif // SWIGPYTHON #if defined(SWIGJAVA) + +%{ +#include "base/jniutil.h" +#include "base/scoped_ptr.h" +%} + %module(directors="1") main %feature("director") LongResultCallback1; %feature("director") LongResultCallback2; @@ -395,4 +401,80 @@ class LongResultCallback3 { $1 = &result; } +// SWIG macros to be used in generating Java wrappers for C++ protocol +// message parameters. Each protocol message is serialized into +// byte[] before passing into (or returning from) C++ code. + +// If the C++ function expects an input protocol message: +// foo(const MyProto* message,...) +// Use PROTO_INPUT macro: +// PROTO_INPUT(MyProto, com.google.proto.protos.test.MyProto, message) +// +// if the C++ function returns a protocol message: +// MyProto* foo(); +// Use PROTO2_RETURN_AND_DELETE macro: +// PROTO2_RETURN_AND_DELETE(MyProto, com.google.proto.protos.test.MyProto) + +// Passing each protocol message from Java to C++ by value. Each ProtocolMessage +// is serialized into byte[] when it is passed from Java to C++, the C++ code +// deserializes into C++ native protocol message. +// +// @param CppProtoType the fully qualified C++ protocol message type +// @param JavaProtoType the corresponding fully qualified Java protocol message +// type +// @param param_name the parameter name +%define PROTO_INPUT(CppProtoType, JavaProtoType, param_name) +%typemap(jni) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "jbyteArray" +%typemap(jtype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "byte[]" +%typemap(jstype) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "JavaProtoType" +%typemap(javain) PROTO_TYPE* INPUT, PROTO_TYPE& INPUT "$javainput.toByteArray()" +%typemap(in) PROTO_TYPE* INPUT (CppProtoType temp), + PROTO_TYPE& INPUT (CppProtoType temp) { + int proto_size = 0; + scoped_array proto_buffer( + JNIUtil::MakeCharArray(jenv, $input, &proto_size)); + bool parsed_ok = temp.ParseFromArray(proto_buffer.get(), proto_size); + if (!parsed_ok) { + SWIG_JavaThrowException(jenv, + SWIG_JavaRuntimeException, + "Unable to parse CppProtoType protocol message."); + } + $1 = &temp; +} + +%apply PROTO_TYPE& INPUT { const CppProtoType& param_name } +%apply PROTO_TYPE& INPUT { CppProtoType& param_name } +%apply PROTO_TYPE* INPUT { const CppProtoType* param_name } +%apply PROTO_TYPE* INPUT { CppProtoType* param_name } + +%enddef // end PROTO_INPUT + +%define PROTO2_RETURN(CppProtoType, JavaProtoType, deleteCppReturn) +%typemap(jni) CppProtoType* "jbyteArray" +%typemap(jtype) CppProtoType* "byte[]" +%typemap(jstype) CppProtoType* "JavaProtoType" +%typemap(javaout) CppProtoType* { + byte[] buf = $jnicall; + if (buf == null || buf.length == 0) { + return null; + } + try { + return JavaProtoType.parseFrom(buf); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw new RuntimeException( + "Unable to parse JavaProtoType protocol message."); + } +} +%typemap(out) CppProtoType* { + scoped_array buf(new char[$1->ByteSize()]); + $1->SerializeWithCachedSizesToArray(reinterpret_cast(buf.get())); + $result = JNIUtil::MakeJByteArray(jenv, buf.get(), $1->ByteSize()); + if (deleteCppReturn) { + // To prevent a memory leak. + delete $1; + $1 = NULL; + } +} +%enddef // end PROTO2_RETURN_AND_DELETE + #endif // SWIGJAVA