24#include "absl/base/attributes.h"
25#include "absl/container/btree_map.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/log/check.h"
29#include "absl/meta/type_traits.h"
30#include "absl/types/span.h"
31#include "google/protobuf/message.h"
37#include "ortools/sat/cp_model.pb.h"
52#include "ortools/sat/sat_parameters.pb.h"
66std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
68 const absl::flat_hash_set<IntegerValue>& encoded_values,
73 const auto* domains =
model.Get<IntegerDomains>();
74 if (domains ==
nullptr ||
index >= domains->size()) {
81 for (
const int64_t v : (*domains)[
index].
Values()) {
82 if (!encoded_values.contains(IntegerValue(v))) {
83 min = IntegerValue(v);
89 const Domain negated_domain = (*domains)[
index].Negation();
90 for (
const int64_t v : negated_domain.Values()) {
91 if (!encoded_values.contains(IntegerValue(-v))) {
92 max = IntegerValue(-v);
105void CollectAffineExpressionWithSingleVariable(
106 const ConstraintProto&
ct, CpModelMapping* mapping, IntegerVariable*
var,
107 std::vector<std::pair<IntegerValue, IntegerValue>>* affines) {
109 CHECK_EQ(
ct.constraint_case(), ConstraintProto::ConstraintCase::kLinMax);
112 for (
const LinearExpressionProto& expr :
ct.lin_max().exprs()) {
113 if (expr.vars().empty()) {
114 affines->push_back({IntegerValue(0), IntegerValue(expr.offset())});
116 CHECK_EQ(expr.vars().size(), 1);
117 const IntegerVariable affine_var = mapping->Integer(expr.vars(0));
122 CHECK_EQ(affine_var, *
var);
124 {IntegerValue(expr.coeffs(0)), IntegerValue(expr.offset())});
128 {IntegerValue(-expr.coeffs(0)), IntegerValue(expr.offset())});
139 int* num_tight,
int* num_loose) {
142 if (encoder ==
nullptr || integer_trail ==
nullptr)
return;
144 std::vector<Literal> at_most_one_ct;
145 absl::flat_hash_set<IntegerValue> encoded_values;
146 std::vector<ValueLiteralPair> encoding;
148 const std::vector<ValueLiteralPair>& initial_encoding =
149 encoder->PartialDomainEncoding(
var);
150 if (initial_encoding.empty())
return;
151 for (
const auto value_literal : initial_encoding) {
160 encoding.push_back(value_literal);
161 at_most_one_ct.push_back(
literal);
162 encoded_values.insert(value_literal.value);
165 if (encoded_values.empty())
return;
172 const auto [min_not_encoded, max_not_encoded] =
173 GetMinAndMaxNotEncoded(
var, encoded_values,
model);
178 const IntegerValue rhs = encoding[0].value;
183 for (
const auto value_literal : encoding) {
187 const IntegerValue
delta = value_literal.value - rhs;
188 if (
delta != IntegerValue(0)) {
189 CHECK_GE(
delta, IntegerValue(0));
210 if (min_not_encoded == max_not_encoded) {
211 const IntegerValue rhs = min_not_encoded;
214 for (
const auto value_literal : encoding) {
216 rhs - value_literal.value));
230 const IntegerValue d_min = min_not_encoded;
233 for (
const auto value_literal : encoding) {
235 d_min - value_literal.value));
245 const IntegerValue d_max = max_not_encoded;
248 for (
const auto value_literal : encoding) {
250 d_max - value_literal.value));
267 if (integer_trail ==
nullptr || encoder ==
nullptr)
return;
269 const auto& greater_than_encoding = encoder->PartialGreaterThanEncoding(
var);
270 if (greater_than_encoding.empty())
return;
275 IntegerValue prev_used_bound = integer_trail->LowerBound(
var);
279 for (
const auto entry : greater_than_encoding) {
280 if (entry.value <= prev_used_bound)
continue;
282 const LiteralIndex literal_index = entry.literal.Index();
283 const IntegerValue diff = prev_used_bound - entry.value;
292 prev_used_bound = entry.value;
293 prev_literal_index = literal_index;
303 IntegerValue prev_used_bound = integer_trail->LowerBound(
NegationOf(
var));
306 for (
const auto entry :
308 if (entry.value <= prev_used_bound)
continue;
309 const IntegerValue diff = prev_used_bound - entry.value;
313 prev_used_bound = entry.value;
323bool AllLiteralsHaveViews(
const IntegerEncoder& encoder,
324 absl::Span<const Literal> literals) {
325 for (
const Literal
lit : literals) {
326 if (!encoder.LiteralOrNegationHasView(
lit))
return false;
337 for (
const int enforcement_ref :
ct.enforcement_literal()) {
338 CHECK(lc.AddLiteralTerm(mapping->Literal(
NegatedRef(enforcement_ref)),
341 for (
const int ref :
ct.bool_or().literals()) {
342 CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
359 if (
ct.enforcement_literal().size() == 1) {
361 for (
const int ref :
ct.bool_and().literals()) {
363 {enforcement, mapping->
Literal(ref).Negated()});
384 if (activity_helper !=
nullptr) {
385 std::vector<int> negated_lits;
386 for (
const int ref :
ct.bool_and().literals()) {
389 for (absl::Span<const int> part :
392 for (
const int negated_ref : part) {
395 for (
const int enforcement_ref :
ct.enforcement_literal()) {
397 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
403 for (
const int ref :
ct.bool_and().literals()) {
406 for (
const int enforcement_ref :
ct.enforcement_literal()) {
408 mapping->Literal(
NegatedRef(enforcement_ref)), IntegerValue(-1)));
422 mapping->Literals(
ct.at_most_one().literals()));
431 const std::vector<Literal> literals =
432 mapping->Literals(
ct.exactly_one().literals());
433 if (AllLiteralsHaveViews(*encoder, literals)) {
450 if (num_literals == 1) {
455 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
459 if (num_literals == 2) {
462 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
468 encoder->AssociateToIntegerEqualValue(
lit.Negated(), var2, IntegerValue(1));
470 return {
lit,
lit.Negated()};
473 std::vector<Literal> literals;
475 for (
int i = 0;
i < num_literals; ++
i) {
478 encoder->GetOrCreateLiteralAssociatedToEquality(
var, IntegerValue(1));
479 literals.push_back(
lit);
491 const int num_arcs =
ct.circuit().literals_size();
492 CHECK_EQ(num_arcs,
ct.circuit().tails_size());
493 CHECK_EQ(num_arcs,
ct.circuit().heads_size());
497 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
498 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
499 for (
int i = 0;
i < num_arcs;
i++) {
501 const int tail =
ct.circuit().tails(
i);
502 const int head =
ct.circuit().heads(
i);
503 outgoing_arc_constraints[
tail].push_back(
arc);
504 incoming_arc_constraints[
head].push_back(
arc);
506 for (
const auto* node_map :
507 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
508 for (
const auto& entry : *node_map) {
509 const std::vector<Literal>& exactly_one = entry.second;
510 if (exactly_one.size() > 1) {
513 for (
const Literal l : exactly_one) {
529 const int num_arcs =
ct.routes().literals_size();
530 CHECK_EQ(num_arcs,
ct.routes().tails_size());
531 CHECK_EQ(num_arcs,
ct.routes().heads_size());
537 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
538 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
539 for (
int i = 0;
i < num_arcs;
i++) {
541 const int tail =
ct.routes().tails(
i);
542 const int head =
ct.routes().heads(
i);
543 outgoing_arc_constraints[
tail].push_back(
arc);
544 incoming_arc_constraints[
head].push_back(
arc);
546 for (
const auto* node_map :
547 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
548 for (
const auto& entry : *node_map) {
549 if (entry.first == 0)
continue;
550 const std::vector<Literal>& exactly_one = entry.second;
551 if (exactly_one.size() > 1) {
554 for (
const Literal l : exactly_one) {
566 for (
const Literal& incoming_arc : incoming_arc_constraints[0]) {
567 CHECK(zero_node_balance_lc.
AddLiteralTerm(incoming_arc, IntegerValue(1)));
569 for (
const Literal& outgoing_arc : outgoing_arc_constraints[0]) {
570 CHECK(zero_node_balance_lc.
AddLiteralTerm(outgoing_arc, IntegerValue(-1)));
577 std::vector<int> tails(
ct.circuit().tails().begin(),
578 ct.circuit().tails().end());
579 std::vector<int> heads(
ct.circuit().heads().begin(),
580 ct.circuit().heads().end());
582 std::vector<Literal> literals = mapping->
Literals(
ct.circuit().literals());
586 num_nodes, tails, heads, literals, m));
591 std::vector<int> tails(
ct.routes().tails().begin(),
592 ct.routes().tails().end());
593 std::vector<int> heads(
ct.routes().heads().begin(),
594 ct.routes().heads().end());
596 std::vector<Literal> literals = mapping->
Literals(
ct.routes().literals());
599 for (
int i = 0;
i <
ct.routes().tails_size(); ++
i) {
600 num_nodes = std::max(num_nodes, 1 +
ct.routes().tails(
i));
601 num_nodes = std::max(num_nodes, 1 +
ct.routes().heads(
i));
603 if (
ct.routes().demands().empty() ||
ct.routes().capacity() == 0) {
608 const std::vector<int64_t> demands(
ct.routes().demands().begin(),
609 ct.routes().demands().end());
611 num_nodes, tails, heads, literals, demands,
ct.routes().capacity(), m));
631 const std::vector<IntervalVariable>& intervals,
632 const std::vector<AffineExpression>& demands,
638 if (!integer_trail->
IsFixed(capacity)) {
644 for (
int i = 0;
i < intervals.size(); ++
i) {
645 if (repository->IsAbsent(intervals[
i]))
continue;
647 horizon, integer_trail->
UpperBound(repository->End(intervals[
i])));
650 const IntegerValue capacity_value = integer_trail->
FixedValue(capacity);
651 for (
int i = 0;
i < intervals.size(); ++
i) {
652 if (!repository->IsPresent(intervals[
i]))
continue;
654 if (integer_trail->
IsFixed(demands[
i]) &&
655 integer_trail->
FixedValue(demands[
i]) == capacity_value &&
658 integer_trail->
LowerBound(repository->Size(intervals[
i])) > 0) {
667std::optional<AffineExpression> DetectMakespanFromPrecedences(
668 const SchedulingConstraintHelper& helper, Model*
model) {
669 if (helper.NumTasks() == 0)
return {};
671 const absl::Span<const AffineExpression> ends = helper.Ends();
672 std::vector<IntegerVariable> end_vars;
673 for (
const AffineExpression&
end : ends) {
676 if (
end.coeff != 1)
return {};
677 end_vars.push_back(
end.var);
680 std::vector<FullIntegerPrecedence> output;
681 auto* precedences =
model->GetOrCreate<PrecedenceRelations>();
682 precedences->ComputeFullPrecedences(end_vars, &output);
683 for (
const auto& p : output) {
685 if (p.indices.size() != ends.size())
continue;
690 for (
int i = 0;
i < p.indices.size(); ++
i) {
700 std::min(min_delta, p.offsets[
i] - ends[p.indices[
i]].constant);
702 VLOG(2) <<
"Makespan detected >= ends + " << min_delta;
703 return AffineExpression(p.var, IntegerValue(1), -min_delta);
717 std::vector<IntervalVariable> intervals =
719 const IntegerValue one(1);
720 std::vector<AffineExpression> demands(intervals.size(), one);
723 std::optional<AffineExpression> makespan;
724 const std::optional<int> makespan_index =
726 if (makespan_index.has_value()) {
727 makespan = repository->
Start(intervals[makespan_index.value()]);
729 intervals.erase(intervals.begin() + makespan_index.value());
737 if (!makespan.has_value()) {
738 makespan = DetectMakespanFromPrecedences(*helper,
model);
743 if (
model->GetOrCreate<SatParameters>()->linearization_level() > 1) {
753 std::vector<IntervalVariable> intervals =
755 std::vector<AffineExpression> demands =
756 mapping->Affines(
ct.cumulative().demands());
758 const std::optional<int> makespan_index =
760 std::optional<AffineExpression> makespan;
762 if (makespan_index.has_value()) {
764 makespan = repository->
Start(intervals[makespan_index.value()]);
765 demands.erase(demands.begin() + makespan_index.value());
766 intervals.erase(intervals.begin() + makespan_index.value());
775 if (!makespan.has_value()) {
776 makespan = DetectMakespanFromPrecedences(*helper,
model);
782 if (
model->GetOrCreate<SatParameters>()->linearization_level() > 1) {
794 const std::optional<AffineExpression>& makespan,
796 const int num_intervals = helper->
NumTasks();
801 int num_variable_energies = 0;
802 int num_optionals = 0;
803 int64_t sizes_gcd = 0;
804 int64_t demands_gcd = 0;
805 int64_t num_active_intervals = 0;
822 num_variable_energies++;
824 if (sizes_gcd != 1) {
826 sizes_gcd = std::gcd(helper->
SizeMin(
index).value(), sizes_gcd);
831 if (demands_gcd != 1) {
839 num_active_intervals++;
840 min_of_starts = std::min(min_of_starts, helper->
StartMin(
index));
841 max_of_ends = std::max(max_of_ends, helper->
EndMax(
index));
844 VLOG(2) <<
"Span [" << min_of_starts <<
".." << max_of_ends <<
"] with "
845 << num_optionals <<
" optional intervals, and "
846 << num_variable_energies <<
" variable energy tasks out of "
847 << num_active_intervals <<
"/" << num_intervals <<
" intervals"
848 << (makespan.has_value() ?
", and 1 makespan" :
"")
849 <<
" sizes_gcd: " << sizes_gcd <<
" demands_gcd: " << demands_gcd;
852 if (num_active_intervals == 0)
return;
856 if (num_variable_energies + num_optionals == 0 && sizes_gcd == 1 &&
862 if (sizes_gcd != 1 && !makespan.has_value()) {
863 VLOG(2) <<
"Cumulative relaxation: sizes_gcd = " << sizes_gcd
864 <<
", demands_gcd = " << demands_gcd
865 <<
", no makespan, capacity is " << capacity.
DebugString();
868 if (!integer_trail->
IsFixed(capacity)) demands_gcd = 1;
879 if (energy_min == 0)
continue;
880 DCHECK_EQ(energy_min % (sizes_gcd * demands_gcd), 0);
882 energy_min / sizes_gcd / demands_gcd)) {
887 std::vector<LiteralValueValue> product =
889 if (!product.empty()) {
893 DCHECK_EQ(entry.left_value % sizes_gcd, 0);
894 entry.left_value /= sizes_gcd;
895 DCHECK_EQ(entry.right_value % demands_gcd, 0);
896 entry.right_value /= demands_gcd;
901 const IntegerValue local_size =
903 DCHECK_EQ(local_size % sizes_gcd, 0);
904 if (demands_gcd == 1) {
906 local_size / sizes_gcd);
908 const IntegerValue local_demand =
910 DCHECK_EQ(local_demand % demands_gcd, 0);
911 lc.
AddConstant(local_size * local_demand / sizes_gcd / demands_gcd);
918 if (integer_trail->
IsFixed(capacity)) {
919 const IntegerValue span = max_of_ends - min_of_starts;
920 const IntegerValue fixed_capacity = integer_trail->
FixedValue(capacity);
924 DCHECK_EQ(demands_gcd, 1);
925 lc.
AddTerm(capacity, -(max_of_ends - min_of_starts) / sizes_gcd);
938 if (energy_min == 0)
continue;
943 const std::vector<LiteralValueValue>& product =
945 if (!product.empty()) {
966 makespan.has_value() ? makespan.value() : max_of_ends;
975 CHECK(
ct.has_no_overlap_2d());
979 std::vector<IntervalVariable> x_intervals =
980 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
981 std::vector<IntervalVariable> y_intervals =
982 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
991 std::vector<AffineExpression> x_sizes;
992 std::vector<AffineExpression> y_sizes;
993 for (
int i = 0;
i <
ct.no_overlap_2d().x_intervals_size(); ++
i) {
994 x_sizes.push_back(intervals_repository->Size(x_intervals[
i]));
995 y_sizes.push_back(intervals_repository->Size(y_intervals[
i]));
996 x_min = std::min(x_min, integer_trail->LevelZeroLowerBound(
997 intervals_repository->Start(x_intervals[
i])));
998 x_max = std::max(x_max, integer_trail->LevelZeroUpperBound(
999 intervals_repository->End(x_intervals[
i])));
1000 y_min = std::min(y_min, integer_trail->LevelZeroLowerBound(
1001 intervals_repository->Start(y_intervals[
i])));
1002 y_max = std::max(y_max, integer_trail->LevelZeroUpperBound(
1003 intervals_repository->End(y_intervals[
i])));
1006 const IntegerValue max_area =
1008 CapSub(y_max.value(), y_min.value())));
1013 for (
int i = 0;
i <
ct.no_overlap_2d().x_intervals_size(); ++
i) {
1014 if (intervals_repository->IsPresent(x_intervals[
i]) &&
1015 intervals_repository->IsPresent(y_intervals[
i])) {
1016 const std::vector<LiteralValueValue>
energy =
1017 product_decomposer->TryToDecompose(x_sizes[
i], y_sizes[
i]);
1019 if (!lc.AddDecomposedProduct(
energy))
return;
1021 lc.AddQuadraticLowerBound(x_sizes[
i], y_sizes[
i], integer_trail);
1023 }
else if (intervals_repository->IsPresent(x_intervals[
i]) ||
1024 intervals_repository->IsPresent(y_intervals[
i]) ||
1025 (intervals_repository->PresenceLiteral(x_intervals[
i]) ==
1026 intervals_repository->PresenceLiteral(y_intervals[
i]))) {
1028 const Literal presence_literal =
1029 intervals_repository->IsPresent(x_intervals[
i])
1030 ? intervals_repository->PresenceLiteral(y_intervals[
i])
1031 : intervals_repository->PresenceLiteral(x_intervals[
i]);
1032 const IntegerValue area_min =
1034 integer_trail->LevelZeroLowerBound(y_sizes[
i]);
1035 if (area_min != 0) {
1037 (void)lc.AddLiteralTerm(presence_literal, area_min);
1051 NegationOf(mapping->GetExprFromProto(
ct.lin_max().target()));
1052 for (
int i = 0;
i <
ct.lin_max().exprs_size(); ++
i) {
1054 mapping->GetExprFromProto(
ct.lin_max().exprs(
i));
1068 IntegerVariable
var;
1069 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
1071 CollectAffineExpressionWithSingleVariable(
ct, mapping, &
var, &affines);
1088 IntegerVariable
var;
1089 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
1091 CollectAffineExpressionWithSingleVariable(
ct, mapping, &
var, &affines);
1098 if (
ct.lin_max().target().vars().empty())
return;
1103 target_expr,
var, affines,
"AffineMax",
model));
1111 IntegerVariable target,
const std::vector<Literal>& alternative_literals,
1112 const std::vector<LinearExpression>& exprs,
Model*
model,
1114 const int num_exprs = exprs.size();
1118 for (
int i = 0;
i < num_exprs; ++
i) {
1121 local_expr.
vars.push_back(target);
1122 local_expr.
coeffs = exprs[
i].coeffs;
1123 local_expr.
coeffs.push_back(IntegerValue(1));
1133 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
1134 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
1138 absl::flat_hash_map<std::pair<int, IntegerVariable>, IntegerValue> cache;
1139 for (
int i = 0;
i < num_exprs; ++
i) {
1140 for (
int j = 0; j < exprs[
i].vars.size(); ++j) {
1141 cache[std::make_pair(
i, exprs[
i].vars[j])] = exprs[
i].coeffs[j];
1144 const auto get_coeff = [&cache](IntegerVariable
var,
int index) {
1145 const auto it = cache.find(std::make_pair(
index,
var));
1146 if (it == cache.end())
return IntegerValue(0);
1151 std::vector<IntegerVariable> active_vars;
1152 for (
int i = 0;
i + 1 < num_exprs; ++
i) {
1153 for (
int j =
i + 1; j < num_exprs; ++j) {
1154 active_vars = exprs[
i].vars;
1155 active_vars.insert(active_vars.end(), exprs[j].vars.begin(),
1156 exprs[j].vars.end());
1158 for (
const IntegerVariable x_var : active_vars) {
1159 const IntegerValue diff = get_coeff(x_var, j) - get_coeff(x_var,
i);
1160 if (diff == 0)
continue;
1164 sum_of_max_corner_diff[
i][j] += std::max(diff * lb, diff * ub);
1165 sum_of_max_corner_diff[j][
i] += std::max(-diff * lb, -diff * ub);
1170 for (
int i = 0;
i < num_exprs; ++
i) {
1172 lc.
AddTerm(target, IntegerValue(1));
1173 for (
int j = 0; j < exprs[
i].vars.size(); ++j) {
1174 lc.
AddTerm(exprs[
i].vars[j], -exprs[
i].coeffs[j]);
1176 for (
int j = 0; j < num_exprs; ++j) {
1178 -exprs[j].offset - sum_of_max_corner_diff[
i][j]));
1185 bool linearize_enforced_constraints,
1198 const IntegerValue rhs_domain_min = IntegerValue(
ct.linear().domain(0));
1199 const IntegerValue rhs_domain_max =
1200 IntegerValue(
ct.linear().domain(
ct.linear().domain_size() - 1));
1201 if (rhs_domain_min == std::numeric_limits<int64_t>::min() &&
1202 rhs_domain_max == std::numeric_limits<int64_t>::max())
1207 for (
int i = 0;
i <
ct.linear().vars_size();
i++) {
1208 const int ref =
ct.linear().vars(
i);
1209 const int64_t coeff =
ct.linear().coeffs(
i);
1210 lc.
AddTerm(mapping->Integer(ref), IntegerValue(coeff));
1217 if (!linearize_enforced_constraints)
return;
1225 if (!mapping->IsHalfEncodingConstraint(&
ct) &&
ct.linear().vars_size() <= 1 &&
1226 ct.enforcement_literal().size() <= 1) {
1230 std::vector<Literal> enforcing_literals;
1231 enforcing_literals.reserve(
ct.enforcement_literal_size());
1232 for (
const int enforcement_ref :
ct.enforcement_literal()) {
1233 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
1237 std::vector<std::pair<int, int64_t>> bool_terms;
1238 IntegerValue min_activity(0);
1239 IntegerValue max_activity(0);
1241 for (
int i = 0;
i <
ct.linear().vars_size();
i++) {
1242 const int ref =
ct.linear().vars(
i);
1243 const IntegerValue coeff(
ct.linear().coeffs(
i));
1244 const IntegerVariable int_var = mapping->Integer(ref);
1249 const IntegerValue lb = integer_trail->LowerBound(int_var);
1250 const IntegerValue ub = integer_trail->UpperBound(int_var);
1251 if (lb == 0 && ub == 1 && activity_helper !=
nullptr) {
1252 bool_terms.push_back({ref, coeff.value()});
1255 min_activity += coeff * lb;
1256 max_activity += coeff * ub;
1258 min_activity += coeff * ub;
1259 max_activity += coeff * lb;
1263 if (activity_helper !=
nullptr) {
1270 if (rhs_domain_min > min_activity) {
1279 for (
int i = 0;
i <
ct.linear().vars_size();
i++) {
1280 const int ref =
ct.linear().vars(
i);
1281 const IntegerValue coeff(
ct.linear().coeffs(
i));
1282 const IntegerVariable int_var = mapping->Integer(ref);
1287 if (rhs_domain_max < max_activity) {
1296 for (
int i = 0;
i <
ct.linear().vars_size();
i++) {
1297 const int ref =
ct.linear().vars(
i);
1298 const IntegerValue coeff(
ct.linear().coeffs(
i));
1299 const IntegerVariable int_var = mapping->Integer(ref);
1318 const ConstraintProto&
ct,
1323 DCHECK_GT(linearization_level, 0);
1326 switch (
ct.constraint_case()) {
1327 case ConstraintProto::ConstraintCase::kBoolOr: {
1328 if (linearization_level > 1) {
1333 case ConstraintProto::ConstraintCase::kBoolAnd: {
1334 if (linearization_level > 1) {
1339 case ConstraintProto::ConstraintCase::kAtMostOne: {
1343 case ConstraintProto::ConstraintCase::kExactlyOne: {
1347 case ConstraintProto::ConstraintCase::kIntProd: {
1348 const LinearArgumentProto& int_prod =
ct.int_prod();
1349 if (int_prod.exprs_size() == 2 &&
1351 int_prod.exprs(1))) {
1360 case ConstraintProto::ConstraintCase::kLinMax: {
1362 const bool is_affine_max =
1364 if (is_affine_max) {
1369 if (linearization_level > 1) {
1370 if (is_affine_max) {
1372 }
else if (
ct.lin_max().exprs().size() < 100) {
1378 case ConstraintProto::ConstraintCase::kAllDiff: {
1383 case ConstraintProto::ConstraintCase::kLinear: {
1385 ct, linearization_level > 1,
model,
1386 relaxation, activity_helper);
1389 case ConstraintProto::ConstraintCase::kCircuit: {
1391 if (linearization_level > 1) {
1396 case ConstraintProto::ConstraintCase::kRoutes: {
1398 if (linearization_level > 1) {
1403 case ConstraintProto::ConstraintCase::kNoOverlap: {
1407 case ConstraintProto::ConstraintCase::kCumulative: {
1411 case ConstraintProto::ConstraintCase::kNoOverlap2D: {
1418 if (linearization_level > 1) {
1434 LOG(INFO) <<
"Possible overflow in linearization of: "
1435 << google::protobuf::ShortFormat(
ct);
1446 if (
ct.int_prod().exprs_size() != 2)
return;
1461 if (x_lb < 0 && x_ub > 0)
return;
1462 if (y_lb < 0 && y_ub > 0)
return;
1476 z,
x,
y, linearization_level, m));
1488 IntegerValue x_lb = integer_trail->LowerBound(
x);
1489 IntegerValue x_ub = integer_trail->UpperBound(
x);
1491 if (x_lb == x_ub)
return;
1494 if (x_lb < 0 && x_ub > 0)
return;
1499 const IntegerValue tmp = x_ub;
1505 if (x_ub > (int64_t{1} << 31))
return;
1514 if (x_lb + 1 < x_ub) {
1530 const IntegerValue x_lb = integer_trail->LowerBound(
x);
1531 const IntegerValue x_ub = integer_trail->UpperBound(
x);
1534 if (x_lb < 0 && x_ub > 0)
return;
1546 int linearization_level,
Model* m,
1551 const int num_exprs =
ct.all_diff().exprs_size();
1553 const std::vector<AffineExpression> exprs =
1554 mapping->Affines(
ct.all_diff().exprs());
1560 if (integer_trail->IsFixed(expr)) {
1561 union_of_domains = union_of_domains.
UnionWith(
1562 Domain(integer_trail->FixedValue(expr).value()));
1564 union_of_domains = union_of_domains.
UnionWith(
1565 integer_trail->InitialVariableDomain(expr.var)
1566 .MultiplicationBy(expr.coeff.value())
1567 .AdditionWith(
Domain(expr.constant.value())));
1571 if (union_of_domains.
Size() == num_exprs) {
1573 int64_t sum_of_values = 0;
1574 for (
const int64_t v : union_of_domains.
Values()) {
1582 }
else if (num_exprs <=
1583 m->
GetOrCreate<SatParameters>()->max_all_diff_cut_size() &&
1584 linearization_level > 1) {
1614 const std::optional<AffineExpression>& makespan,
1617 helper, demands_helper, capacity, m));
1622 helper, demands_helper, capacity, m));
1626 bool has_variable_part =
false;
1630 has_variable_part =
true;
1635 has_variable_part =
true;
1639 if (has_variable_part || !integer_trail->
IsFixed(capacity)) {
1641 helper, demands_helper, capacity, makespan, m));
1646 const std::optional<AffineExpression>& makespan,
1654 bool has_variable_or_optional_part =
false;
1658 has_variable_or_optional_part =
true;
1662 if (has_variable_or_optional_part) {
1673 std::vector<IntervalVariable> x_intervals =
1674 mapping->
Intervals(
ct.no_overlap_2d().x_intervals());
1675 std::vector<IntervalVariable> y_intervals =
1676 mapping->Intervals(
ct.no_overlap_2d().y_intervals());
1689 bool has_variable_part =
false;
1690 for (
int i = 0;
i < x_intervals.size(); ++
i) {
1692 if (intervals_repository->
IsAbsent(x_intervals[
i]) ||
1693 intervals_repository->
IsAbsent(y_intervals[
i])) {
1698 if (!intervals_repository->
IsPresent(x_intervals[
i]) ||
1699 !intervals_repository->
IsPresent(y_intervals[
i])) {
1700 has_variable_part =
true;
1705 if (intervals_repository->
MinSize(x_intervals[
i]) !=
1706 intervals_repository->
MaxSize(x_intervals[
i]) ||
1707 intervals_repository->
MinSize(y_intervals[
i]) !=
1708 intervals_repository->
MaxSize(y_intervals[
i])) {
1709 has_variable_part =
true;
1714 if (!has_variable_part)
return;
1723 std::vector<std::vector<LiteralValueValue>> energies;
1724 const int num_rectangles = x_helper->NumTasks();
1726 for (
int i = 0;
i < num_rectangles; ++
i) {
1727 energies.push_back(product_decomposer->TryToDecompose(
1728 x_helper->Sizes()[
i], y_helper->
Sizes()[
i]));
1732 x_helper, y_helper, x_demands_helper, y_demands_helper, energies, m));
1737 if (!m->
GetOrCreate<SatParameters>()->add_lin_max_cuts())
return;
1742 if (
ct.lin_max().target().vars_size() != 1)
return;
1743 if (
ct.lin_max().target().coeffs(0) != 1)
return;
1744 if (
ct.lin_max().target().offset() != 0)
return;
1746 const IntegerVariable target =
1747 mapping->
Integer(
ct.lin_max().target().vars(0));
1748 std::vector<LinearExpression> exprs;
1749 exprs.reserve(
ct.lin_max().exprs_size());
1750 for (
int i = 0;
i <
ct.lin_max().exprs_size(); ++
i) {
1757 const std::vector<Literal> alternative_literals =
1767 std::vector<IntegerVariable> z_vars;
1769 for (
const Literal lit : alternative_literals) {
1770 z_vars.push_back(encoder->GetLiteralView(
lit));
1789 int num_exactly_one_elements = 0;
1790 for (
const IntegerVariable
var :
1791 element_encodings->GetElementEncodedVariables()) {
1792 for (
const auto& [
index, literal_value_list] :
1793 element_encodings->Get(
var)) {
1795 for (
const auto& literal_value : literal_value_list) {
1796 min_value = std::min(min_value, literal_value.value);
1801 for (
const auto& [
value,
literal] : literal_value_list) {
1802 const IntegerValue delta_min =
value - min_value;
1803 if (delta_min != 0) {
1810 ++num_exactly_one_elements;
1818 if (num_exactly_one_elements != 0) {
1821 "[ElementLinearRelaxation]"
1822 " #from_exactly_one:",
1823 num_exactly_one_elements);
1830 const SatParameters& params = *m->
GetOrCreate<SatParameters>();
1834 if (params.linearization_level() > 1) {
1835 activity_bound_helper.AddAllAtMostOnes(model_proto);
1839 for (
const auto&
ct : model_proto.constraints()) {
1841 &relaxation, &activity_bound_helper);
1845 int num_loose_equality_encoding_relaxations = 0;
1846 int num_tight_equality_encoding_relaxations = 0;
1847 int num_inequality_encoding_relaxations = 0;
1849 for (
int i = 0;
i < model_proto.variables_size(); ++
i) {
1850 if (mapping->IsBoolean(
i))
continue;
1852 const IntegerVariable
var = mapping->Integer(
i);
1857 var, *m, &relaxation, &num_tight_equality_encoding_relaxations,
1858 &num_loose_equality_encoding_relaxations);
1873 ++num_inequality_encoding_relaxations;
1879 if (params.linearization_level() >= 2) {
1891 if (num_tight_equality_encoding_relaxations != 0 ||
1892 num_loose_equality_encoding_relaxations != 0 ||
1893 num_inequality_encoding_relaxations != 0) {
1895 "[EncodingLinearRelaxation]"
1896 " #tight_equality:",
1897 num_tight_equality_encoding_relaxations,
1898 " #loose_equality:", num_loose_equality_encoding_relaxations,
1899 " #inequality:", num_inequality_encoding_relaxations);
1904 "[LinearRelaxationBeforeCliqueExpansion]"
1915 for (
const std::vector<Literal>& at_most_one : relaxation.
at_most_ones) {
1916 if (at_most_one.empty())
continue;
1922 const bool unused ABSL_ATTRIBUTE_UNUSED =
1939 if (lc.
lb > 0 || lc.
ub < 0) {
1976 if (params.linearization_level() > 1 && params.add_clique_cuts()) {
1978 for (
int i = 0;
i < model_proto.variables_size(); ++
i) {
1979 if (!mapping->IsBoolean(
i))
continue;
1983 const bool unused ABSL_ATTRIBUTE_UNUSED =
1989 if (!expr.
vars.empty()) {
1998 "[FinalLinearRelaxation]"
DomainIteratorBeginEnd Values() const &
Domain UnionWith(const Domain &domain) const
int64_t ComputeMaxActivity(absl::Span< const std::pair< int, int64_t > > terms, std::vector< std::array< int64_t, 2 > > *conditional=nullptr)
int64_t ComputeMinActivity(absl::Span< const std::pair< int, int64_t > > terms, std::vector< std::array< int64_t, 2 > > *conditional=nullptr)
std::vector< absl::Span< const int > > PartitionLiteralsIntoAmo(absl::Span< const int > literals)
Similar algo as above for this simpler case.
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
IntegerVariable Integer(int ref) const
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue FixedValue(IntegerVariable i) const
Checks that the variable is fixed and returns its value.
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
SchedulingDemandHelper * GetOrCreateDemandHelper(SchedulingConstraintHelper *helper, absl::Span< const AffineExpression > demands)
AffineExpression Start(IntervalVariable i) const
bool IsAbsent(IntervalVariable i) const
IntegerValue MaxSize(IntervalVariable i) const
Return the maximum size of the given IntervalVariable.
IntegerValue MinSize(IntervalVariable i) const
Return the minimum size of the given IntervalVariable.
SchedulingConstraintHelper * GetOrCreateHelper(const std::vector< IntervalVariable > &variables, bool register_as_disjunctive_helper=false)
bool IsPresent(IntervalVariable i) const
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
LinearExpression BuildExpression()
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddConstant(IntegerValue value)
Adds the corresponding term to the current linear expression.
void Clear()
Clears all added terms and constants. Keeps the original bounds.
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(absl::Span< const LiteralValueValue > product)
void RegisterWith(GenericLiteralWatcher *watcher)
Literal(int signed_value)
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Helper class to express a product as a linear constraint.
void NotifyThatModelIsUnsat()
int CurrentDecisionLevel() const
ABSL_MUST_USE_RESULT bool FinishPropagation()
bool SizeIsFixed(int t) const
int NumTasks() const
Returns the number of task.
absl::Span< const AffineExpression > Sizes() const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
IntegerValue SizeMax(int t) const
IntegerValue StartMin(int t) const
IntegerValue SizeMin(int t) const
ABSL_MUST_USE_RESULT bool SynchronizeAndSetTimeDirection(bool is_forward)
IntegerValue EndMax(int t) const
bool IsAbsent(int t) const
bool IsOptional(int t) const
bool DemandIsFixed(int t) const
const std::vector< std::vector< LiteralValueValue > > & DecomposedEnergies() const
IntegerValue DemandMax(int t) const
void CacheAllEnergyValues()
const std::vector< AffineExpression > & Demands() const
IntegerValue DemandMin(int t) const
IntegerValue EnergyMin(int t) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
std::vector< typename Map::mapped_type > Values(const Map &map, const Keys &keys)
void AppendNoOverlapRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
void AppendCumulativeRelaxationAndCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
void AddSquareCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AppendExactlyOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCVRPCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, std::vector< int64_t > demands, int64_t capacity, Model *model)
void AddCumulativeCutGenerator(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
void AppendRoutesRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
bool BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue > > &affines, Model *model, LinearConstraintBuilder *builder)
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
A cut generator for z = x * y (x and y >= 0).
const LiteralIndex kNoLiteralIndex(-1)
void AddMaxAffineCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddCircuitCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x, AffineExpression square, IntegerValue x_value, Model *model)
CutGenerator CreateCumulativePrecedenceCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
Builds the linear relaxation of a CpModelProto.
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
bool HasEnforcementLiteral(const ConstraintProto &ct)
Small utility functions to deal with half-reified constraints.
void AppendLinearConstraintRelaxation(const ConstraintProto &ct, bool linearize_enforced_constraints, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
void AddIntProdCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
Cut generators.
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper, Model *model)
void AddRoutesCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeEnergyCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, const std::optional< AffineExpression > &makespan, Model *model)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, std::vector< int > tails, std::vector< int > heads, std::vector< Literal > literals, Model *model)
const IntegerVariable kNoIntegerVariable(-1)
bool PossibleOverflow(const IntegerTrail &integer_trail, const LinearConstraint &constraint)
IntType FloorOfRatio(IntType numerator, IntType denominator)
void AddCumulativeRelaxation(const AffineExpression &capacity, SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const std::optional< AffineExpression > &makespan, Model *model, LinearRelaxation *relaxation)
Scheduling relaxations and cut generators.
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
void TryToLinearizeConstraint(const CpModelProto &, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
Adds linearization of different types of constraints.
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateCumulativeTimeTableCutGenerator(SchedulingConstraintHelper *helper, SchedulingDemandHelper *demands_helper, const AffineExpression &capacity, Model *model)
std::optional< int > DetectMakespan(const std::vector< IntervalVariable > &intervals, const std::vector< AffineExpression > &demands, const AffineExpression &capacity, Model *model)
int64_t SafeDoubleToInt64(double value)
void AppendLinMaxRelaxationPart1(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(SchedulingConstraintHelper *helper, Model *model)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue > > affines, const std::string cut_name, Model *model)
int ReindexArcs(IntContainer *tails, IntContainer *heads, absl::flat_hash_map< int, int > *mapping_output=nullptr)
void AppendCircuitRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
Routing relaxation and cut generators.
void AppendElementEncodingRelaxation(Model *m, LinearRelaxation *relaxation)
LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x, AffineExpression square, IntegerValue x_lb, IntegerValue x_ub, Model *model)
void AppendMaxAffineRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendLinMaxRelaxationPart2(IntegerVariable target, const std::vector< Literal > &alternative_literals, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
bool ExpressionsContainsOnlyOneVar(const ExpressionList &exprs)
Returns true if there exactly one variable appearing in all the expressions.
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
CutGenerator CreateNoOverlapEnergyCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *model)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
bool IntervalIsVariable(const IntervalVariable interval, IntervalsRepository *intervals_repository)
void AppendSquareRelaxation(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(SchedulingConstraintHelper *helper, Model *model)
void AddLinMaxCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
Returns the same expression with positive variables.
void AppendBoolAndRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation, ActivityBoundHelper *activity_helper)
void AddAllDiffRelaxationAndCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
void AppendNoOverlap2dRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
Adds the energetic relaxation sum(areas) <= bounding box area.
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
void AppendAtMostOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddNoOverlapCutGenerator(SchedulingConstraintHelper *helper, const std::optional< AffineExpression > &makespan, Model *m, LinearRelaxation *relaxation)
void AddNoOverlap2dCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
CutGenerator CreateNoOverlap2dEnergyCutGenerator(SchedulingConstraintHelper *x_helper, SchedulingConstraintHelper *y_helper, SchedulingDemandHelper *x_demands_helper, SchedulingDemandHelper *y_demands_helper, const std::vector< std::vector< LiteralValueValue > > &energies, Model *model)
bool VariableIsPositive(IntegerVariable i)
std::vector< Literal > CreateAlternativeLiteralsWithView(int num_literals, Model *model, LinearRelaxation *relaxation)
bool LinearExpressionProtosAreEqual(const LinearExpressionProto &a, const LinearExpressionProto &b, int64_t b_scaling)
Returns true iff a == b * b_scaling.
LinearConstraintPropagator< false > IntegerSumLE
void AppendBoolOrRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendRelaxationForEqualityEncoding(IntegerVariable var, const Model &model, LinearRelaxation *relaxation, int *num_tight, int *num_loose)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
std::optional< int64_t > end
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
var * coeff + constant >= bound.
std::string DebugString() const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
var * coeff + constant <= bound.
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
std::vector< IntegerVariable > vars
std::vector< IntegerValue > coeffs
std::vector< CutGenerator > cut_generators
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints
#define SOLVER_LOG(logger,...)