diff --git a/ortools/sat/cp_model.cc b/ortools/sat/cp_model.cc index d033537a396..dbb9ca6f419 100644 --- a/ortools/sat/cp_model.cc +++ b/ortools/sat/cp_model.cc @@ -218,6 +218,12 @@ void CircuitConstraint::AddArc(int tail, int head, BoolVar literal) { proto_->mutable_circuit()->add_literals(literal.index_); } +void MultipleCircuitConstraint::AddArc(int tail, int head, BoolVar literal) { + proto_->mutable_routes()->add_tails(tail); + proto_->mutable_routes()->add_heads(head); + proto_->mutable_routes()->add_literals(literal.index_); +} + void TableConstraint::AddTuple(absl::Span tuple) { CHECK_EQ(tuple.size(), proto_->table().vars_size()); for (const int64 t : tuple) { @@ -551,6 +557,10 @@ CircuitConstraint CpModelBuilder::AddCircuitConstraint() { return CircuitConstraint(cp_model_.add_constraints()); } +MultipleCircuitConstraint CpModelBuilder::AddMultipleCircuitConstraint() { + return MultipleCircuitConstraint(cp_model_.add_constraints()); +} + TableConstraint CpModelBuilder::AddAllowedAssignments( absl::Span vars) { ConstraintProto* const proto = cp_model_.add_constraints(); diff --git a/ortools/sat/cp_model.h b/ortools/sat/cp_model.h index 478b754ff33..38c6abe046b 100644 --- a/ortools/sat/cp_model.h +++ b/ortools/sat/cp_model.h @@ -113,6 +113,7 @@ class BoolVar { friend class CpModelBuilder; friend class IntVar; friend class IntervalVar; + friend class MultipleCircuitConstraint; friend class LinearExpr; friend class ReservoirConstraint; friend bool SolutionBooleanValue(const CpSolverResponse& r, BoolVar x); @@ -458,6 +459,29 @@ class CircuitConstraint : public Constraint { using Constraint::Constraint; }; +/** + * Specialized circuit constraint. + * + * This constraint allows adding arcs to the multiple circuit constraint + * incrementally. + */ +class MultipleCircuitConstraint : public Constraint { + public: + /** + * Add an arc to the circuit. + * + * @param tail the index of the tail node. + * @param head the index of the head node. + * @param literal it will be set to true if the arc is selected. + */ + void AddArc(int tail, int head, BoolVar literal); + + private: + friend class CpModelBuilder; + + using Constraint::Constraint; +}; + /** * Specialized assignment constraint. * @@ -651,9 +675,25 @@ class CpModelBuilder { * * It returns a circuit constraint that allows adding arcs incrementally after * construction. + * */ CircuitConstraint AddCircuitConstraint(); + /** + * Adds a multiple circuit constraint, aka the "VRP" (Vehicle Routing Problem) + * constraint. + * + * The direct graph where arc #i (from tails[i] to head[i]) is present iff + * literals[i] is true must satisfy this set of properties: + * - #incoming arcs == 1 except for node 0. + * - #outgoing arcs == 1 except for node 0. + * - for node zero, #incoming arcs == #outgoing arcs. + * - There are no duplicate arcs. + * - Self-arcs are allowed except for node 0. + * - There is no cycle in this graph, except through node 0. + */ + MultipleCircuitConstraint AddMultipleCircuitConstraint(); + /** * Adds an allowed assignments constraint. * diff --git a/ortools/sat/pseudo_costs.cc b/ortools/sat/pseudo_costs.cc index 022b7fffa82..b5d8f35e0f0 100644 --- a/ortools/sat/pseudo_costs.cc +++ b/ortools/sat/pseudo_costs.cc @@ -44,7 +44,7 @@ void PseudoCosts::UpdateCostForVar(IntegerVariable var, double new_cost) { if (var >= pseudo_costs_.size()) { // Create space for new variable and its negation. const int new_size = std::max(var, NegationOf(var)).value() + 1; - pseudo_costs_.resize(new_size, IncrementalAverage(initial_cost_)); + pseudo_costs_.resize(new_size, IncrementalAverage(0.0)); } CHECK_LT(var, pseudo_costs_.size()); pseudo_costs_[var].AddData(new_cost); @@ -71,8 +71,7 @@ void PseudoCosts::UpdateCost( // Note that this initial value will be overwritten the first time // UpdateCostForVar() is called. if (!pseudo_costs_initialized_) { - initial_cost_ = current_pseudo_cost / 2.0; - InitializeCosts(initial_cost_); + InitializeCosts(0.0); } UpdateCostForVar(decision.var, current_pseudo_cost); } @@ -101,8 +100,8 @@ IntegerVariable PseudoCosts::GetBestDecisionVar() { // TODO(user): Experiment with different ways to merge the costs. const double current_merged_cost = - std::min(GetCost(positive_var), epsilon) * - std::min(GetCost(negative_var), epsilon); + std::max(GetCost(positive_var), epsilon) * + std::max(GetCost(negative_var), epsilon); if (current_merged_cost > best_cost) { chosen_var = positive_var; diff --git a/ortools/sat/pseudo_costs.h b/ortools/sat/pseudo_costs.h index 868b011b61b..5170b694649 100644 --- a/ortools/sat/pseudo_costs.h +++ b/ortools/sat/pseudo_costs.h @@ -69,8 +69,6 @@ class PseudoCosts { bool pseudo_costs_initialized_ = false; - double initial_cost_ = 0.0; - gtl::ITIVector pseudo_costs_; };