24 #include "absl/container/flat_hash_map.h"
25 #include "absl/time/time.h"
31 #include "ortools/constraint_solver/routing_parameters.pb.h"
33 #include "ortools/sat/cp_model.pb.h"
37 #include "ortools/sat/sat_parameters.pb.h"
38 #include "ortools/util/optional_boolean.pb.h"
50 return model.GetVehicleClassesCount() == 1;
54 int AddVariable(CpModelProto* cp_model, int64_t lb, int64_t ub) {
55 const int index = cp_model->variables_size();
56 IntegerVariableProto*
const var = cp_model->add_variables();
64 void AddLinearConstraint(
66 const std::vector<std::pair<int, double>>& variable_coeffs,
67 const std::vector<int>& enforcement_literals) {
69 ConstraintProto*
ct = cp_model->add_constraints();
70 for (
const int enforcement_literal : enforcement_literals) {
71 ct->add_enforcement_literal(enforcement_literal);
73 LinearConstraintProto* arg =
ct->mutable_linear();
76 for (
const auto [
var, coeff] : variable_coeffs) {
78 arg->add_coeffs(coeff);
84 void AddLinearConstraint(
86 const std::vector<std::pair<int, double>>& variable_coeffs) {
99 return a.tail ==
b.tail &&
a.head ==
b.head;
101 friend bool operator!=(
const Arc&
a,
const Arc&
b) {
return !(
a ==
b); }
102 friend bool operator<(
const Arc&
a,
const Arc&
b) {
103 return a.tail ==
b.tail ?
a.head <
b.head :
a.tail <
b.tail;
105 friend std::ostream&
operator<<(std::ostream& strm,
const Arc&
arc) {
106 return strm <<
"{" <<
arc.tail <<
", " <<
arc.head <<
"}";
108 template <
typename H>
110 return H::combine(std::move(h),
a.tail,
a.head);
114 using ArcVarMap = std::map<Arc, int>;
118 void AddDimensions(
const RoutingModel&
model,
const ArcVarMap& arc_vars,
119 CpModelProto* cp_model) {
120 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
123 dimension->transit_evaluator(0);
124 std::vector<int> cumuls(dimension->cumuls().size(), -1);
125 const int64_t min_start = dimension->cumuls()[
model.Start(0)]->Min();
126 const int64_t max_end =
std::min(dimension->cumuls()[
model.End(0)]->Max(),
127 dimension->vehicle_capacities()[0]);
128 for (
int i = 0; i < cumuls.size(); ++i) {
129 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
131 const int64_t cumul_min =
133 std::max(dimension->cumuls()[i]->Min(),
135 const int64_t cumul_max =
137 std::min(dimension->cumuls()[i]->Max(),
139 cumuls[i] = AddVariable(cp_model, cumul_min, cumul_max);
141 for (
const auto arc_var : arc_vars) {
142 const int tail = arc_var.first.tail;
143 const int head = arc_var.first.head;
149 {{cumuls[
head], 1}, {cumuls[
tail], -1}}, {arc_var.second});
154 std::vector<int> CreateRanks(
const RoutingModel&
model,
155 const ArcVarMap& arc_vars,
156 CpModelProto* cp_model) {
157 const int depot = GetDepotFromModel(
model);
158 const int size =
model.Size() +
model.vehicles();
159 const int rank_size =
model.Size() -
model.vehicles();
160 std::vector<int> ranks(size, -1);
161 for (
int i = 0; i < size; ++i) {
162 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
163 ranks[i] = AddVariable(cp_model, 0, rank_size);
165 ranks[depot] = AddVariable(cp_model, 0, 0);
166 for (
const auto arc_var : arc_vars) {
167 const int tail = arc_var.first.tail;
168 const int head = arc_var.first.head;
171 AddLinearConstraint(cp_model, 1, 1, {{ranks[
head], 1}, {ranks[
tail], -1}},
181 std::vector<int> CreateVehicleVars(
const RoutingModel&
model,
182 const ArcVarMap& arc_vars,
183 CpModelProto* cp_model) {
184 const int depot = GetDepotFromModel(
model);
185 const int size =
model.Size() +
model.vehicles();
186 std::vector<int> vehicles(size, -1);
187 for (
int i = 0; i < size; ++i) {
188 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
189 vehicles[i] = AddVariable(cp_model, 0, size - 1);
191 for (
const auto arc_var : arc_vars) {
192 const int tail = arc_var.first.tail;
193 const int head = arc_var.first.head;
197 AddLinearConstraint(cp_model,
head,
head, {{vehicles[
head], 1}},
202 AddLinearConstraint(cp_model, 0, 0,
203 {{vehicles[
head], 1}, {vehicles[
tail], -1}},
209 void AddPickupDeliveryConstraints(
const RoutingModel&
model,
210 const ArcVarMap& arc_vars,
211 CpModelProto* cp_model) {
212 if (
model.GetPickupAndDeliveryPairs().empty())
return;
213 const std::vector<int> ranks = CreateRanks(
model, arc_vars, cp_model);
214 const std::vector<int> vehicles =
215 CreateVehicleVars(
model, arc_vars, cp_model);
216 for (
const auto& pairs :
model.GetPickupAndDeliveryPairs()) {
217 const int64_t pickup = pairs.first[0];
218 const int64_t delivery = pairs.second[0];
221 {{ranks[delivery], 1}, {ranks[pickup], -1}});
223 AddLinearConstraint(cp_model, 0, 0,
224 {{vehicles[delivery], 1}, {vehicles[pickup], -1}});
234 ArcVarMap PopulateMultiRouteModelFromRoutingModel(
const RoutingModel&
model,
235 CpModelProto* cp_model) {
237 const int num_nodes =
model.Nexts().size();
238 const int depot = GetDepotFromModel(
model);
243 std::unique_ptr<IntVarIterator> iter(
244 model.NextVar(
tail)->MakeDomainIterator(
false));
245 for (
int head : InitAndGetValues(iter.get())) {
258 const int index = AddVariable(cp_model, 0, 1);
260 cp_model->mutable_objective()->add_vars(
index);
261 cp_model->mutable_objective()->add_coeffs(
cost);
267 std::vector<std::pair<int, double>> variable_coeffs;
268 for (
int node = 0; node < num_nodes; ++node) {
269 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
271 if (
var ==
nullptr)
continue;
272 variable_coeffs.push_back({*
var, 1});
280 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
282 AddDimensions(
model, arc_vars, cp_model);
287 RoutesConstraintProto* routes_ct =
288 cp_model->add_constraints()->mutable_routes();
289 for (
const auto arc_var : arc_vars) {
290 const int tail = arc_var.first.tail;
291 const int head = arc_var.first.head;
292 routes_ct->add_tails(
tail == 0 ? depot :
tail == depot ? 0 :
tail);
293 routes_ct->add_heads(
head == 0 ? depot :
head == depot ? 0 :
head);
294 routes_ct->add_literals(arc_var.second);
301 const RoutingDimension* primary_dimension =
nullptr;
302 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
304 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr) {
305 primary_dimension = dimension;
309 if (primary_dimension !=
nullptr) {
311 primary_dimension->GetUnaryTransitEvaluator(0);
312 for (
int node = 0; node < num_nodes; ++node) {
315 if (!
model.IsEnd(node) && (!
model.IsStart(node) || node == depot)) {
316 routes_ct->add_demands(transit(node));
319 DCHECK_EQ(routes_ct->demands_size(), num_nodes + 1 -
model.vehicles());
320 routes_ct->set_capacity(primary_dimension->vehicle_capacities()[0]);
328 ArcVarMap PopulateSingleRouteModelFromRoutingModel(
const RoutingModel&
model,
329 CpModelProto* cp_model) {
331 const int num_nodes =
model.Nexts().size();
332 CircuitConstraintProto* circuit =
333 cp_model->add_constraints()->mutable_circuit();
335 std::unique_ptr<IntVarIterator> iter(
336 model.NextVar(
tail)->MakeDomainIterator(
false));
337 for (
int head : InitAndGetValues(iter.get())) {
347 const int index = AddVariable(cp_model, 0, 1);
348 circuit->add_literals(
index);
349 circuit->add_tails(
tail);
350 circuit->add_heads(
head);
351 cp_model->mutable_objective()->add_vars(
index);
352 cp_model->mutable_objective()->add_coeffs(
cost);
356 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
357 AddDimensions(
model, arc_vars, cp_model);
364 ArcVarMap PopulateModelFromRoutingModel(
const RoutingModel&
model,
365 CpModelProto* cp_model) {
366 if (
model.vehicles() == 1) {
367 return PopulateSingleRouteModelFromRoutingModel(
model, cp_model);
369 return PopulateMultiRouteModelFromRoutingModel(
model, cp_model);
373 bool ConvertToSolution(
const CpSolverResponse&
response,
374 const RoutingModel&
model,
const ArcVarMap& arc_vars,
375 Assignment* solution) {
379 const int depot = GetDepotFromModel(
model);
381 for (
const auto& arc_var : arc_vars) {
382 if (
response.solution(arc_var.second) != 0) {
383 const int tail = arc_var.first.tail;
384 const int head = arc_var.first.head;
385 if (
head == depot)
continue;
389 solution->Add(
model.NextVar(
model.Start(vehicle)))->SetValue(
head);
395 for (
int v = 0; v <
model.vehicles(); ++v) {
396 int current =
model.Start(v);
397 while (solution->Contains(
model.NextVar(current))) {
398 current = solution->Value(
model.NextVar(current));
400 solution->Add(
model.NextVar(current))->SetValue(
model.End(v));
407 void AddGeneralizedDimensions(
408 const RoutingModel&
model,
const ArcVarMap& arc_vars,
409 const std::vector<absl::flat_hash_map<int, int>>& vehicle_performs_node,
410 const std::vector<absl::flat_hash_map<int, int>>&
411 vehicle_class_performs_arc,
412 CpModelProto* cp_model) {
413 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
414 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
416 std::vector<int> cumuls(num_cp_nodes, -1);
417 for (
int cp_node = 1; cp_node < num_cp_nodes; ++cp_node) {
418 const int node = cp_node - 1;
419 int64_t cumul_min = dimension->cumuls()[node]->Min();
420 int64_t cumul_max = dimension->cumuls()[node]->Max();
421 if (
model.IsStart(node) ||
model.IsEnd(node)) {
422 const int vehicle =
model.VehicleIndex(node);
424 std::min(cumul_max, dimension->vehicle_capacities()[vehicle]);
426 cumuls[cp_node] = AddVariable(cp_model, cumul_min, cumul_max);
430 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
431 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
432 if (!vehicle_performs_node[vehicle].contains(cp_node))
continue;
433 const int64_t vehicle_capacity =
434 dimension->vehicle_capacities()[vehicle];
436 vehicle_capacity, {{cumuls[cp_node], 1}},
437 {vehicle_performs_node[vehicle].at(cp_node)});
443 std::vector<int> slack(num_cp_nodes, -1);
444 const int64_t span_cost =
445 dimension->GetSpanCostCoefficientForVehicleClass(
vehicle_class);
446 for (
const auto [
arc, arc_var] : arc_vars) {
447 const auto [cp_tail, cp_head] =
arc;
448 if (cp_tail == cp_head || cp_tail == 0 || cp_head == 0)
continue;
449 if (!vehicle_class_performs_arc[
vehicle_class.value()].contains(
454 if (slack[cp_tail] == -1) {
455 const int64_t slack_max =
456 cp_tail - 1 < dimension->slacks().size()
457 ? dimension->slacks()[cp_tail - 1]->Max()
459 slack[cp_tail] = AddVariable(cp_model, 0, slack_max);
460 if (slack_max > 0 && span_cost > 0) {
461 cp_model->mutable_objective()->add_vars(slack[cp_tail]);
462 cp_model->mutable_objective()->add_coeffs(span_cost);
465 const int64_t transit = dimension->class_transit_evaluator(
470 cp_model, transit, transit,
471 {{cumuls[cp_head], 1}, {cumuls[cp_tail], -1}, {slack[cp_tail], -1}},
472 {vehicle_class_performs_arc[
vehicle_class.value()].at(arc_var)});
477 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
478 const int64_t span_limit =
479 dimension->vehicle_span_upper_bounds()[vehicle];
481 int cp_start =
model.Start(vehicle) + 1;
482 int cp_end =
model.End(vehicle) + 1;
485 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}});
489 if (dimension->HasSoftSpanUpperBounds()) {
490 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
492 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
493 const int cp_start =
model.Start(vehicle) + 1;
494 const int cp_end =
model.End(vehicle) + 1;
496 AddVariable(cp_model, 0,
498 dimension->vehicle_capacities()[vehicle]));
502 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}, {extra, -1}});
504 cp_model->mutable_objective()->add_vars(extra);
505 cp_model->mutable_objective()->add_coeffs(
cost);
511 std::vector<int> CreateGeneralizedRanks(
const RoutingModel&
model,
512 const ArcVarMap& arc_vars,
513 const std::vector<int>& is_unperformed,
514 CpModelProto* cp_model) {
516 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
519 std::vector<int> ranks(num_cp_nodes, -1);
520 ranks[depot] = AddVariable(cp_model, 0, 0);
521 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
522 if (
model.IsEnd(cp_node - 1))
continue;
523 ranks[cp_node] = AddVariable(cp_model, 0,
max_rank);
525 AddLinearConstraint(cp_model, 0, 0, {{ranks[cp_node], 1}},
526 {is_unperformed[cp_node]});
528 for (
const auto [
arc, arc_var] : arc_vars) {
529 const auto [cp_tail, cp_head] =
arc;
530 if (cp_head == 0 ||
model.IsEnd(cp_head - 1))
continue;
531 if (cp_tail == cp_head || cp_head == depot)
continue;
533 AddLinearConstraint(cp_model, 1, 1,
534 {{ranks[cp_head], 1}, {ranks[cp_tail], -1}}, {arc_var});
539 void AddGeneralizedPickupDeliveryConstraints(
540 const RoutingModel&
model,
const ArcVarMap& arc_vars,
541 const std::vector<absl::flat_hash_map<int, int>>& vehicle_performs_node,
542 const std::vector<int>& is_unperformed, CpModelProto* cp_model) {
543 if (
model.GetPickupAndDeliveryPairs().empty())
return;
544 const std::vector<int> ranks =
545 CreateGeneralizedRanks(
model, arc_vars, is_unperformed, cp_model);
546 for (
const auto& pairs :
model.GetPickupAndDeliveryPairs()) {
547 for (
const int delivery : pairs.second) {
548 const int cp_delivery = delivery + 1;
549 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
550 const Arc vehicle_start_delivery_arc = {
551 static_cast<int>(
model.Start(vehicle) + 1), cp_delivery};
554 AddLinearConstraint(cp_model, 0, 0,
555 {{arc_vars.at(vehicle_start_delivery_arc), 1}});
559 for (
const int pickup : pairs.first) {
560 const int cp_pickup = pickup + 1;
561 const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
564 AddLinearConstraint(cp_model, 0, 0,
565 {{arc_vars.at(delivery_pickup_arc), 1}});
568 DCHECK_GE(is_unperformed[cp_delivery], 0);
569 DCHECK_GE(is_unperformed[cp_pickup], 0);
572 const int delivery_performed = -is_unperformed[cp_delivery] - 1;
573 const int pickup_performed = -is_unperformed[cp_pickup] - 1;
575 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
581 {{vehicle_performs_node[vehicle].at(cp_delivery), 1},
582 {vehicle_performs_node[vehicle].at(cp_pickup), -1}},
583 {delivery_performed, pickup_performed});
588 std::vector<std::pair<int, double>> ranks_difference;
590 for (
const int pickup : pairs.first) {
591 const int cp_pickup = pickup + 1;
592 ranks_difference.push_back({ranks[cp_pickup], -1});
595 for (
const int delivery : pairs.second) {
596 const int cp_delivery = delivery + 1;
597 ranks_difference.push_back({ranks[cp_delivery], 1});
611 ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
612 const RoutingModel&
model, CpModelProto* cp_model) {
615 const int num_nodes =
model.Nexts().size();
616 const int num_cp_nodes = num_nodes +
model.vehicles() + 1;
619 std::vector<absl::flat_hash_map<int, int>> vehicle_performs_node(
622 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
623 const int cp_start =
model.Start(vehicle) + 1;
624 const Arc start_arc = {depot, cp_start};
625 const int start_arc_var = AddVariable(cp_model, 1, 1);
627 arc_vars.insert({start_arc, start_arc_var});
629 const int cp_end =
model.End(vehicle) + 1;
630 const Arc end_arc = {cp_end, depot};
631 const int end_arc_var = AddVariable(cp_model, 1, 1);
633 arc_vars.insert({end_arc, end_arc_var});
635 vehicle_performs_node[vehicle][cp_start] = start_arc_var;
636 vehicle_performs_node[vehicle][cp_end] = end_arc_var;
641 std::vector<int> is_unperformed(num_cp_nodes, -1);
643 for (
int node = 0; node < num_nodes; node++) {
644 const int cp_node = node + 1;
647 const std::vector<RoutingDisjunctionIndex>& disjunction_indices =
648 model.GetDisjunctionIndices(node);
649 if (disjunction_indices.empty() ||
model.ActiveVar(node)->Min() == 1) {
650 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
654 for (RoutingDisjunctionIndex disjunction_index : disjunction_indices) {
655 const int num_nodes =
656 model.GetDisjunctionNodeIndices(disjunction_index).size();
657 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
658 const int64_t max_cardinality =
659 model.GetDisjunctionMaxCardinality(disjunction_index);
660 if (num_nodes == max_cardinality &&
663 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
670 for (RoutingDisjunctionIndex disjunction_index(0);
671 disjunction_index <
model.GetNumberOfDisjunctions();
672 disjunction_index++) {
673 const std::vector<int64_t>& disjunction_indices =
674 model.GetDisjunctionNodeIndices(disjunction_index);
675 const int disjunction_size = disjunction_indices.size();
676 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
677 const int64_t max_cardinality =
678 model.GetDisjunctionMaxCardinality(disjunction_index);
681 if (disjunction_size == 1 &&
682 model.GetDisjunctionIndices(disjunction_indices[0]).size() == 1 &&
683 is_unperformed[disjunction_indices[0] + 1] == -1) {
684 const int cp_node = disjunction_indices[0] + 1;
685 const Arc
arc = {cp_node, cp_node};
687 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
688 arc_vars.insert({
arc, is_unperformed[cp_node]});
689 cp_model->mutable_objective()->add_vars(is_unperformed[cp_node]);
690 cp_model->mutable_objective()->add_coeffs(penalty);
694 const int num_performed = AddVariable(cp_model, 0, max_cardinality);
695 std::vector<std::pair<int, double>> var_coeffs;
696 var_coeffs.push_back({num_performed, 1});
697 for (
const int node : disjunction_indices) {
698 const int cp_node = node + 1;
700 if (is_unperformed[cp_node] == -1) {
701 const Arc
arc = {cp_node, cp_node};
703 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
704 arc_vars.insert({
arc, is_unperformed[cp_node]});
706 var_coeffs.push_back({is_unperformed[cp_node], 1});
708 AddLinearConstraint(cp_model, disjunction_size, disjunction_size,
713 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
714 {{num_performed, 1}});
719 const int num_violated = AddVariable(cp_model, 0, max_cardinality);
720 cp_model->mutable_objective()->add_vars(num_violated);
721 cp_model->mutable_objective()->add_coeffs(penalty);
723 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
724 {{num_performed, 1}, {num_violated, 1}});
728 const int cp_tail =
tail + 1;
729 std::unique_ptr<IntVarIterator> iter(
730 model.NextVar(
tail)->MakeDomainIterator(
false));
731 for (
int head : InitAndGetValues(iter.get())) {
732 const int cp_head =
head + 1;
743 bool feasible =
false;
744 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
751 if (!feasible)
continue;
753 const Arc
arc = {cp_tail, cp_head};
755 const int arc_var = AddVariable(cp_model, 0, 1);
756 arc_vars.insert({
arc, arc_var});
761 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
763 if (
model.IsStart(cp_node - 1) ||
model.IsEnd(cp_node - 1))
continue;
766 std::vector<std::pair<int, double>> var_coeffs;
767 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
768 vehicle_performs_node[vehicle][cp_node] = AddVariable(cp_model, 0, 1);
769 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
771 var_coeffs.push_back({is_unperformed[cp_node], 1});
772 AddLinearConstraint(cp_model, 1, 1, var_coeffs);
774 const int num_vehicle_classes =
model.GetVehicleClassesCount();
777 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_node(
778 num_vehicle_classes);
779 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
780 const int node = cp_node - 1;
783 if (
model.IsStart(node) ||
model.IsEnd(node)) {
784 const int vehicle =
model.VehicleIndex(node);
787 model.GetVehicleClassIndexOfVehicle(vehicle).value()
788 ? AddVariable(cp_model, 1, 1)
789 : AddVariable(cp_model, 0, 0);
793 AddVariable(cp_model, 0, 1);
794 std::vector<std::pair<int, double>> var_coeffs;
795 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
796 if (
model.GetVehicleClassIndexOfVehicle(vehicle).value() ==
798 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
803 {vehicle_performs_node[vehicle][cp_node]});
809 cp_model, 1, 1, var_coeffs,
815 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_arc(
816 num_vehicle_classes);
818 for (
const auto [
arc, arc_var] : arc_vars) {
819 const auto [cp_tail, cp_head] =
arc;
820 if (cp_tail == depot || cp_head == depot)
continue;
821 const int tail = cp_tail - 1;
822 const int head = cp_head - 1;
825 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
828 if (!vehicle_performs_node[vehicle].contains(cp_tail) ||
829 !vehicle_performs_node[vehicle].contains(cp_head)) {
836 model.GetVehicleClassIndexOfVehicle(vehicle).value();
837 if (!vehicle_class_performs_arc[
vehicle_class].contains(arc_var)) {
839 AddVariable(cp_model, 0, 1);
844 ConstraintProto*
ct = cp_model->add_constraints();
845 ct->add_enforcement_literal(
847 BoolArgumentProto* bool_and =
ct->mutable_bool_and();
848 bool_and->add_literals(
850 bool_and->add_literals(
852 bool_and->add_literals(arc_var);
855 cp_model->mutable_objective()->add_vars(
857 cp_model->mutable_objective()->add_coeffs(
cost);
862 ConstraintProto* ct_arc_tail = cp_model->add_constraints();
863 ct_arc_tail->add_enforcement_literal(arc_var);
864 ct_arc_tail->add_enforcement_literal(
865 vehicle_performs_node[vehicle][cp_tail]);
866 ct_arc_tail->mutable_bool_and()->add_literals(
868 ct_arc_tail->mutable_bool_and()->add_literals(
869 vehicle_performs_node[vehicle][cp_head]);
872 ConstraintProto* ct_arc_head = cp_model->add_constraints();
873 ct_arc_head->add_enforcement_literal(arc_var);
874 ct_arc_head->add_enforcement_literal(
875 vehicle_performs_node[vehicle][cp_head]);
876 ct_arc_head->mutable_bool_and()->add_literals(
878 ct_arc_head->mutable_bool_and()->add_literals(
879 vehicle_performs_node[vehicle][cp_tail]);
883 AddGeneralizedPickupDeliveryConstraints(
884 model, arc_vars, vehicle_performs_node, is_unperformed, cp_model);
886 AddGeneralizedDimensions(
model, arc_vars, vehicle_performs_node,
887 vehicle_class_performs_arc, cp_model);
890 RoutesConstraintProto* routes_ct =
891 cp_model->add_constraints()->mutable_routes();
892 for (
const auto [
arc, arc_var] : arc_vars) {
895 routes_ct->add_tails(
tail);
896 routes_ct->add_heads(
head);
897 routes_ct->add_literals(arc_var);
904 const RoutingDimension* primary_dimension =
nullptr;
905 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
906 bool is_unary =
true;
907 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
908 if (dimension->GetUnaryTransitEvaluator(vehicle) ==
nullptr) {
914 primary_dimension = dimension;
918 if (primary_dimension !=
nullptr) {
919 for (
int cp_node = 0; cp_node < num_cp_nodes; ++cp_node) {
921 if (cp_node != 0 && !
model.IsEnd(cp_node - 1)) {
922 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
924 primary_dimension->GetUnaryTransitEvaluator(vehicle);
925 min_transit =
std::min(min_transit, transit(cp_node - 1));
930 routes_ct->add_demands(min_transit);
932 DCHECK_EQ(routes_ct->demands_size(), num_cp_nodes);
934 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
935 max_capacity =
std::max(max_capacity,
936 primary_dimension->vehicle_capacities()[vehicle]);
938 routes_ct->set_capacity(max_capacity);
944 bool ConvertGeneralizedResponseToSolution(
const CpSolverResponse&
response,
945 const RoutingModel&
model,
946 const ArcVarMap& arc_vars,
947 Assignment* solution) {
953 for (
const auto [
arc, arc_var] : arc_vars) {
954 if (
response.solution(arc_var) == 0)
continue;
956 if (
head == depot ||
tail == depot)
continue;
963 void AddSolutionAsHintToGeneralizedModel(
const Assignment* solution,
964 const RoutingModel&
model,
965 const ArcVarMap& arc_vars,
966 CpModelProto* cp_model) {
967 if (solution ==
nullptr)
return;
968 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
970 const int num_nodes =
model.Nexts().size();
972 const int cp_tail =
tail + 1;
973 const int cp_head = solution->Value(
model.NextVar(
tail)) + 1;
974 const int*
const arc_var =
gtl::FindOrNull(arc_vars, {cp_tail, cp_head});
979 if (arc_var ==
nullptr)
continue;
980 hint->add_vars(*arc_var);
985 void AddSolutionAsHintToModel(
const Assignment* solution,
986 const RoutingModel&
model,
987 const ArcVarMap& arc_vars,
988 CpModelProto* cp_model) {
989 if (solution ==
nullptr)
return;
990 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
992 const int depot = GetDepotFromModel(
model);
993 const int num_nodes =
model.Nexts().size();
999 const int*
const var_index =
1005 if (var_index ==
nullptr)
continue;
1006 hint->add_vars(*var_index);
1007 hint->add_values(1);
1013 CpSolverResponse SolveRoutingModel(
1014 const CpModelProto& cp_model, absl::Duration remaining_time,
1015 const RoutingSearchParameters& search_parameters,
1016 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
1018 SatParameters sat_parameters = search_parameters.sat_parameters();
1019 if (!sat_parameters.has_max_time_in_seconds()) {
1020 sat_parameters.set_max_time_in_seconds(
1021 absl::ToDoubleSeconds(remaining_time));
1023 sat_parameters.set_max_time_in_seconds(
1024 std::min(absl::ToDoubleSeconds(remaining_time),
1025 sat_parameters.max_time_in_seconds()));
1029 if (observer !=
nullptr) {
1039 bool IsFeasibleArcVarMap(
const ArcVarMap& arc_vars,
int max_node_index) {
1040 Bitset64<> present_in_arcs(max_node_index + 1);
1041 for (
const auto [
arc, _] : arc_vars) {
1042 present_in_arcs.Set(
arc.head);
1043 present_in_arcs.Set(
arc.tail);
1045 for (
int i = 0; i <= max_node_index; i++) {
1046 if (!present_in_arcs[i])
return false;
1057 const RoutingSearchParameters& search_parameters,
1060 sat::CpModelProto cp_model;
1061 cp_model.mutable_objective()->set_scaling_factor(
1062 search_parameters.log_cost_scaling_factor());
1063 cp_model.mutable_objective()->set_offset(search_parameters.log_cost_offset());
1064 if (search_parameters.use_generalized_cp_sat() == BOOL_TRUE) {
1065 const sat::ArcVarMap arc_vars =
1066 sat::PopulateGeneralizedRouteModelFromRoutingModel(
model, &cp_model);
1067 const int max_node_index =
model.Nexts().size() +
model.vehicles();
1068 if (!sat::IsFeasibleArcVarMap(arc_vars, max_node_index))
return false;
1069 sat::AddSolutionAsHintToGeneralizedModel(initial_solution,
model, arc_vars,
1071 return sat::ConvertGeneralizedResponseToSolution(
1072 sat::SolveRoutingModel(cp_model,
model.RemainingTime(),
1073 search_parameters,
nullptr),
1074 model, arc_vars, solution);
1076 if (!sat::RoutingModelCanBeSolvedBySat(
model))
return false;
1077 const sat::ArcVarMap arc_vars =
1078 sat::PopulateModelFromRoutingModel(
model, &cp_model);
1079 sat::AddSolutionAsHintToModel(initial_solution,
model, arc_vars, &cp_model);
1080 return sat::ConvertToSolution(
1081 sat::SolveRoutingModel(cp_model,
model.RemainingTime(), search_parameters,
1083 model, arc_vars, solution);
An Assignment is a variable -> domains mapping, used to report solutions to the user.
RoutingTransitCallback1 TransitCallback1
RoutingTransitCallback2 TransitCallback2
SharedResponseManager * response
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
bool operator!=(const IndicatorConstraint &lhs, const IndicatorConstraint &rhs)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
H AbslHashValue(H h, const IntVar &i)
Collection of objects used to extend the Constraint Solver library.
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
LinearRange operator==(const LinearExpr &lhs, const LinearExpr &rhs)