forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrouting.h
3116 lines (2934 loc) · 147 KB
/
routing.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The vehicle routing library lets one model and solve generic vehicle routing
/// problems ranging from the Traveling Salesman Problem to more complex
/// problems such as the Capacitated Vehicle Routing Problem with Time Windows.
///
/// The objective of a vehicle routing problem is to build routes covering a set
/// of nodes minimizing the overall cost of the routes (usually proportional to
/// the sum of the lengths of each segment of the routes) while respecting some
/// problem-specific constraints (such as the length of a route). A route is
/// equivalent to a path connecting nodes, starting/ending at specific
/// starting/ending nodes.
///
/// The term "vehicle routing" is historical and the category of problems solved
/// is not limited to the routing of vehicles: any problem involving finding
/// routes visiting a given number of nodes optimally falls under this category
/// of problems, such as finding the optimal sequence in a playlist.
/// The literature around vehicle routing problems is extremely dense but one
/// can find some basic introductions in the following links:
/// - http://en.wikipedia.org/wiki/Travelling_salesman_problem
/// - http://www.tsp.gatech.edu/history/index.html
/// - http://en.wikipedia.org/wiki/Vehicle_routing_problem
///
/// The vehicle routing library is a vertical layer above the constraint
/// programming library (ortools/constraint_programming:cp).
/// One has access to all underlying constrained variables of the vehicle
/// routing model which can therefore be enriched by adding any constraint
/// available in the constraint programming library.
///
/// There are two sets of variables available:
/// - path variables:
/// * "next(i)" variables representing the immediate successor of the node
/// corresponding to i; use IndexToNode() to get the node corresponding to
/// a "next" variable value; note that node indices are strongly typed
/// integers (cf. ortools/base/int_type.h);
/// * "vehicle(i)" variables representing the vehicle route to which the
/// node corresponding to i belongs;
/// * "active(i)" boolean variables, true if the node corresponding to i is
/// visited and false if not; this can be false when nodes are either
/// optional or part of a disjunction;
/// * The following relationships hold for all i:
/// active(i) == 0 <=> next(i) == i <=> vehicle(i) == -1,
/// next(i) == j => vehicle(j) == vehicle(i).
/// - dimension variables, used when one is accumulating quantities along
/// routes, such as weight or volume carried, distance or time:
/// * "cumul(i,d)" variables representing the quantity of dimension d when
/// arriving at the node corresponding to i;
/// * "transit(i,d)" variables representing the quantity of dimension d added
/// after visiting the node corresponding to i.
/// * The following relationship holds for all (i,d):
/// next(i) == j => cumul(j,d) == cumul(i,d) + transit(i,d).
/// Solving the vehicle routing problems is mainly done using approximate
/// methods (namely local search,
/// cf. http://en.wikipedia.org/wiki/Local_search_(optimization) ), potentially
/// combined with exact techniques based on dynamic programming and exhaustive
/// tree search.
// TODO(user): Add a section on costs (vehicle arc costs, span costs,
// disjunctions costs).
//
/// Advanced tips: Flags are available to tune the search used to solve routing
/// problems. Here is a quick overview of the ones one might want to modify:
/// - Limiting the search for solutions:
/// * routing_solution_limit (default: kint64max): stop the search after
/// finding 'routing_solution_limit' improving solutions;
/// * routing_time_limit (default: kint64max): stop the search after
/// 'routing_time_limit' milliseconds;
/// - Customizing search:
/// * routing_first_solution (default: select the first node with an unbound
/// successor and connect it to the first available node): selects the
/// heuristic to build a first solution which will then be improved by local
/// search; possible values are GlobalCheapestArc (iteratively connect two
/// nodes which produce the cheapest route segment), LocalCheapestArc
/// (select the first node with an unbound successor and connect it to the
/// node which produces the cheapest route segment), PathCheapestArc
/// (starting from a route "start" node, connect it to the node which
/// produces the cheapest route segment, then extend the route by iterating
/// on the last node added to the route).
/// * Local search neighborhoods:
/// - routing_no_lns (default: false): forbids the use of Large Neighborhood
/// Search (LNS); LNS can find good solutions but is usually very slow.
/// Refer to the description of PATHLNS in the LocalSearchOperators enum
/// in constraint_solver.h for more information.
/// - routing_no_tsp (default: true): forbids the use of exact methods to
/// solve "sub"-traveling salesman problems (TSPs) of the current model
/// (such as sub-parts of a route, or one route in a multiple route
/// problem). Uses dynamic programming to solve such TSPs with a maximum
/// size (in number of nodes) up to cp_local_search_tsp_opt_size (flag
/// with a default value of 13 nodes). It is not activated by default
/// because it can slow down the search.
/// * Meta-heuristics: used to guide the search out of local minima found by
/// local search. Note that, in general, a search with metaheuristics
/// activated never stops, therefore one must specify a search limit.
/// Several types of metaheuristics are provided:
/// - routing_guided_local_search (default: false): activates guided local
/// search (cf. http://en.wikipedia.org/wiki/Guided_Local_Search);
/// this is generally the most efficient metaheuristic for vehicle
/// routing;
/// - routing_simulated_annealing (default: false): activates simulated
/// annealing (cf. http://en.wikipedia.org/wiki/Simulated_annealing);
/// - routing_tabu_search (default: false): activates tabu search (cf.
/// http://en.wikipedia.org/wiki/Tabu_search).
///
/// Code sample:
/// Here is a simple example solving a traveling salesman problem given a cost
/// function callback (returns the cost of a route segment):
///
/// - Define a custom distance/cost function from an index to another; in this
/// example just returns the sum of the indices:
///
/// int64_t MyDistance(int64_t from, int64_t to) {
/// return from + to;
/// }
///
/// - Create a routing model for a given problem size (int number of nodes) and
/// number of routes (here, 1):
///
/// RoutingIndexManager manager(...number of nodes..., 1);
/// RoutingModel routing(manager);
///
/// - Set the cost function by registering an std::function<int64_t(int64_t,
/// int64_t)> in the model and passing its index as the vehicle cost.
///
/// const int cost = routing.RegisterTransitCallback(MyDistance);
/// routing.SetArcCostEvaluatorOfAllVehicles(cost);
///
/// - Find a solution using Solve(), returns a solution if any (owned by
/// routing):
///
/// const Assignment* solution = routing.Solve();
/// CHECK(solution != nullptr);
///
/// - Inspect the solution cost and route (only one route here):
///
/// LOG(INFO) << "Cost " << solution->ObjectiveValue();
/// const int route_number = 0;
/// for (int64_t node = routing.Start(route_number);
/// !routing.IsEnd(node);
/// node = solution->Value(routing.NextVar(node))) {
/// LOG(INFO) << manager.IndexToNode(node);
/// }
///
///
/// Keywords: Vehicle Routing, Traveling Salesman Problem, TSP, VRP, CVRPTW,
/// PDP.
#ifndef OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
#define OR_TOOLS_CONSTRAINT_SOLVER_ROUTING_H_
#include <algorithm>
#include <deque>
#include <functional>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/bind_front.h"
#include "absl/memory/memory.h"
#include "absl/time/time.h"
#include "ortools/base/int_type.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/map_util.h"
#include "ortools/base/strong_vector.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
#include "ortools/constraint_solver/routing_index_manager.h"
#include "ortools/constraint_solver/routing_parameters.pb.h"
#include "ortools/constraint_solver/routing_types.h"
#include "ortools/graph/graph.h"
#include "ortools/sat/theta_tree.h"
#include "ortools/util/bitset.h"
#include "ortools/util/piecewise_linear_function.h"
#include "ortools/util/range_query_function.h"
#include "ortools/util/saturated_arithmetic.h"
#include "ortools/util/sorted_interval_list.h"
namespace operations_research {
class GlobalDimensionCumulOptimizer;
class LocalDimensionCumulOptimizer;
class LocalSearchPhaseParameters;
#ifndef SWIG
class IndexNeighborFinder;
class IntVarFilteredDecisionBuilder;
#endif
class RoutingDimension;
#ifndef SWIG
using util::ReverseArcListGraph;
class SweepArranger;
#endif
class RoutingModel {
public:
/// Status of the search.
enum Status {
/// Problem not solved yet (before calling RoutingModel::Solve()).
ROUTING_NOT_SOLVED,
/// Problem solved successfully after calling RoutingModel::Solve().
ROUTING_SUCCESS,
/// No solution found to the problem after calling RoutingModel::Solve().
ROUTING_FAIL,
/// Time limit reached before finding a solution with RoutingModel::Solve().
ROUTING_FAIL_TIMEOUT,
/// Model, model parameters or flags are not valid.
ROUTING_INVALID
};
/// Types of precedence policy applied to pickup and delivery pairs.
enum PickupAndDeliveryPolicy {
/// Any precedence is accepted.
PICKUP_AND_DELIVERY_NO_ORDER,
/// Deliveries must be performed in reverse order of pickups.
PICKUP_AND_DELIVERY_LIFO,
/// Deliveries must be performed in the same order as pickups.
PICKUP_AND_DELIVERY_FIFO
};
typedef RoutingCostClassIndex CostClassIndex;
typedef RoutingDimensionIndex DimensionIndex;
typedef RoutingDisjunctionIndex DisjunctionIndex;
typedef RoutingVehicleClassIndex VehicleClassIndex;
typedef RoutingTransitCallback1 TransitCallback1;
typedef RoutingTransitCallback2 TransitCallback2;
// TODO(user): Remove all SWIG guards by adding the @ignore in .i.
#if !defined(SWIG)
typedef RoutingIndexPair IndexPair;
typedef RoutingIndexPairs IndexPairs;
#endif // SWIG
#if !defined(SWIG)
/// What follows is relevant for models with time/state dependent transits.
/// Such transits, say from node A to node B, are functions f:
/// int64_t->int64_t of the cumuls of a dimension. The user is free to
/// implement the abstract RangeIntToIntFunction interface, but it is expected
/// that the implementation of each method is quite fast. For
/// performance-related reasons, StateDependentTransit keeps an additional
/// pointer to a RangeMinMaxIndexFunction, with similar functionality to
/// RangeIntToIntFunction, for g(x) = f(x)+x, where f is the transit from A to
/// B. In most situations the best solutions are problem-specific, but in case
/// of doubt the user may use the MakeStateDependentTransit function from the
/// routing library, which works out-of-the-box, with very good running time,
/// but memory inefficient in some situations.
struct StateDependentTransit {
RangeIntToIntFunction* transit; /// f(x)
RangeMinMaxIndexFunction* transit_plus_identity; /// g(x) = f(x) + x
};
typedef std::function<StateDependentTransit(int64_t, int64_t)>
VariableIndexEvaluator2;
#endif // SWIG
#if !defined(SWIG)
struct CostClass {
/// Index of the arc cost evaluator, registered in the RoutingModel class.
int evaluator_index = 0;
/// SUBTLE:
/// The vehicle's fixed cost is skipped on purpose here, because we
/// can afford to do so:
/// - We don't really care about creating "strict" equivalence classes;
/// all we care about is to:
/// 1) compress the space of cost callbacks so that
/// we can cache them more efficiently.
/// 2) have a smaller IntVar domain thanks to using a "cost class var"
/// instead of the vehicle var, so that we reduce the search space.
/// Both of these are an incentive for *fewer* cost classes. Ignoring
/// the fixed costs can only be good in that regard.
/// - The fixed costs are only needed when evaluating the cost of the
/// first arc of the route, in which case we know the vehicle, since we
/// have the route's start node.
/// Only dimensions that have non-zero cost evaluator and a non-zero cost
/// coefficient (in this cost class) are listed here. Since we only need
/// their transit evaluator (the raw version that takes var index, not Node
/// Index) and their span cost coefficient, we just store those.
/// This is sorted by the natural operator < (and *not* by DimensionIndex).
struct DimensionCost {
int64_t transit_evaluator_class;
int64_t cost_coefficient;
const RoutingDimension* dimension;
bool operator<(const DimensionCost& cost) const {
if (transit_evaluator_class != cost.transit_evaluator_class) {
return transit_evaluator_class < cost.transit_evaluator_class;
}
return cost_coefficient < cost.cost_coefficient;
}
};
std::vector<DimensionCost>
dimension_transit_evaluator_class_and_cost_coefficient;
explicit CostClass(int evaluator_index)
: evaluator_index(evaluator_index) {}
/// Comparator for STL containers and algorithms.
static bool LessThan(const CostClass& a, const CostClass& b) {
if (a.evaluator_index != b.evaluator_index) {
return a.evaluator_index < b.evaluator_index;
}
return a.dimension_transit_evaluator_class_and_cost_coefficient <
b.dimension_transit_evaluator_class_and_cost_coefficient;
}
};
struct VehicleClass {
/// The cost class of the vehicle.
CostClassIndex cost_class_index;
/// Contrarily to CostClass, here we need strict equivalence.
int64_t fixed_cost;
/// Whether or not the vehicle is used when empty.
bool used_when_empty;
/// Vehicle start and end equivalence classes. Currently if two vehicles
/// have different start/end nodes which are "physically" located at the
/// same place, these two vehicles will be considered as non-equivalent
/// unless the two indices are in the same class.
// TODO(user): Find equivalent start/end nodes wrt dimensions and
// callbacks.
int start_equivalence_class;
int end_equivalence_class;
/// Bounds of cumul variables at start and end vehicle nodes.
/// dimension_{start,end}_cumuls_{min,max}[d] is the bound for dimension d.
absl::StrongVector<DimensionIndex, int64_t> dimension_start_cumuls_min;
absl::StrongVector<DimensionIndex, int64_t> dimension_start_cumuls_max;
absl::StrongVector<DimensionIndex, int64_t> dimension_end_cumuls_min;
absl::StrongVector<DimensionIndex, int64_t> dimension_end_cumuls_max;
absl::StrongVector<DimensionIndex, int64_t> dimension_capacities;
/// dimension_evaluators[d]->Run(from, to) is the transit value of arc
/// from->to for a dimension d.
absl::StrongVector<DimensionIndex, int64_t> dimension_evaluator_classes;
/// Fingerprint of unvisitable non-start/end nodes.
uint64_t unvisitable_nodes_fprint;
/// Sorted set of resource groups for which the vehicle requires a resource.
std::vector<int> required_resource_group_indices;
/// Comparator for STL containers and algorithms.
static bool LessThan(const VehicleClass& a, const VehicleClass& b);
};
#endif // defined(SWIG)
/// Struct used to sort and store vehicles by their type. Two vehicles have
/// the same "vehicle type" iff they have the same cost class and start/end
/// nodes.
struct VehicleTypeContainer {
struct VehicleClassEntry {
int vehicle_class;
int64_t fixed_cost;
bool operator<(const VehicleClassEntry& other) const {
return std::tie(fixed_cost, vehicle_class) <
std::tie(other.fixed_cost, other.vehicle_class);
}
};
int NumTypes() const { return sorted_vehicle_classes_per_type.size(); }
int Type(int vehicle) const {
DCHECK_LT(vehicle, type_index_of_vehicle.size());
return type_index_of_vehicle[vehicle];
}
std::vector<int> type_index_of_vehicle;
// clang-format off
std::vector<std::set<VehicleClassEntry> > sorted_vehicle_classes_per_type;
std::vector<std::deque<int> > vehicles_per_vehicle_class;
// clang-format on
};
/// A ResourceGroup defines a set of available Resources with attributes on
/// one or multiple dimensions.
/// For every ResourceGroup in the model, each (used) vehicle in the solution
/// which requires a resource (see NotifyVehicleRequiresResource()) from this
/// group must be assigned to exactly 1 resource, and each resource can in
/// turn be assigned to at most 1 vehicle requiring it. This
/// vehicle-to-resource assignment will apply the corresponding Attributes to
/// the dimensions affected by the resource group. NOTE: As of 2021/07, each
/// ResourceGroup can only affect a single RoutingDimension at a time, i.e.
/// all Resources in a group must apply attributes to the same single
/// dimension.
class ResourceGroup {
public:
/// Attributes for a dimension.
class Attributes {
public:
Attributes();
Attributes(Domain start_domain, Domain end_domain);
const Domain& start_domain() const { return start_domain_; }
const Domain& end_domain() const { return end_domain_; }
private:
/// The following domains constrain the dimension start/end cumul of the
/// the vehicle assigned to this resource:
/// start_domain_.Min() <= cumul[Start(v)] <= start_domain_.Max()
Domain start_domain_;
/// end_domain_.Min() <= cumul[End(v)] <= end_domain_.Max()
Domain end_domain_;
};
/// A Resource sets attributes (costs/constraints) for a set of dimensions.
class Resource {
public:
const ResourceGroup::Attributes& GetDimensionAttributes(
const RoutingDimension* dimension) const;
private:
explicit Resource(const RoutingModel* model) : model_(model) {}
void SetDimensionAttributes(ResourceGroup::Attributes attributes,
const RoutingDimension* dimension);
const ResourceGroup::Attributes& GetDefaultAttributes() const;
const RoutingModel* const model_;
absl::flat_hash_map<DimensionIndex, ResourceGroup::Attributes>
dimension_attributes_;
friend class ResourceGroup;
};
explicit ResourceGroup(const RoutingModel* model)
: model_(model), vehicle_requires_resource_(model->vehicles(), false) {}
/// Adds a Resource with the given attributes for the corresponding
/// dimension. Returns the index of the added resource in resources_.
int AddResource(Attributes attributes, const RoutingDimension* dimension);
/// Notifies that the given vehicle index requires a resource from this
/// group if the vehicle is used (i.e. if its route is non-empty or
/// vehicle_used_when_empty_[vehicle] is true).
void NotifyVehicleRequiresAResource(int vehicle);
const std::vector<int>& GetVehiclesRequiringAResource() const {
return vehicles_requiring_resource_;
}
bool VehicleRequiresAResource(int vehicle) const {
return vehicle_requires_resource_[vehicle];
}
const std::vector<Resource>& GetResources() const { return resources_; }
const Resource& GetResource(int resource_index) const {
DCHECK_LT(resource_index, resources_.size());
return resources_[resource_index];
}
const absl::flat_hash_set<DimensionIndex>& GetAffectedDimensionIndices()
const {
return affected_dimension_indices_;
}
int Size() const { return resources_.size(); }
private:
const RoutingModel* const model_;
std::vector<Resource> resources_;
std::vector<bool> vehicle_requires_resource_;
std::vector<int> vehicles_requiring_resource_;
/// All indices of dimensions affected by this resource group.
absl::flat_hash_set<DimensionIndex> affected_dimension_indices_;
};
/// Constant used to express a hard constraint instead of a soft penalty.
static const int64_t kNoPenalty;
/// Constant used to express the "no disjunction" index, returned when a node
/// does not appear in any disjunction.
static const DisjunctionIndex kNoDisjunction;
/// Constant used to express the "no dimension" index, returned when a
/// dimension name does not correspond to an actual dimension.
static const DimensionIndex kNoDimension;
/// Constructor taking an index manager. The version which does not take
/// RoutingModelParameters is equivalent to passing
/// DefaultRoutingModelParameters().
explicit RoutingModel(const RoutingIndexManager& index_manager);
RoutingModel(const RoutingIndexManager& index_manager,
const RoutingModelParameters& parameters);
~RoutingModel();
/// Registers 'callback' and returns its index.
int RegisterUnaryTransitVector(std::vector<int64_t> values);
int RegisterUnaryTransitCallback(TransitCallback1 callback);
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback);
int RegisterTransitMatrix(
std::vector<std::vector<int64_t> /*needed_for_swig*/> values);
int RegisterTransitCallback(TransitCallback2 callback);
int RegisterPositiveTransitCallback(TransitCallback2 callback);
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback);
const TransitCallback2& TransitCallback(int callback_index) const {
CHECK_LT(callback_index, transit_evaluators_.size());
return transit_evaluators_[callback_index];
}
const TransitCallback1& UnaryTransitCallbackOrNull(int callback_index) const {
CHECK_LT(callback_index, unary_transit_evaluators_.size());
return unary_transit_evaluators_[callback_index];
}
const VariableIndexEvaluator2& StateDependentTransitCallback(
int callback_index) const {
CHECK_LT(callback_index, state_dependent_transit_evaluators_.size());
return state_dependent_transit_evaluators_[callback_index];
}
/// Model creation
/// Methods to add dimensions to routes; dimensions represent quantities
/// accumulated at nodes along the routes. They represent quantities such as
/// weights or volumes carried along the route, or distance or times.
/// Quantities at a node are represented by "cumul" variables and the increase
/// or decrease of quantities between nodes are represented by "transit"
/// variables. These variables are linked as follows:
/// if j == next(i), cumul(j) = cumul(i) + transit(i) + slack(i)
/// where slack is a positive slack variable (can represent waiting times for
/// a time dimension).
/// Setting the value of fix_start_cumul_to_zero to true will force the
/// "cumul" variable of the start node of all vehicles to be equal to 0.
/// Creates a dimension where the transit variable is constrained to be
/// equal to evaluator(i, next(i)); 'slack_max' is the upper bound of the
/// slack variable and 'capacity' is the upper bound of the cumul variables.
/// 'name' is the name used to reference the dimension; this name is used to
/// get cumul and transit variables from the routing model.
/// Returns false if a dimension with the same name has already been created
/// (and doesn't create the new dimension).
/// Takes ownership of the callback 'evaluator'.
bool AddDimension(int evaluator_index, int64_t slack_max, int64_t capacity,
bool fix_start_cumul_to_zero, const std::string& name);
bool AddDimensionWithVehicleTransits(
const std::vector<int>& evaluator_indices, int64_t slack_max,
int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
bool AddDimensionWithVehicleCapacity(int evaluator_index, int64_t slack_max,
std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
const std::string& name);
bool AddDimensionWithVehicleTransitAndCapacity(
const std::vector<int>& evaluator_indices, int64_t slack_max,
std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
const std::string& name);
/// Creates a dimension where the transit variable is constrained to be
/// equal to 'value'; 'capacity' is the upper bound of the cumul variables.
/// 'name' is the name used to reference the dimension; this name is used to
/// get cumul and transit variables from the routing model.
/// Returns a pair consisting of an index to the registered unary transit
/// callback and a bool denoting whether the dimension has been created.
/// It is false if a dimension with the same name has already been created
/// (and doesn't create the new dimension but still register a new callback).
std::pair<int, bool> AddConstantDimensionWithSlack(
int64_t value, int64_t capacity, int64_t slack_max,
bool fix_start_cumul_to_zero, const std::string& name);
std::pair<int, bool> AddConstantDimension(int64_t value, int64_t capacity,
bool fix_start_cumul_to_zero,
const std::string& name) {
return AddConstantDimensionWithSlack(value, capacity, 0,
fix_start_cumul_to_zero, name);
}
/// Creates a dimension where the transit variable is constrained to be
/// equal to 'values[i]' for node i; 'capacity' is the upper bound of
/// the cumul variables. 'name' is the name used to reference the dimension;
/// this name is used to get cumul and transit variables from the routing
/// model.
/// Returns a pair consisting of an index to the registered unary transit
/// callback and a bool denoting whether the dimension has been created.
/// It is false if a dimension with the same name has already been created
/// (and doesn't create the new dimension but still register a new callback).
std::pair<int, bool> AddVectorDimension(std::vector<int64_t> values,
int64_t capacity,
bool fix_start_cumul_to_zero,
const std::string& name);
/// Creates a dimension where the transit variable is constrained to be
/// equal to 'values[i][next(i)]' for node i; 'capacity' is the upper bound of
/// the cumul variables. 'name' is the name used to reference the dimension;
/// this name is used to get cumul and transit variables from the routing
/// model.
/// Returns a pair consisting of an index to the registered transit callback
/// and a bool denoting whether the dimension has been created.
/// It is false if a dimension with the same name has already been created
/// (and doesn't create the new dimension but still register a new callback).
std::pair<int, bool> AddMatrixDimension(
std::vector<std::vector<int64_t> /*needed_for_swig*/> values,
int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name);
/// Creates a dimension with transits depending on the cumuls of another
/// dimension. 'pure_transits' are the per-vehicle fixed transits as above.
/// 'dependent_transits' is a vector containing for each vehicle an index to a
/// registered state dependent transit callback. 'base_dimension' indicates
/// the dimension from which the cumul variable is taken. If 'base_dimension'
/// is nullptr, then the newly created dimension is self-based.
bool AddDimensionDependentDimensionWithVehicleCapacity(
const std::vector<int>& pure_transits,
const std::vector<int>& dependent_transits,
const RoutingDimension* base_dimension, int64_t slack_max,
std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
const std::string& name) {
return AddDimensionDependentDimensionWithVehicleCapacityInternal(
pure_transits, dependent_transits, base_dimension, slack_max,
std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
}
/// As above, but pure_transits are taken to be zero evaluators.
bool AddDimensionDependentDimensionWithVehicleCapacity(
const std::vector<int>& transits, const RoutingDimension* base_dimension,
int64_t slack_max, std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero, const std::string& name);
/// Homogeneous versions of the functions above.
bool AddDimensionDependentDimensionWithVehicleCapacity(
int transit, const RoutingDimension* base_dimension, int64_t slack_max,
int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
const std::string& name);
bool AddDimensionDependentDimensionWithVehicleCapacity(
int pure_transit, int dependent_transit,
const RoutingDimension* base_dimension, int64_t slack_max,
int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
const std::string& name);
/// Creates a cached StateDependentTransit from an std::function.
static RoutingModel::StateDependentTransit MakeStateDependentTransit(
const std::function<int64_t(int64_t)>& f, int64_t domain_start,
int64_t domain_end);
/// For every vehicle of the routing model:
/// - if total_slacks[vehicle] is not nullptr, constrains it to be the sum of
/// slacks on that vehicle, that is,
/// dimension->CumulVar(end) - dimension->CumulVar(start) -
/// sum_{node in path of vehicle} dimension->FixedTransitVar(node).
/// - if spans[vehicle] is not nullptr, constrains it to be
/// dimension->CumulVar(end) - dimension->CumulVar(start)
/// This does stronger propagation than a decomposition, and takes breaks into
/// account.
Constraint* MakePathSpansAndTotalSlacks(const RoutingDimension* dimension,
std::vector<IntVar*> spans,
std::vector<IntVar*> total_slacks);
/// Outputs the names of all dimensions added to the routing engine.
// TODO(user): rename.
std::vector<std::string> GetAllDimensionNames() const;
/// Returns all dimensions of the model.
const std::vector<RoutingDimension*>& GetDimensions() const {
return dimensions_.get();
}
/// Returns dimensions with soft or vehicle span costs.
std::vector<RoutingDimension*> GetDimensionsWithSoftOrSpanCosts() const;
// clang-format off
/// Returns [global|local]_dimension_optimizers_, which are empty if the model
/// has not been closed.
const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer> >&
GetGlobalDimensionCumulOptimizers() const {
return global_dimension_optimizers_;
}
const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer> >&
GetGlobalDimensionCumulMPOptimizers() const {
return global_dimension_mp_optimizers_;
}
const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer> >&
GetLocalDimensionCumulOptimizers() const {
return local_dimension_optimizers_;
}
const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer> >&
GetLocalDimensionCumulMPOptimizers() const {
return local_dimension_mp_optimizers_;
}
// clang-format on
/// Returns the global/local dimension cumul optimizer for a given dimension,
/// or nullptr if there is none.
GlobalDimensionCumulOptimizer* GetMutableGlobalCumulOptimizer(
const RoutingDimension& dimension) const;
GlobalDimensionCumulOptimizer* GetMutableGlobalCumulMPOptimizer(
const RoutingDimension& dimension) const;
LocalDimensionCumulOptimizer* GetMutableLocalCumulOptimizer(
const RoutingDimension& dimension) const;
LocalDimensionCumulOptimizer* GetMutableLocalCumulMPOptimizer(
const RoutingDimension& dimension) const;
/// Returns true if a dimension exists for a given dimension name.
bool HasDimension(const std::string& dimension_name) const;
/// Returns a dimension from its name. Dies if the dimension does not exist.
const RoutingDimension& GetDimensionOrDie(
const std::string& dimension_name) const;
/// Returns a dimension from its name. Returns nullptr if the dimension does
/// not exist.
RoutingDimension* GetMutableDimension(
const std::string& dimension_name) const;
/// Set the given dimension as "primary constrained". As of August 2013, this
/// is only used by ArcIsMoreConstrainedThanArc().
/// "dimension" must be the name of an existing dimension, or be empty, in
/// which case there will not be a primary dimension after this call.
void SetPrimaryConstrainedDimension(const std::string& dimension_name) {
DCHECK(dimension_name.empty() || HasDimension(dimension_name));
primary_constrained_dimension_ = dimension_name;
}
/// Get the primary constrained dimension, or an empty string if it is unset.
const std::string& GetPrimaryConstrainedDimension() const {
return primary_constrained_dimension_;
}
/// Adds a resource group to the routing model. Returns its index in
/// resource_groups_.
int AddResourceGroup();
// clang-format off
const std::vector<std::unique_ptr<ResourceGroup> >& GetResourceGroups()
const {
return resource_groups_;
}
// clang-format on
ResourceGroup* GetResourceGroup(int rg_index) const {
DCHECK_LT(rg_index, resource_groups_.size());
return resource_groups_[rg_index].get();
}
/// Returns the indices of resource groups for this dimension. This method can
/// only be called after the model has been closed.
const std::vector<int>& GetDimensionResourceGroupIndices(
const RoutingDimension* dimension) const;
/// Returns the index of the resource group attached to the dimension.
/// DCHECKS that there's exactly one resource group for this dimension.
int GetDimensionResourceGroupIndex(const RoutingDimension* dimension) const {
DCHECK_EQ(GetDimensionResourceGroupIndices(dimension).size(), 1);
return GetDimensionResourceGroupIndices(dimension)[0];
}
/// Adds a disjunction constraint on the indices: exactly 'max_cardinality' of
/// the indices are active. Start and end indices of any vehicle cannot be
/// part of a disjunction.
///
/// If a penalty is given, at most 'max_cardinality' of the indices can be
/// active, and if less are active, 'penalty' is payed per inactive index.
/// This is equivalent to adding the constraint:
/// p + Sum(i)active[i] == max_cardinality
/// where p is an integer variable, and the following cost to the cost
/// function:
/// p * penalty.
/// 'penalty' must be positive to make the disjunction optional; a negative
/// penalty will force 'max_cardinality' indices of the disjunction to be
/// performed, and therefore p == 0.
/// Note: passing a vector with a single index will model an optional index
/// with a penalty cost if it is not visited.
DisjunctionIndex AddDisjunction(const std::vector<int64_t>& indices,
int64_t penalty = kNoPenalty,
int64_t max_cardinality = 1);
/// Returns the indices of the disjunctions to which an index belongs.
const std::vector<DisjunctionIndex>& GetDisjunctionIndices(
int64_t index) const {
return index_to_disjunctions_[index];
}
/// Calls f for each variable index of indices in the same disjunctions as the
/// node corresponding to the variable index 'index'; only disjunctions of
/// cardinality 'cardinality' are considered.
template <typename F>
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(
int64_t index, int64_t max_cardinality, F f) const {
for (const DisjunctionIndex disjunction : GetDisjunctionIndices(index)) {
if (disjunctions_[disjunction].value.max_cardinality == max_cardinality) {
for (const int64_t d_index : disjunctions_[disjunction].indices) {
f(d_index);
}
}
}
}
#if !defined(SWIGPYTHON)
/// Returns the variable indices of the nodes in the disjunction of index
/// 'index'.
const std::vector<int64_t>& GetDisjunctionNodeIndices(
DisjunctionIndex index) const {
return disjunctions_[index].indices;
}
#endif // !defined(SWIGPYTHON)
/// Returns the penalty of the node disjunction of index 'index'.
int64_t GetDisjunctionPenalty(DisjunctionIndex index) const {
return disjunctions_[index].value.penalty;
}
/// Returns the maximum number of possible active nodes of the node
/// disjunction of index 'index'.
int64_t GetDisjunctionMaxCardinality(DisjunctionIndex index) const {
return disjunctions_[index].value.max_cardinality;
}
/// Returns the number of node disjunctions in the model.
int GetNumberOfDisjunctions() const { return disjunctions_.size(); }
/// Returns true if the model contains mandatory disjunctions (ones with
/// kNoPenalty as penalty).
bool HasMandatoryDisjunctions() const;
/// Returns true if the model contains at least one disjunction which is
/// constrained by its max_cardinality.
bool HasMaxCardinalityConstrainedDisjunctions() const;
/// Returns the list of all perfect binary disjunctions, as pairs of variable
/// indices: a disjunction is "perfect" when its variables do not appear in
/// any other disjunction. Each pair is sorted (lowest variable index first),
/// and the output vector is also sorted (lowest pairs first).
std::vector<std::pair<int64_t, int64_t>> GetPerfectBinaryDisjunctions() const;
/// SPECIAL: Makes the solver ignore all the disjunctions whose active
/// variables are all trivially zero (i.e. Max() == 0), by setting their
/// max_cardinality to 0.
/// This can be useful when using the BaseBinaryDisjunctionNeighborhood
/// operators, in the context of arc-based routing.
void IgnoreDisjunctionsAlreadyForcedToZero();
/// Adds a soft constraint to force a set of variable indices to be on the
/// same vehicle. If all nodes are not on the same vehicle, each extra vehicle
/// used adds 'cost' to the cost function.
void AddSoftSameVehicleConstraint(const std::vector<int64_t>& indices,
int64_t cost);
/// Sets the vehicles which can visit a given node. If the node is in a
/// disjunction, this will not prevent it from being unperformed.
/// Specifying an empty vector of vehicles has no effect (all vehicles
/// will be allowed to visit the node).
void SetAllowedVehiclesForIndex(const std::vector<int>& vehicles,
int64_t index);
/// Returns true if a vehicle is allowed to visit a given node.
bool IsVehicleAllowedForIndex(int vehicle, int64_t index) {
return allowed_vehicles_[index].empty() ||
allowed_vehicles_[index].find(vehicle) !=
allowed_vehicles_[index].end();
}
/// Notifies that index1 and index2 form a pair of nodes which should belong
/// to the same route. This methods helps the search find better solutions,
/// especially in the local search phase.
/// It should be called each time you have an equality constraint linking
/// the vehicle variables of two node (including for instance pickup and
/// delivery problems):
/// Solver* const solver = routing.solver();
/// int64_t index1 = manager.NodeToIndex(node1);
/// int64_t index2 = manager.NodeToIndex(node2);
/// solver->AddConstraint(solver->MakeEquality(
/// routing.VehicleVar(index1),
/// routing.VehicleVar(index2)));
/// routing.AddPickupAndDelivery(index1, index2);
///
// TODO(user): Remove this when model introspection detects linked nodes.
void AddPickupAndDelivery(int64_t pickup, int64_t delivery);
/// Same as AddPickupAndDelivery but notifying that the performed node from
/// the disjunction of index 'pickup_disjunction' is on the same route as the
/// performed node from the disjunction of index 'delivery_disjunction'.
void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction,
DisjunctionIndex delivery_disjunction);
// clang-format off
/// Returns pairs for which the node is a pickup; the first element of each
/// pair is the index in the pickup and delivery pairs list in which the
/// pickup appears, the second element is its index in the pickups list.
const std::vector<std::pair<int, int> >&
GetPickupIndexPairs(int64_t node_index) const;
/// Same as above for deliveries.
const std::vector<std::pair<int, int> >&
GetDeliveryIndexPairs(int64_t node_index) const;
// clang-format on
/// Sets the Pickup and delivery policy of all vehicles. It is equivalent to
/// calling SetPickupAndDeliveryPolicyOfVehicle on all vehicles.
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy);
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy,
int vehicle);
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(
int vehicle) const;
/// Returns the number of non-start/end nodes which do not appear in a
/// pickup/delivery pair.
int GetNumOfSingletonNodes() const;
#ifndef SWIG
/// Returns pickup and delivery pairs currently in the model.
const IndexPairs& GetPickupAndDeliveryPairs() const {
return pickup_delivery_pairs_;
}
const std::vector<std::pair<DisjunctionIndex, DisjunctionIndex>>&
GetPickupAndDeliveryDisjunctions() const {
return pickup_delivery_disjunctions_;
}
/// Returns implicit pickup and delivery pairs currently in the model.
/// Pairs are implicit if they are not linked by a pickup and delivery
/// constraint but that for a given unary dimension, the first element of the
/// pair has a positive demand d, and the second element has a demand of -d.
const IndexPairs& GetImplicitUniquePickupAndDeliveryPairs() const {
DCHECK(closed_);
return implicit_pickup_delivery_pairs_without_alternatives_;
}
#endif // SWIG
/// Set the node visit types and incompatibilities/requirements between the
/// types (see below).
///
/// NOTE: Before adding any incompatibilities and/or requirements on types:
/// 1) All corresponding node types must have been set.
/// 2) CloseVisitTypes() must be called so all containers are resized
/// accordingly.
///
/// The following enum is used to describe how a node with a given type 'T'
/// impacts the number of types 'T' on the route when visited, and thus
/// determines how temporal incompatibilities and requirements take effect.
enum VisitTypePolicy {
/// When visited, the number of types 'T' on the vehicle increases by one.
TYPE_ADDED_TO_VEHICLE,
/// When visited, one instance of type 'T' previously added to the route
/// (TYPE_ADDED_TO_VEHICLE), if any, is removed from the vehicle.
/// If the type was not previously added to the route or all added instances
/// have already been removed, this visit has no effect on the types.
ADDED_TYPE_REMOVED_FROM_VEHICLE,
/// With the following policy, the visit enforces that type 'T' is
/// considered on the route from its start until this node is visited.
TYPE_ON_VEHICLE_UP_TO_VISIT,
/// The visit doesn't have an impact on the number of types 'T' on the
/// route, as it's (virtually) added and removed directly.
/// This policy can be used for visits which are part of an incompatibility
/// or requirement set without affecting the type count on the route.
TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
};
// TODO(user): Support multiple visit types per node?
void SetVisitType(int64_t index, int type, VisitTypePolicy type_policy);
int GetVisitType(int64_t index) const;
const std::vector<int>& GetSingleNodesOfType(int type) const;
const std::vector<int>& GetPairIndicesOfType(int type) const;
VisitTypePolicy GetVisitTypePolicy(int64_t index) const;
/// This function should be called once all node visit types have been set and
/// prior to adding any incompatibilities/requirements.
// TODO(user): Reconsider the logic and potentially remove the need to
/// "close" types.
void CloseVisitTypes();
int GetNumberOfVisitTypes() const { return num_visit_types_; }
#ifndef SWIG
const std::vector<std::vector<int>>& GetTopologicallySortedVisitTypes()
const {
DCHECK(closed_);
return topologically_sorted_visit_types_;
}
#endif // SWIG
/// Incompatibilities:
/// Two nodes with "hard" incompatible types cannot share the same route at
/// all, while with a "temporal" incompatibility they can't be on the same
/// route at the same time.
void AddHardTypeIncompatibility(int type1, int type2);
void AddTemporalTypeIncompatibility(int type1, int type2);
/// Returns visit types incompatible with a given type.
const absl::flat_hash_set<int>& GetHardTypeIncompatibilitiesOfType(
int type) const;
const absl::flat_hash_set<int>& GetTemporalTypeIncompatibilitiesOfType(
int type) const;
/// Returns true iff any hard (resp. temporal) type incompatibilities have
/// been added to the model.
bool HasHardTypeIncompatibilities() const {
return has_hard_type_incompatibilities_;
}
bool HasTemporalTypeIncompatibilities() const {
return has_temporal_type_incompatibilities_;
}
/// Requirements:
/// NOTE: As of 2019-04, cycles in the requirement graph are not supported,
/// and lead to the dependent nodes being skipped if possible (otherwise
/// the model is considered infeasible).
/// The following functions specify that "dependent_type" requires at least
/// one of the types in "required_type_alternatives".
///
/// For same-vehicle requirements, a node of dependent type type_D requires at
/// least one node of type type_R among the required alternatives on the same
/// route.
void AddSameVehicleRequiredTypeAlternatives(
int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
/// If type_D depends on type_R when adding type_D, any node_D of type_D and
/// VisitTypePolicy TYPE_ADDED_TO_VEHICLE or
/// TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED requires at least one type_R on its
/// vehicle at the time node_D is visited.
void AddRequiredTypeAlternativesWhenAddingType(
int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
/// The following requirements apply when visiting dependent nodes that remove
/// their type from the route, i.e. type_R must be on the vehicle when type_D
/// of VisitTypePolicy ADDED_TYPE_REMOVED_FROM_VEHICLE,
/// TYPE_ON_VEHICLE_UP_TO_VISIT or TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED is
/// visited.
void AddRequiredTypeAlternativesWhenRemovingType(
int dependent_type, absl::flat_hash_set<int> required_type_alternatives);
// clang-format off
/// Returns the set of same-vehicle requirement alternatives for the given
/// type.
const std::vector<absl::flat_hash_set<int> >&
GetSameVehicleRequiredTypeAlternativesOfType(int type) const;
/// Returns the set of requirement alternatives when adding the given type.
const std::vector<absl::flat_hash_set<int> >&
GetRequiredTypeAlternativesWhenAddingType(int type) const;
/// Returns the set of requirement alternatives when removing the given type.
const std::vector<absl::flat_hash_set<int> >&
GetRequiredTypeAlternativesWhenRemovingType(int type) const;
// clang-format on
/// Returns true iff any same-route (resp. temporal) type requirements have
/// been added to the model.
bool HasSameVehicleTypeRequirements() const {
return has_same_vehicle_type_requirements_;
}