67bool RoutingModelCanBeSolvedBySat(
const RoutingModel& model) {
72int AddVariable(
CpModelProto* cp_model, int64_t lb, int64_t ub) {
82void AddLinearConstraint(
83 CpModelProto* cp_model, int64_t lower_bound, int64_t upper_bound,
84 absl::Span<
const std::pair<int, double>> variable_coeffs,
85 absl::Span<const int> enforcement_literals) {
86 CHECK_LE(lower_bound, upper_bound);
88 for (
const int enforcement_literal : enforcement_literals) {
94 for (
const auto [var, coeff] : variable_coeffs) {
102void AddLinearConstraint(
103 CpModelProto* cp_model, int64_t lower_bound, int64_t upper_bound,
104 absl::Span<
const std::pair<int, double>> variable_coeffs) {
105 AddLinearConstraint(cp_model, lower_bound, upper_bound, variable_coeffs, {});
116 friend bool operator==(
const Arc& a,
const Arc&
b) {
117 return a.tail ==
b.tail && a.head ==
b.head;
119 friend bool operator!=(
const Arc& a,
const Arc&
b) {
return !(a ==
b); }
120 friend bool operator<(
const Arc& a,
const Arc&
b) {
121 return a.tail ==
b.tail ? a.head <
b.head : a.tail <
b.tail;
123 friend std::ostream&
operator<<(std::ostream& strm,
const Arc& arc) {
124 return strm <<
"{" << arc.tail <<
", " << arc.head <<
"}";
126 template <
typename H>
128 return H::combine(std::move(h), a.tail, a.head);
133 absl::btree_map<Arc, int>;
135void AddSoftCumulBounds(
const RoutingDimension* dimension,
int index,
int cumul,
136 int64_t cumul_min, int64_t cumul_max,
139 const int64_t soft_ub_coef =
141 if (soft_ub_coef != 0) {
143 const int soft_ub_var =
144 AddVariable(cp_model, 0,
CapSub(cumul_max, soft_ub));
146 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
147 soft_ub, {{cumul, 1}, {soft_ub_var, -1}});
153 const int64_t soft_lb_coef =
155 if (soft_lb_coef != 0) {
157 const int soft_lb_var =
158 AddVariable(cp_model, 0,
CapSub(soft_lb, cumul_min));
160 AddLinearConstraint(cp_model, soft_lb,
161 std::numeric_limits<int64_t>::max(),
162 {{cumul, 1}, {soft_lb_var, 1}});
171void AddDimensions(
const RoutingModel& model,
const ArcVarMap& arc_vars,
177 const int num_cumuls = dimension->
cumuls().size();
178 std::vector<int> cumuls(num_cumuls, -1);
179 const int64_t min_start = dimension->
cumuls()[model.
Start(0)]->Min();
180 const int64_t max_end = std::min(dimension->
cumuls()[model.
End(0)]->Max(),
182 for (
int i = 0;
i < cumuls.size(); ++
i) {
185 const int64_t cumul_min = std::max<int64_t>(
186 std::numeric_limits<int64_t>::min() / num_cumuls,
187 std::max(dimension->
cumuls()[
i]->Min(),
189 const int64_t cumul_max = std::min<int64_t>(
190 std::numeric_limits<int64_t>::max() / num_cumuls,
191 std::min(dimension->
cumuls()[
i]->Max(),
193 cumuls[
i] = AddVariable(cp_model, cumul_min, cumul_max);
194 AddSoftCumulBounds(dimension,
i, cumuls[
i], cumul_min, cumul_max,
197 for (
const auto arc_var : arc_vars) {
198 const int tail = arc_var.first.tail;
199 const int head = arc_var.first.head;
200 if (tail == head || model.
IsStart(tail) || model.
IsStart(head))
continue;
204 cp_model, transit(tail, head), std::numeric_limits<int64_t>::max(),
205 {{cumuls[head], 1}, {cumuls[tail], -1}}, {arc_var.second});
211 const ArcVarMap& arc_vars,
213 const int depot = GetDepotFromModel(model);
216 std::vector<int> ranks(size, -1);
217 for (
int i = 0;
i < size; ++
i) {
219 ranks[
i] = AddVariable(cp_model, 0, rank_size);
221 ranks[depot] = AddVariable(cp_model, 0, 0);
222 for (
const auto arc_var : arc_vars) {
223 const int tail = arc_var.first.tail;
224 const int head = arc_var.first.head;
225 if (tail == head || head == depot)
continue;
227 AddLinearConstraint(cp_model, 1, 1, {{ranks[head], 1}, {ranks[tail], -1}},
237std::vector<int> CreateVehicleVars(
const RoutingModel& model,
238 const ArcVarMap& arc_vars,
240 const int depot = GetDepotFromModel(model);
242 std::vector<int> vehicles(size, -1);
243 for (
int i = 0;
i < size; ++
i) {
245 vehicles[
i] = AddVariable(cp_model, 0, size - 1);
247 for (
const auto arc_var : arc_vars) {
248 const int tail = arc_var.first.tail;
249 const int head = arc_var.first.head;
250 if (tail == head || head == depot)
continue;
253 AddLinearConstraint(cp_model, head, head, {{vehicles[head], 1}},
258 AddLinearConstraint(cp_model, 0, 0,
259 {{vehicles[head], 1}, {vehicles[tail], -1}},
265void AddPickupDeliveryConstraints(
const RoutingModel& model,
266 const ArcVarMap& arc_vars,
269 const std::vector<int> ranks = CreateRanks(model, arc_vars, cp_model);
270 const std::vector<int> vehicles =
271 CreateVehicleVars(model, arc_vars, cp_model);
273 const int64_t pickup = pickups[0];
274 const int64_t delivery = deliveries[0];
276 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
277 {{ranks[delivery], 1}, {ranks[pickup], -1}});
279 AddLinearConstraint(cp_model, 0, 0,
280 {{vehicles[delivery], 1}, {vehicles[pickup], -1}});
290ArcVarMap PopulateMultiRouteModelFromRoutingModel(
const RoutingModel& model,
293 const int num_nodes = model.
Nexts().size();
294 const int depot = GetDepotFromModel(model);
297 for (
int tail = 0; tail < num_nodes; ++tail) {
298 const int tail_index = model.
IsStart(tail) ? depot : tail;
299 std::unique_ptr<IntVarIterator> iter(
306 if (model.
IsStart(head))
continue;
307 const int head_index = model.
IsEnd(head) ? depot : head;
308 if (head_index == tail_index && head_index == depot)
continue;
311 if (cost == std::numeric_limits<int64_t>::max())
continue;
312 const Arc arc = {tail_index, head_index};
313 if (arc_vars.contains(arc))
continue;
314 const int index = AddVariable(cp_model, 0, 1);
323 std::vector<std::pair<int, double>> variable_coeffs;
324 for (
int node = 0; node < num_nodes; ++node) {
327 if (var ==
nullptr)
continue;
328 variable_coeffs.push_back({*var, 1});
336 AddPickupDeliveryConstraints(model, arc_vars, cp_model);
338 AddDimensions(model, arc_vars, cp_model);
345 for (
const auto arc_var : arc_vars) {
346 const int tail = arc_var.first.tail;
347 const int head = arc_var.first.head;
348 routes_ct->
add_tails(tail == 0 ? depot : tail == depot ? 0 : tail);
349 routes_ct->
add_heads(head == 0 ? depot : head == depot ? 0 : head);
361 primary_dimension = dimension;
365 if (primary_dimension !=
nullptr) {
368 for (
int node = 0; node < num_nodes; ++node) {
371 if (!model.
IsEnd(node) && (!model.
IsStart(node) || node == depot)) {
384ArcVarMap PopulateSingleRouteModelFromRoutingModel(
const RoutingModel& model,
387 const int num_nodes = model.
Nexts().size();
390 for (
int tail = 0; tail < num_nodes; ++tail) {
391 std::unique_ptr<IntVarIterator> iter(
398 if (model.
IsStart(head))
continue;
402 if (cost == std::numeric_limits<int64_t>::max())
continue;
403 const int index = AddVariable(cp_model, 0, 1);
412 AddPickupDeliveryConstraints(model, arc_vars, cp_model);
413 AddDimensions(model, arc_vars, cp_model);
420ArcVarMap PopulateModelFromRoutingModel(
const RoutingModel& model,
423 return PopulateSingleRouteModelFromRoutingModel(model, cp_model);
425 return PopulateMultiRouteModelFromRoutingModel(model, cp_model);
438 int64_t cost_value = 0;
445 solution->SetObjectiveValue(cost_value);
464 const int depot = GetDepotFromModel(model);
466 for (
const auto& arc_var : arc_vars) {
467 if (response.
solution(arc_var.second) != 0) {
468 const int tail = arc_var.first.tail;
469 const int head = arc_var.first.head;
470 if (head == depot)
continue;
480 for (
int v = 0; v < model.
vehicles(); ++v) {
481 int current = model.
Start(v);
482 while (!model.
IsEnd(current) &&
486 if (model.
IsEnd(current))
continue;
489 ConvertObjectiveToSolution(response, objective, model,
solution);
495void AddGeneralizedDimensions(
497 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_performs_node,
498 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_class_performs_arc,
500 const int num_cp_nodes = model.
Nexts().size() + model.
vehicles() + 1;
503 std::vector<int> cumuls(num_cp_nodes, -1);
504 for (
int cp_node = 1; cp_node < num_cp_nodes; ++cp_node) {
505 const int node = cp_node - 1;
506 int64_t cumul_min = dimension->
cumuls()[node]->Min();
508 std::min(dimension->
cumuls()[node]->Max(),
509 std::numeric_limits<int64_t>::max() / (2 * num_cp_nodes));
515 cumuls[cp_node] = AddVariable(cp_model, cumul_min, cumul_max);
516 AddSoftCumulBounds(dimension, node, cumuls[cp_node], cumul_min, cumul_max,
521 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
522 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
523 if (!vehicle_performs_node[vehicle].contains(cp_node))
continue;
524 const int64_t vehicle_capacity =
526 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
527 vehicle_capacity, {{cumuls[cp_node], 1}},
528 {vehicle_performs_node[vehicle].at(cp_node)});
532 for (
auto vehicle_class = RoutingVehicleClassIndex(0);
534 std::vector<int> slack(num_cp_nodes, -1);
535 const int64_t slack_cost =
CapAdd(
538 for (
const auto [arc, arc_var] : arc_vars) {
539 const auto [cp_tail, cp_head] = arc;
540 if (cp_tail == cp_head || cp_tail == 0 || cp_head == 0)
continue;
541 if (!vehicle_class_performs_arc[vehicle_class.value()].contains(
546 if (slack[cp_tail] == -1) {
547 const int64_t slack_max =
548 cp_tail - 1 < dimension->
slacks().size()
549 ? dimension->
slacks()[cp_tail - 1]->Max()
551 slack[cp_tail] = AddVariable(
553 std::min(slack_max, std::numeric_limits<int64_t>::max() /
554 (2 * num_cp_nodes)));
555 if (slack_max > 0 && slack_cost > 0) {
561 vehicle_class)(cp_tail - 1, cp_head - 1);
565 cp_model, transit, transit,
566 {{cumuls[cp_head], 1}, {cumuls[cp_tail], -1}, {slack[cp_tail], -1}},
567 {vehicle_class_performs_arc[vehicle_class.value()].at(arc_var)});
572 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
573 const int64_t span_limit =
575 if (span_limit == std::numeric_limits<int64_t>::max())
continue;
576 int cp_start = model.
Start(vehicle) + 1;
577 int cp_end = model.
End(vehicle) + 1;
578 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
580 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}});
585 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
586 const auto [bound, cost] =
588 const int cp_start = model.
Start(vehicle) + 1;
589 const int cp_end = model.
End(vehicle) + 1;
591 AddVariable(cp_model, 0,
592 std::min(dimension->
cumuls()[model.
End(vehicle)]->Max(),
596 cp_model, std::numeric_limits<int64_t>::min(), bound,
597 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}, {extra, -1}});
606std::vector<int> CreateGeneralizedRanks(
const RoutingModel& model,
607 const ArcVarMap& arc_vars,
608 absl::Span<const int> is_unperformed,
611 const int num_cp_nodes = model.
Nexts().size() + model.
vehicles() + 1;
613 const int max_rank = num_cp_nodes - 2 * model.
vehicles();
614 std::vector<int> ranks(num_cp_nodes, -1);
615 ranks[depot] = AddVariable(cp_model, 0, 0);
616 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
617 if (model.
IsEnd(cp_node - 1))
continue;
618 ranks[cp_node] = AddVariable(cp_model, 0, max_rank);
620 AddLinearConstraint(cp_model, 0, 0, {{ranks[cp_node], 1}},
621 {is_unperformed[cp_node]});
623 for (
const auto [arc, arc_var] : arc_vars) {
624 const auto [cp_tail, cp_head] = arc;
625 if (cp_head == 0 || model.
IsEnd(cp_head - 1))
continue;
626 if (cp_tail == cp_head || cp_head == depot)
continue;
628 AddLinearConstraint(cp_model, 1, 1,
629 {{ranks[cp_head], 1}, {ranks[cp_tail], -1}}, {arc_var});
634void AddGeneralizedPickupDeliveryConstraints(
636 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_performs_node,
637 absl::Span<const int> is_unperformed,
CpModelProto* cp_model) {
639 const std::vector<int> ranks =
640 CreateGeneralizedRanks(model, arc_vars, is_unperformed, cp_model);
642 for (
const int delivery : deliveries) {
643 const int cp_delivery = delivery + 1;
644 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
645 const Arc vehicle_start_delivery_arc = {
646 static_cast<int>(model.
Start(vehicle) + 1), cp_delivery};
647 if (arc_vars.contains(vehicle_start_delivery_arc)) {
649 AddLinearConstraint(cp_model, 0, 0,
650 {{arc_vars.at(vehicle_start_delivery_arc), 1}});
654 for (
const int pickup : pickups) {
655 const int cp_pickup = pickup + 1;
656 const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
657 if (arc_vars.contains(delivery_pickup_arc)) {
659 AddLinearConstraint(cp_model, 0, 0,
660 {{arc_vars.at(delivery_pickup_arc), 1}});
663 DCHECK_GE(is_unperformed[cp_delivery], 0);
664 DCHECK_GE(is_unperformed[cp_pickup], 0);
667 const int delivery_performed = -is_unperformed[cp_delivery] - 1;
668 const int pickup_performed = -is_unperformed[cp_pickup] - 1;
670 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
676 {{vehicle_performs_node[vehicle].at(cp_delivery), 1},
677 {vehicle_performs_node[vehicle].at(cp_pickup), -1}},
678 {delivery_performed, pickup_performed});
683 std::vector<std::pair<int, double>> ranks_difference;
685 for (
const int pickup : pickups) {
686 const int cp_pickup = pickup + 1;
687 ranks_difference.push_back({ranks[cp_pickup], -1});
690 for (
const int delivery : deliveries) {
691 const int cp_delivery = delivery + 1;
692 ranks_difference.push_back({ranks[cp_delivery], 1});
695 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
706ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
710 const int num_nodes = model.
Nexts().size();
711 const int num_cp_nodes = num_nodes + model.
vehicles() + 1;
714 std::vector<absl::flat_hash_map<int, int>> vehicle_performs_node(
717 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
718 const int cp_start = model.
Start(vehicle) + 1;
719 const Arc start_arc = {depot, cp_start};
720 const int start_arc_var = AddVariable(cp_model, 1, 1);
721 DCHECK(!arc_vars.contains(start_arc));
722 arc_vars.insert({start_arc, start_arc_var});
724 const int cp_end = model.
End(vehicle) + 1;
725 const Arc end_arc = {cp_end, depot};
726 const int end_arc_var = AddVariable(cp_model, 1, 1);
727 DCHECK(!arc_vars.contains(end_arc));
728 arc_vars.insert({end_arc, end_arc_var});
730 vehicle_performs_node[vehicle][cp_start] = start_arc_var;
731 vehicle_performs_node[vehicle][cp_end] = end_arc_var;
736 std::vector<int> is_unperformed(num_cp_nodes, -1);
738 for (
int node = 0; node < num_nodes; node++) {
739 const int cp_node = node + 1;
742 const std::vector<RoutingDisjunctionIndex>& disjunction_indices =
744 if (disjunction_indices.empty() || model.
ActiveVar(node)->
Min() == 1) {
745 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
749 for (RoutingDisjunctionIndex disjunction_index : disjunction_indices) {
750 const int num_nodes =
753 const int64_t max_cardinality =
755 if (num_nodes == max_cardinality &&
756 (penalty < 0 || penalty == std::numeric_limits<int64_t>::max())) {
758 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
765 for (RoutingDisjunctionIndex disjunction_index(0);
767 disjunction_index++) {
768 const std::vector<int64_t>& disjunction_indices =
770 const int disjunction_size = disjunction_indices.size();
772 const int64_t max_cardinality =
776 if (disjunction_size == 1 &&
778 is_unperformed[disjunction_indices[0] + 1] == -1) {
779 const int cp_node = disjunction_indices[0] + 1;
780 const Arc arc = {cp_node, cp_node};
781 DCHECK(!arc_vars.contains(arc));
782 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
783 arc_vars.insert({arc, is_unperformed[cp_node]});
789 const int num_performed = AddVariable(cp_model, 0, max_cardinality);
790 std::vector<std::pair<int, double>> var_coeffs;
791 var_coeffs.push_back({num_performed, 1});
792 for (
const int node : disjunction_indices) {
793 const int cp_node = node + 1;
795 if (is_unperformed[cp_node] == -1) {
796 const Arc arc = {cp_node, cp_node};
797 DCHECK(!arc_vars.contains(arc));
798 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
799 arc_vars.insert({arc, is_unperformed[cp_node]});
801 var_coeffs.push_back({is_unperformed[cp_node], 1});
803 AddLinearConstraint(cp_model, disjunction_size, disjunction_size,
807 if (penalty < 0 || penalty == std::numeric_limits<int64_t>::max()) {
808 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
809 {{num_performed, 1}});
814 const int num_violated = AddVariable(cp_model, 0, max_cardinality);
818 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
819 {{num_performed, 1}, {num_violated, 1}});
822 std::vector<std::pair<int, double>> first_to_end_arcs;
823 for (
int tail = 0; tail < num_nodes; ++tail) {
824 const int cp_tail = tail + 1;
825 std::unique_ptr<IntVarIterator> iter(
828 const int cp_head = head + 1;
829 if (model.
IsStart(head))
continue;
831 if (tail == head)
continue;
839 bool feasible =
false;
840 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
842 std::numeric_limits<int64_t>::max()) {
847 if (!feasible)
continue;
849 const Arc arc = {cp_tail, cp_head};
850 DCHECK(!arc_vars.contains(arc));
851 const int arc_var = AddVariable(cp_model, 0, 1);
852 arc_vars.insert({arc, arc_var});
854 first_to_end_arcs.push_back({arc_var, 1});
864 model.
vehicles(), first_to_end_arcs);
868 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
869 const int routing_index = cp_node - 1;
871 if (model.
IsStart(routing_index) || model.
IsEnd(routing_index))
continue;
874 std::vector<std::pair<int, double>> var_coeffs;
875 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
876 vehicle_performs_node[vehicle][cp_node] =
878 ? AddVariable(cp_model, 0, 1)
879 : AddVariable(cp_model, 0, 0);
880 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
882 var_coeffs.push_back({is_unperformed[cp_node], 1});
883 AddLinearConstraint(cp_model, 1, 1, var_coeffs);
888 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_node(
889 num_vehicle_classes);
890 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
891 const int node = cp_node - 1;
892 for (
int vehicle_class = 0; vehicle_class < num_vehicle_classes;
896 vehicle_class_performs_node[vehicle_class][cp_node] =
899 ? AddVariable(cp_model, 1, 1)
900 : AddVariable(cp_model, 0, 0);
903 vehicle_class_performs_node[vehicle_class][cp_node] =
904 AddVariable(cp_model, 0, 1);
905 std::vector<std::pair<int, double>> var_coeffs;
906 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
909 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
913 {{vehicle_class_performs_node[vehicle_class][cp_node], 1}},
914 {vehicle_performs_node[vehicle][cp_node]});
920 cp_model, 1, 1, var_coeffs,
921 {vehicle_class_performs_node[vehicle_class][cp_node]});
926 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_arc(
927 num_vehicle_classes);
929 for (
const auto [arc, arc_var] : arc_vars) {
930 const auto [cp_tail, cp_head] = arc;
931 if (cp_tail == depot || cp_head == depot)
continue;
932 const int tail = cp_tail - 1;
933 const int head = cp_head - 1;
935 if (tail == head)
continue;
936 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
939 if (!vehicle_performs_node[vehicle].contains(cp_tail) ||
940 !vehicle_performs_node[vehicle].contains(cp_head)) {
945 if (cost == std::numeric_limits<int64_t>::max())
continue;
946 const int vehicle_class =
948 if (!vehicle_class_performs_arc[vehicle_class].contains(arc_var)) {
949 vehicle_class_performs_arc[vehicle_class][arc_var] =
950 AddVariable(cp_model, 0, 1);
957 vehicle_class_performs_arc[vehicle_class][arc_var]);
960 vehicle_class_performs_node[vehicle_class][cp_tail]);
962 vehicle_class_performs_node[vehicle_class][cp_head]);
967 vehicle_class_performs_arc[vehicle_class][arc_var]);
976 vehicle_performs_node[vehicle][cp_tail]);
978 vehicle_class_performs_arc[vehicle_class][arc_var]);
980 vehicle_performs_node[vehicle][cp_head]);
986 vehicle_performs_node[vehicle][cp_head]);
988 vehicle_class_performs_arc[vehicle_class][arc_var]);
990 vehicle_performs_node[vehicle][cp_tail]);
994 AddGeneralizedPickupDeliveryConstraints(
995 model, arc_vars, vehicle_performs_node, is_unperformed, cp_model);
997 AddGeneralizedDimensions(model, arc_vars, vehicle_performs_node,
998 vehicle_class_performs_arc, cp_model);
1003 for (
const auto [arc, arc_var] : arc_vars) {
1004 const int tail = arc.tail;
1005 const int head = arc.head;
1017 bool is_unary =
true;
1018 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
1025 primary_dimension = dimension;
1029 if (primary_dimension !=
nullptr) {
1030 for (
int cp_node = 0; cp_node < num_cp_nodes; ++cp_node) {
1031 int64_t min_transit = std::numeric_limits<int64_t>::max();
1032 if (cp_node != 0 && !model.
IsEnd(cp_node - 1)) {
1033 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
1036 min_transit = std::min(min_transit, transit(cp_node - 1));
1044 int64_t max_capacity = std::numeric_limits<int64_t>::min();
1045 for (
int vehicle = 0; vehicle < model.
vehicles(); vehicle++) {
1046 max_capacity = std::max(max_capacity,
1058 const ArcVarMap& arc_vars,
1065 const int depot = 0;
1066 for (
const auto [arc, arc_var] : arc_vars) {
1067 if (response.
solution(arc_var) == 0)
continue;
1068 const auto [tail, head] = arc;
1069 if (head == depot || tail == depot)
continue;
1072 ConvertObjectiveToSolution(response, objective, model,
solution);
1079 const ArcVarMap& arc_vars,
1084 const int num_nodes = model.
Nexts().size();
1085 for (
int tail = 0; tail < num_nodes; ++tail) {
1086 const int cp_tail = tail + 1;
1088 const int*
const arc_var =
gtl::FindOrNull(arc_vars, {cp_tail, cp_head});
1093 if (arc_var ==
nullptr)
continue;
1101 const ArcVarMap& arc_vars,
1106 const int depot = GetDepotFromModel(model);
1107 const int num_nodes = model.
Nexts().size();
1108 for (
int tail = 0; tail < num_nodes; ++tail) {
1109 const int tail_index = model.
IsStart(tail) ? depot : tail;
1111 const int head_index = model.
IsEnd(head) ? depot : head;
1112 if (tail_index == depot && head_index == depot)
continue;
1113 const int*
const var_index =
1119 if (var_index ==
nullptr)
continue;
1128 const CpModelProto& cp_model, absl::Duration remaining_time,
1129 std::atomic<bool>* interrupt_solve,
1136 absl::ToDoubleSeconds(remaining_time));
1139 std::min(absl::ToDoubleSeconds(remaining_time),
1146 if (observer !=
nullptr) {
1156bool IsFeasibleArcVarMap(
const ArcVarMap& arc_vars,
int max_node_index) {
1157 Bitset64<> present_in_arcs(max_node_index + 1);
1158 for (
const auto [arc, _] : arc_vars) {
1159 present_in_arcs.
Set(arc.head);
1160 present_in_arcs.
Set(arc.tail);
1162 for (
int i = 0;
i <= max_node_index;
i++) {
1163 if (!present_in_arcs[
i])
return false;