11 #ifndef __CONSTRAINT_SET_H__
12 #define __CONSTRAINT_SET_H__
22 #include <gurobi_c++.h>
27 class GRBCallbackPublic :
public GRBCallback {
29 GRBCallbackPublic() =
default;
30 GRBCallbackPublic(
const GRBCallbackPublic&) =
default;
31 GRBCallbackPublic& operator=(
const GRBCallbackPublic&) =
default;
32 GRBCallbackPublic(GRBCallbackPublic&&) =
default;
33 GRBCallbackPublic& operator=(GRBCallbackPublic&&) =
default;
35 using GRBCallback::where;
37 using GRBCallback::callback;
38 using GRBCallback::getDoubleInfo;
39 using GRBCallback::getIntInfo;
40 using GRBCallback::getStringInfo;
41 using GRBCallback::getSolution;
42 using GRBCallback::getNodeRel;
43 using GRBCallback::setSolution;
45 using GRBCallback::addCut;
46 using GRBCallback::addLazy;
47 using GRBCallback::abort;
53 template<
typename FUNC>
54 class OnMIPSolutionFunctorGRBCallback :
public GRBCallbackPublic {
56 OnMIPSolutionFunctorGRBCallback(FUNC on_MIP_solution_functor)
57 : on_MIP_solution_functor(std::move(on_MIP_solution_functor))
60 void callback()
override {
61 if (where == GRB_CB_MIPSOL) {
66 void onMIPSolution() {
67 on_MIP_solution_functor(*
this);
71 FUNC on_MIP_solution_functor;
74 template<
typename FUNC>
75 auto makeOnMIPSolutionFunctorGRBCallback(FUNC&& on_MIP_solution_functor) {
76 return OnMIPSolutionFunctorGRBCallback<FUNC>(std::forward<FUNC>(on_MIP_solution_functor));
85 class MaybeGRBCallback {
87 MaybeGRBCallback(GRBCallbackPublic* impl)
91 bool hasImpl()
const {
return impl !=
nullptr; }
92 operator bool()
const {
return hasImpl(); }
94 GRBCallbackPublic& get()
const {
95 if (not hasImpl()) {
throw std::logic_error(
"Cannot get implementation - implementation not provided/implementation pointer is null"); }
96 else {
return *impl; }
99 GRBCallbackPublic& operator*()
const {
return get(); }
100 GRBCallbackPublic* operator->()
const {
return &(**this); }
103 GRBCallbackPublic* impl;
113 struct ConstraintID {
114 virtual bool operator==(
const ConstraintID&)
const = 0;
115 virtual std::size_t hash()
const = 0;
116 virtual ~ConstraintID() =
default;
121 struct hash<ConstraintID> {
122 std::size_t operator()(
const ConstraintID& cid)
const {
134 template<
typename REPR>
135 struct StandardConstraintID : ConstraintID {
140 StandardConstraintID(T&& t)
144 StandardConstraintID(
const StandardConstraintID&) =
default;
145 StandardConstraintID& operator=(
const StandardConstraintID&) =
default;
146 StandardConstraintID(StandardConstraintID&&) =
default;
147 StandardConstraintID& operator=(StandardConstraintID&&) =
default;
149 bool operator==(
const ConstraintID& rhs)
const override {
150 auto* rhs_ptr =
dynamic_cast<const StandardConstraintID*
>(&rhs);
151 return rhs_ptr && this->data == rhs_ptr->data;
154 std::size_t hash()
const override {
155 return std::hash<REPR>{}(data);
165 template<
typename... TUP_ARGS>
166 struct TupleConstraintID : ConstraintID {
168 std::tuple<TUP_ARGS...> data;
170 template<
typename... T>
171 TupleConstraintID(T&&... t)
172 : data(std::forward<T>(t)...)
175 TupleConstraintID(
const TupleConstraintID&) =
default;
176 TupleConstraintID& operator=(
const TupleConstraintID&) =
default;
177 TupleConstraintID(TupleConstraintID&&) =
default;
178 TupleConstraintID& operator=(TupleConstraintID&&) =
default;
180 bool operator==(
const ConstraintID& rhs)
const override {
181 auto* rhs_ptr =
dynamic_cast<const TupleConstraintID*
>(&rhs);
182 return rhs_ptr && this->data == rhs_ptr->data;
185 std::size_t hash()
const override {
195 template<
typename REPR>
196 std::unique_ptr<StandardConstraintID<REPR>> makeNewStandardConstraintID(REPR data) {
197 return std::make_unique<StandardConstraintID<REPR>>(std::forward<REPR>(data));
200 template<
typename... TUP_ARGS>
201 std::unique_ptr<TupleConstraintID<TUP_ARGS...>> makeNewTupleConstraintID(TUP_ARGS&&... targs) {
202 return std::make_unique<TupleConstraintID<TUP_ARGS...>>(std::forward<TUP_ARGS>(targs)...);
205 using TypeErasedConstraintEnum = int;
207 enum class ConstraintAddMode {
219 struct ConstraintAddConf {
228 template<
typename CONTAINER>
229 ConstraintAddConf(
const CONTAINER& amodes_and_cgroups) {
230 for (
auto& amode_and_cgroup : amodes_and_cgroups) {
231 addAs(amode_and_cgroup.first, amode_and_cgroup.second);
235 template<
typename Constra
intEnum,
typename CGROUPS = std::set<Constra
intEnum>,
typename CONTAINER = std::vector<std::pair<Constra
intAddMode, CGROUPS>>>
236 static ConstraintAddConf make(
const CONTAINER& amodes_and_cgroups = {}) {
237 return {amodes_and_cgroups};
246 template<
typename CGROUPS>
248 void addAs(ConstraintAddMode amode,
const CGROUPS& cgroups,
bool check_overwrite =
true) {
249 for (
auto& cg : cgroups) {
250 auto emplace_result = add_modes.emplace((TypeErasedConstraintEnum)cg, amode);
251 if (not emplace_result.second) {
252 if (check_overwrite) {
253 throw std::logic_error(
"constraint already added");
255 emplace_result.first->second = amode;
261 bool willRequireLazyModelAttribute() {
262 return std::find_if(
begin(add_modes),
end(add_modes), [](
auto&& e) {
263 return e.second == ConstraintAddMode::LazyViaCallback;
264 }) !=
end(add_modes);
267 template<
typename,
typename>
268 friend class ConstraintSet;
269 std::map<TypeErasedConstraintEnum, ConstraintAddMode> add_modes = {};
272 using IDAndConstraint = std::pair<std::unique_ptr<ConstraintID>, GRBTempConstr>;
277 struct ConstraintCache {
278 std::map<TypeErasedConstraintEnum, std::vector<IDAndConstraint>> added_constraints;
281 struct GenFuncReturn {
282 std::vector<IDAndConstraint> constraints = {};
285 struct ConstraintGeneratorParams {
286 MaybeGRBCallback callback;
288 bool inIncrementalMode() {
return callback; }
289 auto makeReturnValue() {
return GenFuncReturn(); }
292 using CGenFunc = std::function<GenFuncReturn(ConstraintGeneratorParams)>;
298 using ConstraintGenerators = std::vector<std::pair<TypeErasedConstraintEnum, CGen>>;
300 enum class ConstraintDest { ToModel, ViaCallback, };
304 ConstraintCache&& cache,
305 const ConstraintGenerators& constraint_generators,
int verbosity,
306 ConstraintDest destination,
const ConstraintAddConf& add_conf, GRBCallbackPublic* cb, GRBModel* model
313 template<
typename Constra
intEnum,
typename SetOfConstra
intEnums = std::set<Constra
intEnum>>
314 class ConstraintSet {
321 void registerGenerator(ConstraintEnum cgroup, CGenFunc constraint_generator) {
322 constraint_generators.emplace_back((TypeErasedConstraintEnum)cgroup, CGen {
323 std::move(constraint_generator),
327 SetOfConstraintEnums getAllConstraintGroupsRegistered()
const {
328 SetOfConstraintEnums res;
329 auto& cgens = constraint_generators;
330 std::transform(
begin(cgens),
end(cgens), std::inserter(res,
end(res)), [](
auto&& e) {
return (ConstraintEnum)e.first; });
349 ConstraintCache addConstraintsViaCallback(
350 ConstraintCache&& cache,
351 const ConstraintAddConf& add_conf, GRBCallbackPublic& cb
353 return addConstraintsImpl(std::move(cache), constraint_generators, verbosity, ConstraintDest::ViaCallback, add_conf, &cb,
nullptr);
361 ConstraintCache addConstraintsToModel(
362 ConstraintCache&& cache,
363 const ConstraintAddConf& add_conf, GRBModel& model
365 return addConstraintsImpl(std::move(cache), constraint_generators, verbosity, ConstraintDest::ToModel, add_conf,
nullptr, &model);
371 void setVerbosity(
int i) { verbosity = i; }
374 ConstraintGenerators constraint_generators = {};
381 inline int gurobiLazyIntValueOf(ConstraintAddMode add_mode) {
383 case ConstraintAddMode::LazyViaCallback:
return 0;
384 case ConstraintAddMode::LazyToModel1:
return 1;
385 case ConstraintAddMode::LazyToModel2:
return 2;
386 case ConstraintAddMode::LazyToModel3:
return 3;
387 case ConstraintAddMode::NotLazyToModel:
return 0;
388 default:
throw std::out_of_range(
"unexpected ConstraintAddMode");
404 template<
typename WRAPPER_EXCEPTION = cgrame_error>
405 [[noreturn]]
void throwExceptionButWrapIfGurobiException(std::exception_ptr eptr) {
407 std::rethrow_exception(eptr);
408 }
catch (
const GRBException& grbex) {
409 std::throw_with_nested(WRAPPER_EXCEPTION(
410 "wrapped GRBException with code=" +
std::to_string(grbex.getErrorCode())
411 +
" and message='" + grbex.getMessage() +
'\''