Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

An ability to get relative MIP GAP and expose linear solver callbacks API to the .Net #2608

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
8 changes: 4 additions & 4 deletions cmake/dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -116,15 +116,15 @@ 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()
fetch_git_dependency(
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()
Expand Down
1 change: 1 addition & 0 deletions makefiles/Makefile.dotnet.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions makefiles/Makefile.gen.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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 \
Expand Down
5 changes: 5 additions & 0 deletions ortools/linear_solver/csharp/SolverHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ public void Maximize(Variable var)
Objective().SetMaximization();
Objective().SetCoefficient(var, 1.0);
}

public void SetCallback(LinearSolutionCallback callback)
CADBIMDeveloper marked this conversation as resolved.
Show resolved Hide resolved
{
SetCallback((MPCallback)callback);
}
}

} // namespace Google.OrTools.LinearSolver
66 changes: 66 additions & 0 deletions ortools/linear_solver/csharp/linear_solver.i
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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#
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -170,6 +177,8 @@ CONVERT_VECTOR(operations_research::MPVariable, MPVariable)
const std::vector<operations_research::MPVariable*>&,
const std::vector<double>&);
%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;
Expand Down Expand Up @@ -302,7 +311,64 @@ 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
%unignore operations_research::MPCallbackEvent::kUnknown;
CADBIMDeveloper marked this conversation as resolved.
Show resolved Hide resolved
%rename (Unknown) operations_research::MPCallbackEvent::kUnknown;
%unignore operations_research::MPCallbackEvent::kPolling;
%rename (Polling) operations_research::MPCallbackEvent::kPolling;
%unignore operations_research::MPCallbackEvent::kPresolve;
%rename (Presolve) operations_research::MPCallbackEvent::kPresolve;
%unignore operations_research::MPCallbackEvent::kSimplex;
%rename (Simplex) operations_research::MPCallbackEvent::kSimplex;
%unignore operations_research::MPCallbackEvent::kMip;
%rename (Mip) operations_research::MPCallbackEvent::kMip;
%unignore operations_research::MPCallbackEvent::kMipSolution;
%rename (MipSolution) operations_research::MPCallbackEvent::kMipSolution;
%unignore operations_research::MPCallbackEvent::kMipNode;
%rename (MipNode) operations_research::MPCallbackEvent::kMipNode;
%unignore operations_research::MPCallbackEvent::kBarrier;
%rename (Barrier) operations_research::MPCallbackEvent::kBarrier;
%unignore operations_research::MPCallbackEvent::kMessage;
%rename (Message) operations_research::MPCallbackEvent::kMessage;
%unignore operations_research::MPCallbackEvent::kMultiObj;
%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;
%unignore operations_research::LinearSolutionCallback::Event;
%feature("nodirector") operations_research::LinearSolutionCallback::Event;
%rename (GetEventType) 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
6 changes: 6 additions & 0 deletions ortools/linear_solver/linear_solver_callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,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
Expand Down
58 changes: 58 additions & 0 deletions ortools/linear_solver/linear_solver_swig_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.

// See go/mpsolver-callbacks for documentation on how to use this file.
CADBIMDeveloper marked this conversation as resolved.
Show resolved Hide resolved

#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_
2 changes: 2 additions & 0 deletions ortools/linear_solver/scip_callback.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
16 changes: 16 additions & 0 deletions ortools/linear_solver/scip_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
CADBIMDeveloper marked this conversation as resolved.
Show resolved Hide resolved
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.
Expand Down