tweak model_builder_helper

This commit is contained in:
Laurent Perron
2022-04-02 23:26:17 +02:00
parent f698ff22a0
commit 1ff23debca
8 changed files with 90 additions and 26 deletions

View File

@@ -33,21 +33,11 @@ rather than for solving specific optimization problems.
import math
from ortools.linear_solver import linear_solver_pb2
from ortools.model_builder.python import model_builder_helper as mbh
from ortools.model_builder.python import pywrap_model_builder_helper as pwmb
# Forward solve statuses.
OPTIMAL = linear_solver_pb2.MPSOLVER_OPTIMAL
FEASIBLE = linear_solver_pb2.MPSOLVER_FEASIBLE
INFEASIBLE = linear_solver_pb2.MPSOLVER_INFEASIBLE
UNBOUNDED = linear_solver_pb2.MPSOLVER_UNBOUNDED
ABNORMAL = linear_solver_pb2.MPSOLVER_ABNORMAL
NOT_SOLVED = linear_solver_pb2.MPSOLVER_NOT_SOLVED
MODEL_IS_VALID = linear_solver_pb2.MPSOLVER_MODEL_IS_VALID
CANCELLED_BY_USER = linear_solver_pb2.MPSOLVER_CANCELLED_BY_USER
UNKNOWN_STATUS = linear_solver_pb2.MPSOLVER_UNKNOWN_STATUS
MODEL_INVALID = linear_solver_pb2.MPSOLVER_MODEL_INVALID
SolveStatus = pwmb.SolveStatus
class LinearExpr(object):
@@ -842,6 +832,7 @@ class ModelBuilder(object):
def __optimize(self, linear_expr, maximize):
"""Defines the objective."""
self.helper.clear_objective()
coeffs_map = {}
# constant = 0.0
if isinstance(linear_expr, LinearExpr):
@@ -943,7 +934,7 @@ class ModelSolver(object):
if self.log_callback is not None:
self.__solve_helper.set_log_callback(self.log_callback)
self.__solve_helper.solve(model.helper)
return self.__solve_helper.status()
return SolveStatus(self.__solve_helper.status())
def __check_has_feasible_solution(self):
"""Checks that solve has run and has found a feasible solution."""
@@ -982,12 +973,6 @@ class ModelSolver(object):
self.__check_has_feasible_solution()
return self.__solve_helper.best_objective_bound()
def status_name(self, status=None):
"""Returns the name of the status returned by solve()."""
if status is None:
status = self.__solve_helper.status()
return linear_solver_pb2.MPSolverResponseStatus.Name(status)
@property
def status_string(self):
"""Returns additional information of the last solve.

View File

@@ -37,6 +37,7 @@ using ::operations_research::MPModelProto;
using ::operations_research::MPModelRequest;
using ::operations_research::MPSolutionResponse;
using ::operations_research::MPVariableProto;
using ::operations_research::SolveStatus;
using ::pybind11::arg;
// TODO(user): The interface uses serialized protos because of issues building
@@ -190,12 +191,30 @@ PYBIND11_MODULE(pywrap_model_builder_helper, m) {
&ModelBuilderHelper::ConstraintCoefficients, arg("ct_index"))
.def("name", &ModelBuilderHelper::name)
.def("set_name", &ModelBuilderHelper::SetName, arg("name"))
.def("clear_objective", &ModelBuilderHelper::ClearObjective)
.def("maximize", &ModelBuilderHelper::maximize)
.def("set_maximize", &ModelBuilderHelper::SetMaximize, arg("maximize"))
.def("set_objective_offset", &ModelBuilderHelper::SetObjectiveOffset,
arg("offset"))
.def("objective_offset", &ModelBuilderHelper::ObjectiveOffset);
pybind11::enum_<SolveStatus>(m, "SolveStatus")
.value("OPTIMAL", SolveStatus::OPTIMAL)
.value("FEASIBLE", SolveStatus::FEASIBLE)
.value("INFEASIBLE", SolveStatus::INFEASIBLE)
.value("UNBOUNDED", SolveStatus::UNBOUNDED)
.value("ABNORMAL", SolveStatus::ABNORMAL)
.value("NOT_SOLVED", SolveStatus::NOT_SOLVED)
.value("MODEL_IS_VALID", SolveStatus::MODEL_IS_VALID)
.value("CANCELLED_BY_USER", SolveStatus::CANCELLED_BY_USER)
.value("UNKNOWN_STATUS", SolveStatus::UNKNOWN_STATUS)
.value("MODEL_INVALID", SolveStatus::MODEL_INVALID)
.value("INVALID_SOLVER_PARAMETERS",
SolveStatus::INVALID_SOLVER_PARAMETERS)
.value("SOLVER_TYPE_UNAVAILABLE", SolveStatus::SOLVER_TYPE_UNAVAILABLE)
.value("INCOMPATIBLE_OPTIONS", SolveStatus::INCOMPATIBLE_OPTIONS)
.export_values();
pybind11::class_<ModelSolverHelper>(m, "ModelSolverHelper")
.def(pybind11::init<const std::string&>())
.def("solver_is_supported", &ModelSolverHelper::SolverIsSupported)

View File

@@ -75,7 +75,8 @@ def main():
# Print solution.
# [START print_solution]
if status == model_builder.OPTIMAL or status == model_builder.FEASIBLE:
if (status == model_builder.SolveStatus.OPTIMAL or
status == model_builder.SolveStatus.FEASIBLE):
print(f'Total cost = {solver.objective_value}\n')
for i in range(num_workers):
for j in range(num_tasks):

View File

@@ -84,7 +84,7 @@ def main():
# [END solve]
# [START print_solution]
if status == model_builder.OPTIMAL:
if status == model_builder.SolveStatus.OPTIMAL:
num_bins = 0.
for j in data['bins']:
if solver.value(y[j]) == 1:

View File

@@ -56,7 +56,7 @@ def main():
# [END solve]
# [START print_solution]
if status == model_builder.OPTIMAL:
if status == model_builder.SolveStatus.OPTIMAL:
print('Solution:')
print('Objective value =', solver.objective_value)
print('x =', solver.value(x))

View File

@@ -56,7 +56,7 @@ def main():
# [END solve]
# [START print_solution]
if status == model_builder.OPTIMAL:
if status == model_builder.SolveStatus.OPTIMAL:
print('Solution:')
print('Objective value =', solver.objective_value)
print('x =', solver.value(x))

View File

@@ -199,6 +199,11 @@ std::string ModelBuilderHelper::name() const { return model_.name(); }
void ModelBuilderHelper::SetName(const std::string& name) {
model_.set_name(name);
}
void ModelBuilderHelper::ClearObjective() {
for (MPVariableProto& var : *model_.mutable_variable()) {
var.clear_objective_coefficient();
}
}
bool ModelBuilderHelper::maximize() const { return model_.maximize(); }
@@ -221,6 +226,43 @@ std::optional<MPSolutionResponse> ModelSolverHelper::SolveRequest(
return temp;
}
namespace {
SolveStatus MPSolverResponseStatusToSolveStatus(MPSolverResponseStatus s) {
switch (s) {
case MPSOLVER_OPTIMAL:
return SolveStatus::OPTIMAL;
case MPSOLVER_FEASIBLE:
return SolveStatus::FEASIBLE;
case MPSOLVER_INFEASIBLE:
return SolveStatus::INFEASIBLE;
case MPSOLVER_UNBOUNDED:
return SolveStatus::UNBOUNDED;
case MPSOLVER_ABNORMAL:
return SolveStatus::ABNORMAL;
case MPSOLVER_NOT_SOLVED:
return SolveStatus::NOT_SOLVED;
case MPSOLVER_MODEL_IS_VALID:
return SolveStatus::MODEL_IS_VALID;
case MPSOLVER_CANCELLED_BY_USER:
return SolveStatus::CANCELLED_BY_USER;
case MPSOLVER_UNKNOWN_STATUS:
return SolveStatus::UNKNOWN_STATUS;
case MPSOLVER_MODEL_INVALID:
return SolveStatus::MODEL_INVALID;
case MPSOLVER_MODEL_INVALID_SOLUTION_HINT:
return SolveStatus::MODEL_INVALID;
case MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS:
return SolveStatus::INVALID_SOLVER_PARAMETERS;
case MPSOLVER_SOLVER_TYPE_UNAVAILABLE:
return SolveStatus::SOLVER_TYPE_UNAVAILABLE;
case MPSOLVER_INCOMPATIBLE_OPTIONS:
return SolveStatus::INCOMPATIBLE_OPTIONS;
default:
return SolveStatus::UNKNOWN_STATUS;
}
}
} // namespace
ModelSolverHelper::ModelSolverHelper(const std::string& solver_name) {
if (solver_name.empty()) return;
MPSolver::OptimizationProblemType parsed_type;
@@ -318,11 +360,11 @@ const MPSolutionResponse& ModelSolverHelper::response() const {
return response_.value();
}
MPSolverResponseStatus ModelSolverHelper::status() const {
SolveStatus ModelSolverHelper::status() const {
if (!response_.has_value()) {
return MPSolverResponseStatus::MPSOLVER_UNKNOWN_STATUS;
return SolveStatus::UNKNOWN_STATUS;
}
return response_.value().status();
return MPSolverResponseStatusToSolveStatus(response_.value().status());
}
double ModelSolverHelper::objective_value() const {

View File

@@ -91,6 +91,7 @@ class ModelBuilderHelper {
std::string name() const;
void SetName(const std::string& name);
void ClearObjective();
bool maximize() const;
void SetMaximize(bool maximize);
double ObjectiveOffset() const;
@@ -107,6 +108,22 @@ class LogCallback {
virtual void NewMessage(const std::string& message) = 0;
};
enum SolveStatus {
OPTIMAL,
FEASIBLE,
INFEASIBLE,
UNBOUNDED,
ABNORMAL,
NOT_SOLVED,
MODEL_IS_VALID,
CANCELLED_BY_USER,
UNKNOWN_STATUS,
MODEL_INVALID,
INVALID_SOLVER_PARAMETERS,
SOLVER_TYPE_UNAVAILABLE,
INCOMPATIBLE_OPTIONS,
};
// Class used to solve a request. This class is not meant to be exposed to the
// public. Its responsibility is to bridge the MPModelProto in the non-C++
// languages with the C++ Solve method.
@@ -133,7 +150,7 @@ class ModelSolverHelper {
bool has_response() const;
bool has_solution() const;
const MPSolutionResponse& response() const;
MPSolverResponseStatus status() const;
SolveStatus status() const;
// If not defined, or no solution, they will silently return 0.
double objective_value() const;