diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index ec5adf641f0..5c33700ff6f 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -50,7 +50,7 @@ if(BUILD_ZLIB) zlib GIT_REPOSITORY "https://github.com/madler/ZLIB.git" GIT_TAG "v1.2.11" - PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch") + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch") FetchContent_MakeAvailable(zlib) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") @@ -67,7 +67,7 @@ if(BUILD_absl) absl GIT_REPOSITORY "https://github.com/abseil/abseil-cpp.git" GIT_TAG "20210324.2" - PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/../../patches/abseil-cpp-20210324.2.patch") + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/abseil-cpp-20210324.2.patch") FetchContent_MakeAvailable(absl) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") @@ -116,7 +116,7 @@ if(BUILD_Protobuf) GIT_REPOSITORY "https://github.com/protocolbuffers/protobuf.git" GIT_TAG "v3.17.3" GIT_SUBMODULES "" - PATCH_COMMAND git apply "${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v3.17.3.patch" + PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v3.17.3.patch" SOURCE_SUBDIR cmake) FetchContent_MakeAvailable(protobuf) else() @@ -124,7 +124,7 @@ if(BUILD_Protobuf) NAME Protobuf REPOSITORY "https://github.com/protocolbuffers/protobuf.git" TAG "v3.17.3" - PATCH_COMMAND "git apply \"${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v3.17.3.patch\"" + PATCH_COMMAND "git apply --ignore-whitespace \"${CMAKE_CURRENT_LIST_DIR}/../../patches/protobuf-v3.17.3.patch\"" SOURCE_SUBDIR cmake ) endif() diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index 4c1f5efeb55..6fa1653ff1a 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -148,6 +148,7 @@ $(GEN_DIR)/ortools/linear_solver/linear_solver_csharp_wrap.cc: \ $(SRC_DIR)/ortools/linear_solver/csharp/linear_solver.i \ $(SRC_DIR)/ortools/base/base.i \ $(SRC_DIR)/ortools/util/csharp/proto.i \ + $(SRC_DIR)/ortools/linear_solver/linear_solver_swig_helper.h \ $(GLOP_DEPS) \ $(LP_DEPS) \ | $(GEN_DIR)/ortools/linear_solver diff --git a/makefiles/Makefile.gen.mk b/makefiles/Makefile.gen.mk index 193bbf8dd9f..2bb4ad8d2eb 100644 --- a/makefiles/Makefile.gen.mk +++ b/makefiles/Makefile.gen.mk @@ -3625,6 +3625,7 @@ LP_DEPS = \ $(SRC_DIR)/ortools/linear_solver/glop_utils.h \ $(SRC_DIR)/ortools/linear_solver/gurobi_proto_solver.h \ $(SRC_DIR)/ortools/linear_solver/linear_expr.h \ + $(SRC_DIR)/ortools/linear_solver/linear_solver_swig_helper.h \ $(SRC_DIR)/ortools/linear_solver/linear_solver_callback.h \ $(SRC_DIR)/ortools/linear_solver/linear_solver.h \ $(SRC_DIR)/ortools/linear_solver/model_exporter.h \ @@ -3863,6 +3864,7 @@ objs/linear_solver/linear_solver.$O: \ ortools/gen/ortools/linear_solver/linear_solver.pb.h \ ortools/gen/ortools/util/optional_boolean.pb.h \ ortools/linear_solver/linear_solver_callback.h \ + ortools/linear_solver/linear_solver_swig_helper.h \ ortools/port/proto_utils.h ortools/base/accurate_sum.h \ ortools/base/map_util.h ortools/base/status_macros.h \ ortools/base/status_builder.h ortools/base/stl_util.h \ diff --git a/ortools/linear_solver/csharp/SolverHelper.cs b/ortools/linear_solver/csharp/SolverHelper.cs index 9e199abe291..f26569effd9 100644 --- a/ortools/linear_solver/csharp/SolverHelper.cs +++ b/ortools/linear_solver/csharp/SolverHelper.cs @@ -224,6 +224,11 @@ public void Maximize(Variable var) Objective().SetMaximization(); Objective().SetCoefficient(var, 1.0); } + + public void SetCallback(LinearSolutionCallback callback) + { + SetCallback((MPCallback)callback); + } } } // namespace Google.OrTools.LinearSolver diff --git a/ortools/linear_solver/csharp/linear_solver.i b/ortools/linear_solver/csharp/linear_solver.i index 0d3ad75e73e..6c87240986b 100644 --- a/ortools/linear_solver/csharp/linear_solver.i +++ b/ortools/linear_solver/csharp/linear_solver.i @@ -38,9 +38,12 @@ %{ #include "ortools/linear_solver/linear_solver.h" #include "ortools/linear_solver/linear_solver.pb.h" +#include "ortools/linear_solver/linear_solver_callback.h" +#include "ortools/linear_solver/linear_solver_swig_helper.h" #include "ortools/linear_solver/model_exporter.h" %} +%module(directors="1") operations_research_linear // We need to forward-declare the proto here, so that the PROTO_* macros // involving them work correctly. The order matters very much: this declaration @@ -49,6 +52,7 @@ namespace operations_research { class MPModelProto; class MPModelRequest; class MPSolutionResponse; +class MPCallback; } // namespace operations_research // Allow partial C# classes. That way, we can put our C# code extension in C# @@ -82,6 +86,8 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable) %rename (Constraint) operations_research::MPConstraint; %rename (Objective) operations_research::MPObjective; %rename (SolverParameters) operations_research::MPSolverParameters; +%rename (SolverCallbackContext) operations_research::MPCallbackContext; +%rename (SolverSolutionCallbackEvent) operations_research::MPCallbackEvent; // Expose the MPSolver::OptimizationProblemType enum. %unignore operations_research::MPSolver::OptimizationProblemType; @@ -132,6 +138,7 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable) %unignore operations_research::MPSolver::Solve; %unignore operations_research::MPSolver::VerifySolution; %unignore operations_research::MPSolver::Reset; +%unignore operations_research::MPSolver::SupportsCallbacks; %rename (SetTimeLimit) operations_research::MPSolver::set_time_limit; // Expose some of the more advanced MPSolver API. @@ -170,6 +177,8 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable) const std::vector&, const std::vector&); %unignore operations_research::MPSolver::SetNumThreads; +%unignore operations_research::MPSolver::SetCallback; +%csmethodmodifiers operations_research::MPSolver::SetCallback "internal"; %extend operations_research::MPSolver { std::string ExportModelAsLpFormat(bool obfuscated) { operations_research::MPModelExportOptions options; @@ -302,7 +311,53 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable) %unignore operations_research::MPSolverParameters::SCALING_OFF; // no test %unignore operations_research::MPSolverParameters::SCALING_ON; // no test +// Callback API + +// Expose callback event type enum +%rename (Unknown) operations_research::MPCallbackEvent::kUnknown; +%rename (Polling) operations_research::MPCallbackEvent::kPolling; +%rename (Presolve) operations_research::MPCallbackEvent::kPresolve; +%rename (Simplex) operations_research::MPCallbackEvent::kSimplex; +%rename (Mip) operations_research::MPCallbackEvent::kMip; +%rename (MipSolution) operations_research::MPCallbackEvent::kMipSolution; +%rename (MipNode) operations_research::MPCallbackEvent::kMipNode; +%rename (Barrier) operations_research::MPCallbackEvent::kBarrier; +%rename (Message) operations_research::MPCallbackEvent::kMessage; +%rename (MultiObj) operations_research::MPCallbackEvent::kMultiObj; + +// MPCallbackContext +%unignore operations_research::MPCallbackContext::MPCallbackContext; +%unignore operations_research::MPCallbackContext::~MPCallbackContext; +%rename (GetEventType) operations_research::MPCallbackContext::Event; +%unignore operations_research::MPCallbackContext::CanQueryVariableValues; +%unignore operations_research::MPCallbackContext::VariableValue; +%unignore operations_research::MPCallbackContext::NumExploredNodes; + +%unignore operations_research::MPCallback; + +%feature("director") operations_research::LinearSolutionCallback; +%unignore operations_research::LinearSolutionCallback; +%unignore operations_research::LinearSolutionCallback::LinearSolutionCallback; +%unignore operations_research::LinearSolutionCallback::~LinearSolutionCallback; +%unignore operations_research::LinearSolutionCallback::VariableValue; +%feature("nodirector") operations_research::LinearSolutionCallback::VariableValue; +%unignore operations_research::LinearSolutionCallback::CanQueryVariableValues; +%feature("nodirector") operations_research::LinearSolutionCallback::CanQueryVariableValues; +%rename (GetEventType) operations_research::LinearSolutionCallback::Event; +%feature("nodirector") operations_research::LinearSolutionCallback::Event; +%unignore operations_research::LinearSolutionCallback::NumExploredNodes; +%feature("nodirector") operations_research::LinearSolutionCallback::NumExploredNodes; +%unignore operations_research::LinearSolutionCallback::GetRelativeMipGap; +%feature("nodirector") operations_research::LinearSolutionCallback::GetRelativeMipGap; +%unignore operations_research::LinearSolutionCallback::HasValidMipSolution; +%feature("nodirector") operations_research::LinearSolutionCallback::HasValidMipSolution; +%unignore operations_research::LinearSolutionCallback::IsNewSolution; +%feature("nodirector") operations_research::LinearSolutionCallback::IsNewSolution; +%unignore operations_research::LinearSolutionCallback::OnSolutionCallback; + %include "ortools/linear_solver/linear_solver.h" +%include "ortools/linear_solver/linear_solver_callback.h" +%include "ortools/linear_solver/linear_solver_swig_helper.h" %include "ortools/linear_solver/model_exporter.h" %unignoreall diff --git a/ortools/linear_solver/linear_solver_callback.h b/ortools/linear_solver/linear_solver_callback.h index 5dc5779e5d5..14393278ed3 100644 --- a/ortools/linear_solver/linear_solver_callback.h +++ b/ortools/linear_solver/linear_solver_callback.h @@ -11,8 +11,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// See go/mpsolver-callbacks for documentation on how to use this file. - #ifndef OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_ #define OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_ @@ -133,6 +131,12 @@ class MPCallbackContext { // // Call only when the event is kMipSolution or kMipNode. virtual int64_t NumExploredNodes() = 0; + + virtual double GetRelativeMipGap() { return 0; } + + virtual bool HasValidMipSolution() { return false; } + + virtual bool IsNewSolution() { return false; } }; // Extend this class with model specific logic, and register through diff --git a/ortools/linear_solver/linear_solver_swig_helper.h b/ortools/linear_solver/linear_solver_swig_helper.h new file mode 100644 index 00000000000..478605dba0d --- /dev/null +++ b/ortools/linear_solver/linear_solver_swig_helper.h @@ -0,0 +1,56 @@ +// Copyright 2010-2021 Google LLC +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef OR_TOOLS_LINEAR_SOLVER_SWIG_HELPER_H_ +#define OR_TOOLS_LINEAR_SOLVER_SWIG_HELPER_H_ + +#include "ortools/linear_solver/linear_solver.h" +#include "ortools/linear_solver/linear_solver_callback.h" + +namespace operations_research { +class LinearSolutionCallback : public MPCallback { + public: + LinearSolutionCallback() : MPCallback(false, false) { + context_ = NULL; + } + + virtual ~LinearSolutionCallback() {} + + virtual void OnSolutionCallback() {} + + double VariableValue(const MPVariable* variable) { return context_->VariableValue(variable); } + + bool CanQueryVariableValues(){ return context_->CanQueryVariableValues(); } + + MPCallbackEvent Event() { return context_->Event(); } + + int64_t NumExploredNodes() { return context_->NumExploredNodes(); } + + double GetRelativeMipGap() { return context_->GetRelativeMipGap(); } + + bool HasValidMipSolution() { return context_->HasValidMipSolution(); } + + bool IsNewSolution() { return context_->IsNewSolution(); } + + void RunCallback(MPCallbackContext* callback_context) override{ + context_ = callback_context; + + OnSolutionCallback(); + } + + private: + MPCallbackContext* context_; +}; +} // namespace operations_research + +#endif // OR_TOOLS_LINEAR_SOLVER_LINEAR_SOLVER_CALLBACK_H_ diff --git a/ortools/linear_solver/scip_callback.h b/ortools/linear_solver/scip_callback.h index c0c89b6d125..b1530c14d0f 100644 --- a/ortools/linear_solver/scip_callback.h +++ b/ortools/linear_solver/scip_callback.h @@ -101,6 +101,8 @@ class ScipConstraintHandlerContext { // TODO(user): maybe this can be abstracted away. bool is_pseudo_solution() const { return is_pseudo_solution_; } + bool IsNewSolution() const { return solution_ != SCIPgetBestSol(scip_); } + private: SCIP* scip_; SCIP_SOL* solution_; diff --git a/ortools/linear_solver/scip_interface.cc b/ortools/linear_solver/scip_interface.cc index 47181fda527..d7df1b27cf3 100644 --- a/ortools/linear_solver/scip_interface.cc +++ b/ortools/linear_solver/scip_interface.cc @@ -1088,6 +1088,22 @@ class ScipMPCallbackContext : public MPCallbackContext { LOG(FATAL) << "SuggestSolution() not currently supported for SCIP."; } + double GetRelativeMipGap() override { + SCIP* scip = scip_context_->scip(); + SCIP_SET* set = scip->set; + + return SCIPcomputeGap(set->num_epsilon, set->num_infinity, SCIPgetPrimalbound(scip), SCIPgetDualbound(scip)); + } + + bool HasValidMipSolution() override { + SCIP* scip = scip_context_->scip(); + return SCIPgetBestSol(scip) != 0; + } + + bool IsNewSolution() override { + return HasValidMipSolution() && scip_context_->IsNewSolution(); + } + int64_t NumExploredNodes() override { // scip_context_->NumNodesProcessed() returns: // 0 before the root node is solved, e.g. if a heuristic finds a solution.