14#ifndef OR_TOOLS_SAT_PRECEDENCES_H_
15#define OR_TOOLS_SAT_PRECEDENCES_H_
24#include "absl/container/flat_hash_map.h"
25#include "absl/container/inlined_vector.h"
26#include "absl/log/check.h"
27#include "absl/types/span.h"
61 trail_(model->GetOrCreate<
Trail>()),
63 integer_trail_->RegisterReversibleClass(
this);
67 graph_.ReserveNodes(num_variables);
68 graph_.AddNode(num_variables - 1);
113 std::vector<FullIntegerPrecedence>* output);
127 std::vector<PrecedenceData>* output);
153 std::vector<Literal>* literal_reason,
154 std::vector<IntegerLiteral>* integer_reason)
const;
163 void CreateLevelEntryIfNeeded();
171 const auto [it, inserted] = root_relations_.insert({expr, ub});
172 UpdateBestRelationIfBetter(expr, ub);
178 std::max(expr.
vars[0].value(), expr.
vars[1].value()) + 1;
179 if (new_size > after_.size()) after_.
resize(new_size);
184 it->second = std::min(it->second, ub);
188 void UpdateBestRelationIfBetter(LinearExpression2 expr, IntegerValue rhs) {
189 const auto [it, inserted] = best_relations_.insert({expr, rhs});
191 it->second = std::min(it->second, rhs);
195 void UpdateBestRelation(LinearExpression2 expr, IntegerValue rhs) {
196 const auto it = root_relations_.find(expr);
197 if (it != root_relations_.end()) {
198 rhs = std::min(rhs, it->second);
201 best_relations_.erase(expr);
203 best_relations_[expr] = rhs;
207 const SatParameters& params_;
209 IntegerTrail* integer_trail_;
211 util::StaticGraph<> graph_;
212 std::vector<IntegerValue> arc_offsets_;
214 bool is_built_ =
false;
215 bool is_dag_ =
false;
216 std::vector<IntegerVariable> topological_order_;
222 struct ConditionalEntry {
223 ConditionalEntry(
int p, IntegerValue r, LinearExpression2 k,
224 absl::Span<const Literal> e)
225 : prev_entry(p), rhs(r), key(k), enforcements(e.
begin(), e.
end()) {}
229 LinearExpression2 key;
230 absl::InlinedVector<Literal, 4> enforcements;
232 std::vector<ConditionalEntry> conditional_stack_;
233 std::vector<std::pair<int, int>> level_to_stack_size_;
237 absl::flat_hash_map<LinearExpression2, IntegerValue> root_relations_;
238 absl::flat_hash_map<LinearExpression2, int> conditional_relations_;
242 absl::flat_hash_map<LinearExpression2, IntegerValue> best_relations_;
248 util_intops::StrongVector<IntegerVariable, std::vector<IntegerVariable>>
250 util_intops::StrongVector<IntegerVariable, std::vector<IntegerVariable>>
254 std::vector<IntegerVariable> var_with_positive_degree_;
255 util_intops::StrongVector<IntegerVariable, int> var_to_degree_;
256 util_intops::StrongVector<IntegerVariable, int> var_to_last_index_;
257 std::vector<PrecedenceData> tmp_precedences_;
281 trail_(model->GetOrCreate<
Trail>()),
285 watcher_id_(watcher_->Register(this)) {
287 integer_trail_->RegisterWatcher(&modified_vars_);
288 watcher_->SetPropagatorPriority(watcher_id_, 0);
298 void Untrail(const
Trail& trail,
int trail_index) final;
312 IntegerValue offset);
314 IntegerVariable offset_var);
321 IntegerValue offset,
Literal l);
326 IntegerVariable offset_var,
331 IntegerValue offset);
334 DEFINE_STRONG_INDEX_TYPE(ArcIndex);
335 DEFINE_STRONG_INDEX_TYPE(OptionalArcIndex);
339 IntegerVariable tail_var;
340 IntegerVariable head_var;
343 IntegerVariable offset_var;
346 absl::InlinedVector<Literal, 6> presence_literals;
350 mutable bool is_marked;
358 void AdjustSizeFor(IntegerVariable
i);
359 void AddArc(IntegerVariable tail, IntegerVariable head, IntegerValue offset,
360 IntegerVariable offset_var,
361 absl::Span<const Literal> presence_literals);
365 bool EnqueueAndCheck(
const ArcInfo& arc, IntegerValue new_head_lb,
367 IntegerValue ArcOffset(
const ArcInfo& arc)
const;
371 void PropagateOptionalArcs(
Trail* trail);
389 void InitializeBFQueueWithModifiedNodes();
390 bool BellmanFordTarjan(
Trail* trail);
391 bool DisassembleSubtree(
int source,
int target,
392 std::vector<bool>* can_be_skipped);
393 void AnalyzePositiveCycle(ArcIndex first_arc,
Trail* trail,
394 std::vector<Literal>* must_be_all_true,
395 std::vector<Literal>* literal_reason,
396 std::vector<IntegerLiteral>* integer_reason);
397 void CleanUpMarkedArcsAndParents();
401 bool NoPropagationLeft(
const Trail& trail)
const;
404 void PushConditionalRelations(
const ArcInfo& arc);
438 absl::InlinedVector<OptionalArcIndex, 6>>
439 impacted_potential_arcs_;
450 literal_to_new_impacted_arcs_;
454 std::vector<Literal> literal_reason_;
455 std::vector<IntegerLiteral> integer_reason_;
460 std::deque<int> bf_queue_;
461 std::vector<bool> bf_in_queue_;
462 std::vector<bool> bf_can_be_skipped_;
463 std::vector<ArcIndex> bf_parent_arc_of_;
466 std::vector<int> tmp_vector_;
469 int64_t num_cycles_ = 0;
470 int64_t num_pushes_ = 0;
471 int64_t num_enforcement_pushes_ = 0;
492 IntegerValue
coeff = IntegerValue(0);
518 int size()
const {
return relations_.size(); }
526 if (lit >= lit_to_relations_.size())
return {};
527 return lit_to_relations_[lit];
533 IntegerVariable var)
const {
534 if (var >= var_to_relations_.size())
return {};
535 return var_to_relations_[var];
541 IntegerVariable var2)
const {
542 if (var1 > var2) std::swap(var1, var2);
543 const std::pair<IntegerVariable, IntegerVariable> key(var1, var2);
544 const auto it = var_pair_to_relations_.find(key);
545 if (it == var_pair_to_relations_.end())
return {};
574 const absl::flat_hash_map<IntegerVariable, IntegerValue>&
input,
575 absl::flat_hash_map<IntegerVariable, IntegerValue>* output)
const;
578 bool is_built_ =
false;
579 int num_enforced_relations_ = 0;
580 std::vector<Relation> relations_;
583 absl::flat_hash_map<std::pair<IntegerVariable, IntegerVariable>,
585 var_pair_to_relations_;
602 IntegerValue ub)
const;
631 std::vector<Literal>* literal_reason,
632 std::vector<IntegerLiteral>* integer_reason)
const;
642 void NotifyWatchingPropagators()
const;
645 std::pair<LinearExpression2, IntegerValue> FromDifference(
649 std::pair<IntegerValue, IntegerValue> GetImpliedLevelZeroBounds(
658 int64_t num_updates_ = 0;
659 int64_t num_affine_updates_ = 0;
662 absl::flat_hash_map<std::pair<LinearExpression2, IntegerValue>,
Literal>
669 absl::flat_hash_set<BooleanVariable> variable_appearing_in_reified_relations_;
670 std::vector<std::tuple<Literal, LinearExpression2, IntegerValue>>
671 all_reified_relations_;
680 std::pair<AffineExpression, IntegerValue>>
683 absl::btree_set<int> propagator_ids_;
704 bool auto_detect_clauses =
false);
710 int AddGreaterThanAtLeastOneOfConstraintsFromClause(
711 absl::Span<const Literal> clause,
Model* model);
717 int AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
722 bool AddRelationFromIndices(IntegerVariable var,
723 absl::Span<const Literal> clause,
724 absl::Span<const int> indices,
Model* model);
734 IntegerVariable i2) {
740 IntegerVariable i1, IntegerVariable i2, IntegerValue offset) {
752 IntegerVariable i1, IntegerVariable i2, IntegerValue offset,
Literal l) {
757 IntegerVariable i1, IntegerVariable i2, IntegerVariable offset_var) {
758 AddArc(i1, i2, IntegerValue(0), offset_var, {});
762 IntegerVariable i1, IntegerVariable i2, IntegerValue offset,
763 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
764 AddArc(i1, i2, offset, offset_var, presence_literals);
774 return [=](
Model* model) {
783 return [=](
Model* model) {
786 expr, IntegerValue(-offset));
788 a,
b, IntegerValue(offset));
794 AffineExpression a, AffineExpression
b, int64_t offset) {
796 CHECK_EQ(a.coeff, 1);
798 CHECK_EQ(
b.coeff, 1);
799 return [=](
Model* model) {
802 expr, -a.constant +
b.constant - offset);
804 a.var,
b.var, a.constant -
b.constant + offset);
810 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
811 IntegerVariable
b, int64_t ub,
Model* model) {
813 if (enforcement_literals.empty()) {
819 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
820 p->AddPrecedenceWithAllOptions(a,
NegationOf(
b), IntegerValue(-ub),
828 absl::Span<const Literal> enforcement_literals, IntegerVariable a,
829 IntegerVariable
b, IntegerVariable c, int64_t ub,
Model* model) {
830 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
831 p->AddPrecedenceWithAllOptions(a,
NegationOf(c), IntegerValue(-ub),
b,
832 enforcement_literals);
838 return [=](
Model* model) {
839 return model->GetOrCreate<PrecedencesPropagator>()->AddPrecedence(
b, a);
844inline std::function<void(
Model*)>
Equality(IntegerVariable a,
846 return [=](
Model* model) {
856 return [=](
Model* model) {
864 IntegerVariable a, IntegerVariable
b, int64_t offset, Literal is_le) {
865 return [=](
Model* model) {
866 PrecedencesPropagator* p = model->GetOrCreate<PrecedencesPropagator>();
867 p->AddConditionalPrecedenceWithOffset(a,
b, IntegerValue(offset), is_le);
absl::Span< const int > IndicesOfRelationsBetween(IntegerVariable var1, IntegerVariable var2) const
absl::Span< const int > IndicesOfRelationsContaining(IntegerVariable var) const
absl::Span< const int > IndicesOfRelationsEnforcedBy(LiteralIndex lit) const
bool PropagateLocalBounds(const IntegerTrail &integer_trail, Literal lit, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output) const
const Relation & relation(int index) const
The returned relation is guaranteed to only have positive variables.
void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b)
void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs, IntegerValue rhs)
void AddReifiedPrecedenceIfNonTrivial(Literal l, AffineExpression a, AffineExpression b)
IntegerValue UpperBound(LinearExpression2 expr) const
std::vector< LinearExpression2 > GetAllExpressionsWithAffineBounds() const
Warning, the order will not be deterministic.
int NumExpressionsWithAffineBounds() const
void WatchAllLinearExpressions2(int id)
RelationStatus GetLevelZeroPrecedenceStatus(AffineExpression a, AffineExpression b) const
Return the status of a <= b;.
LiteralIndex GetReifiedPrecedence(AffineExpression a, AffineExpression b)
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
RelationStatus GetLevelZeroStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
BinaryRelationsMaps(Model *model)
bool AddAffineUpperBound(LinearExpression2 expr, AffineExpression affine_ub)
void AddRelationBounds(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
GreaterThanAtLeastOneOfDetector(Model *model)
PrecedenceRelations(Model *model)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
bool AddBounds(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
IntegerValue UpperBound(LinearExpression2 expr) const
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2 expr, IntegerValue rhs)
IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
bool AddUpperBound(LinearExpression2 expr, IntegerValue ub)
Same as above, but only for the upper bound.
void SetLevel(int level) final
Called each time we change decision level.
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void Resize(int num_variables)
bool PropagateOutgoingArcs(IntegerVariable var)
bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
This version check current precedence. It is however "slow".
PrecedencesPropagator(const PrecedencesPropagator &)=delete
This type is neither copyable nor movable.
void AddPrecedence(IntegerVariable i1, IntegerVariable i2)
void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
void AddPrecedenceWithAllOptions(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, IntegerVariable offset_var, absl::Span< const Literal > presence_literals)
Generic function that cover all of the above case and more.
PrecedencesPropagator(Model *model)
~PrecedencesPropagator() override
void AddPrecedenceWithVariableOffset(IntegerVariable i1, IntegerVariable i2, IntegerVariable offset_var)
PrecedencesPropagator & operator=(const PrecedencesPropagator &)=delete
void AddConditionalPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset, Literal l)
void Untrail(const Trail &trail, int trail_index) final
void AddConditionalPrecedence(IntegerVariable i1, IntegerVariable i2, Literal l)
Same as above, but the relation is only true when the given literal is.
PropagatorInterface()=default
SatPropagator(const std::string &name)
void AddPropagator(SatPropagator *propagator)
Simple class to add statistics by name and print them at the end.
void push_back(const value_type &val)
void resize(size_type new_size)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Fix v to a given value.
std::function< void(Model *)> LowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset)
a + offset <= b.
std::function< void(Model *)> AffineCoeffOneLowerOrEqualWithOffset(AffineExpression a, AffineExpression b, int64_t offset)
a + offset <= b. (when a and b are of the form 1 * var + offset).
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> EqualityWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset)
a + offset == b.
std::function< void(Model *)> GreaterOrEqual(IntegerVariable v, int64_t lb)
void AddConditionalSum3LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, IntegerVariable c, int64_t ub, Model *model)
void AddConditionalSum2LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, int64_t ub, Model *model)
l => (a + b <= ub).
std::function< void(Model *)> ConditionalLowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le)
is_le => (a + offset <= b).
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
static int input(yyscan_t yyscanner)
std::vector< IntegerValue > offsets
std::vector< int > indices
void SimpleCanonicalization()
LinearTerm(IntegerVariable v, IntegerValue c)
bool operator==(const LinearTerm &other) const
bool operator==(const Relation &other) const