SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
IntervalPropagation.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "Dependencies.h"
4 
5 #include <carl-arith/interval/Interval.h>
6 #include <carl-arith/intervalcontraction/Contractor.h>
7 #include <carl-arith/interval/Sampling.h>
10 
11 namespace smtrat {
12 namespace mcsat {
13 namespace icp {
14 
15 struct QueueEntry {
16  double priority;
17  carl::contractor::Contractor<FormulaT, Poly> contractor;
18 };
19 
21 private:
22  const Model& mModel;
23  std::map<carl::Variable, carl::Interval<double>> mBox;
24  std::vector<QueueEntry> mContractors;
25 
27 
28  static constexpr double weight_age = 0.5;
29  static constexpr double threshold_priority = 0.1;
30  static constexpr double threshold_width = 0.1;
31 
33  return std::any_of(mContractors.begin(), mContractors.end(),
34  [](const auto& qe) { return qe.priority > threshold_priority; }
35  );
36  }
38  return std::any_of(mBox.begin(), mBox.end(),
39  [](const auto& dim) {
40  return (!dim.second.is_unbounded()) && dim.second.diameter() < threshold_width;
41  }
42  );
43  }
44  /**
45  * Samples a rational with a small representation size.
46  * If side < 0: from (|side+1|*lower + upper)/|side|
47  * If side > 0: from (lower + |side-1|*upper)/|side|
48  */
49  Rational sample_small_rational(const Rational& lower, const Rational& upper, int side) const {
50  int absside = std::abs(side);
51  carl::Interval<Rational> i(lower, upper);
52  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Sampling from " << i);
53  if (side < 0) {
54  i.set(
55  i.lower() * absside,
56  i.lower() * (absside - 1) + i.upper()
57  );
58  } else {
59  i.set(
60  i.lower() + i.upper() * (absside - 1),
61  i.upper() * absside
62  );
63  }
64  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "from " << i);
65  auto res = carl::sample_stern_brocot(i / Rational(absside));
66  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "-> " << res);
67  return res;
68  }
69 
70  std::pair<bool,double> update_model(carl::Variable v, const std::vector<carl::Interval<double>>& intervals) {
71  auto it = mBox.find(v);
72  assert(it != mBox.end());
73  auto& cur = it->second;
74  auto old = cur;
75  bool changed = false;
76  if (cur.lower_bound() < intervals.front().lower_bound()) {
77  cur = carl::Interval<double>(intervals.front().lower(), intervals.front().lower_bound_type(), cur.upper(), cur.upper_bound_type());
78  changed = true;
79  }
80  if (intervals.back().upper_bound() < cur.upper_bound()) {
81  cur = carl::Interval<double>(cur.lower(), cur.lower_bound_type(), intervals.back().upper(), intervals.back().upper_bound_type());
82  changed = true;
83  }
84  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", old << " -> " << cur);
85  if (old.is_infinite()) {
86  if (cur.is_infinite()) {
87  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "still infinite");
88  return std::make_pair(changed, 0.0);
89  } else {
90  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "no longer infinite");
91  assert(changed);
92  return std::make_pair(changed, 1.0);
93  }
94  } else if (old.is_unbounded()) {
95  assert(!cur.is_infinite());
96  if (cur.is_unbounded()) {
97  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "still unbounded");
98  if (old.lower() < cur.lower() || cur.upper() < old.upper()) {
99  assert(changed);
100  return std::make_pair(changed, threshold_priority / 2);
101  } else {
102  return std::make_pair(changed, 0.0);
103  }
104  } else {
105  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "no longer unbounded");
106  assert(changed);
107  return std::make_pair(changed, 1.0);
108  }
109  } else {
110  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "possibly reduced size");
111  auto size = old.diameter();
112  return std::make_pair(changed, (size - cur.diameter()) / size);
113  }
114  }
115 
116  std::optional<FormulaT> find_excluded_interval(carl::Variable v, const std::vector<carl::Interval<double>>& admissible) const {
117  if (mModel.find(v) == mModel.end()) {
118  return std::nullopt;
119  }
120  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Checking whether the current model is admissible...");
121  auto value = mModel.evaluated(v);
122  if (value.isRational()) {
123  Rational val = value.asRational();
124  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "model is " << val);
125  std::optional<Rational> lower;
126  std::optional<Rational> upper;
127  for (std::size_t i = 0; i < admissible.size(); ++i) {
128  const auto& a = admissible[i];
129  carl::Interval<Rational> cur(
130  carl::rationalize<Rational>(a.lower()),
131  a.lower_bound_type(),
132  carl::rationalize<Rational>(a.upper()),
133  a.upper_bound_type()
134  );
135  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "cur is " << cur);
136  if (val < cur) {
137  upper = cur.lower();
138  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "cur is above " << val);
139  break;
140  }
141  if (cur.contains(val)) {
142  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "cur contains " << val);
143  return std::nullopt;
144  }
145  lower = cur.upper();
146  }
147  if (lower || upper) {
148  FormulasT subs;
149  if (lower) {
150  lower = sample_small_rational(*lower, val, -100);
151  subs.emplace_back(v - *lower, carl::Relation::LEQ);
152  }
153  if (upper) {
154  upper = sample_small_rational(val, *upper, 100);
155  subs.emplace_back(v - *upper, carl::Relation::GEQ);
156  }
157  return FormulaT(carl::FormulaType::OR, std::move(subs));
158  }
159  }
160  return std::nullopt;
161  }
162 
163  auto construct_direct_conflict(carl::Variable v) const {
164  auto constraints = mDependencies.get(v, true);
165  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Constructing " << constraints);
166  return FormulaT(carl::FormulaType::OR, std::move(constraints));
167  }
168  auto construct_interval_conflict(carl::Variable v, const FormulaT& excluded) const {
169  auto constraints = mDependencies.get(v, true);
170  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Constructing " << constraints << " => " << excluded);
171  constraints.emplace_back(excluded);
172  return FormulaT(carl::FormulaType::OR, std::move(constraints));
173  }
174 
175  void validate_box() const {
176  #ifdef SMTRAT_DEVOPTION_Validation
177  FormulasT s;
178  FormulasT box;
179  for (const auto& kv: mBox) {
180  if (kv.second.lower_bound_type() != carl::BoundType::INFTY || kv.second.upper_bound_type() != carl::BoundType::INFTY) {
181  for (const auto& c : mDependencies.get(kv.first, false)) {
182  s.push_back(c);
183  }
184  }
185 
186  if (kv.second.lower_bound_type() == carl::BoundType::WEAK) {
187  // not l <= x
188  box.emplace_back(ConstraintT(Poly(carl::rationalize<Rational>(kv.second.lower())) - kv.first, carl::Relation::GREATER));
189  } else if (kv.second.lower_bound_type() == carl::BoundType::STRICT) {
190  box.emplace_back(ConstraintT(Poly(carl::rationalize<Rational>(kv.second.lower())) - kv.first, carl::Relation::GEQ));
191  }
192  if (kv.second.upper_bound_type() == carl::BoundType::WEAK) {
193  // not x <= u
194  box.emplace_back(ConstraintT(Poly(kv.first) - carl::rationalize<Rational>(kv.second.upper()), carl::Relation::GREATER));
195  } else if (kv.second.upper_bound_type() == carl::BoundType::STRICT) {
196  box.emplace_back(ConstraintT(Poly(kv.first) - carl::rationalize<Rational>(kv.second.upper()), carl::Relation::GEQ));
197  }
198  }
199  s.emplace_back(carl::FormulaType::OR, std::move(box));
200  SMTRAT_VALIDATION_ADD("smtrat.mcsat.icp.boxes", "box", FormulaT(carl::FormulaType::AND, s), false);
201  #endif
202  }
203 
204 public:
205  IntervalPropagation(const std::vector<carl::Variable>& vars, const std::vector<FormulaT>& constraints, const Model& model): mModel(model) {
206  for (auto v: vars) {
207  mBox.emplace(v, carl::Interval<double>(0, carl::BoundType::INFTY, 0, carl::BoundType::INFTY));
208  }
209  for (const auto& c: constraints) {
210  if (c.constraint().relation() == carl::Relation::NEQ) continue;
211  for (const auto& v: c.variables()) {
212  mContractors.emplace_back(QueueEntry {1.0, carl::contractor::Contractor<FormulaT, Poly>(c, c.constraint(), v)});
213  }
214  }
215  }
216 
217  std::optional<FormulaT> execute() {
218  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Current model: " << mBox);
219  while (true) {
221  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "No contraction candidate above the threshold, terminating.");
222  break;
223  }
225  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "The box is below the threshold, terminating.");
226  break;
227  }
228  bool contracted = false;
229  for (auto& c: mContractors) {
230  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "***************");
231  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Contracting " << mBox <<" with " << c.contractor.var() << " by " << c.contractor.origin());
232  auto res = c.contractor.contract(mBox);
233  if (res.empty()) {
234  mDependencies.add(c.contractor);
235  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Contracted to empty interval, conflict for " << c.contractor.var());
236  validate_box();
237  return construct_direct_conflict(c.contractor.var());
238  } else {
239  auto excluded = find_excluded_interval(c.contractor.var(), res);
240  if (excluded) {
241  mDependencies.add(c.contractor);
242  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Contracted to exclude current model, conflict for " << c.contractor.var());
243  validate_box();
244  return construct_interval_conflict(c.contractor.var(), *excluded);
245  } else {
246  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Contracted " << c.contractor.var() << " to " << res);
247  auto update_result = update_model(c.contractor.var(), res);
248  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Updated: " << update_result.first << "; Contraction factor: " << update_result.second);
249  if (update_result.first) {
250  contracted = true;
251  mDependencies.add(c.contractor);
252  }
253  c.priority = weight_age * c.priority + update_result.second * (1 - c.priority);
254  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "New priority: " << c.priority);
255  validate_box();
256  }
257  }
258  }
259  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "Current model: " << mBox);
260  if (!contracted) {
261  SMTRAT_LOG_DEBUG("smtrat.mcsat.icp", "No contraction candidate worked, reached a fixpoint.");
262  break;
263  }
264  }
265  return std::nullopt;
266  }
267 };
268 
269 }
270 }
271 } // smtrat
#define SMTRAT_VALIDATION_ADD(channel, name, formula, consistent)
Definition: Validation.h:26
std::vector< FormulaT > get(carl::Variable v, bool negate=true) const
Definition: Dependencies.h:40
void add(const Contractor &c)
Definition: Dependencies.h:26
std::pair< bool, double > update_model(carl::Variable v, const std::vector< carl::Interval< double >> &intervals)
IntervalPropagation(const std::vector< carl::Variable > &vars, const std::vector< FormulaT > &constraints, const Model &model)
auto construct_direct_conflict(carl::Variable v) const
auto construct_interval_conflict(carl::Variable v, const FormulaT &excluded) const
std::optional< FormulaT > find_excluded_interval(carl::Variable v, const std::vector< carl::Interval< double >> &admissible) const
Rational sample_small_rational(const Rational &lower, const Rational &upper, int side) const
Samples a rational with a small representation size.
std::map< carl::Variable, carl::Interval< double > > mBox
Numeric abs(const Numeric &_value)
Calculates the absolute value of the given Numeric.
Definition: Numeric.cpp:276
std::optional< FormulaT > qe(const FormulaT &input)
Definition: qe.cpp:14
std::vector< carl::Variable > vars(const std::pair< QuantifierType, std::vector< carl::Variable >> &p)
Definition: QEQuery.h:43
Class to create the formulas for axioms.
carl::Formula< Poly > FormulaT
Definition: types.h:37
carl::Model< Rational, Poly > Model
Definition: model.h:13
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
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
carl::contractor::Contractor< FormulaT, Poly > contractor