small improvements to the routing library; fix disjunctive constraints with variable durations intervals
This commit is contained in:
@@ -3262,7 +3262,9 @@ IntExpr* Solver::MakeSum(const std::vector<IntVar*>& vars) {
|
||||
IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
|
||||
const int size = vars.size();
|
||||
if (size == 0) {
|
||||
return MakeIntConst(0LL);
|
||||
LOG(WARNING) << "operations_research::Solver::MakeMin() was called with an "
|
||||
"empty list of variables. Was this intentional?";
|
||||
return MakeIntConst(kint64max);
|
||||
} else if (size == 1) {
|
||||
return vars[0];
|
||||
} else if (size == 2) {
|
||||
@@ -3303,7 +3305,9 @@ IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
|
||||
IntExpr* Solver::MakeMax(const std::vector<IntVar*>& vars) {
|
||||
const int size = vars.size();
|
||||
if (size == 0) {
|
||||
return MakeIntConst(0LL);
|
||||
LOG(WARNING) << "operations_research::Solver::MakeMax() was called with an "
|
||||
"empty list of variables. Was this intentional?";
|
||||
return MakeIntConst(kint64min);
|
||||
} else if (size == 1) {
|
||||
return vars[0];
|
||||
} else if (size == 2) {
|
||||
@@ -3357,7 +3361,9 @@ Constraint* Solver::MakeMinEquality(const std::vector<IntVar*>& vars,
|
||||
} else if (size == 1) {
|
||||
return MakeEquality(vars[0], min_var);
|
||||
} else {
|
||||
return MakeEquality(min_var, Zero());
|
||||
LOG(WARNING) << "operations_research::Solver::MakeMinEquality() was called "
|
||||
"with an empty list of variables. Was this intentional?";
|
||||
return MakeEquality(min_var, kint64max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3377,7 +3383,9 @@ Constraint* Solver::MakeMaxEquality(const std::vector<IntVar*>& vars,
|
||||
} else if (size == 1) {
|
||||
return MakeEquality(vars[0], max_var);
|
||||
} else {
|
||||
return MakeEquality(max_var, Zero());
|
||||
LOG(WARNING) << "operations_research::Solver::MakeMaxEquality() was called "
|
||||
"with an empty list of variables. Was this intentional?";
|
||||
return MakeEquality(max_var, kint64min);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -962,22 +962,8 @@ Constraint* Solver::MakeNotBetweenCt(IntExpr* e, int64 l, int64 u) {
|
||||
// Catch one-sided constraints.
|
||||
if (emin >= l) return MakeGreater(e, u);
|
||||
if (emax <= u) return MakeLess(e, l);
|
||||
// // Simplify the common factor, if any.
|
||||
// TODO(user): Add back simplification.
|
||||
// int64 coeff = ExtractExprProductCoeff(&e);
|
||||
// if (coeff != 1) {
|
||||
// CHECK_NE(coeff, 0); // Would have been caught by the trivial cases already.
|
||||
// if (coeff < 0) {
|
||||
// std::swap(u, l);
|
||||
// u = -u;
|
||||
// l = -l;
|
||||
// coeff = -coeff;
|
||||
// }
|
||||
// return MakeBetweenCt(e, PosIntDivUp(l, coeff), PosIntDivDown(u, coeff));
|
||||
// } else {
|
||||
// No further reduction is possible.
|
||||
return RevAlloc(new NotBetweenCt(this, e, l, u));
|
||||
// }
|
||||
// TODO(user): Add back simplification code if e is constant * other_expr.
|
||||
return RevAlloc(new NotBetweenCt(this, e, l, u));
|
||||
}
|
||||
|
||||
// ----- is_between_cst Constraint -----
|
||||
@@ -1171,8 +1157,8 @@ Constraint* Solver::MakeMemberCt(IntExpr* expr, const std::vector<int64>& values
|
||||
const int64 coeff = ExtractExprProductCoeff(&expr);
|
||||
if (coeff == 0) {
|
||||
return std::find(values.begin(), values.end(), 0) == values.end()
|
||||
? MakeFalseConstraint()
|
||||
: MakeTrueConstraint();
|
||||
? MakeFalseConstraint()
|
||||
: MakeTrueConstraint();
|
||||
}
|
||||
std::vector<int64> copied_values = values;
|
||||
// If the expression is a non-trivial product, we filter out the values that
|
||||
@@ -1240,8 +1226,8 @@ Constraint* Solver::MakeNotMemberCt(IntExpr* expr,
|
||||
const int64 coeff = ExtractExprProductCoeff(&expr);
|
||||
if (coeff == 0) {
|
||||
return std::find(values.begin(), values.end(), 0) == values.end()
|
||||
? MakeTrueConstraint()
|
||||
: MakeFalseConstraint();
|
||||
? MakeTrueConstraint()
|
||||
: MakeFalseConstraint();
|
||||
}
|
||||
std::vector<int64> copied_values = values;
|
||||
// If the expression is a non-trivial product, we filter out the values that
|
||||
|
||||
@@ -113,55 +113,6 @@ std::string RangeLessOrEqual::DebugString() const {
|
||||
return left_->DebugString() + " <= " + right_->DebugString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RangeGreaterOrEqual
|
||||
|
||||
class RangeGreaterOrEqual : public Constraint {
|
||||
public:
|
||||
RangeGreaterOrEqual(Solver* const s, IntExpr* const l, IntExpr* const r);
|
||||
~RangeGreaterOrEqual() override {}
|
||||
void Post() override;
|
||||
void InitialPropagate() override;
|
||||
std::string DebugString() const override;
|
||||
IntVar* Var() override {
|
||||
return solver()->MakeIsGreaterOrEqualVar(left_, right_);
|
||||
}
|
||||
void Accept(ModelVisitor* const visitor) const override {
|
||||
visitor->BeginVisitConstraint(ModelVisitor::kGreaterOrEqual, this);
|
||||
visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
|
||||
visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
|
||||
right_);
|
||||
visitor->EndVisitConstraint(ModelVisitor::kGreaterOrEqual, this);
|
||||
}
|
||||
|
||||
private:
|
||||
IntExpr* const left_;
|
||||
IntExpr* const right_;
|
||||
Demon* demon_;
|
||||
};
|
||||
|
||||
RangeGreaterOrEqual::RangeGreaterOrEqual(Solver* const s, IntExpr* const l,
|
||||
IntExpr* const r)
|
||||
: Constraint(s), left_(l), right_(r), demon_(nullptr) {}
|
||||
|
||||
void RangeGreaterOrEqual::Post() {
|
||||
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
||||
left_->WhenRange(demon_);
|
||||
right_->WhenRange(demon_);
|
||||
}
|
||||
|
||||
void RangeGreaterOrEqual::InitialPropagate() {
|
||||
left_->SetMin(right_->Min());
|
||||
right_->SetMax(left_->Max());
|
||||
if (left_->Min() >= right_->Max()) {
|
||||
demon_->inhibit(solver());
|
||||
}
|
||||
}
|
||||
|
||||
std::string RangeGreaterOrEqual::DebugString() const {
|
||||
return left_->DebugString() + " >= " + right_->DebugString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RangeLess
|
||||
|
||||
@@ -208,52 +159,6 @@ std::string RangeLess::DebugString() const {
|
||||
return left_->DebugString() + " < " + right_->DebugString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RangeGreater
|
||||
|
||||
class RangeGreater : public Constraint {
|
||||
public:
|
||||
RangeGreater(Solver* const s, IntExpr* const l, IntExpr* const r);
|
||||
~RangeGreater() override {}
|
||||
void Post() override;
|
||||
void InitialPropagate() override;
|
||||
std::string DebugString() const override;
|
||||
IntVar* Var() override { return solver()->MakeIsGreaterVar(left_, right_); }
|
||||
void Accept(ModelVisitor* const visitor) const override {
|
||||
visitor->BeginVisitConstraint(ModelVisitor::kGreater, this);
|
||||
visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
|
||||
visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
|
||||
right_);
|
||||
visitor->EndVisitConstraint(ModelVisitor::kGreater, this);
|
||||
}
|
||||
|
||||
private:
|
||||
IntExpr* const left_;
|
||||
IntExpr* const right_;
|
||||
Demon* demon_;
|
||||
};
|
||||
|
||||
RangeGreater::RangeGreater(Solver* const s, IntExpr* const l, IntExpr* const r)
|
||||
: Constraint(s), left_(l), right_(r), demon_(nullptr) {}
|
||||
|
||||
void RangeGreater::Post() {
|
||||
demon_ = solver()->MakeConstraintInitialPropagateCallback(this);
|
||||
left_->WhenRange(demon_);
|
||||
right_->WhenRange(demon_);
|
||||
}
|
||||
|
||||
void RangeGreater::InitialPropagate() {
|
||||
left_->SetMin(right_->Min() + 1);
|
||||
right_->SetMax(left_->Max() - 1);
|
||||
if (left_->Min() > right_->Max()) {
|
||||
demon_->inhibit(solver());
|
||||
}
|
||||
}
|
||||
|
||||
std::string RangeGreater::DebugString() const {
|
||||
return left_->DebugString() + " > " + right_->DebugString();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// DiffVar
|
||||
|
||||
@@ -637,19 +542,7 @@ Constraint* Solver::MakeLessOrEqual(IntExpr* const l, IntExpr* const r) {
|
||||
}
|
||||
|
||||
Constraint* Solver::MakeGreaterOrEqual(IntExpr* const l, IntExpr* const r) {
|
||||
CHECK(l != nullptr) << "left expression nullptr, maybe a bad cast";
|
||||
CHECK(r != nullptr) << "left expression nullptr, maybe a bad cast";
|
||||
CHECK_EQ(this, l->solver());
|
||||
CHECK_EQ(this, r->solver());
|
||||
if (l == r) {
|
||||
return MakeTrueConstraint();
|
||||
} else if (l->Bound()) {
|
||||
return MakeLessOrEqual(r, l->Min());
|
||||
} else if (r->Bound()) {
|
||||
return MakeGreaterOrEqual(l, r->Min());
|
||||
} else {
|
||||
return RevAlloc(new RangeGreaterOrEqual(this, l, r));
|
||||
}
|
||||
return MakeLessOrEqual(r, l);
|
||||
}
|
||||
|
||||
Constraint* Solver::MakeLess(IntExpr* const l, IntExpr* const r) {
|
||||
@@ -667,17 +560,7 @@ Constraint* Solver::MakeLess(IntExpr* const l, IntExpr* const r) {
|
||||
}
|
||||
|
||||
Constraint* Solver::MakeGreater(IntExpr* const l, IntExpr* const r) {
|
||||
CHECK(l != nullptr) << "left expression nullptr, maybe a bad cast";
|
||||
CHECK(r != nullptr) << "left expression nullptr, maybe a bad cast";
|
||||
CHECK_EQ(this, l->solver());
|
||||
CHECK_EQ(this, r->solver());
|
||||
if (l->Bound()) {
|
||||
return MakeLess(r, l->Min());
|
||||
} else if (r->Bound()) {
|
||||
return MakeGreater(l, r->Min());
|
||||
} else {
|
||||
return RevAlloc(new RangeGreater(this, l, r));
|
||||
}
|
||||
return MakeLess(r, l);
|
||||
}
|
||||
|
||||
Constraint* Solver::MakeNonEquality(IntExpr* const l, IntExpr* const r) {
|
||||
|
||||
@@ -158,7 +158,21 @@ struct ThetaNode {
|
||||
// Single interval element
|
||||
explicit ThetaNode(const IntervalVar* const interval)
|
||||
: total_processing(interval->DurationMin()),
|
||||
total_ect(interval->EndMin()) {}
|
||||
total_ect(interval->StartMin() + interval->DurationMin()) {
|
||||
// NOTE(user): Petr Vilim's thesis assumes that all tasks in the
|
||||
// scheduling problem have fixed duration and that propagation already
|
||||
// updated the bounds of the start/end times accordingly.
|
||||
// The problem in this case is that the recursive formula for computing
|
||||
// total_ect was only proved for the case where the duration is fixed; in
|
||||
// our case, we use StartMin() + DurationMin() for the earliest completion
|
||||
// time of a task, which should not break any assumptions, but may give
|
||||
// bounds that are too loose.
|
||||
// LOG_IF_FIRST_N(WARNING,
|
||||
// (interval->DurationMin() != interval->DurationMax()), 1)
|
||||
// << "You are using the Theta-tree on tasks having variable durations. "
|
||||
// "This may lead to unexpected results, such as discarding valid "
|
||||
// "solutions or allowing invalid ones.";
|
||||
}
|
||||
|
||||
void Compute(const ThetaNode& left, const ThetaNode& right) {
|
||||
total_processing = left.total_processing + right.total_processing;
|
||||
@@ -171,9 +185,8 @@ struct ThetaNode {
|
||||
}
|
||||
|
||||
std::string DebugString() const {
|
||||
return StringPrintf("ThetaNode{ p = %" GG_LL_FORMAT "d, e = %" GG_LL_FORMAT
|
||||
"d }",
|
||||
total_processing, total_ect < 0LL ? -1LL : total_ect);
|
||||
return StrCat("ThetaNode{ p = ", total_processing, ", e = ",
|
||||
total_ect < 0LL ? -1LL : total_ect, " }");
|
||||
}
|
||||
|
||||
int64 total_processing;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "base/thorough_hash.h"
|
||||
#include "base/hash.h"
|
||||
#include "constraint_solver/model.pb.h"
|
||||
#include "graph/connectivity.h"
|
||||
#include "graph/linear_assignment.h"
|
||||
#include "util/saturated_arithmetic.h"
|
||||
|
||||
@@ -2198,7 +2199,8 @@ void RoutingModel::CloseModel() {
|
||||
|
||||
class RoutingModelInspector : public ModelVisitor {
|
||||
public:
|
||||
explicit RoutingModelInspector(RoutingModel* model) {
|
||||
explicit RoutingModelInspector(RoutingModel* model) : model_(model) {
|
||||
same_vehicle_components_.Init(model->Size());
|
||||
for (const std::string& name : model->GetAllDimensionNames()) {
|
||||
RoutingDimension* const dimension = model->GetMutableDimension(name);
|
||||
const std::vector<IntVar*>& cumuls = dimension->cumuls();
|
||||
@@ -2206,9 +2208,35 @@ class RoutingModelInspector : public ModelVisitor {
|
||||
cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
|
||||
}
|
||||
}
|
||||
const std::vector<IntVar*>& vehicle_vars = model->VehicleVars();
|
||||
for (int i = 0; i < vehicle_vars.size(); ++i) {
|
||||
vehicle_var_to_indices_[vehicle_vars[i]] = i;
|
||||
}
|
||||
RegisterInspectors();
|
||||
}
|
||||
~RoutingModelInspector() override {}
|
||||
void EndVisitModel(const std::string& solver_name) override {
|
||||
// Compact same vehicle component indices.
|
||||
hash_map<int, int> component_indices;
|
||||
int component_index = 0;
|
||||
for (int node = 0; node < model_->Size(); ++node) {
|
||||
const int component =
|
||||
same_vehicle_components_.GetClassRepresentative(node);
|
||||
if (InsertIfNotPresent(&component_indices, component, component_index)) {
|
||||
++component_index;
|
||||
}
|
||||
}
|
||||
model_->InitSameVehicleGroups(component_indices.size());
|
||||
for (int node = 0; node < model_->Size(); ++node) {
|
||||
const int component =
|
||||
same_vehicle_components_.GetClassRepresentative(node);
|
||||
DCHECK(ContainsKey(component_indices, component));
|
||||
model_->SetSameVehicleGroup(
|
||||
node, FindWithDefault(component_indices, component, 0));
|
||||
}
|
||||
// TODO(user): Perform transitive closure of dimension precedence graphs.
|
||||
// TODO(user): Have a single annotated precedence graph.
|
||||
}
|
||||
void EndVisitConstraint(const std::string& type_name,
|
||||
const Constraint* const constraint) override {
|
||||
FindWithDefault(constraint_inspectors_, type_name, []() {})();
|
||||
@@ -2233,31 +2261,73 @@ class RoutingModelInspector : public ModelVisitor {
|
||||
expr_inspectors_[kExpressionArgument] = [this](const IntExpr* expr) {
|
||||
expr_ = expr;
|
||||
};
|
||||
expr_inspectors_[kLeftArgument] = [this](const IntExpr* expr) {
|
||||
left_ = expr;
|
||||
};
|
||||
expr_inspectors_[kRightArgument] = [this](const IntExpr* expr) {
|
||||
right_ = expr;
|
||||
};
|
||||
array_inspectors_[kStartsArgument] = [this](
|
||||
const std::vector<int64>& int_array) { starts_argument_ = int_array; };
|
||||
array_inspectors_[kEndsArgument] = [this](const std::vector<int64>& int_array) {
|
||||
ends_argument_ = int_array;
|
||||
};
|
||||
constraint_inspectors_[kNotMember] = [this]() {
|
||||
const auto dim_index = cumul_to_dim_indices_[expr_];
|
||||
RoutingDimension* const dimension = dim_index.first;
|
||||
const int index = dim_index.second;
|
||||
dimension->forbidden_intervals_[index].InsertIntervals(starts_argument_,
|
||||
ends_argument_);
|
||||
VLOG(2) << dimension->name() << " " << index << ": "
|
||||
<< dimension->forbidden_intervals_[index].DebugString();
|
||||
std::pair<RoutingDimension*, int> dim_index;
|
||||
if (FindCopy(cumul_to_dim_indices_, expr_, &dim_index)) {
|
||||
RoutingDimension* const dimension = dim_index.first;
|
||||
const int index = dim_index.second;
|
||||
dimension->forbidden_intervals_[index].InsertIntervals(starts_argument_,
|
||||
ends_argument_);
|
||||
VLOG(2) << dimension->name() << " " << index << ": "
|
||||
<< dimension->forbidden_intervals_[index].DebugString();
|
||||
}
|
||||
expr_ = nullptr;
|
||||
starts_argument_.clear();
|
||||
ends_argument_.clear();
|
||||
};
|
||||
constraint_inspectors_[kEquality] = [this]() {
|
||||
int left_index = 0;
|
||||
int right_index = 0;
|
||||
if (FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
|
||||
FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
|
||||
VLOG(2) << "Vehicle variables for " << left_index << " and "
|
||||
<< right_index << " are equal.";
|
||||
same_vehicle_components_.AddArc(left_index, right_index);
|
||||
}
|
||||
left_ = nullptr;
|
||||
right_ = nullptr;
|
||||
};
|
||||
constraint_inspectors_[kLessOrEqual] = [this]() {
|
||||
std::pair<RoutingDimension*, int> left_index;
|
||||
std::pair<RoutingDimension*, int> right_index;
|
||||
if (FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
|
||||
FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
|
||||
RoutingDimension* const dimension = left_index.first;
|
||||
if (dimension == right_index.first) {
|
||||
VLOG(2) << "For dimension " << dimension->name() << ", cumul for "
|
||||
<< left_index.second << " is less than " << right_index.second
|
||||
<< ".";
|
||||
dimension->precedence_graph_.AddArc(left_index.second,
|
||||
right_index.second);
|
||||
}
|
||||
}
|
||||
left_ = nullptr;
|
||||
right_ = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
RoutingModel* const model_;
|
||||
ConnectedComponents<int, int> same_vehicle_components_;
|
||||
hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
|
||||
cumul_to_dim_indices_;
|
||||
hash_map<const IntExpr*, int> vehicle_var_to_indices_;
|
||||
hash_map<std::string, ExprInspector> expr_inspectors_;
|
||||
hash_map<std::string, ArrayInspector> array_inspectors_;
|
||||
hash_map<std::string, ConstraintInspector> constraint_inspectors_;
|
||||
const IntExpr* expr_ = nullptr;
|
||||
const IntExpr* left_ = nullptr;
|
||||
const IntExpr* right_ = nullptr;
|
||||
std::vector<int64> starts_argument_;
|
||||
std::vector<int64> ends_argument_;
|
||||
};
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
#include "constraint_solver/constraint_solver.h"
|
||||
#include "constraint_solver/constraint_solveri.h"
|
||||
#include "constraint_solver/routing_parameters.pb.h"
|
||||
#include "graph/graph.h"
|
||||
#include "util/range_query_function.h"
|
||||
#include "util/sorted_interval_list.h"
|
||||
#include "base/adjustable_priority_queue-inl.h"
|
||||
@@ -586,6 +587,9 @@ class RoutingModel {
|
||||
//
|
||||
// TODO(user): Remove this when model introspection detects linked nodes.
|
||||
void AddPickupAndDelivery(NodeIndex node1, NodeIndex node2) {
|
||||
// TODO(user): Checking the depot ensures indices are up-to-date but sets
|
||||
// the depot to node 0 if it has not been set already; find a way to avoid
|
||||
// this.
|
||||
CheckDepot();
|
||||
pickup_delivery_pairs_.push_back(
|
||||
std::make_pair(NodeToIndex(node1), NodeToIndex(node2)));
|
||||
@@ -874,6 +878,11 @@ class RoutingModel {
|
||||
}
|
||||
// Returns the number of different vehicle classes in the model.
|
||||
int GetVehicleClassesCount() const { return vehicle_classes_.size(); }
|
||||
// Returns variable indices of nodes constrained to be on the same route.
|
||||
const std::vector<int>& GetSameVehicleIndicesOfIndex(int node) const {
|
||||
DCHECK(closed_);
|
||||
return same_vehicle_groups_[same_vehicle_group_[node]];
|
||||
}
|
||||
// Returns whether the arc from->to1 is more constrained than from->to2,
|
||||
// taking into account, in order:
|
||||
// - whether the destination node isn't an end node
|
||||
@@ -1191,6 +1200,15 @@ class RoutingModel {
|
||||
int64 GetArcCostForCostClassInternal(int64 i, int64 j, int64 cost_class);
|
||||
int GetVehicleStartClass(int64 start) const;
|
||||
|
||||
void InitSameVehicleGroups(int number_of_groups) {
|
||||
same_vehicle_group_.assign(Size(), 0);
|
||||
same_vehicle_groups_.assign(number_of_groups, {});
|
||||
}
|
||||
void SetSameVehicleGroup(int index, int group) {
|
||||
same_vehicle_group_[index] = group;
|
||||
same_vehicle_groups_[group].push_back(index);
|
||||
}
|
||||
|
||||
// Model
|
||||
std::unique_ptr<Solver> solver_;
|
||||
int nodes_;
|
||||
@@ -1236,6 +1254,10 @@ class RoutingModel {
|
||||
std::vector<ValuedNodes<int64> > same_vehicle_costs_;
|
||||
// Pickup and delivery
|
||||
NodePairs pickup_delivery_pairs_;
|
||||
// Same vehicle group to which a node belongs.
|
||||
std::vector<int> same_vehicle_group_;
|
||||
// Same vehicle node groups.
|
||||
std::vector<std::vector<int> > same_vehicle_groups_;
|
||||
// Index management
|
||||
std::vector<NodeIndex> index_to_node_;
|
||||
ITIVector<NodeIndex, int> node_to_index_;
|
||||
@@ -1282,6 +1304,7 @@ class RoutingModel {
|
||||
hash_set<const VariableNodeEvaluator2*> owned_state_dependent_callbacks_;
|
||||
|
||||
friend class RoutingDimension;
|
||||
friend class RoutingModelInspector;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RoutingModel);
|
||||
};
|
||||
@@ -1509,6 +1532,12 @@ class RoutingDimension {
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
// Accessors.
|
||||
#ifndef SWIG
|
||||
const ReverseArcListGraph<int, int>& GetPrecedenceGraph() const {
|
||||
return precedence_graph_;
|
||||
}
|
||||
#endif
|
||||
|
||||
int64 GetSpanUpperBoundForVehicle(int vehicle) const {
|
||||
return vehicle_span_upper_bounds_[vehicle];
|
||||
}
|
||||
@@ -1574,6 +1603,9 @@ class RoutingDimension {
|
||||
// "class_evaluators_" does the de-duplicated ownership.
|
||||
std::vector<RoutingModel::TransitEvaluator2> class_evaluators_;
|
||||
std::vector<int64> vehicle_to_class_;
|
||||
#ifndef SWIG
|
||||
ReverseArcListGraph<int, int> precedence_graph_;
|
||||
#endif
|
||||
|
||||
// The transits of a dimension may depend on its cumuls or the cumuls of
|
||||
// another dimension. There can be no cycles, except for self loops, a typical
|
||||
|
||||
Reference in New Issue
Block a user