23#include "absl/container/flat_hash_map.h"
24#include "absl/time/time.h"
30#include "ortools/constraint_solver/routing_parameters.pb.h"
32#include "ortools/sat/cp_model.pb.h"
36#include "ortools/sat/sat_parameters.pb.h"
37#include "ortools/util/optional_boolean.pb.h"
49 return model.GetVehicleClassesCount() == 1;
53int AddVariable(CpModelProto* cp_model, int64_t lb, int64_t ub) {
54 const int index = cp_model->variables_size();
55 IntegerVariableProto*
const var = cp_model->add_variables();
63void AddLinearConstraint(
65 const std::vector<std::pair<int, double>>& variable_coeffs,
66 const std::vector<int>& enforcement_literals) {
68 ConstraintProto*
ct = cp_model->add_constraints();
69 for (
const int enforcement_literal : enforcement_literals) {
70 ct->add_enforcement_literal(enforcement_literal);
72 LinearConstraintProto* arg =
ct->mutable_linear();
75 for (
const auto [
var,
coeff] : variable_coeffs) {
77 arg->add_coeffs(
coeff);
83void AddLinearConstraint(
85 const std::vector<std::pair<int, double>>& variable_coeffs) {
98 return a.tail ==
b.tail &&
a.head ==
b.head;
101 friend bool operator<(
const Arc&
a,
const Arc&
b) {
102 return a.tail ==
b.tail ?
a.head <
b.head :
a.tail <
b.tail;
105 return strm <<
"{" <<
arc.tail <<
", " <<
arc.head <<
"}";
107 template <
typename H>
109 return H::combine(std::move(h),
a.tail,
a.head);
113using ArcVarMap = std::map<Arc, int>;
117void AddDimensions(
const RoutingModel&
model,
const ArcVarMap& arc_vars,
118 CpModelProto* cp_model) {
119 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
122 dimension->transit_evaluator(0);
123 std::vector<int> cumuls(dimension->cumuls().size(), -1);
124 const int64_t min_start = dimension->cumuls()[
model.Start(0)]->Min();
125 const int64_t max_end =
std::min(dimension->cumuls()[
model.End(0)]->Max(),
126 dimension->vehicle_capacities()[0]);
127 for (
int i = 0; i < cumuls.size(); ++i) {
128 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
130 const int64_t cumul_min =
132 std::max(dimension->cumuls()[i]->Min(),
134 const int64_t cumul_max =
136 std::min(dimension->cumuls()[i]->Max(),
138 cumuls[i] = AddVariable(cp_model, cumul_min, cumul_max);
140 for (
const auto arc_var : arc_vars) {
141 const int tail = arc_var.first.tail;
142 const int head = arc_var.first.head;
148 {{cumuls[
head], 1}, {cumuls[
tail], -1}}, {arc_var.second});
153std::vector<int> CreateRanks(
const RoutingModel&
model,
154 const ArcVarMap& arc_vars,
155 CpModelProto* cp_model) {
156 const int depot = GetDepotFromModel(
model);
157 const int size =
model.Size() +
model.vehicles();
158 const int rank_size =
model.Size() -
model.vehicles();
159 std::vector<int> ranks(size, -1);
160 for (
int i = 0; i < size; ++i) {
161 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
162 ranks[i] = AddVariable(cp_model, 0, rank_size);
164 ranks[depot] = AddVariable(cp_model, 0, 0);
165 for (
const auto arc_var : arc_vars) {
166 const int tail = arc_var.first.tail;
167 const int head = arc_var.first.head;
170 AddLinearConstraint(cp_model, 1, 1, {{ranks[
head], 1}, {ranks[
tail], -1}},
180std::vector<int> CreateVehicleVars(
const RoutingModel&
model,
181 const ArcVarMap& arc_vars,
182 CpModelProto* cp_model) {
183 const int depot = GetDepotFromModel(
model);
184 const int size =
model.Size() +
model.vehicles();
185 std::vector<int> vehicles(size, -1);
186 for (
int i = 0; i < size; ++i) {
187 if (
model.IsStart(i) ||
model.IsEnd(i))
continue;
188 vehicles[i] = AddVariable(cp_model, 0, size - 1);
190 for (
const auto arc_var : arc_vars) {
191 const int tail = arc_var.first.tail;
192 const int head = arc_var.first.head;
196 AddLinearConstraint(cp_model,
head,
head, {{vehicles[
head], 1}},
201 AddLinearConstraint(cp_model, 0, 0,
202 {{vehicles[
head], 1}, {vehicles[
tail], -1}},
208void AddPickupDeliveryConstraints(
const RoutingModel&
model,
209 const ArcVarMap& arc_vars,
210 CpModelProto* cp_model) {
211 if (
model.GetPickupAndDeliveryPairs().empty())
return;
212 const std::vector<int> ranks = CreateRanks(
model, arc_vars, cp_model);
213 const std::vector<int> vehicles =
214 CreateVehicleVars(
model, arc_vars, cp_model);
215 for (
const auto& pairs :
model.GetPickupAndDeliveryPairs()) {
216 const int64_t pickup = pairs.first[0];
217 const int64_t delivery = pairs.second[0];
220 {{ranks[delivery], 1}, {ranks[pickup], -1}});
222 AddLinearConstraint(cp_model, 0, 0,
223 {{vehicles[delivery], 1}, {vehicles[pickup], -1}});
233ArcVarMap PopulateMultiRouteModelFromRoutingModel(
const RoutingModel&
model,
234 CpModelProto* cp_model) {
236 const int num_nodes =
model.Nexts().size();
237 const int depot = GetDepotFromModel(
model);
242 std::unique_ptr<IntVarIterator> iter(
243 model.NextVar(
tail)->MakeDomainIterator(
false));
244 for (
int head : InitAndGetValues(iter.get())) {
257 const int index = AddVariable(cp_model, 0, 1);
259 cp_model->mutable_objective()->add_vars(
index);
260 cp_model->mutable_objective()->add_coeffs(
cost);
266 std::vector<std::pair<int, double>> variable_coeffs;
267 for (
int node = 0; node < num_nodes; ++node) {
268 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
270 if (
var ==
nullptr)
continue;
271 variable_coeffs.push_back({*
var, 1});
279 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
281 AddDimensions(
model, arc_vars, cp_model);
286 RoutesConstraintProto* routes_ct =
287 cp_model->add_constraints()->mutable_routes();
288 for (
const auto arc_var : arc_vars) {
289 const int tail = arc_var.first.tail;
290 const int head = arc_var.first.head;
291 routes_ct->add_tails(
tail == 0 ? depot :
tail == depot ? 0 :
tail);
292 routes_ct->add_heads(
head == 0 ? depot :
head == depot ? 0 :
head);
293 routes_ct->add_literals(arc_var.second);
300 const RoutingDimension* master_dimension =
nullptr;
301 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
303 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr) {
304 master_dimension = dimension;
308 if (master_dimension !=
nullptr) {
310 master_dimension->GetUnaryTransitEvaluator(0);
311 for (
int node = 0; node < num_nodes; ++node) {
314 if (!
model.IsEnd(node) && (!
model.IsStart(node) || node == depot)) {
315 routes_ct->add_demands(transit(node));
318 DCHECK_EQ(routes_ct->demands_size(), num_nodes + 1 -
model.vehicles());
319 routes_ct->set_capacity(master_dimension->vehicle_capacities()[0]);
327ArcVarMap PopulateSingleRouteModelFromRoutingModel(
const RoutingModel&
model,
328 CpModelProto* cp_model) {
330 const int num_nodes =
model.Nexts().size();
331 CircuitConstraintProto* circuit =
332 cp_model->add_constraints()->mutable_circuit();
334 std::unique_ptr<IntVarIterator> iter(
335 model.NextVar(
tail)->MakeDomainIterator(
false));
336 for (
int head : InitAndGetValues(iter.get())) {
346 const int index = AddVariable(cp_model, 0, 1);
347 circuit->add_literals(
index);
348 circuit->add_tails(
tail);
349 circuit->add_heads(
head);
350 cp_model->mutable_objective()->add_vars(
index);
351 cp_model->mutable_objective()->add_coeffs(
cost);
355 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
356 AddDimensions(
model, arc_vars, cp_model);
363ArcVarMap PopulateModelFromRoutingModel(
const RoutingModel&
model,
364 CpModelProto* cp_model) {
365 if (
model.vehicles() == 1) {
366 return PopulateSingleRouteModelFromRoutingModel(
model, cp_model);
368 return PopulateMultiRouteModelFromRoutingModel(
model, cp_model);
372bool ConvertToSolution(
const CpSolverResponse&
response,
373 const RoutingModel&
model,
const ArcVarMap& arc_vars,
374 Assignment* solution) {
375 if (
response.status() != CpSolverStatus::OPTIMAL &&
376 response.status() != CpSolverStatus::FEASIBLE)
378 const int depot = GetDepotFromModel(
model);
380 for (
const auto& arc_var : arc_vars) {
381 if (
response.solution(arc_var.second) != 0) {
382 const int tail = arc_var.first.tail;
383 const int head = arc_var.first.head;
384 if (
head == depot)
continue;
388 solution->Add(
model.NextVar(
model.Start(vehicle)))->SetValue(
head);
394 for (
int v = 0; v <
model.vehicles(); ++v) {
395 int current =
model.Start(v);
396 while (solution->Contains(
model.NextVar(current))) {
397 current = solution->Value(
model.NextVar(current));
399 solution->Add(
model.NextVar(current))->SetValue(
model.End(v));
406void AddGeneralizedDimensions(
407 const RoutingModel&
model,
const ArcVarMap& arc_vars,
408 const std::vector<absl::flat_hash_map<int, int>>& vehicle_performs_node,
409 const std::vector<absl::flat_hash_map<int, int>>&
410 vehicle_class_performs_arc,
411 CpModelProto* cp_model) {
412 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
413 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
415 std::vector<int> cumuls(num_cp_nodes, -1);
416 for (
int cp_node = 1; cp_node < num_cp_nodes; ++cp_node) {
417 const int node = cp_node - 1;
418 int64_t cumul_min = dimension->cumuls()[node]->Min();
419 int64_t cumul_max = dimension->cumuls()[node]->Max();
420 if (
model.IsStart(node) ||
model.IsEnd(node)) {
421 const int vehicle =
model.VehicleIndex(node);
423 std::min(cumul_max, dimension->vehicle_capacities()[vehicle]);
425 cumuls[cp_node] = AddVariable(cp_model, cumul_min, cumul_max);
429 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
430 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
431 if (!vehicle_performs_node[vehicle].contains(cp_node))
continue;
432 const int64_t vehicle_capacity =
433 dimension->vehicle_capacities()[vehicle];
435 vehicle_capacity, {{cumuls[cp_node], 1}},
436 {vehicle_performs_node[vehicle].at(cp_node)});
442 std::vector<int> slack(num_cp_nodes, -1);
443 const int64_t span_cost =
444 dimension->GetSpanCostCoefficientForVehicleClass(
vehicle_class);
445 for (
const auto [
arc, arc_var] : arc_vars) {
446 const auto [cp_tail, cp_head] =
arc;
447 if (cp_tail == cp_head || cp_tail == 0 || cp_head == 0)
continue;
448 if (!vehicle_class_performs_arc[
vehicle_class.value()].contains(
453 if (slack[cp_tail] == -1) {
454 const int64_t slack_max =
455 cp_tail - 1 < dimension->slacks().size()
456 ? dimension->slacks()[cp_tail - 1]->Max()
458 slack[cp_tail] = AddVariable(cp_model, 0, slack_max);
459 if (slack_max > 0 && span_cost > 0) {
460 cp_model->mutable_objective()->add_vars(slack[cp_tail]);
461 cp_model->mutable_objective()->add_coeffs(span_cost);
464 const int64_t transit = dimension->class_transit_evaluator(
469 cp_model, transit, transit,
470 {{cumuls[cp_head], 1}, {cumuls[cp_tail], -1}, {slack[cp_tail], -1}},
471 {vehicle_class_performs_arc[
vehicle_class.value()].at(arc_var)});
476 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
477 const int64_t span_limit =
478 dimension->vehicle_span_upper_bounds()[vehicle];
480 int cp_start =
model.Start(vehicle) + 1;
481 int cp_end =
model.End(vehicle) + 1;
484 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}});
488 if (dimension->HasSoftSpanUpperBounds()) {
489 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
491 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
492 const int cp_start =
model.Start(vehicle) + 1;
493 const int cp_end =
model.End(vehicle) + 1;
495 AddVariable(cp_model, 0,
497 dimension->vehicle_capacities()[vehicle]));
501 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}, {extra, -1}});
503 cp_model->mutable_objective()->add_vars(extra);
504 cp_model->mutable_objective()->add_coeffs(
cost);
510std::vector<int> CreateGeneralizedRanks(
const RoutingModel&
model,
511 const ArcVarMap& arc_vars,
512 const std::vector<int>& is_unperformed,
513 CpModelProto* cp_model) {
515 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
518 std::vector<int> ranks(num_cp_nodes, -1);
519 ranks[depot] = AddVariable(cp_model, 0, 0);
520 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
521 if (
model.IsEnd(cp_node - 1))
continue;
522 ranks[cp_node] = AddVariable(cp_model, 0,
max_rank);
524 AddLinearConstraint(cp_model, 0, 0, {{ranks[cp_node], 1}},
525 {is_unperformed[cp_node]});
527 for (
const auto [
arc, arc_var] : arc_vars) {
528 const auto [cp_tail, cp_head] =
arc;
529 if (
model.IsEnd(cp_head - 1))
continue;
530 if (cp_tail == cp_head || cp_head == depot)
continue;
532 AddLinearConstraint(cp_model, 1, 1,
533 {{ranks[cp_head], 1}, {ranks[cp_tail], -1}}, {arc_var});
538void AddGeneralizedPickupDeliveryConstraints(
539 const RoutingModel&
model,
const ArcVarMap& arc_vars,
540 const std::vector<absl::flat_hash_map<int, int>>& vehicle_performs_node,
541 const std::vector<int>& is_unperformed, CpModelProto* cp_model) {
542 if (
model.GetPickupAndDeliveryPairs().empty())
return;
543 const std::vector<int> ranks =
544 CreateGeneralizedRanks(
model, arc_vars, is_unperformed, cp_model);
545 for (
const auto& pairs :
model.GetPickupAndDeliveryPairs()) {
546 for (
const int delivery : pairs.second) {
547 const int cp_delivery = delivery + 1;
548 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
549 const Arc vehicle_start_delivery_arc = {
550 static_cast<int>(
model.Start(vehicle) + 1), cp_delivery};
553 AddLinearConstraint(cp_model, 0, 0,
554 {{arc_vars.at(vehicle_start_delivery_arc), 1}});
558 for (
const int pickup : pairs.first) {
559 const int cp_pickup = pickup + 1;
560 const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
563 AddLinearConstraint(cp_model, 0, 0,
564 {{arc_vars.at(delivery_pickup_arc), 1}});
567 DCHECK_GE(is_unperformed[cp_delivery], 0);
571 const int delivery_performed = -is_unperformed[cp_delivery] - 1;
572 const int pickup_performed = -is_unperformed[cp_pickup] - 1;
574 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
580 {{vehicle_performs_node[vehicle].at(cp_delivery), 1},
581 {vehicle_performs_node[vehicle].at(cp_pickup), -1}},
582 {delivery_performed, pickup_performed});
587 std::vector<std::pair<int, double>> ranks_difference;
589 for (
const int pickup : pairs.first) {
590 const int cp_pickup = pickup + 1;
591 ranks_difference.push_back({ranks[cp_pickup], -1});
594 for (
const int delivery : pairs.second) {
595 const int cp_delivery = delivery + 1;
596 ranks_difference.push_back({ranks[cp_delivery], 1});
610ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
611 const RoutingModel&
model, CpModelProto* cp_model) {
614 const int num_nodes =
model.Nexts().size();
615 const int num_cp_nodes = num_nodes +
model.vehicles() + 1;
618 std::vector<absl::flat_hash_map<int, int>> vehicle_performs_node(
621 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
622 const int cp_start =
model.Start(vehicle) + 1;
623 const Arc start_arc = {depot, cp_start};
624 const int start_arc_var = AddVariable(cp_model, 1, 1);
626 arc_vars.insert({start_arc, start_arc_var});
628 const int cp_end =
model.End(vehicle) + 1;
629 const Arc end_arc = {cp_end, depot};
630 const int end_arc_var = AddVariable(cp_model, 1, 1);
632 arc_vars.insert({end_arc, end_arc_var});
634 vehicle_performs_node[vehicle][cp_start] = start_arc_var;
635 vehicle_performs_node[vehicle][cp_end] = end_arc_var;
640 std::vector<int> is_unperformed(num_cp_nodes, -1);
642 for (
int node = 0; node < num_nodes; node++) {
643 const int cp_node = node + 1;
646 const std::vector<RoutingDisjunctionIndex>& disjunction_indices =
647 model.GetDisjunctionIndices(node);
648 if (disjunction_indices.empty() ||
model.ActiveVar(node)->Min() == 1) {
649 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
653 for (RoutingDisjunctionIndex disjunction_index : disjunction_indices) {
654 const int num_nodes =
655 model.GetDisjunctionNodeIndices(disjunction_index).size();
656 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
657 const int64_t max_cardinality =
658 model.GetDisjunctionMaxCardinality(disjunction_index);
659 if (num_nodes == max_cardinality &&
662 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
669 for (RoutingDisjunctionIndex disjunction_index(0);
670 disjunction_index <
model.GetNumberOfDisjunctions();
671 disjunction_index++) {
672 const std::vector<int64_t>& disjunction_indices =
673 model.GetDisjunctionNodeIndices(disjunction_index);
674 const int disjunction_size = disjunction_indices.size();
675 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
676 const int64_t max_cardinality =
677 model.GetDisjunctionMaxCardinality(disjunction_index);
680 if (disjunction_size == 1 &&
681 model.GetDisjunctionIndices(disjunction_indices[0]).size() == 1 &&
682 is_unperformed[disjunction_indices[0] + 1] == -1) {
683 const int cp_node = disjunction_indices[0] + 1;
684 const Arc arc = {cp_node, cp_node};
686 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
687 arc_vars.insert({
arc, is_unperformed[cp_node]});
688 cp_model->mutable_objective()->add_vars(is_unperformed[cp_node]);
689 cp_model->mutable_objective()->add_coeffs(penalty);
693 const int num_performed = AddVariable(cp_model, 0, max_cardinality);
694 std::vector<std::pair<int, double>> var_coeffs;
695 var_coeffs.push_back({num_performed, 1});
696 for (
const int node : disjunction_indices) {
697 const int cp_node = node + 1;
699 if (is_unperformed[cp_node] == -1) {
700 const Arc arc = {cp_node, cp_node};
702 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
703 arc_vars.insert({
arc, is_unperformed[cp_node]});
705 var_coeffs.push_back({is_unperformed[cp_node], 1});
707 AddLinearConstraint(cp_model, disjunction_size, disjunction_size,
712 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
713 {{num_performed, 1}});
718 const int num_violated = AddVariable(cp_model, 0, max_cardinality);
719 cp_model->mutable_objective()->add_vars(num_violated);
720 cp_model->mutable_objective()->add_coeffs(penalty);
722 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
723 {{num_performed, 1}, {num_violated, 1}});
727 const int cp_tail =
tail + 1;
728 std::unique_ptr<IntVarIterator> iter(
729 model.NextVar(
tail)->MakeDomainIterator(
false));
730 for (
int head : InitAndGetValues(iter.get())) {
731 const int cp_head =
head + 1;
742 bool feasible =
false;
743 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
750 if (!feasible)
continue;
752 const Arc arc = {cp_tail, cp_head};
754 const int arc_var = AddVariable(cp_model, 0, 1);
755 arc_vars.insert({
arc, arc_var});
760 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
762 if (
model.IsStart(cp_node - 1) ||
model.IsEnd(cp_node - 1))
continue;
765 std::vector<std::pair<int, double>> var_coeffs;
766 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
767 vehicle_performs_node[vehicle][cp_node] = AddVariable(cp_model, 0, 1);
768 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
770 var_coeffs.push_back({is_unperformed[cp_node], 1});
771 AddLinearConstraint(cp_model, 1, 1, var_coeffs);
773 const int num_vehicle_classes =
model.GetVehicleClassesCount();
776 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_node(
777 num_vehicle_classes);
778 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
779 const int node = cp_node - 1;
782 if (
model.IsStart(node) ||
model.IsEnd(node)) {
783 const int vehicle =
model.VehicleIndex(node);
786 model.GetVehicleClassIndexOfVehicle(vehicle).value()
787 ? AddVariable(cp_model, 1, 1)
788 : AddVariable(cp_model, 0, 0);
792 AddVariable(cp_model, 0, 1);
793 std::vector<std::pair<int, double>> var_coeffs;
794 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
795 if (
model.GetVehicleClassIndexOfVehicle(vehicle).value() ==
797 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
802 {vehicle_performs_node[vehicle][cp_node]});
808 cp_model, 1, 1, var_coeffs,
814 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_arc(
815 num_vehicle_classes);
817 for (
const auto [
arc, arc_var] : arc_vars) {
818 const auto [cp_tail, cp_head] =
arc;
819 if (cp_tail == depot || cp_head == depot)
continue;
820 const int tail = cp_tail - 1;
821 const int head = cp_head - 1;
824 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
827 if (!vehicle_performs_node[vehicle].contains(cp_tail) ||
828 !vehicle_performs_node[vehicle].contains(cp_head)) {
835 model.GetVehicleClassIndexOfVehicle(vehicle).value();
836 if (!vehicle_class_performs_arc[
vehicle_class].contains(arc_var)) {
838 AddVariable(cp_model, 0, 1);
843 ConstraintProto*
ct = cp_model->add_constraints();
844 ct->add_enforcement_literal(
846 BoolArgumentProto* bool_and =
ct->mutable_bool_and();
847 bool_and->add_literals(
849 bool_and->add_literals(
851 bool_and->add_literals(arc_var);
854 cp_model->mutable_objective()->add_vars(
856 cp_model->mutable_objective()->add_coeffs(
cost);
861 ConstraintProto* ct_arc_tail = cp_model->add_constraints();
862 ct_arc_tail->add_enforcement_literal(arc_var);
863 ct_arc_tail->add_enforcement_literal(
864 vehicle_performs_node[vehicle][cp_tail]);
865 ct_arc_tail->mutable_bool_and()->add_literals(
867 ct_arc_tail->mutable_bool_and()->add_literals(
868 vehicle_performs_node[vehicle][cp_head]);
871 ConstraintProto* ct_arc_head = cp_model->add_constraints();
872 ct_arc_head->add_enforcement_literal(arc_var);
873 ct_arc_head->add_enforcement_literal(
874 vehicle_performs_node[vehicle][cp_head]);
875 ct_arc_head->mutable_bool_and()->add_literals(
877 ct_arc_head->mutable_bool_and()->add_literals(
878 vehicle_performs_node[vehicle][cp_tail]);
882 AddGeneralizedPickupDeliveryConstraints(
883 model, arc_vars, vehicle_performs_node, is_unperformed, cp_model);
885 AddGeneralizedDimensions(
model, arc_vars, vehicle_performs_node,
886 vehicle_class_performs_arc, cp_model);
889 RoutesConstraintProto* routes_ct =
890 cp_model->add_constraints()->mutable_routes();
891 for (
const auto [
arc, arc_var] : arc_vars) {
894 routes_ct->add_tails(
tail);
895 routes_ct->add_heads(
head);
896 routes_ct->add_literals(arc_var);
903 const RoutingDimension* master_dimension =
nullptr;
904 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
905 bool is_unary =
true;
906 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
907 if (dimension->GetUnaryTransitEvaluator(vehicle) ==
nullptr) {
913 master_dimension = dimension;
917 if (master_dimension !=
nullptr) {
918 for (
int cp_node = 0; cp_node < num_cp_nodes; ++cp_node) {
920 if (cp_node != 0 && !
model.IsEnd(cp_node - 1)) {
921 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
923 master_dimension->GetUnaryTransitEvaluator(vehicle);
924 min_transit =
std::min(min_transit, transit(cp_node - 1));
929 routes_ct->add_demands(min_transit);
931 DCHECK_EQ(routes_ct->demands_size(), num_cp_nodes);
933 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
934 max_capacity =
std::max(max_capacity,
935 master_dimension->vehicle_capacities()[vehicle]);
937 routes_ct->set_capacity(max_capacity);
943bool ConvertGeneralizedResponseToSolution(
const CpSolverResponse&
response,
944 const RoutingModel&
model,
945 const ArcVarMap& arc_vars,
946 Assignment* solution) {
947 if (
response.status() != CpSolverStatus::OPTIMAL &&
948 response.status() != CpSolverStatus::FEASIBLE) {
952 for (
const auto [
arc, arc_var] : arc_vars) {
953 if (
response.solution(arc_var) == 0)
continue;
955 if (
head == depot ||
tail == depot)
continue;
962void AddSolutionAsHintToGeneralizedModel(
const Assignment* solution,
963 const RoutingModel&
model,
964 const ArcVarMap& arc_vars,
965 CpModelProto* cp_model) {
966 if (solution ==
nullptr)
return;
967 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
969 const int num_nodes =
model.Nexts().size();
971 const int cp_tail =
tail + 1;
972 const int cp_head = solution->Value(
model.NextVar(
tail)) + 1;
973 const int*
const arc_var =
gtl::FindOrNull(arc_vars, {cp_tail, cp_head});
978 if (arc_var ==
nullptr)
continue;
979 hint->add_vars(*arc_var);
984void AddSolutionAsHintToModel(
const Assignment* solution,
985 const RoutingModel&
model,
986 const ArcVarMap& arc_vars,
987 CpModelProto* cp_model) {
988 if (solution ==
nullptr)
return;
989 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
991 const int depot = GetDepotFromModel(
model);
992 const int num_nodes =
model.Nexts().size();
998 const int*
const var_index =
1004 if (var_index ==
nullptr)
continue;
1005 hint->add_vars(*var_index);
1006 hint->add_values(1);
1012CpSolverResponse SolveRoutingModel(
1013 const CpModelProto& cp_model, absl::Duration remaining_time,
1014 const RoutingSearchParameters& search_parameters,
1015 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
1017 SatParameters sat_parameters = search_parameters.sat_parameters();
1018 if (!sat_parameters.has_max_time_in_seconds()) {
1019 sat_parameters.set_max_time_in_seconds(
1020 absl::ToDoubleSeconds(remaining_time));
1022 sat_parameters.set_max_time_in_seconds(
1023 std::min(absl::ToDoubleSeconds(remaining_time),
1024 sat_parameters.max_time_in_seconds()));
1028 if (observer !=
nullptr) {
1038bool IsFeasibleArcVarMap(
const ArcVarMap& arc_vars,
int max_node_index) {
1039 Bitset64<> present_in_arcs(max_node_index + 1);
1040 for (
const auto [
arc, _] : arc_vars) {
1041 present_in_arcs.Set(
arc.head);
1042 present_in_arcs.Set(
arc.tail);
1044 for (
int i = 0; i <= max_node_index; i++) {
1045 if (!present_in_arcs[i])
return false;
1056 const RoutingSearchParameters& search_parameters,
1059 sat::CpModelProto cp_model;
1060 cp_model.mutable_objective()->set_scaling_factor(
1061 search_parameters.log_cost_scaling_factor());
1062 cp_model.mutable_objective()->set_offset(search_parameters.log_cost_offset());
1063 if (search_parameters.use_generalized_cp_sat() == BOOL_TRUE) {
1064 const sat::ArcVarMap arc_vars =
1065 sat::PopulateGeneralizedRouteModelFromRoutingModel(
model, &cp_model);
1066 const int max_node_index =
model.Nexts().size() +
model.vehicles();
1067 if (!sat::IsFeasibleArcVarMap(arc_vars, max_node_index))
return false;
1068 sat::AddSolutionAsHintToGeneralizedModel(initial_solution,
model, arc_vars,
1070 return sat::ConvertGeneralizedResponseToSolution(
1071 sat::SolveRoutingModel(cp_model,
model.RemainingTime(),
1072 search_parameters,
nullptr),
1073 model, arc_vars, solution);
1075 if (!sat::RoutingModelCanBeSolvedBySat(
model))
return false;
1076 const sat::ArcVarMap arc_vars =
1077 sat::PopulateModelFromRoutingModel(
model, &cp_model);
1078 sat::AddSolutionAsHintToModel(initial_solution,
model, arc_vars, &cp_model);
1079 return sat::ConvertToSolution(
1080 sat::SolveRoutingModel(cp_model,
model.RemainingTime(), search_parameters,
1082 model, arc_vars, solution);
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
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)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
bool operator!=(const LinearConstraint &lhs, const LinearConstraint &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 LiteralWithCoeff &term)
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)
std::pair< int64_t, int64_t > Arc
LinearRange operator==(const LinearExpr &lhs, const LinearExpr &rhs)