3
0
Fork 0
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:
Leonardo de Moura 2012-10-23 21:53:34 -07:00
parent b89d35dd69
commit 9e299b88c4
101 changed files with 16 additions and 16 deletions

View 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);
}

View 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

View 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
View 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_ */

File diff suppressed because it is too large Load diff

View 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

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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);
}
};

View 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

File diff suppressed because it is too large Load diff

View 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):18531859, 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

View 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

View 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

View 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
};

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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):18531859, 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

View 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):18531859, 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

View 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);
}
};

View 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

View 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>;

View 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

View 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>;

View 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

View 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>;

View 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

View 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>;

View 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

View 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>;

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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);
}

View 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

View 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));
}

View 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