52bool RoutingModelCanBeSolvedBySat(
const RoutingModel&
model) {
53 return model.GetVehicleClassesCount() == 1;
57int AddVariable(CpModelProto* cp_model, int64_t lb, int64_t ub) {
58 const int index = cp_model->variables_size();
59 IntegerVariableProto*
const var = cp_model->add_variables();
67void AddLinearConstraint(
69 absl::Span<
const std::pair<int, double>> variable_coeffs,
70 absl::Span<const int> enforcement_literals) {
72 ConstraintProto*
ct = cp_model->add_constraints();
73 for (
const int enforcement_literal : enforcement_literals) {
74 ct->add_enforcement_literal(enforcement_literal);
76 LinearConstraintProto* arg =
ct->mutable_linear();
79 for (
const auto [
var, coeff] : variable_coeffs) {
81 arg->add_coeffs(coeff);
87void AddLinearConstraint(
89 const std::vector<std::pair<int, double>>& variable_coeffs) {
94int64_t GetDepotFromModel(
const RoutingModel&
model) {
return model.Start(0); }
102 return a.tail ==
b.tail &&
a.head ==
b.head;
104 friend bool operator!=(
const Arc&
a,
const Arc&
b) {
return !(
a ==
b); }
105 friend bool operator<(
const Arc&
a,
const Arc&
b) {
106 return a.tail ==
b.tail ?
a.head <
b.head :
a.tail <
b.tail;
108 friend std::ostream&
operator<<(std::ostream& strm,
const Arc&
arc) {
109 return strm <<
"{" <<
arc.tail <<
", " <<
arc.head <<
"}";
111 template <
typename H>
113 return H::combine(std::move(h),
a.tail,
a.head);
118 absl::btree_map<Arc, int>;
120void AddSoftCumulBounds(
const RoutingDimension* dimension,
int index,
int cumul,
121 int64_t cumul_min, int64_t cumul_max,
122 CpModelProto* cp_model) {
124 const int64_t soft_ub_coef =
125 dimension->GetCumulVarSoftUpperBoundCoefficient(
index);
126 if (soft_ub_coef != 0) {
127 const int64_t soft_ub = dimension->GetCumulVarSoftUpperBound(
index);
128 const int soft_ub_var =
129 AddVariable(cp_model, 0,
CapSub(cumul_max, soft_ub));
131 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
132 soft_ub, {{cumul, 1}, {soft_ub_var, -1}});
133 cp_model->mutable_objective()->add_vars(soft_ub_var);
134 cp_model->mutable_objective()->add_coeffs(soft_ub_coef);
138 const int64_t soft_lb_coef =
139 dimension->GetCumulVarSoftLowerBoundCoefficient(
index);
140 if (soft_lb_coef != 0) {
141 const int64_t soft_lb = dimension->GetCumulVarSoftLowerBound(
index);
142 const int soft_lb_var =
143 AddVariable(cp_model, 0,
CapSub(soft_lb, cumul_min));
145 AddLinearConstraint(cp_model, soft_lb,
146 std::numeric_limits<int64_t>::max(),
147 {{cumul, 1}, {soft_lb_var, 1}});
148 cp_model->mutable_objective()->add_vars(soft_lb_var);
149 cp_model->mutable_objective()->add_coeffs(soft_lb_coef);
156void AddDimensions(
const RoutingModel&
model,
const ArcVarMap& arc_vars,
157 CpModelProto* cp_model) {
158 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
160 const RoutingModel::TransitCallback2& transit =
161 dimension->transit_evaluator(0);
162 std::vector<int> cumuls(dimension->cumuls().size(), -1);
163 const int64_t min_start = dimension->cumuls()[
model.Start(0)]->Min();
164 const int64_t max_end = std::min(dimension->cumuls()[
model.End(0)]->Max(),
165 dimension->vehicle_capacities()[0]);
166 for (
int i = 0;
i < cumuls.size(); ++
i) {
169 const int64_t cumul_min =
171 std::max(dimension->cumuls()[
i]->Min(),
173 const int64_t cumul_max =
175 std::min(dimension->cumuls()[
i]->Max(),
177 cumuls[
i] = AddVariable(cp_model, cumul_min, cumul_max);
178 AddSoftCumulBounds(dimension,
i, cumuls[
i], cumul_min, cumul_max,
181 for (
const auto arc_var : arc_vars) {
182 const int tail = arc_var.first.tail;
183 const int head = arc_var.first.head;
188 cp_model, transit(
tail,
head), std::numeric_limits<int64_t>::max(),
189 {{cumuls[
head], 1}, {cumuls[
tail], -1}}, {arc_var.second});
194std::vector<int> CreateRanks(
const RoutingModel&
model,
195 const ArcVarMap& arc_vars,
196 CpModelProto* cp_model) {
197 const int depot = GetDepotFromModel(
model);
199 const int rank_size =
model.Size() -
model.vehicles();
200 std::vector<int> ranks(
size, -1);
201 for (
int i = 0;
i <
size; ++
i) {
203 ranks[
i] = AddVariable(cp_model, 0, rank_size);
205 ranks[depot] = AddVariable(cp_model, 0, 0);
206 for (
const auto arc_var : arc_vars) {
207 const int tail = arc_var.first.tail;
208 const int head = arc_var.first.head;
211 AddLinearConstraint(cp_model, 1, 1, {{ranks[
head], 1}, {ranks[
tail], -1}},
221std::vector<int> CreateVehicleVars(
const RoutingModel&
model,
222 const ArcVarMap& arc_vars,
223 CpModelProto* cp_model) {
224 const int depot = GetDepotFromModel(
model);
226 std::vector<int> vehicles(
size, -1);
227 for (
int i = 0;
i <
size; ++
i) {
229 vehicles[
i] = AddVariable(cp_model, 0,
size - 1);
231 for (
const auto arc_var : arc_vars) {
232 const int tail = arc_var.first.tail;
233 const int head = arc_var.first.head;
237 AddLinearConstraint(cp_model,
head,
head, {{vehicles[
head], 1}},
242 AddLinearConstraint(cp_model, 0, 0,
243 {{vehicles[
head], 1}, {vehicles[
tail], -1}},
249void AddPickupDeliveryConstraints(
const RoutingModel&
model,
250 const ArcVarMap& arc_vars,
251 CpModelProto* cp_model) {
252 if (
model.GetPickupAndDeliveryPairs().empty())
return;
253 const std::vector<int> ranks = CreateRanks(
model, arc_vars, cp_model);
254 const std::vector<int> vehicles =
255 CreateVehicleVars(
model, arc_vars, cp_model);
256 for (
const auto& [pickups, deliveries] :
model.GetPickupAndDeliveryPairs()) {
257 const int64_t pickup = pickups[0];
258 const int64_t delivery = deliveries[0];
260 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
261 {{ranks[delivery], 1}, {ranks[pickup], -1}});
263 AddLinearConstraint(cp_model, 0, 0,
264 {{vehicles[delivery], 1}, {vehicles[pickup], -1}});
274ArcVarMap PopulateMultiRouteModelFromRoutingModel(
const RoutingModel&
model,
275 CpModelProto* cp_model) {
277 const int num_nodes =
model.Nexts().size();
278 const int depot = GetDepotFromModel(
model);
283 std::unique_ptr<IntVarIterator> iter(
284 model.NextVar(
tail)->MakeDomainIterator(
false));
285 for (
int head : InitAndGetValues(iter.get())) {
295 if (cost == std::numeric_limits<int64_t>::max())
continue;
297 if (arc_vars.contains(
arc))
continue;
298 const int index = AddVariable(cp_model, 0, 1);
300 cp_model->mutable_objective()->add_vars(
index);
301 cp_model->mutable_objective()->add_coeffs(cost);
307 std::vector<std::pair<int, double>> variable_coeffs;
308 for (
int node = 0; node < num_nodes; ++node) {
309 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
311 if (
var ==
nullptr)
continue;
312 variable_coeffs.push_back({*
var, 1});
316 std::min(
model.vehicles(),
model.GetMaximumNumberOfActiveVehicles()),
320 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
322 AddDimensions(
model, arc_vars, cp_model);
327 RoutesConstraintProto* routes_ct =
328 cp_model->add_constraints()->mutable_routes();
329 for (
const auto arc_var : arc_vars) {
330 const int tail = arc_var.first.tail;
331 const int head = arc_var.first.head;
332 routes_ct->add_tails(
tail == 0 ? depot :
tail == depot ? 0 :
tail);
333 routes_ct->add_heads(
head == 0 ? depot :
head == depot ? 0 :
head);
334 routes_ct->add_literals(arc_var.second);
341 const RoutingDimension* primary_dimension =
nullptr;
342 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
344 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr) {
345 primary_dimension = dimension;
349 if (primary_dimension !=
nullptr) {
350 const RoutingModel::TransitCallback1& transit =
351 primary_dimension->GetUnaryTransitEvaluator(0);
352 for (
int node = 0; node < num_nodes; ++node) {
355 if (!
model.IsEnd(node) && (!
model.IsStart(node) || node == depot)) {
356 routes_ct->add_demands(transit(node));
359 DCHECK_EQ(routes_ct->demands_size(), num_nodes + 1 -
model.vehicles());
360 routes_ct->set_capacity(primary_dimension->vehicle_capacities()[0]);
368ArcVarMap PopulateSingleRouteModelFromRoutingModel(
const RoutingModel&
model,
369 CpModelProto* cp_model) {
371 const int num_nodes =
model.Nexts().size();
372 CircuitConstraintProto* circuit =
373 cp_model->add_constraints()->mutable_circuit();
375 std::unique_ptr<IntVarIterator> iter(
376 model.NextVar(
tail)->MakeDomainIterator(
false));
377 for (
int head : InitAndGetValues(iter.get())) {
386 if (cost == std::numeric_limits<int64_t>::max())
continue;
387 const int index = AddVariable(cp_model, 0, 1);
388 circuit->add_literals(
index);
389 circuit->add_tails(
tail);
390 circuit->add_heads(
head);
391 cp_model->mutable_objective()->add_vars(
index);
392 cp_model->mutable_objective()->add_coeffs(cost);
396 AddPickupDeliveryConstraints(
model, arc_vars, cp_model);
397 AddDimensions(
model, arc_vars, cp_model);
404ArcVarMap PopulateModelFromRoutingModel(
const RoutingModel&
model,
405 CpModelProto* cp_model) {
406 if (
model.vehicles() == 1) {
407 return PopulateSingleRouteModelFromRoutingModel(
model, cp_model);
409 return PopulateMultiRouteModelFromRoutingModel(
model, cp_model);
412void ConvertObjectiveToSolution(
const CpSolverResponse& response,
413 const CpObjectiveProto& objective,
414 const RoutingModel&
model,
416 if (response.status() == CpSolverStatus::OPTIMAL) {
422 int64_t cost_value = 0;
423 for (
int i = 0;
i < objective.coeffs_size(); ++
i) {
426 CapProd(objective.coeffs(
i), response.solution(objective.vars(
i))));
429 solution->SetObjectiveValue(cost_value);
430 }
else if (response.status() == CpSolverStatus::FEASIBLE) {
434 solution->SetObjectiveValue(response.inner_objective_lower_bound());
439bool ConvertToSolution(
const CpSolverResponse& response,
440 const CpObjectiveProto& objective,
441 const RoutingModel&
model,
const ArcVarMap& arc_vars,
444 if (response.status() != CpSolverStatus::OPTIMAL &&
445 response.status() != CpSolverStatus::FEASIBLE) {
448 const int depot = GetDepotFromModel(
model);
450 for (
const auto& arc_var : arc_vars) {
451 if (response.solution(arc_var.second) != 0) {
452 const int tail = arc_var.first.tail;
453 const int head = arc_var.first.head;
454 if (
head == depot)
continue;
464 for (
int v = 0; v <
model.vehicles(); ++v) {
465 int current =
model.Start(v);
466 while (!
model.IsEnd(current) &&
470 if (
model.IsEnd(current))
continue;
473 ConvertObjectiveToSolution(response, objective,
model,
solution);
479void AddGeneralizedDimensions(
480 const RoutingModel&
model,
const ArcVarMap& arc_vars,
481 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_performs_node,
482 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_class_performs_arc,
483 CpModelProto* cp_model) {
484 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
485 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
487 std::vector<int> cumuls(num_cp_nodes, -1);
488 for (
int cp_node = 1; cp_node < num_cp_nodes; ++cp_node) {
489 const int node = cp_node - 1;
490 int64_t cumul_min = dimension->cumuls()[node]->Min();
491 int64_t cumul_max = dimension->cumuls()[node]->Max();
492 if (
model.IsStart(node) ||
model.IsEnd(node)) {
493 const int vehicle =
model.VehicleIndex(node);
495 std::min(cumul_max, dimension->vehicle_capacities()[vehicle]);
497 cumuls[cp_node] = AddVariable(cp_model, cumul_min, cumul_max);
498 AddSoftCumulBounds(dimension, node, cumuls[cp_node], cumul_min, cumul_max,
503 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
504 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
505 if (!vehicle_performs_node[vehicle].contains(cp_node))
continue;
506 const int64_t vehicle_capacity =
507 dimension->vehicle_capacities()[vehicle];
508 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
509 vehicle_capacity, {{cumuls[cp_node], 1}},
510 {vehicle_performs_node[vehicle].at(cp_node)});
516 std::vector<int> slack(num_cp_nodes, -1);
517 const int64_t slack_cost =
CapAdd(
518 dimension->GetSpanCostCoefficientForVehicleClass(
vehicle_class),
519 dimension->GetSlackCostCoefficientForVehicleClass(
vehicle_class));
520 for (
const auto [
arc, arc_var] : arc_vars) {
521 const auto [cp_tail, cp_head] =
arc;
522 if (cp_tail == cp_head || cp_tail == 0 || cp_head == 0)
continue;
523 if (!vehicle_class_performs_arc[
vehicle_class.value()].contains(
528 if (slack[cp_tail] == -1) {
529 const int64_t slack_max =
530 cp_tail - 1 < dimension->slacks().size()
531 ? dimension->slacks()[cp_tail - 1]->Max()
533 slack[cp_tail] = AddVariable(cp_model, 0, slack_max);
534 if (slack_max > 0 && slack_cost > 0) {
535 cp_model->mutable_objective()->add_vars(slack[cp_tail]);
536 cp_model->mutable_objective()->add_coeffs(slack_cost);
539 const int64_t transit = dimension->class_transit_evaluator(
544 cp_model, transit, transit,
545 {{cumuls[cp_head], 1}, {cumuls[cp_tail], -1}, {slack[cp_tail], -1}},
546 {vehicle_class_performs_arc[
vehicle_class.value()].at(arc_var)});
551 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
552 const int64_t span_limit =
553 dimension->vehicle_span_upper_bounds()[vehicle];
554 if (span_limit == std::numeric_limits<int64_t>::max())
continue;
555 int cp_start =
model.Start(vehicle) + 1;
556 int cp_end =
model.End(vehicle) + 1;
557 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
559 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}});
563 if (dimension->HasSoftSpanUpperBounds()) {
564 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
565 const auto [
bound, cost] =
566 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
567 const int cp_start =
model.Start(vehicle) + 1;
568 const int cp_end =
model.End(vehicle) + 1;
570 AddVariable(cp_model, 0,
571 std::min(dimension->cumuls()[
model.End(vehicle)]->Max(),
572 dimension->vehicle_capacities()[vehicle]));
575 cp_model, std::numeric_limits<int64_t>::min(),
bound,
576 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}, {extra, -1}});
578 cp_model->mutable_objective()->add_vars(extra);
579 cp_model->mutable_objective()->add_coeffs(cost);
585std::vector<int> CreateGeneralizedRanks(
const RoutingModel&
model,
586 const ArcVarMap& arc_vars,
587 const std::vector<int>& is_unperformed,
588 CpModelProto* cp_model) {
590 const int num_cp_nodes =
model.Nexts().size() +
model.vehicles() + 1;
593 std::vector<int> ranks(num_cp_nodes, -1);
594 ranks[depot] = AddVariable(cp_model, 0, 0);
595 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
596 if (
model.IsEnd(cp_node - 1))
continue;
597 ranks[cp_node] = AddVariable(cp_model, 0,
max_rank);
599 AddLinearConstraint(cp_model, 0, 0, {{ranks[cp_node], 1}},
600 {is_unperformed[cp_node]});
602 for (
const auto [
arc, arc_var] : arc_vars) {
603 const auto [cp_tail, cp_head] =
arc;
604 if (cp_head == 0 ||
model.IsEnd(cp_head - 1))
continue;
605 if (cp_tail == cp_head || cp_head == depot)
continue;
607 AddLinearConstraint(cp_model, 1, 1,
608 {{ranks[cp_head], 1}, {ranks[cp_tail], -1}}, {arc_var});
613void AddGeneralizedPickupDeliveryConstraints(
614 const RoutingModel&
model,
const ArcVarMap& arc_vars,
615 const std::vector<absl::flat_hash_map<int, int>>& vehicle_performs_node,
616 const std::vector<int>& is_unperformed, CpModelProto* cp_model) {
617 if (
model.GetPickupAndDeliveryPairs().empty())
return;
618 const std::vector<int> ranks =
619 CreateGeneralizedRanks(
model, arc_vars, is_unperformed, cp_model);
620 for (
const auto& [pickups, deliveries] :
model.GetPickupAndDeliveryPairs()) {
621 for (
const int delivery : deliveries) {
622 const int cp_delivery = delivery + 1;
623 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
624 const Arc vehicle_start_delivery_arc = {
625 static_cast<int>(
model.Start(vehicle) + 1), cp_delivery};
626 if (arc_vars.contains(vehicle_start_delivery_arc)) {
628 AddLinearConstraint(cp_model, 0, 0,
629 {{arc_vars.at(vehicle_start_delivery_arc), 1}});
633 for (
const int pickup : pickups) {
634 const int cp_pickup = pickup + 1;
635 const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
636 if (arc_vars.contains(delivery_pickup_arc)) {
638 AddLinearConstraint(cp_model, 0, 0,
639 {{arc_vars.at(delivery_pickup_arc), 1}});
642 DCHECK_GE(is_unperformed[cp_delivery], 0);
643 DCHECK_GE(is_unperformed[cp_pickup], 0);
646 const int delivery_performed = -is_unperformed[cp_delivery] - 1;
647 const int pickup_performed = -is_unperformed[cp_pickup] - 1;
649 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
655 {{vehicle_performs_node[vehicle].at(cp_delivery), 1},
656 {vehicle_performs_node[vehicle].at(cp_pickup), -1}},
657 {delivery_performed, pickup_performed});
662 std::vector<std::pair<int, double>> ranks_difference;
664 for (
const int pickup : pickups) {
665 const int cp_pickup = pickup + 1;
666 ranks_difference.push_back({ranks[cp_pickup], -1});
669 for (
const int delivery : deliveries) {
670 const int cp_delivery = delivery + 1;
671 ranks_difference.push_back({ranks[cp_delivery], 1});
674 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
685ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
686 const RoutingModel&
model, CpModelProto* cp_model) {
689 const int num_nodes =
model.Nexts().size();
690 const int num_cp_nodes = num_nodes +
model.vehicles() + 1;
693 std::vector<absl::flat_hash_map<int, int>> vehicle_performs_node(
696 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
697 const int cp_start =
model.Start(vehicle) + 1;
698 const Arc start_arc = {depot, cp_start};
699 const int start_arc_var = AddVariable(cp_model, 1, 1);
700 DCHECK(!arc_vars.contains(start_arc));
701 arc_vars.insert({start_arc, start_arc_var});
703 const int cp_end =
model.End(vehicle) + 1;
704 const Arc end_arc = {cp_end, depot};
705 const int end_arc_var = AddVariable(cp_model, 1, 1);
706 DCHECK(!arc_vars.contains(end_arc));
707 arc_vars.insert({end_arc, end_arc_var});
709 vehicle_performs_node[vehicle][cp_start] = start_arc_var;
710 vehicle_performs_node[vehicle][cp_end] = end_arc_var;
715 std::vector<int> is_unperformed(num_cp_nodes, -1);
717 for (
int node = 0; node < num_nodes; node++) {
718 const int cp_node = node + 1;
721 const std::vector<RoutingDisjunctionIndex>& disjunction_indices =
722 model.GetDisjunctionIndices(node);
723 if (disjunction_indices.empty() ||
model.ActiveVar(node)->Min() == 1) {
724 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
728 for (RoutingDisjunctionIndex disjunction_index : disjunction_indices) {
729 const int num_nodes =
730 model.GetDisjunctionNodeIndices(disjunction_index).size();
731 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
732 const int64_t max_cardinality =
733 model.GetDisjunctionMaxCardinality(disjunction_index);
734 if (num_nodes == max_cardinality &&
735 (penalty < 0 || penalty == std::numeric_limits<int64_t>::max())) {
737 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
744 for (RoutingDisjunctionIndex disjunction_index(0);
745 disjunction_index <
model.GetNumberOfDisjunctions();
746 disjunction_index++) {
747 const std::vector<int64_t>& disjunction_indices =
748 model.GetDisjunctionNodeIndices(disjunction_index);
749 const int disjunction_size = disjunction_indices.size();
750 const int64_t penalty =
model.GetDisjunctionPenalty(disjunction_index);
751 const int64_t max_cardinality =
752 model.GetDisjunctionMaxCardinality(disjunction_index);
755 if (disjunction_size == 1 &&
756 model.GetDisjunctionIndices(disjunction_indices[0]).size() == 1 &&
757 is_unperformed[disjunction_indices[0] + 1] == -1) {
758 const int cp_node = disjunction_indices[0] + 1;
759 const Arc
arc = {cp_node, cp_node};
760 DCHECK(!arc_vars.contains(
arc));
761 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
762 arc_vars.insert({
arc, is_unperformed[cp_node]});
763 cp_model->mutable_objective()->add_vars(is_unperformed[cp_node]);
764 cp_model->mutable_objective()->add_coeffs(penalty);
768 const int num_performed = AddVariable(cp_model, 0, max_cardinality);
769 std::vector<std::pair<int, double>> var_coeffs;
770 var_coeffs.push_back({num_performed, 1});
771 for (
const int node : disjunction_indices) {
772 const int cp_node = node + 1;
774 if (is_unperformed[cp_node] == -1) {
775 const Arc
arc = {cp_node, cp_node};
776 DCHECK(!arc_vars.contains(
arc));
777 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
778 arc_vars.insert({
arc, is_unperformed[cp_node]});
780 var_coeffs.push_back({is_unperformed[cp_node], 1});
782 AddLinearConstraint(cp_model, disjunction_size, disjunction_size,
786 if (penalty < 0 || penalty == std::numeric_limits<int64_t>::max()) {
787 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
788 {{num_performed, 1}});
793 const int num_violated = AddVariable(cp_model, 0, max_cardinality);
794 cp_model->mutable_objective()->add_vars(num_violated);
795 cp_model->mutable_objective()->add_coeffs(penalty);
797 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
798 {{num_performed, 1}, {num_violated, 1}});
802 const int cp_tail =
tail + 1;
803 std::unique_ptr<IntVarIterator> iter(
804 model.NextVar(
tail)->MakeDomainIterator(
false));
805 for (
int head : InitAndGetValues(iter.get())) {
806 const int cp_head =
head + 1;
817 bool feasible =
false;
818 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
820 std::numeric_limits<int64_t>::max()) {
825 if (!feasible)
continue;
827 const Arc
arc = {cp_tail, cp_head};
828 DCHECK(!arc_vars.contains(
arc));
829 const int arc_var = AddVariable(cp_model, 0, 1);
830 arc_vars.insert({
arc, arc_var});
835 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
836 const int routing_index = cp_node - 1;
838 if (
model.IsStart(routing_index) ||
model.IsEnd(routing_index))
continue;
841 std::vector<std::pair<int, double>> var_coeffs;
842 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
843 vehicle_performs_node[vehicle][cp_node] =
844 model.VehicleVar(routing_index)->Contains(vehicle)
845 ? AddVariable(cp_model, 0, 1)
846 : AddVariable(cp_model, 0, 0);
847 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
849 var_coeffs.push_back({is_unperformed[cp_node], 1});
850 AddLinearConstraint(cp_model, 1, 1, var_coeffs);
852 const int num_vehicle_classes =
model.GetVehicleClassesCount();
855 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_node(
856 num_vehicle_classes);
857 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
858 const int node = cp_node - 1;
861 if (
model.IsStart(node) ||
model.IsEnd(node)) {
862 const int vehicle =
model.VehicleIndex(node);
865 model.GetVehicleClassIndexOfVehicle(vehicle).value()
866 ? AddVariable(cp_model, 1, 1)
867 : AddVariable(cp_model, 0, 0);
871 AddVariable(cp_model, 0, 1);
872 std::vector<std::pair<int, double>> var_coeffs;
873 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
874 if (
model.GetVehicleClassIndexOfVehicle(vehicle).value() ==
876 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
881 {vehicle_performs_node[vehicle][cp_node]});
887 cp_model, 1, 1, var_coeffs,
893 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_arc(
894 num_vehicle_classes);
896 for (
const auto [
arc, arc_var] : arc_vars) {
897 const auto [cp_tail, cp_head] =
arc;
898 if (cp_tail == depot || cp_head == depot)
continue;
899 const int tail = cp_tail - 1;
900 const int head = cp_head - 1;
903 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
906 if (!vehicle_performs_node[vehicle].contains(cp_tail) ||
907 !vehicle_performs_node[vehicle].contains(cp_head)) {
910 int64_t cost =
model.GetArcCostForVehicle(
tail,
head, vehicle);
912 if (cost == std::numeric_limits<int64_t>::max())
continue;
914 model.GetVehicleClassIndexOfVehicle(vehicle).value();
915 if (!vehicle_class_performs_arc[
vehicle_class].contains(arc_var)) {
917 AddVariable(cp_model, 0, 1);
922 ConstraintProto*
ct = cp_model->add_constraints();
923 ct->add_enforcement_literal(
925 BoolArgumentProto* bool_and =
ct->mutable_bool_and();
926 bool_and->add_literals(
928 bool_and->add_literals(
930 bool_and->add_literals(arc_var);
933 cp_model->mutable_objective()->add_vars(
935 cp_model->mutable_objective()->add_coeffs(cost);
940 ConstraintProto* ct_arc_tail = cp_model->add_constraints();
941 ct_arc_tail->add_enforcement_literal(arc_var);
942 ct_arc_tail->add_enforcement_literal(
943 vehicle_performs_node[vehicle][cp_tail]);
944 ct_arc_tail->mutable_bool_and()->add_literals(
946 ct_arc_tail->mutable_bool_and()->add_literals(
947 vehicle_performs_node[vehicle][cp_head]);
950 ConstraintProto* ct_arc_head = cp_model->add_constraints();
951 ct_arc_head->add_enforcement_literal(arc_var);
952 ct_arc_head->add_enforcement_literal(
953 vehicle_performs_node[vehicle][cp_head]);
954 ct_arc_head->mutable_bool_and()->add_literals(
956 ct_arc_head->mutable_bool_and()->add_literals(
957 vehicle_performs_node[vehicle][cp_tail]);
961 AddGeneralizedPickupDeliveryConstraints(
962 model, arc_vars, vehicle_performs_node, is_unperformed, cp_model);
964 AddGeneralizedDimensions(
model, arc_vars, vehicle_performs_node,
965 vehicle_class_performs_arc, cp_model);
968 RoutesConstraintProto* routes_ct =
969 cp_model->add_constraints()->mutable_routes();
970 for (
const auto [
arc, arc_var] : arc_vars) {
973 routes_ct->add_tails(
tail);
974 routes_ct->add_heads(
head);
975 routes_ct->add_literals(arc_var);
982 const RoutingDimension* primary_dimension =
nullptr;
983 for (
const RoutingDimension* dimension :
model.GetDimensions()) {
984 bool is_unary =
true;
985 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
986 if (dimension->GetUnaryTransitEvaluator(vehicle) ==
nullptr) {
992 primary_dimension = dimension;
996 if (primary_dimension !=
nullptr) {
997 for (
int cp_node = 0; cp_node < num_cp_nodes; ++cp_node) {
998 int64_t min_transit = std::numeric_limits<int64_t>::max();
999 if (cp_node != 0 && !
model.IsEnd(cp_node - 1)) {
1000 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
1001 const RoutingModel::TransitCallback1& transit =
1002 primary_dimension->GetUnaryTransitEvaluator(vehicle);
1003 min_transit = std::min(min_transit, transit(cp_node - 1));
1008 routes_ct->add_demands(min_transit);
1010 DCHECK_EQ(routes_ct->demands_size(), num_cp_nodes);
1011 int64_t max_capacity = std::numeric_limits<int64_t>::min();
1012 for (
int vehicle = 0; vehicle <
model.vehicles(); vehicle++) {
1013 max_capacity = std::max(max_capacity,
1014 primary_dimension->vehicle_capacities()[vehicle]);
1016 routes_ct->set_capacity(max_capacity);
1022bool ConvertGeneralizedResponseToSolution(
const CpSolverResponse& response,
1023 const CpObjectiveProto& objective,
1024 const RoutingModel&
model,
1025 const ArcVarMap& arc_vars,
1028 if (response.status() != CpSolverStatus::OPTIMAL &&
1029 response.status() != CpSolverStatus::FEASIBLE) {
1032 const int depot = 0;
1033 for (
const auto [
arc, arc_var] : arc_vars) {
1034 if (response.solution(arc_var) == 0)
continue;
1036 if (
head == depot ||
tail == depot)
continue;
1039 ConvertObjectiveToSolution(response, objective,
model,
solution);
1044void AddSolutionAsHintToGeneralizedModel(
const Assignment*
solution,
1045 const RoutingModel&
model,
1046 const ArcVarMap& arc_vars,
1047 CpModelProto* cp_model) {
1049 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
1051 const int num_nodes =
model.Nexts().size();
1053 const int cp_tail =
tail + 1;
1055 const int*
const arc_var =
gtl::FindOrNull(arc_vars, {cp_tail, cp_head});
1060 if (arc_var ==
nullptr)
continue;
1061 hint->add_vars(*arc_var);
1062 hint->add_values(1);
1066void AddSolutionAsHintToModel(
const Assignment*
solution,
1067 const RoutingModel&
model,
1068 const ArcVarMap& arc_vars,
1069 CpModelProto* cp_model) {
1071 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
1073 const int depot = GetDepotFromModel(
model);
1074 const int num_nodes =
model.Nexts().size();
1088 hint->add_values(1);
1094CpSolverResponse SolveRoutingModel(
1095 const CpModelProto& cp_model, absl::Duration remaining_time,
1096 std::atomic<bool>* interrupt_solve,
1097 const RoutingSearchParameters& search_parameters,
1098 const std::function<
void(
const CpSolverResponse& response)>& observer) {
1100 SatParameters sat_parameters = search_parameters.sat_parameters();
1101 if (!sat_parameters.has_max_time_in_seconds()) {
1102 sat_parameters.set_max_time_in_seconds(
1103 absl::ToDoubleSeconds(remaining_time));
1105 sat_parameters.set_max_time_in_seconds(
1106 std::min(absl::ToDoubleSeconds(remaining_time),
1107 sat_parameters.max_time_in_seconds()));
1111 model.GetOrCreate<TimeLimit>()->RegisterExternalBooleanAsLimit(
1113 if (observer !=
nullptr) {
1123bool IsFeasibleArcVarMap(
const ArcVarMap& arc_vars,
int max_node_index) {
1124 Bitset64<> present_in_arcs(max_node_index + 1);
1125 for (
const auto [
arc, _] : arc_vars) {
1126 present_in_arcs.Set(
arc.head);
1127 present_in_arcs.Set(
arc.tail);
1129 for (
int i = 0;
i <= max_node_index;
i++) {
1130 if (!present_in_arcs[
i])
return false;