48using operations_research::sat::BoolArgumentProto;
49using operations_research::sat::CircuitConstraintProto;
50using operations_research::sat::ConstraintProto;
51using operations_research::sat::CpModelProto;
52using operations_research::sat::CpObjectiveProto;
53using operations_research::sat::CpSolverResponse;
54using operations_research::sat::CpSolverStatus;
55using operations_research::sat::IntegerVariableProto;
58using operations_research::sat::LinearConstraintProto;
61using operations_research::sat::PartialVariableAssignment;
62using operations_research::sat::RoutesConstraintProto;
63using operations_research::sat::SatParameters;
69bool RoutingModelCanBeSolvedBySat(
const RoutingModel& model) {
70 return model.GetVehicleClassesCount() == 1;
74int AddVariable(CpModelProto* cp_model, int64_t lb, int64_t ub) {
75 const int index = cp_model->variables_size();
76 IntegerVariableProto*
const var = cp_model->add_variables();
84void AddLinearConstraint(
85 CpModelProto* cp_model, int64_t lower_bound, int64_t upper_bound,
86 absl::Span<
const std::pair<int, double>> variable_coeffs,
87 absl::Span<const int> enforcement_literals) {
88 CHECK_LE(lower_bound, upper_bound);
89 ConstraintProto* ct = cp_model->add_constraints();
90 for (
const int enforcement_literal : enforcement_literals) {
91 ct->add_enforcement_literal(enforcement_literal);
93 LinearConstraintProto* arg = ct->mutable_linear();
94 arg->add_domain(lower_bound);
95 arg->add_domain(upper_bound);
96 for (
const auto [var, coeff] : variable_coeffs) {
98 arg->add_coeffs(coeff);
104void AddLinearConstraint(
105 CpModelProto* cp_model, int64_t lower_bound, int64_t upper_bound,
106 absl::Span<
const std::pair<int, double>> variable_coeffs) {
107 AddLinearConstraint(cp_model, lower_bound, upper_bound, variable_coeffs, {});
111int64_t GetDepotFromModel(
const RoutingModel& model) {
return model.Start(0); }
118 friend bool operator==(
const Arc& a,
const Arc&
b) {
119 return a.tail ==
b.tail && a.head ==
b.head;
121 friend bool operator!=(
const Arc& a,
const Arc&
b) {
return !(a ==
b); }
122 friend bool operator<(
const Arc& a,
const Arc&
b) {
123 return a.tail ==
b.tail ? a.head <
b.head : a.tail <
b.tail;
125 friend std::ostream&
operator<<(std::ostream& strm,
const Arc& arc) {
126 return strm <<
"{" << arc.tail <<
", " << arc.head <<
"}";
128 template <
typename H>
130 return H::combine(std::move(h), a.tail, a.head);
135 absl::btree_map<Arc, int>;
137void AddSoftCumulBounds(
const RoutingDimension* dimension,
int index,
int cumul,
138 int64_t cumul_min, int64_t cumul_max,
139 CpModelProto* cp_model) {
141 const int64_t soft_ub_coef =
142 dimension->GetCumulVarSoftUpperBoundCoefficient(index);
143 if (soft_ub_coef != 0) {
144 const int64_t soft_ub = dimension->GetCumulVarSoftUpperBound(index);
145 const int soft_ub_var =
146 AddVariable(cp_model, 0,
CapSub(cumul_max, soft_ub));
148 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
149 soft_ub, {{cumul, 1}, {soft_ub_var, -1}});
150 cp_model->mutable_objective()->add_vars(soft_ub_var);
151 cp_model->mutable_objective()->add_coeffs(soft_ub_coef);
155 const int64_t soft_lb_coef =
156 dimension->GetCumulVarSoftLowerBoundCoefficient(index);
157 if (soft_lb_coef != 0) {
158 const int64_t soft_lb = dimension->GetCumulVarSoftLowerBound(index);
159 const int soft_lb_var =
160 AddVariable(cp_model, 0,
CapSub(soft_lb, cumul_min));
162 AddLinearConstraint(cp_model, soft_lb,
163 std::numeric_limits<int64_t>::max(),
164 {{cumul, 1}, {soft_lb_var, 1}});
165 cp_model->mutable_objective()->add_vars(soft_lb_var);
166 cp_model->mutable_objective()->add_coeffs(soft_lb_coef);
173void AddDimensions(
const RoutingModel& model,
const ArcVarMap& arc_vars,
174 CpModelProto* cp_model) {
175 for (
const RoutingDimension* dimension : model.GetDimensions()) {
177 const RoutingModel::TransitCallback2& transit =
178 dimension->transit_evaluator(0);
179 std::vector<int> cumuls(dimension->cumuls().size(), -1);
180 const int64_t min_start = dimension->cumuls()[model.Start(0)]->Min();
181 const int64_t max_end = std::min(dimension->cumuls()[model.End(0)]->Max(),
182 dimension->vehicle_capacities()[0]);
183 for (
int i = 0;
i < cumuls.size(); ++
i) {
184 if (model.IsStart(
i) || model.IsEnd(
i))
continue;
186 const int64_t cumul_min =
188 std::max(dimension->cumuls()[
i]->Min(),
189 CapAdd(transit(model.Start(0),
i), min_start)));
190 const int64_t cumul_max =
192 std::min(dimension->cumuls()[
i]->Max(),
193 CapSub(max_end, transit(
i, model.End(0)))));
194 cumuls[
i] = AddVariable(cp_model, cumul_min, cumul_max);
195 AddSoftCumulBounds(dimension,
i, cumuls[
i], cumul_min, cumul_max,
198 for (
const auto arc_var : arc_vars) {
199 const int tail = arc_var.first.tail;
200 const int head = arc_var.first.head;
201 if (tail == head || model.IsStart(tail) || model.IsStart(head))
continue;
205 cp_model, transit(tail, head), std::numeric_limits<int64_t>::max(),
206 {{cumuls[head], 1}, {cumuls[tail], -1}}, {arc_var.second});
211std::vector<int> CreateRanks(
const RoutingModel& model,
212 const ArcVarMap& arc_vars,
213 CpModelProto* cp_model) {
214 const int depot = GetDepotFromModel(model);
215 const int size = model.Size() + model.vehicles();
216 const int rank_size = model.Size() - model.vehicles();
217 std::vector<int> ranks(size, -1);
218 for (
int i = 0;
i < size; ++
i) {
219 if (model.IsStart(
i) || model.IsEnd(
i))
continue;
220 ranks[
i] = AddVariable(cp_model, 0, rank_size);
222 ranks[depot] = AddVariable(cp_model, 0, 0);
223 for (
const auto arc_var : arc_vars) {
224 const int tail = arc_var.first.tail;
225 const int head = arc_var.first.head;
226 if (tail == head || head == depot)
continue;
228 AddLinearConstraint(cp_model, 1, 1, {{ranks[head], 1}, {ranks[tail], -1}},
238std::vector<int> CreateVehicleVars(
const RoutingModel& model,
239 const ArcVarMap& arc_vars,
240 CpModelProto* cp_model) {
241 const int depot = GetDepotFromModel(model);
242 const int size = model.Size() + model.vehicles();
243 std::vector<int> vehicles(size, -1);
244 for (
int i = 0;
i < size; ++
i) {
245 if (model.IsStart(
i) || model.IsEnd(
i))
continue;
246 vehicles[
i] = AddVariable(cp_model, 0, size - 1);
248 for (
const auto arc_var : arc_vars) {
249 const int tail = arc_var.first.tail;
250 const int head = arc_var.first.head;
251 if (tail == head || head == depot)
continue;
254 AddLinearConstraint(cp_model, head, head, {{vehicles[head], 1}},
259 AddLinearConstraint(cp_model, 0, 0,
260 {{vehicles[head], 1}, {vehicles[tail], -1}},
266void AddPickupDeliveryConstraints(
const RoutingModel& model,
267 const ArcVarMap& arc_vars,
268 CpModelProto* cp_model) {
269 if (model.GetPickupAndDeliveryPairs().empty())
return;
270 const std::vector<int> ranks = CreateRanks(model, arc_vars, cp_model);
271 const std::vector<int> vehicles =
272 CreateVehicleVars(model, arc_vars, cp_model);
273 for (
const auto& [pickups, deliveries] : model.GetPickupAndDeliveryPairs()) {
274 const int64_t pickup = pickups[0];
275 const int64_t delivery = deliveries[0];
277 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
278 {{ranks[delivery], 1}, {ranks[pickup], -1}});
280 AddLinearConstraint(cp_model, 0, 0,
281 {{vehicles[delivery], 1}, {vehicles[pickup], -1}});
291ArcVarMap PopulateMultiRouteModelFromRoutingModel(
const RoutingModel& model,
292 CpModelProto* cp_model) {
294 const int num_nodes = model.Nexts().size();
295 const int depot = GetDepotFromModel(model);
298 for (
int tail = 0; tail < num_nodes; ++tail) {
299 const int tail_index = model.IsStart(tail) ? depot : tail;
300 std::unique_ptr<IntVarIterator> iter(
301 model.NextVar(tail)->MakeDomainIterator(
false));
307 if (model.IsStart(head))
continue;
308 const int head_index = model.IsEnd(head) ? depot : head;
309 if (head_index == tail_index && head_index == depot)
continue;
310 const int64_t cost = tail != head ? model.GetHomogeneousCost(tail, head)
311 : model.UnperformedPenalty(tail);
312 if (cost == std::numeric_limits<int64_t>::max())
continue;
313 const Arc arc = {tail_index, head_index};
314 if (arc_vars.contains(arc))
continue;
315 const int index = AddVariable(cp_model, 0, 1);
317 cp_model->mutable_objective()->add_vars(index);
318 cp_model->mutable_objective()->add_coeffs(cost);
324 std::vector<std::pair<int, double>> variable_coeffs;
325 for (
int node = 0; node < num_nodes; ++node) {
326 if (model.IsStart(node) || model.IsEnd(node))
continue;
328 if (var ==
nullptr)
continue;
329 variable_coeffs.push_back({*var, 1});
333 std::min(model.vehicles(), model.GetMaximumNumberOfActiveVehicles()),
337 AddPickupDeliveryConstraints(model, arc_vars, cp_model);
339 AddDimensions(model, arc_vars, cp_model);
344 RoutesConstraintProto* routes_ct =
345 cp_model->add_constraints()->mutable_routes();
346 for (
const auto arc_var : arc_vars) {
347 const int tail = arc_var.first.tail;
348 const int head = arc_var.first.head;
349 routes_ct->add_tails(tail == 0 ? depot : tail == depot ? 0 : tail);
350 routes_ct->add_heads(head == 0 ? depot : head == depot ? 0 : head);
351 routes_ct->add_literals(arc_var.second);
358 const RoutingDimension* primary_dimension =
nullptr;
359 for (
const RoutingDimension* dimension : model.GetDimensions()) {
361 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr) {
362 primary_dimension = dimension;
366 if (primary_dimension !=
nullptr) {
367 const RoutingModel::TransitCallback1& transit =
368 primary_dimension->GetUnaryTransitEvaluator(0);
369 for (
int node = 0; node < num_nodes; ++node) {
372 if (!model.IsEnd(node) && (!model.IsStart(node) || node == depot)) {
373 routes_ct->add_demands(transit(node));
376 DCHECK_EQ(routes_ct->demands_size(), num_nodes + 1 - model.vehicles());
377 routes_ct->set_capacity(primary_dimension->vehicle_capacities()[0]);
385ArcVarMap PopulateSingleRouteModelFromRoutingModel(
const RoutingModel& model,
386 CpModelProto* cp_model) {
388 const int num_nodes = model.Nexts().size();
389 CircuitConstraintProto* circuit =
390 cp_model->add_constraints()->mutable_circuit();
391 for (
int tail = 0; tail < num_nodes; ++tail) {
392 std::unique_ptr<IntVarIterator> iter(
393 model.NextVar(tail)->MakeDomainIterator(
false));
399 if (model.IsStart(head))
continue;
400 if (model.IsEnd(head)) head = model.Start(0);
401 const int64_t cost = tail != head ? model.GetHomogeneousCost(tail, head)
402 : model.UnperformedPenalty(tail);
403 if (cost == std::numeric_limits<int64_t>::max())
continue;
404 const int index = AddVariable(cp_model, 0, 1);
405 circuit->add_literals(index);
406 circuit->add_tails(tail);
407 circuit->add_heads(head);
408 cp_model->mutable_objective()->add_vars(index);
409 cp_model->mutable_objective()->add_coeffs(cost);
413 AddPickupDeliveryConstraints(model, arc_vars, cp_model);
414 AddDimensions(model, arc_vars, cp_model);
421ArcVarMap PopulateModelFromRoutingModel(
const RoutingModel& model,
422 CpModelProto* cp_model) {
423 if (model.vehicles() == 1) {
424 return PopulateSingleRouteModelFromRoutingModel(model, cp_model);
426 return PopulateMultiRouteModelFromRoutingModel(model, cp_model);
429void ConvertObjectiveToSolution(
const CpSolverResponse& response,
430 const CpObjectiveProto& objective,
431 const RoutingModel& model,
433 if (response.status() == CpSolverStatus::OPTIMAL) {
439 int64_t cost_value = 0;
440 for (
int i = 0;
i < objective.coeffs_size(); ++
i) {
443 CapProd(objective.coeffs(
i), response.solution(objective.vars(
i))));
445 solution->AddObjective(model.CostVar());
446 solution->SetObjectiveValue(cost_value);
447 }
else if (response.status() == CpSolverStatus::FEASIBLE) {
450 solution->AddObjective(model.CostVar());
451 solution->SetObjectiveValue(response.inner_objective_lower_bound());
456bool ConvertToSolution(
const CpSolverResponse& response,
457 const CpObjectiveProto& objective,
458 const RoutingModel& model,
const ArcVarMap& arc_vars,
461 if (response.status() != CpSolverStatus::OPTIMAL &&
462 response.status() != CpSolverStatus::FEASIBLE) {
465 const int depot = GetDepotFromModel(model);
467 for (
const auto& arc_var : arc_vars) {
468 if (response.solution(arc_var.second) != 0) {
469 const int tail = arc_var.first.tail;
470 const int head = arc_var.first.head;
471 if (head == depot)
continue;
473 solution->Add(model.NextVar(tail))->SetValue(head);
475 solution->Add(model.NextVar(model.Start(vehicle)))->SetValue(head);
481 for (
int v = 0; v < model.vehicles(); ++v) {
482 int current = model.Start(v);
483 while (!model.IsEnd(current) &&
484 solution->Contains(model.NextVar(current))) {
485 current =
solution->Value(model.NextVar(current));
487 if (model.IsEnd(current))
continue;
488 solution->Add(model.NextVar(current))->SetValue(model.End(v));
490 ConvertObjectiveToSolution(response, objective, model,
solution);
496void AddGeneralizedDimensions(
497 const RoutingModel& model,
const ArcVarMap& arc_vars,
498 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_performs_node,
499 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_class_performs_arc,
500 CpModelProto* cp_model) {
501 const int num_cp_nodes = model.Nexts().size() + model.vehicles() + 1;
502 for (
const RoutingDimension* dimension : model.GetDimensions()) {
504 std::vector<int> cumuls(num_cp_nodes, -1);
505 for (
int cp_node = 1; cp_node < num_cp_nodes; ++cp_node) {
506 const int node = cp_node - 1;
507 int64_t cumul_min = dimension->cumuls()[node]->Min();
508 int64_t cumul_max = dimension->cumuls()[node]->Max();
509 if (model.IsStart(node) || model.IsEnd(node)) {
510 const int vehicle = model.VehicleIndex(node);
512 std::min(cumul_max, dimension->vehicle_capacities()[vehicle]);
514 cumuls[cp_node] = AddVariable(cp_model, cumul_min, cumul_max);
515 AddSoftCumulBounds(dimension, node, cumuls[cp_node], cumul_min, cumul_max,
520 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
521 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
522 if (!vehicle_performs_node[vehicle].contains(cp_node))
continue;
523 const int64_t vehicle_capacity =
524 dimension->vehicle_capacities()[vehicle];
525 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
526 vehicle_capacity, {{cumuls[cp_node], 1}},
527 {vehicle_performs_node[vehicle].at(cp_node)});
531 for (
auto vehicle_class = RoutingVehicleClassIndex(0);
532 vehicle_class < model.GetVehicleClassesCount(); vehicle_class++) {
533 std::vector<int> slack(num_cp_nodes, -1);
534 const int64_t slack_cost =
CapAdd(
535 dimension->GetSpanCostCoefficientForVehicleClass(vehicle_class),
536 dimension->GetSlackCostCoefficientForVehicleClass(vehicle_class));
537 for (
const auto [arc, arc_var] : arc_vars) {
538 const auto [cp_tail, cp_head] = arc;
539 if (cp_tail == cp_head || cp_tail == 0 || cp_head == 0)
continue;
540 if (!vehicle_class_performs_arc[vehicle_class.value()].contains(
545 if (slack[cp_tail] == -1) {
546 const int64_t slack_max =
547 cp_tail - 1 < dimension->slacks().size()
548 ? dimension->slacks()[cp_tail - 1]->Max()
550 slack[cp_tail] = AddVariable(cp_model, 0, slack_max);
551 if (slack_max > 0 && slack_cost > 0) {
552 cp_model->mutable_objective()->add_vars(slack[cp_tail]);
553 cp_model->mutable_objective()->add_coeffs(slack_cost);
556 const int64_t transit = dimension->class_transit_evaluator(
557 vehicle_class)(cp_tail - 1, cp_head - 1);
561 cp_model, transit, transit,
562 {{cumuls[cp_head], 1}, {cumuls[cp_tail], -1}, {slack[cp_tail], -1}},
563 {vehicle_class_performs_arc[vehicle_class.value()].at(arc_var)});
568 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
569 const int64_t span_limit =
570 dimension->vehicle_span_upper_bounds()[vehicle];
571 if (span_limit == std::numeric_limits<int64_t>::max())
continue;
572 int cp_start = model.Start(vehicle) + 1;
573 int cp_end = model.End(vehicle) + 1;
574 AddLinearConstraint(cp_model, std::numeric_limits<int64_t>::min(),
576 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}});
580 if (dimension->HasSoftSpanUpperBounds()) {
581 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
582 const auto [bound, cost] =
583 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
584 const int cp_start = model.Start(vehicle) + 1;
585 const int cp_end = model.End(vehicle) + 1;
587 AddVariable(cp_model, 0,
588 std::min(dimension->cumuls()[model.End(vehicle)]->Max(),
589 dimension->vehicle_capacities()[vehicle]));
592 cp_model, std::numeric_limits<int64_t>::min(), bound,
593 {{cumuls[cp_end], 1}, {cumuls[cp_start], -1}, {extra, -1}});
595 cp_model->mutable_objective()->add_vars(extra);
596 cp_model->mutable_objective()->add_coeffs(cost);
602std::vector<int> CreateGeneralizedRanks(
const RoutingModel& model,
603 const ArcVarMap& arc_vars,
604 absl::Span<const int> is_unperformed,
605 CpModelProto* cp_model) {
607 const int num_cp_nodes = model.Nexts().size() + model.vehicles() + 1;
609 const int max_rank = num_cp_nodes - 2 * model.vehicles();
610 std::vector<int> ranks(num_cp_nodes, -1);
611 ranks[depot] = AddVariable(cp_model, 0, 0);
612 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
613 if (model.IsEnd(cp_node - 1))
continue;
614 ranks[cp_node] = AddVariable(cp_model, 0, max_rank);
616 AddLinearConstraint(cp_model, 0, 0, {{ranks[cp_node], 1}},
617 {is_unperformed[cp_node]});
619 for (
const auto [arc, arc_var] : arc_vars) {
620 const auto [cp_tail, cp_head] = arc;
621 if (cp_head == 0 || model.IsEnd(cp_head - 1))
continue;
622 if (cp_tail == cp_head || cp_head == depot)
continue;
624 AddLinearConstraint(cp_model, 1, 1,
625 {{ranks[cp_head], 1}, {ranks[cp_tail], -1}}, {arc_var});
630void AddGeneralizedPickupDeliveryConstraints(
631 const RoutingModel& model,
const ArcVarMap& arc_vars,
632 absl::Span<
const absl::flat_hash_map<int, int>> vehicle_performs_node,
633 absl::Span<const int> is_unperformed, CpModelProto* cp_model) {
634 if (model.GetPickupAndDeliveryPairs().empty())
return;
635 const std::vector<int> ranks =
636 CreateGeneralizedRanks(model, arc_vars, is_unperformed, cp_model);
637 for (
const auto& [pickups, deliveries] : model.GetPickupAndDeliveryPairs()) {
638 for (
const int delivery : deliveries) {
639 const int cp_delivery = delivery + 1;
640 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
641 const Arc vehicle_start_delivery_arc = {
642 static_cast<int>(model.Start(vehicle) + 1), cp_delivery};
643 if (arc_vars.contains(vehicle_start_delivery_arc)) {
645 AddLinearConstraint(cp_model, 0, 0,
646 {{arc_vars.at(vehicle_start_delivery_arc), 1}});
650 for (
const int pickup : pickups) {
651 const int cp_pickup = pickup + 1;
652 const Arc delivery_pickup_arc = {cp_delivery, cp_pickup};
653 if (arc_vars.contains(delivery_pickup_arc)) {
655 AddLinearConstraint(cp_model, 0, 0,
656 {{arc_vars.at(delivery_pickup_arc), 1}});
659 DCHECK_GE(is_unperformed[cp_delivery], 0);
660 DCHECK_GE(is_unperformed[cp_pickup], 0);
663 const int delivery_performed = -is_unperformed[cp_delivery] - 1;
664 const int pickup_performed = -is_unperformed[cp_pickup] - 1;
666 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
672 {{vehicle_performs_node[vehicle].at(cp_delivery), 1},
673 {vehicle_performs_node[vehicle].at(cp_pickup), -1}},
674 {delivery_performed, pickup_performed});
679 std::vector<std::pair<int, double>> ranks_difference;
681 for (
const int pickup : pickups) {
682 const int cp_pickup = pickup + 1;
683 ranks_difference.push_back({ranks[cp_pickup], -1});
686 for (
const int delivery : deliveries) {
687 const int cp_delivery = delivery + 1;
688 ranks_difference.push_back({ranks[cp_delivery], 1});
691 AddLinearConstraint(cp_model, 1, std::numeric_limits<int64_t>::max(),
702ArcVarMap PopulateGeneralizedRouteModelFromRoutingModel(
703 const RoutingModel& model, CpModelProto* cp_model) {
706 const int num_nodes = model.Nexts().size();
707 const int num_cp_nodes = num_nodes + model.vehicles() + 1;
710 std::vector<absl::flat_hash_map<int, int>> vehicle_performs_node(
713 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
714 const int cp_start = model.Start(vehicle) + 1;
715 const Arc start_arc = {depot, cp_start};
716 const int start_arc_var = AddVariable(cp_model, 1, 1);
717 DCHECK(!arc_vars.contains(start_arc));
718 arc_vars.insert({start_arc, start_arc_var});
720 const int cp_end = model.End(vehicle) + 1;
721 const Arc end_arc = {cp_end, depot};
722 const int end_arc_var = AddVariable(cp_model, 1, 1);
723 DCHECK(!arc_vars.contains(end_arc));
724 arc_vars.insert({end_arc, end_arc_var});
726 vehicle_performs_node[vehicle][cp_start] = start_arc_var;
727 vehicle_performs_node[vehicle][cp_end] = end_arc_var;
732 std::vector<int> is_unperformed(num_cp_nodes, -1);
734 for (
int node = 0; node < num_nodes; node++) {
735 const int cp_node = node + 1;
738 const std::vector<RoutingDisjunctionIndex>& disjunction_indices =
739 model.GetDisjunctionIndices(node);
740 if (disjunction_indices.empty() || model.ActiveVar(node)->Min() == 1) {
741 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
745 for (RoutingDisjunctionIndex disjunction_index : disjunction_indices) {
746 const int num_nodes =
747 model.GetDisjunctionNodeIndices(disjunction_index).size();
748 const int64_t penalty = model.GetDisjunctionPenalty(disjunction_index);
749 const int64_t max_cardinality =
750 model.GetDisjunctionMaxCardinality(disjunction_index);
751 if (num_nodes == max_cardinality &&
752 (penalty < 0 || penalty == std::numeric_limits<int64_t>::max())) {
754 is_unperformed[cp_node] = AddVariable(cp_model, 0, 0);
761 for (RoutingDisjunctionIndex disjunction_index(0);
762 disjunction_index < model.GetNumberOfDisjunctions();
763 disjunction_index++) {
764 const std::vector<int64_t>& disjunction_indices =
765 model.GetDisjunctionNodeIndices(disjunction_index);
766 const int disjunction_size = disjunction_indices.size();
767 const int64_t penalty = model.GetDisjunctionPenalty(disjunction_index);
768 const int64_t max_cardinality =
769 model.GetDisjunctionMaxCardinality(disjunction_index);
772 if (disjunction_size == 1 &&
773 model.GetDisjunctionIndices(disjunction_indices[0]).size() == 1 &&
774 is_unperformed[disjunction_indices[0] + 1] == -1) {
775 const int cp_node = disjunction_indices[0] + 1;
776 const Arc arc = {cp_node, cp_node};
777 DCHECK(!arc_vars.contains(arc));
778 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
779 arc_vars.insert({arc, is_unperformed[cp_node]});
780 cp_model->mutable_objective()->add_vars(is_unperformed[cp_node]);
781 cp_model->mutable_objective()->add_coeffs(penalty);
785 const int num_performed = AddVariable(cp_model, 0, max_cardinality);
786 std::vector<std::pair<int, double>> var_coeffs;
787 var_coeffs.push_back({num_performed, 1});
788 for (
const int node : disjunction_indices) {
789 const int cp_node = node + 1;
791 if (is_unperformed[cp_node] == -1) {
792 const Arc arc = {cp_node, cp_node};
793 DCHECK(!arc_vars.contains(arc));
794 is_unperformed[cp_node] = AddVariable(cp_model, 0, 1);
795 arc_vars.insert({arc, is_unperformed[cp_node]});
797 var_coeffs.push_back({is_unperformed[cp_node], 1});
799 AddLinearConstraint(cp_model, disjunction_size, disjunction_size,
803 if (penalty < 0 || penalty == std::numeric_limits<int64_t>::max()) {
804 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
805 {{num_performed, 1}});
810 const int num_violated = AddVariable(cp_model, 0, max_cardinality);
811 cp_model->mutable_objective()->add_vars(num_violated);
812 cp_model->mutable_objective()->add_coeffs(penalty);
814 AddLinearConstraint(cp_model, max_cardinality, max_cardinality,
815 {{num_performed, 1}, {num_violated, 1}});
818 for (
int tail = 0; tail < num_nodes; ++tail) {
819 const int cp_tail = tail + 1;
820 std::unique_ptr<IntVarIterator> iter(
821 model.NextVar(tail)->MakeDomainIterator(
false));
823 const int cp_head = head + 1;
824 if (model.IsStart(head))
continue;
826 if (tail == head)
continue;
829 if (model.IsStart(tail) && model.IsEnd(head) &&
830 model.VehicleIndex(tail) != model.VehicleIndex(head)) {
834 bool feasible =
false;
835 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
836 if (model.GetArcCostForVehicle(tail, head, vehicle) !=
837 std::numeric_limits<int64_t>::max()) {
842 if (!feasible)
continue;
844 const Arc arc = {cp_tail, cp_head};
845 DCHECK(!arc_vars.contains(arc));
846 const int arc_var = AddVariable(cp_model, 0, 1);
847 arc_vars.insert({arc, arc_var});
852 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
853 const int routing_index = cp_node - 1;
855 if (model.IsStart(routing_index) || model.IsEnd(routing_index))
continue;
858 std::vector<std::pair<int, double>> var_coeffs;
859 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
860 vehicle_performs_node[vehicle][cp_node] =
861 model.VehicleVar(routing_index)->Contains(vehicle)
862 ? AddVariable(cp_model, 0, 1)
863 : AddVariable(cp_model, 0, 0);
864 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
866 var_coeffs.push_back({is_unperformed[cp_node], 1});
867 AddLinearConstraint(cp_model, 1, 1, var_coeffs);
869 const int num_vehicle_classes = model.GetVehicleClassesCount();
872 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_node(
873 num_vehicle_classes);
874 for (
int cp_node = 1; cp_node < num_cp_nodes; cp_node++) {
875 const int node = cp_node - 1;
876 for (
int vehicle_class = 0; vehicle_class < num_vehicle_classes;
878 if (model.IsStart(node) || model.IsEnd(node)) {
879 const int vehicle = model.VehicleIndex(node);
880 vehicle_class_performs_node[vehicle_class][cp_node] =
882 model.GetVehicleClassIndexOfVehicle(vehicle).value()
883 ? AddVariable(cp_model, 1, 1)
884 : AddVariable(cp_model, 0, 0);
887 vehicle_class_performs_node[vehicle_class][cp_node] =
888 AddVariable(cp_model, 0, 1);
889 std::vector<std::pair<int, double>> var_coeffs;
890 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
891 if (model.GetVehicleClassIndexOfVehicle(vehicle).value() ==
893 var_coeffs.push_back({vehicle_performs_node[vehicle][cp_node], 1});
897 {{vehicle_class_performs_node[vehicle_class][cp_node], 1}},
898 {vehicle_performs_node[vehicle][cp_node]});
904 cp_model, 1, 1, var_coeffs,
905 {vehicle_class_performs_node[vehicle_class][cp_node]});
910 std::vector<absl::flat_hash_map<int, int>> vehicle_class_performs_arc(
911 num_vehicle_classes);
913 for (
const auto [arc, arc_var] : arc_vars) {
914 const auto [cp_tail, cp_head] = arc;
915 if (cp_tail == depot || cp_head == depot)
continue;
916 const int tail = cp_tail - 1;
917 const int head = cp_head - 1;
919 if (tail == head)
continue;
920 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
923 if (!vehicle_performs_node[vehicle].contains(cp_tail) ||
924 !vehicle_performs_node[vehicle].contains(cp_head)) {
927 int64_t cost = model.GetArcCostForVehicle(tail, head, vehicle);
929 if (cost == std::numeric_limits<int64_t>::max())
continue;
930 const int vehicle_class =
931 model.GetVehicleClassIndexOfVehicle(vehicle).value();
932 if (!vehicle_class_performs_arc[vehicle_class].contains(arc_var)) {
933 vehicle_class_performs_arc[vehicle_class][arc_var] =
934 AddVariable(cp_model, 0, 1);
939 ConstraintProto* ct = cp_model->add_constraints();
940 ct->add_enforcement_literal(
941 vehicle_class_performs_arc[vehicle_class][arc_var]);
942 BoolArgumentProto* bool_and = ct->mutable_bool_and();
943 bool_and->add_literals(
944 vehicle_class_performs_node[vehicle_class][cp_tail]);
945 bool_and->add_literals(
946 vehicle_class_performs_node[vehicle_class][cp_head]);
947 bool_and->add_literals(arc_var);
950 cp_model->mutable_objective()->add_vars(
951 vehicle_class_performs_arc[vehicle_class][arc_var]);
952 cp_model->mutable_objective()->add_coeffs(cost);
957 ConstraintProto* ct_arc_tail = cp_model->add_constraints();
958 ct_arc_tail->add_enforcement_literal(arc_var);
959 ct_arc_tail->add_enforcement_literal(
960 vehicle_performs_node[vehicle][cp_tail]);
961 ct_arc_tail->mutable_bool_and()->add_literals(
962 vehicle_class_performs_arc[vehicle_class][arc_var]);
963 ct_arc_tail->mutable_bool_and()->add_literals(
964 vehicle_performs_node[vehicle][cp_head]);
967 ConstraintProto* ct_arc_head = cp_model->add_constraints();
968 ct_arc_head->add_enforcement_literal(arc_var);
969 ct_arc_head->add_enforcement_literal(
970 vehicle_performs_node[vehicle][cp_head]);
971 ct_arc_head->mutable_bool_and()->add_literals(
972 vehicle_class_performs_arc[vehicle_class][arc_var]);
973 ct_arc_head->mutable_bool_and()->add_literals(
974 vehicle_performs_node[vehicle][cp_tail]);
978 AddGeneralizedPickupDeliveryConstraints(
979 model, arc_vars, vehicle_performs_node, is_unperformed, cp_model);
981 AddGeneralizedDimensions(model, arc_vars, vehicle_performs_node,
982 vehicle_class_performs_arc, cp_model);
985 RoutesConstraintProto* routes_ct =
986 cp_model->add_constraints()->mutable_routes();
987 for (
const auto [arc, arc_var] : arc_vars) {
988 const int tail = arc.tail;
989 const int head = arc.head;
990 routes_ct->add_tails(tail);
991 routes_ct->add_heads(head);
992 routes_ct->add_literals(arc_var);
999 const RoutingDimension* primary_dimension =
nullptr;
1000 for (
const RoutingDimension* dimension : model.GetDimensions()) {
1001 bool is_unary =
true;
1002 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
1003 if (dimension->GetUnaryTransitEvaluator(vehicle) ==
nullptr) {
1009 primary_dimension = dimension;
1013 if (primary_dimension !=
nullptr) {
1014 for (
int cp_node = 0; cp_node < num_cp_nodes; ++cp_node) {
1015 int64_t min_transit = std::numeric_limits<int64_t>::max();
1016 if (cp_node != 0 && !model.IsEnd(cp_node - 1)) {
1017 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
1018 const RoutingModel::TransitCallback1& transit =
1019 primary_dimension->GetUnaryTransitEvaluator(vehicle);
1020 min_transit = std::min(min_transit, transit(cp_node - 1));
1025 routes_ct->add_demands(min_transit);
1027 DCHECK_EQ(routes_ct->demands_size(), num_cp_nodes);
1028 int64_t max_capacity = std::numeric_limits<int64_t>::min();
1029 for (
int vehicle = 0; vehicle < model.vehicles(); vehicle++) {
1030 max_capacity = std::max(max_capacity,
1031 primary_dimension->vehicle_capacities()[vehicle]);
1033 routes_ct->set_capacity(max_capacity);
1039bool ConvertGeneralizedResponseToSolution(
const CpSolverResponse& response,
1040 const CpObjectiveProto& objective,
1041 const RoutingModel& model,
1042 const ArcVarMap& arc_vars,
1045 if (response.status() != CpSolverStatus::OPTIMAL &&
1046 response.status() != CpSolverStatus::FEASIBLE) {
1049 const int depot = 0;
1050 for (
const auto [arc, arc_var] : arc_vars) {
1051 if (response.solution(arc_var) == 0)
continue;
1052 const auto [tail, head] = arc;
1053 if (head == depot || tail == depot)
continue;
1054 solution->Add(model.NextVar(tail - 1))->SetValue(head - 1);
1056 ConvertObjectiveToSolution(response, objective, model,
solution);
1062 const RoutingModel& model,
1063 const ArcVarMap& arc_vars,
1064 CpModelProto* cp_model) {
1066 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
1068 const int num_nodes = model.Nexts().size();
1069 for (
int tail = 0; tail < num_nodes; ++tail) {
1070 const int cp_tail = tail + 1;
1071 const int cp_head =
solution->Value(model.NextVar(tail)) + 1;
1072 const int*
const arc_var =
gtl::FindOrNull(arc_vars, {cp_tail, cp_head});
1077 if (arc_var ==
nullptr)
continue;
1078 hint->add_vars(*arc_var);
1079 hint->add_values(1);
1084 const RoutingModel& model,
1085 const ArcVarMap& arc_vars,
1086 CpModelProto* cp_model) {
1088 PartialVariableAssignment*
const hint = cp_model->mutable_solution_hint();
1090 const int depot = GetDepotFromModel(model);
1091 const int num_nodes = model.Nexts().size();
1092 for (
int tail = 0; tail < num_nodes; ++tail) {
1093 const int tail_index = model.IsStart(tail) ? depot : tail;
1094 const int head =
solution->Value(model.NextVar(tail));
1095 const int head_index = model.IsEnd(head) ? depot : head;
1096 if (tail_index == depot && head_index == depot)
continue;
1097 const int*
const var_index =
1103 if (var_index ==
nullptr)
continue;
1104 hint->add_vars(*var_index);
1105 hint->add_values(1);
1111CpSolverResponse SolveRoutingModel(
1112 const CpModelProto& cp_model, absl::Duration remaining_time,
1113 std::atomic<bool>* interrupt_solve,
1114 const RoutingSearchParameters& search_parameters,
1115 const std::function<
void(
const CpSolverResponse& response)>& observer) {
1117 SatParameters sat_parameters = search_parameters.sat_parameters();
1118 if (!sat_parameters.has_max_time_in_seconds()) {
1119 sat_parameters.set_max_time_in_seconds(
1120 absl::ToDoubleSeconds(remaining_time));
1122 sat_parameters.set_max_time_in_seconds(
1123 std::min(absl::ToDoubleSeconds(remaining_time),
1124 sat_parameters.max_time_in_seconds()));
1130 if (observer !=
nullptr) {
1140bool IsFeasibleArcVarMap(
const ArcVarMap& arc_vars,
int max_node_index) {
1141 Bitset64<> present_in_arcs(max_node_index + 1);
1142 for (
const auto [arc, _] : arc_vars) {
1143 present_in_arcs.
Set(arc.head);
1144 present_in_arcs.
Set(arc.tail);
1146 for (
int i = 0;
i <= max_node_index;
i++) {
1147 if (!present_in_arcs[
i])
return false;