carl  24.04
Computer ARithmetic Library
SignDetermination.h
Go to the documentation of this file.
1 /*
2  * File: SignDetermination.h
3  * Author: tobias
4  *
5  * Created on 15. August 2016, 11:38
6  */
7 
8 #pragma once
9 
11 #include "../TarskiQuery/TarskiQueryManager.h"
12 #include "SignCondition.h"
13 
14 #include <eigen3/Eigen/Core>
15 #include <eigen3/Eigen/LU>
16 #include <cmath>
17 #include <iterator>
18 #include <list>
19 #include <queue>
20 
21 
22 namespace carl {
23 
24 template<typename Number>
26 
27 private:
30 
31  // an alpha is a mapping from a set of polynomials to the set
32  // {0,1,2} in order to perform sign determination
33  using Alpha = std::list<uint>;
34 
35  std::list<Polynomial> mP;
37  std::list<SignCondition> mSigns;
38  std::list<Polynomial> mProducts;
39  std::list<Alpha> mAda;
40  std::list<uint> mAdaHelper;
41  Eigen::MatrixXf mMatrix;
42  bool mNeedsUpdate = false;
43 
44 
45 
46 public:
47  template<typename InputIt>
48  SignDetermination(InputIt zeroSet_first, InputIt zeroSet_last) :
49  mTaQ(zeroSet_first, zeroSet_last)
50  {}
51 
52 
53  /*
54  * Copy constructor
55  */
57  mP(other.mP),
58  mTaQ(other.mTaQ),
59  mSigns(other.mSigns),
60  mProducts(other.mProducts),
61  mAda(other.mAda),
62  mAdaHelper(other.mAdaHelper),
63  mMatrix(other.mMatrix),
65  {}
66 
67  uint sizeOfZeroSet() const {
68  TaQResType res = mTaQ(Number(1));
69  CARL_LOG_ASSERT("carl.thom.sing", res >= 0, "");
70  return uint(res);
71  }
72 
73  const auto& processedPolynomials() const { return mP; }
74  const auto& signs() const { return mSigns; }
75  const auto& products() const { return mProducts; }
76  const auto& adaptedList() const { return mAda; }
77  const auto& matrix() const { return mMatrix; }
78  bool needsUpdate() const { return mNeedsUpdate; }
79 
80 
81 private:
82  static int sigmaToTheAlpha(const Alpha& alpha, const SignCondition& sigma) {
83  CARL_LOG_ASSERT("carl.thom.sign", alpha.size() == sigma.size(), "invalid arguments");
84  int res = 1;
85  auto it_alpha = alpha.begin();
86  auto it_sigma = sigma.begin();
87  for(; it_alpha != alpha.end(); ) {
88  // there are only these two cases where res can actually change
89  if(*it_sigma == Sign::NEGATIVE && *it_alpha == 1) {
90  res = -res;
91  }
92  else if(*it_sigma == Sign::ZERO && *it_alpha != 0) {
93  return 0;
94  }
95  it_alpha++;
96  it_sigma++;
97  }
98  return res;
99  }
100  static Eigen::MatrixXf adaptedMat(const std::list<Alpha>& ada, const std::list<SignCondition>& signs) {
101  Eigen::MatrixXf res(ada.size(), signs.size());
102  Eigen::Index i = 0;
103  Eigen::Index j = 0;
104  for (const auto& alpha : ada) {
105  for (const auto& sigma : signs) {
106  res(i, j) = float(sigmaToTheAlpha(alpha, sigma));
107  j++;
108  }
109  j = 0;
110  i++;
111  }
112  return res;
113  }
114  static Eigen::MatrixXf kroneckerProduct(const Eigen::MatrixXf& m1, const Eigen::MatrixXf& m2) {
115  Eigen::MatrixXf m3(m1.rows() * m2.rows(), m1.cols() * m2.cols());
116  for(int i = 0; i < m1.cols(); i++) {
117  for(int j = 0; j < m1.rows(); j++) {
118  m3.block(i * m2.rows(), j * m2.cols(), m2.rows(), m2.cols()) = m1(i, j) * m2;
119  }
120  }
121  return m3;
122  }
123  static void removeColumn(Eigen::MatrixXf& matrix, Eigen::Index colToRemove) {
124  Eigen::Index numRows = matrix.rows();
125  Eigen::Index numCols = matrix.cols()-1;
126 
127  if (colToRemove < numCols) {
128  matrix.block(0, colToRemove, numRows, numCols - colToRemove) = matrix.block(0, colToRemove + 1 ,numRows, numCols - colToRemove);
129  }
130  matrix.conservativeResize(numRows, numCols);
131  }
132  static void removeRow(Eigen::MatrixXf& matrix, Eigen::Index rowToRemove) {
133  Eigen::Index numRows = matrix.rows()-1;
134  Eigen::Index numCols = matrix.cols();
135 
136  if (rowToRemove < numRows) {
137  matrix.block(rowToRemove,0,numRows-rowToRemove,numCols) = matrix.block(rowToRemove+1,0,numRows-rowToRemove,numCols);
138  }
139 
140  matrix.conservativeResize(numRows,numCols);
141  }
142 
143  std::list<Polynomial> computeProducts(const Polynomial& p, const std::list<Alpha>& currAda) const {
144  std::list<Polynomial> result;
145  for(const auto& alpha : currAda) {
146  for(const auto& prod : mProducts) {
147  assert(alpha.size() == 1);
148  if(alpha.front() == 0) {
149  result.push_back(prod);
150  }
151  else if(alpha.front() == 1) {
152  result.push_back(mTaQ.reduceProduct(p, prod));
153  }
154  else {
155  assert(alpha.front() == 2);
156  Polynomial psquare = mTaQ.reduceProduct(p, p);
157  result.push_back(mTaQ.reduceProduct(psquare, prod));
158  }
159  }
160  }
161  return result;
162  }
163 
164  std::list<Alpha> firstNLines(
165  const uint n,
166  const Eigen::MatrixXf& mat,
167  const std::vector<Alpha>& ada,
168  std::vector<Polynomial>& products,
169  const uint q) const {
170  CARL_LOG_ASSERT("carl.thom.sign", n > 0, "");
171  std::vector<uint> lines(ada.size(), 0);
172  assert(n <= ada.size());
173  assert((uint)mat.rows() == ada.size());
174  for (std::size_t i = 0; i < n; i++) {
175  lines[i] = 1;
176  }
177 
178  do {
179  Eigen::MatrixXf linesMat(n, mat.cols());
180  Eigen::Index row = 0;
181  for(std::size_t i = 0; i < lines.size(); i++) {
182  if (lines[i] == 0) continue;
183  for (Eigen::Index j = 0; j < mat.cols(); j++) {
184  linesMat(row, j) = mat(Eigen::Index(i), j);
185  }
186  row++;
187  }
188  Eigen::FullPivLU<Eigen::MatrixXf> dec(linesMat);
189  if (dec.rank() == int(n)) break;
190 
191  }
192  while(std::prev_permutation(lines.begin(), lines.end()));
193 
194 
195  std::list<Alpha> res;
196  std::vector<Polynomial> newProducts;
197  for(uint i = 0; i < lines.size(); i++) {
198  if(lines[i] == 1) {
199  res.push_back(ada[i]);
200  newProducts.push_back(products[q*ada.size() + i]);
201  }
202  }
203  assert(res.size() == n);
204  products = newProducts;
205  return res;
206  }
207 
208  void update() {
209  std::list<Alpha> newAda = mAda;
210  for(auto& alpha : newAda) alpha.push_front(0);
211  std::list<Polynomial> adaptedProducts = mProducts;
212  adaptedProducts.resize(mAda.size());
213  uint r1 = mAda.size();
214  if(mSigns.size() != r1) {
215  CARL_LOG_TRACE("carl.thom.sign", "need to compute r2");
216  Eigen::MatrixXf m2 = mMatrix;
217  uint r2 = 0;
218  long index = 0;
219  for(const auto& n : mAdaHelper) {
220  if(n >= 2) {
221  r2++;
222  index++;
223  }
224  else {
225  removeColumn(m2, index);
226  }
227  }
228  std::vector<Polynomial> products(mProducts.begin(), mProducts.end());
229  std::list<Alpha> A_2 = firstNLines(
230  r2,
231  m2,
232  std::vector<Alpha>(mAda.begin(), mAda.end()),
233  products,
234  1);
235  for(auto& alpha : A_2) {
236  alpha.push_front(1);
237  newAda.push_back(alpha);
238  }
239  for(const auto& p : products) {
240  adaptedProducts.push_back(p);
241  }
242  if(mSigns.size() != r1 + r2) {
243  CARL_LOG_TRACE("carl.thom.sign", "need to compute r3");
244  Eigen::MatrixXf m3 = mMatrix;
245  uint r3 = 0;
246  index = 0;
247  for(const auto& n : mAdaHelper) {
248  if(n >= 3) {
249  r3++;
250  index++;
251  }
252  else {
253  removeColumn(m3, index);
254  }
255  }
256  products = std::vector<Polynomial>(mProducts.begin(), mProducts.end());
257  std::list<Alpha> A_3 = firstNLines(
258  r3,
259  m3,
260  std::vector<Alpha>(mAda.begin(), mAda.end()),
261  products,
262  2);
263  for(auto& alpha : A_3) {
264  alpha.push_front(2);
265  newAda.push_back(alpha);
266  }
267  for(const auto& p : products) {
268  adaptedProducts.push_back(p);
269  }
270  }
271  }
272  mAda = newAda;
274  CARL_LOG_ASSERT("carl.thom.sign", Eigen::FullPivLU<Eigen::MatrixXf>(mMatrix).rank() == mMatrix.cols(), "mMatrix must be invertible!");
275  mProducts = adaptedProducts;
276  mNeedsUpdate = false;
277  CARL_LOG_DEBUG("carl.thom.sign", *this);
278  }
279 
280  std::list<SignCondition> getSigns (
281  const Polynomial& p,
282  std::list<Polynomial>& products,
283  std::list<Alpha>& ada,
284  std::list<uint>& adaHelper,
285  Eigen::MatrixXf& matrix
286  ) {
287  if(mNeedsUpdate) this->update();
288 
289  CARL_LOG_TRACE("carl.thom.sign", "processing " << p);
290  std::list<Polynomial> currProducts;
291  TaQResType taq0 = mTaQ(Number(1));
292  currProducts.push_back(Polynomial(Number(1)));
293  if(taq0 == 0) {
294  CARL_LOG_WARN("carl.thom.sign", "doing sign determination on an empty zero set");
295  return {};
296  }
297 
298  // (1) determine sign conditions realized by p on the zero set
299  // and an corrspoding adapted list
300  std::list<SignCondition> currSigns;
301  std::list<Alpha> currAda;
302  TaQResType taq1 = mTaQ(p);
303  currProducts.push_back(p);
304  Polynomial psquare = p*p;
305  TaQResType taq2 = mTaQ(psquare);
306  currProducts.push_back(psquare);
307  CARL_LOG_ASSERT("carl.thom.sign", std::abs(taq1) <= taq0 && std::abs(taq2) <= taq0, "tarski query failure");
308  int czer = taq0 - taq2;
309  int cpos = (taq1 + taq2) / 2; // ensured to be an exact division
310  int cneg = (taq2 - taq1) / 2;
311  // the order in which elements are added to currSigns is important
312  if(czer != 0) currSigns.emplace_back(1, Sign::ZERO);
313  if(cpos != 0) currSigns.emplace_back(1, Sign::POSITIVE);
314  if(cneg != 0) currSigns.emplace_back(1, Sign::NEGATIVE);
315  currAda = {{0}, {1}, {2}};
316  currAda.resize(currSigns.size());
317  currProducts.resize(currSigns.size());
318  Eigen::MatrixXf currM = adaptedMat(currAda, currSigns);
319  // means that p is the first polynomial ever processed in this sign determination
320  if(mP.empty()) {
321  products = currProducts;
322  ada = currAda;
323  matrix = currM;
324  mNeedsUpdate = false;
325  return currSigns;
326  }
327 
328  // (2)
329  products = this->computeProducts(p, currAda);
330 
331  Eigen::VectorXf dprime(long(products.size()));
332  long index = 0;
333  for (const auto& prod : products) {
334  dprime(index) = float(mTaQ(prod));
335  index++;
336  }
337 
338  Eigen::MatrixXf M_prime = kroneckerProduct(currM, mMatrix);
339  CARL_LOG_ASSERT("carl.thom.sign", Eigen::FullPivLU<Eigen::MatrixXf>(M_prime).rank() == M_prime.cols(), "M_prime must be invertible!");
340  Eigen::PartialPivLU<Eigen::MatrixXf> dec(M_prime);
341  Eigen::VectorXf c = dec.solve(dprime);
342  CARL_LOG_ASSERT("carl.thom.sign", (uint)c.size() == currSigns.size() * mSigns.size(), "failure in sign determination");
343 
344  std::list<SignCondition> newSigns;
345  adaHelper = std::list<uint>(mSigns.size(), 0);
346  long k = 0;
347  for(long i = 0; i < long(currSigns.size()); i++) {
348  auto helper_it = adaHelper.begin();
349  for(const auto& sigma : mSigns) {
350  if ((std::round(c(k))) != 0) {
351  uint tmp = *helper_it;
352  helper_it = adaHelper.erase(helper_it);
353  helper_it = adaHelper.insert(helper_it, ++tmp);
354  SignCondition newCond = sigma;
355  auto it = currSigns.begin();
356  std::advance(it, i);
357  newCond.push_front(it->front());
358  newSigns.push_back(newCond);
359  }
360  helper_it++;
361  k++;
362  }
363  helper_it = adaHelper.begin();
364  }
365 
366  return newSigns;
367  }
368 
369 
370 public:
371 
372  /*
373  * MAIN INTERFACES
374  */
375  std::list<SignCondition> getSigns(const Polynomial& p) {
376  std::list<Polynomial> dummyProducts;
377  std::list<Alpha> dummyAda;
378  std::list<uint> dummyHelper;
379  Eigen::MatrixXf dummyMatrix;
380  std::list<SignCondition> newSigns = getSigns(p, dummyProducts, dummyAda, dummyHelper, dummyMatrix);
381  return newSigns;
382  }
383 
384  std::list<SignCondition> getSignsAndAdd(const Polynomial& p) {
385  std::list<Polynomial> newProducts;
386  std::list<Alpha> newAda;
387  std::list<uint> newHelper;
388  Eigen::MatrixXf newMatrix;
389  std::list<SignCondition> newSigns = getSigns(p, newProducts, newAda, newHelper, newMatrix);
390  mNeedsUpdate = true;
391  if(mP.empty()) {
392  mAda = newAda;
393  mMatrix = newMatrix;
394  mNeedsUpdate = false;
395  }
396  mP.push_front(p);
397  mSigns = newSigns;
398  mProducts = newProducts;
399  mAdaHelper = newHelper;
400  CARL_LOG_DEBUG("carl.thom.sign", *this);
401  return newSigns;
402  }
403 
404  template<typename InputIt>
405  std::list<SignCondition> getSignsAndAddAll(InputIt first, InputIt last) {
406  for(; first != last; first++) getSignsAndAdd(*first);
407  return mSigns;
408  }
409 }; // class SignDetermination
410 
411 template<typename N>
412 std::ostream& operator<<(std::ostream& os, const SignDetermination<N>& rhs) {
413  os << "\n---------------------------------------------------------------------" << std::endl;
414  os << "Status of sign determination object:" << std::endl;
415  os << "---------------------------------------------------------------------" << std::endl;
416  os << "processed polynomials:\t\t" << rhs.processedPolynomials() << std::endl;
417  os << "sign conditions:\t\t" << rhs.signs() << std::endl;
418  os << "adapted list:\t\t\t" << rhs.adaptedList() << std::endl;
419  os << "products:\t\t\t" << rhs.products() << std::endl;
420  os << "matrix dimension:\t\t" << rhs.matrix().rows() << " x " << rhs.matrix().cols() << std::endl;
421  os << "needs update:\t\t\t" << rhs.needsUpdate() << std::endl;
422  os << "---------------------------------------------------------------------" << std::endl;
423  return os;
424 }
425 
426 } // namespace carl
#define CARL_LOG_WARN(channel, msg)
Definition: carl-logging.h:41
#define CARL_LOG_TRACE(channel, msg)
Definition: carl-logging.h:44
#define CARL_LOG_ASSERT(channel, condition, msg)
Definition: carl-logging.h:47
#define CARL_LOG_DEBUG(channel, msg)
Definition: carl-logging.h:43
carl is the main namespace for the library.
std::uint64_t uint
Definition: numbers.h:16
Interval< Number > abs(const Interval< Number > &_in)
Method which returns the absolute value of the passed number.
Definition: Interval.h:1511
std::ostream & operator<<(std::ostream &os, const BasicConstraint< Poly > &c)
Prints the given constraint on the given stream.
@ NEGATIVE
Indicates that .
@ ZERO
Indicates that .
@ POSITIVE
Indicates that .
cln::cl_I round(const cln::cl_RA &n)
Round a fraction to next integer.
Definition: operations.h:255
static Eigen::MatrixXf adaptedMat(const std::list< Alpha > &ada, const std::list< SignCondition > &signs)
static void removeColumn(Eigen::MatrixXf &matrix, Eigen::Index colToRemove)
const auto & signs() const
const auto & products() const
std::list< SignCondition > getSignsAndAddAll(InputIt first, InputIt last)
std::list< Alpha > mAda
static void removeRow(Eigen::MatrixXf &matrix, Eigen::Index rowToRemove)
SignDetermination(InputIt zeroSet_first, InputIt zeroSet_last)
std::list< SignCondition > getSigns(const Polynomial &p)
std::list< uint > mAdaHelper
static Eigen::MatrixXf kroneckerProduct(const Eigen::MatrixXf &m1, const Eigen::MatrixXf &m2)
std::list< uint > Alpha
std::list< SignCondition > getSigns(const Polynomial &p, std::list< Polynomial > &products, std::list< Alpha > &ada, std::list< uint > &adaHelper, Eigen::MatrixXf &matrix)
std::list< Polynomial > mProducts
std::list< SignCondition > getSignsAndAdd(const Polynomial &p)
std::list< SignCondition > mSigns
TarskiQueryManager< Number > mTaQ
std::list< Polynomial > computeProducts(const Polynomial &p, const std::list< Alpha > &currAda) const
SignDetermination(const SignDetermination &other)
std::list< Polynomial > mP
std::list< Alpha > firstNLines(const uint n, const Eigen::MatrixXf &mat, const std::vector< Alpha > &ada, std::vector< Polynomial > &products, const uint q) const
const auto & adaptedList() const
const auto & processedPolynomials() const
MultivariatePolynomial< Number > Polynomial
static int sigmaToTheAlpha(const Alpha &alpha, const SignCondition &sigma)
const auto & matrix() const
typename TarskiQueryManager< Number >::QueryResultType TaQResType