3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-15 05:18:44 +00:00

debug emons

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
This commit is contained in:
Lev Nachmanson 2019-04-30 10:25:05 -07:00
parent 82bf62f5fa
commit 8cdf754990
7 changed files with 360 additions and 329 deletions

View file

@ -493,6 +493,7 @@ namespace smt {
TRACE("lemma", tout << strm.str() << "\n";);
display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic);
out.close();
if (m_lemma_id==6998) exit(0);
return m_lemma_id;
}

View file

@ -3,19 +3,19 @@
Module Name:
emonomials.cpp
emonomials.cpp
Abstract:
table that associate monomials to congruence class representatives modulo a union find structure.
table that associate monomials to congruence class representatives modulo a union find structure.
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
Revision History:
to replace rooted_mons.h and rooted_mon, rooted_mon_tabled
to replace rooted_mons.h and rooted_mon, rooted_mon_tabled
--*/
@ -26,342 +26,360 @@
namespace nla {
void emonomials::inc_visited() const {
void emonomials::inc_visited() const {
++m_visited;
if (m_visited == 0) {
for (auto& svt : m_monomials) {
svt.visited() = 0;
}
++m_visited;
if (m_visited == 0) {
for (auto& svt : m_monomials) {
svt.visited() = 0;
}
++m_visited;
}
}
}
void emonomials::push() {
m_lim.push_back(m_monomials.size());
m_region.push_scope();
m_ve.push();
}
void emonomials::push() {
m_lim.push_back(m_monomials.size());
m_region.push_scope();
m_ve.push();
SASSERT(monomials_are_canonized());
}
void emonomials::pop(unsigned n) {
m_ve.pop(n);
unsigned old_sz = m_lim[m_lim.size() - n];
for (unsigned i = m_monomials.size(); i-- > old_sz; ) {
monomial & m = m_monomials[i];
remove_cg(i, m);
m_var2index[m.var()] = UINT_MAX;
lpvar last_var = UINT_MAX;
for (lpvar v : m.vars()) {
if (v != last_var) {
remove_cell(m_use_lists[v], i);
last_var = v;
}
}
}
m_monomials.shrink(old_sz);
m_monomials.shrink(old_sz);
m_region.pop_scope(n);
m_lim.shrink(m_lim.size() - n);
}
void emonomials::remove_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* old_head = cur_head->m_next;
if (old_head == cur_head) {
cur_head = nullptr;
cur_tail = nullptr;
}
else {
cur_head = old_head;
cur_tail->m_next = old_head;
}
}
void emonomials::insert_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* new_head = new (m_region) cell(mIndex, cur_head);
cur_head = new_head;
if (!cur_tail) cur_tail = new_head;
cur_tail->m_next = new_head;
}
void emonomials::merge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (root_head == nullptr) {
root_head = other_head;
root_tail = other_tail;
}
else if (other_head) {
// other_head -> other_tail -> root_head --> root_tail -> other_head.
root_tail->m_next = other_head;
other_tail->m_next = root_head;
root_head = other_head;
}
else {
// other_head = other_tail = nullptr
}
}
void emonomials::unmerge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (other_head == nullptr) {
// no-op
}
else if (root_tail == other_tail) {
root_head = nullptr;
root_tail = nullptr;
}
else {
root_head = other_tail->m_next;
root_tail->m_next = root_head;
other_tail->m_next = other_head;
}
}
emonomials::cell* emonomials::head(lpvar v) const {
v = m_ve.find(v).var();
m_use_lists.reserve(v + 1);
return m_use_lists[v].m_head;
}
monomial const* emonomials::find_canonical(svector<lpvar> const& vars) const {
SASSERT(m_ve.is_root(vars));
m_find_key = vars;
std::sort(m_find_key.begin(), m_find_key.end());
monomial const* result = nullptr;
lpvar w;
if (m_cg_table.find(UINT_MAX, w)) {
result = &m_monomials[m_var2index[w]];
}
return result;
}
void emonomials::remove_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
remove_cg(idx, m);
}
}
while (c != first);
}
void emonomials::remove_cg(unsigned idx, monomial& m) {
monomial& sv = m_monomials[idx];
unsigned next = sv.next();
unsigned prev = sv.prev();
lpvar u = m.var(), w;
// equivalence class of u:
if (m_cg_table.find(u, w) && w == u) {
m_cg_table.erase(u);
// insert other representative:
if (prev != idx) {
m_cg_table.insert(m_monomials[prev].var());
}
}
if (prev != idx) {
m_monomials[next].prev() = prev;
m_monomials[prev].next() = next;
sv.next() = idx;
sv.prev() = idx;
}
}
/**
\brief insert canonized monomials using v into a congruence table.
Prior to insertion, the monomials are canonized according to the current
variable equivalences. The canonized monomials (monomial) are considered
in the same equivalence class if they have the same set of representative
variables. Their signs may differ.
*/
void emonomials::insert_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
insert_cg(idx, m);
}
}
while (c != first);
}
void emonomials::insert_cg(unsigned idx, monomial & m) {
do_canonize(m);
lpvar v = m.var(), w;
if (m_cg_table.find(v, w)) {
SASSERT(w != v);
unsigned idxr = m_var2index[w];
unsigned idxl = m_monomials[idxr].prev();
m_monomials[idx].next() = idxr;
m_monomials[idx].prev() = idxl;
m_monomials[idxr].prev() = idx;
m_monomials[idxl].next() = idx;
}
else {
m_cg_table.insert(v);
SASSERT(m_monomials[idx].next() == idx);
SASSERT(m_monomials[idx].prev() == idx);
}
}
void emonomials::set_visited(monomial& m) const {
m_monomials[m_var2index[m.var()]].visited() = m_visited;
}
bool emonomials::is_visited(monomial const& m) const {
return m_visited == m_monomials[m_var2index[m.var()]].visited();
}
/**
\brief insert a new monomial.
Assume that the variables are canonical, that is, not equal in current
context to another variable. The monomial is inserted into a congruence
class of equal up-to var_eqs monomials.
*/
void emonomials::add(lpvar v, unsigned sz, lpvar const* vs) {
unsigned idx = m_monomials.size();
m_monomials.push_back(monomial(v, sz, vs, idx));
void emonomials::pop(unsigned n) {
m_ve.pop(n);
unsigned old_sz = m_lim[m_lim.size() - n];
for (unsigned i = m_monomials.size(); i-- > old_sz; ) {
monomial & m = m_monomials[i];
remove_cg(i, m);
m_var2index[m.var()] = UINT_MAX;
lpvar last_var = UINT_MAX;
for (unsigned i = 0; i < sz; ++i) {
lpvar w = vs[i];
SASSERT(m_ve.is_root(w));
if (w != last_var) {
m_use_lists.reserve(w + 1);
insert_cell(m_use_lists[w], idx);
last_var = w;
}
}
SASSERT(m_ve.is_root(v));
m_var2index.setx(v, idx, UINT_MAX);
insert_cg(idx, m_monomials[idx]);
}
void emonomials::do_canonize(monomial & m) const {
m.reset_rfields();
for (lpvar v : m.vars()) {
m.push_rvar(m_ve.find(v));
}
m.sort_rvars();
}
bool emonomials::canonize_divides(monomial& m, monomial & n) const {
if (m.size() > n.size()) return false;
unsigned ms = m.size(), ns = n.size();
unsigned i = 0, j = 0;
while (true) {
if (i == ms) {
return true;
}
else if (j == ns) {
return false;
}
else if (m.rvars()[i] == n.rvars()[j]) {
++i; ++j;
}
else if (m.rvars()[i] < n.rvars()[j]) {
return false;
}
else {
++j;
if (v != last_var) {
remove_cell(m_use_lists[v], i);
last_var = v;
}
}
}
m_monomials.shrink(old_sz);
m_monomials.shrink(old_sz);
m_region.pop_scope(n);
m_lim.shrink(m_lim.size() - n);
SASSERT(monomials_are_canonized());
}
// yes, assume that monomials are non-empty.
emonomials::pf_iterator::pf_iterator(emonomials const& m, monomial & mon, bool at_end):
m_em(m), m_mon(&mon), m_it(iterator(m, m.head(mon.vars()[0]), at_end)), m_end(iterator(m, m.head(mon.vars()[0]), true)) {
fast_forward();
void emonomials::remove_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* old_head = cur_head->m_next;
if (old_head == cur_head) {
cur_head = nullptr;
cur_tail = nullptr;
}
emonomials::pf_iterator::pf_iterator(emonomials const& m, lpvar v, bool at_end):
m_em(m), m_mon(nullptr), m_it(iterator(m, m.head(v), at_end)), m_end(iterator(m, m.head(v), true)) {
fast_forward();
else {
cur_head = old_head;
cur_tail->m_next = old_head;
}
}
void emonomials::pf_iterator::fast_forward() {
for (; m_it != m_end; ++m_it) {
if (m_mon && m_mon->var() != (*m_it).var() && m_em.canonize_divides(*m_mon, *m_it) && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
if (!m_mon && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
}
void emonomials::insert_cell(head_tail& v, unsigned mIndex) {
cell*& cur_head = v.m_head;
cell*& cur_tail = v.m_tail;
cell* new_head = new (m_region) cell(mIndex, cur_head);
cur_head = new_head;
if (!cur_tail) cur_tail = new_head;
cur_tail->m_next = new_head;
}
void emonomials::merge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (root_head == nullptr) {
root_head = other_head;
root_tail = other_tail;
}
else if (other_head) {
// other_head -> other_tail -> root_head --> root_tail -> other_head.
root_tail->m_next = other_head;
other_tail->m_next = root_head;
root_head = other_head;
}
else {
// other_head = other_tail = nullptr
}
}
void emonomials::merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
void emonomials::unmerge_cells(head_tail& root, head_tail& other) {
if (&root == &other) return;
cell*& root_head = root.m_head;
cell*& root_tail = root.m_tail;
cell* other_head = other.m_head;
cell* other_tail = other.m_tail;
if (other_head == nullptr) {
// no-op
}
else if (root_tail == other_tail) {
root_head = nullptr;
root_tail = nullptr;
}
else {
root_head = other_tail->m_next;
root_tail->m_next = root_head;
other_tail->m_next = other_head;
}
}
void emonomials::after_merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
if (!r2.sign() && m_ve.find(~r2) != m_ve.find(r1)) {
m_use_lists.reserve(std::max(r2.var(), r1.var()) + 1);
rehash_cg(r1.var());
merge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
}
emonomials::cell* emonomials::head(lpvar v) const {
v = m_ve.find(v).var();
m_use_lists.reserve(v + 1);
return m_use_lists[v].m_head;
}
monomial const* emonomials::find_canonical(svector<lpvar> const& vars) const {
SASSERT(m_ve.is_root(vars));
m_find_key = vars;
std::sort(m_find_key.begin(), m_find_key.end());
monomial const* result = nullptr;
lpvar w;
if (m_cg_table.find(UINT_MAX, w)) {
result = &m_monomials[m_var2index[w]];
}
return result;
}
void emonomials::remove_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
remove_cg(idx, m);
}
}
while (c != first);
}
void emonomials::remove_cg(unsigned idx, monomial& m) {
monomial& sv = m_monomials[idx];
unsigned next = sv.next();
unsigned prev = sv.prev();
lpvar u = m.var(), w;
// equivalence class of u:
if (m_cg_table.find(u, w) && w == u) {
m_cg_table.erase(u);
// insert other representative:
if (prev != idx) {
m_cg_table.insert(m_monomials[prev].var());
}
}
if (prev != idx) {
m_monomials[next].prev() = prev;
m_monomials[prev].next() = next;
sv.next() = idx;
sv.prev() = idx;
}
}
/**
\brief insert canonized monomials using v into a congruence table.
Prior to insertion, the monomials are canonized according to the current
variable equivalences. The canonized monomials (monomial) are considered
in the same equivalence class if they have the same set of representative
variables. Their signs may differ.
*/
void emonomials::insert_cg(lpvar v) {
cell* c = m_use_lists[v].m_head;
if (c == nullptr) {
return;
}
void emonomials::unmerge_eh(signed_var r2, signed_var r1) {
if (!r2.sign() && m_ve.find(~r2) != m_ve.find(r1)) {
unmerge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
rehash_cg(r1.var());
}
cell* first = c;
inc_visited();
do {
unsigned idx = c->m_index;
c = c->m_next;
monomial & m = m_monomials[idx];
if (!is_visited(m)) {
set_visited(m);
insert_cg(idx, m);
}
}
while (c != first);
}
void emonomials::insert_cg(unsigned idx, monomial & m) {
do_canonize(m);
lpvar v = m.var(), w;
if (m_cg_table.find(v, w)) {
SASSERT(w != v);
unsigned idxr = m_var2index[w];
unsigned idxl = m_monomials[idxr].prev();
m_monomials[idx].next() = idxr;
m_monomials[idx].prev() = idxl;
m_monomials[idxr].prev() = idx;
m_monomials[idxl].next() = idx;
}
else {
m_cg_table.insert(v);
SASSERT(m_monomials[idx].next() == idx);
SASSERT(m_monomials[idx].prev() == idx);
}
}
void emonomials::set_visited(monomial& m) const {
m_monomials[m_var2index[m.var()]].visited() = m_visited;
}
bool emonomials::is_visited(monomial const& m) const {
return m_visited == m_monomials[m_var2index[m.var()]].visited();
}
/**
\brief insert a new monomial.
Assume that the variables are canonical, that is, not equal in current
context to another variable. The monomial is inserted into a congruence
class of equal up-to var_eqs monomials.
*/
void emonomials::add(lpvar v, unsigned sz, lpvar const* vs) {
unsigned idx = m_monomials.size();
m_monomials.push_back(monomial(v, sz, vs, idx));
lpvar last_var = UINT_MAX;
for (unsigned i = 0; i < sz; ++i) {
lpvar w = vs[i];
SASSERT(m_ve.is_root(w));
if (w != last_var) {
m_use_lists.reserve(w + 1);
insert_cell(m_use_lists[w], idx);
last_var = w;
}
}
SASSERT(m_ve.is_root(v));
m_var2index.setx(v, idx, UINT_MAX);
insert_cg(idx, m_monomials[idx]);
}
void emonomials::do_canonize(monomial & m) const {
m.reset_rfields();
for (lpvar v : m.vars()) {
m.push_rvar(m_ve.find(v));
}
m.sort_rvars();
}
bool emonomials::is_canonized(const monomial & m) const {
monomial mm(m);
do_canonize(mm);
return mm.rvars() == m.rvars();
}
bool emonomials:: monomials_are_canonized() const {
for (auto & m: m_monomials) {
if (! is_canonized(m)) {
return false;
}
}
return true;
}
bool emonomials::canonize_divides(monomial& m, monomial & n) const {
if (m.size() > n.size()) return false;
unsigned ms = m.size(), ns = n.size();
unsigned i = 0, j = 0;
while (true) {
if (i == ms) {
return true;
}
else if (j == ns) {
return false;
}
else if (m.rvars()[i] == n.rvars()[j]) {
++i; ++j;
}
else if (m.rvars()[i] < n.rvars()[j]) {
return false;
}
else {
++j;
}
}
}
// yes, assume that monomials are non-empty.
emonomials::pf_iterator::pf_iterator(emonomials const& m, monomial & mon, bool at_end):
m_em(m), m_mon(&mon), m_it(iterator(m, m.head(mon.vars()[0]), at_end)), m_end(iterator(m, m.head(mon.vars()[0]), true)) {
fast_forward();
}
emonomials::pf_iterator::pf_iterator(emonomials const& m, lpvar v, bool at_end):
m_em(m), m_mon(nullptr), m_it(iterator(m, m.head(v), at_end)), m_end(iterator(m, m.head(v), true)) {
fast_forward();
}
void emonomials::pf_iterator::fast_forward() {
for (; m_it != m_end; ++m_it) {
if (m_mon && m_mon->var() != (*m_it).var() && m_em.canonize_divides(*m_mon, *m_it) && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
if (!m_mon && !m_em.is_visited(*m_it)) {
m_em.set_visited(*m_it);
break;
}
}
}
void emonomials::merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
// no-op
}
void emonomials::after_merge_eh(signed_var r2, signed_var r1, signed_var v2, signed_var v1) {
if (!r2.sign() && m_ve.find(~r2) != m_ve.find(r1)) {
m_use_lists.reserve(std::max(r2.var(), r1.var()) + 1);
rehash_cg(r1.var());
merge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
}
}
void emonomials::unmerge_eh(signed_var r2, signed_var r1) {
if (!r2.sign() && m_ve.find(~r2) != m_ve.find(r1)) {
unmerge_cells(m_use_lists[r2.var()], m_use_lists[r1.var()]);
rehash_cg(r1.var());
}
}
std::ostream& emonomials::display(const core& cr, std::ostream& out) const {
out << "monomials\n";
unsigned idx = 0;
for (auto const& m : m_monomials) {
out << (idx++) << ": " << pp_mon(cr, m) << "\n";
}
out << "use lists\n";
idx = 0;
for (auto const& ht : m_use_lists) {
cell* c = ht.m_head;
if (c) {
out << "v" << idx << ": ";
do {
out << c->m_index << " ";
c = c->m_next;
}
while (c != ht.m_head);
out << "\n";
}
++idx;
}
return out;
}
out << "monomials\n";
unsigned idx = 0;
for (auto const& m : m_monomials) {
out << (idx++) << ": " << pp_rmon(cr, m) << "\n";
}
out << "use lists\n";
idx = 0;
for (auto const& ht : m_use_lists) {
cell* c = ht.m_head;
if (c) {
out << "v" << idx << ": ";
do {
out << c->m_index << " ";
c = c->m_next;
}
while (c != ht.m_head);
out << "\n";
}
++idx;
}
return out;
}
}

View file

@ -100,7 +100,6 @@ class emonomials : public var_eqs_merge_handler {
void rehash_cg(lpvar v) { remove_cg(v); insert_cg(v); }
void do_canonize(monomial& m) const;
cell* head(lpvar v) const;
void set_visited(monomial& m) const;
bool is_visited(monomial const& m) const;
@ -141,7 +140,9 @@ public:
*/
monomial const& operator[](lpvar v) const { return m_monomials[m_var2index[v]]; }
monomial & operator[](lpvar v) { return m_monomials[m_var2index[v]]; }
bool is_canonized(const monomial&) const;
bool monomials_are_canonized() const;
/**
\brief obtain the representative canonized monomial
*/
@ -204,7 +205,9 @@ public:
public:
pf_iterator(emonomials const& m, monomial& mon, bool at_end);
pf_iterator(emonomials const& m, lpvar v, bool at_end);
monomial & operator*() { return *m_it; }
monomial & operator*() {
return *m_it;
}
pf_iterator& operator++() { ++m_it; fast_forward(); return *this; }
pf_iterator operator++(int) { pf_iterator tmp = *this; ++*this; return tmp; }
bool operator==(pf_iterator const& other) const { return m_it == other.m_it; }

View file

@ -63,7 +63,7 @@ public:
svector<lpvar> const& rvars() const { return m_rvars; }
bool sign() const { return m_rsign; }
rational rsign() const { return rational(m_rsign ? -1 : 1); }
void reset_rfields() { m_rsign = false; m_rvars.reset(); }
void reset_rfields() { m_rsign = false; m_rvars.reset(); SASSERT(m_rvars.size() == 0); }
void push_rvar(signed_var sv) { m_rsign ^= sv.sign(); m_rvars.push_back(sv.var()); }
void sort_rvars() {
std::sort(m_rvars.begin(), m_rvars.end());

View file

@ -121,11 +121,13 @@ bool basics::basic_sign_lemma_on_mon(lpvar v, std::unordered_set<unsigned> & exp
if (!try_insert(v, explored)) {
return false;
}
const monomial& m_v = c().m_emons[v];
TRACE("nla_solver_details", tout << "mon = " << pp_mon(c(), m_v););
TRACE("nla_solver", tout << "m_v = " << pp_rmon(c(), m_v););
SASSERT(c().m_emons.is_canonized(m_v));
for (auto const& m : c().m_emons.enum_sign_equiv_monomials(v)) {
TRACE("nla_solver_details", tout << "m = " << pp_rmon(c(), m););
SASSERT(m.rvars() == m_v.rvars());
if (m_v.var() != m.var() && basic_sign_lemma_on_two_monomials(m_v, m) && done())
return true;
}
@ -154,12 +156,14 @@ bool basics::basic_sign_lemma(bool derived) {
void basics::generate_sign_lemma(const monomial& m, const monomial& n, const rational& sign) {
add_empty_lemma();
TRACE("nla_solver",
tout << "m = "; c().print_monomial_with_vars(m, tout);
tout << "n = "; c().print_monomial_with_vars(n, tout);
tout << "m = " << pp_rmon(_(), m);
tout << "n = " << pp_rmon(_(), n);
);
c().mk_ineq(m.var(), -sign, n.var(), llc::EQ);
explain(m);
TRACE("nla_solver", tout << "m exp = "; _().print_explanation(_().current_expl(), tout););
explain(n);
TRACE("nla_solver", tout << "n exp = "; _().print_explanation(_().current_expl(), tout););
TRACE("nla_solver", c().print_lemma(tout););
}
// try to find a variable j such that val(j) = 0

View file

@ -112,7 +112,9 @@ void core::push() {
void core::pop(unsigned n) {
TRACE("nla_solver", tout << "n = " << n << "\n";);
m_emons.pop(n);
TRACE("nla_solver", tout << "before pop mons = " << pp_emons(*this, m_emons) << "\n";);
m_emons.pop(n);
TRACE("nla_solver", tout << "after pop mons = " << pp_emons(*this, m_emons) << "\n";);
}
rational core::product_value(const unsigned_vector & m) const {
@ -718,7 +720,8 @@ std::ostream & core::print_var(lpvar j, std::ostream & out) const {
out << " = " << val(j);;
}
m_lar_solver.print_column_info(j, out) <<";";
m_lar_solver.print_column_info(j, out);
out << "root=" << m_evars.find(j) << "\n";
return out;
}

View file

@ -120,6 +120,8 @@ void var_eqs::explain_dfs(signed_var v1, signed_var v2, lp::explanation& e) cons
}
void var_eqs::explain_bfs(signed_var v1, signed_var v2, lp::explanation& e) const {
static int ddd=0;
TRACE("nla_solver", tout << ++ddd << "\n";);
SASSERT(find(v1) == find(v2));
if (v1 == v2) {
return;