SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
CellApproximator.h
Go to the documentation of this file.
2 #include "ran_approximation.h"
3 #include "criteria.h"
4 #include <carl-arith/poly/Conversion.h>
5 
7 
9 
11  private:
15  carl::Variable m_var;
17 
20  const datastructures::Delineation& del() const {return m_r_del;}
21  carl::Variable var() const {return m_var;}
22  Assignment sample() const {return m_sample;}
24 
25  template<ApxPoly PA>
26  IR apx_bound(const IR& p, const RAN& bound, bool below);
27 
28  template<ApxPoly PA>
29  IR apx_between(const IR& p_l, const IR& p_u, const RAN& l, const RAN& u);
30  public:
31  template<typename T>
33  : m_r_proj(der->proj()), m_r_cell(der->cell()), m_r_del(der->delin()), m_var(der->main_var()),
34  m_sample(der->sample()) {}
35 
36  IR approximate_bound(const IR& p, const RAN& bound, bool below);
37  IR approximate_between(const IR& p_l, const IR& p_u, const RAN& l, const RAN& u);
38 
40 };
41 
42 template<>
43 inline IR CellApproximator::apx_bound<ApxPoly::SIMPLE>(const IR& /*p*/, const RAN& bound, bool below) {
44  Rational root = approximate_root<ApxSettings::root>(main_sample(),bound,below);
45  return IR(proj().polys()(carl::get_denom(root)*Polynomial(proj().polys().context(),var()) - carl::get_num(root)), 1); // TODO poly context
46 }
47 
48 template<>
49 inline IR CellApproximator::apx_bound<ApxPoly::LINEAR_GRADIENT>(const IR& p, const RAN& bound, bool below) {
50  Poly carl_poly = carl::convert<Poly,Polynomial>(proj().polys()(p.poly));
51  Poly derivative = carl::derivative(carl_poly, var());
52  Poly gradient = carl::substitute(derivative, var(), Poly(approximate_RAN(bound)));
53  if (carl::is_zero(gradient)) return apx_bound<ApxPoly::SIMPLE>(p, bound, below);
54  Poly carl_res = gradient*Poly(var()) - gradient*approximate_root<ApxSettings::root>(main_sample(),bound,below);
55  Polynomial res = carl::convert<Polynomial, Poly>(proj().polys().context(), carl_res);
56  return IR(proj().polys()(res), 1);
57 }
58 
59 template<>
60 inline IR CellApproximator::apx_bound<ApxPoly::TAYLOR>(const IR& p, const RAN& bound, bool below) {
61  assert(apx_settings().taylor_deg < proj().degree(p.poly));
62  assert(apx_settings().taylor_deg <= 2);
63 
64  Poly carl_poly = carl::convert<Poly,Polynomial>(proj().polys()(p.poly));
65 
66  std::size_t dim = sample().size();
67  VariableOrdering var_order = proj().polys().var_order();
68  auto sample_root = sample();
69  sample_root[var()] = bound; // TODO : can choose other points here (like the actual sample)
70  auto sample_new_root = sample();
71  sample_new_root[var()] = RAN(approximate_root<ApxSettings::root>(main_sample(), bound, below));
72  #ifdef SMTRAT_DEVOPTION_Statistics
73  std::size_t leftOutVars = 0;
74  for (const auto& [var, val] : sample_new_root) {
75  if (!val.is_numeric()) ++leftOutVars;
76  }
77  OCApproximationStatistics::get_instance().taylorIgnoredVars(leftOutVars, dim);
78  #endif
79  auto apx_sample = sample_root;
80  //for (const auto& [key, value] : sample_root) apx_sample[key] = approximate_RAN_sb(value);
81 
82  auto one_step_differentiate = [&] (const Poly& poly, Poly& result, std::vector<Poly>& jacobian) {
83  Rational evaluated_deriv = 0;
84  for (std::size_t i = 0; i < dim; i++) {
85  // Skip variables with irrational assignment, since (x_i - s_i) cannot be used
86  if (!sample_new_root[var_order[i]].is_numeric()) continue;
87  jacobian[i] = carl::derivative(poly, var_order[i]);
88  evaluated_deriv = approximate_RAN(*carl::evaluate(carl::convert<Polynomial,Poly>(proj().polys().context(),jacobian[i]), apx_sample));
89  if (evaluated_deriv.get_den() != 1) {
90  // find approximate value with smaller representation
91  Rational ub = evaluated_deriv * (Rational(10001)/Rational(10000));
92  Rational lb = evaluated_deriv * (Rational(9999)/Rational(10000));
93  Rational c = carl::ceil(evaluated_deriv);
94  Rational f = carl::floor(evaluated_deriv);
95  if (lb > ub) {
96  std::swap(lb,ub);
97  }
98  if (c < ub) evaluated_deriv = c;
99  else if (lb < f) evaluated_deriv = f;
100  else evaluated_deriv = carl::sample_stern_brocot(RationalInterval(lb,ub), false);
101  }
102  result = result + Poly(evaluated_deriv) * (Poly(var_order[i]) - Poly(sample_new_root[var_order[i]].value()));
103  }
104  // return the sign of the main variable derivative
105  if (carl::is_zero(evaluated_deriv)) return 0;
106  else if (evaluated_deriv > 0) return 1;
107  else return -1;
108  };
109  std::vector<Poly> jacobian(dim);
110  Poly result;
111  // first order taylor approximation
112  int jacobian_sign = one_step_differentiate(carl_poly, result, jacobian);
113  if ((apx_settings().taylor_deg < 2) && (jacobian_sign == 0)) {
114  // in this case, p and p' have a common root at s => disc(p)(s_1,...,s_{n-1}) = 0
115  // => the next level is a section => should choose artificial root close to actual root?
116  // however, we do not actually use p'(s), but an approximation?
117  #ifdef SMTRAT_DEVOPTION_Statistics
118  OCApproximationStatistics::get_instance().taylorFailure();
119  #endif
120  return apx_bound<ApxPoly::SIMPLE>(p, bound, below);
121  }
122  // second order
123  if (apx_settings().taylor_deg == 2) {
124  int hessian_sign = 0;
125  for (std::size_t i = 0; i < dim; i++) {
126  if (!sample_new_root[var_order[i]].is_numeric()) continue;
127  std::vector<Poly> hessian_row(dim);
128  Poly res_i;
129  hessian_sign = one_step_differentiate(jacobian[i], res_i, hessian_row);
130  result = result + (Poly(Rational(1)/Rational(2)) * (Poly(var_order[i]) - Poly(sample_new_root[var_order[i]].value())) * res_i);
131  }
132  if (hessian_sign == 0 && jacobian_sign == 0) {
133  #ifdef SMTRAT_DEVOPTION_Statistics
134  OCApproximationStatistics::get_instance().taylorFailure();
135  #endif
136  return apx_bound<ApxPoly::SIMPLE>(p, bound, below);
137  } else if (hessian_sign*jacobian_sign == 1) {
138  Polynomial result_lp = carl::convert<Polynomial,Poly>(proj().polys().context(),result);
139  return IR(proj().polys()(result_lp), 2);
140  }
141  }
142  Polynomial result_lp = carl::convert<Polynomial,Poly>(proj().polys().context(),result);
143  return IR(proj().polys()(result_lp), 1);
144 }
145 
146 template<>
147 inline IR CellApproximator::apx_bound<ApxPoly::TAYLOR_LIN>(const IR& p, const RAN& bound, bool below) {
148  assert(apx_settings().taylor_deg < proj().degree(p.poly));
149  assert(apx_settings().taylor_deg <= 2);
150 
151  Poly carl_poly = carl::convert<Poly,Polynomial>(proj().polys()(p.poly));
152 
153  std::size_t dim = sample().size();
154  VariableOrdering var_order = proj().polys().var_order();
155  auto sample_root = sample();
156  sample_root[var()] = bound; // TODO : can choose other points here (like the actual sample)
157  auto sample_new_root = sample();
158  sample_new_root[var()] = RAN(approximate_root<ApxSettings::root>(main_sample(), bound, below));
159  #ifdef SMTRAT_DEVOPTION_Statistics
160  std::size_t leftOutVars = 0;
161  for (const auto& [var, val] : sample_new_root) {
162  if (!val.is_numeric()) ++leftOutVars;
163  }
164  OCApproximationStatistics::get_instance().taylorIgnoredVars(leftOutVars, dim);
165  #endif
166  auto apx_sample = sample_root;
167  for (const auto& [key, value] : sample_root) apx_sample[key] = approximate_RAN_sb(value);
168 
169  auto one_step_differentiate = [&] (const Poly& poly, Poly& result, std::vector<Poly>& jacobian, std::size_t d) {
170  Rational evaluated_deriv = 0;
171  for (std::size_t i = 0; i < d; i++) {
172  // Skip variables with irrational assignment, since (x_i - s_i) cannot be used
173  if (!sample_new_root[var_order[i]].is_numeric()) continue;
174  jacobian[i] = carl::derivative(poly, var_order[i]);
175  evaluated_deriv = approximate_RAN(*carl::evaluate(carl::convert<Polynomial,Poly>(proj().polys().context(),jacobian[i]), apx_sample));
176  if (evaluated_deriv.get_den() != 1) {
177  // find approximate value with smaller representation
178  Rational ub = evaluated_deriv * (Rational(10001)/Rational(10000));
179  Rational lb = evaluated_deriv * (Rational(9999)/Rational(10000));
180  Rational c = carl::ceil(evaluated_deriv);
181  Rational f = carl::floor(evaluated_deriv);
182  if (lb > ub) {
183  std::swap(lb,ub);
184  }
185  if (c < ub) evaluated_deriv = c;
186  else if (lb < f) evaluated_deriv = f;
187  else evaluated_deriv = carl::sample_stern_brocot(RationalInterval(lb,ub), false);
188  }
189  result = result + Poly(evaluated_deriv) * (Poly(var_order[i]) - Poly(sample_new_root[var_order[i]].value()));
190  }
191  // return the sign of the main variable derivative
192  if (carl::is_zero(evaluated_deriv)) return 0;
193  else if (evaluated_deriv > 0) return 1;
194  else return -1;
195  };
196  std::vector<Poly> jacobian(dim);
197  Poly result;
198  // first order taylor approximation
199  int jacobian_sign = one_step_differentiate(carl_poly, result, jacobian, dim);
200  if (jacobian_sign == 0) {
201  #ifdef SMTRAT_DEVOPTION_Statistics
202  OCApproximationStatistics::get_instance().taylorFailure();
203  #endif
204  return apx_bound<ApxPoly::SIMPLE>(p, bound, below);
205  }
206  // second order
207  if (apx_settings().taylor_deg == 2) {
208  for (std::size_t i = 0; i < dim-1; i++) {
209  if (!sample_new_root[var_order[i]].is_numeric()) continue;
210  std::vector<Poly> hessian_row(dim-1);
211  Poly res_i;
212  one_step_differentiate(jacobian[i], res_i, hessian_row, dim-1);
213  result = result + (Poly(Rational(1)/Rational(2)) * (Poly(var_order[i]) - Poly(sample_new_root[var_order[i]].value())) * res_i);
214  }
215  }
216  Polynomial result_lp = carl::convert<Polynomial,Poly>(proj().polys().context(),result);
217  return IR(proj().polys()(result_lp), 1);
218 }
219 
220 template<>
221 inline IR CellApproximator::apx_bound<ApxPoly::MAXIMIZE>(const IR& p, const RAN& bound, bool below) {
222 
223  Rational inner = below ? approximate_RAN_below(main_sample()) : approximate_RAN_above(main_sample());
224  Rational outer = below ? approximate_RAN_above(bound) : approximate_RAN_below(bound);
225 
226  if (sample().size() < 2) { // lowest level -> just get as close as possible
227  return IR(proj().polys()(carl::get_denom(outer) * Polynomial(proj().polys().context(), var()) - carl::get_num(outer)), 1);
228  }
229 
230  Rational extra_root = approximate_root<ApxRoot::FIXED_RATIO>(inner, outer, below);
231 
232  RAN lower_var_value = sample()[proj().polys().var_order()[sample().size() - 2]];
233 
234  bool left_unbounded = false, right_unbounded = false;
235  RAN lb = main_sample(), ub = main_sample();
236  RAN l, u;
237  Rational area = 0, new_area = 0;
238 
239  for (std::size_t i = 0; i < apx_settings().maximize_n_iter; i++) {
240  Polynomial artificial_bound = carl::get_denom(extra_root) * Polynomial(proj().polys().context(),var()) - carl::get_num(extra_root);
241  Polynomial res = carl::resultant(artificial_bound, proj().polys()(p.poly));
242  carl::RealRootsResult<RAN> roots_result = carl::real_roots(res, sample());
243  assert(!roots_result.is_nullified());
244  std::vector<RAN> roots = roots_result.roots();
245 
246  if (roots.empty()) break; // unbounded is good enough
247 
248  // find roots closest to sample
249  auto it = roots.begin();
250  while (it != roots.end() && lower_var_value > *it) it++;
251 
252  if (it == roots.begin()) { // left unbounded
253  if (right_unbounded) { // there must be a point inbetween giving an unbounded interval
254  extra_root = approximate_root<ApxRoot::SIMPLE_REPRESENTATION>(extra_root, outer, below);
255  continue;
256  }
257  if (left_unbounded && ub > *it) break; // new interval is smaller -> stop
258 
259  l = lower_var_value;
260  u = *it;
261  if (!left_unbounded) {
262  left_unbounded = true;
263  area = 0; // new area will definitely be bigger
264  }
265  } else if (it == roots.end()) { // right unbounded
266  it--;
267  if (left_unbounded) { // there must be a point inbetween giving an unbounded interval
268  extra_root = approximate_root<ApxRoot::SIMPLE_REPRESENTATION>(extra_root, outer, below);
269  continue;
270  }
271  if (right_unbounded && lb < *it) break; // new interval is smaller -> stop
272 
273  u = lower_var_value;
274  l = *it;
275  if (!right_unbounded) {
276  right_unbounded = true;
277  area = 0; // new area will definitely be bigger
278  }
279  } else { // bounded from both sides
280  if (left_unbounded || right_unbounded) break; // new interval is smaller -> stop
281  u = *it;
282  it--;
283  l = *it;
284  if ((l > lb) && (u < ub)) break; // new interval is smaller -> stop
285  }
286 
287  Rational apx_l = approximate_RAN_below(l);
288  Rational apx_u = approximate_RAN_above(u);
289  new_area = (extra_root - inner)*(apx_u - apx_l);
290  if (below) new_area = -new_area;
291 
292  if (new_area <= area) break; // new interval is smaller -> stop
293  // otherwise continue with new interval
294  area = new_area;
295  lb = l;
296  ub = u;
297  outer = extra_root;
298  extra_root = approximate_root<ApxRoot::FIXED_RATIO>(inner, outer, below);
299  }
300  return IR(proj().polys()(carl::get_denom(outer) * Polynomial(proj().polys().context(),var()) - carl::get_num(outer)), 1);
301 }
302 
303 template<>
304 inline IR CellApproximator::apx_between<ApxPoly::SIMPLE>(const IR& /*p_l*/, const IR& /*p_u*/, const RAN& l, const RAN& u) {
305  Rational root = approximate_root<ApxSettings::root>(l,u,false);
306  return IR(proj().polys()(carl::get_denom(root) * Polynomial(proj().polys().context(),var()) - carl::get_num(root)), 1);
307 }
308 
309 inline IR CellApproximator::approximate_bound(const IR& p, const RAN& bound, bool below) {
310  #ifdef SMTRAT_DEVOPTION_Statistics
311  OCApproximationStatistics::get_instance().approximated(proj().degree(p.poly));
312  #endif
313  IR result = apx_bound<ApxSettings::bound>(p, bound, below);
314  ApxCriteria::inform(proj().polys()(result.poly), result.index);
315  return result;
316 }
317 
318 inline IR CellApproximator::approximate_between(const IR& p_l, const IR& p_u, const RAN& l, const RAN& u) {
319  #ifdef SMTRAT_DEVOPTION_Statistics
320  OCApproximationStatistics::get_instance().approximated(std::max(proj().degree(p_l.poly), proj().degree(p_u.poly)));
321  #endif
322  return apx_between<ApxSettings::between>(p_l, p_u, l, u);
323 }
324 
326  if (cell().is_section()) { // Section case as before
327  return datastructures::SymbolicInterval(util::simplest_bound(proj(), cell().lower()->second));
328  } else if (cell().lower_unbounded() && cell().upper_unbounded()) {
330  } else if (cell().lower_unbounded()) {
331  IR upper = util::simplest_bound(proj(), cell().upper()->second);
332  if (ApxCriteria::side(proj(), upper, cell().upper(), del().roots().end()))
333  upper = approximate_bound(upper, cell().upper()->first, false);
335  } else if (cell().upper_unbounded()) {
336  IR lower = util::simplest_bound(proj(), cell().lower()->second);
337  if (ApxCriteria::side(proj(), lower, del().roots().begin(), del().roots().end()))
338  lower = approximate_bound(lower, cell().lower()->first, true);
340  } else {
341  IR lower = util::simplest_bound(proj(), cell().lower()->second);
342  IR upper = util::simplest_bound(proj(), cell().upper()->second);
343  if (ApxCriteria::side(proj(), upper, cell().upper(), del().roots().end()))
344  upper = approximate_bound(upper, cell().upper()->first, false);
345  if (ApxCriteria::side(proj(), lower, del().roots().begin(), cell().upper()))
346  lower = approximate_bound(lower, cell().lower()->first, true);
348  }
349 }
350 
351 }
static Bound strict(RootFunction value)
Definition: roots.h:215
Represents the delineation of a set of polynomials (at a sample), that is.
Definition: delineation.h:118
Encapsulates all computations on polynomials.
Definition: projections.h:46
A symbolic interal whose bounds are defined by indexed root expressions.
Definition: roots.h:250
static bool side(datastructures::Projections &proj, const IR &ir, datastructures::RootMap::const_iterator start, datastructures::RootMap::const_iterator end)
Definition: criteria.h:133
static void inform(const Polynomial &p, std::size_t root_index)
Definition: criteria.h:95
IR apx_bound(const IR &p, const RAN &bound, bool below)
const datastructures::DelineationInterval & cell() const
IR approximate_bound(const IR &p, const RAN &bound, bool below)
IR apx_between(const IR &p_l, const IR &p_u, const RAN &l, const RAN &u)
IR approximate_between(const IR &p_l, const IR &p_u, const RAN &l, const RAN &u)
const datastructures::DelineationInterval & m_r_cell
CellApproximator(datastructures::SampledDerivationRef< T > &der)
int var(Lit p)
Definition: SolverTypes.h:64
Poly resultant(carl::Variable variable, const Poly &p, const Poly &q)
Computes the resultant of two polynomials.
Definition: utils.h:48
std::shared_ptr< SampledDerivation< Properties > > SampledDerivationRef
Definition: derivation.h:25
std::optional< datastructures::IndexedRoot > simplest_bound(datastructures::Projections &proj, const std::vector< datastructures::TaggedIndexedRoot > &bounds, datastructures::PolyRef origin_filter)
Definition: util.h:29
std::vector< carl::Variable > VariableOrdering
Definition: common.h:11
Polynomial::RootType RAN
Definition: common.h:24
carl::Assignment< RAN > Assignment
Definition: common.h:25
carl::ContextPolynomial< Rational > Polynomial
Definition: common.h:16
Rational evaluate(carl::Variable v, const Poly &p, const Rational &r)
Definition: utils.h:11
bool substitute(const FormulaT &constr, const carl::Variable var, const carl::vs::Term< Poly > &term, FormulaT &result)
Definition: VSHelper.h:138
carl::Interval< Rational > RationalInterval
Definition: model.h:27
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
mpq_class Rational
Definition: types.h:19
Represents the i-th root of a multivariate polynomial at its main variable (given an appropriate samp...
Definition: roots.h:15
PolyRef poly
A multivariate polynomial.
Definition: roots.h:17
Note: If connected(i) holds, then the indexed root ordering must contain an ordering between the inte...
Definition: heuristics.h:24