mirror of
https://github.com/Z3Prover/z3
synced 2025-04-23 17:15:31 +00:00
reorganizing the code
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
parent
b89d35dd69
commit
9e299b88c4
101 changed files with 16 additions and 16 deletions
855
src/math/euclid/euclidean_solver.cpp
Normal file
855
src/math/euclid/euclidean_solver.cpp
Normal file
|
@ -0,0 +1,855 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
euclidean_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Euclidean Solver with support for explanations.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-07-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"euclidean_solver.h"
|
||||
#include"numeral_buffer.h"
|
||||
#include"heap.h"
|
||||
|
||||
struct euclidean_solver::imp {
|
||||
typedef unsigned var;
|
||||
typedef unsigned justification;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
typedef numeral_buffer<mpq, numeral_manager> mpq_buffer;
|
||||
typedef svector<justification> justification_vector;
|
||||
static const justification null_justification = UINT_MAX;
|
||||
#define null_var UINT_MAX
|
||||
#define null_eq_idx UINT_MAX
|
||||
typedef svector<var> var_vector;
|
||||
typedef svector<mpz> mpz_vector;
|
||||
typedef svector<mpq> mpq_vector;
|
||||
|
||||
struct elim_order_lt {
|
||||
unsigned_vector & m_solved;
|
||||
elim_order_lt(unsigned_vector & s):m_solved(s) {}
|
||||
bool operator()(var x1, var x2) const { return m_solved[x1] < m_solved[x2]; }
|
||||
};
|
||||
|
||||
typedef heap<elim_order_lt> var_queue; // queue used for scheduling variables for applying substitution.
|
||||
|
||||
static unsigned pos(unsigned_vector const & xs, unsigned x_i) {
|
||||
if (xs.empty())
|
||||
return UINT_MAX;
|
||||
int low = 0;
|
||||
int high = xs.size() - 1;
|
||||
while (true) {
|
||||
int mid = low + ((high - low) / 2);
|
||||
var x_mid = xs[mid];
|
||||
if (x_i > x_mid) {
|
||||
low = mid + 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else if (x_i < x_mid) {
|
||||
high = mid - 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Equation as[0]*xs[0] + ... + as[n-1]*xs[n-1] + c = 0 with justification bs[0]*js[0] + ... + bs[m-1]*js[m-1]
|
||||
*/
|
||||
struct equation {
|
||||
mpz_vector m_as;
|
||||
var_vector m_xs;
|
||||
mpz m_c;
|
||||
// justification
|
||||
mpq_vector m_bs;
|
||||
justification_vector m_js;
|
||||
|
||||
unsigned size() const { return m_xs.size(); }
|
||||
unsigned js_size() const { return m_js.size(); }
|
||||
var x(unsigned i) const { return m_xs[i]; }
|
||||
var & x(unsigned i) { return m_xs[i]; }
|
||||
mpz const & a(unsigned i) const { return m_as[i]; }
|
||||
mpz & a(unsigned i) { return m_as[i]; }
|
||||
mpz const & c() const { return m_c; }
|
||||
mpz & c() { return m_c; }
|
||||
var j(unsigned i) const { return m_js[i]; }
|
||||
var & j(unsigned i) { return m_js[i]; }
|
||||
mpq const & b(unsigned i) const { return m_bs[i]; }
|
||||
mpq & b(unsigned i) { return m_bs[i]; }
|
||||
|
||||
unsigned pos_x(unsigned x_i) const { return pos(m_xs, x_i); }
|
||||
};
|
||||
|
||||
typedef ptr_vector<equation> equations;
|
||||
typedef svector<unsigned> occs;
|
||||
|
||||
numeral_manager * m_manager;
|
||||
bool m_owns_m;
|
||||
volatile bool m_cancel;
|
||||
|
||||
equations m_equations;
|
||||
equations m_solution;
|
||||
|
||||
svector<bool> m_parameter;
|
||||
unsigned_vector m_solved; // null_eq_idx if var is not solved, otherwise the position in m_solution
|
||||
vector<occs> m_occs; // occurrences of the variable in m_equations.
|
||||
|
||||
unsigned m_inconsistent; // null_eq_idx if not inconsistent, otherwise it is the index of an unsatisfiable equality in m_equations.
|
||||
unsigned m_next_justification;
|
||||
mpz_buffer m_decompose_buffer;
|
||||
mpz_buffer m_as_buffer;
|
||||
mpq_buffer m_bs_buffer;
|
||||
|
||||
var_vector m_tmp_xs;
|
||||
mpz_buffer m_tmp_as;
|
||||
mpq_buffer m_tmp_bs;
|
||||
|
||||
var_vector m_norm_xs_vector;
|
||||
mpz_vector m_norm_as_vector;
|
||||
mpq_vector m_norm_bs_vector;
|
||||
|
||||
var_queue m_var_queue;
|
||||
|
||||
// next candidate
|
||||
unsigned m_next_eq;
|
||||
var m_next_x;
|
||||
mpz m_next_a;
|
||||
bool m_next_pos_a;
|
||||
|
||||
numeral_manager & m() const { return *m_manager; }
|
||||
|
||||
bool solved(var x) const { return m_solved[x] != null_eq_idx; }
|
||||
|
||||
template<typename Numeral>
|
||||
void sort_core(svector<Numeral> & as, unsigned_vector & xs, numeral_buffer<Numeral, numeral_manager> & buffer) {
|
||||
std::sort(xs.begin(), xs.end());
|
||||
unsigned num = as.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
m().swap(as[i], buffer[xs[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Numeral>
|
||||
void sort(svector<Numeral> & as, unsigned_vector & xs, numeral_buffer<Numeral, numeral_manager> & buffer) {
|
||||
unsigned num = as.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
m().set(buffer[xs[i]], as[i]);
|
||||
}
|
||||
sort_core(as, xs, buffer);
|
||||
}
|
||||
|
||||
equation * mk_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, unsigned num_js, mpq const * bs, justification const * js,
|
||||
bool sort = true) {
|
||||
equation * new_eq = alloc(equation);
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
m().set(m_as_buffer[xs[i]], as[i]);
|
||||
new_eq->m_as.push_back(mpz());
|
||||
new_eq->m_xs.push_back(xs[i]);
|
||||
}
|
||||
sort_core(new_eq->m_as, new_eq->m_xs, m_as_buffer);
|
||||
m().set(new_eq->m_c, c);
|
||||
for (unsigned i = 0; i < num_js; i++) {
|
||||
m().set(m_bs_buffer[js[i]], bs[i]);
|
||||
new_eq->m_bs.push_back(mpq());
|
||||
new_eq->m_js.push_back(js[i]);
|
||||
}
|
||||
if (sort)
|
||||
sort_core(new_eq->m_bs, new_eq->m_js, m_bs_buffer);
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
template<typename Numeral>
|
||||
void div(svector<Numeral> & as, mpz const & g) {
|
||||
unsigned n = as.size();
|
||||
for (unsigned i = 0; i < n; i++)
|
||||
m().div(as[i], g, as[i]);
|
||||
}
|
||||
|
||||
void normalize_eq(unsigned eq_idx) {
|
||||
if (inconsistent())
|
||||
return;
|
||||
equation & eq = *(m_equations[eq_idx]);
|
||||
TRACE("euclidean_solver", tout << "normalizing:\n"; display(tout, eq); tout << "\n";);
|
||||
unsigned num = eq.size();
|
||||
if (num == 0) {
|
||||
// c == 0 inconsistency
|
||||
if (!m().is_zero(eq.c())) {
|
||||
TRACE("euclidean_solver", tout << "c = 0 inconsistency detected\n";);
|
||||
m_inconsistent = eq_idx;
|
||||
}
|
||||
else {
|
||||
del_eq(&eq);
|
||||
m_equations[eq_idx] = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mpz g;
|
||||
mpz a;
|
||||
m().set(g, eq.a(0));
|
||||
m().abs(g);
|
||||
for (unsigned i = 1; i < num; i++) {
|
||||
if (m().is_one(g))
|
||||
break;
|
||||
m().set(a, eq.a(i));
|
||||
m().abs(a);
|
||||
m().gcd(g, a, g);
|
||||
}
|
||||
if (m().is_one(g))
|
||||
return;
|
||||
if (!m().divides(g, eq.c())) {
|
||||
// g does not divide c
|
||||
TRACE("euclidean_solver", tout << "gcd inconsistency detected\n";);
|
||||
m_inconsistent = eq_idx;
|
||||
return;
|
||||
}
|
||||
div(eq.m_as, g);
|
||||
div(eq.m_bs, g);
|
||||
m().del(g);
|
||||
m().del(a);
|
||||
TRACE("euclidean_solver", tout << "after normalization:\n"; display(tout, eq); tout << "\n";);
|
||||
}
|
||||
|
||||
bool is_better(mpz const & a, var x, unsigned eq_sz) {
|
||||
SASSERT(m().is_pos(a));
|
||||
if (m_next_x == null_var)
|
||||
return true;
|
||||
if (m().lt(a, m_next_a))
|
||||
return true;
|
||||
if (m().lt(m_next_a, a))
|
||||
return false;
|
||||
if (m_occs[x].size() < m_occs[m_next_x].size())
|
||||
return true;
|
||||
if (m_occs[x].size() > m_occs[m_next_x].size())
|
||||
return false;
|
||||
return eq_sz < m_equations[m_next_eq]->size();
|
||||
}
|
||||
|
||||
void updt_next_candidate(unsigned eq_idx) {
|
||||
if (!m_equations[eq_idx])
|
||||
return;
|
||||
mpz abs_a;
|
||||
equation const & eq = *(m_equations[eq_idx]);
|
||||
unsigned num = eq.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
mpz const & a = eq.a(i);
|
||||
m().set(abs_a, a);
|
||||
m().abs(abs_a);
|
||||
if (is_better(abs_a, eq.x(i), num)) {
|
||||
m().set(m_next_a, abs_a);
|
||||
m_next_x = eq.x(i);
|
||||
m_next_eq = eq_idx;
|
||||
m_next_pos_a = m().is_pos(a);
|
||||
}
|
||||
}
|
||||
m().del(abs_a);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Select next variable to be eliminated.
|
||||
Return false if there is not variable to eliminate.
|
||||
|
||||
The result is in
|
||||
m_next_x variable to be eliminated
|
||||
m_next_eq id of the equation containing x
|
||||
m_next_a absolute value of the coefficient of x in eq.
|
||||
m_next_pos_a true if the coefficient of x is positive in eq.
|
||||
*/
|
||||
bool select_next_var() {
|
||||
while (!m_equations.empty() && m_equations.back() == 0)
|
||||
m_equations.pop_back();
|
||||
if (m_equations.empty())
|
||||
return false;
|
||||
SASSERT(!m_equations.empty() && m_equations.back() != 0);
|
||||
m_next_x = null_var;
|
||||
unsigned eq_idx = m_equations.size();
|
||||
while (eq_idx > 0) {
|
||||
--eq_idx;
|
||||
updt_next_candidate(eq_idx);
|
||||
// stop as soon as possible
|
||||
// TODO: use heuristics
|
||||
if (m_next_x != null_var && m().is_one(m_next_a))
|
||||
return true;
|
||||
}
|
||||
CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout););
|
||||
SASSERT(m_next_x != null_var);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Numeral>
|
||||
void del_nums(svector<Numeral> & as) {
|
||||
unsigned sz = as.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
m().del(as[i]);
|
||||
as.reset();
|
||||
}
|
||||
|
||||
void del_eq(equation * eq) {
|
||||
m().del(eq->c());
|
||||
del_nums(eq->m_as);
|
||||
del_nums(eq->m_bs);
|
||||
dealloc(eq);
|
||||
}
|
||||
|
||||
void del_equations(equations & eqs) {
|
||||
unsigned sz = eqs.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (eqs[i])
|
||||
del_eq(eqs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Store the "solved" variables in xs into m_var_queue.
|
||||
*/
|
||||
void schedule_var_subst(unsigned num, var const * xs) {
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (solved(xs[i]))
|
||||
m_var_queue.insert(xs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_var_subst(var_vector const & xs) {
|
||||
schedule_var_subst(xs.size(), xs.c_ptr());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Store as1*xs1 + k*as2*xs2 into new_as*new_xs
|
||||
|
||||
If UpdateOcc == true,
|
||||
Then,
|
||||
1) for each variable x occurring in xs2 but not in xs1:
|
||||
- eq_idx is added to m_occs[x]
|
||||
2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero,
|
||||
- eq_idx is removed from m_occs[x] IF x != except_var
|
||||
|
||||
If UpdateQueue == true
|
||||
Then,
|
||||
1) for each variable x occurring in xs2 but not in xs1:
|
||||
- if x is solved, then x is inserted into m_var_queue
|
||||
2) for each variable that occurs in xs1 and xs2 and the resultant coefficient is zero,
|
||||
- if x is solved, then x is removed from m_var_queue
|
||||
|
||||
*/
|
||||
template<typename Numeral, bool UpdateOcc, bool UpdateQueue>
|
||||
void addmul(svector<Numeral> const & as1, var_vector const & xs1,
|
||||
mpz const & k, svector<Numeral> const & as2, var_vector const & xs2,
|
||||
numeral_buffer<Numeral, numeral_manager> & new_as, var_vector & new_xs,
|
||||
unsigned eq_idx = null_eq_idx, var except_var = null_var) {
|
||||
Numeral new_a;
|
||||
SASSERT(as1.size() == xs1.size());
|
||||
SASSERT(as2.size() == xs2.size());
|
||||
new_as.reset();
|
||||
new_xs.reset();
|
||||
unsigned sz1 = xs1.size();
|
||||
unsigned sz2 = xs2.size();
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
while (true) {
|
||||
if (i1 == sz1) {
|
||||
// copy remaining entries from as2*xs2
|
||||
while (i2 < sz2) {
|
||||
var x2 = xs2[i2];
|
||||
if (UpdateOcc)
|
||||
m_occs[x2].push_back(eq_idx);
|
||||
if (UpdateQueue && solved(x2))
|
||||
m_var_queue.insert(x2);
|
||||
new_as.push_back(Numeral());
|
||||
m().mul(k, as2[i2], new_as.back());
|
||||
new_xs.push_back(x2);
|
||||
i2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i2 == sz2) {
|
||||
// copy remaining entries from as1*xs1
|
||||
while (i1 < sz1) {
|
||||
new_as.push_back(as1[i1]);
|
||||
new_xs.push_back(xs1[i1]);
|
||||
i1++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var x1 = xs1[i1];
|
||||
var x2 = xs2[i2];
|
||||
if (x1 < x2) {
|
||||
new_as.push_back(as1[i1]);
|
||||
new_xs.push_back(xs1[i1]);
|
||||
i1++;
|
||||
}
|
||||
else if (x1 > x2) {
|
||||
if (UpdateOcc)
|
||||
m_occs[x2].push_back(eq_idx);
|
||||
if (UpdateQueue && solved(x2))
|
||||
m_var_queue.insert(x2);
|
||||
new_as.push_back(Numeral());
|
||||
m().mul(k, as2[i2], new_as.back());
|
||||
new_xs.push_back(x2);
|
||||
i2++;
|
||||
}
|
||||
else {
|
||||
m().addmul(as1[i1], k, as2[i2], new_a);
|
||||
TRACE("euclidean_solver_add_mul", tout << "i1: " << i1 << ", i2: " << i2 << " new_a: " << m().to_string(new_a) << "\n";
|
||||
tout << "as1: " << m().to_string(as1[i1]) << ", k: " << m().to_string(k) << ", as2: " << m().to_string(as2[i2]) << "\n";);
|
||||
if (m().is_zero(new_a)) {
|
||||
// variable was canceled
|
||||
if (UpdateOcc && x1 != except_var)
|
||||
m_occs[x1].erase(eq_idx);
|
||||
if (UpdateQueue && solved(x1) && m_var_queue.contains(x1))
|
||||
m_var_queue.erase(x1);
|
||||
}
|
||||
else {
|
||||
new_as.push_back(new_a);
|
||||
new_xs.push_back(x1);
|
||||
}
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
m().del(new_a);
|
||||
}
|
||||
|
||||
template<bool UpdateOcc, bool UpdateQueue>
|
||||
void apply_solution(var x, mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js,
|
||||
unsigned eq_idx = null_eq_idx, var except_var = null_var) {
|
||||
SASSERT(solved(x));
|
||||
unsigned idx = pos(xs, x);
|
||||
if (idx == UINT_MAX)
|
||||
return;
|
||||
mpz const & a1 = as[idx];
|
||||
SASSERT(!m().is_zero(a1));
|
||||
equation const & eq2 = *(m_solution[m_solved[x]]);
|
||||
SASSERT(eq2.pos_x(x) != UINT_MAX);
|
||||
SASSERT(m().is_minus_one(eq2.a(eq2.pos_x(x))));
|
||||
TRACE("euclidean_solver_apply",
|
||||
tout << "applying: " << m().to_string(a1) << " * "; display(tout, eq2); tout << "\n";
|
||||
for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
addmul<mpz, UpdateOcc, UpdateQueue>(as, xs, a1, eq2.m_as, eq2.m_xs, m_tmp_as, m_tmp_xs, eq_idx, except_var);
|
||||
m().addmul(c, a1, eq2.m_c, c);
|
||||
m_tmp_as.swap(as);
|
||||
m_tmp_xs.swap(xs);
|
||||
SASSERT(as.size() == xs.size());
|
||||
TRACE("euclidean_solver_apply", for (unsigned i = 0; i < xs.size(); i++) tout << m().to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
addmul<mpq, false, false>(bs, js, a1, eq2.m_bs, eq2.m_js, m_tmp_bs, m_tmp_xs);
|
||||
m_tmp_bs.swap(bs);
|
||||
m_tmp_xs.swap(js);
|
||||
SASSERT(pos(xs, x) == UINT_MAX);
|
||||
}
|
||||
|
||||
void apply_solution(mpz_vector & as, var_vector & xs, mpz & c, mpq_vector & bs, justification_vector & js) {
|
||||
m_var_queue.reset();
|
||||
schedule_var_subst(xs);
|
||||
while (!m_var_queue.empty()) {
|
||||
var x = m_var_queue.erase_min();
|
||||
apply_solution<false, true>(x, as, xs, c, bs, js);
|
||||
}
|
||||
}
|
||||
|
||||
void apply_solution(equation & eq) {
|
||||
apply_solution(eq.m_as, eq.m_xs, eq.m_c, eq.m_bs, eq.m_js);
|
||||
}
|
||||
|
||||
void display(std::ostream & out, equation const & eq) const {
|
||||
unsigned num = eq.js_size();
|
||||
for (unsigned i = 0; i < num; i ++) {
|
||||
if (i > 0) out << " ";
|
||||
out << m().to_string(eq.b(i)) << "*j" << eq.j(i);
|
||||
}
|
||||
if (num > 0) out << " ";
|
||||
out << "|= ";
|
||||
num = eq.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
out << m().to_string(eq.a(i)) << "*x" << eq.x(i) << " + ";
|
||||
}
|
||||
out << m().to_string(eq.c()) << " = 0";
|
||||
}
|
||||
|
||||
void display(std::ostream & out, equations const & eqs) const {
|
||||
unsigned num = eqs.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
if (eqs[i]) {
|
||||
display(out, *(eqs[i]));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void display(std::ostream & out) const {
|
||||
if (inconsistent()) {
|
||||
out << "inconsistent: ";
|
||||
display(out, *(m_equations[m_inconsistent]));
|
||||
out << "\n";
|
||||
}
|
||||
out << "solution set:\n";
|
||||
display(out, m_solution);
|
||||
out << "todo:\n";
|
||||
display(out, m_equations);
|
||||
}
|
||||
|
||||
void add_occs(unsigned eq_idx) {
|
||||
equation const & eq = *(m_equations[eq_idx]);
|
||||
unsigned sz = eq.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
m_occs[eq.x(i)].push_back(eq_idx);
|
||||
}
|
||||
|
||||
imp(numeral_manager * m):
|
||||
m_manager(m == 0 ? alloc(numeral_manager) : m),
|
||||
m_owns_m(m == 0),
|
||||
m_decompose_buffer(*m_manager),
|
||||
m_as_buffer(*m_manager),
|
||||
m_bs_buffer(*m_manager),
|
||||
m_tmp_as(*m_manager),
|
||||
m_tmp_bs(*m_manager),
|
||||
m_var_queue(16, elim_order_lt(m_solved)) {
|
||||
m_inconsistent = null_eq_idx;
|
||||
m_next_justification = 0;
|
||||
m_cancel = false;
|
||||
m_next_x = null_var;
|
||||
m_next_eq = null_eq_idx;
|
||||
}
|
||||
|
||||
~imp() {
|
||||
m().del(m_next_a);
|
||||
del_equations(m_equations);
|
||||
del_equations(m_solution);
|
||||
if (m_owns_m)
|
||||
dealloc(m_manager);
|
||||
}
|
||||
|
||||
var mk_var(bool parameter) {
|
||||
var x = m_solved.size();
|
||||
m_parameter.push_back(parameter);
|
||||
m_solved.push_back(null_eq_idx);
|
||||
m_occs.push_back(occs());
|
||||
m_as_buffer.push_back(mpz());
|
||||
m_var_queue.reserve(x+1);
|
||||
return x;
|
||||
}
|
||||
|
||||
justification mk_justification() {
|
||||
justification r = m_next_justification;
|
||||
m_bs_buffer.push_back(mpq());
|
||||
m_next_justification++;
|
||||
return r;
|
||||
}
|
||||
|
||||
void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) {
|
||||
if (inconsistent())
|
||||
return;
|
||||
equation * eq;
|
||||
if (j == null_justification) {
|
||||
eq = mk_eq(num, as, xs, c, 0, 0, 0);
|
||||
}
|
||||
else {
|
||||
mpq one(1);
|
||||
eq = mk_eq(num, as, xs, c, 1, &one, &j);
|
||||
}
|
||||
TRACE("euclidean_solver", tout << "new-eq:\n"; display(tout, *eq); tout << "\n";);
|
||||
unsigned eq_idx = m_equations.size();
|
||||
m_equations.push_back(eq);
|
||||
apply_solution(*eq);
|
||||
normalize_eq(eq_idx);
|
||||
add_occs(eq_idx);
|
||||
TRACE("euclidean_solver", tout << "asserted:\n"; display(tout, *eq); tout << "\n";);
|
||||
}
|
||||
|
||||
justification_vector const & get_justification() const {
|
||||
SASSERT(inconsistent());
|
||||
return m_equations[m_inconsistent]->m_js;
|
||||
}
|
||||
|
||||
template<typename Numeral>
|
||||
void neg_coeffs(svector<Numeral> & as) {
|
||||
unsigned sz = as.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m().neg(as[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void substitute_most_recent_solution(var x) {
|
||||
SASSERT(!m_solution.empty());
|
||||
equation & eq = *(m_solution.back());
|
||||
TRACE("euclidean_solver", tout << "applying solution for x" << x << "\n"; display(tout, eq); tout << "\n";);
|
||||
occs & use_list = m_occs[x];
|
||||
occs::iterator it = use_list.begin();
|
||||
occs::iterator end = use_list.end();
|
||||
for (; it != end; ++it) {
|
||||
unsigned eq_idx = *it;
|
||||
// remark we don't want to update the use_list of x while we are traversing it.
|
||||
equation & eq2 = *(m_equations[eq_idx]);
|
||||
TRACE("euclidean_solver", tout << "eq before substituting x" << x << "\n"; display(tout, eq2); tout << "\n";);
|
||||
apply_solution<true, false>(x, eq2.m_as, eq2.m_xs, eq2.m_c, eq2.m_bs, eq2.m_js, eq_idx, x);
|
||||
TRACE("euclidean_solver", tout << "eq after substituting x" << x << "\n"; display(tout, eq2); tout << "\n";);
|
||||
normalize_eq(eq_idx);
|
||||
if (inconsistent())
|
||||
break;
|
||||
}
|
||||
use_list.reset();
|
||||
}
|
||||
|
||||
void elim_unit() {
|
||||
SASSERT(m().is_one(m_next_a));
|
||||
equation & eq = *(m_equations[m_next_eq]);
|
||||
TRACE("euclidean_solver", tout << "eliminating equation with unit coefficient:\n"; display(tout, eq); tout << "\n";);
|
||||
if (m_next_pos_a) {
|
||||
// neg coeffs... to make sure that m_next_x is -1
|
||||
neg_coeffs(eq.m_as);
|
||||
neg_coeffs(eq.m_bs);
|
||||
}
|
||||
unsigned sz = eq.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m_occs[eq.x(i)].erase(m_next_eq);
|
||||
}
|
||||
m_solved[m_next_x] = m_solution.size();
|
||||
m_solution.push_back(&eq);
|
||||
m_equations[m_next_eq] = 0;
|
||||
substitute_most_recent_solution(m_next_x);
|
||||
}
|
||||
|
||||
void decompose(bool pos_a, mpz const & abs_a, mpz const & a_i, mpz & new_a_i, mpz & r_i) {
|
||||
mpz abs_a_i;
|
||||
bool pos_a_i = m().is_pos(a_i);
|
||||
m().set(abs_a_i, a_i);
|
||||
if (!pos_a_i)
|
||||
m().neg(abs_a_i);
|
||||
bool new_pos_a_i = pos_a_i;
|
||||
if (pos_a)
|
||||
new_pos_a_i = !new_pos_a_i;
|
||||
m().div(abs_a_i, abs_a, new_a_i);
|
||||
if (m().divides(abs_a, a_i)) {
|
||||
m().reset(r_i);
|
||||
}
|
||||
else {
|
||||
if (pos_a_i)
|
||||
m().submul(a_i, abs_a, new_a_i, r_i);
|
||||
else
|
||||
m().addmul(a_i, abs_a, new_a_i, r_i);
|
||||
}
|
||||
if (!new_pos_a_i)
|
||||
m().neg(new_a_i);
|
||||
m().del(abs_a_i);
|
||||
}
|
||||
|
||||
void decompose_and_elim() {
|
||||
m_tmp_xs.reset();
|
||||
mpz_buffer & buffer = m_decompose_buffer;
|
||||
buffer.reset();
|
||||
var p = mk_var(true);
|
||||
mpz new_a_i;
|
||||
equation & eq = *(m_equations[m_next_eq]);
|
||||
TRACE("euclidean_solver", tout << "decompositing equation for x" << m_next_x << "\n"; display(tout, eq); tout << "\n";);
|
||||
unsigned sz = eq.size();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq.x(i);
|
||||
if (x_i == m_next_x) {
|
||||
m().set(new_a_i, -1);
|
||||
buffer.push_back(new_a_i);
|
||||
m_tmp_xs.push_back(m_next_x);
|
||||
m_occs[x_i].erase(m_next_eq);
|
||||
}
|
||||
else {
|
||||
decompose(m_next_pos_a, m_next_a, eq.a(i), new_a_i, eq.m_as[j]);
|
||||
buffer.push_back(new_a_i);
|
||||
m_tmp_xs.push_back(x_i);
|
||||
if (m().is_zero(eq.m_as[j])) {
|
||||
m_occs[x_i].erase(m_next_eq);
|
||||
}
|
||||
else {
|
||||
eq.m_xs[j] = x_i;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
SASSERT(j < sz);
|
||||
// add parameter: p to new equality, and m_next_pos_a * m_next_a * p to current eq
|
||||
m().set(new_a_i, 1);
|
||||
buffer.push_back(new_a_i);
|
||||
m_tmp_xs.push_back(p);
|
||||
m().set(eq.m_as[j], m_next_a);
|
||||
if (!m_next_pos_a)
|
||||
m().neg(eq.m_as[j]);
|
||||
eq.m_xs[j] = p;
|
||||
j++;
|
||||
unsigned new_sz = j;
|
||||
// shrink current eq
|
||||
for (; j < sz; j++)
|
||||
m().del(eq.m_as[j]);
|
||||
eq.m_as.shrink(new_sz);
|
||||
eq.m_xs.shrink(new_sz);
|
||||
// ajust c
|
||||
mpz new_c;
|
||||
decompose(m_next_pos_a, m_next_a, eq.m_c, new_c, eq.m_c);
|
||||
// create auxiliary equation
|
||||
equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, 0, 0, false);
|
||||
// new_eq doesn't need to normalized, since it has unit coefficients
|
||||
TRACE("euclidean_solver", tout << "decomposition: new parameter x" << p << " aux eq:\n";
|
||||
display(tout, *new_eq); tout << "\n";
|
||||
display(tout, eq); tout << "\n";);
|
||||
m_solved[m_next_x] = m_solution.size();
|
||||
m_solution.push_back(new_eq);
|
||||
substitute_most_recent_solution(m_next_x);
|
||||
m().del(new_a_i);
|
||||
m().del(new_c);
|
||||
}
|
||||
|
||||
bool solve() {
|
||||
if (inconsistent()) return false;
|
||||
TRACE("euclidean_solver", tout << "solving...\n"; display(tout););
|
||||
while (select_next_var()) {
|
||||
CTRACE("euclidean_solver_bug", m_next_x == null_var, display(tout););
|
||||
TRACE("euclidean_solver", tout << "eliminating x" << m_next_x << "\n";);
|
||||
if (m().is_one(m_next_a) || m().is_minus_one(m_next_a))
|
||||
elim_unit();
|
||||
else
|
||||
decompose_and_elim();
|
||||
TRACE("euclidean_solver_step", display(tout););
|
||||
if (inconsistent()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool inconsistent() const {
|
||||
return m_inconsistent != null_eq_idx;
|
||||
}
|
||||
|
||||
bool is_parameter(var x) const {
|
||||
return m_parameter[x];
|
||||
}
|
||||
|
||||
void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js) {
|
||||
TRACE("euclidean_solver", tout << "before applying solution set\n";
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
tout << m().to_string(as[i]) << "*x" << xs[i] << " ";
|
||||
}
|
||||
tout << "\n";);
|
||||
m_norm_xs_vector.reset();
|
||||
m_norm_as_vector.reset();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
m_norm_xs_vector.push_back(xs[i]);
|
||||
m_norm_as_vector.push_back(mpz());
|
||||
m().set(m_norm_as_vector.back(), as[i]);
|
||||
}
|
||||
sort(m_norm_as_vector, m_norm_xs_vector, m_as_buffer);
|
||||
m_norm_bs_vector.reset();
|
||||
js.reset();
|
||||
m().set(c_prime, c);
|
||||
apply_solution(m_norm_as_vector, m_norm_xs_vector, c_prime, m_norm_bs_vector, js);
|
||||
TRACE("euclidean_solver", tout << "after applying solution set\n";
|
||||
for (unsigned i = 0; i < m_norm_as_vector.size(); i++) {
|
||||
tout << m().to_string(m_norm_as_vector[i]) << "*x" << m_norm_xs_vector[i] << " ";
|
||||
}
|
||||
tout << "\n";);
|
||||
// compute gcd of the result m_norm_as_vector
|
||||
if (m_norm_as_vector.empty()) {
|
||||
m().set(a_prime, 0);
|
||||
}
|
||||
else {
|
||||
mpz a;
|
||||
m().set(a_prime, m_norm_as_vector[0]);
|
||||
m().abs(a_prime);
|
||||
unsigned sz = m_norm_as_vector.size();
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
if (m().is_one(a_prime))
|
||||
break;
|
||||
m().set(a, m_norm_as_vector[i]);
|
||||
m().abs(a);
|
||||
m().gcd(a_prime, a, a_prime);
|
||||
}
|
||||
m().del(a);
|
||||
}
|
||||
// REMARK: m_norm_bs_vector contains the linear combination of the justifications. It may be useful if we
|
||||
// decided (one day) to generate detailed proofs for this step.
|
||||
del_nums(m_norm_as_vector);
|
||||
del_nums(m_norm_bs_vector);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
euclidean_solver::euclidean_solver(numeral_manager * m):
|
||||
m_imp(alloc(imp, m)) {
|
||||
}
|
||||
|
||||
euclidean_solver::~euclidean_solver() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
euclidean_solver::numeral_manager & euclidean_solver::m() const {
|
||||
return m_imp->m();
|
||||
}
|
||||
|
||||
void euclidean_solver::reset() {
|
||||
numeral_manager * m = m_imp->m_manager;
|
||||
bool owns_m = m_imp->m_owns_m;
|
||||
m_imp->m_owns_m = false;
|
||||
#pragma omp critical (euclidean_solver)
|
||||
{
|
||||
dealloc(m_imp);
|
||||
m_imp = alloc(imp, m);
|
||||
m_imp->m_owns_m = owns_m;
|
||||
}
|
||||
}
|
||||
|
||||
euclidean_solver::var euclidean_solver::mk_var() {
|
||||
return m_imp->mk_var(false);
|
||||
}
|
||||
|
||||
euclidean_solver::justification euclidean_solver::mk_justification() {
|
||||
return m_imp->mk_justification();
|
||||
}
|
||||
|
||||
void euclidean_solver::assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j) {
|
||||
m_imp->assert_eq(num, as, xs, c, j);
|
||||
}
|
||||
|
||||
bool euclidean_solver::solve() {
|
||||
return m_imp->solve();
|
||||
}
|
||||
|
||||
euclidean_solver::justification_vector const & euclidean_solver::get_justification() const {
|
||||
return m_imp->get_justification();
|
||||
}
|
||||
|
||||
bool euclidean_solver::inconsistent() const {
|
||||
return m_imp->inconsistent();
|
||||
}
|
||||
|
||||
bool euclidean_solver::is_parameter(var x) const {
|
||||
return m_imp->is_parameter(x);
|
||||
}
|
||||
|
||||
void euclidean_solver::normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime,
|
||||
justification_vector & js) {
|
||||
return m_imp->normalize(num, as, xs, c, a_prime, c_prime, js);
|
||||
}
|
||||
|
||||
void euclidean_solver::set_cancel(bool f) {
|
||||
#pragma omp critical (euclidean_solver)
|
||||
{
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
}
|
||||
|
||||
void euclidean_solver::display(std::ostream & out) const {
|
||||
m_imp->display(out);
|
||||
}
|
||||
|
||||
|
106
src/math/euclid/euclidean_solver.h
Normal file
106
src/math/euclid/euclidean_solver.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
euclidean_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Euclidean Solver with support for explanations.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-07-08.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _EUCLIDEAN_SOLVER_H_
|
||||
#define _EUCLIDEAN_SOLVER_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"vector.h"
|
||||
|
||||
class euclidean_solver {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
public:
|
||||
typedef unsigned var;
|
||||
typedef unsigned justification;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef svector<justification> justification_vector;
|
||||
static const justification null_justification = UINT_MAX;
|
||||
|
||||
/**
|
||||
\brief If m == 0, then the solver will create its own numeral manager.
|
||||
*/
|
||||
euclidean_solver(numeral_manager * m);
|
||||
|
||||
~euclidean_solver();
|
||||
|
||||
numeral_manager & m() const;
|
||||
|
||||
/**
|
||||
\brief Reset the state of the euclidean solver.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
\brief Creates a integer variable.
|
||||
*/
|
||||
var mk_var();
|
||||
|
||||
/**
|
||||
\brief Creates a fresh justification id.
|
||||
*/
|
||||
justification mk_justification();
|
||||
|
||||
/**
|
||||
\brief Asserts an equation of the form as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c = 0 with justification j.
|
||||
|
||||
The numerals must be created using the numeral_manager m().
|
||||
*/
|
||||
void assert_eq(unsigned num, mpz const * as, var const * xs, mpz const & c, justification j = null_justification);
|
||||
|
||||
/**
|
||||
\brief Solve the current set of equations. Return false if it is inconsistent.
|
||||
*/
|
||||
bool solve();
|
||||
|
||||
/**
|
||||
\brief Return a set of justifications (proof) for the inconsitency.
|
||||
|
||||
\pre inconsistent()
|
||||
*/
|
||||
justification_vector const & get_justification() const;
|
||||
|
||||
bool inconsistent() const;
|
||||
|
||||
/**
|
||||
\brief Return true if the variable is a "parameter" created by the Euclidean solver.
|
||||
*/
|
||||
bool is_parameter(var x) const;
|
||||
|
||||
/**
|
||||
Given a linear polynomial as[0]*xs[0] + ... + as[num-1]*xs[num-1] + c and the current solution set,
|
||||
It applies the solution set to produce a polynomial of the for a_prime * p + c_prime, where
|
||||
a_prime * p represents a linear polynomial where the coefficient of every monomial is a multiple of
|
||||
a_prime.
|
||||
|
||||
The justification is stored in js.
|
||||
Note that, this function does not return the actual p.
|
||||
|
||||
The numerals must be created using the numeral_manager m().
|
||||
*/
|
||||
void normalize(unsigned num, mpz const * as, var const * xs, mpz const & c, mpz & a_prime, mpz & c_prime, justification_vector & js);
|
||||
|
||||
/**
|
||||
\brief Set/Reset the cancel flag.
|
||||
*/
|
||||
void set_cancel(bool f);
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
};
|
||||
|
||||
#endif
|
961
src/math/grobner/grobner.cpp
Normal file
961
src/math/grobner/grobner.cpp
Normal file
|
@ -0,0 +1,961 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
grobner.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2008-12-04.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"grobner.h"
|
||||
#include"ast_pp.h"
|
||||
#include"ref_util.h"
|
||||
|
||||
// #define PROFILE_GB
|
||||
|
||||
grobner::grobner(ast_manager & m, v_dependency_manager & d):
|
||||
m_manager(m),
|
||||
m_dep_manager(d),
|
||||
m_util(m),
|
||||
m_var_lt(m_var2weight),
|
||||
m_monomial_lt(m_var_lt),
|
||||
m_changed_leading_term(false),
|
||||
m_unsat(0) {
|
||||
}
|
||||
|
||||
grobner::~grobner() {
|
||||
flush();
|
||||
}
|
||||
|
||||
void grobner::flush() {
|
||||
dec_ref_map_keys(m_manager, m_var2weight);
|
||||
del_equations(0);
|
||||
}
|
||||
|
||||
void grobner::del_equations(unsigned old_size) {
|
||||
SASSERT(m_equations_to_delete.size() >= old_size);
|
||||
equation_vector::iterator it = m_equations_to_delete.begin();
|
||||
equation_vector::iterator end = m_equations_to_delete.end();
|
||||
it += old_size;
|
||||
for (; it != end; ++it) {
|
||||
equation * eq = *it;
|
||||
if (eq)
|
||||
del_equation(eq);
|
||||
}
|
||||
m_equations_to_delete.shrink(old_size);
|
||||
}
|
||||
|
||||
void grobner::del_equation(equation * eq) {
|
||||
m_processed.erase(eq);
|
||||
m_to_process.erase(eq);
|
||||
SASSERT(m_equations_to_delete[eq->m_bidx] == eq);
|
||||
m_equations_to_delete[eq->m_bidx] = 0;
|
||||
ptr_vector<monomial>::iterator it1 = eq->m_monomials.begin();
|
||||
ptr_vector<monomial>::iterator end1 = eq->m_monomials.end();
|
||||
for (; it1 != end1; ++it1) {
|
||||
monomial * m = *it1;
|
||||
del_monomial(m);
|
||||
}
|
||||
dealloc(eq);
|
||||
}
|
||||
|
||||
void grobner::del_monomial(monomial * m) {
|
||||
ptr_vector<expr>::iterator it2 = m->m_vars.begin();
|
||||
ptr_vector<expr>::iterator end2 = m->m_vars.end();
|
||||
for (; it2 != end2; ++it2) {
|
||||
expr * v = *it2;
|
||||
m_manager.dec_ref(v);
|
||||
}
|
||||
dealloc(m);
|
||||
}
|
||||
|
||||
void grobner::unfreeze_equations(unsigned old_size) {
|
||||
SASSERT(m_equations_to_unfreeze.size() >= old_size);
|
||||
equation_vector::iterator it = m_equations_to_unfreeze.begin();
|
||||
equation_vector::iterator end = m_equations_to_unfreeze.end();
|
||||
it += old_size;
|
||||
for (; it != end; ++it) {
|
||||
equation * eq = *it;
|
||||
m_to_process.insert(eq);
|
||||
}
|
||||
m_equations_to_unfreeze.shrink(old_size);
|
||||
}
|
||||
|
||||
void grobner::push_scope() {
|
||||
m_scopes.push_back(scope());
|
||||
scope & s = m_scopes.back();
|
||||
s.m_equations_to_unfreeze_lim = m_equations_to_unfreeze.size();
|
||||
s.m_equations_to_delete_lim = m_equations_to_delete.size();
|
||||
}
|
||||
|
||||
void grobner::pop_scope(unsigned num_scopes) {
|
||||
SASSERT(num_scopes >= get_scope_level());
|
||||
unsigned new_lvl = get_scope_level() - num_scopes;
|
||||
scope & s = m_scopes[new_lvl];
|
||||
unfreeze_equations(s.m_equations_to_unfreeze_lim);
|
||||
del_equations(s.m_equations_to_delete_lim);
|
||||
m_scopes.shrink(new_lvl);
|
||||
}
|
||||
|
||||
void grobner::reset() {
|
||||
flush();
|
||||
m_processed.reset();
|
||||
m_to_process.reset();
|
||||
m_equations_to_unfreeze.reset();
|
||||
m_equations_to_delete.reset();
|
||||
m_unsat = 0;
|
||||
}
|
||||
|
||||
void grobner::display_var(std::ostream & out, expr * var) const {
|
||||
if (is_app(var) && to_app(var)->get_num_args() > 0)
|
||||
out << "#" << var->get_id();
|
||||
else
|
||||
out << mk_pp(var, m_manager);
|
||||
}
|
||||
|
||||
void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const {
|
||||
for (unsigned i = 0; i < num_vars; i++) {
|
||||
display_var(out, vars[i]);
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
|
||||
void grobner::display_monomial(std::ostream & out, monomial const & m) const {
|
||||
if (!m.m_coeff.is_one() || m.m_vars.empty()) {
|
||||
out << m.m_coeff;
|
||||
if (!m.m_vars.empty())
|
||||
out << "*";
|
||||
}
|
||||
|
||||
if (!m.m_vars.empty()) {
|
||||
ptr_vector<expr>::const_iterator it = m.m_vars.begin();
|
||||
ptr_vector<expr>::const_iterator end = m.m_vars.end();
|
||||
unsigned power = 1;
|
||||
expr * prev = *it;
|
||||
it++;
|
||||
for (; it != end; ++it) {
|
||||
expr * curr = *it;
|
||||
if (curr == prev) {
|
||||
power++;
|
||||
}
|
||||
else {
|
||||
display_var(out, prev);
|
||||
if (power > 1)
|
||||
out << "^" << power;
|
||||
power = 1;
|
||||
prev = curr;
|
||||
out << "*";
|
||||
}
|
||||
}
|
||||
display_var(out, prev);
|
||||
if (power > 1)
|
||||
out << "^" << power;
|
||||
}
|
||||
}
|
||||
|
||||
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
|
||||
bool first = true;
|
||||
for (unsigned i = 0; i < num_monomials; i++) {
|
||||
monomial const * m = monomials[i];
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out << " + ";
|
||||
display_monomial(out, *m);
|
||||
}
|
||||
}
|
||||
|
||||
void grobner::display_equation(std::ostream & out, equation const & eq) const {
|
||||
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.c_ptr());
|
||||
out << " = 0\n";
|
||||
}
|
||||
|
||||
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const {
|
||||
if (!v.empty()) {
|
||||
out << header << "\n";
|
||||
equation_set::iterator it = v.begin();
|
||||
equation_set::iterator end = v.end();
|
||||
for (; it != end; ++it)
|
||||
display_equation(out, *(*it));
|
||||
}
|
||||
}
|
||||
|
||||
void grobner::display(std::ostream & out) const {
|
||||
display_equations(out, m_processed, "processed:");
|
||||
display_equations(out, m_to_process, "to process:");
|
||||
}
|
||||
|
||||
void grobner::set_weight(expr * n, int weight) {
|
||||
if (weight == 0)
|
||||
return;
|
||||
if (!m_var2weight.contains(n))
|
||||
m_manager.inc_ref(n);
|
||||
m_var2weight.insert(n, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Update equation using the new variable order.
|
||||
Return true if the leading term was modified.
|
||||
*/
|
||||
bool grobner::update_order(equation * eq) {
|
||||
if (eq->get_num_monomials() == 0)
|
||||
return false;
|
||||
monomial * first = eq->m_monomials[0];
|
||||
ptr_vector<monomial>::iterator it = eq->m_monomials.begin();
|
||||
ptr_vector<monomial>::iterator end = eq->m_monomials.end();
|
||||
for (; it != end; ++it) {
|
||||
monomial * m = *it;
|
||||
std::stable_sort(m->m_vars.begin(), m->m_vars.end(), m_var_lt);
|
||||
}
|
||||
std::stable_sort(eq->m_monomials.begin(), eq->m_monomials.end(), m_monomial_lt);
|
||||
return eq->m_monomials[0] != first;
|
||||
}
|
||||
|
||||
void grobner::update_order(equation_set & s, bool processed) {
|
||||
ptr_buffer<equation> to_remove;
|
||||
equation_set::iterator it = s.begin();
|
||||
equation_set::iterator end = s.end();
|
||||
for (;it != end; ++it) {
|
||||
equation * eq = *it;
|
||||
if (update_order(eq)) {
|
||||
if (processed) {
|
||||
to_remove.push_back(eq);
|
||||
m_to_process.insert(eq);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr_buffer<equation>::iterator it2 = to_remove.begin();
|
||||
ptr_buffer<equation>::iterator end2 = to_remove.end();
|
||||
for (; it2 != end2; ++it2)
|
||||
s.erase(*it2);
|
||||
}
|
||||
|
||||
void grobner::update_order() {
|
||||
update_order(m_to_process, false);
|
||||
update_order(m_processed, true);
|
||||
}
|
||||
|
||||
bool grobner::var_lt::operator()(expr * v1, expr * v2) const {
|
||||
int w1 = 0;
|
||||
int w2 = 0;
|
||||
m_var2weight.find(v1, w1);
|
||||
m_var2weight.find(v2, w2);
|
||||
return (w1 > w2) || (w1 == w2 && v1->get_id() < v2->get_id());
|
||||
}
|
||||
|
||||
bool grobner::monomial_lt::operator()(monomial * m1, monomial * m2) const {
|
||||
// Using graded lex order.
|
||||
if (m1->get_degree() > m2->get_degree())
|
||||
return true;
|
||||
if (m1->get_degree() < m2->get_degree())
|
||||
return false;
|
||||
ptr_vector<expr>::iterator it1 = m1->m_vars.begin();
|
||||
ptr_vector<expr>::iterator it2 = m2->m_vars.begin();
|
||||
ptr_vector<expr>::iterator end1 = m1->m_vars.end();
|
||||
for (; it1 != end1; ++it1, ++it2) {
|
||||
expr * v1 = *it1;
|
||||
expr * v2 = *it2;
|
||||
if (m_var_lt(v1, v2))
|
||||
return true;
|
||||
if (v1 != v2)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void grobner::add_var(monomial * m, expr * v) {
|
||||
SASSERT(!m_util.is_numeral(v));
|
||||
m_manager.inc_ref(v);
|
||||
m->m_vars.push_back(v);
|
||||
}
|
||||
|
||||
grobner::monomial * grobner::mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars) {
|
||||
monomial * r = alloc(monomial);
|
||||
r->m_coeff = coeff;
|
||||
for (unsigned i = 0; i < num_vars; i++)
|
||||
add_var(r, vars[i]);
|
||||
std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt);
|
||||
return r;
|
||||
}
|
||||
|
||||
grobner::monomial * grobner::mk_monomial(rational const & coeff, expr * m) {
|
||||
monomial * r = alloc(monomial);
|
||||
if (m_util.is_numeral(m, r->m_coeff)) {
|
||||
r->m_coeff *= coeff;
|
||||
return r;
|
||||
}
|
||||
if (m_util.is_mul(m)) {
|
||||
expr * body = m;
|
||||
SASSERT(!m_util.is_numeral(to_app(m)->get_arg(1))); // monomial is in normal form
|
||||
if (m_util.is_numeral(to_app(m)->get_arg(0), r->m_coeff)) {
|
||||
r->m_coeff *= coeff;
|
||||
body = to_app(m)->get_arg(1);
|
||||
}
|
||||
else {
|
||||
r->m_coeff = coeff;
|
||||
}
|
||||
while (m_util.is_mul(body)) {
|
||||
add_var(r, to_app(body)->get_arg(0));
|
||||
body = to_app(body)->get_arg(1);
|
||||
}
|
||||
add_var(r, body);
|
||||
std::stable_sort(r->m_vars.begin(), r->m_vars.end(), m_var_lt);
|
||||
}
|
||||
else {
|
||||
r->m_coeff = coeff;
|
||||
r->m_vars.push_back(m);
|
||||
m_manager.inc_ref(m);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void grobner::init_equation(equation * eq, v_dependency * d) {
|
||||
eq->m_scope_lvl = get_scope_level();
|
||||
unsigned bidx = m_equations_to_delete.size();
|
||||
eq->m_bidx = bidx;
|
||||
eq->m_dep = d;
|
||||
eq->m_lc = true;
|
||||
m_equations_to_delete.push_back(eq);
|
||||
SASSERT(m_equations_to_delete[eq->m_bidx] == eq);
|
||||
}
|
||||
|
||||
void grobner::assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex) {
|
||||
ptr_vector<monomial> ms;
|
||||
ms.append(num_monomials, monomials);
|
||||
std::stable_sort(ms.begin(), ms.end(), m_monomial_lt);
|
||||
merge_monomials(ms);
|
||||
if (!ms.empty()) {
|
||||
normalize_coeff(ms);
|
||||
equation * eq = alloc(equation);
|
||||
eq->m_monomials.swap(ms);
|
||||
init_equation(eq, ex);
|
||||
m_to_process.insert(eq);
|
||||
}
|
||||
}
|
||||
|
||||
void grobner::assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex) {
|
||||
#define MK_EQ(COEFF) \
|
||||
ptr_vector<monomial> ms; \
|
||||
for (unsigned i = 0; i < num_monomials; i++) \
|
||||
ms.push_back(mk_monomial(COEFF, monomials[i])); \
|
||||
std::stable_sort(ms.begin(), ms.end(), m_monomial_lt); \
|
||||
merge_monomials(ms); \
|
||||
if (!ms.empty()) { \
|
||||
equation * eq = alloc(equation); \
|
||||
normalize_coeff(ms); \
|
||||
eq->m_monomials.swap(ms); \
|
||||
init_equation(eq, ex); \
|
||||
m_to_process.insert(eq); \
|
||||
}
|
||||
|
||||
MK_EQ(coeffs[i]);
|
||||
}
|
||||
|
||||
void grobner::assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex) {
|
||||
rational one(1);
|
||||
MK_EQ(one);
|
||||
}
|
||||
|
||||
void grobner::extract_monomials(expr * lhs, ptr_buffer<expr> & monomials) {
|
||||
while (m_util.is_add(lhs)) {
|
||||
SASSERT(!m_util.is_add(to_app(lhs)->get_arg(0)));
|
||||
monomials.push_back(to_app(lhs)->get_arg(0));
|
||||
lhs = to_app(lhs)->get_arg(1);
|
||||
}
|
||||
monomials.push_back(lhs);
|
||||
}
|
||||
|
||||
void grobner::assert_eq(expr * eq, v_dependency * ex) {
|
||||
SASSERT(m_manager.is_eq(eq));
|
||||
expr * lhs = to_app(eq)->get_arg(0);
|
||||
expr * rhs = to_app(eq)->get_arg(1);
|
||||
SASSERT(m_util.is_numeral(rhs));
|
||||
ptr_buffer<expr> monomials;
|
||||
extract_monomials(lhs, monomials);
|
||||
rational c;
|
||||
bool is_int = false;
|
||||
m_util.is_numeral(rhs, c, is_int);
|
||||
expr_ref new_c(m_manager);
|
||||
if (!c.is_zero()) {
|
||||
c.neg();
|
||||
new_c = m_util.mk_numeral(c, is_int);
|
||||
monomials.push_back(new_c);
|
||||
}
|
||||
assert_eq_0(monomials.size(), monomials.c_ptr(), ex);
|
||||
}
|
||||
|
||||
void grobner::assert_monomial_tautology(expr * m) {
|
||||
equation * eq = alloc(equation);
|
||||
eq->m_monomials.push_back(mk_monomial(rational(1), m));
|
||||
// create (quote m)
|
||||
monomial * m1 = alloc(monomial);
|
||||
m1->m_coeff = rational(-1);
|
||||
m_manager.inc_ref(m);
|
||||
m1->m_vars.push_back(m);
|
||||
eq->m_monomials.push_back(m1);
|
||||
normalize_coeff(eq->m_monomials);
|
||||
init_equation(eq, static_cast<v_dependency*>(0)); \
|
||||
m_to_process.insert(eq);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the body of m1 and m2 are equal
|
||||
*/
|
||||
bool grobner::is_eq_monomial_body(monomial const * m1, monomial const * m2) {
|
||||
if (m1->get_degree() != m2->get_degree())
|
||||
return false;
|
||||
ptr_vector<expr>::const_iterator it1 = m1->m_vars.begin();
|
||||
ptr_vector<expr>::const_iterator it2 = m2->m_vars.begin();
|
||||
ptr_vector<expr>::const_iterator end1 = m1->m_vars.end();
|
||||
for (; it1 != end1; ++it1, ++it2) {
|
||||
expr * v1 = *it1;
|
||||
expr * v2 = *it2;
|
||||
if (v1 != v2)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Merge monomials (* c1 m) (* c2 m).
|
||||
|
||||
\remark This method assumes the monomials are sorted.
|
||||
*/
|
||||
void grobner::merge_monomials(ptr_vector<monomial> & monomials) {
|
||||
TRACE("grobner", tout << "before merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";);
|
||||
unsigned j = 0;
|
||||
unsigned sz = monomials.size();
|
||||
if (sz == 0)
|
||||
return;
|
||||
for (unsigned i = 1; i < sz; ++i) {
|
||||
monomial * m1 = monomials[j];
|
||||
monomial * m2 = monomials[i];
|
||||
if (is_eq_monomial_body(m1, m2)) {
|
||||
m1->m_coeff += m2->m_coeff;
|
||||
del_monomial(m2);
|
||||
}
|
||||
else {
|
||||
if (m1->m_coeff.is_zero())
|
||||
del_monomial(m1); // cancelled
|
||||
else
|
||||
j++;
|
||||
monomials[j] = m2;
|
||||
}
|
||||
}
|
||||
SASSERT(j < sz);
|
||||
monomial * m1 = monomials[j];
|
||||
if (m1->m_coeff.is_zero())
|
||||
del_monomial(m1); // cancelled
|
||||
else
|
||||
j++;
|
||||
monomials.shrink(j);
|
||||
TRACE("grobner", tout << "after merging monomials:\n"; display_monomials(tout, monomials.size(), monomials.c_ptr()); tout << "\n";);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Divide the coefficients by the coefficient of the leading term.
|
||||
*/
|
||||
void grobner::normalize_coeff(ptr_vector<monomial> & monomials) {
|
||||
if (monomials.empty())
|
||||
return;
|
||||
rational c = monomials[0]->m_coeff;
|
||||
if (c.is_one())
|
||||
return;
|
||||
unsigned sz = monomials.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
monomials[i]->m_coeff /= c;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Simplify the given monomials
|
||||
*/
|
||||
void grobner::simplify(ptr_vector<monomial> & monomials) {
|
||||
std::stable_sort(monomials.begin(), monomials.end(), m_monomial_lt);
|
||||
merge_monomials(monomials);
|
||||
normalize_coeff(monomials);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the equation is of the form k = 0, where k is a numeral different from zero.
|
||||
|
||||
\remark This method assumes the equation is simplified.
|
||||
*/
|
||||
inline bool grobner::is_inconsistent(equation * eq) const {
|
||||
SASSERT(!(eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0) || !eq->m_monomials[0]->m_coeff.is_zero());
|
||||
return eq->m_monomials.size() == 1 && eq->m_monomials[0]->get_degree() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the equation is of the form 0 = 0.
|
||||
*/
|
||||
inline bool grobner::is_trivial(equation * eq) const {
|
||||
return eq->m_monomials.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Sort monomials, and merge monomials with the same body.
|
||||
*/
|
||||
void grobner::simplify(equation * eq) {
|
||||
simplify(eq->m_monomials);
|
||||
if (is_inconsistent(eq) && !m_unsat)
|
||||
m_unsat = eq;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if monomial m1 is (* c1 M) and m2 is (* c2 M M').
|
||||
Store M' in rest.
|
||||
|
||||
\remark This method assumes the variables of m1 and m2 are sorted.
|
||||
*/
|
||||
bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest) const {
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
unsigned sz1 = m1->m_vars.size();
|
||||
unsigned sz2 = m2->m_vars.size();
|
||||
if (sz1 <= sz2) {
|
||||
while (true) {
|
||||
if (i1 >= sz1) {
|
||||
for (; i2 < sz2; i2++)
|
||||
rest.push_back(m2->m_vars[i2]);
|
||||
TRACE("grobner",
|
||||
tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of ";
|
||||
display_monomial(tout, *m2); tout << "\n";
|
||||
tout << "rest: "; display_vars(tout, rest.size(), rest.c_ptr()); tout << "\n";);
|
||||
return true;
|
||||
}
|
||||
if (i2 >= sz2)
|
||||
break;
|
||||
expr * var1 = m1->m_vars[i1];
|
||||
expr * var2 = m2->m_vars[i2];
|
||||
if (var1 == var2) {
|
||||
i1++;
|
||||
i2++;
|
||||
continue;
|
||||
}
|
||||
if (m_var_lt(var2, var1)) {
|
||||
i2++;
|
||||
rest.push_back(var2);
|
||||
continue;
|
||||
}
|
||||
SASSERT(m_var_lt(var1, var2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// is not subset
|
||||
TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of ";
|
||||
display_monomial(tout, *m2); tout << "\n";);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Multiply the monomials of source starting at position start_idx by (coeff * vars), and store the resultant monomials
|
||||
at result.
|
||||
*/
|
||||
void grobner::mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector<expr> const & vars, ptr_vector<monomial> & result) {
|
||||
unsigned sz = source->get_num_monomials();
|
||||
for (unsigned i = start_idx; i < sz; i++) {
|
||||
monomial const * m = source->get_monomial(i);
|
||||
monomial * new_m = alloc(monomial);
|
||||
new_m->m_coeff = m->m_coeff;
|
||||
new_m->m_coeff *= coeff;
|
||||
new_m->m_vars.append(m->m_vars.size(), m->m_vars.c_ptr());
|
||||
new_m->m_vars.append(vars.size(), vars.c_ptr());
|
||||
ptr_vector<expr>::iterator it = new_m->m_vars.begin();
|
||||
ptr_vector<expr>::iterator end = new_m->m_vars.end();
|
||||
for (; it != end; ++it)
|
||||
m_manager.inc_ref(*it);
|
||||
std::stable_sort(new_m->m_vars.begin(), new_m->m_vars.end(), m_var_lt);
|
||||
result.push_back(new_m);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Copy the given monomial.
|
||||
*/
|
||||
grobner::monomial * grobner::copy_monomial(monomial const * m) {
|
||||
monomial * r = alloc(monomial);
|
||||
r->m_coeff = m->m_coeff;
|
||||
ptr_vector<expr>::const_iterator it = m->m_vars.begin();
|
||||
ptr_vector<expr>::const_iterator end = m->m_vars.end();
|
||||
for (; it != end; ++it)
|
||||
add_var(r, *it);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Copy the given equation.
|
||||
*/
|
||||
grobner::equation * grobner::copy_equation(equation const * eq) {
|
||||
equation * r = alloc(equation);
|
||||
unsigned sz = eq->get_num_monomials();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
r->m_monomials.push_back(copy_monomial(eq->get_monomial(i)));
|
||||
init_equation(r, eq->m_dep);
|
||||
r->m_lc = eq->m_lc;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Simplify the target equation using the source as a rewrite rule.
|
||||
Return 0 if target was not simplified.
|
||||
Return target if target was simplified but source->m_scope_lvl <= target->m_scope_lvl.
|
||||
Return new_equation if source->m_scope_lvl > target->m_scope_lvl, moreover target is freezed, and new_equation contains the result.
|
||||
*/
|
||||
grobner::equation * grobner::simplify(equation const * source, equation * target) {
|
||||
TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source););
|
||||
if (source->get_num_monomials() == 0)
|
||||
return 0;
|
||||
m_stats.m_simplify++;
|
||||
bool result = false;
|
||||
bool simplified;
|
||||
do {
|
||||
simplified = false;
|
||||
unsigned i = 0;
|
||||
unsigned j = 0;
|
||||
unsigned sz = target->m_monomials.size();
|
||||
monomial const * LT = source->get_monomial(0);
|
||||
ptr_vector<monomial> & new_monomials = m_tmp_monomials;
|
||||
new_monomials.reset();
|
||||
ptr_vector<expr> & rest = m_tmp_vars1;
|
||||
for (; i < sz; i++) {
|
||||
monomial * curr = target->m_monomials[i];
|
||||
rest.reset();
|
||||
if (is_subset(LT, curr, rest)) {
|
||||
if (i == 0)
|
||||
m_changed_leading_term = true;
|
||||
if (source->m_scope_lvl > target->m_scope_lvl) {
|
||||
target = copy_equation(target);
|
||||
SASSERT(target->m_scope_lvl >= source->m_scope_lvl);
|
||||
}
|
||||
if (!result) {
|
||||
// first time that source is being applied.
|
||||
target->m_dep = m_dep_manager.mk_join(target->m_dep, source->m_dep);
|
||||
}
|
||||
simplified = true;
|
||||
result = true;
|
||||
rational coeff = curr->m_coeff;
|
||||
coeff /= LT->m_coeff;
|
||||
coeff.neg();
|
||||
if (!rest.empty())
|
||||
target->m_lc = false;
|
||||
mul_append(1, source, coeff, rest, new_monomials);
|
||||
del_monomial(curr);
|
||||
}
|
||||
else {
|
||||
target->m_monomials[j] = curr;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (simplified) {
|
||||
target->m_monomials.shrink(j);
|
||||
target->m_monomials.append(new_monomials.size(), new_monomials.c_ptr());
|
||||
simplify(target);
|
||||
}
|
||||
}
|
||||
while (simplified);
|
||||
TRACE("grobner", tout << "result: "; display_equation(tout, *target););
|
||||
return result ? target : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Simplify given equation using processed equalities.
|
||||
Return 0, if the equation was not simplified.
|
||||
Return eq, if the equation was simplified using destructive updates.
|
||||
Return new_eq otherwise.
|
||||
*/
|
||||
grobner::equation * grobner::simplify_using_processed(equation * eq) {
|
||||
bool result = false;
|
||||
bool simplified;
|
||||
TRACE("grobner", tout << "simplifying: "; display_equation(tout, *eq); tout << "using already processed equalities\n";);
|
||||
do {
|
||||
simplified = false;
|
||||
equation_set::iterator it = m_processed.begin();
|
||||
equation_set::iterator end = m_processed.end();
|
||||
for (; it != end; ++it) {
|
||||
equation const * p = *it;
|
||||
equation * new_eq = simplify(p, eq);
|
||||
if (new_eq) {
|
||||
result = true;
|
||||
simplified = true;
|
||||
eq = new_eq;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (simplified);
|
||||
TRACE("grobner", tout << "simplification result: "; display_equation(tout, *eq););
|
||||
return result ? eq : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if eq1 is a better than e2, for being the next equation to be processed.
|
||||
*/
|
||||
bool grobner::is_better_choice(equation * eq1, equation * eq2) {
|
||||
if (!eq2)
|
||||
return true;
|
||||
if (eq1->m_monomials.empty())
|
||||
return true;
|
||||
if (eq2->m_monomials.empty())
|
||||
return false;
|
||||
if (eq1->m_monomials[0]->get_degree() < eq2->m_monomials[0]->get_degree())
|
||||
return true;
|
||||
if (eq1->m_monomials[0]->get_degree() > eq2->m_monomials[0]->get_degree())
|
||||
return false;
|
||||
return eq1->m_monomials.size() < eq2->m_monomials.size();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Pick next unprocessed equation
|
||||
*/
|
||||
grobner::equation * grobner::pick_next() {
|
||||
equation * r = 0;
|
||||
ptr_buffer<equation> to_delete;
|
||||
equation_set::iterator it = m_to_process.begin();
|
||||
equation_set::iterator end = m_to_process.end();
|
||||
for (; it != end; ++it) {
|
||||
equation * curr = *it;
|
||||
if (is_trivial(curr))
|
||||
to_delete.push_back(curr);
|
||||
else if (is_better_choice(curr, r))
|
||||
r = curr;
|
||||
}
|
||||
ptr_buffer<equation>::const_iterator it1 = to_delete.begin();
|
||||
ptr_buffer<equation>::const_iterator end1 = to_delete.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
del_equation(*it1);
|
||||
if (r)
|
||||
m_to_process.erase(r);
|
||||
TRACE("grobner", tout << "selected equation: "; if (!r) tout << "<null>\n"; else display_equation(tout, *r););
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Use the given equation to simplify processed terms.
|
||||
*/
|
||||
void grobner::simplify_processed(equation * eq) {
|
||||
ptr_buffer<equation> to_insert;
|
||||
ptr_buffer<equation> to_remove;
|
||||
ptr_buffer<equation> to_delete;
|
||||
equation_set::iterator it = m_processed.begin();
|
||||
equation_set::iterator end = m_processed.end();
|
||||
for (; it != end; ++it) {
|
||||
equation * curr = *it;
|
||||
m_changed_leading_term = false;
|
||||
// if the leading term is simplified, then the equation has to be moved to m_to_process
|
||||
equation * new_curr = simplify(eq, curr);
|
||||
if (new_curr != 0) {
|
||||
if (new_curr != curr) {
|
||||
m_equations_to_unfreeze.push_back(curr);
|
||||
to_remove.push_back(curr);
|
||||
if (m_changed_leading_term) {
|
||||
m_to_process.insert(new_curr);
|
||||
to_remove.push_back(curr);
|
||||
}
|
||||
else {
|
||||
to_insert.push_back(new_curr);
|
||||
}
|
||||
curr = new_curr;
|
||||
}
|
||||
else {
|
||||
if (m_changed_leading_term) {
|
||||
m_to_process.insert(curr);
|
||||
to_remove.push_back(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_trivial(curr))
|
||||
to_delete.push_back(curr);
|
||||
}
|
||||
ptr_buffer<equation>::const_iterator it1 = to_insert.begin();
|
||||
ptr_buffer<equation>::const_iterator end1 = to_insert.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
m_processed.insert(*it1);
|
||||
it1 = to_remove.begin();
|
||||
end1 = to_remove.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
m_processed.erase(*it1);
|
||||
it1 = to_delete.begin();
|
||||
end1 = to_delete.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
del_equation(*it1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Use the given equation to simplify to-process terms.
|
||||
*/
|
||||
void grobner::simplify_to_process(equation * eq) {
|
||||
equation_set::iterator it = m_to_process.begin();
|
||||
equation_set::iterator end = m_to_process.end();
|
||||
ptr_buffer<equation> to_insert;
|
||||
ptr_buffer<equation> to_remove;
|
||||
ptr_buffer<equation> to_delete;
|
||||
for (; it != end; ++it) {
|
||||
equation * curr = *it;
|
||||
equation * new_curr = simplify(eq, curr);
|
||||
if (new_curr != 0 && new_curr != curr) {
|
||||
m_equations_to_unfreeze.push_back(curr);
|
||||
to_insert.push_back(new_curr);
|
||||
to_remove.push_back(curr);
|
||||
curr = new_curr;
|
||||
}
|
||||
if (is_trivial(curr))
|
||||
to_delete.push_back(curr);
|
||||
}
|
||||
ptr_buffer<equation>::const_iterator it1 = to_insert.begin();
|
||||
ptr_buffer<equation>::const_iterator end1 = to_insert.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
m_to_process.insert(*it1);
|
||||
it1 = to_remove.begin();
|
||||
end1 = to_remove.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
m_to_process.erase(*it1);
|
||||
it1 = to_delete.begin();
|
||||
end1 = to_delete.end();
|
||||
for (; it1 != end1; ++it1)
|
||||
del_equation(*it1);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief If m1 = (* c M M1) and m2 = (* d M M2) and M is non empty, then return true and store M1 in rest1 and M2 in rest2.
|
||||
*/
|
||||
bool grobner::unify(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest1, ptr_vector<expr> & rest2) {
|
||||
TRACE("grobner", tout << "unifying: "; display_monomial(tout, *m1); tout << " "; display_monomial(tout, *m2); tout << "\n";);
|
||||
bool found_M = false;
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
unsigned sz1 = m1->m_vars.size();
|
||||
unsigned sz2 = m2->m_vars.size();
|
||||
while (true) {
|
||||
if (i1 >= sz1) {
|
||||
if (found_M) {
|
||||
for (; i2 < sz2; i2++)
|
||||
rest2.push_back(m2->m_vars[i2]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (i2 >= sz2) {
|
||||
if (found_M) {
|
||||
for (; i1 < sz1; i1++)
|
||||
rest1.push_back(m1->m_vars[i1]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
expr * var1 = m1->m_vars[i1];
|
||||
expr * var2 = m2->m_vars[i2];
|
||||
if (var1 == var2) {
|
||||
found_M = true;
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
else if (m_var_lt(var2, var1)) {
|
||||
i2++;
|
||||
rest2.push_back(var2);
|
||||
}
|
||||
else {
|
||||
i1++;
|
||||
rest1.push_back(var1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Superpose the given two equations.
|
||||
*/
|
||||
void grobner::superpose(equation * eq1, equation * eq2) {
|
||||
if (eq1->m_monomials.empty() || eq2->m_monomials.empty())
|
||||
return;
|
||||
m_stats.m_superpose++;
|
||||
ptr_vector<expr> & rest1 = m_tmp_vars1;
|
||||
rest1.reset();
|
||||
ptr_vector<expr> & rest2 = m_tmp_vars2;
|
||||
rest2.reset();
|
||||
if (unify(eq1->m_monomials[0], eq2->m_monomials[0], rest1, rest2)) {
|
||||
TRACE("grobner", tout << "superposing:\n"; display_equation(tout, *eq1); display_equation(tout, *eq2);
|
||||
tout << "rest1: "; display_vars(tout, rest1.size(), rest1.c_ptr()); tout << "\n";
|
||||
tout << "rest2: "; display_vars(tout, rest2.size(), rest2.c_ptr()); tout << "\n";);
|
||||
ptr_vector<monomial> & new_monomials = m_tmp_monomials;
|
||||
new_monomials.reset();
|
||||
mul_append(1, eq1, eq2->m_monomials[0]->m_coeff, rest2, new_monomials);
|
||||
rational c = eq1->m_monomials[0]->m_coeff;
|
||||
c.neg();
|
||||
mul_append(1, eq2, c, rest1, new_monomials);
|
||||
simplify(new_monomials);
|
||||
TRACE("grobner", tout << "resulting monomials: "; display_monomials(tout, new_monomials.size(), new_monomials.c_ptr()); tout << "\n";);
|
||||
if (new_monomials.empty())
|
||||
return;
|
||||
m_num_new_equations++;
|
||||
equation * new_eq = alloc(equation);
|
||||
new_eq->m_monomials.swap(new_monomials);
|
||||
init_equation(new_eq, m_dep_manager.mk_join(eq1->m_dep, eq2->m_dep));
|
||||
new_eq->m_lc = false;
|
||||
m_to_process.insert(new_eq);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Superpose the given equations with the equations in m_processed.
|
||||
*/
|
||||
void grobner::superpose(equation * eq) {
|
||||
equation_set::iterator it = m_processed.begin();
|
||||
equation_set::iterator end = m_processed.end();
|
||||
for (; it != end; ++it) {
|
||||
equation * curr = *it;
|
||||
superpose(eq, curr);
|
||||
}
|
||||
}
|
||||
|
||||
bool grobner::compute_basis(unsigned threshold) {
|
||||
m_stats.m_compute_basis++;
|
||||
m_num_new_equations = 0;
|
||||
while (m_num_new_equations < threshold) {
|
||||
equation * eq = pick_next();
|
||||
if (!eq)
|
||||
return true;
|
||||
m_stats.m_num_processed++;
|
||||
#ifdef PROFILE_GB
|
||||
if (m_stats.m_num_processed % 100 == 0) {
|
||||
verbose_stream() << "[grobner] " << m_processed.size() << " " << m_to_process.size() << "\n";
|
||||
}
|
||||
#endif
|
||||
equation * new_eq = simplify_using_processed(eq);
|
||||
if (new_eq != 0 && eq != new_eq) {
|
||||
// equation was updated using non destructive updates
|
||||
m_equations_to_unfreeze.push_back(eq);
|
||||
eq = new_eq;
|
||||
}
|
||||
simplify_processed(eq);
|
||||
superpose(eq);
|
||||
m_processed.insert(eq);
|
||||
simplify_to_process(eq);
|
||||
TRACE("grobner", tout << "end of iteration:\n"; display(tout););
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void grobner::copy_to(equation_set const & s, ptr_vector<equation> & result) const {
|
||||
equation_set::iterator it = s.begin();
|
||||
equation_set::iterator end = s.end();
|
||||
for (; it != end; ++it)
|
||||
result.push_back(*it);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Copy the equations in m_processed and m_to_process to result.
|
||||
|
||||
\warning This equations can be deleted when compute_basis is invoked.
|
||||
*/
|
||||
void grobner::get_equations(ptr_vector<equation> & result) const {
|
||||
copy_to(m_processed, result);
|
||||
copy_to(m_to_process, result);
|
||||
}
|
||||
|
281
src/math/grobner/grobner.h
Normal file
281
src/math/grobner/grobner.h
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
grobner.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2008-12-04.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _GROBNER_H_
|
||||
#define _GROBNER_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"heap.h"
|
||||
#include"obj_hashtable.h"
|
||||
#include"region.h"
|
||||
#include"dependency.h"
|
||||
|
||||
|
||||
struct grobner_stats {
|
||||
long m_simplify; long m_superpose; long m_compute_basis; long m_num_processed;
|
||||
void reset() { memset(this, 0, sizeof(grobner_stats)); }
|
||||
grobner_stats() { reset(); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
\brief Simple Grobner basis implementation with no indexing.
|
||||
*/
|
||||
class grobner {
|
||||
protected:
|
||||
struct monomial_lt;
|
||||
public:
|
||||
grobner_stats m_stats;
|
||||
class monomial {
|
||||
rational m_coeff;
|
||||
ptr_vector<expr> m_vars; //!< sorted variables
|
||||
|
||||
friend class grobner;
|
||||
friend struct monomial_lt;
|
||||
|
||||
monomial() {}
|
||||
public:
|
||||
rational const & get_coeff() const { return m_coeff; }
|
||||
unsigned get_degree() const { return m_vars.size(); }
|
||||
unsigned get_size() const { return get_degree(); }
|
||||
expr * get_var(unsigned idx) const { return m_vars[idx]; }
|
||||
};
|
||||
|
||||
class equation {
|
||||
unsigned m_scope_lvl; //!< scope level when this equation was created.
|
||||
unsigned m_bidx:31; //!< position at m_equations_to_delete
|
||||
unsigned m_lc:1; //!< true if equation if a linear combination of the input equations.
|
||||
ptr_vector<monomial> m_monomials; //!< sorted monomials
|
||||
v_dependency * m_dep; //!< justification for the equality
|
||||
friend class grobner;
|
||||
equation() {}
|
||||
public:
|
||||
unsigned get_num_monomials() const { return m_monomials.size(); }
|
||||
monomial const * get_monomial(unsigned idx) const { return m_monomials[idx]; }
|
||||
monomial * const * get_monomials() const { return m_monomials.c_ptr(); }
|
||||
v_dependency * get_dependency() const { return m_dep; }
|
||||
unsigned hash() const { return m_bidx; }
|
||||
bool is_linear_combination() const { return m_lc; }
|
||||
};
|
||||
|
||||
protected:
|
||||
static bool is_eq_monomial_body(monomial const * m1, monomial const * m2);
|
||||
|
||||
struct var_lt {
|
||||
obj_map<expr, int> & m_var2weight;
|
||||
var_lt(obj_map<expr, int> & m):m_var2weight(m) {}
|
||||
bool operator()(expr * v1, expr * v2) const;
|
||||
};
|
||||
|
||||
struct monomial_lt {
|
||||
var_lt & m_var_lt;
|
||||
monomial_lt(var_lt & lt):m_var_lt(lt) {}
|
||||
bool operator()(monomial * m1, monomial * m2) const;
|
||||
};
|
||||
|
||||
typedef obj_hashtable<equation> equation_set;
|
||||
typedef ptr_vector<equation> equation_vector;
|
||||
|
||||
ast_manager & m_manager;
|
||||
v_dependency_manager & m_dep_manager;
|
||||
arith_util m_util;
|
||||
obj_map<expr, int> m_var2weight;
|
||||
var_lt m_var_lt;
|
||||
monomial_lt m_monomial_lt;
|
||||
equation_set m_processed;
|
||||
equation_set m_to_process;
|
||||
equation_vector m_equations_to_unfreeze;
|
||||
equation_vector m_equations_to_delete;
|
||||
bool m_changed_leading_term; // set to true, if the leading term was simplified.
|
||||
equation * m_unsat;
|
||||
struct scope {
|
||||
unsigned m_equations_to_unfreeze_lim;
|
||||
unsigned m_equations_to_delete_lim;
|
||||
};
|
||||
svector<scope> m_scopes;
|
||||
ptr_vector<monomial> m_tmp_monomials;
|
||||
ptr_vector<expr> m_tmp_vars1;
|
||||
ptr_vector<expr> m_tmp_vars2;
|
||||
unsigned m_num_new_equations; // temporary variable
|
||||
|
||||
bool is_monomial_lt(monomial const & m1, monomial const & m2) const;
|
||||
|
||||
void display_vars(std::ostream & out, unsigned num_vars, expr * const * vars) const;
|
||||
|
||||
void display_var(std::ostream & out, expr * var) const;
|
||||
|
||||
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const;
|
||||
|
||||
void display_equations(std::ostream & out, equation_set const & v, char const * header) const;
|
||||
|
||||
void del_equations(unsigned old_size);
|
||||
|
||||
void unfreeze_equations(unsigned old_size);
|
||||
|
||||
void del_equation(equation * eq);
|
||||
|
||||
void flush();
|
||||
|
||||
bool update_order(equation * eq);
|
||||
|
||||
void update_order(equation_set & s, bool processed);
|
||||
|
||||
void add_var(monomial * m, expr * v);
|
||||
|
||||
monomial * mk_monomial(rational const & coeff, expr * m);
|
||||
|
||||
void init_equation(equation * eq, v_dependency * d);
|
||||
|
||||
void extract_monomials(expr * lhs, ptr_buffer<expr> & monomials);
|
||||
|
||||
void merge_monomials(ptr_vector<monomial> & monomials);
|
||||
|
||||
bool is_inconsistent(equation * eq) const;
|
||||
|
||||
bool is_trivial(equation * eq) const;
|
||||
|
||||
void normalize_coeff(ptr_vector<monomial> & monomials);
|
||||
|
||||
void simplify(ptr_vector<monomial> & monomials);
|
||||
|
||||
void simplify(equation * eq);
|
||||
|
||||
bool is_subset(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest) const;
|
||||
|
||||
void mul_append(unsigned start_idx, equation const * source, rational const & coeff, ptr_vector<expr> const & vars, ptr_vector<monomial> & result);
|
||||
|
||||
monomial * copy_monomial(monomial const * m);
|
||||
|
||||
equation * copy_equation(equation const * eq);
|
||||
|
||||
equation * simplify(equation const * source, equation * target);
|
||||
|
||||
equation * simplify_using_processed(equation * eq);
|
||||
|
||||
bool is_better_choice(equation * eq1, equation * eq2);
|
||||
|
||||
equation * pick_next();
|
||||
|
||||
void simplify_processed(equation * eq);
|
||||
|
||||
void simplify_to_process(equation * eq);
|
||||
|
||||
bool unify(monomial const * m1, monomial const * m2, ptr_vector<expr> & rest1, ptr_vector<expr> & rest2);
|
||||
|
||||
void superpose(equation * eq1, equation * eq2);
|
||||
|
||||
void superpose(equation * eq);
|
||||
|
||||
void copy_to(equation_set const & s, ptr_vector<equation> & result) const;
|
||||
|
||||
public:
|
||||
grobner(ast_manager & m, v_dependency_manager & dep_m);
|
||||
|
||||
~grobner();
|
||||
|
||||
unsigned get_scope_level() const { return m_scopes.size(); }
|
||||
|
||||
/**
|
||||
\brief Set the weight of a term that is viewed as a variable by this module.
|
||||
The weight is used to order monomials. If the weight is not set for a term t, then the
|
||||
weight of t is assumed to be 0.
|
||||
*/
|
||||
void set_weight(expr * n, int weight);
|
||||
|
||||
int get_weight(expr * n) const { int w = 0; m_var2weight.find(n, w); return w; }
|
||||
|
||||
/**
|
||||
\brief Update equations after set_weight was invoked once or more.
|
||||
*/
|
||||
void update_order();
|
||||
|
||||
/**
|
||||
\brief Create a new monomial. The caller owns the monomial until it invokes assert_eq_0.
|
||||
A monomial cannot be use to create several equations.
|
||||
*/
|
||||
monomial * mk_monomial(rational const & coeff, unsigned num_vars, expr * const * vars);
|
||||
|
||||
void del_monomial(monomial * m);
|
||||
|
||||
/**
|
||||
\brief Assert the given equality.
|
||||
This method assumes eq is simplified.
|
||||
*/
|
||||
void assert_eq(expr * eq, v_dependency * ex = 0);
|
||||
|
||||
/**
|
||||
\brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0.
|
||||
This method assumes the monomials were simplified.
|
||||
*/
|
||||
void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = 0);
|
||||
|
||||
/**
|
||||
\brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0.
|
||||
This method assumes the monomials were simplified.
|
||||
*/
|
||||
void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = 0);
|
||||
|
||||
/**
|
||||
\brief Assert the equality coeffs[0] * monomials[0] + ... + coeffs[num_monomials-1] * monomials[num_monomials - 1] = 0.
|
||||
This method assumes the monomials were simplified.
|
||||
*/
|
||||
void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = 0);
|
||||
|
||||
/**
|
||||
\brief Assert the monomial tautology (quote (x_1 * ... * x_n)) - x_1 * ... * x_n = 0
|
||||
*/
|
||||
void assert_monomial_tautology(expr * m);
|
||||
|
||||
/**
|
||||
\brief Compute Grobner basis.
|
||||
Return true if the threshold was not reached.
|
||||
*/
|
||||
bool compute_basis(unsigned threshold);
|
||||
|
||||
/**
|
||||
\brief Return true if an inconsistency was detected.
|
||||
*/
|
||||
bool inconsistent() const { return m_unsat != 0; }
|
||||
|
||||
/**
|
||||
\brief Simplify the given expression using the equalities asserted
|
||||
using assert_eq. Store the result in 'result'.
|
||||
*/
|
||||
void simplify(expr * n, expr_ref & result);
|
||||
|
||||
/**
|
||||
\brief Reset state. Remove all equalities asserted with assert_eq.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
void get_equations(ptr_vector<equation> & result) const;
|
||||
|
||||
void push_scope();
|
||||
|
||||
void pop_scope(unsigned num_scopes);
|
||||
|
||||
void display_equation(std::ostream & out, equation const & eq) const;
|
||||
|
||||
void display_monomial(std::ostream & out, monomial const & m) const;
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
};
|
||||
|
||||
#endif /* _GROBNER_H_ */
|
||||
|
3117
src/math/polynomial/algebraic_numbers.cpp
Normal file
3117
src/math/polynomial/algebraic_numbers.cpp
Normal file
File diff suppressed because it is too large
Load diff
493
src/math/polynomial/algebraic_numbers.h
Normal file
493
src/math/polynomial/algebraic_numbers.h
Normal file
|
@ -0,0 +1,493 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
algebraic_numbers.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Real Algebraic Numbers
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-11-22
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _ALGEBRAIC_NUMBERS_H_
|
||||
#define _ALGEBRAIC_NUMBERS_H_
|
||||
|
||||
#include"rational.h"
|
||||
#include"mpq.h"
|
||||
#include"polynomial.h"
|
||||
#include"z3_exception.h"
|
||||
#include"scoped_numeral.h"
|
||||
#include"scoped_numeral_vector.h"
|
||||
#include"tptr.h"
|
||||
#include"statistics.h"
|
||||
#include"params.h"
|
||||
|
||||
class small_object_allocator;
|
||||
class mpbq_manager;
|
||||
class mpbq;
|
||||
|
||||
namespace algebraic_numbers {
|
||||
class anum;
|
||||
class manager;
|
||||
|
||||
class algebraic_exception : public default_exception {
|
||||
public:
|
||||
algebraic_exception(char const * msg):default_exception(msg) {}
|
||||
};
|
||||
|
||||
class manager {
|
||||
public:
|
||||
struct imp;
|
||||
private:
|
||||
imp * m_imp;
|
||||
small_object_allocator * m_allocator;
|
||||
bool m_own_allocator;
|
||||
public:
|
||||
static bool precise() { return true; }
|
||||
static bool field() { return true; }
|
||||
typedef anum numeral;
|
||||
typedef svector<numeral> numeral_vector;
|
||||
typedef _scoped_numeral<manager> scoped_numeral;
|
||||
typedef _scoped_numeral_vector<manager> scoped_numeral_vector;
|
||||
|
||||
manager(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
~manager();
|
||||
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
unsynch_mpq_manager & qm() const;
|
||||
|
||||
mpbq_manager & bqm() const;
|
||||
|
||||
void del(numeral & a);
|
||||
|
||||
/**
|
||||
\brief a <- 0
|
||||
*/
|
||||
void reset(numeral & a);
|
||||
|
||||
/**
|
||||
\brief Return true if a is zero.
|
||||
*/
|
||||
bool is_zero(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Return true if a is positive.
|
||||
*/
|
||||
bool is_pos(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Return true if a is negative.
|
||||
*/
|
||||
bool is_neg(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Return true if a is a rational number.
|
||||
*/
|
||||
bool is_rational(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Return true if a is an integer.
|
||||
*/
|
||||
bool is_int(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Degree of the algebraic number.
|
||||
That is, degree of the polynomial that is used to encode \c a.
|
||||
*/
|
||||
unsigned degree(numeral const & a);
|
||||
|
||||
/**
|
||||
\brief Convert a into a rational number.
|
||||
|
||||
\pre is_rational(a)
|
||||
*/
|
||||
void to_rational(numeral const & a, mpq & r);
|
||||
|
||||
/**
|
||||
\brief Convert a into a rational number.
|
||||
|
||||
\pre is_rational(a)
|
||||
*/
|
||||
void to_rational(numeral const & a, rational & r);
|
||||
|
||||
/**
|
||||
\brief a <- n
|
||||
*/
|
||||
void set(numeral & a, int n);
|
||||
void set(numeral & a, mpz const & n);
|
||||
void set(numeral & a, mpq const & n);
|
||||
void set(numeral & a, numeral const & n);
|
||||
|
||||
void swap(numeral & a, numeral & b);
|
||||
|
||||
/**
|
||||
\brief Store in b an integer value smaller than 'a'.
|
||||
|
||||
Remark: this is not the floor, but b <= floor(a)
|
||||
*/
|
||||
void int_lt(numeral const & a, numeral & b);
|
||||
|
||||
/**
|
||||
\brief Store in b an integer value bigger than 'a'
|
||||
|
||||
Remark: this is not the ceil, but b >= ceil(a)
|
||||
*/
|
||||
void int_gt(numeral const & a, numeral & b);
|
||||
|
||||
/**
|
||||
\brief Store in result a value in the interval (prev, next)
|
||||
|
||||
\pre lt(pre,v next)
|
||||
*/
|
||||
void select(numeral const & prev, numeral const & curr, numeral & result);
|
||||
|
||||
/**
|
||||
\brief Isolate the roots of (an univariate polynomial) p, and store them as algebraic numbers in \c root.
|
||||
That is, p is in Z[x].
|
||||
*/
|
||||
void isolate_roots(polynomial_ref const & p, numeral_vector & roots);
|
||||
|
||||
/**
|
||||
\brief Isolate the roots of a multivariate polynomial p such that all but one variable of p is fixed by x2v, and
|
||||
store them as algebraic numbers in \c root.
|
||||
|
||||
That is, we are viewing p as a polynomial in Z[y_1, ..., y_n][x]:
|
||||
q_n(y_1, ..., y_n)x^n + ... + q_1(y_1, ..., y_n)*x + q_0
|
||||
And we are returning the roots of
|
||||
q_n(x2v(y_1), ..., x2v(y_n))x^n + ... + q_1(x2v(y_1), ..., x2v(y_n))*x + q_0
|
||||
*/
|
||||
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots);
|
||||
|
||||
/**
|
||||
\brief Isolate the roots of the given polynomial, and compute its sign between them.
|
||||
*/
|
||||
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector<int> & signs);
|
||||
|
||||
/**
|
||||
\brief Store in r the i-th root of p.
|
||||
|
||||
This method throws an exception if p does not have at least i roots.
|
||||
|
||||
This method is not really used in the nonlinear procedure.
|
||||
It is mainly used for debugging purposes, and creating regression tests
|
||||
|
||||
\pre i > 0
|
||||
*/
|
||||
void mk_root(polynomial_ref const & p, unsigned i, numeral & r);
|
||||
|
||||
/**
|
||||
\brief Store in r the i-th root of p.
|
||||
This method throws an exception if the s-expression p does not represent
|
||||
an univariate polynomial, of if p does not have at least i roots.
|
||||
|
||||
This method is not really used in the nonlinear procedure.
|
||||
It is mainly used for debugging purposes, and "reading" root objects in the SMT 2.0 front-end.
|
||||
|
||||
\pre i > 0
|
||||
*/
|
||||
void mk_root(sexpr const * p, unsigned i, numeral & r);
|
||||
|
||||
/**
|
||||
\brief Return a^{1/k}
|
||||
|
||||
Throws an exception if the result is not a real.
|
||||
That is, (a is negative and k is even) or (k is zero).
|
||||
*/
|
||||
void root(numeral const & a, unsigned k, numeral & b);
|
||||
|
||||
/**
|
||||
\brief Return a^k
|
||||
|
||||
Throws an exception if 0^0.
|
||||
*/
|
||||
void power(numeral const & a, unsigned k, numeral & b);
|
||||
|
||||
/**
|
||||
\brief c <- a + b
|
||||
*/
|
||||
void add(numeral const & a, numeral const & b, numeral & c);
|
||||
void add(numeral const & a, mpz const & b, numeral & c);
|
||||
|
||||
/**
|
||||
\brief c <- a - b
|
||||
*/
|
||||
void sub(numeral const & a, numeral const & b, numeral & c);
|
||||
|
||||
/**
|
||||
\brief c <- a * b
|
||||
*/
|
||||
void mul(numeral const & a, numeral const & b, numeral & c);
|
||||
|
||||
/**
|
||||
\brief a <- -a
|
||||
*/
|
||||
void neg(numeral & a);
|
||||
|
||||
/**
|
||||
\brief a <- 1/a if a != 0
|
||||
*/
|
||||
void inv(numeral & a);
|
||||
|
||||
/**
|
||||
\brief c <- a/b if b != 0
|
||||
*/
|
||||
void div(numeral const & a, numeral const & b, numeral & c);
|
||||
|
||||
/**
|
||||
Return -1 if a < b
|
||||
Return 0 if a == b
|
||||
Return 1 if a > b
|
||||
*/
|
||||
int compare(numeral const & a, numeral const & b);
|
||||
|
||||
/**
|
||||
\brief a == b
|
||||
*/
|
||||
bool eq(numeral const & a, numeral const & b);
|
||||
bool eq(numeral const & a, mpq const & b);
|
||||
bool eq(numeral const & a, mpz const & b);
|
||||
|
||||
/**
|
||||
\brief a != b
|
||||
*/
|
||||
bool neq(numeral const & a, numeral const & b) { return !eq(a, b); }
|
||||
bool neq(numeral const & a, mpq const & b) { return !eq(a, b); }
|
||||
bool neq(numeral const & a, mpz const & b) { return !eq(a, b); }
|
||||
|
||||
/**
|
||||
\brief a < b
|
||||
*/
|
||||
bool lt(numeral const & a, numeral const & b);
|
||||
bool lt(numeral const & a, mpq const & b);
|
||||
bool lt(numeral const & a, mpz const & b);
|
||||
|
||||
/**
|
||||
\brief a > b
|
||||
*/
|
||||
bool gt(numeral const & a, numeral const & b) { return lt(b, a); }
|
||||
bool gt(numeral const & a, mpq const & b);
|
||||
bool gt(numeral const & a, mpz const & b);
|
||||
|
||||
/**
|
||||
\brief a <= b
|
||||
*/
|
||||
bool le(numeral const & a, numeral const & b) { return !gt(a, b); }
|
||||
bool le(numeral const & a, mpq const & b) { return !gt(a, b); }
|
||||
bool le(numeral const & a, mpz const & b) { return !gt(a, b); }
|
||||
|
||||
/**
|
||||
\brief a >= b
|
||||
*/
|
||||
bool ge(numeral const & a, numeral const & b) { return !lt(a, b); }
|
||||
bool ge(numeral const & a, mpq const & b) { return !lt(a, b); }
|
||||
bool ge(numeral const & a, mpz const & b) { return !lt(a, b); }
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of a multivariate polynomial p(x_1, ..., x_n)
|
||||
at assignment x2v: [x_1 -> alpha_1, ..., x_n -> alpha_n].
|
||||
|
||||
\remark forall variable x in p, we have that x2v.contains(x) is true
|
||||
|
||||
Return negative number if p(alpha_1, ..., alpha_n) < 0
|
||||
Return 0 if p(alpha_1, ..., alpha_n) == 0
|
||||
Return positive number if p(alpha_1, ..., alpha_n) > 0
|
||||
*/
|
||||
int eval_sign_at(polynomial_ref const & p, polynomial::var2anum const & x2v);
|
||||
|
||||
void get_polynomial(numeral const & a, svector<mpz> & r);
|
||||
|
||||
// Procedures for getting lower and upper bounds for irrational numbers
|
||||
void get_lower(numeral const & a, mpbq & l);
|
||||
void get_lower(numeral const & a, mpq & l);
|
||||
void get_lower(numeral const & a, rational & l);
|
||||
void get_lower(numeral const & a, mpq & l, unsigned precision);
|
||||
void get_lower(numeral const & a, rational & l, unsigned precision);
|
||||
|
||||
void get_upper(numeral const & a, mpbq & u);
|
||||
void get_upper(numeral const & a, mpq & u);
|
||||
void get_upper(numeral const & a, rational & u);
|
||||
void get_upper(numeral const & a, mpq & l, unsigned precision);
|
||||
void get_upper(numeral const & a, rational & l, unsigned precision);
|
||||
|
||||
/**
|
||||
\brief Display algebraic number as a rational if is_rational(n)
|
||||
Otherwise, display it as an interval.
|
||||
*/
|
||||
void display_interval(std::ostream & out, numeral const & a) const;
|
||||
|
||||
/**
|
||||
\brief Display algebraic number in decimal notation.
|
||||
A question mark is added based on the precision requested.
|
||||
*/
|
||||
void display_decimal(std::ostream & out, numeral const & a, unsigned precision = 10) const;
|
||||
|
||||
/**
|
||||
\brief Display algebraic number as a root object: (p, i)
|
||||
That is, 'a' is the i-th root of p.
|
||||
*/
|
||||
void display_root(std::ostream & out, numeral const & a) const;
|
||||
|
||||
/**
|
||||
\brief Display algebraic number as a root object in SMT 2.0 style: (root-obj p i)
|
||||
That is, 'a' is the i-th root of p.
|
||||
*/
|
||||
void display_root_smt2(std::ostream & out, numeral const & a) const;
|
||||
|
||||
/**
|
||||
\brief Display algebraic number in Mathematica format.
|
||||
*/
|
||||
void display_mathematica(std::ostream & out, numeral const & a) const;
|
||||
|
||||
void display(std::ostream & out, numeral const & a) { return display_decimal(out, a); }
|
||||
|
||||
void reset_statistics();
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
};
|
||||
|
||||
struct basic_cell;
|
||||
struct algebraic_cell;
|
||||
|
||||
enum anum_kind { BASIC = 0, ROOT };
|
||||
|
||||
class anum {
|
||||
friend struct manager::imp;
|
||||
friend class manager;
|
||||
void * m_cell;
|
||||
anum(basic_cell * cell):m_cell(TAG(void*, cell, BASIC)) {}
|
||||
anum(algebraic_cell * cell):m_cell(TAG(void*, cell, ROOT)) {}
|
||||
bool is_basic() const { return GET_TAG(m_cell) == BASIC; }
|
||||
basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); }
|
||||
algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); }
|
||||
public:
|
||||
anum():m_cell(0) {}
|
||||
};
|
||||
};
|
||||
|
||||
typedef algebraic_numbers::manager anum_manager;
|
||||
typedef algebraic_numbers::manager::numeral anum;
|
||||
typedef algebraic_numbers::manager::numeral_vector anum_vector;
|
||||
typedef algebraic_numbers::manager::scoped_numeral scoped_anum;
|
||||
typedef algebraic_numbers::manager::scoped_numeral_vector scoped_anum_vector;
|
||||
|
||||
#define AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, TYPE) \
|
||||
inline bool EXTERNAL(scoped_anum const & a, TYPE const & b) { \
|
||||
anum_manager & m = a.m(); \
|
||||
scoped_anum _b(m); \
|
||||
m.set(_b, b); \
|
||||
return m.INTERNAL(a, _b); \
|
||||
}
|
||||
|
||||
#define AN_MK_COMPARISON(EXTERNAL, INTERNAL) \
|
||||
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, int) \
|
||||
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpz) \
|
||||
AN_MK_COMPARISON_CORE(EXTERNAL, INTERNAL, mpq)
|
||||
|
||||
AN_MK_COMPARISON(operator==, eq);
|
||||
AN_MK_COMPARISON(operator!=, neq);
|
||||
AN_MK_COMPARISON(operator<, lt);
|
||||
AN_MK_COMPARISON(operator<=, le);
|
||||
AN_MK_COMPARISON(operator>, gt);
|
||||
AN_MK_COMPARISON(operator>=, ge);
|
||||
|
||||
#undef AN_MK_COMPARISON
|
||||
#undef AN_MK_COMPARISON_CORE
|
||||
|
||||
#define AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, TYPE) \
|
||||
inline scoped_anum EXTERNAL(scoped_anum const & a, TYPE const & b) { \
|
||||
anum_manager & m = a.m(); \
|
||||
scoped_anum _b(m); \
|
||||
m.set(_b, b); \
|
||||
scoped_anum r(m); \
|
||||
m.INTERNAL(a, _b, r); \
|
||||
return r; \
|
||||
}
|
||||
|
||||
#define AN_MK_BINARY(EXTERNAL, INTERNAL) \
|
||||
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, int) \
|
||||
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpz) \
|
||||
AN_MK_BINARY_CORE(EXTERNAL, INTERNAL, mpq)
|
||||
|
||||
AN_MK_BINARY(operator+, add)
|
||||
AN_MK_BINARY(operator-, sub)
|
||||
AN_MK_BINARY(operator*, mul)
|
||||
AN_MK_BINARY(operator/, div)
|
||||
|
||||
#undef AN_MK_BINARY
|
||||
#undef AN_MK_BINARY_CORE
|
||||
|
||||
inline scoped_anum root(scoped_anum const & a, unsigned k) {
|
||||
scoped_anum r(a.m());
|
||||
a.m().root(a, k, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline scoped_anum power(scoped_anum const & a, unsigned k) {
|
||||
scoped_anum r(a.m());
|
||||
a.m().power(a, k, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
inline scoped_anum operator^(scoped_anum const & a, unsigned k) {
|
||||
return power(a, k);
|
||||
}
|
||||
|
||||
inline bool is_int(scoped_anum const & a) {
|
||||
return a.m().is_int(a);
|
||||
}
|
||||
|
||||
inline bool is_rational(scoped_anum const & a) {
|
||||
return a.m().is_rational(a);
|
||||
}
|
||||
|
||||
struct root_obj_pp {
|
||||
anum_manager & m;
|
||||
anum const & n;
|
||||
root_obj_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
|
||||
root_obj_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
|
||||
};
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & out, root_obj_pp const & n) {
|
||||
n.m.display_root(out, n.n);
|
||||
return out;
|
||||
}
|
||||
|
||||
struct decimal_pp {
|
||||
anum_manager & m;
|
||||
anum const & n;
|
||||
unsigned prec;
|
||||
decimal_pp(anum_manager & _m, anum const & _n, unsigned p):m(_m), n(_n), prec(p) {}
|
||||
decimal_pp(scoped_anum const & _n, unsigned p):m(_n.m()), n(_n.get()), prec(p) {}
|
||||
};
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & out, decimal_pp const & n) {
|
||||
n.m.display_decimal(out, n.n, n.prec);
|
||||
return out;
|
||||
}
|
||||
|
||||
struct interval_pp {
|
||||
anum_manager & m;
|
||||
anum const & n;
|
||||
interval_pp(anum_manager & _m, anum const & _n):m(_m), n(_n) {}
|
||||
interval_pp(scoped_anum const & _n):m(_n.m()), n(_n.get()) {}
|
||||
};
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & out, interval_pp const & n) {
|
||||
n.m.display_interval(out, n.n);
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
152
src/math/polynomial/linear_eq_solver.h
Normal file
152
src/math/polynomial/linear_eq_solver.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_eq_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple equational solver template for any number field.
|
||||
No special optimization, just the basics for solving small systems.
|
||||
It is a solver target to dense system of equations.
|
||||
Main client: Sparse Modular GCD algorithm.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-01-22
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _LINEAR_EQ_SOLVER_H_
|
||||
#define _LINEAR_EQ_SOLVER_H_
|
||||
|
||||
template<typename numeral_manager>
|
||||
class linear_eq_solver {
|
||||
typedef typename numeral_manager::numeral numeral;
|
||||
numeral_manager & m;
|
||||
unsigned n; // number of variables
|
||||
vector<svector<numeral> > A;
|
||||
svector<numeral> b;
|
||||
public:
|
||||
linear_eq_solver(numeral_manager & _m):m(_m), n(0) { SASSERT(m.field()); }
|
||||
~linear_eq_solver() { flush(); }
|
||||
|
||||
void flush() {
|
||||
SASSERT(b.size() == A.size());
|
||||
unsigned sz = A.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
svector<numeral> & as = A[i];
|
||||
m.del(b[i]);
|
||||
SASSERT(as.size() == n);
|
||||
for (unsigned j = 0; j < n; j++)
|
||||
m.del(as[j]);
|
||||
}
|
||||
A.reset();
|
||||
b.reset();
|
||||
n = 0;
|
||||
}
|
||||
|
||||
void resize(unsigned _n) {
|
||||
if (n != _n) {
|
||||
flush();
|
||||
n = _n;
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
A.push_back(svector<numeral>());
|
||||
svector<numeral> & as = A.back();
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
as.push_back(numeral());
|
||||
}
|
||||
b.push_back(numeral());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
svector<numeral> & A_i = A[i];
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
m.set(A_i[j], 0);
|
||||
}
|
||||
m.set(b[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Set row i with _as[0]*x_0 + ... + _as[n-1]*x_{n-1} = b
|
||||
void add(unsigned i, numeral const * _as, numeral const & _b) {
|
||||
SASSERT(i < n);
|
||||
m.set(b[i], _b);
|
||||
svector<numeral> & A_i = A[i];
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
m.set(A_i[j], _as[j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the system of equations has a solution.
|
||||
// Return false if the matrix is singular
|
||||
bool solve(numeral * xs) {
|
||||
for (unsigned k = 0; k < n; k++) {
|
||||
TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout););
|
||||
// find pivot
|
||||
unsigned i = k;
|
||||
for (; i < n; i++) {
|
||||
if (!m.is_zero(A[i][k]))
|
||||
break;
|
||||
}
|
||||
if (i == n)
|
||||
return false; // matrix is singular
|
||||
A[k].swap(A[i]); // swap rows
|
||||
svector<numeral> & A_k = A[k];
|
||||
numeral & A_k_k = A_k[k];
|
||||
SASSERT(!m.is_zero(A_k_k));
|
||||
// normalize row
|
||||
for (unsigned i = k+1; i < n; i++)
|
||||
m.div(A_k[i], A_k_k, A_k[i]);
|
||||
m.div(b[k], A_k_k, b[k]);
|
||||
m.set(A_k_k, 1);
|
||||
// check if first k-1 positions are zero
|
||||
DEBUG_CODE({ for (unsigned i = 0; i < k; i++) { SASSERT(m.is_zero(A_k[i])); } });
|
||||
// for all rows below pivot
|
||||
for (unsigned i = k+1; i < n; i++) {
|
||||
svector<numeral> & A_i = A[i];
|
||||
numeral & A_i_k = A_i[k];
|
||||
for (unsigned j = k+1; j < n; j++) {
|
||||
m.submul(A_i[j], A_i_k, A_k[j], A_i[j]);
|
||||
}
|
||||
m.submul(b[i], A_i_k, b[k], b[i]);
|
||||
m.set(A_i_k, 0);
|
||||
}
|
||||
}
|
||||
unsigned k = n;
|
||||
while (k > 0) {
|
||||
--k;
|
||||
TRACE("linear_eq_solver", tout << "iteration " << k << "\n"; display(tout););
|
||||
SASSERT(m.is_one(A[k][k]));
|
||||
// save result
|
||||
m.set(xs[k], b[k]);
|
||||
// back substitute
|
||||
unsigned i = k;
|
||||
while (i > 0) {
|
||||
--i;
|
||||
m.submul(b[i], A[i][k], b[k], b[i]);
|
||||
m.set(A[i][k], 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void display(std::ostream & out) const {
|
||||
for (unsigned i = 0; i < A.size(); i++) {
|
||||
SASSERT(A[i].size() == n);
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
m.display(out, A[i][j]);
|
||||
out << " ";
|
||||
}
|
||||
m.display(out, b[i]); out << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
7386
src/math/polynomial/polynomial.cpp
Normal file
7386
src/math/polynomial/polynomial.cpp
Normal file
File diff suppressed because it is too large
Load diff
1380
src/math/polynomial/polynomial.h
Normal file
1380
src/math/polynomial/polynomial.h
Normal file
File diff suppressed because it is too large
Load diff
235
src/math/polynomial/polynomial_cache.cpp
Normal file
235
src/math/polynomial/polynomial_cache.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polynomial_cache.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
"Hash-consing" for polynomials
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-01-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"polynomial_cache.h"
|
||||
#include"chashtable.h"
|
||||
|
||||
namespace polynomial {
|
||||
|
||||
struct poly_hash_proc {
|
||||
manager & m;
|
||||
poly_hash_proc(manager & _m):m(_m) {}
|
||||
unsigned operator()(polynomial const * p) const { return m.hash(p); }
|
||||
};
|
||||
|
||||
struct poly_eq_proc {
|
||||
manager & m;
|
||||
poly_eq_proc(manager & _m):m(_m) {}
|
||||
bool operator()(polynomial const * p1, polynomial const * p2) const { return m.eq(p1, p2); }
|
||||
};
|
||||
|
||||
struct psc_chain_entry {
|
||||
polynomial const * m_p;
|
||||
polynomial const * m_q;
|
||||
var m_x;
|
||||
unsigned m_hash;
|
||||
unsigned m_result_sz;
|
||||
polynomial ** m_result;
|
||||
|
||||
psc_chain_entry(polynomial const * p, polynomial const * q, var x, unsigned h):
|
||||
m_p(p),
|
||||
m_q(q),
|
||||
m_x(x),
|
||||
m_hash(h),
|
||||
m_result_sz(0),
|
||||
m_result(0) {
|
||||
}
|
||||
|
||||
struct hash_proc { unsigned operator()(psc_chain_entry const * entry) const { return entry->m_hash; } };
|
||||
|
||||
struct eq_proc {
|
||||
bool operator()(psc_chain_entry const * e1, psc_chain_entry const * e2) const {
|
||||
return e1->m_p == e2->m_p && e1->m_q == e2->m_q && e1->m_x == e2->m_x;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct factor_entry {
|
||||
polynomial const * m_p;
|
||||
unsigned m_hash;
|
||||
unsigned m_result_sz;
|
||||
polynomial ** m_result;
|
||||
|
||||
factor_entry(polynomial const * p, unsigned h):
|
||||
m_p(p),
|
||||
m_hash(h),
|
||||
m_result_sz(0),
|
||||
m_result(0) {
|
||||
}
|
||||
|
||||
struct hash_proc { unsigned operator()(factor_entry const * entry) const { return entry->m_hash; } };
|
||||
|
||||
struct eq_proc {
|
||||
bool operator()(factor_entry const * e1, factor_entry const * e2) const {
|
||||
return e1->m_p == e2->m_p;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
typedef chashtable<polynomial*, poly_hash_proc, poly_eq_proc> polynomial_table;
|
||||
typedef chashtable<psc_chain_entry*, psc_chain_entry::hash_proc, psc_chain_entry::eq_proc> psc_chain_cache;
|
||||
typedef chashtable<factor_entry*, factor_entry::hash_proc, factor_entry::eq_proc> factor_cache;
|
||||
|
||||
struct cache::imp {
|
||||
manager & m;
|
||||
polynomial_table m_poly_table;
|
||||
psc_chain_cache m_psc_chain_cache;
|
||||
factor_cache m_factor_cache;
|
||||
polynomial_ref_vector m_cached_polys;
|
||||
svector<char> m_in_cache;
|
||||
small_object_allocator & m_allocator;
|
||||
|
||||
imp(manager & _m):m(_m), m_poly_table(poly_hash_proc(m), poly_eq_proc(m)), m_cached_polys(m), m_allocator(m.allocator()) {
|
||||
}
|
||||
|
||||
~imp() {
|
||||
reset_psc_chain_cache();
|
||||
reset_factor_cache();
|
||||
}
|
||||
|
||||
void del_psc_chain_entry(psc_chain_entry * entry) {
|
||||
if (entry->m_result_sz != 0)
|
||||
m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result);
|
||||
entry->~psc_chain_entry();
|
||||
m_allocator.deallocate(sizeof(psc_chain_entry), entry);
|
||||
}
|
||||
|
||||
void del_factor_entry(factor_entry * entry) {
|
||||
if (entry->m_result_sz != 0)
|
||||
m_allocator.deallocate(sizeof(polynomial*)*entry->m_result_sz, entry->m_result);
|
||||
entry->~factor_entry();
|
||||
m_allocator.deallocate(sizeof(factor_entry), entry);
|
||||
}
|
||||
|
||||
void reset_psc_chain_cache() {
|
||||
psc_chain_cache::iterator it = m_psc_chain_cache.begin();
|
||||
psc_chain_cache::iterator end = m_psc_chain_cache.end();
|
||||
for (; it != end; ++it) {
|
||||
del_psc_chain_entry(*it);
|
||||
}
|
||||
m_psc_chain_cache.reset();
|
||||
}
|
||||
|
||||
void reset_factor_cache() {
|
||||
factor_cache::iterator it = m_factor_cache.begin();
|
||||
factor_cache::iterator end = m_factor_cache.end();
|
||||
for (; it != end; ++it) {
|
||||
del_factor_entry(*it);
|
||||
}
|
||||
m_factor_cache.reset();
|
||||
}
|
||||
|
||||
unsigned pid(polynomial * p) const { return m.id(p); }
|
||||
|
||||
polynomial * mk_unique(polynomial * p) {
|
||||
if (m_in_cache.get(pid(p), false))
|
||||
return p;
|
||||
polynomial * p_prime = m_poly_table.insert_if_not_there(p);
|
||||
if (p == p_prime) {
|
||||
m_cached_polys.push_back(p_prime);
|
||||
m_in_cache.setx(pid(p_prime), true, false);
|
||||
}
|
||||
return p_prime;
|
||||
}
|
||||
|
||||
void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) {
|
||||
p = mk_unique(p);
|
||||
q = mk_unique(q);
|
||||
unsigned h = hash_u_u(pid(p), pid(q));
|
||||
psc_chain_entry * entry = new (m_allocator.allocate(sizeof(psc_chain_entry))) psc_chain_entry(p, q, x, h);
|
||||
psc_chain_entry * old_entry = m_psc_chain_cache.insert_if_not_there(entry);
|
||||
if (entry != old_entry) {
|
||||
entry->~psc_chain_entry();
|
||||
m_allocator.deallocate(sizeof(psc_chain_entry), entry);
|
||||
S.reset();
|
||||
for (unsigned i = 0; i < old_entry->m_result_sz; i++) {
|
||||
S.push_back(old_entry->m_result[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m.psc_chain(p, q, x, S);
|
||||
unsigned sz = S.size();
|
||||
entry->m_result_sz = sz;
|
||||
entry->m_result = static_cast<polynomial**>(m_allocator.allocate(sizeof(polynomial*)*sz));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
polynomial * h = mk_unique(S.get(i));
|
||||
S.set(i, h);
|
||||
entry->m_result[i] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void factor(polynomial * p, polynomial_ref_vector & distinct_factors) {
|
||||
distinct_factors.reset();
|
||||
p = mk_unique(p);
|
||||
unsigned h = hash_u(pid(p));
|
||||
factor_entry * entry = new (m_allocator.allocate(sizeof(factor_entry))) factor_entry(p, h);
|
||||
factor_entry * old_entry = m_factor_cache.insert_if_not_there(entry);
|
||||
if (entry != old_entry) {
|
||||
entry->~factor_entry();
|
||||
m_allocator.deallocate(sizeof(factor_entry), entry);
|
||||
distinct_factors.reset();
|
||||
for (unsigned i = 0; i < old_entry->m_result_sz; i++) {
|
||||
distinct_factors.push_back(old_entry->m_result[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
factors fs(m);
|
||||
m.factor(p, fs);
|
||||
unsigned sz = fs.distinct_factors();
|
||||
entry->m_result_sz = sz;
|
||||
entry->m_result = static_cast<polynomial**>(m_allocator.allocate(sizeof(polynomial*)*sz));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
polynomial * h = mk_unique(fs[i]);
|
||||
distinct_factors.push_back(h);
|
||||
entry->m_result[i] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cache::cache(manager & m) {
|
||||
m_imp = alloc(imp, m);
|
||||
}
|
||||
|
||||
cache::~cache() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
manager & cache::m() const {
|
||||
return m_imp->m;
|
||||
}
|
||||
|
||||
polynomial * cache::mk_unique(polynomial * p) {
|
||||
return m_imp->mk_unique(p);
|
||||
}
|
||||
|
||||
void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
|
||||
m_imp->psc_chain(const_cast<polynomial*>(p), const_cast<polynomial*>(q), x, S);
|
||||
}
|
||||
|
||||
void cache::factor(polynomial const * p, polynomial_ref_vector & distinct_factors) {
|
||||
m_imp->factor(const_cast<polynomial*>(p), distinct_factors);
|
||||
}
|
||||
|
||||
void cache::reset() {
|
||||
manager & _m = m();
|
||||
dealloc(m_imp);
|
||||
m_imp = alloc(imp, _m);
|
||||
}
|
||||
};
|
44
src/math/polynomial/polynomial_cache.h
Normal file
44
src/math/polynomial/polynomial_cache.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polynomial_cache.h
|
||||
|
||||
Abstract:
|
||||
|
||||
"Hash-consing" for polynomials
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-01-07
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _POLYNOMIAL_CACHE_H_
|
||||
#define _POLYNOMIAL_CACHE_H_
|
||||
|
||||
#include"polynomial.h"
|
||||
|
||||
namespace polynomial {
|
||||
|
||||
/**
|
||||
\brief Functor for creating unique polynomials and caching results of operations
|
||||
*/
|
||||
class cache {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
public:
|
||||
cache(manager & m);
|
||||
~cache();
|
||||
manager & m() const;
|
||||
manager & pm() const { return m(); }
|
||||
polynomial * mk_unique(polynomial * p);
|
||||
void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S);
|
||||
void factor(polynomial const * p, polynomial_ref_vector & distinct_factors);
|
||||
void reset();
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
1143
src/math/polynomial/polynomial_factorization.cpp
Normal file
1143
src/math/polynomial/polynomial_factorization.cpp
Normal file
File diff suppressed because it is too large
Load diff
65
src/math/polynomial/polynomial_factorization.h
Normal file
65
src/math/polynomial/polynomial_factorization.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polynomial_factorization.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Methods for factoring polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Dejan (t-dejanj) 2011-11-29
|
||||
|
||||
Notes:
|
||||
|
||||
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
|
||||
46(8-10):1853–1859, 1967.
|
||||
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
|
||||
edition, 1997.
|
||||
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if 0
|
||||
// Disabled for reorg
|
||||
#include "polynomial.h"
|
||||
#include "upolynomial.h"
|
||||
#include "bit_vector.h"
|
||||
#include "z3_exception.h"
|
||||
|
||||
namespace polynomial {
|
||||
|
||||
/**
|
||||
\brief Something to throw when we are in trouble.
|
||||
*/
|
||||
class factorization_exception : public default_exception {
|
||||
public:
|
||||
factorization_exception(char const * msg) : default_exception(msg) {}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Factor the polynomial f from Z[x1, ..., x_n]. Returns the index of the last factor that is completely
|
||||
factored. I.e., if the method returns m, then f_1, ..., f_m are true irreducible factors, and the rest might
|
||||
be further reducible.
|
||||
*/
|
||||
unsigned factor(polynomial_ref & f, factors & factors);
|
||||
|
||||
/**
|
||||
\brief Factor the square-free primitive polynomial f from Z[x1, ..., x_n]. Returns true if the factorization
|
||||
was sucesseful, i.e. it was completed and the result is complete. Otherwise the quarantee is that the all but
|
||||
the last factor are irreducible.
|
||||
*/
|
||||
bool factor_square_free_primitive(polynomial_ref const & f, factors & factors);
|
||||
}
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & out, polynomial::factors & factors) {
|
||||
factors.display(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
72
src/math/polynomial/polynomial_primes.h
Normal file
72
src/math/polynomial/polynomial_primes.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polynomial_primes.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Some prime numbers for modular computations.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-21
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _POLYNOMIAL_PRIMES_H_
|
||||
#define _POLYNOMIAL_PRIMES_H_
|
||||
|
||||
namespace polynomial {
|
||||
#define NUM_SMALL_PRIMES 11
|
||||
const unsigned g_small_primes[NUM_SMALL_PRIMES] = {
|
||||
13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
|
||||
};
|
||||
|
||||
#if 0
|
||||
#define NUM_BIG_PRIMES 77
|
||||
const unsigned g_big_primes[NUM_BIG_PRIMES] = {
|
||||
16777259, 16777289, 16777291, 16777331, 16777333, 16777337, 16777381,
|
||||
16777421, 16777441, 16777447, 16777469, 16777499, 16777507, 16777531,
|
||||
16777571, 16777577, 16777597, 16777601, 16777619, 16777633, 16777639,
|
||||
16777643, 16777669, 16777679, 16777681, 16777699, 16777711, 16777721,
|
||||
16777723, 16777729, 16777751, 16777777, 16777781, 16777807, 16777811,
|
||||
16777823, 16777829, 16777837, 16777853, 16777903, 16777907, 16777949,
|
||||
16777967, 16777973, 16777987, 16777991, 16778009, 16778023, 16778071,
|
||||
16778077, 16778089, 16778123, 16778129, 16778137, 16778147, 16778173,
|
||||
16778227, 16778231, 16778291, 16778309, 16778357, 16778383, 16778401,
|
||||
16778413, 16778429, 16778441, 16778449, 16778453, 16778497, 16778521,
|
||||
16778537, 16778543, 16778549, 16778561, 16778603, 16778623, 16778627
|
||||
};
|
||||
#else
|
||||
#define NUM_BIG_PRIMES 231
|
||||
const unsigned g_big_primes[NUM_BIG_PRIMES] = {
|
||||
39103, 39107, 39113, 39119, 39133, 39139, 39157, 39161, 39163, 39181, 39191,
|
||||
39199, 39209, 39217, 39227, 39229, 39233, 39239, 39241, 39251, 39293, 39301,
|
||||
39313, 39317, 39323, 39341, 39343, 39359, 39367, 39371, 39373, 39383, 39397,
|
||||
39409, 39419, 39439, 39443, 39451, 39461, 39499, 39503, 39509, 39511, 39521,
|
||||
39541, 39551, 39563, 39569, 39581, 39607, 39619, 39623, 39631, 39659, 39667,
|
||||
39671, 39679, 39703, 39709, 39719, 39727, 39733, 39749, 39761, 39769, 39779,
|
||||
39791, 39799, 39821, 39827, 39829, 39839, 39841, 39847, 39857, 39863, 39869,
|
||||
39877, 39883, 39887, 39901, 39929, 39937, 39953, 39971, 39979, 39983, 39989,
|
||||
40009, 40013, 40031, 40037, 40039, 40063, 40087, 40093, 40099, 40111, 40123,
|
||||
40127, 40129, 40151, 40153, 40163, 40169, 40177, 40189, 40193, 40213, 40231,
|
||||
40237, 40241, 40253, 40277, 40283, 40289, 40343, 40351, 40357, 40361, 40387,
|
||||
40423, 40427, 40429, 40433, 40459, 40471, 40483, 40487, 40493, 40499, 40507,
|
||||
40519, 40529, 40531, 40543, 40559, 40577, 40583, 40591, 40597, 40609, 40627,
|
||||
40637, 40639, 40693, 40697, 40699, 40709, 40739, 40751, 40759, 40763, 40771,
|
||||
40787, 40801, 40813, 40819, 40823, 40829, 40841, 40847, 40849, 40853, 40867,
|
||||
40879, 40883, 40897, 40903, 40927, 40933, 40939, 40949, 40961, 40973, 40993,
|
||||
41011, 41017, 41023, 41039, 41047, 41051, 41057, 41077, 41081, 41113, 41117,
|
||||
41131, 41141, 41143, 41149, 41161, 41177, 41179, 41183, 41189, 41201, 41203,
|
||||
41213, 41221, 41227, 41231, 41233, 41243, 41257, 41263, 41269, 41281, 41299,
|
||||
41333, 41341, 41351, 41357, 41381, 41387, 41389, 41399, 41411, 41413, 41443,
|
||||
41453, 41467, 41479, 41491, 41507, 41513, 41519, 41521, 41539, 41543, 41549
|
||||
};
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
50
src/math/polynomial/polynomial_var2value.h
Normal file
50
src/math/polynomial/polynomial_var2value.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
polynomial_var2value.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple implementations of the var2value interface.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-01-05
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _POLYNOMIAL_VAR2VALUE_H_
|
||||
#define _POLYNOMIAL_VAR2VALUE_H_
|
||||
|
||||
#include"polynomial.h"
|
||||
#include"scoped_numeral_vector.h"
|
||||
|
||||
namespace polynomial {
|
||||
|
||||
// default implementation used for debugging purposes
|
||||
template<typename ValManager>
|
||||
class simple_var2value : public var2value<ValManager, typename ValManager::numeral> {
|
||||
var_vector m_xs;
|
||||
_scoped_numeral_vector<ValManager> m_vs;
|
||||
public:
|
||||
simple_var2value(ValManager & m):m_vs(m) {}
|
||||
void push_back(var x, typename ValManager::numeral const & v) { m_xs.push_back(x); m_vs.push_back(v); }
|
||||
virtual ValManager & m() const { return m_vs.m(); }
|
||||
virtual bool contains(var x) const { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); }
|
||||
virtual typename ValManager::numeral const & operator()(var x) const {
|
||||
for (unsigned i = 0; i < m_xs.size(); i++)
|
||||
if (m_xs[i] == x)
|
||||
return m_vs[i];
|
||||
UNREACHABLE();
|
||||
static typename ValManager::numeral zero;
|
||||
return zero;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
800
src/math/polynomial/rpolynomial.cpp
Normal file
800
src/math/polynomial/rpolynomial.cpp
Normal file
|
@ -0,0 +1,800 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rpolynomial.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Goodies for creating and handling polynomials in dense recursive representation.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-06-11
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"rpolynomial.h"
|
||||
#include"tptr.h"
|
||||
#include"buffer.h"
|
||||
|
||||
namespace rpolynomial {
|
||||
|
||||
typedef void poly_or_num;
|
||||
|
||||
inline bool is_poly(poly_or_num * p) { return GET_TAG(p) == 0; }
|
||||
inline bool is_num(poly_or_num * p) { return GET_TAG(p) == 1; }
|
||||
polynomial * to_poly(poly_or_num * p) { SASSERT(is_poly(p)); return UNTAG(polynomial*, p); }
|
||||
manager::numeral * to_num_ptr(poly_or_num * p) { SASSERT(is_num(p)); return (UNTAG(manager::numeral *, p)); }
|
||||
manager::numeral & to_num(poly_or_num * p) { return *to_num_ptr(p); }
|
||||
poly_or_num * to_poly_or_num(polynomial * p) { return TAG(poly_or_num*, p, 0); }
|
||||
poly_or_num * to_poly_or_num(manager::numeral * n) { return TAG(poly_or_num*, n, 1); }
|
||||
|
||||
class polynomial {
|
||||
friend class manager;
|
||||
friend struct manager::imp;
|
||||
|
||||
unsigned m_ref_count;
|
||||
var m_var;
|
||||
unsigned m_size;
|
||||
poly_or_num * m_args[0];
|
||||
|
||||
public:
|
||||
unsigned ref_count() const { return m_ref_count; }
|
||||
void inc_ref() { m_ref_count++; }
|
||||
void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; }
|
||||
|
||||
static unsigned get_obj_size(unsigned n) { return sizeof(polynomial) + n*sizeof(void*); }
|
||||
|
||||
var max_var() const { return m_var; }
|
||||
unsigned size() const { return m_size; }
|
||||
poly_or_num * arg(unsigned i) const { SASSERT(i < size()); return m_args[i]; }
|
||||
};
|
||||
|
||||
struct manager::imp {
|
||||
manager & m_wrapper;
|
||||
numeral_manager & m_manager;
|
||||
small_object_allocator * m_allocator;
|
||||
bool m_own_allocator;
|
||||
volatile bool m_cancel;
|
||||
|
||||
imp(manager & w, numeral_manager & m, small_object_allocator * a):
|
||||
m_wrapper(w),
|
||||
m_manager(m),
|
||||
m_allocator(a),
|
||||
m_own_allocator(a == 0) {
|
||||
if (a == 0)
|
||||
m_allocator = alloc(small_object_allocator, "rpolynomial");
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
~imp() {
|
||||
if (m_own_allocator)
|
||||
dealloc(m_allocator);
|
||||
}
|
||||
|
||||
// Remark: recursive calls should be fine since I do not expect to have polynomials with more than 100 variables.
|
||||
|
||||
manager & pm() const { return m_wrapper; }
|
||||
|
||||
numeral * mk_numeral() {
|
||||
void * mem = m_allocator->allocate(sizeof(numeral));
|
||||
return new (mem) numeral();
|
||||
}
|
||||
|
||||
void del_numeral(numeral * n) {
|
||||
m_manager.del(*n);
|
||||
m_allocator->deallocate(sizeof(numeral), n);
|
||||
}
|
||||
|
||||
static void inc_ref(polynomial * p) {
|
||||
if (p)
|
||||
p->inc_ref();
|
||||
}
|
||||
|
||||
static void inc_ref(poly_or_num * p) {
|
||||
if (p && is_poly(p))
|
||||
inc_ref(to_poly(p));
|
||||
}
|
||||
|
||||
void del_poly(polynomial * p) {
|
||||
SASSERT(p != 0);
|
||||
ptr_buffer<polynomial> todo;
|
||||
todo.push_back(p);
|
||||
while (!todo.empty()) {
|
||||
p = todo.back();
|
||||
todo.pop_back();
|
||||
unsigned sz = p->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn = p->arg(i);
|
||||
if (pn == 0)
|
||||
continue;
|
||||
if (is_num(pn)) {
|
||||
del_numeral(to_num_ptr(pn));
|
||||
}
|
||||
else {
|
||||
SASSERT(is_poly(p));
|
||||
polynomial * p_arg = to_poly(p);
|
||||
p_arg->dec_ref();
|
||||
if (p_arg->ref_count() == 0) {
|
||||
todo.push_back(p_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned obj_sz = polynomial::get_obj_size(sz);
|
||||
m_allocator->deallocate(obj_sz, p);
|
||||
}
|
||||
}
|
||||
|
||||
void dec_ref(polynomial * p) {
|
||||
if (p) {
|
||||
p->dec_ref();
|
||||
if (p->ref_count() == 0)
|
||||
del_poly(p);
|
||||
}
|
||||
}
|
||||
|
||||
void dec_ref(poly_or_num * p) {
|
||||
if (p && is_poly(p))
|
||||
dec_ref(to_poly(p));
|
||||
}
|
||||
|
||||
static bool is_const(polynomial const * p) {
|
||||
SASSERT(p == 0 || (p->max_var() == null_var) == (p->size() == 1 && p->arg(0) != 0 && is_num(p->arg(0))));
|
||||
return p == 0 || p->max_var() == null_var;
|
||||
}
|
||||
|
||||
bool is_zero(polynomial const * p) {
|
||||
return p == 0;
|
||||
}
|
||||
|
||||
static bool is_univariate(polynomial const * p) {
|
||||
if (is_const(p))
|
||||
return false;
|
||||
unsigned sz = p->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn = p->arg(i);
|
||||
if (pn == 0)
|
||||
continue;
|
||||
if (is_poly(pn))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_monomial(polynomial const * p) {
|
||||
if (is_const(p))
|
||||
return true;
|
||||
unsigned sz = p->size();
|
||||
SASSERT(sz > 0);
|
||||
SASSERT(p->arg(sz - 1) != 0);
|
||||
for (unsigned i = 0; i < sz - 1; i++) {
|
||||
if (p->arg(i) != 0)
|
||||
return false;
|
||||
}
|
||||
SASSERT(is_poly(p->arg(sz - 1)));
|
||||
return is_monomial(to_poly(p->arg(sz-1)));
|
||||
}
|
||||
|
||||
unsigned degree(polynomial const * p) {
|
||||
SASSERT(p != 0);
|
||||
SASSERT(p->size() > 0);
|
||||
return p == 0 ? 0 : p->size() - 1;
|
||||
}
|
||||
|
||||
bool eq(polynomial const * p1, polynomial const * p2) {
|
||||
if (p1 == p2)
|
||||
return true;
|
||||
if (p1 == 0 || p2 == 0)
|
||||
return false;
|
||||
if (p1->size() != p2->size())
|
||||
return false;
|
||||
if (p1->max_var() != p2->max_var())
|
||||
return false;
|
||||
unsigned sz = p1->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn1 = p1->arg(i);
|
||||
poly_or_num * pn2 = p2->arg(i);
|
||||
if (pn1 == 0 && pn2 == 0)
|
||||
continue;
|
||||
if (pn1 == 0 || pn2 == 0)
|
||||
return false;
|
||||
if (is_num(pn1) && is_num(pn2)) {
|
||||
if (!m_manager.eq(to_num(pn1), to_num(pn2)))
|
||||
return false;
|
||||
}
|
||||
else if (is_poly(pn1) && is_poly(pn2)) {
|
||||
if (!eq(to_poly(pn1), to_poly(pn2)))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void inc_ref_args(unsigned sz, poly_or_num * const * args) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn = args[i];
|
||||
if (pn == 0 || is_num(pn))
|
||||
continue;
|
||||
inc_ref(to_poly(pn));
|
||||
}
|
||||
}
|
||||
|
||||
void dec_ref_args(unsigned sz, poly_or_num * const * args) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn = args[i];
|
||||
if (pn == 0 || is_num(pn))
|
||||
continue;
|
||||
dec_ref(to_poly(pn));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned trim(unsigned sz, poly_or_num * const * args) {
|
||||
while (sz > 0) {
|
||||
if (args[sz - 1] != 0)
|
||||
return sz;
|
||||
sz--;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
polynomial * allocate_poly(unsigned sz, poly_or_num * const * args, var max_var) {
|
||||
SASSERT(sz > 0);
|
||||
SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0])));
|
||||
unsigned obj_sz = polynomial::get_obj_size(sz);
|
||||
void * mem = m_allocator->allocate(obj_sz);
|
||||
polynomial * new_pol = new (mem) polynomial();
|
||||
new_pol->m_ref_count = 0;
|
||||
new_pol->m_var = max_var;
|
||||
new_pol->m_size = sz;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
poly_or_num * pn = args[i];
|
||||
if (is_poly(pn)) {
|
||||
inc_ref(to_poly(pn));
|
||||
new_pol->m_args[i] = pn;
|
||||
SASSERT(max_var == null_var || to_poly(pn)->max_var() < max_var);
|
||||
}
|
||||
else {
|
||||
SASSERT(!m_manager.is_zero(to_num(pn)));
|
||||
new_pol->m_args[i] = pn;
|
||||
}
|
||||
}
|
||||
return new_pol;
|
||||
}
|
||||
|
||||
poly_or_num * mk_poly_core(unsigned sz, poly_or_num * const * args, var max_var) {
|
||||
sz = trim(sz, args);
|
||||
SASSERT(sz > 0);
|
||||
if (sz == 1) {
|
||||
poly_or_num * pn0 = args[0];
|
||||
SASSERT(!is_num(pn0) || !m_manager.is_zero(to_num(pn0)));
|
||||
return pn0;
|
||||
}
|
||||
SASSERT((max_var == null_var) == (sz == 1 && is_num(args[0])));
|
||||
SASSERT(sz > 1);
|
||||
return to_poly_or_num(allocate_poly(sz, args, max_var));
|
||||
}
|
||||
|
||||
polynomial * mk_poly(unsigned sz, poly_or_num * const * args, var max_var) {
|
||||
poly_or_num * _p = mk_poly_core(sz, args, max_var);
|
||||
if (_p == 0)
|
||||
return 0;
|
||||
else if (is_num(_p))
|
||||
return allocate_poly(1, &_p, null_var);
|
||||
else
|
||||
return to_poly(_p);
|
||||
}
|
||||
|
||||
polynomial * mk_const(numeral const & n) {
|
||||
if (m_manager.is_zero(n))
|
||||
return 0;
|
||||
numeral * a = mk_numeral();
|
||||
m_manager.set(*a, n);
|
||||
poly_or_num * _a = to_poly_or_num(a);
|
||||
return allocate_poly(1, &_a, null_var);
|
||||
}
|
||||
|
||||
polynomial * mk_const(rational const & a) {
|
||||
SASSERT(a.is_int());
|
||||
scoped_numeral tmp(m_manager);
|
||||
m_manager.set(tmp, a.to_mpq().numerator());
|
||||
return mk_const(tmp);
|
||||
}
|
||||
|
||||
polynomial * mk_polynomial(var x, unsigned k) {
|
||||
SASSERT(x != null_var);
|
||||
if (k == 0) {
|
||||
numeral one;
|
||||
m_manager.set(one, 1);
|
||||
return mk_const(one);
|
||||
}
|
||||
ptr_buffer<poly_or_num> new_args;
|
||||
for (unsigned i = 0; i < k; i++)
|
||||
new_args.push_back(0);
|
||||
numeral * new_arg = mk_numeral();
|
||||
m_manager.set(*new_arg, 1);
|
||||
new_args.push_back(to_poly_or_num(new_arg));
|
||||
return mk_poly(new_args.size(), new_args.c_ptr(), x);
|
||||
}
|
||||
|
||||
poly_or_num * unpack(polynomial const * p) {
|
||||
if (p == 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (is_const(p)) {
|
||||
SASSERT(p->size() == 1);
|
||||
SASSERT(p->max_var() == null_var);
|
||||
return p->arg(0);
|
||||
}
|
||||
else {
|
||||
return to_poly_or_num(const_cast<polynomial*>(p));
|
||||
}
|
||||
}
|
||||
|
||||
polynomial * pack(poly_or_num * p) {
|
||||
if (p == 0)
|
||||
return 0;
|
||||
else if (is_num(p))
|
||||
return mk_poly(1, &p, null_var);
|
||||
else
|
||||
return to_poly(p);
|
||||
}
|
||||
|
||||
poly_or_num * mul_core(numeral const & c, poly_or_num * p) {
|
||||
if (m_manager.is_zero(c) || p == 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (is_num(p)) {
|
||||
numeral * r = mk_numeral();
|
||||
m_manager.mul(c, to_num(p), *r);
|
||||
return to_poly_or_num(r);
|
||||
}
|
||||
else {
|
||||
polynomial * _p = to_poly(p);
|
||||
unsigned sz = _p->size();
|
||||
SASSERT(sz > 1);
|
||||
ptr_buffer<poly_or_num> new_args;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
new_args.push_back(mul_core(c, _p->arg(i)));
|
||||
}
|
||||
return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var());
|
||||
}
|
||||
}
|
||||
|
||||
polynomial * mul(numeral const & c, polynomial const * p) {
|
||||
return pack(mul_core(c, unpack(p)));
|
||||
}
|
||||
|
||||
polynomial * neg(polynomial const * p) {
|
||||
numeral minus_one;
|
||||
m_manager.set(minus_one, -1);
|
||||
return pack(mul_core(minus_one, unpack(p)));
|
||||
}
|
||||
|
||||
poly_or_num * add_core(numeral const & c, poly_or_num * p) {
|
||||
if (m_manager.is_zero(c)) {
|
||||
return p;
|
||||
}
|
||||
else if (p == 0) {
|
||||
numeral * r = mk_numeral();
|
||||
m_manager.set(*r, c);
|
||||
return to_poly_or_num(r);
|
||||
}
|
||||
else if (is_num(p)) {
|
||||
numeral a;
|
||||
m_manager.add(c, to_num(p), a);
|
||||
if (m_manager.is_zero(a))
|
||||
return 0;
|
||||
numeral * new_arg = mk_numeral();
|
||||
m_manager.swap(*new_arg, a);
|
||||
return to_poly_or_num(new_arg);
|
||||
}
|
||||
else {
|
||||
polynomial * _p = to_poly(p);
|
||||
unsigned sz = _p->size();
|
||||
SASSERT(sz > 1);
|
||||
ptr_buffer<poly_or_num> new_args;
|
||||
new_args.push_back(add_core(c, _p->arg(0)));
|
||||
for (unsigned i = 1; i < sz; i++)
|
||||
new_args.push_back(_p->arg(1));
|
||||
return mk_poly_core(new_args.size(), new_args.c_ptr(), _p->max_var());
|
||||
}
|
||||
}
|
||||
|
||||
polynomial * add(numeral const & c, polynomial const * p) {
|
||||
return pack(add_core(c, unpack(p)));
|
||||
}
|
||||
|
||||
#if 0
|
||||
polynomial * add_lt(polynomial const * p1, polynomial const * p2) {
|
||||
// Add non-constant polynomials p1 and p2 when max_var(p1) < max_var(p2)
|
||||
SASSERT(p1->max_var() != null_var);
|
||||
SASSERT(p2->max_var() != null_var);
|
||||
SASSERT(p1->max_var() < p2->max_var());
|
||||
|
||||
unsigned sz = p2->size();
|
||||
ptr_buffer<poly_or_num> new_args;
|
||||
poly_or_num * pn0 = p2->arg(0);
|
||||
if (pn0 == 0) {
|
||||
new_args.push_back(to_poly_or_num(const_cast<polynomial*>(p1)));
|
||||
}
|
||||
else if (is_num(pn0)) {
|
||||
SASSERT(!is_const(p1));
|
||||
polynomial * new_arg = add(to_num(pn0), p1);
|
||||
SASSERT(!is_zero(new_arg));
|
||||
SASSERT(!is_const(new_arg));
|
||||
new_args.push_back(to_poly_or_num(new_arg));
|
||||
}
|
||||
else {
|
||||
SASSERT(is_poly(pn0));
|
||||
polynomial * new_arg = add(p1, to_poly(pn0));
|
||||
new_args.push_back(to_poly_or_num(new_arg));
|
||||
}
|
||||
for (unsigned i = 1; i < sz; i++)
|
||||
new_args.push_back(p2->arg(i));
|
||||
return mk_poly(sz, new_args.c_ptr(), p2->max_var());
|
||||
}
|
||||
|
||||
polynomial * add(polynomial const * p1, polynomial const * p2) {
|
||||
if (is_zero(p1))
|
||||
return const_cast<polynomial*>(p2);
|
||||
if (is_zero(p2))
|
||||
return const_cast<polynomial*>(p1);
|
||||
var x1 = p1->max_var();
|
||||
var x2 = p2->max_var();
|
||||
if (x1 == null_var) {
|
||||
SASSERT(is_const(p1));
|
||||
return add(to_num(p1->arg(0)), p2);
|
||||
}
|
||||
if (x2 == null_var) {
|
||||
SASSERT(is_const(p2));
|
||||
return add(to_num(p2->arg(0)), p1);
|
||||
}
|
||||
if (x1 < x2)
|
||||
return add_lt(p1, p2);
|
||||
if (x2 < x1)
|
||||
return add_lt(p2, p1);
|
||||
SASSERT(x1 == x2);
|
||||
unsigned sz1 = p1->size();
|
||||
unsigned sz2 = p2->size();
|
||||
unsigned msz = std::min(sz1, sz2);
|
||||
ptr_buffer<poly_or_num> new_args;
|
||||
for (unsigned i = 0; i < msz; i++) {
|
||||
poly_or_num * pn1 = p1->arg(i);
|
||||
poly_or_num * pn2 = p2->arg(i);
|
||||
if (pn1 == 0) {
|
||||
new_args.push_back(pn2);
|
||||
continue;
|
||||
}
|
||||
if (pn2 == 0) {
|
||||
new_args.push_back(pn1);
|
||||
continue;
|
||||
}
|
||||
SASSERT(pn1 != 0 && pn2 != 0);
|
||||
if (is_num(pn1)) {
|
||||
if (is_num(pn2)) {
|
||||
SASSERT(is_num(pn1) && is_num(pn2));
|
||||
numeral a;
|
||||
m_manager.add(to_num(pn1), to_num(pn2), a);
|
||||
if (m_manager.is_zero(a)) {
|
||||
new_args.push_back(0);
|
||||
}
|
||||
else {
|
||||
numeral * new_arg = mk_numeral();
|
||||
m_manager.swap(*new_arg, a);
|
||||
new_args.push_back(to_poly_or_num(new_arg));
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(is_num(pn1) && is_poly(pn2));
|
||||
new_args.push_back(to_poly_or_num(add(to_num(pn1), to_poly(pn2))));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_num(pn2)) {
|
||||
SASSERT(is_poly(pn1) && is_num(pn2));
|
||||
new_args.push_back(to_poly_or_num(add(to_num(pn2), to_poly(pn1))));
|
||||
}
|
||||
else {
|
||||
SASSERT(is_poly(pn1) && is_poly(pn2));
|
||||
new_args.push_back(to_poly_or_num(add(to_poly(pn1), to_poly(pn2))));
|
||||
}
|
||||
}
|
||||
}
|
||||
SASSERT(new_args.size() == sz1 || new_args.size() == sz2);
|
||||
for (unsigned i = msz; i < sz1; i++) {
|
||||
new_args.push_back(p1->arg(i));
|
||||
}
|
||||
for (unsigned i = msz; i < sz2; i++) {
|
||||
new_args.push_back(p2->arg(i));
|
||||
}
|
||||
SASSERT(new_args.size() == std::max(sz1, sz2));
|
||||
return mk_poly(new_args.size(), new_args.c_ptr(), x1);
|
||||
}
|
||||
|
||||
class resetter_mul_buffer;
|
||||
friend class resetter_mul_buffer;
|
||||
class resetter_mul_buffer {
|
||||
imp & m_owner;
|
||||
ptr_buffer<poly_or_num> m_buffer;
|
||||
public:
|
||||
resetter_mul_buffer(imp & o, ptr_buffer<poly_or_num> & b):m_owner(o), m_buffer(b) {}
|
||||
~resetter_mul_buffer() {
|
||||
m_owner.dec_ref_args(m_buffer.size(), m_buffer.c_ptr());
|
||||
m_buffer.reset();
|
||||
}
|
||||
};
|
||||
|
||||
void acc_mul_xk(ptr_buffer<poly_or_num> & mul_buffer, unsigned k, polynomial * p) {
|
||||
if (mul_buffer[k] == 0) {
|
||||
mul_buffer[k] = to_poly_or_num(p);
|
||||
inc_ref(p);
|
||||
}
|
||||
else {
|
||||
polynomial * new_p;
|
||||
if (is_num(mul_buffer[k]))
|
||||
new_p = add(to_num(mul_buffer.get(k)), p);
|
||||
else
|
||||
new_p = add(p, to_poly(mul_buffer.get(k)));
|
||||
if (is_zero(new_p)) {
|
||||
dec_ref(mul_buffer[k]);
|
||||
mul_buffer[k] = 0;
|
||||
}
|
||||
else {
|
||||
inc_ref(new_p);
|
||||
dec_ref(mul_buffer[k]);
|
||||
mul_buffer[k] = to_poly_or_num(new_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void acc_mul_xk(ptr_buffer<poly_or_num> & mul_buffer, unsigned k, numeral & a) {
|
||||
if (mul_buffer.get(k) == 0) {
|
||||
numeral * new_arg = mk_numeral();
|
||||
m_manager.swap(*new_arg, a);
|
||||
mul_buffer[k] = to_poly_or_num(new_arg);
|
||||
}
|
||||
else {
|
||||
if (is_num(mul_buffer[k])) {
|
||||
m_manager.add(to_num(mul_buffer[k]), a, to_num(mul_buffer[k]));
|
||||
if (m_manager.is_zero(to_num(mul_buffer[k]))) {
|
||||
del_numeral(to_num_ptr(mul_buffer[k]));
|
||||
mul_buffer[k] = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
polynomial * new_p = add(a, to_poly(mul_buffer.get(k)));
|
||||
if (is_zero(new_p)) {
|
||||
dec_ref(mul_buffer[k]);
|
||||
mul_buffer[k] = 0;
|
||||
}
|
||||
else {
|
||||
inc_ref(new_p);
|
||||
dec_ref(mul_buffer[k]);
|
||||
mul_buffer[k] = to_poly_or_num(new_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
polynomial * mul_lt(polynomial const * p1, polynomial const * p2) {
|
||||
unsigned sz2 = p2->size();
|
||||
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
polynomial * mul(polynomial const * p1, polynomial const * p2) {
|
||||
var x1 = p1->max_var();
|
||||
var x2 = p2->max_var();
|
||||
if (x1 == null_var) {
|
||||
SASSERT(is_const(p1));
|
||||
return mul(to_num(p1->arg(0)), p2);
|
||||
}
|
||||
if (x2 == null_var) {
|
||||
SASSERT(is_const(p2));
|
||||
return mul(to_num(p2->arg(0)), p1);
|
||||
}
|
||||
if (x1 < x2)
|
||||
return mul_lt(p1, p2);
|
||||
if (x2 < x1)
|
||||
return mul_lt(p2, p1);
|
||||
SASSERT(x1 == x2);
|
||||
if (degree(p1) < degree(p2))
|
||||
std::swap(p1, p2);
|
||||
unsigned sz = degree(p1) * degree(p2) + 1;
|
||||
ptr_buffer<poly_or_num> mul_buffer;
|
||||
resetter_mul_buffer resetter(*this, mul_buffer);
|
||||
mul_buffer.resize(sz);
|
||||
unsigned sz1 = p1->size();
|
||||
unsigned sz2 = p2->size();
|
||||
for (unsigned i1 = 0; i1 < sz1; i1++) {
|
||||
poly_or_num * pn1 = p1->arg(i1);
|
||||
if (pn1 == 0)
|
||||
continue;
|
||||
for (unsigned i2 = 0; i2 < sz2; i2++) {
|
||||
poly_or_num * pn2 = p2->arg(i2);
|
||||
if (pn2 == 0)
|
||||
continue;
|
||||
unsigned i = i1+i2;
|
||||
if (is_num(pn1)) {
|
||||
if (is_num(pn2)) {
|
||||
SASSERT(is_num(pn1) && is_num(pn2));
|
||||
scoped_numeral a(m_manager);
|
||||
m_manager.mul(to_num(pn1), to_num(pn2), a);
|
||||
acc_mul_xk(mul_buffer, i, a);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_num(pn1) && is_poly(pn2));
|
||||
polynomial_ref p(pm());
|
||||
p = mul(to_num(pn1), to_poly(pn2));
|
||||
acc_mul_xk(mul_buffer, i, p);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (is_num(pn2)) {
|
||||
SASSERT(is_poly(pn1) && is_num(pn2));
|
||||
polynomial_ref p(pm());
|
||||
p = mul(to_num(pn2), to_poly(pn1));
|
||||
acc_mul_xk(mul_buffer, i, p);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_poly(pn1) && is_poly(pn2));
|
||||
polynomial_ref p(pm());
|
||||
p = mul(to_poly(pn2), to_poly(pn1));
|
||||
acc_mul_xk(mul_buffer, i, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mk_poly(mul_buffer.size(), mul_buffer.c_ptr(), x1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) {
|
||||
var x = p->max_var();
|
||||
bool first = true;
|
||||
unsigned i = p->size();
|
||||
while (i > 0) {
|
||||
--i;
|
||||
poly_or_num * pn = p->arg(i);
|
||||
if (pn == 0)
|
||||
continue;
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
out << " + ";
|
||||
if (is_num(pn)) {
|
||||
numeral & a = to_num(pn);
|
||||
if (i == 0) {
|
||||
m_manager.display(out, a);
|
||||
}
|
||||
else {
|
||||
if (m_manager.is_one(a)) {
|
||||
proc(out, x);
|
||||
if (i > 1)
|
||||
out << "^" << i;
|
||||
}
|
||||
else {
|
||||
m_manager.display(out, a);
|
||||
if (use_star)
|
||||
out << "*";
|
||||
else
|
||||
out << " ";
|
||||
proc(out, x);
|
||||
if (i > 1)
|
||||
out << "^" << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(is_poly(pn));
|
||||
if (i == 0) {
|
||||
display(out, to_poly(pn), proc, use_star);
|
||||
}
|
||||
else {
|
||||
bool add_paren = false;
|
||||
if (i > 0)
|
||||
add_paren = !is_monomial(to_poly(pn));
|
||||
if (add_paren)
|
||||
out << "(";
|
||||
display(out, to_poly(pn), proc, use_star);
|
||||
if (add_paren)
|
||||
out << ")";
|
||||
if (i > 0) {
|
||||
if (use_star)
|
||||
out << "*";
|
||||
else
|
||||
out << " ";
|
||||
proc(out, x);
|
||||
if (i > 1)
|
||||
out << "^" << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
manager:: manager(numeral_manager & m, small_object_allocator * a) {
|
||||
m_imp = alloc(imp, *this, m, a);
|
||||
}
|
||||
|
||||
manager::~manager() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
bool manager::is_zero(polynomial const * p) {
|
||||
return p == 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool manager::is_const(polynomial const * p) {
|
||||
return imp::is_const(p);
|
||||
}
|
||||
|
||||
bool manager::is_univariate(polynomial const * p) {
|
||||
return imp::is_univariate(p);
|
||||
}
|
||||
|
||||
bool manager::is_monomial(polynomial const * p) const {
|
||||
return m_imp->is_monomial(p);
|
||||
}
|
||||
|
||||
bool manager::eq(polynomial const * p1, polynomial const * p2) {
|
||||
return m_imp->eq(p1, p2);
|
||||
}
|
||||
|
||||
polynomial * manager::mk_zero() {
|
||||
return m_imp->mk_zero();
|
||||
}
|
||||
|
||||
polynomial * manager::mk_const(numeral const & r) {
|
||||
return m_imp->mk_const(r);
|
||||
}
|
||||
|
||||
polynomial * manager::mk_const(rational const & a) {
|
||||
return m_imp->mk_const(a);
|
||||
}
|
||||
|
||||
polynomial * manager::mk_polynomial(var x, unsigned k) {
|
||||
return m_imp->mk_polynomial(x, k);
|
||||
}
|
||||
|
||||
polynomial * manager::mul(numeral const & r, polynomial const * p) {
|
||||
return m_imp->mul(r, p);
|
||||
}
|
||||
|
||||
polynomial * manager::neg(polynomial const * p) {
|
||||
return m_imp->neg(p);
|
||||
}
|
||||
|
||||
polynomial * manager::add(numeral const & r, polynomial const * p) {
|
||||
return m_imp->add(r, p);
|
||||
}
|
||||
|
||||
polynomial * manager::add(polynomial const * p1, polynomial const * p2) {
|
||||
return m_imp->add(p1, p2);
|
||||
}
|
||||
|
||||
var manager::max_var(polynomial const * p) {
|
||||
return p->max_var();
|
||||
}
|
||||
|
||||
unsigned manager::size(polynomial const * p) {
|
||||
return p->size();
|
||||
}
|
||||
|
||||
void manager::display(std::ostream & out, polynomial const * p, display_var_proc const & proc, bool use_star) const {
|
||||
return m_imp->display(out, p, proc, use_star);
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
208
src/math/polynomial/rpolynomial.h
Normal file
208
src/math/polynomial/rpolynomial.h
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
rpolynomial.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Goodies for creating and handling polynomials in dense recursive representation.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-06-11
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _RPOLYNOMIAL_H_
|
||||
#define _RPOLYNOMIAL_H_
|
||||
|
||||
#include"mpz.h"
|
||||
#include"rational.h"
|
||||
#include"obj_ref.h"
|
||||
#include"ref_vector.h"
|
||||
#include"z3_exception.h"
|
||||
#include"polynomial.h"
|
||||
|
||||
namespace rpolynomial {
|
||||
|
||||
typedef polynomial::var var;
|
||||
const var null_var = polynomial::null_var;
|
||||
typedef polynomial::var_vector var_vector;
|
||||
typedef polynomial::display_var_proc display_var_proc;
|
||||
typedef polynomial::polynomial som_polynomial;
|
||||
|
||||
class polynomial;
|
||||
class manager;
|
||||
typedef obj_ref<polynomial, manager> polynomial_ref;
|
||||
typedef ref_vector<polynomial, manager> polynomial_ref_vector;
|
||||
typedef ptr_vector<polynomial> polynomial_vector;
|
||||
|
||||
class manager {
|
||||
public:
|
||||
typedef unsynch_mpz_manager numeral_manager;
|
||||
typedef numeral_manager::numeral numeral;
|
||||
typedef svector<numeral> numeral_vector;
|
||||
typedef _scoped_numeral<numeral_manager> scoped_numeral;
|
||||
typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
|
||||
struct imp;
|
||||
private:
|
||||
imp * m_imp;
|
||||
public:
|
||||
manager(numeral_manager & m, small_object_allocator * a = 0);
|
||||
~manager();
|
||||
|
||||
numeral_manager & m() const;
|
||||
small_object_allocator & allocator() const;
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
/**
|
||||
\brief Create a new variable.
|
||||
*/
|
||||
var mk_var();
|
||||
|
||||
/**
|
||||
\brief Return the number of variables in the manager.
|
||||
*/
|
||||
unsigned num_vars() const;
|
||||
|
||||
/**
|
||||
\brief Return true if x is a valid variable in this manager.
|
||||
*/
|
||||
bool is_valid(var x) const { return x < num_vars(); }
|
||||
|
||||
/**
|
||||
\brief Increment reference counter.
|
||||
*/
|
||||
void inc_ref(polynomial * p);
|
||||
|
||||
/**
|
||||
\brief Decrement reference counter.
|
||||
*/
|
||||
void dec_ref(polynomial * p);
|
||||
|
||||
/**
|
||||
\brief Return true if \c p is the zero polynomial.
|
||||
*/
|
||||
bool is_zero(polynomial const * p);
|
||||
|
||||
/**
|
||||
\brief Return true if p1 == p2.
|
||||
*/
|
||||
bool eq(polynomial const * p1, polynomial const * p2);
|
||||
|
||||
/**
|
||||
\brief Return true if \c p is the constant polynomial.
|
||||
*/
|
||||
static bool is_const(polynomial const * p);
|
||||
|
||||
/**
|
||||
\brief Return true if \c p is an univariate polynomial.
|
||||
*/
|
||||
static bool is_univariate(polynomial const * p);
|
||||
|
||||
/**
|
||||
\brief Return true if \c p is a monomial.
|
||||
*/
|
||||
bool is_monomial(polynomial const * p) const;
|
||||
|
||||
/**
|
||||
\brief Return the maximal variable occurring in p.
|
||||
|
||||
Return null_var if p is a constant polynomial.
|
||||
*/
|
||||
static var max_var(polynomial const * p);
|
||||
|
||||
/**
|
||||
\brief Return the size of the polynomail p.
|
||||
It is the degree(p) on max_var(p) + 1.
|
||||
*/
|
||||
static unsigned size(polynomial const * p);
|
||||
|
||||
/**
|
||||
\brief Return a polynomial h that is the coefficient of max_var(p)^k in p.
|
||||
if p does not contain any monomial containing max_var(p)^k, then return 0.
|
||||
*/
|
||||
polynomial * coeff(polynomial const * p, unsigned k);
|
||||
|
||||
/**
|
||||
\brief Create the zero polynomial.
|
||||
*/
|
||||
polynomial * mk_zero();
|
||||
|
||||
/**
|
||||
\brief Create the constant polynomial \c r.
|
||||
|
||||
\warning r is a number managed by the numeral_manager in the polynomial manager
|
||||
|
||||
\warning r is reset.
|
||||
*/
|
||||
polynomial * mk_const(numeral const & r);
|
||||
|
||||
/**
|
||||
\brief Create the constant polynomial \c r.
|
||||
|
||||
\pre r must be an integer
|
||||
*/
|
||||
polynomial * mk_const(rational const & r);
|
||||
|
||||
/**
|
||||
\brief Create the polynomial x^k
|
||||
*/
|
||||
polynomial * mk_polynomial(var x, unsigned k = 1);
|
||||
|
||||
polynomial * mul(numeral const & r, polynomial const * p);
|
||||
|
||||
polynomial * neg(polynomial const * p);
|
||||
|
||||
polynomial * add(numeral const & r, polynomial const * p);
|
||||
|
||||
polynomial * add(int c, polynomial const * p);
|
||||
|
||||
polynomial * add(polynomial const * p1, polynomial const * p2);
|
||||
|
||||
/**
|
||||
\brief Convert the given polynomial in sum-of-monomials form into a polynomial in dense recursive form.
|
||||
*/
|
||||
polynomial * translate(som_polynomial const * p);
|
||||
|
||||
void display(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
|
||||
|
||||
void display_smt2(std::ostream & out, polynomial const * p, display_var_proc const & proc = display_var_proc()) const;
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & out, polynomial_ref const & p) {
|
||||
p.m().display(out, p);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
typedef rpolynomial::polynomial_ref rpolynomial_ref;
|
||||
typedef rpolynomial::polynomial_ref_vector rpolynomial_ref_vector;
|
||||
|
||||
inline rpolynomial_ref neg(rpolynomial_ref const & p) {
|
||||
rpolynomial::manager & m = p.m();
|
||||
return rpolynomial_ref(m.neg(p), m);
|
||||
}
|
||||
|
||||
inline rpolynomial_ref operator-(rpolynomial_ref const & p) {
|
||||
rpolynomial::manager & m = p.m();
|
||||
return rpolynomial_ref(m.neg(p), m);
|
||||
}
|
||||
|
||||
inline rpolynomial_ref operator+(int a, rpolynomial_ref const & p) {
|
||||
rpolynomial::manager & m = p.m();
|
||||
return rpolynomial_ref(m.add(a, p), m);
|
||||
}
|
||||
|
||||
inline rpolynomial_ref operator+(rpolynomial_ref const & p, int a) {
|
||||
rpolynomial::manager & m = p.m();
|
||||
return rpolynomial_ref(m.add(a, p), m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
112
src/math/polynomial/sexpr2upolynomial.cpp
Normal file
112
src/math/polynomial/sexpr2upolynomial.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sexpr2upolynomial.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
sexpr to upolynomial converter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-28
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"sexpr2upolynomial.h"
|
||||
#include"sexpr.h"
|
||||
|
||||
sexpr2upolynomial_exception::sexpr2upolynomial_exception(char const * msg, sexpr const * s):
|
||||
cmd_exception(msg, s->get_line(), s->get_pos()) {
|
||||
}
|
||||
|
||||
#define MAX_POLYNOMIAL_DEPTH 1 << 16
|
||||
|
||||
// Simple recursive parser
|
||||
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p, unsigned depth) {
|
||||
if (depth > MAX_POLYNOMIAL_DEPTH)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, too complex", s);
|
||||
if (s->is_composite()) {
|
||||
unsigned num = s->get_num_children();
|
||||
if (num == 0)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s);
|
||||
sexpr * h = s->get_child(0);
|
||||
if (!h->is_symbol())
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, symbol expected", s);
|
||||
symbol op = h->get_symbol();
|
||||
if (op == "+") {
|
||||
if (num <= 1)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, '+' operator expects at least one argument", s);
|
||||
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
|
||||
upolynomial::scoped_numeral_vector arg(m);
|
||||
for (unsigned i = 2; i < num; i++) {
|
||||
m.reset(arg);
|
||||
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
|
||||
m.add(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p);
|
||||
}
|
||||
}
|
||||
else if (op == "-") {
|
||||
if (num <= 1)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, '-' operator expects at least one argument", s);
|
||||
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
|
||||
if (num == 2) {
|
||||
m.neg(p);
|
||||
return;
|
||||
}
|
||||
upolynomial::scoped_numeral_vector arg(m);
|
||||
for (unsigned i = 2; i < num; i++) {
|
||||
m.reset(arg);
|
||||
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
|
||||
m.sub(p.size(), p.c_ptr(), arg.size(), arg.c_ptr(), p);
|
||||
}
|
||||
}
|
||||
else if (op == "*") {
|
||||
if (num <= 1)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, '*' operator expects at least one argument", s);
|
||||
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
|
||||
upolynomial::scoped_numeral_vector arg(m);
|
||||
for (unsigned i = 2; i < num; i++) {
|
||||
m.reset(arg);
|
||||
sexpr2upolynomial(m, s->get_child(i), arg, depth+1);
|
||||
m.mul(arg.size(), arg.c_ptr(), p.size(), p.c_ptr(), p);
|
||||
}
|
||||
}
|
||||
else if (op == "^") {
|
||||
if (num != 3)
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, '^' operator expects two arguments", s);
|
||||
sexpr2upolynomial(m, s->get_child(1), p, depth+1);
|
||||
sexpr * arg2 = s->get_child(2);
|
||||
if (!arg2->is_numeral() || !arg2->get_numeral().is_unsigned())
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, exponent must be an unsigned integer", arg2);
|
||||
unsigned k = arg2->get_numeral().get_unsigned();
|
||||
m.pw(p.size(), p.c_ptr(), k, p);
|
||||
}
|
||||
else {
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, '+', '-', '^' or '*' expected", s);
|
||||
}
|
||||
}
|
||||
else if (s->is_numeral()) {
|
||||
// make constant polynomial
|
||||
rational a = s->get_numeral();
|
||||
if (!a.is_int())
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, integer coefficient expected", s);
|
||||
m.set(1, &a, p);
|
||||
}
|
||||
else if (s->is_symbol()) {
|
||||
if (s->get_symbol() != "x")
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, variable 'x' expected", s);
|
||||
// make identity
|
||||
rational as[2] = { rational(0), rational(1) };
|
||||
m.set(2, as, p);
|
||||
}
|
||||
else {
|
||||
throw sexpr2upolynomial_exception("invalid univariate polynomial, unexpected ", s);
|
||||
}
|
||||
}
|
||||
|
||||
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p) {
|
||||
sexpr2upolynomial(m, s, p, 0);
|
||||
}
|
33
src/math/polynomial/sexpr2upolynomial.h
Normal file
33
src/math/polynomial/sexpr2upolynomial.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sexpr2upolynomial.h
|
||||
|
||||
Abstract:
|
||||
|
||||
sexpr to upolynomial converter
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-28
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _SEXPR2UPOLYNOMIAL_H_
|
||||
#define _SEXPR2UPOLYNOMIAL_H_
|
||||
|
||||
#include"upolynomial.h"
|
||||
#include"cmd_context_types.h"
|
||||
class sexpr;
|
||||
|
||||
class sexpr2upolynomial_exception : public cmd_exception {
|
||||
public:
|
||||
sexpr2upolynomial_exception(char const * msg, sexpr const * s);
|
||||
};
|
||||
|
||||
void sexpr2upolynomial(upolynomial::manager & m, sexpr const * s, upolynomial::numeral_vector & p);
|
||||
|
||||
#endif
|
3131
src/math/polynomial/upolynomial.cpp
Normal file
3131
src/math/polynomial/upolynomial.cpp
Normal file
File diff suppressed because it is too large
Load diff
922
src/math/polynomial/upolynomial.h
Normal file
922
src/math/polynomial/upolynomial.h
Normal file
|
@ -0,0 +1,922 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
upolynomial.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Goodies for creating and handling univariate polynomials.
|
||||
|
||||
A dense representation is much better for Root isolation algorithms,
|
||||
encoding algebraic numbers, factorization, etc.
|
||||
|
||||
We also use integers as the coefficients of univariate polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-11-29
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _UPOLYNOMIAL_H_
|
||||
#define _UPOLYNOMIAL_H_
|
||||
|
||||
#include"mpzzp.h"
|
||||
#include"rational.h"
|
||||
#include"polynomial.h"
|
||||
#include"z3_exception.h"
|
||||
#include"mpbq.h"
|
||||
#define FACTOR_VERBOSE_LVL 1000
|
||||
|
||||
namespace upolynomial {
|
||||
|
||||
typedef polynomial::factor_params factor_params;
|
||||
|
||||
// It is used only for signing cancellation.
|
||||
class upolynomial_exception : public default_exception {
|
||||
public:
|
||||
upolynomial_exception(char const * msg):default_exception(msg) {}
|
||||
};
|
||||
|
||||
typedef mpz numeral;
|
||||
typedef mpzzp_manager numeral_manager;
|
||||
typedef mpzzp_manager zp_numeral_manager;
|
||||
typedef unsynch_mpz_manager z_numeral_manager;
|
||||
typedef svector<numeral> numeral_vector;
|
||||
|
||||
class core_manager {
|
||||
public:
|
||||
typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
|
||||
typedef _scoped_numeral<numeral_manager> scoped_numeral;
|
||||
/**
|
||||
\brief Convenient vector of polynomials that manages its own memory and keeps the degree, of each polynomial.
|
||||
Polynomial is c*f_1^k_1*...*f_n^k_n.
|
||||
*/
|
||||
class factors {
|
||||
private:
|
||||
vector<numeral_vector> m_factors;
|
||||
svector<unsigned> m_degrees;
|
||||
core_manager & m_upm;
|
||||
numeral m_constant;
|
||||
unsigned m_total_factors;
|
||||
unsigned m_total_degree;
|
||||
public:
|
||||
factors(core_manager & upm);
|
||||
~factors();
|
||||
|
||||
core_manager & upm() const { return m_upm; }
|
||||
core_manager & pm() const { return m_upm; }
|
||||
numeral_manager & nm() const;
|
||||
|
||||
unsigned distinct_factors() const { return m_factors.size(); }
|
||||
unsigned total_factors() const { return m_total_factors; }
|
||||
void clear();
|
||||
void reset() { clear(); }
|
||||
|
||||
numeral_vector const & operator[](unsigned i) const { return m_factors[i]; }
|
||||
|
||||
numeral const & get_constant() const { return m_constant; }
|
||||
void set_constant(numeral const & constant);
|
||||
|
||||
unsigned get_degree() const { return m_total_degree; }
|
||||
unsigned get_degree(unsigned i) const { return m_degrees[i]; }
|
||||
void set_degree(unsigned i, unsigned degree);
|
||||
void push_back(numeral_vector const & p, unsigned degree);
|
||||
// push p to vectors and kill it
|
||||
void push_back_swap(numeral_vector & p, unsigned degree);
|
||||
|
||||
void swap_factor(unsigned i, numeral_vector & p);
|
||||
void swap(factors & other);
|
||||
void multiply(numeral_vector & out) const;
|
||||
|
||||
void display(std::ostream & out) const;
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & out, factors const & fs) {
|
||||
fs.display(out);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
numeral_manager m_manager;
|
||||
numeral_vector m_basic_tmp;
|
||||
numeral_vector m_div_tmp1;
|
||||
numeral_vector m_div_tmp2;
|
||||
numeral_vector m_exact_div_tmp;
|
||||
numeral_vector m_gcd_tmp1;
|
||||
numeral_vector m_gcd_tmp2;
|
||||
numeral_vector m_CRA_tmp;
|
||||
#define UPOLYNOMIAL_MGCD_TMPS 6
|
||||
numeral_vector m_mgcd_tmp[UPOLYNOMIAL_MGCD_TMPS];
|
||||
numeral_vector m_sqf_tmp1;
|
||||
numeral_vector m_sqf_tmp2;
|
||||
numeral_vector m_pw_tmp;
|
||||
volatile bool m_cancel;
|
||||
|
||||
static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != 0 && buffer.c_ptr() == p; }
|
||||
void neg_core(unsigned sz1, numeral const * p1, numeral_vector & buffer);
|
||||
void add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
void sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
void mul_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
|
||||
void flip_sign_if_lm_neg(numeral_vector & buffer);
|
||||
|
||||
void mod_gcd(unsigned sz_u, numeral const * u, unsigned sz_v, numeral const * v, numeral_vector & result);
|
||||
void CRA_combine_images(numeral_vector const & q, numeral const & p, numeral_vector & C, numeral & bound);
|
||||
|
||||
public:
|
||||
core_manager(z_numeral_manager & m);
|
||||
~core_manager();
|
||||
|
||||
z_numeral_manager & zm() const { return m_manager.m(); }
|
||||
numeral_manager & m() const { return const_cast<core_manager*>(this)->m_manager; }
|
||||
|
||||
/**
|
||||
\brief Return true if Z_p[X]
|
||||
*/
|
||||
bool modular() const { return m().modular(); }
|
||||
bool field() const { return m().field(); }
|
||||
/**
|
||||
\brief Return p in Z_p[X]
|
||||
\pre modular
|
||||
*/
|
||||
numeral const & p() const { return m().p(); }
|
||||
/**
|
||||
\brief Set manager as Z[X]
|
||||
*/
|
||||
void set_z() { m().set_z(); }
|
||||
/**
|
||||
\brief Set manager as Z_p[X]
|
||||
*/
|
||||
void set_zp(numeral const & p) { m().set_zp(p); }
|
||||
void set_zp(uint64 p) { m().set_zp(p); }
|
||||
|
||||
void checkpoint();
|
||||
|
||||
void set_cancel(bool f);
|
||||
|
||||
/**
|
||||
\brief set p size to 0. That is, p is the zero polynomial after this operation.
|
||||
*/
|
||||
void reset(numeral_vector & p);
|
||||
|
||||
/**
|
||||
\brief Remove zero leading coefficients.
|
||||
After applying this method, we have that p is empty() or p[p.size()-1] is not zero.
|
||||
*/
|
||||
void trim(numeral_vector & p);
|
||||
|
||||
void set_size(unsigned sz, numeral_vector & buffer);
|
||||
|
||||
/**
|
||||
\brief Return the actual degree of p.
|
||||
*/
|
||||
unsigned degree(numeral_vector const & p) {
|
||||
unsigned sz = p.size();
|
||||
return sz == 0 ? 0 : sz - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if p is the zero polynomial.
|
||||
*/
|
||||
bool is_zero(numeral_vector & p) { trim(p); return p.empty(); }
|
||||
|
||||
/**
|
||||
\brief Return true if p is a constant polynomial
|
||||
*/
|
||||
bool is_const(numeral_vector & p) { trim(p); return p.size() <= 1; }
|
||||
|
||||
/**
|
||||
\brief Copy p to buffer.
|
||||
*/
|
||||
void set(unsigned sz, numeral const * p, numeral_vector & buffer);
|
||||
void set(numeral_vector & target, numeral_vector const & source) { set(source.size(), source.c_ptr(), target); }
|
||||
|
||||
/**
|
||||
\brief Copy p to buffer.
|
||||
|
||||
Coefficients of p must be integer.
|
||||
*/
|
||||
void set(unsigned sz, rational const * p, numeral_vector & buffer);
|
||||
|
||||
/**
|
||||
\brief Compute the primitive part and the content of f (pp can alias f).
|
||||
*/
|
||||
void get_primitive_and_content(unsigned f_sz, numeral const * f, numeral_vector & pp, numeral & cont);
|
||||
void get_primitive_and_content(numeral_vector const & f, numeral_vector & pp, numeral & cont) {
|
||||
get_primitive_and_content(f.size(), f.c_ptr(), pp, cont);
|
||||
}
|
||||
void get_primitive(numeral_vector const & f, numeral_vector & pp) {
|
||||
scoped_numeral cont(m());
|
||||
get_primitive_and_content(f.size(), f.c_ptr(), pp, cont);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief p := -p
|
||||
*/
|
||||
void neg(unsigned sz, numeral * p);
|
||||
void neg(numeral_vector & p) { neg(p.size(), p.c_ptr()); }
|
||||
|
||||
/**
|
||||
\brief buffer := -p
|
||||
*/
|
||||
void neg(unsigned sz, numeral const * p, numeral_vector & buffer);
|
||||
void neg(numeral_vector const & p, numeral_vector & p_neg) { neg(p.size(), p.c_ptr(), p_neg); }
|
||||
|
||||
/**
|
||||
\brief buffer := p1 + p2
|
||||
*/
|
||||
void add(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
void add(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { add(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
|
||||
|
||||
/**
|
||||
\brief buffer := p1 - p2
|
||||
*/
|
||||
void sub(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
void sub(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { sub(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
|
||||
|
||||
/**
|
||||
\brief buffer := p1 * p2
|
||||
*/
|
||||
void mul(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer);
|
||||
void mul(numeral_vector const & a, numeral_vector const & b, numeral_vector & c) { mul(a.size(), a.c_ptr(), b.size(), b.c_ptr(), c); }
|
||||
|
||||
/**
|
||||
\brief r := p^k
|
||||
*/
|
||||
void pw(unsigned sz, numeral const * p, unsigned k, numeral_vector & r);
|
||||
|
||||
/**
|
||||
\brief buffer := dp/dx
|
||||
*/
|
||||
void derivative(unsigned sz1, numeral const * p, numeral_vector & buffer);
|
||||
void derivative(numeral_vector const & p, numeral_vector & d_p) { derivative(p.size(), p.c_ptr(), d_p); }
|
||||
|
||||
/**
|
||||
\brief Divide coeffients of p by their GCD
|
||||
*/
|
||||
void normalize(unsigned sz, numeral * p);
|
||||
|
||||
/**
|
||||
\brief Divide coeffients of p by their GCD
|
||||
*/
|
||||
void normalize(numeral_vector & p);
|
||||
|
||||
/**
|
||||
\brief Divide the coefficients of p by b.
|
||||
This method assumes that every coefficient of p is a multiple of b, and b != 0.
|
||||
*/
|
||||
void div(unsigned sz, numeral * p, numeral const & b);
|
||||
void div(numeral_vector & p, numeral const & b) { div(p.size(), p.c_ptr(), b); }
|
||||
|
||||
/**
|
||||
\brief Multiply the coefficients of p by b.
|
||||
|
||||
This method assume b != 0.
|
||||
*/
|
||||
void mul(unsigned sz, numeral * p, numeral const & b);
|
||||
|
||||
/**
|
||||
\brief Multiply the coefficients of p by b.
|
||||
If b == 0, it is equivalent to a reset()
|
||||
*/
|
||||
void mul(numeral_vector & p, numeral const & b);
|
||||
|
||||
/**
|
||||
\brief Similar to div_rem but p1 and p2 must not be q and r.
|
||||
*/
|
||||
void div_rem_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
|
||||
|
||||
/**
|
||||
\brief If numeral is a field, then
|
||||
return q and r s.t. p1 = q*p2 + r
|
||||
And degree(r) < degree(p2).
|
||||
|
||||
If numeral is not a field, then
|
||||
return q and r s.t. (b_m)^d * p1 = q * p2 + r
|
||||
where b_m is the leading coefficient of p2 and d <= sz1 - sz2 + 1
|
||||
if sz1 >= sz2.
|
||||
|
||||
The output value d is irrelevant if numeral is a field.
|
||||
*/
|
||||
void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & q, numeral_vector & r);
|
||||
|
||||
/**
|
||||
\see div_rem
|
||||
*/
|
||||
void div_rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q, numeral_vector & r) {
|
||||
unsigned d = 0;
|
||||
div_rem(sz1, p1, sz2, p2, d, q, r);
|
||||
}
|
||||
|
||||
void div_rem(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q, numeral_vector & r) {
|
||||
div_rem(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q, r);
|
||||
}
|
||||
|
||||
/**
|
||||
\see div_rem
|
||||
*/
|
||||
void div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q);
|
||||
|
||||
/**
|
||||
\see div_rem
|
||||
*/
|
||||
void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, unsigned & d, numeral_vector & r);
|
||||
|
||||
/**
|
||||
\see div_rem
|
||||
*/
|
||||
void rem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r) {
|
||||
unsigned d = 0;
|
||||
rem(sz1, p1, sz2, p2, d, r);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Signed pseudo-remainder.
|
||||
Alias for rem(sz1, p1, sz2, p2, r); neg(r);
|
||||
*/
|
||||
void srem(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & r);
|
||||
|
||||
/**
|
||||
\brief Return true if p2 divides p1.
|
||||
*/
|
||||
bool divides(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2);
|
||||
bool divides(numeral_vector const & p1, numeral_vector const & p2) { return divides(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); }
|
||||
|
||||
/**
|
||||
\brief Return true if p2 divides p1, and store the quotient in q.
|
||||
*/
|
||||
bool exact_div(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & q);
|
||||
bool exact_div(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & q) {
|
||||
return exact_div(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), q);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Assuming that we can, make the polynomial monic by dividing with the leading coefficient. It
|
||||
puts the leading coefficient into lc, and it's inverse into lc_inv.
|
||||
*/
|
||||
void mk_monic(unsigned sz, numeral * p, numeral & lc, numeral & lc_inv);
|
||||
void mk_monic(unsigned sz, numeral * p, numeral & lc) { numeral lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc_inv); }
|
||||
void mk_monic(unsigned sz, numeral * p) { numeral lc, lc_inv; mk_monic(sz, p, lc, lc_inv); m().del(lc); m().del(lc_inv); }
|
||||
void mk_monic(numeral_vector & p) { mk_monic(p.size(), p.c_ptr()); }
|
||||
|
||||
/**
|
||||
\brief g := gcd(p1, p2)
|
||||
If in a field the coefficients don't matter, so we also make sure that D is monic.
|
||||
*/
|
||||
void gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
|
||||
void euclid_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
|
||||
void subresultant_gcd(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & g);
|
||||
void gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) {
|
||||
gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g);
|
||||
}
|
||||
void subresultant_gcd(numeral_vector const & p1, numeral_vector const & p2, numeral_vector & g) {
|
||||
subresultant_gcd(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr(), g);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief g := square free part of p
|
||||
*/
|
||||
void square_free(unsigned sz, numeral const * p, numeral_vector & g);
|
||||
|
||||
/**
|
||||
\brief Return true if p is a square-free polynomial.
|
||||
*/
|
||||
bool is_square_free(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return true if p is a square-free polynomial.
|
||||
*/
|
||||
bool is_square_free(numeral_vector const & p) {
|
||||
return is_square_free(p.size(), p.c_ptr());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Convert a multi-variate polynomial (that is) actually representing an univariate polynomial
|
||||
into a vector of coefficients.
|
||||
*/
|
||||
template<typename polynomial_ref>
|
||||
void to_numeral_vector(polynomial_ref const & p, numeral_vector & r) {
|
||||
typename polynomial_ref::manager & pm = p.m();
|
||||
SASSERT(pm.is_univariate(p));
|
||||
polynomial_ref np(pm);
|
||||
np = pm.normalize(p);
|
||||
unsigned sz = pm.size(p);
|
||||
unsigned deg = pm.total_degree(p);
|
||||
r.reserve(deg+1);
|
||||
for (unsigned i = 0; i <= deg; i++) {
|
||||
m().reset(r[i]);
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
unsigned k = pm.total_degree(pm.get_monomial(p, i));
|
||||
SASSERT(k <= deg);
|
||||
m().set(r[k], pm.coeff(p, i));
|
||||
}
|
||||
set_size(deg+1, r);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Convert a multi-variate polynomial in [x, y1, ..., yn] to a univariate polynomial in just x
|
||||
by removing everything multivariate.
|
||||
*/
|
||||
template<typename polynomial_ref>
|
||||
void to_numeral_vector(polynomial_ref const & p, polynomial::var x, numeral_vector & r) {
|
||||
typename polynomial_ref::manager & pm = p.m();
|
||||
polynomial_ref np(pm);
|
||||
np = pm.normalize(p);
|
||||
unsigned sz = pm.size(p);
|
||||
unsigned deg = pm.degree(p, x);
|
||||
r.reserve(deg+1);
|
||||
for (unsigned i = 0; i <= deg; i++) {
|
||||
m().reset(r[i]);
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
typename polynomial::monomial * mon = pm.get_monomial(p, i);
|
||||
if (pm.size(mon) == 0) {
|
||||
m().set(r[0], pm.coeff(p, i));
|
||||
} else if (pm.size(mon) == 1 && pm.get_var(mon, 0) == x) {
|
||||
unsigned m_deg_x = pm.degree(mon, 0);
|
||||
m().set(r[m_deg_x], pm.coeff(p, i));
|
||||
}
|
||||
}
|
||||
set_size(deg+1, r);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Extended GCD
|
||||
This method assumes that numeral is a field.
|
||||
It determines U, V, D such that
|
||||
A*U + B*V = D and D is the GCD of A and B.
|
||||
Since in a field the coefficients don't matter, we also make sure that D is monic.
|
||||
*/
|
||||
void ext_gcd(unsigned szA, numeral const * A, unsigned szB, numeral const * B, numeral_vector & U, numeral_vector & V, numeral_vector & D);
|
||||
void ext_gcd(numeral_vector const & A, numeral_vector const & B, numeral_vector & U, numeral_vector & V, numeral_vector & D) {
|
||||
ext_gcd(A.size(), A.c_ptr(), B.size(), B.c_ptr(), U, V, D);
|
||||
}
|
||||
|
||||
bool eq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2);
|
||||
bool eq(numeral_vector const & p1, numeral_vector const & p2) { return eq(p1.size(), p1.c_ptr(), p2.size(), p2.c_ptr()); }
|
||||
|
||||
void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const;
|
||||
void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const { display(out, p.size(), p.c_ptr(), var_name); }
|
||||
void display_star(std::ostream & out, unsigned sz, numeral const * p) { display(out, sz, p, "x", true); }
|
||||
void display_star(std::ostream & out, numeral_vector const & p) { display_star(out, p.size(), p.c_ptr()); }
|
||||
|
||||
void display_smt2(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x") const;
|
||||
void display_smt2(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
|
||||
return display_smt2(out, p.size(), p.c_ptr(), var_name);
|
||||
}
|
||||
};
|
||||
|
||||
class scoped_set_z {
|
||||
core_manager & m;
|
||||
bool m_modular;
|
||||
core_manager::scoped_numeral m_p;
|
||||
public:
|
||||
scoped_set_z(core_manager & _m):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_z(); }
|
||||
~scoped_set_z() { if (m_modular) m.set_zp(m_p); }
|
||||
};
|
||||
|
||||
class scoped_set_zp {
|
||||
core_manager & m;
|
||||
bool m_modular;
|
||||
core_manager::scoped_numeral m_p;
|
||||
public:
|
||||
scoped_set_zp(core_manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); }
|
||||
scoped_set_zp(core_manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); }
|
||||
~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); }
|
||||
};
|
||||
|
||||
class manager;
|
||||
|
||||
typedef core_manager z_manager;
|
||||
typedef core_manager zp_manager;
|
||||
|
||||
typedef z_manager::factors factors;
|
||||
typedef zp_manager::factors zp_factors;
|
||||
|
||||
typedef svector<numeral> numeral_vector;
|
||||
|
||||
class scoped_numeral_vector : public _scoped_numeral_vector<numeral_manager> {
|
||||
public:
|
||||
scoped_numeral_vector(numeral_manager & m):_scoped_numeral_vector<numeral_manager>(m) {}
|
||||
scoped_numeral_vector(manager & m);
|
||||
};
|
||||
|
||||
class upolynomial_sequence {
|
||||
numeral_vector m_seq_coeffs; // coefficients of all polynomials in the sequence
|
||||
unsigned_vector m_begins; // start position (in m_seq_coeffs) of each polynomial in the sequence
|
||||
unsigned_vector m_szs; // size of each polynomial in the sequence
|
||||
friend class manager;
|
||||
public:
|
||||
/**
|
||||
\brief Add a new polynomial to the sequence.
|
||||
The contents of p is erased.
|
||||
*/
|
||||
void push(unsigned sz, numeral * p);
|
||||
|
||||
/**
|
||||
\brief Add a new polynomial to the sequence.
|
||||
The contents of p is preserved.
|
||||
*/
|
||||
void push(numeral_manager & m, unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return the number of polynomials in the sequence.
|
||||
*/
|
||||
unsigned size() const { return m_szs.size(); }
|
||||
|
||||
/**
|
||||
\brief Return the vector of coefficients for the i-th polynomial in the sequence.
|
||||
*/
|
||||
numeral const * coeffs(unsigned i) const { return m_seq_coeffs.c_ptr() + m_begins[i]; }
|
||||
|
||||
/**
|
||||
\brief Return the size of the i-th polynomial in the sequence.
|
||||
*/
|
||||
unsigned size(unsigned i) const { return m_szs[i]; }
|
||||
};
|
||||
|
||||
class scoped_upolynomial_sequence : public upolynomial_sequence {
|
||||
manager & m_manager;
|
||||
public:
|
||||
scoped_upolynomial_sequence(manager & m):m_manager(m) {}
|
||||
~scoped_upolynomial_sequence();
|
||||
};
|
||||
|
||||
class manager : public core_manager {
|
||||
numeral_vector m_db_tmp;
|
||||
numeral_vector m_dbab_tmp1;
|
||||
numeral_vector m_dbab_tmp2;
|
||||
numeral_vector m_tr_tmp;
|
||||
numeral_vector m_push_tmp;
|
||||
|
||||
int sign_of(numeral const & c);
|
||||
struct drs_frame;
|
||||
void pop_top_frame(numeral_vector & p_stack, svector<drs_frame> & frame_stack);
|
||||
void push_child_frames(unsigned sz, numeral const * p, numeral_vector & p_stack, svector<drs_frame> & frame_stack);
|
||||
void add_isolating_interval(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
void add_root(svector<drs_frame> const & frame_stack, mpbq_manager & bqm, mpbq_vector & roots);
|
||||
void drs_isolate_0_1_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
void drs_isolate_roots(unsigned sz, numeral * p, numeral & U, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
void drs_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
void sqf_nz_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
void sturm_seq_core(upolynomial_sequence & seq);
|
||||
enum location { PLUS_INF, MINUS_INF, ZERO, MPBQ };
|
||||
template<location loc>
|
||||
unsigned sign_variations_at_core(upolynomial_sequence const & seq, mpbq const & b);
|
||||
|
||||
void flip_sign(factors & r);
|
||||
void flip_factor_sign_if_lm_neg(numeral_vector & p, factors & r, unsigned k);
|
||||
void factor_2_sqf_pp(numeral_vector & p, factors & r, unsigned k);
|
||||
bool factor_sqf_pp(numeral_vector & p, factors & r, unsigned k, factor_params const & params);
|
||||
bool factor_core(unsigned sz, numeral const * p, factors & r, factor_params const & params);
|
||||
|
||||
public:
|
||||
manager(z_numeral_manager & m):core_manager(m) {}
|
||||
~manager();
|
||||
|
||||
void reset(numeral_vector & p) { core_manager::reset(p); }
|
||||
|
||||
void reset(upolynomial_sequence & seq);
|
||||
|
||||
/**
|
||||
\brief Return true if 0 is a root of p.
|
||||
*/
|
||||
bool has_zero_roots(unsigned sz, numeral const * p) { SASSERT(sz > 0); return m().is_zero(p[0]); }
|
||||
|
||||
/**
|
||||
\brief Store in buffer a polynomial that has the same roots of p but the zero roots.
|
||||
We have that:
|
||||
forall u, p(u) = 0 and u != 0 implies buffer(u) = 0
|
||||
forall u, buffer(u) = 0 implies p(u) = 0
|
||||
|
||||
This method assumes p is not the zero polynomial
|
||||
*/
|
||||
void remove_zero_roots(unsigned sz, numeral const * p, numeral_vector & buffer);
|
||||
|
||||
/**
|
||||
\brief Return true if 1/2 is a root of p.
|
||||
*/
|
||||
bool has_one_half_root(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Store in buffer a polynomial that has the same roots of p, but a 1/2 root is removed.
|
||||
|
||||
This method assumes that 1/2 is a root of p.
|
||||
*/
|
||||
void remove_one_half_root(unsigned sz, numeral const * p, numeral_vector & buffer);
|
||||
|
||||
/**
|
||||
\brief Return the number of sign changes in the coefficients of p.
|
||||
Zero coefficients are ignored.
|
||||
*/
|
||||
unsigned sign_changes(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return the descartes bound for the number of roots of p in the interval (0, +oo)
|
||||
|
||||
Result:
|
||||
0 - p has no roots in (0,1)
|
||||
1 - p has one root in (0,1)
|
||||
>1 - p has more than one root in (0,1)
|
||||
*/
|
||||
unsigned descartes_bound(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return the descartes bound for the number of roots of p in the interval (0, 1)
|
||||
|
||||
\see descartes_bound
|
||||
*/
|
||||
unsigned descartes_bound_0_1(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return the descartes bound for the number of roots of p in the interval (a, b)
|
||||
|
||||
\see descartes_bound
|
||||
*/
|
||||
unsigned descartes_bound_a_b(unsigned sz, numeral const * p, mpbq_manager & m, mpbq const & a, mpbq const & b);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(x+1)
|
||||
*/
|
||||
void translate(unsigned sz, numeral * p);
|
||||
void translate(unsigned sz, numeral const * p, numeral_vector & buffer) { set(sz, p, buffer); translate(sz, buffer.c_ptr()); }
|
||||
|
||||
/**
|
||||
\brief p(x) := p(x+2^k)
|
||||
*/
|
||||
void translate_k(unsigned sz, numeral * p, unsigned k);
|
||||
void translate_k(unsigned sz, numeral const * p, unsigned k, numeral_vector & buffer) { set(sz, p, buffer); translate_k(sz, buffer.c_ptr(), k); }
|
||||
|
||||
/**
|
||||
\brief p(x) := p(x+c)
|
||||
*/
|
||||
void translate_z(unsigned sz, numeral * p, numeral const & c);
|
||||
void translate_z(unsigned sz, numeral const * p, numeral const & c, numeral_vector & buffer) { set(sz, p, buffer); translate_z(sz, buffer.c_ptr(), c); }
|
||||
|
||||
/**
|
||||
\brief p(x) := p(x+b) where b = c/2^k
|
||||
buffer := (2^k)^n * p(x + c/(2^k))
|
||||
*/
|
||||
void translate_bq(unsigned sz, numeral * p, mpbq const & b);
|
||||
void translate_bq(unsigned sz, numeral const * p, mpbq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_bq(sz, buffer.c_ptr(), b); }
|
||||
|
||||
/**
|
||||
\brief p(x) := p(x+b) where b = c/d
|
||||
buffer := d^n * p(x + c/d)
|
||||
*/
|
||||
void translate_q(unsigned sz, numeral * p, mpq const & b);
|
||||
void translate_q(unsigned sz, numeral const * p, mpq const & b, numeral_vector & buffer) { set(sz, p, buffer); translate_q(sz, buffer.c_ptr(), b); }
|
||||
|
||||
/**
|
||||
\brief p(x) := 2^n*p(x/2) where n = sz-1
|
||||
*/
|
||||
void compose_2n_p_x_div_2(unsigned sz, numeral * p);
|
||||
|
||||
/**
|
||||
\brief p(x) := (2^k)^n * p(x/(2^k))
|
||||
*/
|
||||
void compose_2kn_p_x_div_2k(unsigned sz, numeral * p, unsigned k);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(2^k * x)
|
||||
|
||||
If u is a root of old(p), then u/2^k is a root of p
|
||||
*/
|
||||
void compose_p_2k_x(unsigned sz, numeral * p, unsigned k);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(b * x)
|
||||
|
||||
If u is a root of old(p), then u/b is a root of p
|
||||
*/
|
||||
void compose_p_b_x(unsigned sz, numeral * p, numeral const & b);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(b * x)
|
||||
|
||||
If u is a root of old(p), then u/b is a root of p
|
||||
|
||||
Let b be of the form c/(2^k), then this operation is equivalent to:
|
||||
(2^k)^n*p(c*x/(2^k))
|
||||
|
||||
Let old(p) be of the form:
|
||||
a_n * x^n + a_{n-1}*x^{n-1} + ... + a_1 * x + a_0
|
||||
|
||||
Then p is of the form:
|
||||
a_n * c^n * x^n + a_{n-1} * c^{n-1} * 2^k * x^{n-1} + ... + a_1 * c * (2^k)^(n-1) * x + a_0
|
||||
*/
|
||||
void compose_p_b_x(unsigned sz, numeral * p, mpbq const & b);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(q*x)
|
||||
*/
|
||||
void compose_p_q_x(unsigned sz, numeral * p, mpq const & q);
|
||||
|
||||
/**
|
||||
\brief p(x) := a^n * p(x/a)
|
||||
*/
|
||||
void compose_an_p_x_div_a(unsigned sz, numeral * p, numeral const & a);
|
||||
|
||||
/**
|
||||
\brief p(x) := p(-x)
|
||||
*/
|
||||
void p_minus_x(unsigned sz, numeral * p);
|
||||
|
||||
/**
|
||||
\brief p(x) := x^n * p(1/x)
|
||||
*/
|
||||
void p_1_div_x(unsigned sz, numeral * p);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(b)
|
||||
*/
|
||||
int eval_sign_at(unsigned sz, numeral const * p, mpbq const & b);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(b)
|
||||
*/
|
||||
int eval_sign_at(unsigned sz, numeral const * p, mpq const & b);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(b)
|
||||
*/
|
||||
int eval_sign_at(unsigned sz, numeral const * p, mpz const & b);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(0)
|
||||
*/
|
||||
int eval_sign_at_zero(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(+oo)
|
||||
*/
|
||||
int eval_sign_at_plus_inf(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign of p(-oo)
|
||||
*/
|
||||
int eval_sign_at_minus_inf(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign variations in the polynomial sequence at -oo
|
||||
*/
|
||||
unsigned sign_variations_at_minus_inf(upolynomial_sequence const & seq);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign variations in the polynomial sequence at +oo
|
||||
*/
|
||||
unsigned sign_variations_at_plus_inf(upolynomial_sequence const & seq);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign variations in the polynomial sequence at 0
|
||||
*/
|
||||
unsigned sign_variations_at_zero(upolynomial_sequence const & seq);
|
||||
|
||||
/**
|
||||
\brief Evaluate the sign variations in the polynomial sequence at b
|
||||
*/
|
||||
unsigned sign_variations_at(upolynomial_sequence const & seq, mpbq const & b);
|
||||
|
||||
/**
|
||||
\brief Return an upper bound U for all roots of p.
|
||||
U is a positive value.
|
||||
We have that if u is a root of p, then |u| < U
|
||||
*/
|
||||
void root_upper_bound(unsigned sz, numeral const * p, numeral & U);
|
||||
|
||||
unsigned knuth_positive_root_upper_bound(unsigned sz, numeral const * p);
|
||||
unsigned knuth_negative_root_upper_bound(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Return k s.t. for any nonzero root alpha of p(x):
|
||||
|alpha| > 1/2^k
|
||||
*/
|
||||
unsigned nonzero_root_lower_bound(unsigned sz, numeral const * p);
|
||||
|
||||
/**
|
||||
\brief Isolate roots of a square free polynomial p.
|
||||
The result is stored in three vectors: roots, lowers and uppers.
|
||||
The vector roots contains actual roots of p.
|
||||
The vectors lowers and uppers have the same size, and
|
||||
For all i in [0, lowers.size()), we have that there is only and only one root of p in the interval (lowers[i], uppers[i]).
|
||||
Every root of p in roots or in an interval (lowers[i], uppers[i])
|
||||
|
||||
The total number of roots of p is roots.size() + lowers.size()
|
||||
|
||||
\pre p is not the zero polynomial, that is, sz > 0
|
||||
*/
|
||||
void sqf_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
|
||||
/**
|
||||
\brief Isolate roots of an arbitrary polynomial p.
|
||||
|
||||
\see sqf_isolate_roots.
|
||||
|
||||
\pre p is not the zero polynomial, that is, sz > 0
|
||||
*/
|
||||
void isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
|
||||
void drs_isolate_roots(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k,
|
||||
mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
|
||||
void sturm_isolate_roots_core(unsigned sz, numeral * p, unsigned neg_k, unsigned pos_k,
|
||||
mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
|
||||
void sturm_isolate_roots(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq_vector & roots, mpbq_vector & lowers, mpbq_vector & uppers);
|
||||
|
||||
/**
|
||||
\brief Compute the sturm sequence for p1 and p2.
|
||||
*/
|
||||
void sturm_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
|
||||
|
||||
/**
|
||||
\brief Compute the sturm sequence for p and p'.
|
||||
*/
|
||||
void sturm_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
|
||||
|
||||
/**
|
||||
\brief Compute the sturm tarski sequence for p1 and p1'*p2.
|
||||
*/
|
||||
void sturm_tarski_seq(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, upolynomial_sequence & seq);
|
||||
|
||||
/**
|
||||
\brief Compute the Fourier sequence for p.
|
||||
*/
|
||||
void fourier_seq(unsigned sz, numeral const * p, upolynomial_sequence & seq);
|
||||
|
||||
/**
|
||||
\brief Convert an isolating interval into a refinable one.
|
||||
See comments in upolynomial.cpp.
|
||||
*/
|
||||
bool isolating2refinable(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b);
|
||||
|
||||
//
|
||||
// Interval refinement procedures
|
||||
// They all assume p is square free and (a, b) is a refinable isolating interval.
|
||||
//
|
||||
// Return TRUE, if interval was squeezed, and new interval is stored in (a,b).
|
||||
// Return FALSE, if the actual root was found, it is stored in a.
|
||||
//
|
||||
// See upolynomial.cpp for additional comments
|
||||
bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b);
|
||||
|
||||
bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b);
|
||||
|
||||
bool refine_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k);
|
||||
|
||||
bool refine(unsigned sz, numeral const * p, mpbq_manager & bqm, mpbq & a, mpbq & b, unsigned prec_k);
|
||||
/////////////////////
|
||||
|
||||
/**
|
||||
\brief Convert a isolating (refinable) rational interval into a
|
||||
isolating refinable binary rational interval.
|
||||
|
||||
Return TRUE, if interval was found and the result is stored in (c, d).
|
||||
Return FALSE, if the actual root was found, it is stored in c.
|
||||
*/
|
||||
bool convert_q2bq_interval(unsigned sz, numeral const * p, mpq const & a, mpq const & b, mpbq_manager & bqm, mpbq & c, mpbq & d);
|
||||
|
||||
/**
|
||||
\brief Given a polynomial p, and a lower bound l. Return
|
||||
the root id i. That is, the first root u > l is the i-th root of p.
|
||||
*/
|
||||
unsigned get_root_id(unsigned sz, numeral const * p, mpbq const & l);
|
||||
|
||||
/**
|
||||
\brief Make sure that isolating interval (a, b) for p does not contain zero.
|
||||
|
||||
Return TRUE, if updated (a, b) does not contain zero.
|
||||
Return FALSE, if zero is a root of p
|
||||
*/
|
||||
bool normalize_interval_core(unsigned sz, numeral const * p, int sign_a, mpbq_manager & m, mpbq & a, mpbq & b);
|
||||
|
||||
/**
|
||||
\brief Similar to normalize_interval_core, but sign_a does not need to be provided.
|
||||
*/
|
||||
bool normalize_interval(unsigned sz, numeral const * p, mpbq_manager & m, mpbq & a, mpbq & b);
|
||||
|
||||
/**
|
||||
\brief Return true if all irreducible factors were found.
|
||||
That is, if the result if false, there is no guarantee that the factors in r are irreducible.
|
||||
This can happen when limits (e.g., on the search space size) are set in params.
|
||||
*/
|
||||
bool factor(unsigned sz, numeral const * p, factors & r, factor_params const & params = factor_params());
|
||||
bool factor(numeral_vector const & p, factors & r, factor_params const & params = factor_params()) { return factor(p.size(), p.c_ptr(), r, params); }
|
||||
|
||||
void display(std::ostream & out, unsigned sz, numeral const * p, char const * var_name = "x", bool use_star = false) const {
|
||||
return core_manager::display(out, sz, p, var_name);
|
||||
}
|
||||
void display(std::ostream & out, numeral_vector const & p, char const * var_name = "x") const {
|
||||
return core_manager::display(out, p, var_name);
|
||||
}
|
||||
void display(std::ostream & out, upolynomial_sequence const & seq, char const * var_name = "x") const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
1300
src/math/polynomial/upolynomial_factorization.cpp
Normal file
1300
src/math/polynomial/upolynomial_factorization.cpp
Normal file
File diff suppressed because it is too large
Load diff
96
src/math/polynomial/upolynomial_factorization.h
Normal file
96
src/math/polynomial/upolynomial_factorization.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
upolynomial_factorization.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Methods for factoring polynomials.
|
||||
|
||||
Author:
|
||||
|
||||
Dejan (t-dejanj) 2011-11-29
|
||||
|
||||
Notes:
|
||||
|
||||
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
|
||||
46(8-10):1853–1859, 1967.
|
||||
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
|
||||
edition, 1997.
|
||||
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
|
||||
|
||||
--*/
|
||||
#ifndef _UPOLYNOMIAL_FACTORIZATION_H_
|
||||
#define _UPOLYNOMIAL_FACTORIZATION_H_
|
||||
|
||||
#include"upolynomial.h"
|
||||
#include"polynomial.h"
|
||||
#include"bit_vector.h"
|
||||
#include"z3_exception.h"
|
||||
|
||||
namespace upolynomial {
|
||||
typedef manager::scoped_numeral scoped_numeral;
|
||||
|
||||
/**
|
||||
\breif Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime.
|
||||
*/
|
||||
void zp_square_free_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & sq_free_factors);
|
||||
|
||||
/**
|
||||
\brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false
|
||||
if f is an irreducible square-free polynomial in Z_p[x].
|
||||
*/
|
||||
bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors);
|
||||
|
||||
inline bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, factor_params const & params) {
|
||||
return zp_factor_square_free(zp_upm, f, factors);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Factor the monic square-free polynomial f from Z_p[x] using the Berlekamp algorithm. If randomized is true
|
||||
the factor splitting is done randomly [3], otherwise it is done as in the original Berlekamp [1].
|
||||
*/
|
||||
bool zp_factor_square_free_berlekamp(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, bool randomized = true);
|
||||
|
||||
/**
|
||||
\brief Factor the polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false if f is
|
||||
an irreducible polynomial in Z_p[x]
|
||||
*/
|
||||
bool zp_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors);
|
||||
|
||||
/**
|
||||
\brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k},
|
||||
r = (a, b), with the following assumptions:
|
||||
* UA + VB = 1 (mod a)
|
||||
* C = AB (mod b)
|
||||
* (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A))
|
||||
the output of is two polynomials A1, B1 (replacing A and B) such that A1 = A (mod b), B1 = B (mod b),
|
||||
l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if
|
||||
r is prime. See [3] p. 138.
|
||||
|
||||
The method will also change the zp_manager's module from b to b*r
|
||||
*/
|
||||
void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral const & r,
|
||||
numeral_vector const & U, numeral_vector const & A, numeral_vector const & V, numeral_vector const & B,
|
||||
numeral_vector const & C, numeral_vector & A_lifted, numeral_vector & B_lifted);
|
||||
|
||||
/**
|
||||
\brief Performs the Hensel lift for the (monic!) factors_p of f in Z_p to Z_{p^e}.
|
||||
*/
|
||||
void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & factors_p, unsigned e, zp_factors & factors_pe);
|
||||
|
||||
/**
|
||||
\brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was sucesseful, or false if
|
||||
f is an irreducible polynomial in Z[x]. The vector of factors is cleared.
|
||||
*/
|
||||
bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & ps = factor_params());
|
||||
/**
|
||||
Similar to factor_square_free, but it is used to factor the k-th component f^k of a polynomial.
|
||||
That is, the factors of f are inserted as factors of degree k into fs.
|
||||
*/
|
||||
bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, unsigned k, factor_params const & ps = factor_params());
|
||||
};
|
||||
|
||||
#endif
|
420
src/math/polynomial/upolynomial_factorization_int.h
Normal file
420
src/math/polynomial/upolynomial_factorization_int.h
Normal file
|
@ -0,0 +1,420 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
upolynomial_factorization_int.h
|
||||
|
||||
Abstract:
|
||||
|
||||
(Internal) header file for univariate polynomial factorization.
|
||||
This classes are exposed for debugging purposes only.
|
||||
|
||||
Author:
|
||||
|
||||
Dejan (t-dejanj) 2011-11-29
|
||||
|
||||
Notes:
|
||||
|
||||
[1] Elwyn Ralph Berlekamp. Factoring Polynomials over Finite Fields. Bell System Technical Journal,
|
||||
46(8-10):1853–1859, 1967.
|
||||
[2] Donald Ervin Knuth. The Art of Computer Programming, volume 2: Seminumerical Algorithms. Addison Wesley, third
|
||||
edition, 1997.
|
||||
[3] Henri Cohen. A Course in Computational Algebraic Number Theory. Springer Verlag, 1993.
|
||||
|
||||
--*/
|
||||
#ifndef _UPOLYNOMIAL_FACTORIZATION_INT_H_
|
||||
#define _UPOLYNOMIAL_FACTORIZATION_INT_H_
|
||||
|
||||
#include"upolynomial_factorization.h"
|
||||
|
||||
namespace upolynomial {
|
||||
// copy p from some manager to zp_p in Z_p[x]
|
||||
inline void to_zp_manager(zp_manager & zp_upm, numeral_vector & p) {
|
||||
zp_numeral_manager & zp_nm(zp_upm.m());
|
||||
for (unsigned i = 0; i < p.size(); ++ i) {
|
||||
zp_nm.p_normalize(p[i]);
|
||||
}
|
||||
zp_upm.trim(p);
|
||||
}
|
||||
|
||||
// copy p from some manager to zp_p in Z_p[x]
|
||||
inline void to_zp_manager(zp_manager & zp_upm, numeral_vector const & p, numeral_vector & zp_p) {
|
||||
zp_numeral_manager & zp_nm(zp_upm.m());
|
||||
zp_upm.reset(zp_p);
|
||||
for (unsigned i = 0; i < p.size(); ++ i) {
|
||||
numeral p_i; // no need to delete, we keep it pushed in zp_p
|
||||
zp_nm.set(p_i, p[i]);
|
||||
zp_p.push_back(p_i);
|
||||
}
|
||||
zp_upm.trim(zp_p);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Contains all possible degrees of a factorization of a polynomial.
|
||||
If
|
||||
p = p1^{k_1} * ... * pn^{k_n} with p_i of degree d_i
|
||||
then it is represents numbers of the for \sum a_i*d_i, where a_i <= k_i. Two numbers always in the set are
|
||||
deg(p) and 0.
|
||||
|
||||
*/
|
||||
class factorization_degree_set {
|
||||
|
||||
// the set itself, a (m_max_degree)-binary number
|
||||
bit_vector m_set;
|
||||
|
||||
public:
|
||||
|
||||
factorization_degree_set() { }
|
||||
|
||||
factorization_degree_set(zp_factors const & factors)
|
||||
{
|
||||
zp_manager & upm = factors.upm();
|
||||
// the set contains only {0}
|
||||
m_set.push_back(true);
|
||||
for (unsigned i = 0; i < factors.distinct_factors(); ++ i) {
|
||||
unsigned degree = upm.degree(factors[i]);
|
||||
unsigned multiplicity = factors.get_degree(i);
|
||||
for (unsigned k = 0; k < multiplicity; ++ k) {
|
||||
bit_vector tmp(m_set);
|
||||
m_set.shift_right(degree);
|
||||
m_set |= tmp;
|
||||
}
|
||||
}
|
||||
SASSERT(in_set(0) && in_set(factors.get_degree()));
|
||||
}
|
||||
|
||||
unsigned max_degree() const { return m_set.size() - 1; }
|
||||
|
||||
void swap(factorization_degree_set & other) {
|
||||
m_set.swap(other.m_set);
|
||||
}
|
||||
|
||||
bool is_trivial() const {
|
||||
// check if set = {0, n}
|
||||
for (int i = 1; i < (int) m_set.size() - 1; ++ i) {
|
||||
if (m_set.get(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(unsigned k) {
|
||||
m_set.set(k, false);
|
||||
}
|
||||
|
||||
bool in_set(unsigned k) const {
|
||||
return m_set.get(k);
|
||||
}
|
||||
|
||||
void intersect(const factorization_degree_set& other) {
|
||||
m_set &= other.m_set;
|
||||
}
|
||||
|
||||
void display(std::ostream & out) const {
|
||||
out << "[0";
|
||||
for (unsigned i = 1; i <= max_degree(); ++ i) {
|
||||
if (in_set(i)) {
|
||||
out << ", " << i;
|
||||
}
|
||||
}
|
||||
out << "] represented by " << m_set;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\brief A to iterate through all combinations of factors. This is only needed for the factorization, and we
|
||||
always iterate through the
|
||||
*/
|
||||
template <typename factors_type>
|
||||
class factorization_combination_iterator_base {
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
// total size of available factors
|
||||
int m_total_size;
|
||||
// maximal size of the selection
|
||||
int m_max_size;
|
||||
// the factors to select from
|
||||
factors_type const & m_factors;
|
||||
// which factors are enabled
|
||||
svector<bool> m_enabled;
|
||||
// the size of the current selection
|
||||
int m_current_size;
|
||||
// the current selection: indices at positions < m_current_size, other values are maxed out
|
||||
svector<int> m_current;
|
||||
|
||||
/**
|
||||
Assuming a valid selection m_current[0], ..., m_current[position], try to find the next option for
|
||||
m_current[position], i.e. the first bigger one that's enabled.
|
||||
*/
|
||||
int find(int position, int upper_bound) {
|
||||
int current = m_current[position] + 1;
|
||||
while (current < upper_bound && !m_enabled[current]) {
|
||||
current ++;
|
||||
}
|
||||
if (current == upper_bound) {
|
||||
return -1;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
factorization_combination_iterator_base(factors_type const & factors)
|
||||
: m_total_size(factors.distinct_factors()),
|
||||
m_max_size(factors.distinct_factors()/2),
|
||||
m_factors(factors)
|
||||
{
|
||||
SASSERT(factors.total_factors() > 1);
|
||||
SASSERT(factors.total_factors() == factors.distinct_factors());
|
||||
// enable all to start with
|
||||
m_enabled.resize(m_factors.distinct_factors(), true);
|
||||
// max out the m_current so that it always fits
|
||||
m_current.resize(m_factors.distinct_factors()+1, m_factors.distinct_factors());
|
||||
m_current_size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the factors we are enumerating through.
|
||||
*/
|
||||
factors_type const & get_factors() const {
|
||||
return m_factors;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Computes the next combination of factors and returns true if it exists. If remove current is true
|
||||
it will eliminate the current selected elements from any future selection.
|
||||
*/
|
||||
bool next(bool remove_current) {
|
||||
|
||||
int max_upper_bound = m_factors.distinct_factors();
|
||||
|
||||
do {
|
||||
|
||||
// the index we are currently trying to fix
|
||||
int current_i = m_current_size - 1;
|
||||
// the value we found as plausable (-1 we didn't find anything)
|
||||
int current_value = -1;
|
||||
|
||||
if (remove_current) {
|
||||
SASSERT(m_current_size > 0);
|
||||
// disable the elements of the current selection from ever appearing again
|
||||
for (current_i = m_current_size - 1; current_i > 0; -- current_i) {
|
||||
SASSERT(m_enabled[m_current[current_i]]);
|
||||
m_enabled[m_current[current_i]] = false;
|
||||
m_current[current_i] = max_upper_bound;
|
||||
}
|
||||
// the last one
|
||||
SASSERT(m_enabled[m_current[0]]);
|
||||
m_enabled[m_current[0]] = false;
|
||||
// not removing current anymore
|
||||
remove_current = false;
|
||||
// out max size is also going down
|
||||
m_total_size -= m_current_size;
|
||||
m_max_size = m_total_size/2;
|
||||
}
|
||||
|
||||
// we go back to the first one that can be increased (if removing current go all the way)
|
||||
while (current_i >= 0) {
|
||||
current_value = find(current_i, m_current[current_i + 1]);
|
||||
if (current_value >= 0) {
|
||||
// found one
|
||||
m_current[current_i] = current_value;
|
||||
break;
|
||||
} else {
|
||||
// go back some more
|
||||
current_i --;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
if (current_value == -1) {
|
||||
// we couldn't find any options, we have to increse size and start from the first one of that size
|
||||
if (m_current_size >= m_max_size) {
|
||||
return false;
|
||||
} else {
|
||||
m_current_size ++;
|
||||
m_current[0] = -1;
|
||||
current_i = 0;
|
||||
current_value = find(current_i, max_upper_bound);
|
||||
// if we didn't find any, we are done
|
||||
if (current_value == -1) {
|
||||
return false;
|
||||
} else {
|
||||
m_current[current_i] = current_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ok we have a new selection for the current one
|
||||
for (current_i ++; current_i < m_current_size; ++ current_i) {
|
||||
// start from the previous one
|
||||
m_current[current_i] = m_current[current_i-1];
|
||||
current_value = find(current_i, max_upper_bound);
|
||||
if (current_value == -1) {
|
||||
// screwed, didn't find the next one, this means we need to increase the size
|
||||
m_current[0] = -1;
|
||||
break;
|
||||
} else {
|
||||
m_current[current_i] = current_value;
|
||||
}
|
||||
}
|
||||
|
||||
} while (current_value == -1);
|
||||
|
||||
} while (filter_current());
|
||||
|
||||
// found the next one, hurray
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief A function that returns true if the current combination should be ignored.
|
||||
*/
|
||||
virtual bool filter_current() const = 0;
|
||||
|
||||
/**
|
||||
\brief Returns the size of the current selection (cardinality)
|
||||
*/
|
||||
unsigned left_size() const {
|
||||
return m_current_size;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the size of the rest of the current selection (cardinality)
|
||||
*/
|
||||
unsigned right_size() const {
|
||||
return m_total_size - m_current_size;
|
||||
}
|
||||
|
||||
void display(std::ostream& out) const {
|
||||
out << "[ ";
|
||||
for (unsigned i = 0; i < m_current.size(); ++ i) {
|
||||
out << m_current[i] << " ";
|
||||
}
|
||||
out << "] from [ ";
|
||||
for (unsigned i = 0; i < m_factors.distinct_factors(); ++ i) {
|
||||
if (m_enabled[i]) {
|
||||
out << i << " ";
|
||||
}
|
||||
}
|
||||
out << "]" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
class ufactorization_combination_iterator : public factorization_combination_iterator_base<zp_factors> {
|
||||
|
||||
// the degree sets to choose from
|
||||
factorization_degree_set const & m_degree_set;
|
||||
|
||||
public:
|
||||
|
||||
ufactorization_combination_iterator(zp_factors const & factors, factorization_degree_set const & degree_set)
|
||||
: factorization_combination_iterator_base<zp_factors>(factors),
|
||||
m_degree_set(degree_set)
|
||||
{}
|
||||
|
||||
/**
|
||||
\brief Filter the ones not in the degree set.
|
||||
*/
|
||||
bool filter_current() const {
|
||||
|
||||
// select only the ones that have degrees in the degree set
|
||||
if (!m_degree_set.in_set(current_degree())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the degree of the current selection.
|
||||
*/
|
||||
unsigned current_degree() const {
|
||||
unsigned degree = 0;
|
||||
zp_manager & upm = m_factors.pm();
|
||||
for (unsigned i = 0; i < left_size(); ++ i) {
|
||||
degree += upm.degree(m_factors[m_current[i]]);
|
||||
}
|
||||
return degree;
|
||||
}
|
||||
|
||||
void left(numeral_vector & out) const {
|
||||
SASSERT(m_current_size > 0);
|
||||
zp_manager & upm = m_factors.upm();
|
||||
upm.set(m_factors[m_current[0]].size(), m_factors[m_current[0]].c_ptr(), out);
|
||||
for (int i = 1; i < m_current_size; ++ i) {
|
||||
upm.mul(out.size(), out.c_ptr(), m_factors[m_current[i]].size(), m_factors[m_current[i]].c_ptr(), out);
|
||||
}
|
||||
}
|
||||
|
||||
void get_left_tail_coeff(numeral const & m, numeral & out) {
|
||||
zp_numeral_manager & nm = m_factors.upm().m();
|
||||
nm.set(out, m);
|
||||
for (int i = 0; i < m_current_size; ++ i) {
|
||||
nm.mul(out, m_factors[m_current[i]][0], out);
|
||||
}
|
||||
}
|
||||
|
||||
void get_right_tail_coeff(numeral const & m, numeral & out) {
|
||||
zp_numeral_manager & nm = m_factors.upm().m();
|
||||
nm.set(out, m);
|
||||
|
||||
unsigned current = 0;
|
||||
unsigned selection_i = 0;
|
||||
|
||||
// selection is ordered, so we just take the ones in between that are not disable
|
||||
while (current < m_factors.distinct_factors()) {
|
||||
if (!m_enabled[current]) {
|
||||
// by skipping the disabled we never skip a selected one
|
||||
current ++;
|
||||
} else {
|
||||
if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) {
|
||||
SASSERT(m_factors.get_degree(current) == 1);
|
||||
nm.mul(out, m_factors[current][0], out);
|
||||
current ++;
|
||||
} else {
|
||||
current ++;
|
||||
selection_i ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void right(numeral_vector & out) const {
|
||||
SASSERT(m_current_size > 0);
|
||||
zp_manager & upm = m_factors.upm();
|
||||
upm.reset(out);
|
||||
|
||||
unsigned current = 0;
|
||||
unsigned selection_i = 0;
|
||||
|
||||
// selection is ordered, so we just take the ones in between that are not disable
|
||||
while (current < m_factors.distinct_factors()) {
|
||||
if (!m_enabled[current]) {
|
||||
// by skipping the disabled we never skip a selected one
|
||||
current ++;
|
||||
} else {
|
||||
if (selection_i >= m_current.size() || (int) current < m_current[selection_i]) {
|
||||
SASSERT(m_factors.get_degree(current) == 1);
|
||||
if (out.size() == 0) {
|
||||
upm.set(m_factors[current].size(), m_factors[current].c_ptr(), out);
|
||||
} else {
|
||||
upm.mul(out.size(), out.c_ptr(), m_factors[current].size(), m_factors[current].c_ptr(), out);
|
||||
}
|
||||
current ++;
|
||||
} else {
|
||||
current ++;
|
||||
selection_i ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
288
src/math/subpaving/subpaving.cpp
Normal file
288
src/math/subpaving/subpaving.cpp
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic.
|
||||
This is a wrapper for the different implementations
|
||||
of the subpaving module.
|
||||
This wrapper is the main interface between Z3 other modules and subpaving.
|
||||
Thus, it assumes that polynomials have precise integer coefficients, and
|
||||
bounds are rationals. If a particular implementation uses floats, then
|
||||
internally the bounds are approximated.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving.h"
|
||||
#include"subpaving_types.h"
|
||||
#include"subpaving_mpq.h"
|
||||
#include"subpaving_mpf.h"
|
||||
#include"subpaving_hwf.h"
|
||||
#include"subpaving_mpff.h"
|
||||
#include"subpaving_mpfx.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
template<typename CTX>
|
||||
class context_wrapper : public context {
|
||||
protected:
|
||||
CTX m_ctx;
|
||||
public:
|
||||
context_wrapper(typename CTX::numeral_manager & m, params_ref const & p, small_object_allocator * a):m_ctx(m, p, a) {}
|
||||
virtual ~context_wrapper() {}
|
||||
virtual unsigned num_vars() const { return m_ctx.num_vars(); }
|
||||
virtual var mk_var(bool is_int) { return m_ctx.mk_var(is_int); }
|
||||
virtual bool is_int(var x) const { return m_ctx.is_int(x); }
|
||||
virtual var mk_monomial(unsigned sz, power const * pws) { return m_ctx.mk_monomial(sz, pws); }
|
||||
virtual void inc_ref(ineq * a) { m_ctx.inc_ref(reinterpret_cast<typename CTX::ineq*>(a)); }
|
||||
virtual void dec_ref(ineq * a) { m_ctx.dec_ref(reinterpret_cast<typename CTX::ineq*>(a)); }
|
||||
virtual void add_clause(unsigned sz, ineq * const * atoms) { m_ctx.add_clause(sz, reinterpret_cast<typename CTX::ineq * const *>(atoms)); }
|
||||
virtual void display_constraints(std::ostream & out, bool use_star) const { m_ctx.display_constraints(out, use_star); }
|
||||
virtual void set_cancel(bool f) { m_ctx.set_cancel(f); }
|
||||
virtual void set_display_proc(display_var_proc * p) { m_ctx.set_display_proc(p); }
|
||||
virtual void reset_statistics() { m_ctx.reset_statistics(); }
|
||||
virtual void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); }
|
||||
virtual void collect_param_descrs(param_descrs & r) { m_ctx.collect_param_descrs(r); }
|
||||
virtual void updt_params(params_ref const & p) { m_ctx.updt_params(p); }
|
||||
virtual void operator()() { m_ctx(); }
|
||||
virtual void display_bounds(std::ostream & out) const { m_ctx.display_bounds(out); }
|
||||
};
|
||||
|
||||
class context_mpq_wrapper : public context_wrapper<context_mpq> {
|
||||
scoped_mpq m_c;
|
||||
scoped_mpq_vector m_as;
|
||||
public:
|
||||
context_mpq_wrapper(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a):
|
||||
context_wrapper<context_mpq>(m, p, a),
|
||||
m_c(m),
|
||||
m_as(m)
|
||||
{}
|
||||
|
||||
virtual ~context_mpq_wrapper() {}
|
||||
|
||||
virtual unsynch_mpq_manager & qm() const { return m_ctx.nm(); }
|
||||
|
||||
virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) {
|
||||
m_as.reserve(sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m_ctx.nm().set(m_as[i], as[i]);
|
||||
}
|
||||
m_ctx.nm().set(m_c, c);
|
||||
return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs);
|
||||
}
|
||||
virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) {
|
||||
return reinterpret_cast<ineq*>(m_ctx.mk_ineq(x, k, lower, open));
|
||||
}
|
||||
};
|
||||
|
||||
class context_mpf_wrapper : public context_wrapper<context_mpf> {
|
||||
f2n<mpf_manager> & m_fm;
|
||||
unsynch_mpq_manager & m_qm;
|
||||
scoped_mpf m_c;
|
||||
scoped_mpf_vector m_as;
|
||||
scoped_mpq m_q1, m_q2;
|
||||
|
||||
// Convert the mpz (integer) into a mpf, and throws an exception if the conversion is not precise.
|
||||
void int2mpf(mpz const & a, mpf & o) {
|
||||
m_qm.set(m_q1, a);
|
||||
m_ctx.nm().set(o, m_q1);
|
||||
m_ctx.nm().m().to_rational(o, m_q2);
|
||||
if (!m_qm.eq(m_q1, m_q2))
|
||||
throw subpaving::exception();
|
||||
}
|
||||
|
||||
public:
|
||||
context_mpf_wrapper(f2n<mpf_manager> & fm, params_ref const & p, small_object_allocator * a):
|
||||
context_wrapper<context_mpf>(fm, p, a),
|
||||
m_fm(fm),
|
||||
m_qm(fm.m().mpq_manager()),
|
||||
m_c(fm.m()),
|
||||
m_as(fm.m()),
|
||||
m_q1(m_qm),
|
||||
m_q2(m_qm) {
|
||||
}
|
||||
|
||||
virtual ~context_mpf_wrapper() {}
|
||||
|
||||
virtual unsynch_mpq_manager & qm() const { return m_qm; }
|
||||
|
||||
virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) {
|
||||
try {
|
||||
m_as.reserve(sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
int2mpf(as[i], m_as[i]);
|
||||
}
|
||||
int2mpf(c, m_c);
|
||||
return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs);
|
||||
}
|
||||
catch (f2n<mpf_manager>::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) {
|
||||
try {
|
||||
f2n<mpf_manager> & m = m_ctx.nm();
|
||||
if (lower)
|
||||
m.round_to_minus_inf();
|
||||
else
|
||||
m.round_to_plus_inf();
|
||||
m.set(m_c, k);
|
||||
return reinterpret_cast<ineq*>(m_ctx.mk_ineq(x, m_c, lower, open));
|
||||
}
|
||||
catch (f2n<mpf_manager>::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class context_hwf_wrapper : public context_wrapper<context_hwf> {
|
||||
f2n<hwf_manager> & m_fm;
|
||||
unsynch_mpq_manager & m_qm;
|
||||
hwf m_c;
|
||||
svector<hwf> m_as;
|
||||
|
||||
// Convert the mpz (integer) into a hwf, and throws an exception if the conversion is not precise.
|
||||
void int2hwf(mpz const & a, hwf & o) {
|
||||
if (!m_qm.is_int64(a))
|
||||
throw subpaving::exception();
|
||||
int64 val = m_qm.get_int64(a);
|
||||
double dval = static_cast<double>(val);
|
||||
m_ctx.nm().set(o, dval);
|
||||
double _dval = m_ctx.nm().m().to_double(o);
|
||||
// TODO check the following test
|
||||
if (static_cast<int64>(_dval) != val)
|
||||
throw subpaving::exception();
|
||||
}
|
||||
|
||||
public:
|
||||
context_hwf_wrapper(f2n<hwf_manager> & fm, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a):
|
||||
context_wrapper<context_hwf>(fm, p, a),
|
||||
m_fm(fm),
|
||||
m_qm(qm) {
|
||||
}
|
||||
|
||||
virtual ~context_hwf_wrapper() {}
|
||||
|
||||
virtual unsynch_mpq_manager & qm() const { return m_qm; }
|
||||
|
||||
virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) {
|
||||
try {
|
||||
m_as.reserve(sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
int2hwf(as[i], m_as[i]);
|
||||
}
|
||||
int2hwf(c, m_c);
|
||||
return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs);
|
||||
}
|
||||
catch (f2n<mpf_manager>::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) {
|
||||
try {
|
||||
f2n<hwf_manager> & m = m_ctx.nm();
|
||||
if (lower)
|
||||
m.round_to_minus_inf();
|
||||
else
|
||||
m.round_to_plus_inf();
|
||||
m.set(m_c, k);
|
||||
return reinterpret_cast<ineq*>(m_ctx.mk_ineq(x, m_c, lower, open));
|
||||
}
|
||||
catch (f2n<mpf_manager>::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename context_fpoint>
|
||||
class context_fpoint_wrapper : public context_wrapper<context_fpoint> {
|
||||
unsynch_mpq_manager & m_qm;
|
||||
_scoped_numeral<typename context_fpoint::numeral_manager> m_c;
|
||||
_scoped_numeral_vector<typename context_fpoint::numeral_manager> m_as;
|
||||
scoped_mpz m_z1, m_z2;
|
||||
|
||||
void int2fpoint(mpz const & a, typename context_fpoint::numeral & o) {
|
||||
m_qm.set(m_z1, a);
|
||||
this->m_ctx.nm().set(o, m_qm, m_z1);
|
||||
this->m_ctx.nm().to_mpz(o, m_qm, m_z2);
|
||||
if (!m_qm.eq(m_z1, m_z2))
|
||||
throw subpaving::exception();
|
||||
}
|
||||
|
||||
public:
|
||||
context_fpoint_wrapper(typename context_fpoint::numeral_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a):
|
||||
context_wrapper<context_fpoint>(m, p, a),
|
||||
m_qm(qm),
|
||||
m_c(m),
|
||||
m_as(m),
|
||||
m_z1(m_qm),
|
||||
m_z2(m_qm) {
|
||||
}
|
||||
|
||||
virtual ~context_fpoint_wrapper() {}
|
||||
|
||||
virtual unsynch_mpq_manager & qm() const { return m_qm; }
|
||||
|
||||
virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) {
|
||||
try {
|
||||
m_as.reserve(sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
int2fpoint(as[i], m_as[i]);
|
||||
}
|
||||
int2fpoint(c, m_c);
|
||||
return this->m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs);
|
||||
}
|
||||
catch (typename context_fpoint::numeral_manager::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) {
|
||||
try {
|
||||
typename context_fpoint::numeral_manager & m = this->m_ctx.nm();
|
||||
if (lower)
|
||||
m.round_to_minus_inf();
|
||||
else
|
||||
m.round_to_plus_inf();
|
||||
m.set(m_c, m_qm, k);
|
||||
return reinterpret_cast<ineq*>(this->m_ctx.mk_ineq(x, m_c, lower, open));
|
||||
}
|
||||
catch (typename context_fpoint::numeral_manager::exception) {
|
||||
throw subpaving::exception();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef context_fpoint_wrapper<context_mpff> context_mpff_wrapper;
|
||||
typedef context_fpoint_wrapper<context_mpfx> context_mpfx_wrapper;
|
||||
|
||||
context * mk_mpq_context(unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) {
|
||||
return alloc(context_mpq_wrapper, m, p, a);
|
||||
}
|
||||
|
||||
context * mk_mpf_context(f2n<mpf_manager> & m, params_ref const & p, small_object_allocator * a) {
|
||||
return alloc(context_mpf_wrapper, m, p, a);
|
||||
}
|
||||
|
||||
context * mk_hwf_context(f2n<hwf_manager> & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) {
|
||||
return alloc(context_hwf_wrapper, m, qm, p, a);
|
||||
}
|
||||
|
||||
context * mk_mpff_context(mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) {
|
||||
return alloc(context_mpff_wrapper, m, qm, p, a);
|
||||
}
|
||||
|
||||
context * mk_mpfx_context(mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a) {
|
||||
return alloc(context_mpfx_wrapper, m, qm, p, a);
|
||||
}
|
||||
|
||||
};
|
124
src/math/subpaving/subpaving.h
Normal file
124
src/math/subpaving/subpaving.h
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic.
|
||||
This is a wrapper for the different implementations
|
||||
of the subpaving module.
|
||||
This wrapper is the main interface between Z3 other modules and subpaving.
|
||||
Thus, it assumes that polynomials have precise integer coefficients, and
|
||||
bounds are rationals. If a particular implementation uses floats, then
|
||||
internally the bounds are approximated.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_H_
|
||||
#define __SUBPAVING_H_
|
||||
|
||||
#include"mpq.h"
|
||||
#include"subpaving_types.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
|
||||
template<typename fmanager> class f2n;
|
||||
class mpf_manager;
|
||||
class hwf_manager;
|
||||
class mpff_manager;
|
||||
class mpfx_manager;
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
class context {
|
||||
public:
|
||||
virtual ~context() {}
|
||||
|
||||
virtual unsynch_mpq_manager & qm() const = 0;
|
||||
|
||||
/**
|
||||
\brief Return the number of variables in this subpaving object.
|
||||
*/
|
||||
virtual unsigned num_vars() const = 0;
|
||||
|
||||
/**
|
||||
\brief Create a new variable.
|
||||
*/
|
||||
virtual var mk_var(bool is_int) = 0;
|
||||
|
||||
/**
|
||||
\brief Return true if \c x is an integer variable.
|
||||
*/
|
||||
virtual bool is_int(var x) const = 0;
|
||||
|
||||
/**
|
||||
\brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
|
||||
The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
|
||||
|
||||
\pre for all i \in [0, sz-1] : ks[i] > 0
|
||||
\pre sz > 0
|
||||
*/
|
||||
virtual var mk_monomial(unsigned sz, power const * pws) = 0;
|
||||
|
||||
/**
|
||||
\brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
|
||||
The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
|
||||
|
||||
\pre sz > 0
|
||||
\pre for all i \in [0, sz-1] : as[i] != 0
|
||||
*/
|
||||
virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) = 0;
|
||||
|
||||
/**
|
||||
\brief Create an inequality.
|
||||
*/
|
||||
virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) = 0;
|
||||
virtual void inc_ref(ineq * a) = 0;
|
||||
virtual void dec_ref(ineq * a) = 0;
|
||||
|
||||
/**
|
||||
\brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1]
|
||||
\pre sz >= 1
|
||||
*/
|
||||
virtual void add_clause(unsigned sz, ineq * const * atoms) = 0;
|
||||
|
||||
/**
|
||||
\brief Display constraints asserted in the subpaving.
|
||||
*/
|
||||
virtual void display_constraints(std::ostream & out, bool use_star = false) const = 0;
|
||||
|
||||
virtual void set_cancel(bool f) = 0;
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) = 0;
|
||||
|
||||
virtual void updt_params(params_ref const & p) = 0;
|
||||
|
||||
virtual void set_display_proc(display_var_proc * p) = 0;
|
||||
|
||||
virtual void reset_statistics() = 0;
|
||||
|
||||
virtual void collect_statistics(statistics & st) const = 0;
|
||||
|
||||
virtual void operator()() = 0;
|
||||
|
||||
virtual void display_bounds(std::ostream & out) const = 0;
|
||||
};
|
||||
|
||||
context * mk_mpq_context(unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
context * mk_mpf_context(f2n<mpf_manager> & m, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
context * mk_hwf_context(f2n<hwf_manager> & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
context * mk_mpff_context(mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
context * mk_mpfx_context(mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
23
src/math/subpaving/subpaving_hwf.cpp
Normal file
23
src/math/subpaving/subpaving_hwf.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_hwf.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using hardware floats.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-06.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving_hwf.h"
|
||||
#include"subpaving_t_def.h"
|
||||
|
||||
// force template instantiation
|
||||
template class subpaving::context_t<subpaving::config_hwf>;
|
48
src/math/subpaving/subpaving_hwf.h
Normal file
48
src/math/subpaving/subpaving_hwf.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_hwf.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using hardware floats.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-06.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_HWF_H_
|
||||
#define __SUBPAVING_HWF_H_
|
||||
|
||||
#include"subpaving_t.h"
|
||||
#include"f2n.h"
|
||||
#include"hwf.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
struct config_hwf {
|
||||
f2n<hwf_manager> & m_manager;
|
||||
public:
|
||||
typedef f2n<hwf_manager> numeral_manager;
|
||||
typedef f2n<hwf_manager>::exception exception;
|
||||
|
||||
static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); }
|
||||
static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); }
|
||||
static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); }
|
||||
config_hwf(f2n<hwf_manager> & m):m_manager(m) {}
|
||||
f2n<hwf_manager> & m() const { return const_cast<f2n<hwf_manager> &>(m_manager); }
|
||||
};
|
||||
|
||||
class context_hwf : public context_t<config_hwf> {
|
||||
public:
|
||||
context_hwf(f2n<hwf_manager> & m, params_ref const & p, small_object_allocator * a):context_t<config_hwf>(config_hwf(m), p, a) {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
23
src/math/subpaving/subpaving_mpf.cpp
Normal file
23
src/math/subpaving/subpaving_mpf.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpf.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using multi-precision floats.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-07-31.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving_mpf.h"
|
||||
#include"subpaving_t_def.h"
|
||||
|
||||
// force template instantiation
|
||||
template class subpaving::context_t<subpaving::config_mpf>;
|
49
src/math/subpaving/subpaving_mpf.h
Normal file
49
src/math/subpaving/subpaving_mpf.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpf.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using multi-precision floats.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-07-31.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_MPF_H_
|
||||
#define __SUBPAVING_MPF_H_
|
||||
|
||||
#include"subpaving_t.h"
|
||||
#include"mpf.h"
|
||||
#include"f2n.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
struct config_mpf {
|
||||
f2n<mpf_manager> & m_manager;
|
||||
public:
|
||||
typedef f2n<mpf_manager> numeral_manager;
|
||||
typedef mpf numeral;
|
||||
typedef f2n<mpf_manager>::exception exception;
|
||||
|
||||
static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); }
|
||||
static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); }
|
||||
static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); }
|
||||
config_mpf(f2n<mpf_manager> & m):m_manager(m) {}
|
||||
f2n<mpf_manager> & m() const { return const_cast<f2n<mpf_manager> &>(m_manager); }
|
||||
};
|
||||
|
||||
class context_mpf : public context_t<config_mpf> {
|
||||
public:
|
||||
context_mpf(f2n<mpf_manager> & m, params_ref const & p, small_object_allocator * a):context_t<config_mpf>(config_mpf(m), p, a) {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
23
src/math/subpaving/subpaving_mpff.cpp
Normal file
23
src/math/subpaving/subpaving_mpff.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpff.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using mpff numerals.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-09-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving_mpff.h"
|
||||
#include"subpaving_t_def.h"
|
||||
|
||||
// force template instantiation
|
||||
template class subpaving::context_t<subpaving::config_mpff>;
|
45
src/math/subpaving/subpaving_mpff.h
Normal file
45
src/math/subpaving/subpaving_mpff.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpff.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using mpff numerals
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-09-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_MPFF_H_
|
||||
#define __SUBPAVING_MPFF_H_
|
||||
|
||||
#include"subpaving_t.h"
|
||||
#include"mpff.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
struct config_mpff {
|
||||
typedef mpff_manager numeral_manager;
|
||||
typedef mpff_manager::exception exception;
|
||||
|
||||
static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); }
|
||||
static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); }
|
||||
static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); }
|
||||
|
||||
numeral_manager & m_manager;
|
||||
|
||||
config_mpff(numeral_manager & m):m_manager(m) {}
|
||||
numeral_manager & m() const { return m_manager; }
|
||||
};
|
||||
|
||||
typedef context_t<config_mpff> context_mpff;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
23
src/math/subpaving/subpaving_mpfx.cpp
Normal file
23
src/math/subpaving/subpaving_mpfx.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpfx.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using mpfx numerals.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-09-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving_mpfx.h"
|
||||
#include"subpaving_t_def.h"
|
||||
|
||||
// force template instantiation
|
||||
template class subpaving::context_t<subpaving::config_mpfx>;
|
45
src/math/subpaving/subpaving_mpfx.h
Normal file
45
src/math/subpaving/subpaving_mpfx.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpfx.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using mpfx numerals
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-09-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_MPFX_H_
|
||||
#define __SUBPAVING_MPFX_H_
|
||||
|
||||
#include"subpaving_t.h"
|
||||
#include"mpfx.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
struct config_mpfx {
|
||||
typedef mpfx_manager numeral_manager;
|
||||
typedef mpfx_manager::exception exception;
|
||||
|
||||
static void round_to_minus_inf(numeral_manager & m) { m.round_to_minus_inf(); }
|
||||
static void round_to_plus_inf(numeral_manager & m) { m.round_to_plus_inf(); }
|
||||
static void set_rounding(numeral_manager & m, bool to_plus_inf) { m.set_rounding(to_plus_inf); }
|
||||
|
||||
numeral_manager & m_manager;
|
||||
|
||||
config_mpfx(numeral_manager & m):m_manager(m) {}
|
||||
numeral_manager & m() const { return m_manager; }
|
||||
};
|
||||
|
||||
typedef context_t<config_mpfx> context_mpfx;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
23
src/math/subpaving/subpaving_mpq.cpp
Normal file
23
src/math/subpaving/subpaving_mpq.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpq.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using rationals.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-07-31.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"subpaving_mpq.h"
|
||||
#include"subpaving_t_def.h"
|
||||
|
||||
// force template instantiation
|
||||
template class subpaving::context_t<subpaving::config_mpq>;
|
43
src/math/subpaving/subpaving_mpq.h
Normal file
43
src/math/subpaving/subpaving_mpq.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_mpq.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving for non-linear arithmetic using rationals
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-07-31.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_MPQ_H_
|
||||
#define __SUBPAVING_MPQ_H_
|
||||
|
||||
#include"subpaving_t.h"
|
||||
#include"mpq.h"
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
struct config_mpq {
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
struct exception {};
|
||||
|
||||
static void round_to_minus_inf(numeral_manager & m) {}
|
||||
static void round_to_plus_inf(numeral_manager & m) {}
|
||||
static void set_rounding(numeral_manager & m, bool to_plus_info) {}
|
||||
numeral_manager & m_manager;
|
||||
config_mpq(numeral_manager & m):m_manager(m) {}
|
||||
numeral_manager & m() const { return m_manager; }
|
||||
};
|
||||
|
||||
typedef context_t<config_mpq> context_mpq;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
853
src/math/subpaving/subpaving_t.h
Normal file
853
src/math/subpaving/subpaving_t.h
Normal file
|
@ -0,0 +1,853 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_t.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving template for non-linear arithmetic.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-07-31.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_T_H_
|
||||
#define __SUBPAVING_T_H_
|
||||
|
||||
#include<iostream>
|
||||
#include"tptr.h"
|
||||
#include"small_object_allocator.h"
|
||||
#include"chashtable.h"
|
||||
#include"parray.h"
|
||||
#include"interval.h"
|
||||
#include"scoped_numeral_vector.h"
|
||||
#include"subpaving_types.h"
|
||||
#include"params.h"
|
||||
#include"statistics.h"
|
||||
#include"lbool.h"
|
||||
#include"id_gen.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4200)
|
||||
#pragma warning(disable : 4355)
|
||||
#endif
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
template<typename C>
|
||||
class context_t {
|
||||
public:
|
||||
typedef typename C::numeral_manager numeral_manager;
|
||||
typedef typename numeral_manager::numeral numeral;
|
||||
|
||||
/**
|
||||
\brief Inequalities used to encode a problem.
|
||||
*/
|
||||
class ineq {
|
||||
friend class context_t;
|
||||
var m_x;
|
||||
numeral m_val;
|
||||
unsigned m_ref_count:30;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_open:1;
|
||||
public:
|
||||
var x() const { return m_x; }
|
||||
numeral const & value() const { return m_val; }
|
||||
bool is_lower() const { return m_lower; }
|
||||
bool is_open() const { return m_open; }
|
||||
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
|
||||
struct lt_var_proc { bool operator()(ineq const * a, ineq const * b) const { return a->m_x < b->m_x; } };
|
||||
};
|
||||
|
||||
class node;
|
||||
|
||||
class constraint {
|
||||
public:
|
||||
enum kind {
|
||||
CLAUSE, MONOMIAL, POLYNOMIAL
|
||||
// TODO: add SIN, COS, TAN, ...
|
||||
};
|
||||
protected:
|
||||
kind m_kind;
|
||||
uint64 m_timestamp;
|
||||
public:
|
||||
constraint(kind k):m_kind(k), m_timestamp(0) {}
|
||||
|
||||
kind get_kind() const { return m_kind; }
|
||||
|
||||
// Return the timestamp of the last propagation visit
|
||||
uint64 timestamp() const { return m_timestamp; }
|
||||
// Reset propagation visit time
|
||||
void set_visited(uint64 ts) { m_timestamp = ts; }
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Clauses in the problem description and lemmas learned during paving.
|
||||
*/
|
||||
class clause : public constraint {
|
||||
friend class context_t;
|
||||
unsigned m_size; //!< Number of atoms in the clause.
|
||||
unsigned m_lemma:1; //!< True if it is a learned clause.
|
||||
unsigned m_watched:1; //!< True if it we are watching this clause. All non-lemmas are watched.
|
||||
unsigned m_num_jst:30; //!< Number of times it is used to justify some bound.
|
||||
ineq * m_atoms[0];
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(clause) + sz*sizeof(ineq*); }
|
||||
public:
|
||||
clause():constraint(constraint::CLAUSE) {}
|
||||
unsigned size() const { return m_size; }
|
||||
bool watched() const { return m_watched; }
|
||||
ineq * operator[](unsigned i) const { SASSERT(i < size()); return m_atoms[i]; }
|
||||
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
|
||||
};
|
||||
|
||||
class justification {
|
||||
void * m_data;
|
||||
public:
|
||||
enum kind {
|
||||
AXIOM = 0,
|
||||
ASSUMPTION,
|
||||
CLAUSE,
|
||||
VAR_DEF
|
||||
};
|
||||
|
||||
justification(bool axiom = true) {
|
||||
m_data = axiom ? reinterpret_cast<void*>(static_cast<size_t>(AXIOM)) : reinterpret_cast<void*>(static_cast<size_t>(ASSUMPTION));
|
||||
}
|
||||
justification(justification const & source) { m_data = source.m_data; }
|
||||
explicit justification(clause * c) { m_data = TAG(void*, c, CLAUSE); }
|
||||
explicit justification(var x) { m_data = BOXTAGINT(void*, x, VAR_DEF); }
|
||||
|
||||
kind get_kind() const { return static_cast<kind>(GET_TAG(m_data)); }
|
||||
bool is_clause() const { return get_kind() == CLAUSE; }
|
||||
bool is_axiom() const { return get_kind() == AXIOM; }
|
||||
bool is_assumption() const { return get_kind() == ASSUMPTION; }
|
||||
bool is_var_def() const { return get_kind() == VAR_DEF; }
|
||||
|
||||
clause * get_clause() const {
|
||||
SASSERT(is_clause());
|
||||
return UNTAG(clause*, m_data);
|
||||
}
|
||||
|
||||
var get_var() const {
|
||||
SASSERT(is_var_def());
|
||||
return UNBOXINT(m_data);
|
||||
}
|
||||
|
||||
bool operator==(justification const & other) const { return m_data == other.m_data; }
|
||||
bool operator!=(justification const & other) const { return !operator==(other); }
|
||||
};
|
||||
|
||||
class bound {
|
||||
friend class context_t;
|
||||
numeral m_val;
|
||||
unsigned m_x:29;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_open:1;
|
||||
unsigned m_mark:1;
|
||||
uint64 m_timestamp;
|
||||
bound * m_prev;
|
||||
justification m_jst;
|
||||
void set_timestamp(uint64 ts) { m_timestamp = ts; }
|
||||
public:
|
||||
var x() const { return static_cast<var>(m_x); }
|
||||
numeral const & value() const { return m_val; }
|
||||
numeral & value() { return m_val; }
|
||||
bool is_lower() const { return m_lower; }
|
||||
bool is_open() const { return m_open; }
|
||||
uint64 timestamp() const { return m_timestamp; }
|
||||
bound * prev() const { return m_prev; }
|
||||
justification jst() const { return m_jst; }
|
||||
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc());
|
||||
};
|
||||
|
||||
struct bound_array_config {
|
||||
typedef context_t value_manager;
|
||||
typedef small_object_allocator allocator;
|
||||
typedef bound * value;
|
||||
static const bool ref_count = false;
|
||||
static const bool preserve_roots = true;
|
||||
static const unsigned max_trail_sz = 16;
|
||||
static const unsigned factor = 2;
|
||||
};
|
||||
|
||||
// auxiliary declarations for parray_manager
|
||||
void dec_ref(bound *) {}
|
||||
void inc_ref(bound *) {}
|
||||
|
||||
typedef parray_manager<bound_array_config> bound_array_manager;
|
||||
typedef typename bound_array_manager::ref bound_array;
|
||||
|
||||
/**
|
||||
\brief Node in the context_t.
|
||||
*/
|
||||
class node {
|
||||
bound_array_manager & m_bm;
|
||||
bound_array m_lowers;
|
||||
bound_array m_uppers;
|
||||
var m_conflict;
|
||||
unsigned m_id;
|
||||
unsigned m_depth;
|
||||
bound * m_trail;
|
||||
node * m_parent; //!< parent node
|
||||
node * m_first_child;
|
||||
node * m_next_sibling;
|
||||
// Doubly linked list of leaves to be processed
|
||||
node * m_prev;
|
||||
node * m_next;
|
||||
public:
|
||||
node(context_t & s, unsigned id);
|
||||
node(node * parent, unsigned id);
|
||||
// return unique indentifier.
|
||||
unsigned id() const { return m_id; }
|
||||
bound_array_manager & bm() const { return m_bm; }
|
||||
bound_array & lowers() { return m_lowers; }
|
||||
bound_array & uppers() { return m_uppers; }
|
||||
bool inconsistent() const { return m_conflict != null_var; }
|
||||
void set_conflict(var x) { SASSERT(!inconsistent()); m_conflict = x; }
|
||||
bound * trail_stack() const { return m_trail; }
|
||||
bound * parent_trail_stack() const { return m_parent == 0 ? 0 : m_parent->m_trail; }
|
||||
bound * lower(var x) const { return bm().get(m_lowers, x); }
|
||||
bound * upper(var x) const { return bm().get(m_uppers, x); }
|
||||
node * parent() const { return m_parent; }
|
||||
node * first_child() const { return m_first_child; }
|
||||
node * next_sibling() const { return m_next_sibling; }
|
||||
node * prev() const { return m_prev; }
|
||||
node * next() const { return m_next; }
|
||||
/**
|
||||
\brief Return true if x is unbounded in this node
|
||||
*/
|
||||
bool is_unbounded(var x) const { return lower(x) == 0 && upper(x) == 0; }
|
||||
void push(bound * b);
|
||||
|
||||
void set_first_child(node * n) { m_first_child = n; }
|
||||
void set_next_sibling(node * n) { m_next_sibling = n; }
|
||||
void set_next(node * n) { m_next = n; }
|
||||
void set_prev(node * n) { m_prev = n; }
|
||||
|
||||
unsigned depth() const { return m_depth; }
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Intervals are just temporary place holders.
|
||||
The pavers maintain bounds.
|
||||
*/
|
||||
struct interval {
|
||||
bool m_constant; // Flag: constant intervals are pairs <node*, var>
|
||||
// constant intervals
|
||||
node * m_node;
|
||||
var m_x;
|
||||
// mutable intervals
|
||||
numeral m_l_val;
|
||||
bool m_l_inf;
|
||||
bool m_l_open;
|
||||
numeral m_u_val;
|
||||
bool m_u_inf;
|
||||
bool m_u_open;
|
||||
|
||||
interval():m_constant(false) {}
|
||||
void set_constant(node * n, var x) {
|
||||
m_constant = true;
|
||||
m_node = n;
|
||||
m_x = x;
|
||||
}
|
||||
void set_mutable() { m_constant = false; }
|
||||
};
|
||||
|
||||
class interval_config {
|
||||
public:
|
||||
typedef typename C::numeral_manager numeral_manager;
|
||||
typedef typename numeral_manager::numeral numeral;
|
||||
typedef typename context_t::interval interval;
|
||||
private:
|
||||
numeral_manager & m_manager;
|
||||
public:
|
||||
interval_config(numeral_manager & m):m_manager(m) {}
|
||||
|
||||
numeral_manager & m() const { return m_manager; }
|
||||
void round_to_minus_inf() { C::round_to_minus_inf(m()); }
|
||||
void round_to_plus_inf() { C::round_to_plus_inf(m()); }
|
||||
void set_rounding(bool to_plus_inf) { C::set_rounding(m(), to_plus_inf); }
|
||||
numeral const & lower(interval const & a) const {
|
||||
if (a.m_constant) {
|
||||
bound * b = a.m_node->lower(a.m_x);
|
||||
return b == 0 ? a.m_l_val /* don't care */ : b->value();
|
||||
}
|
||||
return a.m_l_val;
|
||||
}
|
||||
numeral const & upper(interval const & a) const {
|
||||
if (a.m_constant) {
|
||||
bound * b = a.m_node->upper(a.m_x);
|
||||
return b == 0 ? a.m_u_val /* don't care */ : b->value();
|
||||
}
|
||||
return a.m_u_val;
|
||||
}
|
||||
numeral & lower(interval & a) { SASSERT(!a.m_constant); return a.m_l_val; }
|
||||
numeral & upper(interval & a) { SASSERT(!a.m_constant); return a.m_u_val; }
|
||||
bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == 0 : a.m_l_inf; }
|
||||
bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == 0 : a.m_u_inf; }
|
||||
bool lower_is_open(interval const & a) const {
|
||||
if (a.m_constant) {
|
||||
bound * b = a.m_node->lower(a.m_x);
|
||||
return b == 0 || b->is_open();
|
||||
}
|
||||
return a.m_l_open;
|
||||
}
|
||||
bool upper_is_open(interval const & a) const {
|
||||
if (a.m_constant) {
|
||||
bound * b = a.m_node->upper(a.m_x);
|
||||
return b == 0 || b->is_open();
|
||||
}
|
||||
return a.m_u_open;
|
||||
}
|
||||
// Setters
|
||||
void set_lower(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_l_val, n); }
|
||||
void set_upper(interval & a, numeral const & n) { SASSERT(!a.m_constant); m().set(a.m_u_val, n); }
|
||||
void set_lower_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_open = v; }
|
||||
void set_upper_is_open(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_open = v; }
|
||||
void set_lower_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_l_inf = v; }
|
||||
void set_upper_is_inf(interval & a, bool v) { SASSERT(!a.m_constant); a.m_u_inf = v; }
|
||||
};
|
||||
|
||||
typedef ::interval_manager<interval_config> interval_manager;
|
||||
|
||||
class definition : public constraint {
|
||||
public:
|
||||
definition(typename constraint::kind k):constraint(k) {}
|
||||
};
|
||||
|
||||
class monomial : public definition {
|
||||
friend class context_t;
|
||||
unsigned m_size;
|
||||
power m_powers[0];
|
||||
monomial(unsigned sz, power const * pws);
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz*sizeof(power); }
|
||||
public:
|
||||
unsigned size() const { return m_size; }
|
||||
power const & get_power(unsigned idx) const { SASSERT(idx < size()); return m_powers[idx]; }
|
||||
power const * get_powers() const { return m_powers; }
|
||||
var x(unsigned idx) const { return get_power(idx).x(); }
|
||||
unsigned degree(unsigned idx) const { return get_power(idx).degree(); }
|
||||
void display(std::ostream & out, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
|
||||
};
|
||||
|
||||
class polynomial : public definition {
|
||||
friend class context_t;
|
||||
unsigned m_size;
|
||||
numeral m_c;
|
||||
numeral * m_as;
|
||||
var * m_xs;
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(polynomial) + sz*sizeof(numeral) + sz*sizeof(var); }
|
||||
public:
|
||||
polynomial():definition(constraint::POLYNOMIAL) {}
|
||||
unsigned size() const { return m_size; }
|
||||
numeral const & a(unsigned i) const { return m_as[i]; }
|
||||
var x(unsigned i) const { return m_xs[i]; }
|
||||
var const * xs() const { return m_xs; }
|
||||
numeral const * as() const { return m_as; }
|
||||
numeral const & c() const { return m_c; }
|
||||
void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc(), bool use_star = false) const;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Watched element (aka occurence) can be:
|
||||
|
||||
- A clause
|
||||
- A definition (i.e., a variable)
|
||||
|
||||
Remark: we cannot use the two watched literal approach since we process multiple nodes.
|
||||
*/
|
||||
class watched {
|
||||
public:
|
||||
enum kind { CLAUSE=0, DEFINITION };
|
||||
private:
|
||||
void * m_data;
|
||||
public:
|
||||
watched():m_data(0) {}
|
||||
explicit watched(var x) { m_data = BOXTAGINT(void*, x, DEFINITION); }
|
||||
explicit watched(clause * c) { m_data = TAG(void*, c, CLAUSE); }
|
||||
kind get_kind() const { return static_cast<kind>(GET_TAG(m_data)); }
|
||||
bool is_clause() const { return get_kind() != DEFINITION; }
|
||||
bool is_definition() const { return get_kind() == DEFINITION; }
|
||||
clause * get_clause() const { SASSERT(is_clause()); return UNTAG(clause*, m_data); }
|
||||
var get_var() const { SASSERT(is_definition()); return UNBOXINT(m_data); }
|
||||
bool operator==(watched const & other) const { return m_data == other.m_data; }
|
||||
bool operator!=(watched const & other) const { return !operator==(other); }
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Abstract functor for selecting the next leaf node to be explored.
|
||||
*/
|
||||
class node_selector {
|
||||
context_t * m_ctx;
|
||||
public:
|
||||
node_selector(context_t * ctx):m_ctx(ctx) {}
|
||||
virtual ~node_selector() {}
|
||||
|
||||
context_t * ctx() const { return m_ctx; }
|
||||
|
||||
// Return the next leaf node to be processed.
|
||||
// Front and back are the first and last nodes in the doubly linked list of
|
||||
// leaf nodes.
|
||||
// Remark: new nodes are always inserted in the front of the list.
|
||||
virtual node * operator()(node * front, node * back) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Abstract functor for selecting the next variable to branch.
|
||||
*/
|
||||
class var_selector {
|
||||
context_t * m_ctx;
|
||||
public:
|
||||
var_selector(context_t * ctx):m_ctx(ctx) {}
|
||||
virtual ~var_selector() {}
|
||||
|
||||
context_t * ctx() const { return m_ctx; }
|
||||
|
||||
// Return the next variable to branch.
|
||||
virtual var operator()(node * n) = 0;
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
// Invoked when a new variable is created.
|
||||
virtual void new_var_eh(var x) {}
|
||||
// Invoked when node n is created
|
||||
virtual void new_node_eh(node * n) {}
|
||||
// Invoked before deleting node n.
|
||||
virtual void del_node_eh(node * n) {}
|
||||
// Invoked when variable x is used during conflict resolution.
|
||||
virtual void used_var_eh(node * n, var x) {}
|
||||
};
|
||||
|
||||
class node_splitter;
|
||||
friend class node_splitter;
|
||||
|
||||
/**
|
||||
\brief Abstract functor for creating children for node n by branching on a given variable.
|
||||
*/
|
||||
class node_splitter {
|
||||
context_t * m_ctx;
|
||||
public:
|
||||
node_splitter(context_t * ctx):m_ctx(ctx) {}
|
||||
virtual ~node_splitter() {}
|
||||
|
||||
context_t * ctx() const { return m_ctx; }
|
||||
node * mk_node(node * p) { return ctx()->mk_node(p); }
|
||||
bound * mk_decided_bound(var x, numeral const & val, bool lower, bool open, node * n) {
|
||||
return ctx()->mk_bound(x, val, lower, open, n, justification());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create children nodes for n by splitting on x.
|
||||
|
||||
\pre n is a leaf. The interval for x in n has more than one element.
|
||||
*/
|
||||
virtual void operator()(node * n, var x) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Return most recent splitting var for node n.
|
||||
*/
|
||||
var splitting_var(node * n) const;
|
||||
|
||||
/**
|
||||
\brief Return true if x is a definition.
|
||||
*/
|
||||
bool is_definition(var x) const { return m_defs[x] != 0; }
|
||||
|
||||
typedef svector<watched> watch_list;
|
||||
typedef _scoped_numeral_vector<numeral_manager> scoped_numeral_vector;
|
||||
|
||||
private:
|
||||
C m_c;
|
||||
bool m_arith_failed; //!< True if the arithmetic module produced an exception.
|
||||
bool m_own_allocator;
|
||||
small_object_allocator * m_allocator;
|
||||
bound_array_manager m_bm;
|
||||
interval_manager m_im;
|
||||
scoped_numeral_vector m_num_buffer;
|
||||
|
||||
svector<bool> m_is_int;
|
||||
ptr_vector<definition> m_defs;
|
||||
vector<watch_list> m_wlist;
|
||||
|
||||
ptr_vector<ineq> m_unit_clauses;
|
||||
ptr_vector<clause> m_clauses;
|
||||
ptr_vector<clause> m_lemmas;
|
||||
|
||||
id_gen m_node_id_gen;
|
||||
|
||||
uint64 m_timestamp;
|
||||
node * m_root;
|
||||
// m_leaf_head is the head of a doubly linked list of leaf nodes to be processed.
|
||||
node * m_leaf_head;
|
||||
node * m_leaf_tail;
|
||||
|
||||
var m_conflict;
|
||||
ptr_vector<bound> m_queue;
|
||||
unsigned m_qhead;
|
||||
|
||||
display_var_proc m_default_display_proc;
|
||||
display_var_proc * m_display_proc;
|
||||
|
||||
scoped_ptr<node_selector> m_node_selector;
|
||||
scoped_ptr<var_selector> m_var_selector;
|
||||
scoped_ptr<node_splitter> m_node_splitter;
|
||||
|
||||
svector<power> m_pws;
|
||||
|
||||
// Configuration
|
||||
numeral m_epsilon; //!< If upper - lower < epsilon, then new bound is not propagated.
|
||||
bool m_zero_epsilon;
|
||||
numeral m_max_bound; //!< Bounds > m_max and < -m_max are not propagated
|
||||
numeral m_minus_max_bound; //!< -m_max_bound
|
||||
numeral m_nth_root_prec; //!< precision for computing the nth root
|
||||
unsigned m_max_depth; //!< Maximum depth
|
||||
unsigned m_max_nodes; //!< Maximum number of nodes in the tree
|
||||
unsigned long long m_max_memory; // in bytes
|
||||
|
||||
// Counters
|
||||
unsigned m_num_nodes;
|
||||
|
||||
// Statistics
|
||||
unsigned m_num_conflicts;
|
||||
unsigned m_num_mk_bounds;
|
||||
unsigned m_num_splits;
|
||||
unsigned m_num_visited;
|
||||
|
||||
// Temporary
|
||||
numeral m_tmp1, m_tmp2, m_tmp3;
|
||||
interval m_i_tmp1, m_i_tmp2, m_i_tmp3;
|
||||
|
||||
// Cancel flag
|
||||
volatile bool m_cancel;
|
||||
|
||||
friend class node;
|
||||
|
||||
void set_arith_failed() { m_arith_failed = true; }
|
||||
|
||||
void checkpoint();
|
||||
|
||||
bound_array_manager & bm() { return m_bm; }
|
||||
interval_manager & im() { return m_im; }
|
||||
small_object_allocator & allocator() const { return *m_allocator; }
|
||||
|
||||
bound * mk_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst);
|
||||
void del_bound(bound * b);
|
||||
// Create a new bound and add it to the propagation queue.
|
||||
void propagate_bound(var x, numeral const & val, bool lower, bool open, node * n, justification jst);
|
||||
|
||||
bool is_int(monomial const * m) const;
|
||||
bool is_int(polynomial const * p) const;
|
||||
|
||||
bool is_monomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::MONOMIAL; }
|
||||
monomial * get_monomial(var x) const { SASSERT(is_monomial(x)); return static_cast<monomial*>(m_defs[x]); }
|
||||
bool is_polynomial(var x) const { return m_defs[x] != 0 && m_defs[x]->get_kind() == constraint::POLYNOMIAL; }
|
||||
polynomial * get_polynomial(var x) const { SASSERT(is_polynomial(x)); return static_cast<polynomial*>(m_defs[x]); }
|
||||
static void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc, var x, numeral & k, bool lower, bool open);
|
||||
void display(std::ostream & out, var x) const;
|
||||
void display_definition(std::ostream & out, definition const * d, bool use_star = false) const;
|
||||
void display(std::ostream & out, constraint * a, bool use_star = false) const;
|
||||
void display(std::ostream & out, bound * b) const;
|
||||
void display(std::ostream & out, ineq * a) const;
|
||||
void display_params(std::ostream & out) const;
|
||||
void add_unit_clause(ineq * a, bool axiom);
|
||||
// Remark: Not all lemmas need to be watched. Some of them can be used to justify clauses only.
|
||||
void add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watched);
|
||||
void del_clause(clause * cls);
|
||||
|
||||
node * mk_node(node * parent = 0);
|
||||
void del_node(node * n);
|
||||
void del_nodes();
|
||||
|
||||
void del(interval & a);
|
||||
void del_clauses(ptr_vector<clause> & cs);
|
||||
void del_unit_clauses();
|
||||
void del_clauses();
|
||||
void del_monomial(monomial * m);
|
||||
void del_sum(polynomial * p);
|
||||
void del_definitions();
|
||||
|
||||
/**
|
||||
\brief Insert n in the beginning of the doubly linked list of leaves.
|
||||
|
||||
\pre n is a leaf, and it is not already in the list.
|
||||
*/
|
||||
void push_front(node * n);
|
||||
|
||||
/**
|
||||
\brief Insert n in the end of the doubly linked list of leaves.
|
||||
|
||||
\pre n is a leaf, and it is not already in the list.
|
||||
*/
|
||||
void push_back(node * n);
|
||||
|
||||
/**
|
||||
\brief Remove n from the doubly linked list of leaves.
|
||||
|
||||
\pre n is a leaf, and it is in the list.
|
||||
*/
|
||||
void remove_from_leaf_dlist(node * n);
|
||||
|
||||
/**
|
||||
\brief Remove all nodes from the leaf dlist.
|
||||
*/
|
||||
void reset_leaf_dlist();
|
||||
|
||||
/**
|
||||
\brief Add all leaves back to the leaf dlist.
|
||||
*/
|
||||
void rebuild_leaf_dlist(node * n);
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Propagation
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
/**
|
||||
\brief Return true if the given node is in an inconsistent state.
|
||||
*/
|
||||
bool inconsistent(node * n) const { return n->inconsistent(); }
|
||||
|
||||
/**
|
||||
\brief Set a conflict produced by the bounds of x at the given node.
|
||||
*/
|
||||
void set_conflict(var x, node * n);
|
||||
|
||||
/**
|
||||
\brief Return true if bound b may propagate a new bound using constraint c at node n.
|
||||
*/
|
||||
bool may_propagate(bound * b, constraint * c, node * n);
|
||||
|
||||
/**
|
||||
\brief Normalize bound if x is integer.
|
||||
|
||||
Examples:
|
||||
x < 2 --> x <= 1
|
||||
x <= 2.3 --> x <= 2
|
||||
*/
|
||||
void normalize_bound(var x, numeral & val, bool lower, bool & open);
|
||||
|
||||
/**
|
||||
\brief Return true if (x, k, lower, open) is a relevant new bound at node n.
|
||||
That is, it improves the current bound, and satisfies m_epsilon and m_max_bound.
|
||||
*/
|
||||
bool relevant_new_bound(var x, numeral const & k, bool lower, bool open, node * n);
|
||||
|
||||
/**
|
||||
\brief Return true if the lower and upper bounds of x are 0 at node n.
|
||||
*/
|
||||
bool is_zero(var x, node * n) const;
|
||||
|
||||
/**
|
||||
\brief Return true if upper bound of x is 0 at node n.
|
||||
*/
|
||||
bool is_upper_zero(var x, node * n) const;
|
||||
|
||||
/**
|
||||
\brief Return true if lower and upper bounds of x are conflicting at node n. That is, upper(x) < lower(x)
|
||||
*/
|
||||
bool conflicting_bounds(var x, node * n) const;
|
||||
|
||||
/**
|
||||
\brief Return true if x is unbounded at node n.
|
||||
*/
|
||||
bool is_unbounded(var x, node * n) const { return n->is_unbounded(x); }
|
||||
|
||||
/**
|
||||
\brief Return true if b is the most recent lower/upper bound for variable b->x() at node n.
|
||||
*/
|
||||
bool most_recent(bound * b, node * n) const;
|
||||
|
||||
/**
|
||||
\brief Add most recent bounds of node n into the propagation queue.
|
||||
That is, all bounds b s.t. b is in the trail of n, but not in the tail of parent(n), and most_recent(b, n).
|
||||
*/
|
||||
void add_recent_bounds(node * n);
|
||||
|
||||
/**
|
||||
\brief Propagate new bounds at node n using get_monomial(x)
|
||||
\pre is_monomial(x)
|
||||
*/
|
||||
void propagate_monomial(var x, node * n);
|
||||
void propagate_monomial_upward(var x, node * n);
|
||||
void propagate_monomial_downward(var x, node * n, unsigned i);
|
||||
|
||||
/**
|
||||
\brief Propagate new bounds at node n using get_polynomial(x)
|
||||
\pre is_polynomial(x)
|
||||
*/
|
||||
void propagate_polynomial(var x, node * n);
|
||||
// Propagate a new bound for y using the polynomial associated with x. x may be equal to y.
|
||||
void propagate_polynomial(var x, node * n, var y);
|
||||
|
||||
/**
|
||||
\brief Propagate new bounds at node n using clause c.
|
||||
*/
|
||||
void propagate_clause(clause * c, node * n);
|
||||
|
||||
/**
|
||||
\brief Return the truth value of inequaliy t at node n.
|
||||
*/
|
||||
lbool value(ineq * t, node * n);
|
||||
|
||||
/**
|
||||
\brief Propagate new bounds at node n using the definition of variable x.
|
||||
\pre is_definition(x)
|
||||
*/
|
||||
void propagate_def(var x, node * n);
|
||||
|
||||
/**
|
||||
\brief Propagate constraints in b->x()'s watch list.
|
||||
*/
|
||||
void propagate(node * n, bound * b);
|
||||
|
||||
/**
|
||||
\brief Perform bound propagation at node n.
|
||||
*/
|
||||
void propagate(node * n);
|
||||
|
||||
/**
|
||||
\brief Try to propagate at node n using all definitions.
|
||||
*/
|
||||
void propagate_all_definitions(node * n);
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Main
|
||||
//
|
||||
// -----------------------------------
|
||||
void init();
|
||||
|
||||
/**
|
||||
\brief Assert unit clauses in the node n.
|
||||
*/
|
||||
void assert_units(node * n);
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Debugging support
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
/**
|
||||
\brief Return true if b is a bound for node n.
|
||||
*/
|
||||
bool is_bound_of(bound * b, node * n) const;
|
||||
|
||||
/**
|
||||
\brief Check the consistency of the doubly linked list of leaves.
|
||||
*/
|
||||
bool check_leaf_dlist() const;
|
||||
|
||||
/**
|
||||
\brief Check paving tree structure.
|
||||
*/
|
||||
bool check_tree() const;
|
||||
|
||||
/**
|
||||
\brief Check main invariants.
|
||||
*/
|
||||
bool check_invariant() const;
|
||||
|
||||
public:
|
||||
context_t(C const & c, params_ref const & p, small_object_allocator * a);
|
||||
~context_t();
|
||||
|
||||
/**
|
||||
\brief Return true if the arithmetic module failed.
|
||||
*/
|
||||
bool arith_failed() const { return m_arith_failed; }
|
||||
|
||||
numeral_manager & nm() const { return m_c.m(); }
|
||||
|
||||
unsigned num_vars() const { return m_is_int.size(); }
|
||||
|
||||
bool is_int(var x) const { SASSERT(x < num_vars()); return m_is_int[x]; }
|
||||
|
||||
/**
|
||||
\brief Create a new variable.
|
||||
*/
|
||||
var mk_var(bool is_int);
|
||||
|
||||
/**
|
||||
\brief Create the monomial xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
|
||||
The result is a variable y s.t. y = xs[0]^ks[0] * ... * xs[sz-1]^ks[sz-1].
|
||||
|
||||
\pre for all i \in [0, sz-1] : ks[i] > 0
|
||||
\pre sz > 0
|
||||
*/
|
||||
var mk_monomial(unsigned sz, power const * pws);
|
||||
|
||||
/**
|
||||
\brief Create the sum c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
|
||||
The result is a variable y s.t. y = c + as[0]*xs[0] + ... + as[sz-1]*xs[sz-1].
|
||||
|
||||
\pre sz > 0
|
||||
\pre for all i \in [0, sz-1] : as[i] != 0
|
||||
*/
|
||||
var mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs);
|
||||
|
||||
/**
|
||||
\brief Create an inequality.
|
||||
*/
|
||||
ineq * mk_ineq(var x, numeral const & k, bool lower, bool open);
|
||||
void inc_ref(ineq * a);
|
||||
void dec_ref(ineq * a);
|
||||
|
||||
/**
|
||||
\brief Assert the clause atoms[0] \/ ... \/ atoms[sz-1]
|
||||
\pre sz > 1
|
||||
*/
|
||||
void add_clause(unsigned sz, ineq * const * atoms) { add_clause_core(sz, atoms, false, true); }
|
||||
|
||||
/**
|
||||
\brief Assert a constraint of one of the forms: x < k, x > k, x <= k, x >= k.
|
||||
|
||||
If axiom == true, then the constraint is not tracked in proofs.
|
||||
*/
|
||||
void add_ineq(var x, numeral const & k, bool lower, bool open, bool axiom);
|
||||
|
||||
/**
|
||||
\brief Store in the given vector all leaves of the paving tree.
|
||||
*/
|
||||
void collect_leaves(ptr_vector<node> & leaves) const;
|
||||
|
||||
/**
|
||||
\brief Display constraints asserted in the subpaving.
|
||||
*/
|
||||
void display_constraints(std::ostream & out, bool use_star = false) const;
|
||||
|
||||
/**
|
||||
\brief Display bounds for each leaf of the tree.
|
||||
*/
|
||||
void display_bounds(std::ostream & out) const;
|
||||
|
||||
void display_bounds(std::ostream & out, node * n) const;
|
||||
|
||||
void set_display_proc(display_var_proc * p) { m_display_proc = p; }
|
||||
|
||||
void set_cancel(bool f) { m_cancel = f; im().set_cancel(f); }
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
|
||||
static void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void reset_statistics();
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
|
||||
void operator()();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
1942
src/math/subpaving/subpaving_t_def.h
Normal file
1942
src/math/subpaving/subpaving_t_def.h
Normal file
File diff suppressed because it is too large
Load diff
52
src/math/subpaving/subpaving_types.h
Normal file
52
src/math/subpaving/subpaving_types.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_types.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Subpaving auxiliary types.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_TYPES_H_
|
||||
#define __SUBPAVING_TYPES_H_
|
||||
|
||||
namespace subpaving {
|
||||
|
||||
class ineq;
|
||||
|
||||
typedef unsigned var;
|
||||
|
||||
const var null_var = UINT_MAX;
|
||||
|
||||
class exception {
|
||||
};
|
||||
|
||||
class power : public std::pair<var, unsigned> {
|
||||
public:
|
||||
power():std::pair<var, unsigned>() {}
|
||||
power(var v, unsigned d):std::pair<var, unsigned>(v, d) {}
|
||||
power(power const & p):std::pair<var, unsigned>(p) {}
|
||||
var x() const { return first; }
|
||||
var get_var() const { return first; }
|
||||
unsigned degree() const { return second; }
|
||||
unsigned & degree() { return second; }
|
||||
void set_var(var x) { first = x; }
|
||||
struct lt_proc { bool operator()(power const & p1, power const & p2) { return p1.get_var() < p2.get_var(); } };
|
||||
};
|
||||
|
||||
struct display_var_proc {
|
||||
virtual void operator()(std::ostream & out, var x) const { out << "x" << x; }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
395
src/math/subpaving/tactic/expr2subpaving.cpp
Normal file
395
src/math/subpaving/tactic/expr2subpaving.cpp
Normal file
|
@ -0,0 +1,395 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
expr2subpaving.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Translator from Z3 expressions into generic subpaving data-structure.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-08-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"expr2subpaving.h"
|
||||
#include"expr2var.h"
|
||||
#include"ref_util.h"
|
||||
#include"z3_exception.h"
|
||||
#include"cooperate.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"scoped_numeral_buffer.h"
|
||||
|
||||
struct expr2subpaving::imp {
|
||||
struct frame {
|
||||
app * m_curr;
|
||||
unsigned m_idx;
|
||||
frame():m_curr(0), m_idx(0) {}
|
||||
frame(app * t):m_curr(t), m_idx(0) {}
|
||||
};
|
||||
|
||||
ast_manager & m_manager;
|
||||
subpaving::context & m_subpaving;
|
||||
unsynch_mpq_manager & m_qm;
|
||||
arith_util m_autil;
|
||||
expr2var * m_expr2var;
|
||||
bool m_expr2var_owner;
|
||||
|
||||
expr_ref_vector m_var2expr;
|
||||
|
||||
typedef svector<subpaving::var> var_vector;
|
||||
|
||||
obj_map<expr, unsigned> m_cache;
|
||||
var_vector m_cached_vars;
|
||||
scoped_mpz_vector m_cached_numerators;
|
||||
scoped_mpz_vector m_cached_denominators;
|
||||
|
||||
obj_map<expr, subpaving::ineq*> m_lit_cache;
|
||||
|
||||
volatile bool m_cancel;
|
||||
|
||||
imp(ast_manager & m, subpaving::context & s, expr2var * e2v):
|
||||
m_manager(m),
|
||||
m_subpaving(s),
|
||||
m_qm(s.qm()),
|
||||
m_autil(m),
|
||||
m_var2expr(m),
|
||||
m_cached_numerators(m_qm),
|
||||
m_cached_denominators(m_qm) {
|
||||
|
||||
if (e2v == 0) {
|
||||
m_expr2var = alloc(expr2var, m);
|
||||
m_expr2var_owner = true;
|
||||
}
|
||||
else {
|
||||
m_expr2var = e2v;
|
||||
m_expr2var_owner = false;
|
||||
}
|
||||
|
||||
m_cancel = false;
|
||||
}
|
||||
|
||||
~imp() {
|
||||
reset_cache();
|
||||
if (m_expr2var_owner)
|
||||
dealloc(m_expr2var);
|
||||
}
|
||||
|
||||
ast_manager & m() { return m_manager; }
|
||||
|
||||
subpaving::context & s() { return m_subpaving; }
|
||||
|
||||
unsynch_mpq_manager & qm() const { return m_qm; }
|
||||
|
||||
void reset_cache() {
|
||||
dec_ref_map_keys(m(), m_cache);
|
||||
m_cached_vars.reset();
|
||||
m_cached_numerators.reset();
|
||||
m_cached_denominators.reset();
|
||||
dec_ref_map_key_values(m(), s(), m_lit_cache);
|
||||
}
|
||||
|
||||
void checkpoint() {
|
||||
if (m_cancel)
|
||||
throw default_exception("canceled");
|
||||
cooperate("expr2subpaving");
|
||||
}
|
||||
|
||||
subpaving::var mk_var_for(expr * t) {
|
||||
SASSERT(!m_autil.is_numeral(t));
|
||||
subpaving::var x = m_expr2var->to_var(t);
|
||||
if (x == subpaving::null_var) {
|
||||
bool is_int = m_autil.is_int(t);
|
||||
x = s().mk_var(is_int);
|
||||
m_expr2var->insert(t, x);
|
||||
if (x >= m_var2expr.size())
|
||||
m_var2expr.resize(x+1, 0);
|
||||
m_var2expr.set(x, t);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
void found_non_simplified() {
|
||||
throw default_exception("you must apply simplifier before internalizing expressions into the subpaving module.");
|
||||
}
|
||||
|
||||
bool is_cached(expr * t) {
|
||||
return t->get_ref_count() > 1 && m_cache.contains(t);
|
||||
}
|
||||
|
||||
bool is_int_real(expr * t) {
|
||||
return m_autil.is_int_real(t);
|
||||
}
|
||||
|
||||
void cache_result(expr * t, subpaving::var x, mpz const & n, mpz const & d) {
|
||||
SASSERT(!m_cache.contains(t));
|
||||
SASSERT(m_cached_numerators.size() == m_cached_vars.size());
|
||||
SASSERT(m_cached_denominators.size() == m_cached_vars.size());
|
||||
if (t->get_ref_count() <= 1)
|
||||
return;
|
||||
unsigned idx = m_cached_vars.size();
|
||||
m_cache.insert(t, idx);
|
||||
m().inc_ref(t);
|
||||
m_cached_vars.push_back(x);
|
||||
m_cached_numerators.push_back(n);
|
||||
m_cached_denominators.push_back(d);
|
||||
}
|
||||
|
||||
subpaving::var process_num(app * t, unsigned depth, mpz & n, mpz & d) {
|
||||
rational k;
|
||||
VERIFY(m_autil.is_numeral(t, k));
|
||||
qm().set(n, k.to_mpq().numerator());
|
||||
qm().set(d, k.to_mpq().denominator());
|
||||
return subpaving::null_var;
|
||||
}
|
||||
|
||||
// Put t as a^k.
|
||||
void as_power(expr * t, expr * & a, unsigned & k) {
|
||||
if (!m_autil.is_power(t)) {
|
||||
a = t;
|
||||
k = 1;
|
||||
return;
|
||||
}
|
||||
rational _k;
|
||||
if (!m_autil.is_numeral(to_app(t)->get_arg(1), _k) || !_k.is_int() || !_k.is_unsigned()) {
|
||||
a = t;
|
||||
k = 1;
|
||||
return;
|
||||
}
|
||||
a = to_app(t)->get_arg(0);
|
||||
k = _k.get_unsigned();
|
||||
}
|
||||
|
||||
subpaving::var process_mul(app * t, unsigned depth, mpz & n, mpz & d) {
|
||||
unsigned num_args = t->get_num_args();
|
||||
if (num_args <= 1)
|
||||
found_non_simplified();
|
||||
rational k;
|
||||
expr * m;
|
||||
if (m_autil.is_numeral(t->get_arg(0), k)) {
|
||||
if (num_args != 2)
|
||||
found_non_simplified();
|
||||
qm().set(n, k.to_mpq().numerator());
|
||||
qm().set(d, k.to_mpq().denominator());
|
||||
m = t->get_arg(1);
|
||||
}
|
||||
else {
|
||||
qm().set(n, 1);
|
||||
qm().set(d, 1);
|
||||
m = t;
|
||||
}
|
||||
expr * const * margs;
|
||||
unsigned sz;
|
||||
if (m_autil.is_mul(m)) {
|
||||
margs = to_app(m)->get_args();
|
||||
sz = to_app(m)->get_num_args();
|
||||
}
|
||||
else {
|
||||
margs = &m;
|
||||
sz = 1;
|
||||
}
|
||||
scoped_mpz n_arg(qm());
|
||||
scoped_mpz d_arg(qm());
|
||||
sbuffer<subpaving::power> pws;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * arg = margs[i];
|
||||
unsigned k;
|
||||
as_power(arg, arg, k);
|
||||
subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg);
|
||||
qm().power(n_arg, k, n_arg);
|
||||
qm().power(d_arg, k, d_arg);
|
||||
qm().mul(n, n_arg, n);
|
||||
qm().mul(d, d_arg, d);
|
||||
if (x_arg != subpaving::null_var)
|
||||
pws.push_back(subpaving::power(x_arg, k));
|
||||
}
|
||||
subpaving::var x;
|
||||
if (pws.empty())
|
||||
x = subpaving::null_var;
|
||||
else if (pws.size() == 1 && pws[0].degree() == 1)
|
||||
x = pws[0].get_var();
|
||||
else
|
||||
x = s().mk_monomial(pws.size(), pws.c_ptr());
|
||||
cache_result(t, x, n, d);
|
||||
return x;
|
||||
}
|
||||
|
||||
typedef _scoped_numeral_buffer<unsynch_mpz_manager> mpz_buffer;
|
||||
typedef sbuffer<subpaving::var> var_buffer;
|
||||
|
||||
subpaving::var process_add(app * t, unsigned depth, mpz & n, mpz & d) {
|
||||
unsigned num_args = t->get_num_args();
|
||||
mpz_buffer ns(qm()), ds(qm());
|
||||
var_buffer xs;
|
||||
scoped_mpq c(qm()), c_arg(qm());
|
||||
scoped_mpz n_arg(qm()), d_arg(qm());
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = t->get_arg(i);
|
||||
subpaving::var x_arg = process(arg, depth+1, n_arg, d_arg);
|
||||
if (x_arg == subpaving::null_var) {
|
||||
qm().set(c_arg, n_arg, d_arg);
|
||||
qm().add(c, c_arg, c);
|
||||
}
|
||||
else {
|
||||
xs.push_back(x_arg);
|
||||
ns.push_back(n_arg);
|
||||
ds.push_back(d_arg);
|
||||
}
|
||||
}
|
||||
qm().set(d, c.get().denominator());
|
||||
unsigned sz = xs.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
qm().lcm(d, ds[i], d);
|
||||
}
|
||||
scoped_mpz & k = d_arg;
|
||||
qm().div(d, c.get().denominator(), k);
|
||||
scoped_mpz sum_c(qm());
|
||||
qm().mul(c.get().numerator(), k, sum_c);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
qm().div(d, ds[i], k);
|
||||
qm().mul(ns[i], k, ns[i]);
|
||||
}
|
||||
subpaving::var x;
|
||||
if (sz == 0) {
|
||||
qm().set(n, sum_c);
|
||||
x = subpaving::null_var;
|
||||
}
|
||||
else {
|
||||
x = s().mk_sum(sum_c, sz, ns.c_ptr(), xs.c_ptr());
|
||||
qm().set(n, 1);
|
||||
}
|
||||
cache_result(t, x, n, d);
|
||||
return x;
|
||||
}
|
||||
|
||||
subpaving::var process_power(app * t, unsigned depth, mpz & n, mpz & d) {
|
||||
rational k;
|
||||
SASSERT(t->get_num_args() == 2);
|
||||
if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) {
|
||||
qm().set(n, 1);
|
||||
qm().set(d, 1);
|
||||
return mk_var_for(t);
|
||||
}
|
||||
unsigned _k = k.get_unsigned();
|
||||
subpaving::var x = process(t->get_arg(0), depth+1, n, d);
|
||||
if (x != subpaving::null_var) {
|
||||
subpaving::power p(x, _k);
|
||||
x = s().mk_monomial(1, &p);
|
||||
}
|
||||
qm().power(n, _k, n);
|
||||
qm().power(d, _k, d);
|
||||
cache_result(t, x, n, d);
|
||||
return x;
|
||||
}
|
||||
|
||||
subpaving::var process_arith_app(app * t, unsigned depth, mpz & n, mpz & d) {
|
||||
SASSERT(m_autil.is_arith_expr(t));
|
||||
|
||||
switch (t->get_decl_kind()) {
|
||||
case OP_NUM:
|
||||
return process_num(t, depth, n, d);
|
||||
case OP_ADD:
|
||||
return process_add(t, depth, n, d);
|
||||
case OP_MUL:
|
||||
return process_mul(t, depth, n, d);
|
||||
case OP_POWER:
|
||||
return process_power(t, depth, n, d);
|
||||
case OP_TO_REAL:
|
||||
return process(t->get_arg(0), depth+1, n, d);
|
||||
case OP_SUB:
|
||||
case OP_UMINUS:
|
||||
found_non_simplified();
|
||||
break;
|
||||
case OP_TO_INT:
|
||||
case OP_DIV:
|
||||
case OP_IDIV:
|
||||
case OP_MOD:
|
||||
case OP_REM:
|
||||
case OP_IRRATIONAL_ALGEBRAIC_NUM:
|
||||
throw default_exception("you must apply arithmetic purifier before internalizing expressions into the subpaving module.");
|
||||
case OP_SIN:
|
||||
case OP_COS:
|
||||
case OP_TAN:
|
||||
case OP_ASIN:
|
||||
case OP_ACOS:
|
||||
case OP_ATAN:
|
||||
case OP_SINH:
|
||||
case OP_COSH:
|
||||
case OP_TANH:
|
||||
case OP_ASINH:
|
||||
case OP_ACOSH:
|
||||
case OP_ATANH:
|
||||
// TODO
|
||||
throw default_exception("transcendental and hyperbolic functions are not supported yet.");
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return subpaving::null_var;
|
||||
}
|
||||
|
||||
subpaving::var process(expr * t, unsigned depth, mpz & n, mpz & d) {
|
||||
SASSERT(is_int_real(t));
|
||||
checkpoint();
|
||||
|
||||
if (is_cached(t)) {
|
||||
unsigned idx = m_cache.find(t);
|
||||
qm().set(n, m_cached_numerators[idx]);
|
||||
qm().set(d, m_cached_denominators[idx]);
|
||||
return m_cached_vars[idx];
|
||||
}
|
||||
|
||||
SASSERT(!is_quantifier(t));
|
||||
if (::is_var(t) || !m_autil.is_arith_expr(t)) {
|
||||
qm().set(n, 1);
|
||||
qm().set(d, 1);
|
||||
return mk_var_for(t);
|
||||
}
|
||||
|
||||
return process_arith_app(to_app(t), depth, n, d);
|
||||
}
|
||||
|
||||
bool is_var(expr * t) const {
|
||||
return m_expr2var->is_var(t);
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_cancel = f;
|
||||
}
|
||||
|
||||
subpaving::var internalize_term(expr * t, mpz & n, mpz & d) {
|
||||
return process(t, 0, n, d);
|
||||
}
|
||||
};
|
||||
|
||||
expr2subpaving::expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v) {
|
||||
m_imp = alloc(imp, m, s, e2v);
|
||||
}
|
||||
|
||||
expr2subpaving::~expr2subpaving() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
ast_manager & expr2subpaving::m() const {
|
||||
return m_imp->m();
|
||||
}
|
||||
|
||||
subpaving::context & expr2subpaving::s() const {
|
||||
return m_imp->s();
|
||||
}
|
||||
|
||||
bool expr2subpaving::is_var(expr * t) const {
|
||||
return m_imp->is_var(t);
|
||||
}
|
||||
|
||||
void expr2subpaving::set_cancel(bool f) {
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
|
||||
subpaving::var expr2subpaving::internalize_term(expr * t, mpz & n, mpz & d) {
|
||||
return m_imp->internalize_term(t, n, d);
|
||||
}
|
58
src/math/subpaving/tactic/expr2subpaving.h
Normal file
58
src/math/subpaving/tactic/expr2subpaving.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
expr2subpaving.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Translator from Z3 expressions into generic subpaving data-structure.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-08-08
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _EXPR2SUBPAVING_H_
|
||||
#define _EXPR2SUBPAVING_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"subpaving.h"
|
||||
|
||||
class expr2var;
|
||||
|
||||
class expr2subpaving {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
public:
|
||||
expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v = 0);
|
||||
~expr2subpaving();
|
||||
|
||||
ast_manager & m() const;
|
||||
|
||||
subpaving::context & s() const;
|
||||
|
||||
/**
|
||||
\brief Return true if t was encoded as a variable by the translator.
|
||||
*/
|
||||
bool is_var(expr * t) const;
|
||||
|
||||
/**
|
||||
\brief Cancel/Interrupt execution.
|
||||
*/
|
||||
void set_cancel(bool f);
|
||||
|
||||
/**
|
||||
\brief Internalize a Z3 arithmetical expression into the subpaving data-structure.
|
||||
|
||||
\remark throws subpaving::exception there is a translation error (when using imprecise representations, i.e. floats, in the subpaving module)
|
||||
*/
|
||||
subpaving::var internalize_term(expr * t, /* out */ mpz & n, /* out */ mpz & d);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
308
src/math/subpaving/tactic/subpaving_tactic.cpp
Normal file
308
src/math/subpaving/tactic/subpaving_tactic.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
"Fake" tactic used to test subpaving module.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"tactical.h"
|
||||
#include"simplify_tactic.h"
|
||||
#include"expr2subpaving.h"
|
||||
#include"expr2var.h"
|
||||
#include"arith_decl_plugin.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
#include"hwf.h"
|
||||
#include"mpff.h"
|
||||
#include"mpfx.h"
|
||||
#include"f2n.h"
|
||||
|
||||
class subpaving_tactic : public tactic {
|
||||
|
||||
struct display_var_proc : public subpaving::display_var_proc {
|
||||
expr_ref_vector m_inv;
|
||||
|
||||
display_var_proc(expr2var & e2v):m_inv(e2v.m()) {
|
||||
e2v.mk_inv(m_inv);
|
||||
}
|
||||
|
||||
ast_manager & m() const { return m_inv.get_manager(); }
|
||||
|
||||
virtual void operator()(std::ostream & out, subpaving::var x) const {
|
||||
expr * t = m_inv.get(x, 0);
|
||||
if (t != 0)
|
||||
out << mk_ismt2_pp(t, m());
|
||||
else
|
||||
out << "k!" << x;
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
enum engine_kind { MPQ, MPF, HWF, MPFF, MPFX, NONE };
|
||||
|
||||
ast_manager & m_manager;
|
||||
unsynch_mpq_manager m_qm;
|
||||
mpf_manager m_fm_core;
|
||||
f2n<mpf_manager> m_fm;
|
||||
hwf_manager m_hm_core;
|
||||
f2n<hwf_manager> m_hm;
|
||||
mpff_manager m_ffm;
|
||||
mpfx_manager m_fxm;
|
||||
arith_util m_autil;
|
||||
engine_kind m_kind;
|
||||
scoped_ptr<subpaving::context> m_ctx;
|
||||
scoped_ptr<display_var_proc> m_proc;
|
||||
expr2var m_e2v;
|
||||
scoped_ptr<expr2subpaving> m_e2s;
|
||||
bool m_display;
|
||||
|
||||
imp(ast_manager & m, params_ref const & p):
|
||||
m_manager(m),
|
||||
m_fm(m_fm_core),
|
||||
m_hm(m_hm_core),
|
||||
m_autil(m),
|
||||
m_kind(NONE),
|
||||
m_e2v(m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
ast_manager & m() const { return m_manager; }
|
||||
|
||||
void collect_param_descrs(param_descrs & r) {
|
||||
m_ctx->collect_param_descrs(r);
|
||||
// #ifndef _EXTERNAL_RELEASE
|
||||
r.insert(":numeral", CPK_SYMBOL, "(default: mpq) options: mpq, mpf, hwf, mpff, mpfx.");
|
||||
r.insert(":print-nodes", CPK_BOOL, "(default: false) display subpaving tree leaves.");
|
||||
// #endif
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_display = p.get_bool(":print-nodes", false);
|
||||
symbol engine = p.get_sym(":numeral", symbol("mpq"));
|
||||
engine_kind new_kind;
|
||||
if (engine == "mpq")
|
||||
new_kind = MPQ;
|
||||
else if (engine == "mpf")
|
||||
new_kind = MPF;
|
||||
else if (engine == "mpff")
|
||||
new_kind = MPFF;
|
||||
else if (engine == "mpfx")
|
||||
new_kind = MPFX;
|
||||
else
|
||||
new_kind = HWF;
|
||||
if (m_kind != new_kind) {
|
||||
m_kind = new_kind;
|
||||
switch (m_kind) {
|
||||
case MPQ: m_ctx = subpaving::mk_mpq_context(m_qm); break;
|
||||
case MPF: m_ctx = subpaving::mk_mpf_context(m_fm); break;
|
||||
case HWF: m_ctx = subpaving::mk_hwf_context(m_hm, m_qm); break;
|
||||
case MPFF: m_ctx = subpaving::mk_mpff_context(m_ffm, m_qm); break;
|
||||
case MPFX: m_ctx = subpaving::mk_mpfx_context(m_fxm, m_qm); break;
|
||||
default: UNREACHABLE(); break;
|
||||
}
|
||||
m_e2s = alloc(expr2subpaving, m_manager, *m_ctx, &m_e2v);
|
||||
}
|
||||
m_ctx->updt_params(p);
|
||||
}
|
||||
|
||||
void collect_statistics(statistics & st) const {
|
||||
m_ctx->collect_statistics(st);
|
||||
}
|
||||
|
||||
void reset_statistics() {
|
||||
m_ctx->reset_statistics();
|
||||
}
|
||||
|
||||
void set_cancel(bool f) {
|
||||
m_e2s->set_cancel(f);
|
||||
m_ctx->set_cancel(f);
|
||||
}
|
||||
|
||||
subpaving::ineq * mk_ineq(expr * a) {
|
||||
bool neg = false;
|
||||
while (m().is_not(a, a))
|
||||
neg = !neg;
|
||||
bool lower;
|
||||
bool open = false;
|
||||
if (m_autil.is_le(a)) {
|
||||
lower = false;
|
||||
}
|
||||
else if (m_autil.is_ge(a)) {
|
||||
lower = true;
|
||||
}
|
||||
else {
|
||||
throw tactic_exception("unsupported atom");
|
||||
}
|
||||
if (neg) {
|
||||
lower = !lower;
|
||||
open = !open;
|
||||
}
|
||||
rational _k;
|
||||
if (!m_autil.is_numeral(to_app(a)->get_arg(1), _k))
|
||||
throw tactic_exception("use simplify tactic with option :arith-lhs true");
|
||||
scoped_mpq k(m_qm);
|
||||
k = _k.to_mpq();
|
||||
scoped_mpz n(m_qm), d(m_qm);
|
||||
subpaving::var x = m_e2s->internalize_term(to_app(a)->get_arg(0), n, d);
|
||||
m_qm.mul(d, k, k);
|
||||
m_qm.div(k, n, k);
|
||||
if (is_neg(n))
|
||||
lower = !lower;
|
||||
TRACE("subpaving_tactic", tout << x << " " << k << " " << lower << " " << open << "\n";);
|
||||
return m_ctx->mk_ineq(x, k, lower, open);
|
||||
}
|
||||
|
||||
void process_clause(expr * c) {
|
||||
expr * const * args = 0;
|
||||
unsigned sz;
|
||||
if (m().is_or(c)) {
|
||||
args = to_app(c)->get_args();
|
||||
sz = to_app(c)->get_num_args();
|
||||
}
|
||||
else {
|
||||
args = &c;
|
||||
sz = 1;
|
||||
}
|
||||
ref_buffer<subpaving::ineq, subpaving::context> ineq_buffer(*m_ctx);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
ineq_buffer.push_back(mk_ineq(args[i]));
|
||||
}
|
||||
m_ctx->add_clause(sz, ineq_buffer.c_ptr());
|
||||
}
|
||||
|
||||
void internalize(goal const & g) {
|
||||
try {
|
||||
for (unsigned i = 0; i < g.size(); i++) {
|
||||
process_clause(g.form(i));
|
||||
}
|
||||
}
|
||||
catch (subpaving::exception) {
|
||||
throw tactic_exception("failed to internalize goal into subpaving module");
|
||||
}
|
||||
}
|
||||
|
||||
void process(goal const & g) {
|
||||
internalize(g);
|
||||
m_proc = alloc(display_var_proc, m_e2v);
|
||||
m_ctx->set_display_proc(m_proc.get());
|
||||
try {
|
||||
(*m_ctx)();
|
||||
}
|
||||
catch (subpaving::exception) {
|
||||
throw tactic_exception("failed building subpaving tree...");
|
||||
}
|
||||
if (m_display) {
|
||||
m_ctx->display_constraints(std::cout);
|
||||
std::cout << "bounds at leaves: \n";
|
||||
m_ctx->display_bounds(std::cout);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
statistics m_stats;
|
||||
public:
|
||||
|
||||
subpaving_tactic(ast_manager & m, params_ref const & p):
|
||||
m_imp(alloc(imp, m, p)),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
virtual ~subpaving_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
virtual tactic * translate(ast_manager & m) {
|
||||
return alloc(subpaving_tactic, m, m_params);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_params = p;
|
||||
m_imp->updt_params(p);
|
||||
}
|
||||
|
||||
virtual void collect_param_descrs(param_descrs & r) {
|
||||
m_imp->collect_param_descrs(r);
|
||||
}
|
||||
|
||||
virtual void collect_statistics(statistics & st) const {
|
||||
st.copy(m_stats);
|
||||
}
|
||||
|
||||
virtual void reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
virtual void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc,
|
||||
expr_dependency_ref & core) {
|
||||
m_imp->process(*in);
|
||||
m_imp->collect_statistics(m_stats);
|
||||
result.reset();
|
||||
result.push_back(in.get());
|
||||
mc = 0;
|
||||
pc = 0;
|
||||
core = 0;
|
||||
}
|
||||
|
||||
virtual void cleanup() {
|
||||
ast_manager & m = m_imp->m();
|
||||
imp * d = m_imp;
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
d = m_imp;
|
||||
}
|
||||
dealloc(d);
|
||||
d = alloc(imp, m, m_params);
|
||||
#pragma omp critical (tactic_cancel)
|
||||
{
|
||||
m_imp = d;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void set_cancel(bool f) {
|
||||
if (m_imp)
|
||||
m_imp->set_cancel(f);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
tactic * mk_subpaving_tactic_core(ast_manager & m, params_ref const & p) {
|
||||
return alloc(subpaving_tactic, m, p);
|
||||
}
|
||||
|
||||
tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref simp_p = p;
|
||||
simp_p.set_bool(":arith-lhs", true);
|
||||
simp_p.set_bool(":expand-power", true);
|
||||
simp_p.set_uint(":max-power", UINT_MAX);
|
||||
simp_p.set_bool(":som", true);
|
||||
simp_p.set_bool(":eq2ineq", true);
|
||||
simp_p.set_bool(":elim-and", true);
|
||||
simp_p.set_bool(":blast-distinct", true);
|
||||
|
||||
params_ref simp2_p = p;
|
||||
simp2_p.set_bool(":mul-to-power", true);
|
||||
|
||||
return and_then(using_params(mk_simplify_tactic(m, p),
|
||||
simp_p),
|
||||
using_params(mk_simplify_tactic(m, p),
|
||||
simp2_p),
|
||||
mk_subpaving_tactic_core(m, p));
|
||||
}
|
||||
|
||||
|
28
src/math/subpaving/tactic/subpaving_tactic.h
Normal file
28
src/math/subpaving/tactic/subpaving_tactic.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
subpaving_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
"Fake" tactic used to test subpaving module.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-08-07.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef __SUBPAVING_TACTIC_H_
|
||||
#define __SUBPAVING_TACTIC_H_
|
||||
|
||||
#include"params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_subpaving_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue