Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
z3-z3-4.12.6.src.math.lp.nla_core.cpp Maven / Gradle / Ivy
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
nla_core.cpp
Author:
Lev Nachmanson (levnach)
Nikolaj Bjorner (nbjorner)
--*/
#include "util/uint_set.h"
#include "math/lp/nla_core.h"
#include "math/lp/factorization_factory_imp.h"
#include "math/lp/nex.h"
#include "math/grobner/pdd_solver.h"
#include "math/dd/pdd_interval.h"
#include "math/dd/pdd_eval.h"
using namespace nla;
typedef lp::lar_term term;
core::core(lp::lar_solver& s, params_ref const& p, reslimit & lim) :
m_evars(),
lra(s),
m_reslim(lim),
m_params(p),
m_tangents(this),
m_basics(this),
m_order(this),
m_monotone(this),
m_powers(*this),
m_divisions(*this),
m_intervals(this, lim),
m_monomial_bounds(this),
m_horner(this),
m_grobner(this),
m_emons(m_evars),
m_use_nra_model(false),
m_nra(s, m_nra_lim, *this)
{
m_nlsat_delay_bound = lp_settings().nlsat_delay();
lra.m_find_monics_with_changed_bounds_func = [&](const indexed_uint_set& columns_with_changed_bounds) {
for (lpvar j : columns_with_changed_bounds) {
if (is_monic_var(j))
m_monics_with_changed_bounds.insert(j);
for (const auto & m: m_emons.get_use_list(j))
m_monics_with_changed_bounds.insert(m.var());
}
};
}
bool core::compare_holds(const rational& ls, llc cmp, const rational& rs) const {
switch(cmp) {
case llc::LE: return ls <= rs;
case llc::LT: return ls < rs;
case llc::GE: return ls >= rs;
case llc::GT: return ls > rs;
case llc::EQ: return ls == rs;
case llc::NE: return ls != rs;
default: SASSERT(false);
};
return false;
}
rational core::value(const lp::lar_term& r) const {
rational ret(0);
for (lp::lar_term::ival t : r)
ret += t.coeff() * val(t.j());
return ret;
}
bool core::ineq_holds(const ineq& n) const {
return compare_holds(value(n.term()), n.cmp(), n.rs());
}
bool core::lemma_holds(const lemma& l) const {
for (const ineq &i : l.ineqs())
if (ineq_holds(i))
return true;
return false;
}
lpvar core::map_to_root(lpvar j) const {
return m_evars.find(j).var();
}
svector core::sorted_rvars(const factor& f) const {
if (f.is_var()) {
svector r; r.push_back(map_to_root(f.var()));
return r;
}
return m_emons[f.var()].rvars();
}
// the value of the factor is equal to the value of the variable multiplied
// by the canonize_sign
bool core::canonize_sign(const factor& f) const {
return f.sign() ^ (f.is_var()? canonize_sign(f.var()) : canonize_sign(m_emons[f.var()]));
}
bool core::canonize_sign(lpvar j) const {
return m_evars.find(j).sign();
}
bool core::canonize_sign_is_correct(const monic& m) const {
bool r = false;
for (lpvar j : m.vars()) {
r ^= canonize_sign(j);
}
return r == m.rsign();
}
bool core::canonize_sign(const monic& m) const {
SASSERT(canonize_sign_is_correct(m));
return m.rsign();
}
bool core::canonize_sign(const factorization& f) const {
bool r = false;
for (const factor & a : f)
r ^= canonize_sign(a);
return r;
}
void core::add_monic(lpvar v, unsigned sz, lpvar const* vs) {
m_add_buffer.resize(sz);
for (unsigned i = 0; i < sz; i++) {
m_add_buffer[i] = vs[i];
}
m_emons.add(v, m_add_buffer);
m_monics_with_changed_bounds.insert(v);
}
void core::push() {
TRACE("nla_solver_verbose", tout << "\n";);
m_emons.push();
}
void core::pop(unsigned n) {
TRACE("nla_solver_verbose", tout << "n = " << n << "\n";);
m_emons.pop(n);
SASSERT(elists_are_consistent(false));
}
rational core::product_value(const monic& m) const {
rational r(1);
for (auto j : m.vars()) {
r *= lra.get_column_value(j).x;
}
return r;
}
// return true iff the monic value is equal to the product of the values of the factors
// or if the variable associated with the monomial is not relevant.
bool core::check_monic(const monic& m) const {
#if 0
// TODO test this
if (!is_relevant(m.var()))
return true;
#endif
if (lra.column_is_int(m.var()) && !lra.get_column_value(m.var()).is_int())
return true;
bool ret = product_value(m) == lra.get_column_value(m.var()).x;
CTRACE("nla_solver_check_monic", !ret, print_monic(m, tout) << '\n';);
return ret;
}
template
std::ostream& core::print_product(const T & m, std::ostream& out) const {
bool first = true;
for (lpvar v : m) {
if (!first) out << "*"; else first = false;
if (lp_settings().print_external_var_name())
out << "(" << lra.get_variable_name(v) << "=" << val(v) << ")";
else
out << "(j" << v << " = " << val(v) << ")";
}
return out;
}
template
std::string core::product_indices_str(const T & m) const {
std::stringstream out;
bool first = true;
for (lpvar v : m) {
if (!first)
out << "*";
else
first = false;
out << "j" << v;;
}
return out.str();
}
std::ostream & core::print_factor(const factor& f, std::ostream& out) const {
if (f.sign())
out << "- ";
if (f.is_var()) {
out << "VAR, " << pp(f.var());
} else {
out << "MON, v" << m_emons[f.var()] << " = ";
print_product(m_emons[f.var()].rvars(), out);
}
out << "\n";
return out;
}
std::ostream & core::print_factor_with_vars(const factor& f, std::ostream& out) const {
if (f.is_var()) {
out << pp(f.var());
}
else {
out << " MON = " << pp_mon_with_vars(*this, m_emons[f.var()]);
}
return out;
}
std::ostream& core::print_monic(const monic& m, std::ostream& out) const {
if (lp_settings().print_external_var_name())
out << "([" << m.var() << "] = " << lra.get_variable_name(m.var()) << " = " << val(m.var()) << " = ";
else
out << "(j" << m.var() << " = " << val(m.var()) << " = ";
print_product(m.vars(), out) << ")\n";
return out;
}
std::ostream& core::print_bfc(const factorization& m, std::ostream& out) const {
SASSERT(m.size() == 2);
out << "( x = " << pp(m[0]) << "* y = " << pp(m[1]) << ")";
return out;
}
std::ostream& core::print_monic_with_vars(lpvar v, std::ostream& out) const {
return print_monic_with_vars(m_emons[v], out);
}
template
std::ostream& core::print_product_with_vars(const T& m, std::ostream& out) const {
print_product(m, out) << "\n";
for (unsigned k = 0; k < m.size(); k++) {
print_var(m[k], out);
}
return out;
}
std::ostream& core::print_monic_with_vars(const monic& m, std::ostream& out) const {
out << "[" << pp(m.var()) << "]\n";
out << "vars:"; print_product_with_vars(m.vars(), out) << "\n";
if (m.vars() == m.rvars())
out << "same rvars, and m.rsign = " << m.rsign() << " of course\n";
else {
out << "rvars:"; print_product_with_vars(m.rvars(), out) << "\n";
out << "rsign:" << m.rsign() << "\n";
}
return out;
}
std::ostream& core::print_explanation(const lp::explanation& exp, std::ostream& out) const {
out << "expl: ";
unsigned i = 0;
for (auto p : exp) {
out << "(" << p.ci() << ")";
lra.constraints().display(out, [this](lpvar j) { return var_str(j);}, p.ci());
if (++i < exp.size())
out << " ";
}
return out;
}
bool core::explain_upper_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const {
rational b(0); // the bound
for (lp::lar_term::ival p : t) {
rational pb;
if (explain_coeff_upper_bound(p, pb, e)) {
b += pb;
} else {
e.clear();
return false;
}
}
if (b > rs ) {
e.clear();
return false;
}
return true;
}
bool core::explain_lower_bound(const lp::lar_term& t, const rational& rs, lp::explanation& e) const {
rational b(0); // the bound
for (lp::lar_term::ival p : t) {
rational pb;
if (explain_coeff_lower_bound(p, pb, e)) {
b += pb;
} else {
e.clear();
return false;
}
}
if (b < rs ) {
e.clear();
return false;
}
return true;
}
bool core::explain_coeff_lower_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const {
const rational& a = p.coeff();
SASSERT(!a.is_zero());
if (a.is_pos()) {
auto* dep = lra.get_column_lower_bound_witness(p.j());
if (!dep)
return false;
bound = a * lra.get_lower_bound(p.j()).x;
lra.push_explanation(dep, e);
return true;
}
// a.is_neg()
auto* dep = lra.get_column_upper_bound_witness(p.j());
if (!dep)
return false;
bound = a * lra.get_upper_bound(p.j()).x;
lra.push_explanation(dep, e);
return true;
}
bool core::explain_coeff_upper_bound(const lp::lar_term::ival& p, rational& bound, lp::explanation& e) const {
const rational& a = p.coeff();
lpvar j = p.j();
SASSERT(!a.is_zero());
if (a.is_neg()) {
auto *dep = lra.get_column_lower_bound_witness(j);
if (!dep)
return false;
bound = a * lra.get_lower_bound(j).x;
lra.push_explanation(dep, e);
return true;
}
// a.is_pos()
auto* dep = lra.get_column_upper_bound_witness(j);
if (!dep)
return false;
bound = a * lra.get_upper_bound(j).x;
lra.push_explanation(dep, e);
return true;
}
// return true iff the negation of the ineq can be derived from the constraints
bool core::explain_ineq(new_lemma& lemma, const lp::lar_term& t, llc cmp, const rational& rs) {
// check that we have something like 0 < 0, which is always false and can be safely
// removed from the lemma
if (t.is_empty() && rs.is_zero() &&
(cmp == llc::LT || cmp == llc::GT || cmp == llc::NE)) return true;
lp::explanation exp;
bool r;
switch (negate(cmp)) {
case llc::LE:
r = explain_upper_bound(t, rs, exp);
break;
case llc::LT:
r = explain_upper_bound(t, rs - rational(1), exp);
break;
case llc::GE:
r = explain_lower_bound(t, rs, exp);
break;
case llc::GT:
r = explain_lower_bound(t, rs + rational(1), exp);
break;
case llc::EQ:
r = (explain_lower_bound(t, rs, exp) && explain_upper_bound(t, rs, exp)) ||
(rs.is_zero() && explain_by_equiv(t, exp));
break;
case llc::NE:
// TBD - NB: does this work for Reals?
r = explain_lower_bound(t, rs + rational(1), exp) || explain_upper_bound(t, rs - rational(1), exp);
break;
default:
UNREACHABLE();
return false;
}
if (r) {
lemma &= exp;
return true;
}
return false;
}
/**
* \brief
if t is an octagon term -+x -+ y try to explain why the term always is
equal zero
*/
bool core::explain_by_equiv(const lp::lar_term& t, lp::explanation& e) const {
lpvar i,j;
bool sign;
if (!is_octagon_term(t, sign, i, j))
return false;
if (m_evars.find(signed_var(i, false)) != m_evars.find(signed_var(j, sign)))
return false;
m_evars.explain(signed_var(i, false), signed_var(j, sign), e);
TRACE("nla_solver", tout << "explained :"; lra.print_term_as_indices(t, tout););
return true;
}
void core::mk_ineq_no_expl_check(new_lemma& lemma, lp::lar_term& t, llc cmp, const rational& rs) {
TRACE("nla_solver_details", lra.print_term_as_indices(t, tout << "t = "););
lemma |= ineq(cmp, t, rs);
CTRACE("nla_solver", ineq_holds(ineq(cmp, t, rs)), print_ineq(ineq(cmp, t, rs), tout) << "\n";);
SASSERT(!ineq_holds(ineq(cmp, t, rs)));
}
llc apply_minus(llc cmp) {
switch(cmp) {
case llc::LE: return llc::GE;
case llc::LT: return llc::GT;
case llc::GE: return llc::LE;
case llc::GT: return llc::LT;
default: break;
}
return cmp;
}
// the monics should be equal by modulo sign but this is not so in the model
void core::fill_explanation_and_lemma_sign(new_lemma& lemma, const monic& a, const monic & b, rational const& sign) {
SASSERT(sign == 1 || sign == -1);
lemma &= a;
lemma &= b;
TRACE("nla_solver", tout << "used constraints: " << lemma;);
SASSERT(lemma.num_ineqs() == 0);
lemma |= ineq(term(rational(1), a.var(), -sign, b.var()), llc::EQ, 0);
}
// Replaces each variable index by the root in the tree and flips the sign if the var comes with a minus.
// Also sorts the result.
//
svector core::reduce_monic_to_rooted(const svector & vars, rational & sign) const {
svector ret;
bool s = false;
for (lpvar v : vars) {
auto root = m_evars.find(v);
s ^= root.sign();
TRACE("nla_solver_eq",
tout << pp(v) << " mapped to " << pp(root.var()) << "\n";);
ret.push_back(root.var());
}
sign = rational(s? -1: 1);
std::sort(ret.begin(), ret.end());
return ret;
}
// Replaces definition m_v = v1* .. * vn by
// m_v = coeff * w1 * ... * wn, where w1, .., wn are canonical
// representatives, which are the roots of the equivalence tree, under current equations.
//
monic_coeff core::canonize_monic(monic const& m) const {
rational sign = rational(1);
svector vars = reduce_monic_to_rooted(m.vars(), sign);
return monic_coeff(vars, sign);
}
int core::vars_sign(const svector& v) {
int sign = 1;
for (lpvar j : v) {
sign *= nla::rat_sign(val(j));
if (sign == 0)
return 0;
}
return sign;
}
bool core::has_upper_bound(lpvar j) const {
return lra.column_has_upper_bound(j);
}
bool core::has_lower_bound(lpvar j) const {
return lra.column_has_lower_bound(j);
}
const rational& core::get_upper_bound(unsigned j) const {
return lra.get_upper_bound(j).x;
}
const rational& core::get_lower_bound(unsigned j) const {
return lra.get_lower_bound(j).x;
}
bool core::zero_is_an_inner_point_of_bounds(lpvar j) const {
if (has_upper_bound(j) && get_upper_bound(j) <= rational(0))
return false;
if (has_lower_bound(j) && get_lower_bound(j) >= rational(0))
return false;
return true;
}
int core::rat_sign(const monic& m) const {
int sign = 1;
for (lpvar j : m.vars()) {
auto v = val(j);
if (v.is_neg()) {
sign = - sign;
continue;
}
if (v.is_pos()) {
continue;
}
sign = 0;
break;
}
return sign;
}
// Returns true if the monic sign is incorrect
bool core::sign_contradiction(const monic& m) const {
return nla::rat_sign(var_val(m)) != rat_sign(m);
}
/*
unsigned_vector eq_vars(lpvar j) const {
TRACE("nla_solver_eq", tout << "j = " << pp(j) << "eqs = ";
for(auto jj : m_evars.eq_vars(j)) tout << pp(jj) << " ";
});
return m_evars.eq_vars(j);
}
*/
bool core::var_is_fixed_to_zero(lpvar j) const {
return
lra.column_is_fixed(j) &&
lra.get_lower_bound(j) == lp::zero_of_type();
}
bool core::var_is_fixed_to_val(lpvar j, const rational& v) const {
return
lra.column_is_fixed(j) &&
lra.get_lower_bound(j) == lp::impq(v);
}
bool core::var_is_fixed(lpvar j) const {
return lra.column_is_fixed(j);
}
bool core::var_is_free(lpvar j) const {
return lra.column_is_free(j);
}
std::ostream & core::print_ineq(const ineq & in, std::ostream & out) const {
lra.print_term_as_indices(in.term(), out);
return out << " " << lconstraint_kind_string(in.cmp()) << " " << in.rs();
}
std::ostream & core::print_var(lpvar j, std::ostream & out) const {
if (is_monic_var(j))
print_monic(m_emons[j], out);
lra.print_column_info(j, out);
signed_var jr = m_evars.find(j);
out << "root=";
if (jr.sign()) {
out << "-";
}
out << lra.get_variable_name(jr.var()) << "\n";
return out;
}
std::ostream & core::print_monics(std::ostream & out) const {
for (auto &m : m_emons) {
print_monic_with_vars(m, out);
}
return out;
}
std::ostream & core::print_ineqs(const lemma& l, std::ostream & out) const {
std::unordered_set vars;
out << "ineqs: ";
if (l.ineqs().size() == 0) {
out << "conflict\n";
} else {
for (unsigned i = 0; i < l.ineqs().size(); i++) {
auto & in = l.ineqs()[i];
print_ineq(in, out);
if (i + 1 < l.ineqs().size()) out << " or ";
for (lp::lar_term::ival p: in.term())
vars.insert(p.j());
}
out << std::endl;
for (lpvar j : vars) {
print_var(j, out);
}
out << "\n";
}
return out;
}
std::ostream & core::print_factorization(const factorization& f, std::ostream& out) const {
if (f.is_mon()){
out << "is_mon " << pp_mon(*this, f.mon());
}
else {
for (unsigned k = 0; k < f.size(); k++ ) {
out << "(" << pp(f[k]) << ")";
if (k < f.size() - 1)
out << "*";
}
}
return out;
}
bool core::find_canonical_monic_of_vars(const svector& vars, lpvar & i) const {
monic const* sv = m_emons.find_canonical(vars);
return sv && (i = sv->var(), true);
}
bool core::is_canonical_monic(lpvar j) const {
return m_emons.is_canonical_monic(j);
}
void core::trace_print_monic_and_factorization(const monic& rm, const factorization& f, std::ostream& out) const {
out << "rooted vars: ";
print_product(rm.rvars(), out) << "\n";
out << "mon: " << pp_mon(*this, rm.var()) << "\n";
out << "value: " << var_val(rm) << "\n";
print_factorization(f, out << "fact: ") << "\n";
}
bool core::var_has_positive_lower_bound(lpvar j) const {
return lra.column_has_lower_bound(j) && lra.get_lower_bound(j) > lp::zero_of_type();
}
bool core::var_has_negative_upper_bound(lpvar j) const {
return lra.column_has_upper_bound(j) && lra.get_upper_bound(j) < lp::zero_of_type();
}
bool core::var_is_separated_from_zero(lpvar j) const {
return
var_has_negative_upper_bound(j) ||
var_has_positive_lower_bound(j);
}
bool core::vars_are_equiv(lpvar a, lpvar b) const {
SASSERT(abs(val(a)) == abs(val(b)));
return m_evars.vars_are_equiv(a, b);
}
bool core::has_zero_factor(const factorization& factorization) const {
for (factor f : factorization) {
if (val(f).is_zero())
return true;
}
return false;
}
template
bool core::mon_has_zero(const T& product) const {
for (lpvar j: product) {
if (val(j).is_zero())
return true;
}
return false;
}
template bool core::mon_has_zero(const unsigned_vector& product) const;
lp::lp_settings& core::lp_settings() {
return lra.settings();
}
const lp::lp_settings& core::lp_settings() const {
return lra.settings();
}
unsigned core::random() { return lp_settings().random_next(); }
// we look for octagon constraints here, with a left part +-x +- y
void core::collect_equivs() {
const lp::lar_solver& s = lra;
for (const auto * t : s.terms()) {
if (!s.column_associated_with_row(t->j()))
continue;
lpvar j = t->j();
if (var_is_fixed_to_zero(j)) {
TRACE("nla_solver_mons", s.print_term_as_indices(*t, tout << "term = ") << "\n";);
add_equivalence_maybe(t, s.get_column_upper_bound_witness(j), s.get_column_lower_bound_witness(j));
}
}
m_emons.ensure_canonized();
}
// returns true iff the term is in a form +-x-+y.
// the sign is true iff the term is x+y, -x-y.
bool core::is_octagon_term(const lp::lar_term& t, bool & sign, lpvar& i, lpvar &j) const {
if (t.size() != 2)
return false;
bool seen_minus = false;
bool seen_plus = false;
i = null_lpvar;
for(lp::lar_term::ival p : t) {
const auto & c = p.coeff();
if (c == 1) {
seen_plus = true;
} else if (c == - 1) {
seen_minus = true;
} else {
return false;
}
if (i == null_lpvar)
i = p.j();
else
j = p.j();
}
SASSERT(j != null_lpvar);
sign = (seen_minus && seen_plus)? false : true;
return true;
}
void core::add_equivalence_maybe(const lp::lar_term* t, u_dependency* c0, u_dependency* c1) {
bool sign;
lpvar i, j;
if (!is_octagon_term(*t, sign, i, j))
return;
if (sign)
m_evars.merge_minus(i, j, eq_justification({c0, c1}));
else
m_evars.merge_plus(i, j, eq_justification({c0, c1}));
}
// x is equivalent to y if x = +- y
void core::init_vars_equivalence() {
collect_equivs();
// SASSERT(tables_are_ok());
}
bool core::vars_table_is_ok() const {
// return m_var_eqs.is_ok();
return true;
}
bool core::rm_table_is_ok() const {
// return m_emons.is_ok();
return true;
}
bool core::tables_are_ok() const {
return vars_table_is_ok() && rm_table_is_ok();
}
bool core::var_is_a_root(lpvar j) const { return m_evars.is_root(j); }
template
bool core::vars_are_roots(const T& v) const {
for (lpvar j: v) {
if (!var_is_a_root(j))
return false;
}
return true;
}
template
void core::trace_print_rms(const T& p, std::ostream& out) {
out << "p = {\n";
for (auto j : p) {
out << "j = " << j << ", rm = " << m_emons[j] << "\n";
}
out << "}";
}
void core::print_monic_stats(const monic& m, std::ostream& out) {
if (m.size() == 2) return;
monic_coeff mc = canonize_monic(m);
for(unsigned i = 0; i < mc.vars().size(); i++){
if (abs(val(mc.vars()[i])) == rational(1)) {
auto vv = mc.vars();
vv.erase(vv.begin()+i);
monic const* sv = m_emons.find_canonical(vv);
if (!sv) {
out << "nf length" << vv.size() << "\n"; ;
}
}
}
}
void core::print_stats(std::ostream& out) {
}
void core::clear() {
m_lemmas.clear();
m_literals.clear();
m_fixed_equalities.clear();
m_equalities.clear();
m_conflicts = 0;
m_check_feasible = false;
}
void core::init_search() {
TRACE("nla_solver_mons", tout << "init\n";);
SASSERT(m_emons.invariant());
clear();
init_vars_equivalence();
SASSERT(m_emons.invariant());
SASSERT(elists_are_consistent(false));
}
void core::insert_to_refine(lpvar j) {
TRACE("lar_solver", tout << "j=" << j << '\n';);
m_to_refine.insert(j);
}
void core::erase_from_to_refine(lpvar j) {
TRACE("lar_solver", tout << "j=" << j << '\n';);
if (m_to_refine.contains(j))
m_to_refine.remove(j);
}
void core::init_to_refine() {
TRACE("nla_solver_details", tout << "emons:" << pp_emons(*this, m_emons););
m_to_refine.reset();
unsigned r = random(), sz = m_emons.number_of_monics();
for (unsigned k = 0; k < sz; k++) {
auto const & m = *(m_emons.begin() + (k + r)% sz);
if (!check_monic(m))
insert_to_refine(m.var());
}
TRACE("nla_solver",
tout << m_to_refine.size() << " mons to refine:\n";
for (lpvar v : m_to_refine) tout << pp_mon(*this, m_emons[v]) << ":error = " <<
(val(v) - mul_val(m_emons[v])).get_double() << "\n";);
}
std::unordered_set core::collect_vars(const lemma& l) const {
std::unordered_set vars;
auto insert_j = [&](lpvar j) {
vars.insert(j);
if (is_monic_var(j)) {
for (lpvar k : m_emons[j].vars())
vars.insert(k);
}
};
for (const auto& i : l.ineqs()) {
for (lp::lar_term::ival p : i.term()) {
insert_j(p.j());
}
}
for (auto p : l.expl()) {
const auto& c = lra.constraints()[p.ci()];
for (const auto& r : c.coeffs()) {
insert_j(r.second);
}
}
return vars;
}
// divides bc by c, so bc = b*c
bool core::divide(const monic& bc, const factor& c, factor & b) const {
svector c_rvars = sorted_rvars(c);
TRACE("nla_solver_div", tout << "c_rvars = "; print_product(c_rvars, tout); tout << "\nbc_rvars = "; print_product(bc.rvars(), tout););
if (!lp::is_proper_factor(c_rvars, bc.rvars()))
return false;
auto b_rvars = lp::vector_div(bc.rvars(), c_rvars);
TRACE("nla_solver_div", tout << "b_rvars = "; print_product(b_rvars, tout););
SASSERT(b_rvars.size() > 0);
if (b_rvars.size() == 1) {
b = factor(b_rvars[0], factor_type::VAR);
} else {
monic const* sv = m_emons.find_canonical(b_rvars);
if (sv == nullptr) {
TRACE("nla_solver_div", tout << "not in rooted";);
return false;
}
b = factor(sv->var(), factor_type::MON);
}
SASSERT(!b.sign());
// We have bc = canonize_sign(bc)*bc.rvars() = canonize_sign(b)*b.rvars()*canonize_sign(c)*c.rvars().
// Dividing by bc.rvars() we get canonize_sign(bc) = canonize_sign(b)*canonize_sign(c)
// Currently, canonize_sign(b) is 1, we might need to adjust it
b.sign() = canonize_sign(b) ^ canonize_sign(c) ^ canonize_sign(bc);
TRACE("nla_solver", tout << "success div:" << pp(b) << "\n";);
return true;
}
void core::negate_factor_equality(new_lemma& lemma, const factor& c,
const factor& d) {
if (c == d)
return;
lpvar i = var(c);
lpvar j = var(d);
auto iv = val(i), jv = val(j);
SASSERT(abs(iv) == abs(jv));
lemma |= ineq(term(i, rational(iv == jv ? -1 : 1), j), llc::NE, 0);
}
void core::negate_factor_relation(new_lemma& lemma, const rational& a_sign, const factor& a, const rational& b_sign, const factor& b) {
rational a_fs = sign_to_rat(canonize_sign(a));
rational b_fs = sign_to_rat(canonize_sign(b));
llc cmp = a_sign*val(a) < b_sign*val(b)? llc::GE : llc::LE;
lemma |= ineq(term(a_fs*a_sign, var(a), - b_fs*b_sign, var(b)), cmp, 0);
}
std::ostream& core::print_lemma(const lemma& l, std::ostream& out) const {
static int n = 0;
out << "lemma:" << ++n << " ";
print_ineqs(l, out);
print_explanation(l.expl(), out);
for (lpvar j : collect_vars(l)) {
print_var(j, out);
}
return out;
}
void core::trace_print_ol(const monic& ac,
const factor& a,
const factor& c,
const monic& bc,
const factor& b,
std::ostream& out) {
out << "ac = " << pp_mon(*this, ac) << "\n";
out << "bc = " << pp_mon(*this, bc) << "\n";
out << "a = ";
print_factor_with_vars(a, out);
out << ", \nb = ";
print_factor_with_vars(b, out);
out << "\nc = ";
print_factor_with_vars(c, out);
}
void core::maybe_add_a_factor(lpvar i,
const factor& c,
std::unordered_set& found_vars,
std::unordered_set& found_rm,
vector & r) const {
SASSERT(abs(val(i)) == abs(val(c)));
if (!is_monic_var(i)) {
i = m_evars.find(i).var();
if (try_insert(i, found_vars)) {
r.push_back(factor(i, factor_type::VAR));
}
} else {
if (try_insert(i, found_rm)) {
r.push_back(factor(i, factor_type::MON));
TRACE("nla_solver", tout << "inserting factor = "; print_factor_with_vars(factor(i, factor_type::MON), tout); );
}
}
}
// Returns rooted monics by arity
std::unordered_map core::get_rm_by_arity() {
std::unordered_map m;
for (auto const& mon : m_emons) {
unsigned arity = mon.vars().size();
auto it = m.find(arity);
if (it == m.end()) {
it = m.insert(it, std::make_pair(arity, unsigned_vector()));
}
it->second.push_back(mon.var());
}
return m;
}
bool core::rm_check(const monic& rm) const {
return check_monic(m_emons[rm.var()]);
}
bool core::has_relevant_monomial() const {
return any_of(emons(), [&](auto const& m) { return is_relevant(m.var()); });
}
bool core::find_bfc_to_refine_on_monic(const monic& m, factorization & bf) {
for (auto f : factorization_factory_imp(m, *this)) {
if (f.size() == 2) {
auto a = f[0];
auto b = f[1];
if (var_val(m) != val(a) * val(b)) {
bf = f;
TRACE("nla_solver", tout << "found bf";
tout << ":m:" << pp_mon_with_vars(*this, m) << "\n";
tout << "bf:"; print_bfc(bf, tout););
return true;
}
}
}
return false;
}
// finds a monic to refine with its binary factorization
bool core::find_bfc_to_refine(const monic* & m, factorization & bf){
m = nullptr;
unsigned r = random(), sz = m_to_refine.size();
for (unsigned k = 0; k < sz; k++) {
lpvar i = m_to_refine[(k + r) % sz];
m = &m_emons[i];
SASSERT (!check_monic(*m));
if (has_real(m))
continue;
if (m->size() == 2) {
bf.set_mon(m);
bf.push_back(factor(m->vars()[0], factor_type::VAR));
bf.push_back(factor(m->vars()[1], factor_type::VAR));
return true;
}
if (find_bfc_to_refine_on_monic(*m, bf)) {
TRACE("nla_solver",
tout << "bf = "; print_factorization(bf, tout);
tout << "\nval(*m) = " << var_val(*m) << ", should be = (val(bf[0])=" << val(bf[0]) << ")*(val(bf[1]) = " << val(bf[1]) << ") = " << val(bf[0])*val(bf[1]) << "\n";);
return true;
}
}
return false;
}
rational core::val(const factorization& f) const {
rational r(1);
for (const factor &p : f) {
r *= val(p);
}
return r;
}
new_lemma::new_lemma(core& c, char const* name):name(name), c(c) {
c.m_lemmas.push_back(lemma());
}
new_lemma& new_lemma::operator|=(ineq const& ineq) {
if (!c.explain_ineq(*this, ineq.term(), ineq.cmp(), ineq.rs())) {
CTRACE("nla_solver", c.ineq_holds(ineq), c.print_ineq(ineq, tout) << "\n";);
SASSERT(!c.ineq_holds(ineq));
current().push_back(ineq);
}
return *this;
}
new_lemma::~new_lemma() {
static int i = 0;
(void)i;
(void)name;
// code for checking lemma can be added here
if (current().is_conflict()) {
c.m_conflicts++;
}
TRACE("nla_solver", tout << name << " " << (++i) << "\n" << *this; );
}
lemma& new_lemma::current() const {
return c.m_lemmas.back();
}
new_lemma& new_lemma::operator&=(lp::explanation const& e) {
expl().add_expl(e);
return *this;
}
new_lemma& new_lemma::operator&=(const monic& m) {
for (lpvar j : m.vars())
*this &= j;
return *this;
}
new_lemma& new_lemma::operator&=(const factor& f) {
if (f.type() == factor_type::VAR)
*this &= f.var();
else
*this &= c.m_emons[f.var()];
return *this;
}
new_lemma& new_lemma::operator&=(const factorization& f) {
if (f.is_mon())
return *this;
for (const auto& fc : f) {
*this &= fc;
}
return *this;
}
new_lemma& new_lemma::operator&=(lpvar j) {
c.m_evars.explain(j, expl());
return *this;
}
new_lemma& new_lemma::explain_fixed(lpvar j) {
SASSERT(c.var_is_fixed(j));
explain_existing_lower_bound(j);
explain_existing_upper_bound(j);
return *this;
}
new_lemma& new_lemma::explain_equiv(lpvar a, lpvar b) {
SASSERT(abs(c.val(a)) == abs(c.val(b)));
if (c.vars_are_equiv(a, b)) {
*this &= a;
*this &= b;
} else {
explain_fixed(a);
explain_fixed(b);
}
return *this;
}
new_lemma& new_lemma::explain_var_separated_from_zero(lpvar j) {
SASSERT(c.var_is_separated_from_zero(j));
if (c.lra.column_has_upper_bound(j) &&
(c.lra.get_upper_bound(j)< lp::zero_of_type()))
explain_existing_upper_bound(j);
else
explain_existing_lower_bound(j);
return *this;
}
new_lemma& new_lemma::explain_existing_lower_bound(lpvar j) {
SASSERT(c.has_lower_bound(j));
lp::explanation ex;
c.lra.push_explanation(c.lra.get_column_lower_bound_witness(j), ex);
*this &= ex;
TRACE("nla_solver", tout << j << ": " << *this << "\n";);
return *this;
}
new_lemma& new_lemma::explain_existing_upper_bound(lpvar j) {
SASSERT(c.has_upper_bound(j));
lp::explanation ex;
c.lra.push_explanation(c.lra.get_column_upper_bound_witness(j), ex);
*this &= ex;
return *this;
}
std::ostream& new_lemma::display(std::ostream & out) const {
auto const& lemma = current();
for (auto p : lemma.expl()) {
out << "(" << p.ci() << ") ";
c.lra.constraints().display(out, [this](lpvar j) { return c.var_str(j);}, p.ci());
}
out << " ==> ";
if (lemma.ineqs().empty()) {
out << "false";
}
else {
bool first = true;
for (auto & in : lemma.ineqs()) {
if (first) first = false; else out << " or ";
c.print_ineq(in, out);
}
}
out << "\n";
for (lpvar j : c.collect_vars(lemma)) {
c.print_var(j, out);
}
return out;
}
void core::negate_relation(new_lemma& lemma, unsigned j, const rational& a) {
SASSERT(val(j) != a);
lemma |= ineq(j, val(j) < a ? llc::GE : llc::LE, a);
}
bool core::conflict_found() const {
return any_of(m_lemmas, [&](const auto& l) { return l.is_conflict(); });
}
bool core::done() const {
return m_lemmas.size() >= 10 ||
conflict_found() ||
lp_settings().get_cancel_flag();
}
bool core::elist_is_consistent(const std::unordered_set & list) const {
bool first = true;
bool p;
for (lpvar j : list) {
if (first) {
p = check_monic(m_emons[j]);
first = false;
} else
if (check_monic(m_emons[j]) != p)
return false;
}
return true;
}
bool core::elists_are_consistent(bool check_in_model) const {
std::unordered_map, hash_svector> lists;
if (!m_emons.elists_are_consistent(lists))
return false;
if (!check_in_model)
return true;
for (const auto & p : lists) {
if (! elist_is_consistent(p.second))
return false;
}
return true;
}
bool core::var_breaks_correct_monic_as_factor(lpvar j, const monic& m) const {
if (!val(var(m)).is_zero())
return true;
if (!val(j).is_zero()) // j was not zero: the new value does not matter - m must have another zero factor
return false;
// do we have another zero in m?
for (lpvar k : m) {
if (k != j && val(k).is_zero()) {
return false; // not breaking
}
}
// j was the only zero in m
return true;
}
bool core::var_breaks_correct_monic(lpvar j) const {
if (is_monic_var(j) && !m_to_refine.contains(j)) {
TRACE("nla_solver", tout << "j = " << j << ", m = "; print_monic(emon(j), tout) << "\n";);
return true; // changing the value of a correct monic
}
for (const monic & m : emons().get_use_list(j)) {
if (m_to_refine.contains(m.var()))
continue;
if (var_breaks_correct_monic_as_factor(j, m))
return true;
}
return false;
}
void core::update_to_refine_of_var(lpvar j) {
for (const monic & m : emons().get_use_list(j)) {
if (var_val(m) == mul_val(m))
erase_from_to_refine(var(m));
else
insert_to_refine(var(m));
}
if (is_monic_var(j)) {
const monic& m = emon(j);
if (var_val(m) == mul_val(m))
erase_from_to_refine(j);
else
insert_to_refine(j);
}
}
bool core::var_is_big(lpvar j) const {
return !var_is_int(j) && val(j).is_big();
}
bool core::has_big_num(const monic& m) const {
if (var_is_big(var(m)))
return true;
for (lpvar j : m.vars())
if (var_is_big(j))
return true;
return false;
}
bool core::has_real(const factorization& f) const {
for (const factor& fc: f) {
lpvar j = var(fc);
if (!var_is_int(j))
return true;
}
return false;
}
bool core::has_real(const monic& m) const {
for (lpvar j : m.vars())
if (!var_is_int(j))
return true;
return false;
}
// returns true if the patching is blocking
bool core::is_patch_blocked(lpvar u, const lp::impq& ival) const {
TRACE("nla_solver", tout << "u = " << u << '\n';);
if (m_cautious_patching &&
(!lra.inside_bounds(u, ival) || (var_is_int(u) && ival.is_int() == false))) {
TRACE("nla_solver", tout << "u = " << u << " blocked, for feas or integr\n";);
return true; // block
}
if (u == m_patched_var) {
TRACE("nla_solver", tout << "u == m_patched_var, no block\n";);
return false; // do not block
}
// we can change only one variable in variables of m_patched_var
if (m_patched_monic->contains_var(u) || u == var(*m_patched_monic)) {
TRACE("nla_solver", tout << "u = " << u << " blocked as contained\n";);
return true; // block
}
if (var_breaks_correct_monic(u)) {
TRACE("nla_solver", tout << "u = " << u << " blocked as used in a correct monomial\n";);
return true;
}
TRACE("nla_solver", tout << "u = " << u << ", m_patched_m = "; print_monic(*m_patched_monic, tout) <<
", not blocked\n";);
return false;
}
// it tries to patch m_patched_var
bool core::try_to_patch(const rational& v) {
auto is_blocked = [this](lpvar u, const lp::impq& iv) { return is_patch_blocked(u, iv); };
auto change_report = [this](lpvar u) { update_to_refine_of_var(u); };
return lra.try_to_patch(m_patched_var, v, is_blocked, change_report);
}
bool in_power(const svector& vs, unsigned l) {
unsigned k = vs[l];
return (l != 0 && vs[l - 1] == k) || (l + 1 < vs.size() && k == vs[l + 1]);
}
bool core::to_refine_is_correct() const {
for (unsigned j = 0; j < lra.number_of_vars(); j++) {
if (!is_monic_var(j)) continue;
bool valid = check_monic(emon(j));
if (valid == m_to_refine.contains(j)) {
TRACE("nla_solver", tout << "inconstency in m_to_refine : ";
print_monic(emon(j), tout) << "\n";
if (valid) tout << "should NOT be in to_refine\n";
else tout << "should be in to_refine\n";);
return false;
}
}
return true;
}
void core::patch_monomial(lpvar j) {
m_patched_monic =& (emon(j));
m_patched_var = j;
TRACE("nla_solver", tout << "m = "; print_monic(*m_patched_monic, tout) << "\n";);
rational v = mul_val(*m_patched_monic);
if (val(j) == v) {
erase_from_to_refine(j);
return;
}
if (!var_breaks_correct_monic(j) && try_to_patch(v)) {
SASSERT(to_refine_is_correct());
return;
}
// We could not patch j, now we try patching the factor variables.
TRACE("nla_solver", tout << " trying squares\n";);
// handle perfect squares
if ((*m_patched_monic).vars().size() == 2 && (*m_patched_monic).vars()[0] == (*m_patched_monic).vars()[1]) {
rational root;
if (v.is_perfect_square(root)) {
m_patched_var = (*m_patched_monic).vars()[0];
if (!var_breaks_correct_monic(m_patched_var) && (try_to_patch(root) || try_to_patch(-root))) {
TRACE("nla_solver", tout << "patched square\n";);
return;
}
}
TRACE("nla_solver", tout << " cannot patch\n";);
return;
}
// We have v != abc, but we need to have v = abc.
// If we patch b then b should be equal to v/ac = v/(abc/b) = b(v/abc)
if (!v.is_zero()) {
rational r = val(j) / v;
SASSERT((*m_patched_monic).is_sorted());
TRACE("nla_solver", tout << "r = " << r << ", v = " << v << "\n";);
for (unsigned l = 0; l < (*m_patched_monic).size(); l++) {
m_patched_var = (*m_patched_monic).vars()[l];
if (!in_power((*m_patched_monic).vars(), l) &&
!var_breaks_correct_monic(m_patched_var) &&
try_to_patch(r * val(m_patched_var))) { // r * val(k) gives the right value of k
TRACE("nla_solver", tout << "patched " << m_patched_var << "\n";);
SASSERT(mul_val((*m_patched_monic)) == val(j));
erase_from_to_refine(j);
break;
}
}
}
}
void core::patch_monomials_on_to_refine() {
// the rest of the function might change m_to_refine, so have to copy
unsigned_vector to_refine;
for (unsigned j : m_to_refine)
to_refine.push_back(j);
unsigned sz = to_refine.size();
unsigned start = random();
for (unsigned i = 0; i < sz && !m_to_refine.empty(); i++)
patch_monomial(to_refine[(start + i) % sz]);
TRACE("nla_solver", tout << "sz = " << sz << ", m_to_refine = " << m_to_refine.size() <<
(sz > m_to_refine.size()? " less" : " same" ) << "\n";);
}
void core::patch_monomials() {
m_cautious_patching = true;
patch_monomials_on_to_refine();
}
/**
* Cycle through different end-game solvers weighted by probability.
*/
void core::check_weighted(unsigned sz, std::pair>* checks) {
unsigned bound = 0;
for (unsigned i = 0; i < sz; ++i)
bound += checks[i].first;
uint_set seen;
while (bound > 0 && !done() && m_lemmas.empty()) {
unsigned n = random() % bound;
for (unsigned i = 0; i < sz; ++i) {
if (seen.contains(i))
continue;
if (n < checks[i].first) {
seen.insert(i);
checks[i].second();
bound -= checks[i].first;
break;
}
n -= checks[i].first;
}
}
}
lbool core::check_power(lpvar r, lpvar x, lpvar y) {
clear();
return m_powers.check(r, x, y, m_lemmas);
}
void core::check_bounded_divisions() {
clear();
m_divisions.check_bounded_divisions();
}
// looking for a free variable inside of a monic to split
void core::add_bounds() {
unsigned r = random(), sz = m_to_refine.size();
for (unsigned k = 0; k < sz; k++) {
lpvar i = m_to_refine[(k + r) % sz];
auto const& m = m_emons[i];
for (lpvar j : m.vars()) {
if (!var_is_free(j))
continue;
if (m.is_bound_propagated())
continue;
m_emons.set_bound_propagated(m);
// split the free variable (j <= 0, or j > 0), and return
m_literals.push_back(ineq(j, lp::lconstraint_kind::EQ, rational::zero()));
TRACE("nla_solver", print_ineq(m_literals.back(), tout) << "\n");
++lp_settings().stats().m_nla_add_bounds;
return;
}
}
}
lbool core::check() {
lp_settings().stats().m_nla_calls++;
TRACE("nla_solver", tout << "calls = " << lp_settings().stats().m_nla_calls << "\n";);
lra.get_rid_of_inf_eps();
if (!(lra.get_status() == lp::lp_status::OPTIMAL ||
lra.get_status() == lp::lp_status::FEASIBLE)) {
TRACE("nla_solver", tout << "unknown because of the lra.m_status = " << lra.get_status() << "\n";);
return l_undef;
}
init_to_refine();
patch_monomials();
set_use_nra_model(false);
if (m_to_refine.empty())
return l_true;
init_search();
lbool ret = l_undef;
bool run_grobner = need_run_grobner();
bool run_horner = need_run_horner();
bool run_bounds = params().arith_nl_branching();
auto no_effect = [&]() { return ret == l_undef && !done() && m_lemmas.empty() && m_literals.empty() && !m_check_feasible; };
if (no_effect())
m_monomial_bounds.propagate();
{
std::function check1 = [&]() { if (no_effect() && run_horner) m_horner.horner_lemmas(); };
std::function check2 = [&]() { if (no_effect() && run_grobner) m_grobner(); };
std::function check3 = [&]() { if (no_effect() && run_bounds) add_bounds(); };
std::pair> checks[] =
{ {1, check1},
{1, check2},
{1, check3} };
check_weighted(3, checks);
if (lp_settings().get_cancel_flag())
return l_undef;
if (!m_lemmas.empty() || !m_literals.empty() || m_check_feasible)
return l_false;
}
if (no_effect() && should_run_bounded_nlsat())
ret = bounded_nlsat();
if (no_effect())
m_basics.basic_lemma(true);
if (no_effect())
m_basics.basic_lemma(false);
if (no_effect())
m_divisions.check();
if (no_effect()) {
std::function check1 = [&]() { m_order.order_lemma(); };
std::function check2 = [&]() { m_monotone.monotonicity_lemma(); };
std::function check3 = [&]() { m_tangents.tangent_lemma(); };
std::pair> checks[] =
{ { 6, check1 },
{ 2, check2 },
{ 1, check3 }};
check_weighted(3, checks);
unsigned num_calls = lp_settings().stats().m_nla_calls;
if (!conflict_found() && params().arith_nl_nra() && num_calls % 50 == 0 && num_calls > 500)
ret = bounded_nlsat();
}
if (no_effect() && params().arith_nl_nra()) {
ret = m_nra.check();
lp_settings().stats().m_nra_calls++;
}
if (ret == l_undef && !no_effect() && m_reslim.inc())
ret = l_false;
lp_settings().stats().m_nla_lemmas += m_lemmas.size();
TRACE("nla_solver", tout << "ret = " << ret << ", lemmas count = " << m_lemmas.size() << "\n";);
IF_VERBOSE(5, if(ret == l_undef) {verbose_stream() << "Monomials\n"; print_monics(verbose_stream());});
CTRACE("nla_solver", ret == l_undef, tout << "Monomials\n"; print_monics(tout););
return ret;
}
bool core::should_run_bounded_nlsat() {
if (!params().arith_nl_nra())
return false;
if (m_nlsat_delay > 0)
--m_nlsat_delay;
return m_nlsat_delay < 2;
}
lbool core::bounded_nlsat() {
params_ref p;
lbool ret;
p.set_uint("max_conflicts", 100);
m_nra.updt_params(p);
{
scoped_limits sl(m_reslim);
sl.push_child(&m_nra_lim);
scoped_rlimit sr(m_nra_lim, 100000);
ret = m_nra.check();
}
p.set_uint("max_conflicts", UINT_MAX);
m_nra.updt_params(p);
lp_settings().stats().m_nra_calls++;
if (ret == l_undef)
++m_nlsat_delay_bound;
else if (m_nlsat_delay_bound > 0)
m_nlsat_delay_bound /= 2;
m_nlsat_delay = m_nlsat_delay_bound;
if (ret == l_true)
clear();
return ret;
}
bool core::no_lemmas_hold() const {
for (auto & l : m_lemmas) {
if (lemma_holds(l)) {
TRACE("nla_solver", print_lemma(l, tout););
return false;
}
}
return true;
}
lbool core::test_check() {
lra.set_status(lp::lp_status::OPTIMAL);
return check();
}
std::ostream& core::print_terms(std::ostream& out) const {
for (const auto * t: lra.terms()) {
out << "term:"; print_term(*t, out) << std::endl;
print_var(t->j(), out);
}
return out;
}
std::string core::var_str(lpvar j) const {
std::string result;
if (is_monic_var(j))
result += product_indices_str(m_emons[j].vars()) + (check_monic(m_emons[j])? "": "_");
else
result += std::string("j") + lp::T_to_string(j);
// result += ":w" + lp::T_to_string(get_var_weight(j));
return result;
}
std::ostream& core::print_term( const lp::lar_term& t, std::ostream& out) const {
return lp::print_linear_combination_customized(
t.coeffs_as_vector(),
[this](lpvar j) { return var_str(j); },
out);
}
std::unordered_set core::get_vars_of_expr_with_opening_terms(const nex *e ) {
auto ret = get_vars_of_expr(e);
auto & ls = lra;
svector added;
for (auto j : ret) {
added.push_back(j);
}
for (unsigned i = 0; i < added.size(); ++i) {
lpvar j = added[i];
if (ls.column_has_term(j)) {
const auto& t = lra.get_term(j);
for (auto p : t) {
if (ret.find(p.j()) == ret.end()) {
added.push_back(p.j());
ret.insert(p.j());
}
}
}
}
return ret;
}
bool core::is_nl_var(lpvar j) const {
return is_monic_var(j) || m_emons.is_used_in_monic(j);
}
unsigned core::get_var_weight(lpvar j) const {
unsigned k;
switch (lra.get_column_type(j)) {
case lp::column_type::fixed:
k = 0;
break;
case lp::column_type::boxed:
k = 3;
break;
case lp::column_type::lower_bound:
case lp::column_type::upper_bound:
k = 6;
break;
case lp::column_type::free_column:
k = 9;
break;
default:
UNREACHABLE();
break;
}
if (is_monic_var(j)) {
k++;
if (m_to_refine.contains(j))
k++;
}
return k;
}
void core::set_active_vars_weights(nex_creator& nc) {
nc.set_number_of_vars(lra.column_count());
for (lpvar j : active_var_set())
nc.set_var_weight(j, get_var_weight(j));
}
bool core::influences_nl_var(lpvar j) const {
if (is_nl_var(j))
return true;
for (const auto & c : lra.A_r().m_columns[j]) {
lpvar basic_in_row = lra.r_basis()[c.var()];
if (is_nl_var(basic_in_row))
return true;
}
return false;
}
void core::set_use_nra_model(bool m) {
if (m != m_use_nra_model) {
trail().push(value_trail(m_use_nra_model));
m_use_nra_model = m;
}
}
void core::propagate() {
#if Z3DEBUG
flet f(lra.validate_blocker(), true);
#endif
clear();
m_monomial_bounds.unit_propagate();
m_monics_with_changed_bounds.reset();
}
void core::simplify() {
// in-processing simplifiation can go here, such as bounds improvements.
}