SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
OpenCAD.h
Go to the documentation of this file.
1 #pragma once
2 
3 /**
4  * @file
5  * Construct a single open CAD Cell around a given point that is sign-invariant
6  * on a given set of polynomials.
7  *
8  * References:
9  * [1] Christopher W. Brown. 2013. Constructing a single open cell in a
10  * cylindrical algebraic decomposition. In Proceedings of the 38th
11  * International Symposium on Symbolic and Algebraic Computation (ISSAC '13).
12  * ACM
13  */
14 
15 #include <algorithm>
16 #include <optional>
17 #include <set>
18 #include <unordered_map>
19 #include <vector>
20 
21 #include <carl-arith/poly/umvpoly/CoCoAAdaptor.h>
22 #include <carl-arith/poly/umvpoly/MultivariatePolynomial.h>
23 #include <carl-arith/poly/umvpoly/functions/Resultant.h>
24 #include <carl-arith/poly/umvpoly/UnivariatePolynomial.h>
25 #include <carl-arith/core/Variable.h>
26 #include <carl-arith/core/VariablePool.h>
27 
28 #include <carl-arith/ran/ran.h>
29 #include <carl-arith/ran/RealAlgebraicPoint.h>
30 
32 
33 namespace smtrat {
34 namespace onecellcad {
35 namespace recursive {
36 
37 using UniPoly = carl::UnivariatePolynomial<smtrat::Rational>;
38 using MultiPoly = carl::MultivariatePolynomial<smtrat::Rational>;
39 using MultiCoeffUniPoly = carl::UnivariatePolynomial<MultiPoly>;
40 using RANPoint = RealAlgebraicPoint<smtrat::Rational>;
41 using RANMap = std::map<carl::Variable, RAN>;
42 
43 /**
44  * Represent a cell's closed-interval-boundary object along one single axis by an
45  * irreducible, multivariate polynomial of level k.
46  * A section is an algebraic/"moving" boundary, because it's basically a
47  * function f: algReal^{k-1} -> algReal; from a multi-dimensional input point
48  * of level k-1 (whose components are algebraic reals) to an algebraic real
49  * (the bound along k-th axis that changes depending on the input point).
50  */
51 struct Section {
53 
54  /**
55  * A single, special bound after having plugged a specific point of level k-1
56  * can be cached for performance (needed for [1]).
57  */
59 };
60 
61 std::ostream& operator<<(std::ostream& o, const Section& s) {
62  return o << "(section " << s.poly << " " << s.cachedPoint << ")";
63 }
64 
65 /**
66  * Represent a cell's open-interval boundary object along one single axis by two
67  * irreducible, multivariate polynomials of level k.
68  * A sector is an algebraic/"moving" boundary, because it's basically a
69  * function f: algReal^{k-1} -> (algReal,algReal); from a multi-dimensional
70  * input point of level k-1 (whose components are algebraic reals) to a pair
71  * of algebraic reals (the lower and upper bound for an open interval along
72  * k-th axis that changes depending on the input point).
73  * Note that if 'lowBound' or 'highBound' is not defined, then this
74  * represents negative and positive infinity, respectively.
75  */
76 struct Sector {
77  std::optional<Section> lowBound;
78 
79  std::optional<Section> highBound;
80 
81  bool isLowBoundNegInfty() const {
82  return lowBound == std::nullopt;
83  }
84 
85  bool isHighBoundInfty() const {
86  return highBound == std::nullopt;
87  }
88 };
89 
90 std::ostream& operator<<(std::ostream& o, const Sector& s) {
91  o << "(sector (low ";
92  s.isLowBoundNegInfty() ? o << "-infty) " : o << s.lowBound.value() << ") ";
93  o << "(high ";
94  s.isHighBoundInfty() ? o << "infty)" : o << s.highBound.value() << ")";
95  return o << ")";
96 }
97 
98 /**
99  * Represent a single open cell [1].
100  * A cell is a collection of boundary objects along each axis.
101  * In case of an open cell, the boundary objects are all sectors.
102  */
103 using OpenCADCell = std::vector<Sector>;
104 
105 std::ostream& operator<<(std::ostream& o, const OpenCADCell& c) {
106  o << "(cell (level " << c.size() << ") ";
107  for (auto& sector : c) {
108  o << sector << " ";
109  }
110  return o << ")";
111 }
112 
114  return OpenCADCell(level);
115 }
116 
117 /**
118  * Find the index of the highest variable (wrt. the ordering
119  * in 'variableOrder') that occurs with positive degree in 'poly'.
120  * Since levelOf is a math concept it starts counting at 1!
121  * Examples:
122  * - levelOf(2) == 0 wrt. any variable order
123  * - levelOf(0*x+2) == 0 wrt. any variable order
124  * - levelOf(x+2) == 1 wrt. [x < y < z]
125  * - levelOf(x+2) == 2 wrt. [y < x < z]
126  * - levelOf(x+2) == 3 wrt. [y < z < x]
127  * - levelOf(x*y+2) == 2 wrt. [x < y < z] because of y
128  * - levelOf(x*y+2) == 2 wrt. [y < x < z] because of x
129  * - levelOf(x*y+2) == 3 wrt. [x < z < y] because of y
130  * Preconditions:
131  * - 'variables(poly)' must be a subset of 'variableOrder'.
132  */
133 size_t levelOf(const MultiPoly& poly,
134  const std::vector<carl::Variable>& variableOrder) {
135  // 'variables()' collects only vars with positive degree
136  std::set<carl::Variable> polyVarSet = carl::variables(poly).as_set();
137  // Algorithm:
138  // Iterate through each variable inside 'variableOrder' in ascending order
139  // and remove it from 'polyVarSet'. The last variable in 'polyVarSet' before
140  // it becomes empty is the highest appearing variable in 'poly'.
141 
142  if (polyVarSet.empty())
143  return 0; // for const-polys like '2'
144 
145  size_t level = 1;
146  for (const auto& var : variableOrder) {
147  polyVarSet.erase(var);
148  // Last variable before 'polyVars' becomes empty is the highest variable.
149  if (polyVarSet.empty()) {
150  return level;
151  }
152  level++;
153  }
154  assert(false); // Should never be reached
155  return 0;
156 }
157 
158 /**
159  * Create a mapping from the first 'count'-many variables in the given
160  * 'variableOrder' to the first 'count'-many components of the given 'point'.
161  */
162 std::map<carl::Variable, RAN> toStdMap(
163  const RANPoint& point,
164  size_t count,
165  const std::vector<carl::Variable>& variableOrder) {
166  std::map<carl::Variable, RAN> pointAsMap;
167  for (size_t i = 0; i < count; i++)
168  pointAsMap[variableOrder[i]] = point[i];
169  return pointAsMap;
170 }
171 
172 /**
173  * Merge the given open OpenCADCell 'cell' that contains the given 'point'
174  * (called "alpha" in [1]) with a polynomial 'poly'.
175  * Before the merge 'cell' represents a region that is sign-invariant
176  * on other (previously merged) polynomials (all signs are non-zero).
177  * The returned cell represents a region that is additionally sign-invariant on
178  * 'poly' (also with non-zero sign).
179  * @return either an OpenCADCell or nothing (representing a failed construction)
180  */
181 std::optional<OpenCADCell> mergeCellWithPoly(
182  OpenCADCell& cell,
183  const RANPoint& point,
184  const std::vector<carl::Variable>& variableOrder,
185  const MultiPoly poly) {
186  SMTRAT_LOG_INFO("smtrat.opencad", "Merge poly" << poly);
187  assert(!carl::is_zero(poly));
188  size_t level = levelOf(poly, variableOrder);
189  // level for first variable starts at 1, but need it as index to start at 0.
190  size_t levelVariableIdx = level - 1;
191  SMTRAT_LOG_DEBUG("smtrat.opencad", "At level " << level << " merge it with " << cell);
192  if (level == 0) // We have a non-zero, constant-poly, so no roots and nothing to do
193  return std::optional<OpenCADCell>(cell);
194 
195  auto result = carl::evaluate(
196  carl::BasicConstraint<Poly>(poly, carl::Relation::EQ),
197  toStdMap(point, level, variableOrder));
198  assert(result);
199  if (*result) {
200  SMTRAT_LOG_WARN("smtrat.opencad", "Poly vanished at point.");
201  return std::nullopt;
202  }
203 
204  std::optional<OpenCADCell> newCell(cell);
205  carl::Variable mainVariable = variableOrder[levelVariableIdx];
206  SMTRAT_LOG_TRACE("smtrat.opencad", "Current level variable: " << mainVariable);
207  MultiCoeffUniPoly polyAsUnivar = carl::to_univariate_polynomial(poly, mainVariable);
208  if (level > 1) {
209  SMTRAT_LOG_INFO("smtrat.opencad", "Do Open-McCallum projection of this poly into level " << level - 1);
210  std::vector<MultiPoly> projectionPolys;
211  projectionPolys.emplace_back(polyAsUnivar.lcoeff());
212  projectionPolys.emplace_back(MultiPoly(carl::discriminant(polyAsUnivar)));
213  SMTRAT_LOG_TRACE("smtrat.opencad", "Add leading coeff: " << polyAsUnivar.lcoeff());
214  SMTRAT_LOG_TRACE("smtrat.opencad", "Add discriminant: " << carl::discriminant(polyAsUnivar));
215 
216  Sector& sectorAtLvl = (*newCell)[levelVariableIdx];
217  // Add resultant of poly and lower sector bound
218  if (!sectorAtLvl.isLowBoundNegInfty()) {
219  MultiPoly resultant(carl::resultant(carl::to_univariate_polynomial(sectorAtLvl.lowBound->poly, mainVariable), polyAsUnivar));
220  projectionPolys.emplace_back(resultant);
221  SMTRAT_LOG_TRACE("smtrat.opencad", "Add resultant with cell's low bound: " << resultant);
222  }
223 
224  // Add resultant of poly and higher sector bound
225  if (!sectorAtLvl.isHighBoundInfty()) {
226  MultiPoly resultant(carl::resultant(carl::to_univariate_polynomial(sectorAtLvl.highBound->poly,mainVariable), polyAsUnivar));
227  projectionPolys.emplace_back(resultant);
228  SMTRAT_LOG_TRACE("smtrat.opencad", "Add resultant with cell's high bound: " << resultant);
229  }
230  SMTRAT_LOG_DEBUG("smtrat.opencad", "Projection result: " << projectionPolys);
231  SMTRAT_LOG_INFO("smtrat.opencad", "Projection complete. Merge into cell");
232 
233  // Each poly in 'projectionPolys' must be factorized into its irreducible
234  // factors.
235  carl::CoCoAAdaptor<MultiPoly> factorizer(projectionPolys);
236  for (auto& p : projectionPolys) {
237  for (const auto& factor : factorizer.irreducible_factors(p, false)) {
238  SMTRAT_LOG_DEBUG("smtrat.opencad", "Merge irreducible factor: " << factor);
239  if (!(newCell = mergeCellWithPoly(
240  *newCell,
241  point,
242  variableOrder,
243  factor))) {
244  // If submerge fails, this merge fails too
245  return std::nullopt;
246  }
247  }
248  }
249  }
250 
251  // Isolate real roots of level-k 'poly' after plugin in a level-(k-1) point.
252  // level is >= 1
253  RAN point_k = point[levelVariableIdx]; // called alpha_k in [1]
254  SMTRAT_LOG_INFO("smtrat.opencad", "Continue at level " << level
255  << ". Search closest bounds to " << point_k);
256  // It must be ensured that poly does not vanish under this point!
257  std::vector<RAN> roots = carl::real_roots(polyAsUnivar,
258  toStdMap(point, level - 1, variableOrder)).roots();
259  if (roots.empty()) {
260  SMTRAT_LOG_INFO("smtrat.opencad", "No bound candidates");
261  return newCell;
262  }
263 
264  std::sort(roots.begin(), roots.end());
265  SMTRAT_LOG_DEBUG("smtrat.opencad", "Bound candidates: " << roots);
266  Sector& sectorAtLvl = (*newCell)[levelVariableIdx]; // Called D[k] in [1]
267  SMTRAT_LOG_DEBUG("smtrat.opencad", "Bounds before: " << sectorAtLvl);
268 
269  // Search for closest roots to point_k, i.e.
270  // someRoot ... < root_lower < point_k < root_higher < ... someOtherRoot
271  auto root_higher = std::find_if(
272  roots.begin(),
273  roots.end(),
274  [&point_k](const RAN& otherPoint) { return otherPoint > point_k; });
275  assert(root_higher == roots.end() || *root_higher != point_k);
276  // Update high bound if a tighter root is found s.t.
277  // point_k < root_higher < cell_highBound
278  if (root_higher != roots.end() &&
279  (sectorAtLvl.isHighBoundInfty() ||
280  *root_higher < sectorAtLvl.highBound->cachedPoint)) {
281  sectorAtLvl.highBound = Section{poly, *root_higher};
282  }
283 
284  // Update low bound if a tighter root is found s.t.
285  // cell_lowBound < root_lower < point_k
286  if (root_higher != roots.begin()) {
287  auto root_lower = --root_higher;
288  assert(*root_lower != point_k);
289  if (sectorAtLvl.isLowBoundNegInfty() ||
290  *root_lower > sectorAtLvl.lowBound->cachedPoint) {
291  sectorAtLvl.lowBound = Section{poly, *root_lower};
292  }
293  }
294  SMTRAT_LOG_DEBUG("smtrat.opencad", "Bounds after: " << sectorAtLvl);
295  return newCell;
296 }
297 
298 /**
299  * Construct a OpenCADCell for the given set of polynomials 'polySet'
300  * that contains the given 'point'. The returned cell represents
301  * the largest region that is sign-invariant on all polynomials in
302  * the 'polySet' and is cylindrical with respect to the
303  * variables ordering given in 'variableOrder'.
304  * Note that this construction is only correct if plugging in
305  * the 'point' into any polynomial of the 'polySet' yields a non-zero
306  * value, i.e. 'point' is not a root of any polynomial in 'polySet',
307  * otherwise no OpenCADCell is returned.
308  * Note that the dimension (number of components) of the 'point' == the number of variables
309  * in 'variableOrder'
310  * and that any polynomial of the 'polySet' must be irreducible and
311  * mention only variables from 'variableOrder'.
312  *
313  */
314 std::optional<OpenCADCell> createOpenCADCell(
315  const std::vector<MultiPoly> polySet,
316  const RANPoint& point,
317  const std::vector<carl::Variable>& variableOrder) {
318  // Note that combining 'variableOrder' and 'point' into
319  // a single object like Variable-to-RAN-map
320  // is bad because a map may rearrange the variables and destroy
321  // any desired variable ordering.
322  assert(variableOrder.size() == point.dim());
323  SMTRAT_LOG_INFO("smtrat.opencad", "Create BrownOpenOneCell");
324  SMTRAT_LOG_DEBUG("smtrat.opencad", "Use point " << point << " wrt. variable order " << variableOrder);
325 
326  std::optional<OpenCADCell> cell = createFullspaceCoveringCell(point.dim());
327  for (const auto& poly : polySet) {
328  SMTRAT_LOG_INFO("smtrat.opencad", "Merge input poly");
329  SMTRAT_LOG_DEBUG("smtrat.opencad", "Input poly: " << poly);
330  if (!(cell = mergeCellWithPoly(*cell, point, variableOrder, poly))) {
331  // If any merge fails, this whole construction fails too
332  SMTRAT_LOG_WARN("smtrat.opencad", "Construction failed");
333  return std::nullopt;
334  }
335  }
336  SMTRAT_LOG_DEBUG("smtrat.opencad", "Final cell: " << cell.value());
337  return cell;
338 }
339 
340 } // namespace recursive
341 } // namespace onecellcad
342 } // namespace smtrat
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:67
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
Poly discriminant(carl::Variable variable, const Poly &p)
Computes the discriminant of a polynomial.
Definition: utils.h:63
Polynomial::RootType RAN
Definition: common.h:24
Rational evaluate(carl::Variable v, const Poly &p, const Rational &r)
Definition: utils.h:11
size_t levelOf(const MultiPoly &poly, const std::vector< carl::Variable > &variableOrder)
Find the index of the highest variable (wrt.
Definition: OpenCAD.h:133
std::map< carl::Variable, RAN > toStdMap(const RANPoint &point, size_t count, const std::vector< carl::Variable > &variableOrder)
Create a mapping from the first 'count'-many variables in the given 'variableOrder' to the first 'cou...
Definition: OpenCAD.h:162
carl::MultivariatePolynomial< smtrat::Rational > MultiPoly
Definition: OpenCAD.h:38
RealAlgebraicPoint< smtrat::Rational > RANPoint
Definition: OpenCAD.h:40
std::ostream & operator<<(std::ostream &o, const Section &s)
Definition: OpenCAD.h:61
carl::UnivariatePolynomial< MultiPoly > MultiCoeffUniPoly
Definition: OpenCAD.h:39
std::optional< OpenCADCell > createOpenCADCell(const std::vector< MultiPoly > polySet, const RANPoint &point, const std::vector< carl::Variable > &variableOrder)
Construct a OpenCADCell for the given set of polynomials 'polySet' that contains the given 'point'.
Definition: OpenCAD.h:314
OpenCADCell createFullspaceCoveringCell(size_t level)
Definition: OpenCAD.h:113
std::map< carl::Variable, RAN > RANMap
Definition: OpenCAD.h:41
std::optional< OpenCADCell > mergeCellWithPoly(OpenCADCell &cell, const RANPoint &point, const std::vector< carl::Variable > &variableOrder, const MultiPoly poly)
Merge the given open OpenCADCell 'cell' that contains the given 'point' (called "alpha" in [1]) with ...
Definition: OpenCAD.h:181
carl::UnivariatePolynomial< smtrat::Rational > UniPoly
Definition: OpenCAD.h:37
std::vector< Sector > OpenCADCell
Represent a single open cell [1].
Definition: OpenCAD.h:103
Class to create the formulas for axioms.
#define SMTRAT_LOG_TRACE(channel, msg)
Definition: logging.h:36
#define SMTRAT_LOG_WARN(channel, msg)
Definition: logging.h:33
#define SMTRAT_LOG_INFO(channel, msg)
Definition: logging.h:34
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35
Represent a cell's closed-interval-boundary object along one single axis by an irreducible,...
Definition: OpenCAD.h:51
RAN cachedPoint
A single, special bound after having plugged a specific point of level k-1 can be cached for performa...
Definition: OpenCAD.h:58
Represent a cell's open-interval boundary object along one single axis by two irreducible,...
Definition: OpenCAD.h:76
std::optional< Section > highBound
Definition: OpenCAD.h:79
std::optional< Section > lowBound
Definition: OpenCAD.h:77