backport from main
This commit is contained in:
@@ -131,7 +131,6 @@ cc_library(
|
||||
name = "hungarian",
|
||||
srcs = ["hungarian.cc"],
|
||||
hdrs = ["hungarian.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//ortools/base",
|
||||
"@abseil-cpp//absl/container:flat_hash_map",
|
||||
@@ -158,7 +157,6 @@ cc_test(
|
||||
cc_library(
|
||||
name = "adjustable_k_ary_heap",
|
||||
hdrs = ["adjustable_k_ary_heap.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@abseil-cpp//absl/log:check"],
|
||||
)
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#ifndef ORTOOLS_BASE_PYTHON_SWIG_H_
|
||||
#define ORTOOLS_BASE_PYTHON_SWIG_H_
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03030000 // Py3.3+
|
||||
// Use Py3 unicode str() type for C++ strings.
|
||||
#ifdef PyString_FromStringAndSize
|
||||
#undef PyString_FromStringAndSize
|
||||
@@ -51,7 +50,6 @@ static inline int PyString_AsStringAndSize(PyObject* obj, char** buf,
|
||||
PyErr_SetString(PyExc_TypeError, "Expecting str or bytes");
|
||||
return -1;
|
||||
}
|
||||
#endif // Py3.3+
|
||||
|
||||
template <class T>
|
||||
inline bool PyObjAs(PyObject* pystr, T* cstr) {
|
||||
@@ -67,14 +65,12 @@ template <>
|
||||
inline bool PyObjAs(PyObject* pystr, ::std::string* cstr) {
|
||||
char* buf;
|
||||
Py_ssize_t len;
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyUnicode_Check(pystr)) {
|
||||
buf = PyUnicode_AsUTF8AndSize(pystr, &len);
|
||||
if (!buf) return false;
|
||||
} else // NOLINT
|
||||
#endif
|
||||
if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1)
|
||||
} else if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1) { // NOLINT
|
||||
return false;
|
||||
}
|
||||
if (cstr) cstr->assign(buf, len);
|
||||
return true;
|
||||
}
|
||||
@@ -83,14 +79,12 @@ template <class T>
|
||||
inline bool PyObjAs(PyObject* pystr, std::string* cstr) {
|
||||
char* buf;
|
||||
Py_ssize_t len;
|
||||
#if PY_VERSION_HEX >= 0x03030000
|
||||
if (PyUnicode_Check(pystr)) {
|
||||
buf = const_cast<char*>(PyUnicode_AsUTF8AndSize(pystr, &len));
|
||||
if (!buf) return false;
|
||||
} else // NOLINT
|
||||
#endif
|
||||
if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1)
|
||||
} else if (PyBytes_AsStringAndSize(pystr, &buf, &len) == -1) { // NOLINT
|
||||
return false;
|
||||
}
|
||||
if (cstr) cstr->assign(buf, len);
|
||||
return true;
|
||||
}
|
||||
@@ -132,36 +126,18 @@ inline bool PyObjAs(PyObject* py, unsigned int* c) {
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, int64_t* c) { // NOLINT
|
||||
int64_t i; // NOLINT
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check(py)) {
|
||||
i = PyInt_AsLong(py);
|
||||
} else {
|
||||
if (!PyLong_Check(py)) return false; // Not a Python long.
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
i = PyLong_AsLongLong(py);
|
||||
if (i == -1 && PyErr_Occurred()) return false; // Not a C long long.
|
||||
}
|
||||
const int64_t i = PyLong_AsLongLong(py);
|
||||
if (i == -1 && PyErr_Occurred()) return false; // Not a C long long.
|
||||
if (c) *c = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool PyObjAs(PyObject* py, uint64_t* c) { // NOLINT
|
||||
uint64_t i; // NOLINT
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
if (PyInt_Check(py)) {
|
||||
i = PyInt_AsUnsignedLongLongMask(py);
|
||||
} else // NOLINT
|
||||
#endif
|
||||
{
|
||||
if (!PyLong_Check(py)) return false; // Not a Python long.
|
||||
i = PyLong_AsUnsignedLongLong(py);
|
||||
if (i == (uint64_t)-1 && PyErr_Occurred()) // NOLINT
|
||||
return false;
|
||||
}
|
||||
if (!PyLong_Check(py)) return false; // Not a Python long.
|
||||
const uint64_t i = PyLong_AsUnsignedLongLong(py);
|
||||
if (i == (uint64_t)-1 && PyErr_Occurred()) // NOLINT
|
||||
return false;
|
||||
if (c) *c = i;
|
||||
return true;
|
||||
}
|
||||
@@ -171,12 +147,6 @@ inline bool PyObjAs(PyObject* py, double* c) {
|
||||
double d;
|
||||
if (PyFloat_Check(py)) {
|
||||
d = PyFloat_AsDouble(py);
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
} else if (PyInt_Check(py)) {
|
||||
d = PyInt_AsLong(py);
|
||||
} else if (!PyLong_Check(py)) {
|
||||
return false; // float or int/long expected
|
||||
#endif
|
||||
} else {
|
||||
d = PyLong_AsDouble(py);
|
||||
if (d == -1.0 && PyErr_Occurred()) {
|
||||
@@ -210,13 +180,7 @@ inline bool PyObjAs(PyObject* py, bool* c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int SwigPyIntOrLong_Check(PyObject* o) {
|
||||
return (PyLong_Check(o)
|
||||
#if PY_MAJOR_VERSION <= 2
|
||||
|| PyInt_Check(o)
|
||||
#endif
|
||||
); // NOLINT
|
||||
}
|
||||
inline int SwigPyIntOrLong_Check(PyObject* o) { return PyLong_Check(o); }
|
||||
|
||||
inline int SwigString_Check(PyObject* o) { return PyUnicode_Check(o); }
|
||||
|
||||
@@ -352,13 +316,11 @@ inline PyObject* vector_output_wrap_helper(const std::vector<T*>* vec,
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION > 2
|
||||
/* SWIG 2's own C preprocessor macro for this is too strict.
|
||||
* It requires a (x) parameter which doesn't work for the case where the
|
||||
* function is being passed by & as a converter into a helper such as
|
||||
* vector_output_helper above. */
|
||||
#undef PyInt_FromLong
|
||||
#define PyInt_FromLong PyLong_FromLong
|
||||
#endif
|
||||
|
||||
#endif // ORTOOLS_BASE_PYTHON_SWIG_H_
|
||||
|
||||
@@ -10,12 +10,17 @@ MOI implementation for the CP-Sat solver
|
||||
mutable struct CPSATOptimizer <: MOI.AbstractOptimizer
|
||||
model::Union{Nothing,CpModel}
|
||||
parameters::Union{Nothing,SatParameters}
|
||||
# Set of constraints in variable indices
|
||||
variables_constraints::Set{MOI.ConstraintIndex}
|
||||
constraint_types_present::Set{Tuple{Type,Type}}
|
||||
# This structure is updated by the optimize! function.
|
||||
solve_response::Union{Nothing,CpSolverResponse}
|
||||
|
||||
function CPSATOptimizer(; name::String = "")
|
||||
model = CpModel(name = name)
|
||||
parameters = SatParameters()
|
||||
variables_constraints = Set{MOI.ConstraintIndex}()
|
||||
constraint_types_present = Set{Tuple{Type,Type}}()
|
||||
|
||||
new(model, parameters, nothing)
|
||||
end
|
||||
@@ -32,6 +37,8 @@ end
|
||||
|
||||
function MOI.empty!(optimizer::CPSATOptimizer)
|
||||
optimizer.model = CpModel()
|
||||
optimizer.variables_constraints = Set{MOI.ConstraintIndex}()
|
||||
optimizer.constraint_types_present = Set{Tuple{Type,Type}}()
|
||||
optimizer.solve_response = nothing
|
||||
|
||||
return nothing
|
||||
@@ -203,3 +210,153 @@ function MOI.get(optimizer::CPSATOptimizer, ::Type{MOI.VariableIndex}, name::Str
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
|
||||
Constraint overrides.
|
||||
|
||||
"""
|
||||
|
||||
function MOI.supports_constraint(
|
||||
::CPSATOptimizer,
|
||||
::Type{MOI.VariableIndex},
|
||||
::Type{<:SCALAR_SET},
|
||||
)
|
||||
return true
|
||||
end
|
||||
|
||||
function MOI.add_constraint(
|
||||
optimizer::CPSATOptimizer,
|
||||
vi::MOI.VariableIndex,
|
||||
c::S,
|
||||
) where {S<:SCALAR_SET}
|
||||
if S <: MOI.LessThan
|
||||
throw_if_upper_bound_is_already_set(optimizer, vi, c)
|
||||
end
|
||||
|
||||
if S <: MOI.GreaterThan
|
||||
throw_if_lower_bound_is_already_set(optimizer, vi, c)
|
||||
end
|
||||
|
||||
if S <: MOI.Interval
|
||||
throw_if_upper_bound_is_already_set(optimizer, vi, c)
|
||||
throw_if_lower_bound_is_already_set(optimizer, vi, c)
|
||||
end
|
||||
|
||||
if S <: MOI.EqualTo
|
||||
throw_if_upper_bound_is_already_set(optimizer, vi, c)
|
||||
throw_if_lower_bound_is_already_set(optimizer, vi, c)
|
||||
end
|
||||
|
||||
variable_index = vi.value
|
||||
|
||||
# retrieve the constraint bounds
|
||||
lower_bound, upper_bound = bounds(c)
|
||||
|
||||
# TODO: (b/452908268) - Update variable's domain instead of creating a new linear constraint.
|
||||
linear_constraint = CPSatLinearConstraintProto()
|
||||
|
||||
push!(linear_constraint.vars, variable_index)
|
||||
# In this case, we set the coefficient to 1.
|
||||
push!(linear_constraint.coeffs, 1)
|
||||
# Update the domain
|
||||
push!(linear_constraint.domain, lower_bound)
|
||||
push!(linear_constraint.domain, upper_bound)
|
||||
|
||||
constraint = CPSATConstraint()
|
||||
constraint.name = :linear
|
||||
constraint.value = linear_constraint
|
||||
|
||||
push!(optimizer.model.constraints, constraint)
|
||||
|
||||
push!(
|
||||
optimizer.variables_constraints,
|
||||
MOI.ConstraintIndex{MOI.VariableIndex,typeof(c)}(variable_index),
|
||||
)
|
||||
push!(optimizer.constraint_types_present, (MOI.VariableIndex, typeof(c)))
|
||||
|
||||
return MOI.ConstraintIndex{MOI.VariableIndex,typeof(c)}(variable_index)
|
||||
end
|
||||
|
||||
function throw_if_upper_bound_is_already_set(
|
||||
optimizer::CPSATOptimizer,
|
||||
vi::MOI.VariableIndex,
|
||||
c::S,
|
||||
) where {S<:SCALAR_SET}
|
||||
# Assumes type consistency across all constraints.
|
||||
T = typeof(c).parameters[1]
|
||||
less_than_idx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{T}}(vi.value)
|
||||
interval_idx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{T}}(vi.value)
|
||||
equal_to_idx = MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}(vi.value)
|
||||
|
||||
if in(less_than_idx, optimizer.variables_constraints)
|
||||
throw(MOI.UpperBoundAlreadySet{MOI.LessThan{T},S}(vi))
|
||||
end
|
||||
|
||||
if in(interval_idx, optimizer.variables_constraints)
|
||||
throw(MOI.UpperBoundAlreadySet{MOI.Interval{T},S}(vi))
|
||||
end
|
||||
|
||||
if in(equal_to_idx, optimizer.variables_constraints)
|
||||
throw(MOI.UpperBoundAlreadySet{MOI.EqualTo{T},S}(vi))
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
function throw_if_lower_bound_is_already_set(
|
||||
optimizer::CPSATOptimizer,
|
||||
vi::MOI.VariableIndex,
|
||||
c::S,
|
||||
) where {S<:SCALAR_SET}
|
||||
# Assumes type consistency across all constraints.
|
||||
T = typeof(c).parameters[1]
|
||||
greater_than_idx = MOI.ConstraintIndex{typeof(vi),MOI.GreaterThan{T}}(vi.value)
|
||||
interval_idx = MOI.ConstraintIndex{typeof(vi),MOI.Interval{T}}(vi.value)
|
||||
equal_to_idx = MOI.ConstraintIndex{typeof(vi),MOI.EqualTo{T}}(vi.value)
|
||||
|
||||
if in(greater_than_idx, optimizer.variables_constraints)
|
||||
throw(MOI.LowerBoundAlreadySet{MOI.GreaterThan{T},S}(vi))
|
||||
end
|
||||
|
||||
if in(interval_idx, optimizer.variables_constraints)
|
||||
throw(MOI.LowerBoundAlreadySet{MOI.Interval{T},S}(vi))
|
||||
end
|
||||
|
||||
if in(equal_to_idx, optimizer.variables_constraints)
|
||||
throw(MOI.LowerBoundAlreadySet{MOI.EqualTo{T},S}(vi))
|
||||
end
|
||||
|
||||
return nothing
|
||||
end
|
||||
|
||||
function MOI.supports_constraint(
|
||||
::CPSATOptimizer,
|
||||
::Type{MOI.VariableIndex},
|
||||
::Type{MOI.ZeroOne},
|
||||
)
|
||||
return true
|
||||
end
|
||||
|
||||
function MOI.add_constraint(
|
||||
optimizer::CPSATOptimizer,
|
||||
vi::MOI.VariableIndex,
|
||||
c::MOI.ZeroOne,
|
||||
)
|
||||
# Get the int value of the variable index
|
||||
index = vi.value
|
||||
|
||||
# Set the variable bounds to [0, 1] (override the existing domain)
|
||||
optimizer.model.variables[index].domain = [zero(Int), one(Int)]
|
||||
|
||||
# Update the associated metadata.
|
||||
# TODO: (b/452416646) - Fetch constraint types dynamically at runtime
|
||||
push!(optimizer.constraint_types_present, (MOI.VariableIndex, MOI.ZeroOne))
|
||||
push!(
|
||||
optimizer.variables_constraints,
|
||||
MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(index),
|
||||
)
|
||||
|
||||
return MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(index)
|
||||
end
|
||||
|
||||
@@ -66,8 +66,8 @@ simple_sat_program()
|
||||
|
||||
The interface to the C++ CP-SAT solver is implemented through the
|
||||
**CpModelBuilder** class described in
|
||||
*ortools/sat/cp_model.h*. This class is just a helper to fill
|
||||
in the cp_model protobuf.
|
||||
*ortools/sat/cp_model.h*. This class is just a helper to
|
||||
fill in the cp_model protobuf.
|
||||
|
||||
Calling Solve() method will return a CpSolverResponse protobuf that contains the
|
||||
solve status, the values for each variable in the model if solve was successful,
|
||||
|
||||
@@ -12,14 +12,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
load("@protobuf//bazel:cc_proto_library.bzl", "cc_proto_library")
|
||||
load("@protobuf//bazel:java_proto_library.bzl", "java_proto_library")
|
||||
load("@protobuf//bazel:proto_library.bzl", "proto_library")
|
||||
load("@protobuf//bazel:py_proto_library.bzl", "py_proto_library")
|
||||
|
||||
package(default_visibility = [
|
||||
"//ortools/math_opt:__subpackages__",
|
||||
"//ortools/service:__subpackages__",
|
||||
])
|
||||
package(default_visibility = ["//ortools/math_opt:__subpackages__"])
|
||||
|
||||
proto_library(
|
||||
name = "optimization_proto",
|
||||
|
||||
Reference in New Issue
Block a user