GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/util/poly_util.cpp Lines: 102 163 62.6 %
Date: 2021-08-16 Branches: 116 364 31.9 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Gereon Kremer
4
 *
5
 * This file is part of the cvc5 project.
6
 *
7
 * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 * in the top-level source directory and their institutional affiliations.
9
 * All rights reserved.  See the file COPYING in the top-level source
10
 * directory for licensing information.
11
 * ****************************************************************************
12
 *
13
 * Utilities for working with LibPoly.
14
 */
15
16
#include "poly_util.h"
17
18
#ifdef CVC5_POLY_IMP
19
20
#include <poly/polyxx.h>
21
22
#include <map>
23
#include <sstream>
24
25
#include "base/check.h"
26
#include "maybe.h"
27
#include "util/integer.h"
28
#include "util/rational.h"
29
#include "util/real_algebraic_number.h"
30
31
namespace cvc5 {
32
namespace poly_utils {
33
34
namespace {
35
/**
36
 * Convert arbitrary data using a string as intermediary.
37
 * Assumes the existence of operator<<(std::ostream&, const From&) and To(const
38
 * std::string&); should be the last resort for type conversions: it may not
39
 * only yield bad performance, but is also dependent on compatible string
40
 * representations. Use with care!
41
 */
42
template <typename To, typename From>
43
212
To cast_by_string(const From& f)
44
{
45
424
  std::stringstream s;
46
212
  s << f;
47
424
  return To(s.str());
48
}
49
}  // namespace
50
51
132
Integer toInteger(const poly::Integer& i)
52
{
53
132
  const mpz_class& gi = *poly::detail::cast_to_gmp(&i);
54
#ifdef CVC5_GMP_IMP
55
  return Integer(gi);
56
#endif
57
#ifdef CVC5_CLN_IMP
58
264
  if (std::numeric_limits<long>::min() <= gi
59
132
      && gi <= std::numeric_limits<long>::max())
60
  {
61
132
    return Integer(gi.get_si());
62
  }
63
  else
64
  {
65
    return cast_by_string<Integer, poly::Integer>(i);
66
  }
67
#endif
68
}
69
30
Rational toRational(const poly::Integer& i) { return Rational(toInteger(i)); }
70
30
Rational toRational(const poly::Rational& r)
71
{
72
#ifdef CVC5_GMP_IMP
73
  return Rational(*poly::detail::cast_to_gmp(&r));
74
#endif
75
#ifdef CVC5_CLN_IMP
76
30
  return Rational(toInteger(numerator(r)), toInteger(denominator(r)));
77
#endif
78
}
79
21
Rational toRational(const poly::DyadicRational& dr)
80
{
81
21
  return Rational(toInteger(numerator(dr)), toInteger(denominator(dr)));
82
}
83
Rational toRationalAbove(const poly::Value& v)
84
{
85
  if (is_algebraic_number(v))
86
  {
87
    return toRational(get_upper_bound(as_algebraic_number(v)));
88
  }
89
  else if (is_dyadic_rational(v))
90
  {
91
    return toRational(as_dyadic_rational(v));
92
  }
93
  else if (is_integer(v))
94
  {
95
    return toRational(as_integer(v));
96
  }
97
  else if (is_rational(v))
98
  {
99
    return toRational(as_rational(v));
100
  }
101
  Assert(false) << "Can not convert " << v << " to rational.";
102
  return Rational();
103
}
104
Rational toRationalBelow(const poly::Value& v)
105
{
106
  if (is_algebraic_number(v))
107
  {
108
    return toRational(get_lower_bound(as_algebraic_number(v)));
109
  }
110
  else if (is_dyadic_rational(v))
111
  {
112
    return toRational(as_dyadic_rational(v));
113
  }
114
  else if (is_integer(v))
115
  {
116
    return toRational(as_integer(v));
117
  }
118
  else if (is_rational(v))
119
  {
120
    return toRational(as_rational(v));
121
  }
122
  Assert(false) << "Can not convert " << v << " to rational.";
123
  return Rational();
124
}
125
126
2152
poly::Integer toInteger(const Integer& i)
127
{
128
#ifdef CVC5_GMP_IMP
129
  return poly::Integer(i.getValue());
130
#endif
131
#ifdef CVC5_CLN_IMP
132
6456
  if (std::numeric_limits<long>::min() <= i.getValue()
133
6456
      && i.getValue() <= std::numeric_limits<long>::max())
134
  {
135
1940
    return poly::Integer(cln::cl_I_to_long(i.getValue()));
136
  }
137
  else
138
  {
139
212
    return poly::Integer(cast_by_string<mpz_class, Integer>(i));
140
  }
141
#endif
142
}
143
std::vector<poly::Integer> toInteger(const std::vector<Integer>& vi)
144
{
145
  std::vector<poly::Integer> res;
146
  for (const auto& i : vi) res.emplace_back(toInteger(i));
147
  return res;
148
}
149
98
poly::Rational toRational(const Rational& r)
150
{
151
#ifdef CVC5_GMP_IMP
152
  return poly::Rational(r.getValue());
153
#endif
154
#ifdef CVC5_CLN_IMP
155
196
  return poly::Rational(toInteger(r.getNumerator()),
156
294
                        toInteger(r.getDenominator()));
157
#endif
158
}
159
160
10
Maybe<poly::DyadicRational> toDyadicRational(const Rational& r)
161
{
162
20
  Integer den = r.getDenominator();
163
10
  if (den.isOne())
164
  {  // It's an integer anyway.
165
2
    return poly::DyadicRational(toInteger(r.getNumerator()));
166
  }
167
8
  unsigned long exp = den.isPow2();
168
8
  if (exp > 0)
169
  {
170
    // It's a dyadic rational.
171
4
    return div_2exp(poly::DyadicRational(toInteger(r.getNumerator())), exp - 1);
172
  }
173
4
  return Maybe<poly::DyadicRational>();
174
}
175
176
4
Maybe<poly::DyadicRational> toDyadicRational(const poly::Rational& r)
177
{
178
8
  poly::Integer den = denominator(r);
179
4
  if (den == poly::Integer(1))
180
  {  // It's an integer anyway.
181
4
    return poly::DyadicRational(numerator(r));
182
  }
183
  // Use bit_size as an estimate for the dyadic exponent.
184
  unsigned long size = bit_size(den) - 1;
185
  if (mul_pow2(poly::Integer(1), size) == den)
186
  {
187
    // It's a dyadic rational.
188
    return div_2exp(poly::DyadicRational(numerator(r)), size);
189
  }
190
  return Maybe<poly::DyadicRational>();
191
}
192
193
poly::Rational approximateToDyadic(const poly::Rational& r,
194
                                   const poly::Rational& original)
195
{
196
  // Multiply both numerator and denominator by two.
197
  // Increase or decrease the numerator, depending on whether r is too small or
198
  // too large.
199
  poly::Integer n = mul_pow2(numerator(r), 1);
200
  if (r < original)
201
  {
202
    ++n;
203
  }
204
  else if (r > original)
205
  {
206
    --n;
207
  }
208
  return poly::Rational(n, mul_pow2(denominator(r), 1));
209
}
210
211
4
poly::AlgebraicNumber toPolyRanWithRefinement(poly::UPolynomial&& p,
212
                                              const Rational& lower,
213
                                              const Rational& upper)
214
{
215
8
  Maybe<poly::DyadicRational> ml = toDyadicRational(lower);
216
8
  Maybe<poly::DyadicRational> mu = toDyadicRational(upper);
217
4
  if (ml && mu)
218
  {
219
2
    return poly::AlgebraicNumber(std::move(p),
220
4
                                 poly::DyadicInterval(ml.value(), mu.value()));
221
  }
222
  // The encoded real algebraic number did not have dyadic rational endpoints.
223
4
  poly::Rational origl = toRational(lower);
224
4
  poly::Rational origu = toRational(upper);
225
4
  poly::Rational l(floor(origl));
226
4
  poly::Rational u(ceil(origu));
227
4
  poly::RationalInterval ri(l, u);
228
2
  while (count_real_roots(p, ri) != 1)
229
  {
230
    l = approximateToDyadic(l, origl);
231
    u = approximateToDyadic(u, origu);
232
    ri = poly::RationalInterval(l, u);
233
  }
234
2
  Assert(count_real_roots(p, poly::RationalInterval(l, u)) == 1);
235
2
  ml = toDyadicRational(l);
236
2
  mu = toDyadicRational(u);
237
2
  Assert(ml && mu) << "Both bounds should be dyadic by now.";
238
2
  return poly::AlgebraicNumber(std::move(p),
239
4
                               poly::DyadicInterval(ml.value(), mu.value()));
240
}
241
242
2
RealAlgebraicNumber toRanWithRefinement(poly::UPolynomial&& p,
243
                                        const Rational& lower,
244
                                        const Rational& upper)
245
{
246
  return RealAlgebraicNumber(
247
2
      toPolyRanWithRefinement(std::move(p), lower, upper));
248
}
249
250
73042
std::size_t totalDegree(const poly::Polynomial& p)
251
{
252
73042
  std::size_t tdeg = 0;
253
254
73042
  lp_polynomial_traverse_f f =
255
289836
      [](const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* data) {
256
144918
        std::size_t sum = 0;
257
237010
        for (std::size_t i = 0; i < m->n; ++i)
258
        {
259
92092
          sum += m->p[i].d;
260
        }
261
262
144918
        std::size_t* td = static_cast<std::size_t*>(data);
263
144918
        *td = std::max(*td, sum);
264
289836
      };
265
266
73042
  lp_polynomial_traverse(p.get_internal(), f, &tdeg);
267
268
73042
  return tdeg;
269
}
270
271
std::ostream& operator<<(std::ostream& os, const VariableInformation& vi)
272
{
273
  if (vi.var == poly::Variable())
274
  {
275
    os << "Totals: ";
276
    os << "max deg " << vi.max_degree;
277
    os << ", sum term deg " << vi.sum_term_degree;
278
    os << ", sum poly deg " << vi.sum_poly_degree;
279
    os << ", num polys " << vi.num_polynomials;
280
    os << ", num terms " << vi.num_terms;
281
  }
282
  else
283
  {
284
    os << "Info for " << vi.var << ": ";
285
    os << "max deg " << vi.max_degree;
286
    os << ", max lc deg: " << vi.max_lc_degree;
287
    os << ", max term tdeg: " << vi.max_terms_tdegree;
288
    os << ", sum term deg " << vi.sum_term_degree;
289
    os << ", sum poly deg " << vi.sum_poly_degree;
290
    os << ", num polys " << vi.num_polynomials;
291
    os << ", num terms " << vi.num_terms;
292
  }
293
  return os;
294
}
295
296
1760
struct GetVarInfo
297
{
298
  VariableInformation* info;
299
  std::size_t cur_var_degree = 0;
300
  std::size_t cur_lc_degree = 0;
301
};
302
1760
void getVariableInformation(VariableInformation& vi,
303
                            const poly::Polynomial& poly)
304
{
305
1760
  GetVarInfo varinfo;
306
1760
  varinfo.info = &vi;
307
1760
  lp_polynomial_traverse_f f =
308
7498
      [](const lp_polynomial_context_t* ctx, lp_monomial_t* m, void* data) {
309
3749
        GetVarInfo* gvi = static_cast<GetVarInfo*>(data);
310
3749
        VariableInformation* info = gvi->info;
311
        // Total degree of this term
312
3749
        std::size_t tdeg = 0;
313
        // Degree of this variable within this term
314
3749
        std::size_t vardeg = 0;
315
6797
        for (std::size_t i = 0; i < m->n; ++i)
316
        {
317
3048
          tdeg += m->p[i].d;
318
3048
          if (m->p[i].x == info->var)
319
          {
320
1271
            info->max_degree = std::max(info->max_degree, m->p[i].d);
321
1271
            info->sum_term_degree += m->p[i].d;
322
1271
            vardeg = m->p[i].d;
323
          }
324
        }
325
3749
        if (info->var == poly::Variable())
326
        {
327
          ++info->num_terms;
328
          info->max_degree = std::max(info->max_degree, tdeg);
329
          info->sum_term_degree += tdeg;
330
        }
331
3749
        else if (vardeg > 0)
332
        {
333
1271
          ++info->num_terms;
334
1271
          if (gvi->cur_var_degree < vardeg)
335
          {
336
1271
            gvi->cur_lc_degree = tdeg - vardeg;
337
          }
338
1271
          info->max_terms_tdegree = std::max(info->max_terms_tdegree, tdeg);
339
        }
340
7498
      };
341
1760
  std::size_t tmp_max_degree = vi.max_degree;
342
1760
  std::size_t tmp_num_terms = vi.num_terms;
343
1760
  vi.max_degree = 0;
344
1760
  vi.num_terms = 0;
345
1760
  lp_polynomial_traverse(poly.get_internal(), f, &varinfo);
346
1760
  vi.max_lc_degree = std::max(vi.max_lc_degree, varinfo.cur_lc_degree);
347
1760
  if (vi.num_terms > 0)
348
  {
349
1103
    ++vi.num_polynomials;
350
  }
351
1760
  vi.sum_poly_degree += vi.max_degree;
352
1760
  vi.max_degree = std::max(vi.max_degree, tmp_max_degree);
353
1760
  vi.num_terms += tmp_num_terms;
354
1760
}
355
356
}  // namespace poly_utils
357
29340
}  // namespace cvc5
358
359
#endif