14#ifndef OR_TOOLS_SAT_ROUTING_CUTS_H_
15#define OR_TOOLS_SAT_ROUTING_CUTS_H_
28#include "absl/container/flat_hash_map.h"
29#include "absl/log/check.h"
30#include "absl/strings/str_cat.h"
31#include "absl/types/span.h"
62 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
63 absl::Span<const Literal> literals,
108 const NodeExpression& x_expr,
const NodeExpression& y_expr,
110 const std::pair<IntegerValue, IntegerValue>& x_var_bounds,
111 const std::pair<IntegerValue, IntegerValue>& y_var_bounds);
129 static std::unique_ptr<RouteRelationsHelper>
Create(
130 int num_nodes,
const std::vector<int>& tails,
131 const std::vector<int>& heads,
const std::vector<Literal>& literals,
132 absl::Span<const AffineExpression> flat_node_dim_expressions,
139 return flat_node_dim_expressions_.size() / num_dimensions_;
143 return flat_arc_dim_relations_.size() / num_dimensions_;
148 return flat_node_dim_expressions_[node * num_dimensions_ + dimension];
164 DCHECK_GE(dimension, 0);
165 DCHECK_LT(dimension, num_dimensions_);
167 return flat_arc_dim_relations_[arc * num_dimensions_ + dimension];
171 return !flat_shortest_path_lbs_.empty();
177 if (flat_shortest_path_lbs_.empty())
return true;
178 const int num_nodes_squared = num_nodes_ * num_nodes_;
179 for (
int dim = 0; dim < num_dimensions_; ++dim) {
180 if (flat_shortest_path_lbs_[dim * num_nodes_squared + tail * num_nodes_ +
182 std::numeric_limits<IntegerValue>::max()) {
191 const absl::flat_hash_map<IntegerVariable, IntegerValue>&
input,
192 absl::flat_hash_map<IntegerVariable, IntegerValue>* output)
const {
193 if (flat_shortest_path_lbs_.empty())
return true;
194 const int num_nodes_squared = num_nodes_ * num_nodes_;
195 for (
int dim = 0; dim < num_dimensions_; ++dim) {
198 const IntegerValue head_minus_tail_lb =
199 flat_shortest_path_lbs_[dim * num_nodes_squared + tail * num_nodes_ +
205 auto it =
input.find(tail_expr.
var);
206 if (it !=
input.end()) {
207 tail_lb = std::max(tail_lb, tail_expr.
ValueAt(it->second));
217 auto [it, inserted] = output->insert({i_lit.
var, i_lit.
bound});
219 it->second = std::max(it->second, i_lit.
bound);
229 bool negate_expressions)
const;
231 void RemoveArcs(absl::Span<const int> sorted_arc_indices);
235 std::vector<AffineExpression> flat_node_dim_expressions,
236 std::vector<HeadMinusTailBounds> flat_arc_dim_relations,
237 std::vector<IntegerValue> flat_shortest_path_lbs);
241 const int num_nodes_ = 0;
242 const int num_dimensions_ = 0;
246 std::vector<AffineExpression> flat_node_dim_expressions_;
250 std::vector<HeadMinusTailBounds> flat_arc_dim_relations_;
256 std::vector<IntegerValue> flat_shortest_path_lbs_;
266 const CpModelProto& input_model, CpModelProto& output_model);
319 absl::Span<ItemOrBin> objects,
320 std::vector<int>& objects_that_cannot_be_bin_and_reach_minimum,
347 int ComputeMinNumberOfBinsInternal(
348 absl::Span<ItemOrBin> objects,
349 std::vector<int>& objects_that_cannot_be_bin_and_reach_minimum);
351 const double dp_effort_ = 1e8;
352 std::vector<IntegerValue> tmp_capacities_;
353 std::vector<int64_t> tmp_demands_;
370 absl::Span<const int> cannot_be_first = {},
371 absl::Span<const int> cannot_be_last = {}) {
372 if (new_bound < bound_)
return;
373 if (new_bound > bound_) {
376 cannot_be_last_.clear();
377 cannot_be_first_.clear();
379 cannot_be_first_.insert(cannot_be_first_.begin(), cannot_be_first.begin(),
380 cannot_be_first.end());
381 cannot_be_last_.insert(cannot_be_last_.begin(), cannot_be_last.begin(),
382 cannot_be_last.end());
387 std::string
name()
const {
return absl::StrCat(base_name_, name_); }
388 int bound()
const {
return bound_; }
399 std::string base_name_;
401 std::vector<int> tmp_;
402 std::vector<int> cannot_be_last_;
403 std::vector<int> cannot_be_first_;
413 const std::vector<int>& heads,
414 const std::vector<Literal>& literals,
Model* model);
456 int k, absl::Span<const int> subset,
459 bool use_forward_direction =
true);
469 absl::Span<const int>
CannotBeLast()
const {
return cannot_be_last_; };
476 void PrecomputeDataForSubset(absl::Span<const int> subset);
529 absl::Span<SpecialBinPackingHelper::ItemOrBin>
530 RelaxIntoSpecialBinPackingProblem(absl::Span<const int> subset,
int dimension,
531 bool negate_expressions,
bool use_incoming,
538 int GetMinOutgoingFlow(
int subset_size,
int longest_path_length,
539 int max_longest_paths);
541 const std::vector<int>& tails_;
542 const std::vector<int>& heads_;
543 const std::vector<Literal>& literals_;
560 std::vector<int> tmp_subset_;
561 std::vector<bool> in_subset_;
562 std::vector<int> index_in_subset_;
566 std::vector<std::vector<int>> incoming_arc_indices_;
567 std::vector<std::vector<int>> outgoing_arc_indices_;
574 std::vector<bool> has_incoming_arcs_from_outside_;
575 std::vector<bool> has_outgoing_arcs_to_outside_;
581 bool IsUnique()
const {
return num_seen_ == 1; }
582 int Get()
const {
return arc_; }
593 std::vector<UniqueArc> incoming_arcs_from_outside_;
594 std::vector<UniqueArc> outgoing_arcs_to_outside_;
598 std::vector<bool> reachable_;
599 std::vector<bool> next_reachable_;
605 std::vector<absl::flat_hash_map<IntegerVariable, IntegerValue>>
606 node_var_lower_bounds_;
607 std::vector<absl::flat_hash_map<IntegerVariable, IntegerValue>>
608 next_node_var_lower_bounds_;
610 SpecialBinPackingHelper bin_packing_helper_;
611 std::vector<SpecialBinPackingHelper::ItemOrBin> tmp_bin_packing_problem_;
613 std::vector<int> objects_that_cannot_be_bin_and_reach_minimum_;
615 std::vector<int> cannot_be_last_;
616 std::vector<int> cannot_be_first_;
619 int64_t num_full_dp_skips_ = 0;
620 int64_t num_full_dp_calls_ = 0;
621 int64_t num_full_dp_early_abort_ = 0;
622 int64_t num_full_dp_work_abort_ = 0;
623 int64_t num_full_dp_rc_skip_ = 0;
624 int64_t num_full_dp_unique_arc_ = 0;
625 absl::flat_hash_map<std::string, int> num_by_type_;
649 absl::Span<
const std::pair<int, int>> arcs,
650 int stop_at_num_components,
651 std::vector<int>* subset_data,
652 std::vector<absl::Span<const int>>* subsets);
668 absl::Span<const int> parent, std::vector<int>* subset_data,
669 std::vector<absl::Span<const int>>* subsets,
670 int node_limit = std::numeric_limits<int>::max());
708 int num_nodes, absl::Span<const ArcWithLpValue> relevant_arcs);
718 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
719 absl::Span<const Literal> literals,
Model* model);
729 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
730 absl::Span<const Literal> literals,
731 absl::Span<const AffineExpression> flat_node_dim_expressions,
Model* model);
744 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
745 const std::vector<AffineExpression>& arc_capacities,
746 std::function<
void(
const std::vector<bool>& in_subset,
747 IntegerValue* min_incoming_flow,
748 IntegerValue* min_outgoing_flow)>
Keep the best min outgoing/incoming flow out of a subset.
BestBoundHelper()=default
BestBoundHelper(std::string base_name)
absl::Span< const int > CannotBeFirst() const
absl::Span< const int > CannotBeLast() const
void Update(int new_bound, std::string name, absl::Span< const int > cannot_be_first={}, absl::Span< const int > cannot_be_last={})
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
Similar to MaxBoundedSubsetSum() above but use a different algo.
int ComputeTightMinOutgoingFlow(absl::Span< const int > subset)
void ReportDpSkip()
Just for stats reporting.
absl::Span< const int > CannotBeLast() const
int ComputeMinOutgoingFlow(absl::Span< const int > subset)
bool SubsetMightBeServedWithKRoutes(int k, absl::Span< const int > subset, RouteRelationsHelper *helper=nullptr, LinearConstraintManager *manager=nullptr, int special_node=-1, bool use_forward_direction=true)
absl::Span< const int > CannotBeFirst() const
int ComputeDimensionBasedMinOutgoingFlow(absl::Span< const int > subset, const RouteRelationsHelper &helper, BestBoundHelper *best_bound)
MinOutgoingFlowHelper(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
bool PathExists(int tail, int head) const
const HeadMinusTailBounds & GetArcRelation(int arc, int dimension) const
bool HasShortestPathsInformation() const
IntegerValue GetArcOffsetLowerBound(int arc, int dimension, bool negate_expressions) const
const AffineExpression & GetNodeExpression(int node, int dimension) const
Returns the expression associated with the given node and dimension.
static std::unique_ptr< RouteRelationsHelper > Create(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, absl::Span< const AffineExpression > flat_node_dim_expressions, const BinaryRelationRepository &binary_relation_repository, Model *model)
bool PropagateLocalBoundsUsingShortestPaths(const IntegerTrail &integer_trail, int tail, int head, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output) const
int num_dimensions() const
Returns the number of "dimensions", such as time or vehicle load.
void RemoveArcs(absl::Span< const int > sorted_arc_indices)
RouteRelationsHelper()=default
Visible for testing.
Simple class to add statistics by name and print them at the end.
SpecialBinPackingHelper(double dp_effort)
int ComputeMinNumberOfBins(absl::Span< ItemOrBin > objects, std::vector< int > &objects_that_cannot_be_bin_and_reach_minimum, std::string &info)
bool UseDpToTightenCapacities(absl::Span< ItemOrBin > objects)
SpecialBinPackingHelper()=default
bool GreedyPackingWorks(int num_bins, absl::Span< const ItemOrBin > objects)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, Model *model)
std::vector< int > ComputeGomoryHuTree(int num_nodes, absl::Span< const ArcWithLpValue > relevant_arcs)
std::pair< int, int > MaybeFillMissingRoutesConstraintNodeExpressions(const CpModelProto &input_model, CpModelProto &output_model)
RoutingCumulExpressions DetectDimensionsAndCumulExpressions(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, const BinaryRelationRepository &binary_relation_repository)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
std::pair< IntegerValue, IntegerValue > GetDifferenceBounds(const NodeExpression &x_expr, const NodeExpression &y_expr, const sat::Relation &r, const std::pair< IntegerValue, IntegerValue > &x_var_bounds, const std::pair< IntegerValue, IntegerValue > &y_var_bounds)
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
CutGenerator CreateFlowCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< AffineExpression > &arc_capacities, std::function< void(const std::vector< bool > &in_subset, IntegerValue *min_incoming_flow, IntegerValue *min_outgoing_flow)> get_flows, Model *model)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
CutGenerator CreateCVRPCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, absl::Span< const AffineExpression > flat_node_dim_expressions, Model *model)
void SymmetrizeArcs(std::vector< ArcWithLpValue > *arcs)
void ExtractAllSubsetsFromForest(absl::Span< const int > parent, std::vector< int > *subset_data, std::vector< absl::Span< const int > > *subsets, int node_limit)
bool VariableIsPositive(IntegerVariable i)
void GenerateInterestingSubsets(int num_nodes, absl::Span< const std::pair< int, int > > arcs, int stop_at_num_components, std::vector< int > *subset_data, std::vector< absl::Span< const int > > *subsets)
In SWIG mode, we don't want anything besides these top-level includes.
void LogStats(const SetCoverModel &model)
static int input(yyscan_t yyscanner)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
var * coeff + constant >= bound.
IntegerValue ValueAt(IntegerValue var_value) const
Returns the value of this affine expression given its variable value.
bool operator==(const ArcWithLpValue &o) const
IntegerValue ValueAt(IntegerValue x) const
NodeExpression(IntegerVariable var, IntegerValue coeff, IntegerValue offset)
NodeExpression(const AffineExpression &expr)
NodeExpression Negated() const
bool operator==(const HeadMinusTailBounds &r) const
std::vector< AffineExpression > flat_node_dim_expressions
const AffineExpression & GetNodeExpression(int node, int dimension) const
int node
The initial routing node that correspond to this object.