SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
CardinalityEncoder.cpp
Go to the documentation of this file.
1 #include"CardinalityEncoder.h"
2 
3 #include <array>
4 #include <deque>
5 #include <iterator>
6 
7 namespace smtrat {
8  std::optional<FormulaT> CardinalityEncoder::doEncode(const ConstraintT& constraint) {
9  bool allCoeffPositive = true;
10  bool allCoeffNegative = true;
11  unsigned numberOfTerms = 0;
12 
13  for (const auto& it : constraint.lhs()) {
14  if (it.is_constant()) continue;
15  assert(it.coeff() == 1 || it.coeff() == -1);
16 
17  if (it.coeff() < 0) allCoeffPositive = false;
18  if (it.coeff() > 0) allCoeffNegative = false;
19 
20  numberOfTerms += 1;
21  }
22 
23  assert(!allCoeffNegative || !allCoeffPositive);
24 
25  bool mixedCoeff = !allCoeffNegative && !allCoeffPositive;
26  Rational constant = -constraint.lhs().constant_part();
27 
28  if (constraint.relation() == carl::Relation::EQ && !mixedCoeff) {
29  // For equality -x1 - x2 - x3 ~ -2 and x1 + x2 + x3 ~ 2 are the same
30  ConstraintT normalizedConstraint;
31  if (allCoeffNegative) {
32  normalizedConstraint = ConstraintT(constraint.lhs() * Rational(-1), constraint.relation());
33  constant = constant * Rational(-1);
34  } else {
35  normalizedConstraint = constraint;
36  }
37 
38  // x1 + x2 + x3 = -1 or -x1 - x2 - x3 = 1
39  if (constant < 0) return FormulaT(carl::FormulaType::FALSE);
40  // x1 + x2 + x3 = 4 or -x1 - x2 - x3 = -4
41  if (numberOfTerms < constant) return FormulaT(carl::FormulaType::FALSE);
42 
43  return encodeExactly(normalizedConstraint);
44  } else if (!mixedCoeff) {
45  // we only expect normalized constraints!
46  assert(constraint.relation() == carl::Relation::LEQ);
47 
48  // -x1 -x2 -x3 <= -4 iff x1 + x2 + x3 >= 4
49  if (allCoeffNegative && carl::abs(constant) > constraint.variables().size())
50  return FormulaT(carl::FormulaType::FALSE);
51  // x1 + x2 + x3 <= 10
52  if (allCoeffPositive && constant >= constraint.variables().size())
53  return FormulaT(carl::FormulaType::TRUE);
54  // -x1 - x2 - x3 <= 0 iff x1 + x2 + x3 >= 0
55  if (allCoeffNegative && constant >= 0) return FormulaT(carl::FormulaType::TRUE);
56 
57 
58  if (allCoeffNegative) return encodeAtLeast(constraint);
59  else if (allCoeffPositive) return encodeAtMost(constraint);
60  else assert(false);
61 
62  } else {
63  assert(mixedCoeff);
64 
65  // TODO we probably want to put more thought into this
66  return {};
67  }
68 
69  assert(false && "All cases should have been handled - a return statement is missing");
70  return {};
71  }
72 
73  std::optional<FormulaT> CardinalityEncoder::encodeExactly(const ConstraintT& constraint) {
74  // if (!encodeAsBooleanFormula(constraint)) return std::nullopt;
75 
76  return encodeExactly(constraint.variables().as_vector(), -constraint.lhs().constant_part());
77  }
78 
79  FormulaT CardinalityEncoder::encodeExactly(const std::vector<carl::Variable>& variables, const Rational constant) {
80  // assert(variables.size() > constant && "This should have been handled before!");
81  assert(constant >= 0 && "This should have been handled before!");
82 
83  // either a permutation contains the negation of the variable or the positive variable
84  // This has nothing to do with the actual coefficient signs!
85  std::deque<bool> signs;
86 
87  for (unsigned int i = 0; i < variables.size(); i++) {
88  if (i < constant) signs.push_front(true);
89  else signs.push_front(false);
90  }
91 
92  FormulasT resultFormulaSet;
93  do {
94  FormulasT terms;
95  for (unsigned i = 0; i < signs.size(); i++) {
96  if(signs[i]){
97  terms.push_back(FormulaT(variables[i]));
98  }else{
99  terms.push_back(FormulaT(carl::FormulaType::NOT, FormulaT(variables[i])));
100  }
101  }
102 
103  resultFormulaSet.push_back(FormulaT(carl::FormulaType::AND, std::move(terms)));
104  } while(std::next_permutation(std::begin(signs), std::end(signs)));
105 
106  FormulaT resultFormula = FormulaT(carl::FormulaType::OR, resultFormulaSet);
107 
108  return resultFormula;
109  }
110 
111  std::optional<FormulaT> CardinalityEncoder::encodeAtLeast(const ConstraintT& constraint) {
113  Rational constant = constraint.lhs().constant_part();
114  assert(constant > 0);
115  if (constant <= constraint.variables().size()/2) {
116  for (Rational i = constant - 1; i > 0; i--) {
117  result.push_back(!encodeExactly(constraint.variables().as_vector(), i));
118  }
119 
120  FormulasT orSet;
121  for (const auto& var : constraint.variables()) {
122  orSet.push_back(FormulaT(var));
123  }
124 
128  } else { // constant > variables.size()/2
129  for (Rational i = constant; i <= constraint.variables().size(); i++) {
130  result.push_back(encodeExactly(constraint.variables().as_vector(), i));
131  }
132 
134  }
135  }
136 
137  std::optional<FormulaT> CardinalityEncoder::encodeAtMost(const ConstraintT& constraint) {
139 
140  Rational constant = -constraint.lhs().constant_part();
141  if (constant < constraint.variables().size()/2) {
142  for (unsigned i = 0 ; i <= constant; i++) {
143  result.push_back(FormulaT(encodeExactly(constraint.variables().as_vector(), i)));
144  }
145 
147 
148  } else {
149  for (size_t i = constraint.variables().size() ; i > constant; i--) {
150  result.push_back(FormulaT(!encodeExactly(constraint.variables().as_vector(), i)));
151  }
152 
154  }
155  }
156 
157  bool CardinalityEncoder::canEncode(const ConstraintT& constraint) {
158  bool encodable = true;
159  bool allCoeffPositive = true;
160  bool allCoeffNegative = true;
161 
162  for (const auto& it : constraint.lhs()) {
163  if (it.is_constant()) continue;
164 
165  encodable = encodable && (it.coeff() == 1 || it.coeff() == -1);
166  if (it.coeff() < 0) allCoeffPositive = false;
167  if (it.coeff() > 0) allCoeffNegative = false;
168  }
169 
170  encodable = encodable && allCoeffNegative != allCoeffPositive;
171 
172  return encodable;
173  }
174 
176  Rational factorial(std::size_t);
177 
179 
180  SMTRAT_LOG_DEBUG("smtrat.pbc", "Calculating encodingSize for Cardinality.");
181 
182  std::size_t nVars = constraint.variables().size();
183  Rational constantPart = carl::abs(constraint.lhs().constant_part());
184 
185  Rational binomPositiveFormulation = factorial(nVars)/(factorial(constantPart) * factorial(nVars - constantPart));
186  Rational binomNegativeFormulation = factorial(nVars)/(factorial(nVars - constantPart - 1) * factorial(constantPart - 1));
187 
188  return std::min(binomPositiveFormulation, binomNegativeFormulation);
189  }
190 
191  Rational factorial(std::size_t n) {
192  return factorial(Rational(n));
193  }
194 
196  Rational res = 1;
197  for (Rational i = 1; i < n; i++) {
198  res = res * i;
199  }
200 
201  return res;
202  }
203 }
204 
std::optional< FormulaT > encodeAtMost(const ConstraintT &constraint)
std::optional< FormulaT > encodeAtLeast(const ConstraintT &constraint)
Rational encodingSize(const ConstraintT &constraint)
bool canEncode(const ConstraintT &constraint)
std::optional< FormulaT > doEncode(const ConstraintT &constraint)
std::optional< FormulaT > encodeExactly(const ConstraintT &constraint)
int var(Lit p)
Definition: SolverTypes.h:64
Numeric abs(const Numeric &_value)
Calculates the absolute value of the given Numeric.
Definition: Numeric.cpp:276
Class to create the formulas for axioms.
carl::Formula< Poly > FormulaT
Definition: types.h:37
Rational factorial(Rational n)
carl::Constraint< Poly > ConstraintT
Definition: types.h:29
carl::Formulas< Poly > FormulasT
Definition: types.h:39
mpq_class Rational
Definition: types.h:19
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35