SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
CSplitModule.tpp
Go to the documentation of this file.
1 /**
2  * @file CSplitModule.cpp
3  * @author Ă–mer Sali <oemer.sali@rwth-aachen.de>
4  *
5  * @version 2018-04-04
6  * Created on 2017-11-01.
7  */
8 
9 #include "CSplitModule.h"
10 
11 #include <carl-arith/interval/SetTheory.h>
12 
13 namespace smtrat
14 {
15  template<class Settings>
16  CSplitModule<Settings>::CSplitModule(const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager)
17  : Module( _formula, _conditionals, _manager )
18  , mPurifications()
19  , mExpansions()
20  , mLinearizations()
21  , mVariableBounds()
22  , mLRAModel()
23  , mCheckedWithBackends(false)
24  {}
25 
26  template<class Settings>
27  bool CSplitModule<Settings>::addCore(ModuleInput::const_iterator _subformula)
28  {
29  addReceivedSubformulaToPassedFormula(_subformula);
30  const FormulaT& formula{_subformula->formula()};
31  if (formula.type() == carl::FormulaType::FALSE)
32  mInfeasibleSubsets.push_back({formula});
33  else if (formula.is_bound())
34  {
35  /// Update the variable domain with the asserted bound
36  mVariableBounds.addBound(formula, formula);
37  const carl::Variable& variable{*formula.variables().begin()};
38  auto expansionIter{mExpansions.firstFind(variable)};
39  if (expansionIter == mExpansions.end())
40  expansionIter = mExpansions.emplace(variable);
41  expansionIter->mChangedBounds = true;
42  if (mVariableBounds.isConflicting())
43  mInfeasibleSubsets.emplace_back(mVariableBounds.getConflict());
44  }
45  else if (formula.type() == carl::FormulaType::CONSTRAINT)
46  {
47  /// Normalize the left hand side of the constraint and turn the relation accordingly
48  const ConstraintT& constraint{formula.constraint()};
49  const Poly normalization{constraint.lhs().normalize()};
50  carl::Relation relation{constraint.relation()};
51  if (carl::is_negative(constraint.lhs().lcoeff()))
52  relation = carl::turn_around(relation);
53 
54  /// Purifiy and discretize the normalized left hand side to construct the linearization
55  auto linearizationIter{mLinearizations.firstFind(normalization)};
56  if (linearizationIter == mLinearizations.end())
57  {
58  Poly discretization;
59  std::vector<Purification *> purifications;
60  bool hasRealVariables{false};
61  for (TermT term : normalization)
62  {
63  if (!term.is_constant())
64  {
65  size_t realVariables{0};
66  for (const auto& exponent : term.monomial()->exponents())
67  if (exponent.first.type() == carl::VariableType::VT_REAL)
68  realVariables += exponent.second;
69  if (realVariables)
70  {
71  term.coeff() /= carl::pow(Rational(Settings::discrDenom), realVariables);
72  hasRealVariables = true;
73  }
74 
75  if (!term.is_linear())
76  {
77  Purification& purification{mPurifications[term.monomial()]};
78  purifications.emplace_back(&purification);
79  term = term.coeff()*purification.mSubstitutions[0];
80  }
81  else if (realVariables)
82  {
83  const carl::Variable variable{term.single_variable()};
84  auto expansionIter{mExpansions.firstFind(variable)};
85  if (expansionIter == mExpansions.end())
86  expansionIter = mExpansions.emplace(variable);
87  term = term.coeff()*expansionIter->mQuotients[0];
88  }
89  }
90  discretization += term;
91  }
92  linearizationIter = mLinearizations.emplace(normalization, std::move(discretization.normalize()), std::move(purifications), hasRealVariables);
93  }
94  Linearization& linearization{*linearizationIter};
95  propagateFormula(FormulaT(linearization.mLinearization, relation), true);
96  if (linearization.mRelations.empty())
97  for (Purification *purification : linearization.mPurifications)
98  ++purification->mUsage;
99  linearization.mRelations.emplace(relation);
100 
101  /// Check if the asserted relation trivially conflicts with other asserted relations
102  switch (relation)
103  {
104  case carl::Relation::EQ:
105  if (linearization.mRelations.count(carl::Relation::NEQ))
106  mInfeasibleSubsets.push_back({
107  FormulaT(normalization, carl::Relation::EQ),
108  FormulaT(normalization, carl::Relation::NEQ)
109  });
110  if (linearization.mRelations.count(carl::Relation::LESS))
111  mInfeasibleSubsets.push_back({
112  FormulaT(normalization, carl::Relation::EQ),
113  FormulaT(normalization, carl::Relation::LESS)
114  });
115  if (linearization.mRelations.count(carl::Relation::GREATER))
116  mInfeasibleSubsets.push_back({
117  FormulaT(normalization, carl::Relation::EQ),
118  FormulaT(normalization, carl::Relation::GREATER)
119  });
120  break;
121  case carl::Relation::NEQ:
122  if (linearization.mRelations.count(carl::Relation::EQ))
123  mInfeasibleSubsets.push_back({
124  FormulaT(normalization, carl::Relation::NEQ),
125  FormulaT(normalization, carl::Relation::EQ)
126  });
127  break;
128  case carl::Relation::LESS:
129  if (linearization.mRelations.count(carl::Relation::EQ))
130  mInfeasibleSubsets.push_back({
131  FormulaT(normalization, carl::Relation::LESS),
132  FormulaT(normalization, carl::Relation::EQ)
133  });
134  if (linearization.mRelations.count(carl::Relation::GEQ))
135  mInfeasibleSubsets.push_back({
136  FormulaT(normalization, carl::Relation::LESS),
137  FormulaT(normalization, carl::Relation::GEQ)
138  });
139  [[fallthrough]];
140  case carl::Relation::LEQ:
141  if (linearization.mRelations.count(carl::Relation::GREATER))
142  mInfeasibleSubsets.push_back({
143  FormulaT(normalization, relation),
144  FormulaT(normalization, carl::Relation::GREATER)
145  });
146  break;
147  case carl::Relation::GREATER:
148  if (linearization.mRelations.count(carl::Relation::EQ))
149  mInfeasibleSubsets.push_back({
150  FormulaT(normalization, carl::Relation::GREATER),
151  FormulaT(normalization, carl::Relation::EQ)
152  });
153  if (linearization.mRelations.count(carl::Relation::LEQ))
154  mInfeasibleSubsets.push_back({
155  FormulaT(normalization, carl::Relation::GREATER),
156  FormulaT(normalization, carl::Relation::LEQ)
157  });
158  [[fallthrough]];
159  case carl::Relation::GEQ:
160  if (linearization.mRelations.count(carl::Relation::LESS))
161  mInfeasibleSubsets.push_back({
162  FormulaT(normalization, relation),
163  FormulaT(normalization, carl::Relation::LESS)
164  });
165  break;
166  default:
167  assert(false);
168  }
169  }
170  return mInfeasibleSubsets.empty();
171  }
172 
173  template<class Settings>
174  void CSplitModule<Settings>::removeCore(ModuleInput::const_iterator _subformula)
175  {
176  const FormulaT& formula{_subformula->formula()};
177  if (formula.is_bound())
178  {
179  /// Update the variable domain with the removed bound
180  mVariableBounds.removeBound(formula, formula);
181  mExpansions.firstAt(*formula.variables().begin()).mChangedBounds = true;
182  }
183  else if (formula.type() == carl::FormulaType::CONSTRAINT)
184  {
185  /// Normalize the left hand side of the constraint and turn the relation accordingly
186  const ConstraintT& constraint{formula.constraint()};
187  const Poly normalization{constraint.lhs().normalize()};
188  carl::Relation relation{constraint.relation()};
189  if (carl::is_negative(constraint.lhs().lcoeff()))
190  relation = carl::turn_around(relation);
191 
192  /// Retrieve the normalized constraint and mark the separator object as changed
193  Linearization& linearization{mLinearizations.firstAt(normalization)};
194  propagateFormula(FormulaT(linearization.mLinearization, relation), false);
195  linearization.mRelations.erase(relation);
196  if (linearization.mRelations.empty())
197  for (Purification *purification : linearization.mPurifications)
198  ++purification->mUsage;
199  }
200  }
201 
202  template<class Settings>
203  void CSplitModule<Settings>::updateModel() const
204  {
205  if(!mModelComputed)
206  {
207  clearModel();
208  if (mCheckedWithBackends)
209  {
210  getBackendsModel();
211  excludeNotReceivedVariablesFromModel();
212  }
213  else
214  {
215  for (const Expansion& expansion : mExpansions)
216  if (receivedVariable(expansion.mRationalization))
217  {
218  Rational value{mLRAModel.at(expansion.mDiscretization).asRational()};
219  if (expansion.mRationalization.type() == carl::VariableType::VT_REAL)
220  value /= Settings::discrDenom;
221  mModel.emplace(expansion.mRationalization, value);
222  }
223  }
224  mModelComputed = true;
225  }
226  }
227 
228  template<class Settings>
229  Answer CSplitModule<Settings>::checkCore()
230  {
231  /// Report unsatisfiability if the already found conflicts are still unresolved
232  if (!mInfeasibleSubsets.empty())
233  return Answer::UNSAT;
234 
235  /// Apply the method only if the asserted formula is not trivially undecidable
236  if (rReceivedFormula().is_constraint_conjunction())
237  {
238  mLRAModule.push();
239  if (resetExpansions())
240  {
241  mCheckedWithBackends = false;
242  for (size_t i = 1; i <= Settings::maxIter; ++i)
243  if (mLRAModule.check(true) == Answer::SAT)
244  {
245  mLRAModel = mLRAModule.model();
246  mLRAModule.pop();
247  return Answer::SAT;
248  }
249  else
250  {
251  FormulaSetT conflict{mLRAModule.infeasibleSubsets()[0]};
252  if (bloatDomains(conflict))
253  {
254  mLRAModule.pop();
255  return analyzeConflict(conflict);
256  }
257  }
258  }
259  mLRAModule.pop();
260  }
261 
262  /// Check the asserted formula with the backends
263  mCheckedWithBackends = true;
264  Answer answer{runBackends()};
265  if (answer == Answer::UNSAT)
266  getInfeasibleSubsets();
267 
268  return answer;
269  }
270 
271  template<class Settings>
272  bool CSplitModule<Settings>::resetExpansions()
273  {
274  /// Update the variable domains and watch out for discretization conflicts
275  for (Expansion& expansion : mExpansions)
276  {
277  RationalInterval& maximalDomain{expansion.mMaximalDomain};
278  if (expansion.mChangedBounds)
279  {
280  maximalDomain = mVariableBounds.getInterval(expansion.mRationalization);
281  if (expansion.mRationalization.type() == carl::VariableType::VT_REAL)
282  maximalDomain *= Rational(Settings::discrDenom);
283  maximalDomain.integralPart_assign();
284  if (expansion.mMaximalDomain.is_unbounded())
285  expansion.mMaximalDomainSize = DomainSize::UNBOUNDED;
286  else if (expansion.mMaximalDomain.diameter() > Settings::maxDomainSize)
287  expansion.mMaximalDomainSize = DomainSize::LARGE;
288  else
289  expansion.mMaximalDomainSize = DomainSize::SMALL;
290  expansion.mChangedBounds = false;
291  }
292  if (maximalDomain.is_empty())
293  return false;
294  expansion.mActiveDomain = RationalInterval::empty_interval();
295  expansion.mPurifications.clear();
296  }
297 
298  /// Activate all used purifications bottom-up
299  for (auto purificationIter = mPurifications.begin(); purificationIter != mPurifications.end(); ++purificationIter)
300  {
301  Purification& purification{purificationIter->second};
302  if (purification.mUsage)
303  {
304  carl::Monomial::Arg monomial{purificationIter->first};
305 
306  /// Find set of variables with maximal domain size
307  carl::Variables maxVariables;
308  DomainSize maxDomainSize{DomainSize::SMALL};
309  for (const auto& exponent : monomial->exponents())
310  {
311  const carl::Variable& variable{exponent.first};
312  auto expansionIter{mExpansions.firstFind(variable)};
313  if (expansionIter == mExpansions.end())
314  expansionIter = mExpansions.emplace(variable);
315  Expansion& expansion{*expansionIter};
316 
317  if (maxDomainSize <= expansion.mMaximalDomainSize)
318  {
319  if (maxDomainSize < expansion.mMaximalDomainSize)
320  {
321  maxVariables.clear();
322  maxDomainSize = expansion.mMaximalDomainSize;
323  }
324  maxVariables.emplace(variable);
325  }
326  }
327 
328  /// Find a locally optimal reduction for the monomial
329  const auto isReducible = [&](const auto& purificationsEntry) {
330  return purificationsEntry.second.mActive
331  && monomial->divisible(purificationsEntry.first)
332  && std::any_of(
333  maxVariables.begin(),
334  maxVariables.end(),
335  [&](const carl::Variable& variable) {
336  return purificationsEntry.first->has(variable);
337  }
338  );
339  };
340  auto reductionIter{std::find_if(std::make_reverse_iterator(purificationIter), mPurifications.rend(), isReducible)};
341 
342  /// Activate the sequence of reductions top-down
343  carl::Monomial::Arg guidance;
344  if (reductionIter == mPurifications.rend())
345  monomial->divide(*maxVariables.begin(), guidance);
346  else
347  monomial->divide(reductionIter->first, guidance);
348  auto hintIter{purificationIter};
349  for (const auto& exponentPair : guidance->exponents())
350  {
351  const carl::Variable& variable{exponentPair.first};
352  Expansion& expansion{mExpansions.firstAt(variable)};
353  for (carl::exponent exponent = 1; exponent <= exponentPair.second; ++exponent)
354  {
355  hintIter->second.mActive = true;
356  expansion.mPurifications.emplace_back(&hintIter->second);
357  monomial->divide(variable, monomial);
358  if (monomial->isAtMostLinear())
359  hintIter->second.mReduction = mExpansions.firstAt(monomial->single_variable()).mQuotients[0];
360  else
361  {
362  auto temp{mPurifications.emplace_hint(hintIter, std::piecewise_construct, std::make_tuple(monomial), std::make_tuple())};
363  hintIter->second.mReduction = temp->second.mSubstitutions[0];
364  hintIter = temp;
365  }
366  }
367  }
368  }
369  else
370  purification.mActive = false;
371  }
372 
373  /// Activate expansions that are used for case splits and deactivate them otherwise
374  for (Expansion& expansion : mExpansions)
375  {
376  /// Calculate the center point where the initial domain is located
377  expansion.mNucleus = 0;
378  if (expansion.mMaximalDomain.lower_bound_type() != carl::BoundType::INFTY
379  && expansion.mNucleus < expansion.mMaximalDomain.lower())
380  expansion.mNucleus = expansion.mMaximalDomain.lower();
381  else if (expansion.mMaximalDomain.upper_bound_type() != carl::BoundType::INFTY
382  && expansion.mNucleus > expansion.mMaximalDomain.upper())
383  expansion.mNucleus = expansion.mMaximalDomain.upper();
384 
385  /// Calculate and activate the corresponding domain
386  RationalInterval domain(0, 1);
387  domain.mul_assign(carl::Interval<Rational>(Rational(Settings::initialRadius)));
388  domain.add_assign(carl::Interval<Rational>(expansion.mNucleus));
389  domain = carl::set_intersection(domain, expansion.mMaximalDomain);
390  changeActiveDomain(expansion, std::move(domain));
391  }
392 
393  return true;
394  }
395 
396  template<class Settings>
397  bool CSplitModule<Settings>::bloatDomains(const FormulaSetT& LRAConflict)
398  {
399  /// Data structure for potential bloating candidates
400  struct Candidate
401  {
402  Expansion& mExpansion;
403  const Rational mDirection;
404  const Rational mRadius;
405 
406  Candidate(Expansion& expansion, Rational&& direction, Rational&& radius)
407  : mExpansion(expansion)
408  , mDirection(std::move(direction))
409  , mRadius(std::move(radius))
410  {}
411 
412  bool operator<(const Candidate& rhs) const
413  {
414  if (carl::is_one(mDirection*rhs.mDirection))
415  return mRadius < rhs.mRadius;
416  else if (carl::is_one(mDirection))
417  return mRadius < Rational(Settings::thresholdRadius);
418  else
419  return rhs.mRadius >= Rational(Settings::thresholdRadius);
420  }
421  };
422  std::set<Candidate> candidates;
423 
424  /// Scan the infeasible subset of the LRA solver for potential candidates
425  for (const FormulaT& formula : LRAConflict)
426  if (formula.is_bound())
427  {
428  const ConstraintT& constraint{formula.constraint()};
429  const carl::Variable variable{constraint.variables().as_vector().front()};
430  auto expansionIter{mExpansions.secondFind(variable)};
431  if (expansionIter != mExpansions.end())
432  {
433  Expansion& expansion{*expansionIter};
434  Rational direction;
435  if (carl::is_lower_bound(constraint)
436  && (expansion.mMaximalDomain.lower_bound_type() == carl::BoundType::INFTY
437  || expansion.mMaximalDomain.lower() < expansion.mActiveDomain.lower()))
438  direction = -1;
439  else if (carl::is_upper_bound(constraint)
440  && (expansion.mMaximalDomain.upper_bound_type() == carl::BoundType::INFTY
441  || expansion.mMaximalDomain.upper() > expansion.mActiveDomain.upper()))
442  direction = 1;
443  if (!carl::is_zero(direction))
444  {
445  Rational radius{(direction*(expansion.mActiveDomain-expansion.mNucleus)).upper()};
446  if (radius <= Settings::maximalRadius)
447  {
448  candidates.emplace(expansion, std::move(direction), std::move(radius));
449  if (candidates.size() > Settings::maxBloatedDomains)
450  candidates.erase(std::prev(candidates.end()));
451  }
452  }
453  }
454  }
455 
456  /// Change the active domain of the candidates with highest priority
457  for (const Candidate& candidate : candidates)
458  {
459  RationalInterval domain;
460  if (candidate.mRadius <= Settings::thresholdRadius)
461  domain = RationalInterval(0, 1);
462  else if (candidate.mExpansion.mPurifications.empty())
463  domain = RationalInterval(0, carl::BoundType::WEAK, 0, carl::BoundType::INFTY);
464  else
465  domain = RationalInterval(0, candidate.mRadius);
466  domain.mul_assign(carl::Interval<Rational>(candidate.mDirection));
467  domain.add_assign(candidate.mExpansion.mActiveDomain);
468  domain = carl::set_intersection(domain, candidate.mExpansion.mMaximalDomain);
469  changeActiveDomain(candidate.mExpansion, std::move(domain));
470  }
471 
472  /// Report if any variable domain was bloated
473  return candidates.empty();
474  }
475 
476  template<class Settings>
477  Answer CSplitModule<Settings>::analyzeConflict(const FormulaSetT& LRAConflict)
478  {
479  FormulaSetT infeasibleSubset;
480  for (const FormulaT& formula : LRAConflict)
481  {
482  if (formula.is_bound())
483  {
484  auto expansionIter{mExpansions.secondFind(*formula.variables().begin())};
485  if (expansionIter != mExpansions.end())
486  {
487  const Expansion& expansion{*expansionIter};
488  if (expansion.mRationalization.type() == carl::VariableType::VT_REAL
489  || expansion.mMaximalDomain != expansion.mActiveDomain)
490  return Answer::UNKNOWN;
491  else
492  {
493  FormulaSetT boundOrigins{mVariableBounds.getOriginSetOfBounds(expansion.mRationalization)};
494  infeasibleSubset.insert(boundOrigins.begin(), boundOrigins.end());
495  }
496  }
497  }
498  else if (formula.type() == carl::FormulaType::CONSTRAINT)
499  {
500  const ConstraintT& constraint{formula.constraint()};
501  auto linearizationIter{mLinearizations.secondFind(constraint.lhs().normalize())};
502  if (linearizationIter != mLinearizations.end())
503  {
504  const Linearization& linearization{*linearizationIter};
505  if (linearization.mHasRealVariables)
506  return Answer::UNKNOWN;
507  else
508  {
509  carl::Relation relation{constraint.relation()};
510  if (carl::is_negative(constraint.lhs().lcoeff()))
511  relation = carl::turn_around(relation);
512  infeasibleSubset.emplace(linearization.mNormalization, relation);
513  }
514  }
515  }
516  }
517  mInfeasibleSubsets.emplace_back(std::move(infeasibleSubset));
518  return Answer::UNSAT;
519  }
520 
521  template<class Settings>
522  void CSplitModule<Settings>::changeActiveDomain(Expansion& expansion, RationalInterval&& domain)
523  {
524  RationalInterval activeDomain{std::move(expansion.mActiveDomain)};
525  expansion.mActiveDomain = domain;
526 
527  /// Update the variable bounds
528  if (!activeDomain.is_empty())
529  {
530  if (activeDomain.lower_bound_type() != carl::BoundType::INFTY
531  && (domain.lower_bound_type() == carl::BoundType::INFTY
532  || domain.lower() != activeDomain.lower()
533  || domain.is_empty()))
534  propagateFormula(FormulaT(expansion.mQuotients[0]-Poly(activeDomain.lower()), carl::Relation::GEQ), false);
535  if (activeDomain.upper_bound_type() != carl::BoundType::INFTY
536  && (domain.upper_bound_type() == carl::BoundType::INFTY
537  || domain.upper() != activeDomain.upper()
538  || domain.is_empty()))
539  propagateFormula(FormulaT(expansion.mQuotients[0]-Poly(activeDomain.upper()), carl::Relation::LEQ), false);
540  }
541  if (!domain.is_empty())
542  {
543  if (domain.lower_bound_type() != carl::BoundType::INFTY
544  && (activeDomain.lower_bound_type() == carl::BoundType::INFTY
545  || activeDomain.lower() != domain.lower()
546  || activeDomain.is_empty()))
547  propagateFormula(FormulaT(expansion.mQuotients[0]-Poly(domain.lower()), carl::Relation::GEQ), true);
548  if (domain.upper_bound_type() != carl::BoundType::INFTY
549  && (activeDomain.upper_bound_type() == carl::BoundType::INFTY
550  || activeDomain.upper() != domain.upper()
551  || activeDomain.is_empty()))
552  propagateFormula(FormulaT(expansion.mQuotients[0]-Poly(domain.upper()), carl::Relation::LEQ), true);
553  }
554 
555  /// Check if the digits of the expansion need to be encoded
556  if (expansion.mPurifications.empty())
557  {
558  activeDomain = RationalInterval::empty_interval();
559  domain = RationalInterval::empty_interval();
560  }
561 
562  /// Update the case splits of the corresponding digits
563  for (size_t i = 0; activeDomain != domain; ++i)
564  {
565  if (domain.diameter() <= Settings::maxDomainSize)
566  {
567  /// Update the currently active linear encoding
568  Rational lower{activeDomain.is_empty() ? domain.lower() : activeDomain.lower()};
569  Rational upper{activeDomain.is_empty() ? domain.lower() : activeDomain.upper()+1};
570  for (const Purification *purification : expansion.mPurifications)
571  {
572  for (Rational alpha = domain.lower(); alpha < lower; ++alpha)
573  propagateFormula(
574  FormulaT(
575  carl::FormulaType::IMPLIES,
576  FormulaT(Poly(expansion.mQuotients[i])-Poly(alpha), carl::Relation::EQ),
577  FormulaT(Poly(purification->mSubstitutions[i])-Poly(alpha)*Poly(purification->mReduction), carl::Relation::EQ)
578  ),
579  true
580  );
581  for (Rational alpha = upper; alpha <= domain.upper(); ++alpha)
582  propagateFormula(
583  FormulaT(
584  carl::FormulaType::IMPLIES,
585  FormulaT(Poly(expansion.mQuotients[i])-Poly(alpha), carl::Relation::EQ),
586  FormulaT(Poly(purification->mSubstitutions[i])-Poly(alpha)*Poly(purification->mReduction), carl::Relation::EQ)
587  ),
588  true
589  );
590  }
591  }
592  else if (activeDomain.diameter() <= Settings::maxDomainSize)
593  {
594  /// Switch from the linear to a logarithmic encoding
595  if (expansion.mQuotients.size() <= i+1)
596  {
597  expansion.mQuotients.emplace_back(carl::fresh_integer_variable());
598  expansion.mRemainders.emplace_back(carl::fresh_integer_variable());
599  }
600  for (Purification *purification : expansion.mPurifications)
601  {
602  if (purification->mSubstitutions.size() <= i+1)
603  purification->mSubstitutions.emplace_back(carl::fresh_integer_variable());
604  for (Rational alpha = activeDomain.lower(); alpha <= activeDomain.upper(); ++alpha)
605  propagateFormula(
606  FormulaT(
607  carl::FormulaType::IMPLIES,
608  FormulaT(Poly(expansion.mQuotients[i])-Poly(alpha), carl::Relation::EQ),
609  FormulaT(Poly(purification->mSubstitutions[i])-Poly(alpha)*Poly(purification->mReduction), carl::Relation::EQ)
610  ),
611  false
612  );
613  for (Rational alpha = 0; alpha < Settings::expansionBase; ++alpha)
614  propagateFormula(
615  FormulaT(
616  carl::FormulaType::IMPLIES,
617  FormulaT(Poly(expansion.mRemainders[i])-Poly(alpha), carl::Relation::EQ),
618  FormulaT(Poly(purification->mSubstitutions[i])-Poly(Settings::expansionBase)*Poly(purification->mSubstitutions[i+1])-Poly(alpha)*Poly(purification->mReduction), carl::Relation::EQ)
619  ),
620  true
621  );
622  }
623  propagateFormula(FormulaT(Poly(expansion.mQuotients[i])-Poly(Settings::expansionBase)*Poly(expansion.mQuotients[i+1])-Poly(expansion.mRemainders[i]), carl::Relation::EQ), true);
624  propagateFormula(FormulaT(Poly(expansion.mRemainders[i]), carl::Relation::GEQ), true);
625  propagateFormula(FormulaT(Poly(expansion.mRemainders[i])-Poly(Settings::expansionBase-1), carl::Relation::LEQ), true);
626  }
627 
628  /// Calculate the domain of the next digit
629  if (!activeDomain.is_empty()) {
630  if (activeDomain.diameter() <= Settings::maxDomainSize)
631  activeDomain = RationalInterval::empty_interval();
632  else
633  activeDomain = carl::floor(activeDomain/Rational(Settings::expansionBase));
634  }
635  if (!domain.is_empty()) {
636  if (domain.diameter() <= Settings::maxDomainSize)
637  domain = RationalInterval::empty_interval();
638  else
639  domain = carl::floor(domain/Rational(Settings::expansionBase));
640  }
641 
642  /// Update the variable bounds of the next digit
643  if (!activeDomain.is_empty())
644  {
645  if (domain.is_empty() || domain.lower() != activeDomain.lower())
646  propagateFormula(FormulaT(expansion.mQuotients[i+1]-Poly(activeDomain.lower()), carl::Relation::GEQ), false);
647  if (domain.is_empty() || domain.upper() != activeDomain.upper())
648  propagateFormula(FormulaT(expansion.mQuotients[i+1]-Poly(activeDomain.upper()), carl::Relation::LEQ), false);
649  }
650  if (!domain.is_empty())
651  {
652  if (activeDomain.is_empty() || activeDomain.lower() != domain.lower())
653  propagateFormula(FormulaT(expansion.mQuotients[i+1]-Poly(domain.lower()), carl::Relation::GEQ), true);
654  if (activeDomain.is_empty() || activeDomain.upper() != domain.upper())
655  propagateFormula(FormulaT(expansion.mQuotients[i+1]-Poly(domain.upper()), carl::Relation::LEQ), true);
656  }
657  }
658  }
659 
660  template<class Settings>
661  inline void CSplitModule<Settings>::propagateFormula(const FormulaT& formula, bool assert)
662  {
663  if (assert)
664  mLRAModule.add(formula);
665  else
666  mLRAModule.remove(std::find(mLRAModule.formulaBegin(), mLRAModule.formulaEnd(), formula));
667  }
668 }
669 
670