1 /********************* */
2 /*! \file poly_util.cpp
4 ** Top contributors (to current version):
6 ** This file is part of the CVC4 project.
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.\endverbatim
12 ** This file is part of the CVC4 project.
13 ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS
14 ** in the top-level source directory) and their institutional affiliations.
15 ** All rights reserved. See the file COPYING in the top-level source
16 ** directory for licensing information.\endverbatim
18 ** \brief Utilities for working with LibPoly.
20 ** Utilities for working with LibPoly.
23 #include "poly_util.h"
27 #include <poly/polyxx.h>
32 #include "base/check.h"
34 #include "util/integer.h"
35 #include "util/rational.h"
36 #include "util/real_algebraic_number.h"
39 namespace poly_utils
{
43 * Convert arbitrary data using a string as intermediary.
44 * Assumes the existence of operator<<(std::ostream&, const From&) and To(const
45 * std::string&); should be the last resort for type conversions: it may not
46 * only yield bad performance, but is also dependent on compatible string
47 * representations. Use with care!
49 template <typename To
, typename From
>
50 To
cast_by_string(const From
& f
)
58 Integer
toInteger(const poly::Integer
& i
)
60 const mpz_class
& gi
= *poly::detail::cast_to_gmp(&i
);
65 if (std::numeric_limits
<long>::min() <= gi
66 && gi
<= std::numeric_limits
<long>::max())
68 return Integer(gi
.get_si());
72 return cast_by_string
<Integer
, poly::Integer
>(i
);
76 Rational
toRational(const poly::Integer
& i
) { return Rational(toInteger(i
)); }
77 Rational
toRational(const poly::Rational
& r
)
80 return Rational(*poly::detail::cast_to_gmp(&r
));
83 return Rational(toInteger(numerator(r
)), toInteger(denominator(r
)));
86 Rational
toRational(const poly::DyadicRational
& dr
)
88 return Rational(toInteger(numerator(dr
)), toInteger(denominator(dr
)));
90 Rational
toRationalAbove(const poly::Value
& v
)
92 if (is_algebraic_number(v
))
94 return toRational(get_upper_bound(as_algebraic_number(v
)));
96 else if (is_dyadic_rational(v
))
98 return toRational(as_dyadic_rational(v
));
100 else if (is_integer(v
))
102 return toRational(as_integer(v
));
104 else if (is_rational(v
))
106 return toRational(as_rational(v
));
108 Assert(false) << "Can not convert " << v
<< " to rational.";
111 Rational
toRationalBelow(const poly::Value
& v
)
113 if (is_algebraic_number(v
))
115 return toRational(get_lower_bound(as_algebraic_number(v
)));
117 else if (is_dyadic_rational(v
))
119 return toRational(as_dyadic_rational(v
));
121 else if (is_integer(v
))
123 return toRational(as_integer(v
));
125 else if (is_rational(v
))
127 return toRational(as_rational(v
));
129 Assert(false) << "Can not convert " << v
<< " to rational.";
133 poly::Integer
toInteger(const Integer
& i
)
136 return poly::Integer(i
.getValue());
139 if (std::numeric_limits
<long>::min() <= i
.getValue()
140 && i
.getValue() <= std::numeric_limits
<long>::max())
142 return poly::Integer(cln::cl_I_to_long(i
.getValue()));
146 return poly::Integer(cast_by_string
<mpz_class
, Integer
>(i
));
150 std::vector
<poly::Integer
> toInteger(const std::vector
<Integer
>& vi
)
152 std::vector
<poly::Integer
> res
;
153 for (const auto& i
: vi
) res
.emplace_back(toInteger(i
));
156 poly::Rational
toRational(const Rational
& r
)
159 return poly::Rational(r
.getValue());
162 return poly::Rational(toInteger(r
.getNumerator()),
163 toInteger(r
.getDenominator()));
167 Maybe
<poly::DyadicRational
> toDyadicRational(const Rational
& r
)
169 Integer den
= r
.getDenominator();
171 { // It's an integer anyway.
172 return poly::DyadicRational(toInteger(r
.getNumerator()));
174 unsigned long exp
= den
.isPow2();
177 // It's a dyadic rational.
178 return div_2exp(poly::DyadicRational(toInteger(r
.getNumerator())), exp
- 1);
180 return Maybe
<poly::DyadicRational
>();
183 Maybe
<poly::DyadicRational
> toDyadicRational(const poly::Rational
& r
)
185 poly::Integer den
= denominator(r
);
186 if (den
== poly::Integer(1))
187 { // It's an integer anyway.
188 return poly::DyadicRational(numerator(r
));
190 // Use bit_size as an estimate for the dyadic exponent.
191 unsigned long size
= bit_size(den
) - 1;
192 if (mul_pow2(poly::Integer(1), size
) == den
)
194 // It's a dyadic rational.
195 return div_2exp(poly::DyadicRational(numerator(r
)), size
);
197 return Maybe
<poly::DyadicRational
>();
200 poly::Rational
approximateToDyadic(const poly::Rational
& r
,
201 const poly::Rational
& original
)
203 // Multiply both numerator and denominator by two.
204 // Increase or decrease the numerator, depending on whether r is too small or
206 poly::Integer n
= mul_pow2(numerator(r
), 1);
211 else if (r
> original
)
215 return poly::Rational(n
, mul_pow2(denominator(r
), 1));
218 poly::AlgebraicNumber
toPolyRanWithRefinement(poly::UPolynomial
&& p
,
219 const Rational
& lower
,
220 const Rational
& upper
)
222 Maybe
<poly::DyadicRational
> ml
= toDyadicRational(lower
);
223 Maybe
<poly::DyadicRational
> mu
= toDyadicRational(upper
);
226 return poly::AlgebraicNumber(std::move(p
),
227 poly::DyadicInterval(ml
.value(), mu
.value()));
229 // The encoded real algebraic number did not have dyadic rational endpoints.
230 poly::Rational origl
= toRational(lower
);
231 poly::Rational origu
= toRational(upper
);
232 poly::Rational
l(floor(origl
));
233 poly::Rational
u(ceil(origu
));
234 poly::RationalInterval
ri(l
, u
);
235 while (count_real_roots(p
, ri
) != 1)
237 l
= approximateToDyadic(l
, origl
);
238 u
= approximateToDyadic(u
, origu
);
239 ri
= poly::RationalInterval(l
, u
);
241 Assert(count_real_roots(p
, poly::RationalInterval(l
, u
)) == 1);
242 ml
= toDyadicRational(l
);
243 mu
= toDyadicRational(u
);
244 Assert(ml
&& mu
) << "Both bounds should be dyadic by now.";
245 return poly::AlgebraicNumber(std::move(p
),
246 poly::DyadicInterval(ml
.value(), mu
.value()));
249 RealAlgebraicNumber
toRanWithRefinement(poly::UPolynomial
&& p
,
250 const Rational
& lower
,
251 const Rational
& upper
)
253 return RealAlgebraicNumber(
254 toPolyRanWithRefinement(std::move(p
), lower
, upper
));
257 std::size_t totalDegree(const poly::Polynomial
& p
)
259 std::size_t tdeg
= 0;
261 lp_polynomial_traverse_f f
=
262 [](const lp_polynomial_context_t
* ctx
, lp_monomial_t
* m
, void* data
) {
264 for (std::size_t i
= 0; i
< m
->n
; ++i
)
269 std::size_t* td
= static_cast<std::size_t*>(data
);
270 *td
= std::max(*td
, sum
);
273 lp_polynomial_traverse(p
.get_internal(), f
, &tdeg
);
278 std::ostream
& operator<<(std::ostream
& os
, const VariableInformation
& vi
)
280 if (vi
.var
== poly::Variable())
283 os
<< "max deg " << vi
.max_degree
;
284 os
<< ", sum term deg " << vi
.sum_term_degree
;
285 os
<< ", sum poly deg " << vi
.sum_poly_degree
;
286 os
<< ", num polys " << vi
.num_polynomials
;
287 os
<< ", num terms " << vi
.num_terms
;
291 os
<< "Info for " << vi
.var
<< ": ";
292 os
<< "max deg " << vi
.max_degree
;
293 os
<< ", max lc deg: " << vi
.max_lc_degree
;
294 os
<< ", max term tdeg: " << vi
.max_terms_tdegree
;
295 os
<< ", sum term deg " << vi
.sum_term_degree
;
296 os
<< ", sum poly deg " << vi
.sum_poly_degree
;
297 os
<< ", num polys " << vi
.num_polynomials
;
298 os
<< ", num terms " << vi
.num_terms
;
305 VariableInformation
* info
;
306 std::size_t cur_var_degree
= 0;
307 std::size_t cur_lc_degree
= 0;
309 void getVariableInformation(VariableInformation
& vi
,
310 const poly::Polynomial
& poly
)
314 lp_polynomial_traverse_f f
=
315 [](const lp_polynomial_context_t
* ctx
, lp_monomial_t
* m
, void* data
) {
316 GetVarInfo
* gvi
= static_cast<GetVarInfo
*>(data
);
317 VariableInformation
* info
= gvi
->info
;
318 // Total degree of this term
319 std::size_t tdeg
= 0;
320 // Degree of this variable within this term
321 std::size_t vardeg
= 0;
322 for (std::size_t i
= 0; i
< m
->n
; ++i
)
325 if (m
->p
[i
].x
== info
->var
)
327 info
->max_degree
= std::max(info
->max_degree
, m
->p
[i
].d
);
328 info
->sum_term_degree
+= m
->p
[i
].d
;
332 if (info
->var
== poly::Variable())
335 info
->max_degree
= std::max(info
->max_degree
, tdeg
);
336 info
->sum_term_degree
+= tdeg
;
341 if (gvi
->cur_var_degree
< vardeg
)
343 gvi
->cur_lc_degree
= tdeg
- vardeg
;
345 info
->max_terms_tdegree
= std::max(info
->max_terms_tdegree
, tdeg
);
348 std::size_t tmp_max_degree
= vi
.max_degree
;
349 std::size_t tmp_num_terms
= vi
.num_terms
;
352 lp_polynomial_traverse(poly
.get_internal(), f
, &varinfo
);
353 vi
.max_lc_degree
= std::max(vi
.max_lc_degree
, varinfo
.cur_lc_degree
);
354 if (vi
.num_terms
> 0)
356 ++vi
.num_polynomials
;
358 vi
.sum_poly_degree
+= vi
.max_degree
;
359 vi
.max_degree
= std::max(vi
.max_degree
, tmp_max_degree
);
360 vi
.num_terms
+= tmp_num_terms
;
363 } // namespace poly_utils