-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
Copy pathrouting.h
2728 lines (2550 loc) · 128 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-2018 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 MyDistance(int64 from, int64 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(int64, int64)>
// 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 node = routing.Start(route_number);
// !routing.IsEnd(node);
// node = solution->Value(routing.NextVar(node))) {
// LOG(INFO) << routing.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 <cstddef>
#include <functional>
#include <memory>
#include <queue>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/hash/hash.h"
#include "ortools/base/adjustable_priority_queue-inl.h"
#include "ortools/base/adjustable_priority_queue.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/hash.h"
#include "ortools/base/int_type_indexed_vector.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.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/glop/lp_solver.h"
#include "ortools/graph/graph.h"
#include "ortools/lp_data/lp_data.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/sat/theta_tree.h"
#include "ortools/util/range_query_function.h"
#include "ortools/util/sorted_interval_list.h"
namespace operations_research {
class IntVarFilteredDecisionBuilder;
class LocalSearchOperator;
class RoutingDimension;
#ifndef SWIG
using util::ReverseArcListGraph;
class SweepArranger;
#endif
struct SweepIndex;
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
};
#ifndef SWIG
// Types of precedence policy applied to pickup and delivery pairs.
enum class PickupAndDeliveryPolicy {
// Any precedence is accepted.
ANY,
// Deliveries must be performed in reverse order of pickups.
LIFO,
// Deliveries must be performed in the same order as pickups.
FIFO
};
#endif // SWIG
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->int64
// 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, int64)>
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 transit_evaluator_class;
int64 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 fixed_cost;
// 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.
gtl::ITIVector<DimensionIndex, int64> dimension_start_cumuls_min;
gtl::ITIVector<DimensionIndex, int64> dimension_start_cumuls_max;
gtl::ITIVector<DimensionIndex, int64> dimension_end_cumuls_min;
gtl::ITIVector<DimensionIndex, int64> dimension_end_cumuls_max;
gtl::ITIVector<DimensionIndex, int64> dimension_capacities;
// dimension_evaluators[d]->Run(from, to) is the transit value of arc
// from->to for a dimension d.
gtl::ITIVector<DimensionIndex, int64> dimension_evaluator_classes;
// Fingerprint of unvisitable non-start/end nodes.
uint64 unvisitable_nodes_fprint;
// Comparator for STL containers and algorithms.
static bool LessThan(const VehicleClass& a, const VehicleClass& b);
};
#endif // defined(SWIG)
// Constant used to express a hard constraint instead of a soft penalty.
static const int64 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 RegisterUnaryTransitCallback(TransitCallback1 callback);
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 slack_max, int64 capacity,
bool fix_start_cumul_to_zero, const std::string& name);
bool AddDimensionWithVehicleTransits(
const std::vector<int>& evaluator_indices, int64 slack_max,
int64 capacity, bool fix_start_cumul_to_zero, const std::string& name);
bool AddDimensionWithVehicleCapacity(int evaluator_index, int64 slack_max,
std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
const std::string& name);
bool AddDimensionWithVehicleTransitAndCapacity(
const std::vector<int>& evaluator_indices, int64 slack_max,
std::vector<int64> 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 false if a dimension with the same name has already been created
// (and doesn't create the new dimension).
bool AddConstantDimensionWithSlack(int64 value, int64 capacity,
int64 slack_max,
bool fix_start_cumul_to_zero,
const std::string& name);
bool AddConstantDimension(int64 value, int64 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 false if a dimension with the same name has already been created
// (and doesn't create the new dimension).
bool AddVectorDimension(std::vector<int64> values, int64 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 false if a dimension with the same name has already been created
// (and doesn't create the new dimension).
bool AddMatrixDimension(std::vector<std::vector<int64>> values,
int64 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 slack_max,
std::vector<int64> 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 slack_max, std::vector<int64> 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 slack_max,
int64 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 slack_max,
int64 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(int64)>& f, int64 domain_start,
int64 domain_end);
// 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 and vehicle span costs.
std::vector<RoutingDimension*> GetDimensionsWithSoftAndSpanCosts() const;
// Returns dimensions with soft or vehicle span costs.
std::vector<RoutingDimension*> GetDimensionsWithSoftOrSpanCosts() 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 std::string if it is
// unset.
const std::string& GetPrimaryConstrainedDimension() const {
return primary_constrained_dimension_;
}
// 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>& indices,
int64 penalty = kNoPenalty,
int64 max_cardinality = 1);
// Returns the indices of the disjunctions to which an index belongs.
const std::vector<DisjunctionIndex>& GetDisjunctionIndices(
int64 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 index, int64 max_cardinality, F f) const {
for (const DisjunctionIndex disjunction : GetDisjunctionIndices(index)) {
if (disjunctions_[disjunction].value.max_cardinality == max_cardinality) {
for (const int64 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>& GetDisjunctionIndices(
DisjunctionIndex index) const {
return disjunctions_[index].indices;
}
#endif // !defined(SWIGPYTHON)
// Returns the penalty of the node disjunction of index 'index'.
int64 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 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 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, int64>> 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 contraint 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>& indices,
int64 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 index);
// Returns true if a vehicle is allowed to visit a given node.
bool IsVehicleAllowedForIndex(int vehicle, int64 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 index1 = manager.NodeToIndex(node1);
// int64 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 pickup, int64 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 node_index) const;
// Same as above for deliveries.
const std::vector<std::pair<int, int> >&
GetDeliveryIndexPairs(int64 node_index) const;
// clang-format on
#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 the number of non-start/end nodes which do not appear in a
// pickup/delivery pair.
int GetNumOfSingletonNodes() const;
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy,
int vehicle) {
vehicle_pickup_delivery_policy_[vehicle] = policy;
}
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(
int vehicle) const {
return vehicle_pickup_delivery_policy_[vehicle];
}
#endif // SWIG
// Set the node visit types and incompatibilities between the types.
// Two nodes with incompatible types cannot be visited by the same vehicle.
// TODO(user): Forbid incompatible types from being on the same route at
// the same time (instead of at any time).
// The visit type of a node must be positive.
// TODO(user): Support multiple visit types per node?
void SetVisitType(int64 index, int type);
int GetVisitType(int64 index) const;
void AddTypeIncompatibility(int type1, int type2);
// Returns visit types incompatible to a given type.
const absl::flat_hash_set<int>& GetTypeIncompatibilities(int type) const;
int GetNumberOfVisitTypes() const { return num_visit_types_; }
// Get the "unperformed" penalty of a node. This is only well defined if the
// node is only part of a single Disjunction involving only itself, and that
// disjunction has a penalty. In all other cases, including forced active
// nodes, this returns 0.
int64 UnperformedPenalty(int64 var_index) const;
// Same as above except that it returns default_value instead of 0 when
// penalty is not well defined (default value is passed as first argument to
// simplify the usage of the method in a callback).
int64 UnperformedPenaltyOrValue(int64 default_value, int64 var_index) const;
// Returns the variable index of the first starting or ending node of all
// routes. If all routes start and end at the same node (single depot), this
// is the node returned.
int64 GetDepot() const;
// Sets the cost function of the model such that the cost of a segment of a
// route between node 'from' and 'to' is evaluator(from, to), whatever the
// route or vehicle performing the route.
void SetArcCostEvaluatorOfAllVehicles(int evaluator_index);
// Sets the cost function for a given vehicle route.
void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle);
// Sets the fixed cost of all vehicle routes. It is equivalent to calling
// SetFixedCostOfVehicle on all vehicle routes.
void SetFixedCostOfAllVehicles(int64 cost);
// Sets the fixed cost of one vehicle route.
void SetFixedCostOfVehicle(int64 cost, int vehicle);
// Returns the route fixed cost taken into account if the route of the
// vehicle is not empty, aka there's at least one node on the route other than
// the first and last nodes.
int64 GetFixedCostOfVehicle(int vehicle) const;
// The following methods set the linear and quadratic cost factors of vehicles
// (must be positive values). The default value of these parameters is zero
// for all vehicles.
// When set, the cost_ of the model will contain terms aiming at reducing the
// number of vehicles used in the model, by adding the following to the
// objective for every vehicle v:
// INDICATOR(v used in the model) *
// [linear_cost_factor_of_vehicle_[v]
// - quadratic_cost_factor_of_vehicle_[v]*(square of length of route v)]
// i.e. for every used vehicle, we add the linear factor as fixed cost, and
// subtract the square of the route length multiplied by the quadratic factor.
// This second term aims at making the routes as dense as possible.
//
// Sets the linear and quadratic cost factor of all vehicles.
void SetAmortizedCostFactorsOfAllVehicles(int64 linear_cost_factor,
int64 quadratic_cost_factor);
// Sets the linear and quadratic cost factor of the given vehicle.
void SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor,
int64 quadratic_cost_factor,
int vehicle);
const std::vector<int64>& GetAmortizedLinearCostFactorOfVehicles() const {
return linear_cost_factor_of_vehicle_;
}
const std::vector<int64>& GetAmortizedQuadraticCostFactorOfVehicles() const {
return quadratic_cost_factor_of_vehicle_;
}
// Search
// Gets/sets the evaluator used during the search. Only relevant when
// RoutingSearchParameters.first_solution_strategy = EVALUATOR_STRATEGY.
#ifndef SWIG
const Solver::IndexEvaluator2& first_solution_evaluator() const {
return first_solution_evaluator_;
}
#endif
// Takes ownership of evaluator.
void SetFirstSolutionEvaluator(Solver::IndexEvaluator2 evaluator) {
first_solution_evaluator_ = std::move(evaluator);
}
// Adds a local search operator to the set of operators used to solve the
// vehicle routing problem.
void AddLocalSearchOperator(LocalSearchOperator* ls_operator);
// Adds a search monitor to the search used to solve the routing model.
void AddSearchMonitor(SearchMonitor* const monitor);
// Adds a callback called each time a solution is found during the search.
// This is a shortcut to creating a monitor to call the callback on
// AtSolution() and adding it with AddSearchMonitor.
void AddAtSolutionCallback(std::function<void()> callback);
// Adds a variable to minimize in the solution finalizer. The solution
// finalizer is called each time a solution is found during the search and
// allows to instantiate secondary variables (such as dimension cumul
// variables).
void AddVariableMinimizedByFinalizer(IntVar* var);
// Adds a variable to maximize in the solution finalizer (see above for
// information on the solution finalizer).
void AddVariableMaximizedByFinalizer(IntVar* var);
// Closes the current routing model; after this method is called, no
// modification to the model can be done, but RoutesToAssignment becomes
// available. Note that CloseModel() is automatically called by Solve() and
// other methods that produce solution.
// This is equivalent to calling
// CloseModelWithParameters(DefaultRoutingSearchParameters()).
void CloseModel();
// Same as above taking search parameters (as of 10/2015 some the parameters
// have to be set when closing the model).
void CloseModelWithParameters(
const RoutingSearchParameters& search_parameters);
// Solves the current routing model; closes the current model.
// This is equivalent to calling
// SolveWithParameters(DefaultRoutingSearchParameters())
// or
// SolveFromAssignmentWithParameters(assignment,
// DefaultRoutingSearchParameters()).
const Assignment* Solve(const Assignment* assignment = nullptr);
// Solves the current routing model with the given parameters. If 'solutions'
// is specified, it will contain the k best solutions found during the search
// (from worst to best, including the one returned by this method), where k
// corresponds to the 'number_of_solutions_to_collect' in 'search_parameters'.
// Note that the Assignment returned by the method and the ones in solutions
// are owned by the underlying solver and should not be deleted.
const Assignment* SolveWithParameters(
const RoutingSearchParameters& search_parameters,
std::vector<const Assignment*>* solutions = nullptr);
const Assignment* SolveFromAssignmentWithParameters(
const Assignment* assignment,
const RoutingSearchParameters& search_parameters,
std::vector<const Assignment*>* solutions = nullptr);
// Given a "source_model" and its "source_assignment", resets
// "target_assignment" with the IntVar variables (nexts_, and vehicle_vars_
// if costs aren't homogeneous across vehicles) of "this" model, with the
// values set according to those in "other_assignment".
// The objective_element of target_assignment is set to this->cost_.
void SetAssignmentFromOtherModelAssignment(
Assignment* target_assignment, const RoutingModel* source_model,
const Assignment* source_assignment);
// Computes a lower bound to the routing problem solving a linear assignment
// problem. The routing model must be closed before calling this method.
// Note that problems with node disjunction constraints (including optional
// nodes) and non-homogenous costs are not supported (the method returns 0 in
// these cases).
// TODO(user): Add support for non-homogeneous costs and disjunctions.
int64 ComputeLowerBound();
// Returns the current status of the routing model.
Status status() const { return status_; }
// Applies a lock chain to the next search. 'locks' represents an ordered
// vector of nodes representing a partial route which will be fixed during the
// next search; it will constrain next variables such that:
// next[locks[i]] == locks[i+1].
// Returns the next variable at the end of the locked chain; this variable is
// not locked. An assignment containing the locks can be obtained by calling
// PreAssignment().
IntVar* ApplyLocks(const std::vector<int64>& locks);
// Applies lock chains to all vehicles to the next search, such that locks[p]
// is the lock chain for route p. Returns false if the locks do not contain
// valid routes; expects that the routes do not contain the depots,
// i.e. there are empty vectors in place of empty routes.
// If close_routes is set to true, adds the end nodes to the route of each
// vehicle and deactivates other nodes.
// An assignment containing the locks can be obtained by calling
// PreAssignment().
bool ApplyLocksToAllVehicles(const std::vector<std::vector<int64>>& locks,
bool close_routes);
// Returns an assignment used to fix some of the variables of the problem.
// In practice, this assignment locks partial routes of the problem. This
// can be used in the context of locking the parts of the routes which have
// already been driven in online routing problems.
const Assignment* const PreAssignment() const { return preassignment_; }
Assignment* MutablePreAssignment() { return preassignment_; }
// Writes the current solution to a file containing an AssignmentProto.
// Returns false if the file cannot be opened or if there is no current
// solution.
bool WriteAssignment(const std::string& file_name) const;
// Reads an assignment from a file and returns the current solution.
// Returns nullptr if the file cannot be opened or if the assignment is not
// valid.
Assignment* ReadAssignment(const std::string& file_name);
// Restores an assignment as a solution in the routing model and returns the
// new solution. Returns nullptr if the assignment is not valid.
Assignment* RestoreAssignment(const Assignment& solution);
// Restores the routes as the current solution. Returns nullptr if the
// solution cannot be restored (routes do not contain a valid solution). Note
// that calling this method will run the solver to assign values to the
// dimension variables; this may take considerable amount of time, especially
// when using dimensions with slack.
Assignment* ReadAssignmentFromRoutes(
const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices);
// Fills an assignment from a specification of the routes of the
// vehicles. The routes are specified as lists of variable indices that appear
// on the routes of the vehicles. The indices of the outer vector in
// 'routes' correspond to vehicles IDs, the inner vector contains the
// variable indices on the routes for the given vehicle. The inner vectors
// must not contain the start and end indices, as these are determined by the
// routing model. Sets the value of NextVars in the assignment, adding the
// variables to the assignment if necessary. The method does not touch other
// variables in the assignment. The method can only be called after the model
// is closed. With ignore_inactive_indices set to false, this method will
// fail (return nullptr) in case some of the route contain indices that are
// deactivated in the model; when set to true, these indices will be
// skipped. Returns true if routes were successfully
// loaded. However, such assignment still might not be a valid
// solution to the routing problem due to more complex constraints;
// it is advisible to call solver()->CheckSolution() afterwards.
bool RoutesToAssignment(const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices, bool close_routes,
Assignment* const assignment) const;
// Converts the solution in the given assignment to routes for all vehicles.
// Expects that assignment contains a valid solution (i.e. routes for all
// vehicles end with an end index for that vehicle).
void AssignmentToRoutes(const Assignment& assignment,
std::vector<std::vector<int64>>* const routes) const;
// Returns a compacted version of the given assignment, in which all vehicles
// with id lower or equal to some N have non-empty routes, and all vehicles
// with id greater than N have empty routes. Does not take ownership of the
// returned object.
// If found, the cost of the compact assignment is the same as in the
// original assignment and it preserves the values of 'active' variables.
// Returns nullptr if a compact assignment was not found.
// This method only works in homogenous mode, and it only swaps equivalent
// vehicles (vehicles with the same start and end nodes). When creating the
// compact assignment, the empty plan is replaced by the route assigned to the
// compatible vehicle with the highest id. Note that with more complex
// constraints on vehicle variables, this method might fail even if a compact
// solution exists.
// This method changes the vehicle and dimension variables as necessary.
// While compacting the solution, only basic checks on vehicle variables are
// performed; if one of these checks fails no attempts to repair it are made
// (instead, the method returns nullptr).
Assignment* CompactAssignment(const Assignment& assignment) const;
// Same as CompactAssignment() but also checks the validity of the final
// compact solution; if it is not valid, no attempts to repair it are made
// (instead, the method returns nullptr).
Assignment* CompactAndCheckAssignment(const Assignment& assignment) const;
// Adds an extra variable to the vehicle routing assignment.
void AddToAssignment(IntVar* const var);
void AddIntervalToAssignment(IntervalVar* const interval);
#ifndef SWIG
// TODO(user): Revisit if coordinates are added to the RoutingModel class.
void SetSweepArranger(SweepArranger* sweep_arranger) {
sweep_arranger_.reset(sweep_arranger);
}
// Returns the sweep arranger to be used by routing heuristics.
SweepArranger* sweep_arranger() const { return sweep_arranger_.get(); }
#endif
// Adds a custom local search filter to the list of filters used to speed up
// local search by pruning unfeasible variable assignments.
// Calling this method after the routing model has been closed (CloseModel()
// or Solve() has been called) has no effect.
// The routing model does not take ownership of the filter.
void AddLocalSearchFilter(LocalSearchFilter* filter) {
CHECK(filter != nullptr);
if (closed_) {
LOG(WARNING) << "Model is closed, filter addition will be ignored.";
}
extra_filters_.push_back(filter);
}
// Model inspection.
// Returns the variable index of the starting node of a vehicle route.
int64 Start(int vehicle) const { return starts_[vehicle]; }
// Returns the variable index of the ending node of a vehicle route.
int64 End(int vehicle) const { return ends_[vehicle]; }
// Returns true if 'index' represents the first node of a route.
bool IsStart(int64 index) const;
// Returns true if 'index' represents the last node of a route.
bool IsEnd(int64 index) const { return index >= Size(); }
// Assignment inspection
// Returns the variable index of the node directly after the node
// corresponding to 'index' in 'assignment'.
int64 Next(const Assignment& assignment, int64 index) const;
// Returns true if the route of 'vehicle' is non empty in 'assignment'.
bool IsVehicleUsed(const Assignment& assignment, int vehicle) const;
// Variables
#if !defined(SWIGPYTHON)
// Returns all next variables of the model, such that Nexts(i) is the next
// variable of the node corresponding to i.
const std::vector<IntVar*>& Nexts() const { return nexts_; }
// Returns all vehicle variables of the model, such that VehicleVars(i) is
// the vehicle variable of the node corresponding to i.
const std::vector<IntVar*>& VehicleVars() const { return vehicle_vars_; }
#endif // !defined(SWIGPYTHON)
// Returns the next variable of the node corresponding to index. Note that
// NextVar(index) == index is equivalent to ActiveVar(index) == 0.
IntVar* NextVar(int64 index) const { return nexts_[index]; }
// Returns the active variable of the node corresponding to index.
IntVar* ActiveVar(int64 index) const { return active_[index]; }
// Returns the vehicle variable of the node corresponding to index. Note that
// VehicleVar(index) == -1 is equivalent to ActiveVar(index) == 0.
IntVar* VehicleVar(int64 index) const { return vehicle_vars_[index]; }
// Returns the global cost variable which is being minimized.
IntVar* CostVar() const { return cost_; }
// Returns the cost of the transit arc between two nodes for a given vehicle.
// Input are variable indices of node. This returns 0 if vehicle < 0.
int64 GetArcCostForVehicle(int64 from_index, int64 to_index, int64 vehicle);
// Whether costs are homogeneous across all vehicles.
bool CostsAreHomogeneousAcrossVehicles() const {
return costs_are_homogeneous_across_vehicles_;
}
// Returns the cost of the segment between two nodes supposing all vehicle
// costs are the same (returns the cost for the first vehicle otherwise).
int64 GetHomogeneousCost(int64 from_index, int64 to_index) {
return GetArcCostForVehicle(from_index, to_index, /*vehicle=*/0);
}
// Returns the cost of the arc in the context of the first solution strategy.
// This is typically a simplification of the actual cost; see the .cc.
int64 GetArcCostForFirstSolution(int64 from_index, int64 to_index);
// Returns the cost of the segment between two nodes for a given cost
// class. Input are variable indices of nodes and the cost class.
// Unlike GetArcCostForVehicle(), if cost_class is kNoCost, then the
// returned cost won't necessarily be zero: only some of the components
// of the cost that depend on the cost class will be omited. See the code
// for details.
int64 GetArcCostForClass(int64 from_index, int64 to_index,
int64 /*CostClassIndex*/ cost_class_index);
// Get the cost class index of the given vehicle.
CostClassIndex GetCostClassIndexOfVehicle(int64 vehicle) const {
DCHECK(closed_);
return cost_class_index_of_vehicle_[vehicle];
}
// Returns true iff the model contains a vehicle with the given
// cost_class_index.
bool HasVehicleWithCostClassIndex(CostClassIndex cost_class_index) const {
DCHECK(closed_);
if (cost_class_index == kCostClassIndexOfZeroCost) {
return has_vehicle_with_zero_cost_class_;
}
return cost_class_index < cost_classes_.size();
}
// Returns the number of different cost classes in the model.
int GetCostClassesCount() const { return cost_classes_.size(); }
// Ditto, minus the 'always zero', built-in cost class.
int GetNonZeroCostClassesCount() const {
return std::max(0, GetCostClassesCount() - 1);