3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 09:35:32 +00:00

Merge branch 'master' into polysat

This commit is contained in:
Jakob Rath 2022-07-21 12:56:50 +02:00
commit e168d8a2eb
109 changed files with 4372 additions and 2743 deletions

View file

@ -46,11 +46,15 @@ public:
void operator()(model_ref & md) override {
TRACE("ackermannize", tout << (fixed_model? "fixed" : "nonfixed") << "\n";);
SASSERT(!fixed_model || md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions()));
model_ref& old_model = fixed_model ? abstr_model : md;
SASSERT(old_model.get());
model * new_model = alloc(model, m);
convert(old_model.get(), new_model);
CTRACE("ackermannize", md, tout << *md << "\n");
CTRACE("ackermannize", fixed_model, tout << *abstr_model << "\n");
model* new_model = alloc(model, m);
if (abstr_model)
convert(abstr_model.get(), new_model);
if (md)
convert(md.get(), new_model);
md = new_model;
}

View file

@ -35,11 +35,12 @@ namespace api {
object::object(context& c): m_ref_count(0), m_context(c) { this->m_id = m_context.add_object(this); }
void object::inc_ref() { m_ref_count++; }
void object::inc_ref() { ++m_ref_count; }
void object::dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) m_context.del_object(this); }
void object::dec_ref() { SASSERT(m_ref_count > 0); if (--m_ref_count == 0) m_context.del_object(this); }
unsigned context::add_object(api::object* o) {
flush_objects();
unsigned id = m_allocated_objects.size();
if (!m_free_object_ids.empty()) {
id = m_free_object_ids.back();
@ -50,9 +51,52 @@ namespace api {
}
void context::del_object(api::object* o) {
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
#ifndef SINGLE_THREAD
if (m_concurrent_dec_ref) {
lock_guard lock(m_mux);
m_objects_to_flush.push_back(o);
}
else
#endif
{
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
}
}
void context::dec_ref(ast* a) {
#ifndef SINGLE_THREAD
if (m_concurrent_dec_ref) {
lock_guard lock(m_mux);
m_asts_to_flush.push_back(a);
}
else
#endif
m().dec_ref(a);
}
void context::flush_objects() {
#ifndef SINGLE_THREAD
if (!m_concurrent_dec_ref)
return;
{
lock_guard lock(m_mux);
if (m_asts_to_flush.empty() && m_objects_to_flush.empty())
return;
m_asts_to_flush2.swap(m_asts_to_flush);
m_objects_to_flush2.swap(m_objects_to_flush);
}
for (ast* a : m_asts_to_flush2)
m().dec_ref(a);
for (auto* o : m_objects_to_flush2) {
m_free_object_ids.push_back(o->id());
m_allocated_objects.remove(o->id());
dealloc(o);
}
m_objects_to_flush2.reset();
m_asts_to_flush2.reset();
#endif
}
static void default_error_handler(Z3_context ctx, Z3_error_code c) {
@ -106,6 +150,7 @@ namespace api {
context::~context() {
m_last_obj = nullptr;
flush_objects();
for (auto& kv : m_allocated_objects) {
api::object* val = kv.m_value;
DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", kv.m_key, typeid(*val).name()););
@ -356,6 +401,13 @@ extern "C" {
Z3_CATCH;
}
void Z3_API Z3_enable_concurrent_dec_ref(Z3_context c) {
Z3_TRY;
LOG_Z3_enable_concurrent_dec_ref(c);
mk_c(c)->enable_concurrent_dec_ref();
Z3_CATCH;
}
void Z3_API Z3_toggle_warning_messages(bool enabled) {
LOG_Z3_toggle_warning_messages(enabled);
enable_warning_messages(enabled != 0);
@ -365,6 +417,7 @@ extern "C" {
Z3_TRY;
LOG_Z3_inc_ref(c, a);
RESET_ERROR_CODE();
mk_c(c)->flush_objects();
mk_c(c)->m().inc_ref(to_ast(a));
Z3_CATCH;
}
@ -379,7 +432,7 @@ extern "C" {
return;
}
if (a) {
mk_c(c)->m().dec_ref(to_ast(a));
mk_c(c)->dec_ref(to_ast(a));
}
Z3_CATCH;
}

View file

@ -75,6 +75,9 @@ namespace api {
struct add_plugins { add_plugins(ast_manager & m); };
ast_context_params m_params;
bool m_user_ref_count; //!< if true, the user is responsible for managing reference counters.
#ifndef SINGLE_THREAD
bool m_concurrent_dec_ref = false;
#endif
scoped_ptr<ast_manager> m_manager;
scoped_ptr<cmd_context> m_cmd;
add_plugins m_plugins;
@ -91,8 +94,12 @@ namespace api {
smt_params m_fparams;
// -------------------------------
ast_ref_vector m_ast_trail;
#ifndef SINGLE_THREAD
ptr_vector<ast> m_asts_to_flush, m_asts_to_flush2;
ptr_vector<api::object> m_objects_to_flush, m_objects_to_flush2;
#endif
ast_ref_vector m_ast_trail;
ref<api::object> m_last_obj; //!< reference to the last API object returned by the APIs
u_map<api::object*> m_allocated_objects; // !< table containing current set of allocated API objects
unsigned_vector m_free_object_ids; // !< free list of identifiers available for allocated objects.
@ -169,9 +176,18 @@ namespace api {
void set_error_code(Z3_error_code err, char const* opt_msg);
void set_error_code(Z3_error_code err, std::string &&opt_msg);
void set_error_handler(Z3_error_handler h) { m_error_handler = h; }
void enable_concurrent_dec_ref() {
#ifdef SINGLE_THREAD
set_error_code(Z3_EXCEPTION, "Can't use concurrent features with a single-thread build");
#else
m_concurrent_dec_ref = true;
#endif
}
unsigned add_object(api::object* o);
void del_object(api::object* o);
void dec_ref(ast* a);
void flush_objects();
Z3_ast_print_mode get_print_mode() const { return m_print_mode; }
void set_print_mode(Z3_ast_print_mode m) { m_print_mode = m; }

View file

@ -35,6 +35,136 @@ extern "C" {
// ---------------
// Support for SMTLIB2
struct Z3_parser_context_ref : public api::object {
scoped_ptr<cmd_context> ctx;
Z3_parser_context_ref(api::context& c): api::object(c) {
ast_manager& m = c.m();
ctx = alloc(cmd_context, false, &(m));
install_dl_cmds(*ctx.get());
install_opt_cmds(*ctx.get());
install_smt2_extra_cmds(*ctx.get());
ctx->register_plist();
ctx->set_ignore_check(true);
}
~Z3_parser_context_ref() override {}
};
inline Z3_parser_context_ref * to_parser_context(Z3_parser_context pc) { return reinterpret_cast<Z3_parser_context_ref*>(pc); }
inline Z3_parser_context of_parser_context(Z3_parser_context_ref* pc) { return reinterpret_cast<Z3_parser_context>(pc); }
Z3_parser_context Z3_API Z3_mk_parser_context(Z3_context c) {
Z3_TRY;
LOG_Z3_mk_parser_context(c);
RESET_ERROR_CODE();
Z3_parser_context_ref * pc = alloc(Z3_parser_context_ref, *mk_c(c));
mk_c(c)->save_object(pc);
Z3_parser_context r = of_parser_context(pc);
RETURN_Z3(r);
Z3_CATCH_RETURN(nullptr);
}
void Z3_API Z3_parser_context_inc_ref(Z3_context c, Z3_parser_context pc) {
Z3_TRY;
LOG_Z3_parser_context_inc_ref(c, pc);
RESET_ERROR_CODE();
to_parser_context(pc)->inc_ref();
Z3_CATCH;
}
void Z3_API Z3_parser_context_dec_ref(Z3_context c, Z3_parser_context pc) {
Z3_TRY;
LOG_Z3_parser_context_dec_ref(c, pc);
RESET_ERROR_CODE();
to_parser_context(pc)->dec_ref();
Z3_CATCH;
}
static void insert_datatype(ast_manager& m, scoped_ptr<cmd_context>& ctx, sort* srt) {
datatype_util dt(m);
if (!dt.is_datatype(srt))
return;
for (func_decl * c : *dt.get_datatype_constructors(srt)) {
ctx->insert(c->get_name(), c);
func_decl * r = dt.get_constructor_recognizer(c);
ctx->insert(r->get_name(), r);
for (func_decl * a : *dt.get_constructor_accessors(c)) {
ctx->insert(a->get_name(), a);
}
}
}
static void insert_sort(ast_manager& m, scoped_ptr<cmd_context>& ctx, symbol const& name, sort* srt) {
if (ctx->find_psort_decl(name))
return;
psort* ps = ctx->pm().mk_psort_cnst(srt);
ctx->insert(ctx->pm().mk_psort_user_decl(0, name, ps));
insert_datatype(m, ctx, srt);
}
void Z3_API Z3_parser_context_add_sort(Z3_context c, Z3_parser_context pc, Z3_sort s) {
Z3_TRY;
LOG_Z3_parser_context_add_sort(c, pc, s);
RESET_ERROR_CODE();
auto& ctx = to_parser_context(pc)->ctx;
sort* srt = to_sort(s);
symbol name = srt->get_name();
insert_sort(mk_c(c)->m(), ctx, name, srt);
Z3_CATCH;
}
void Z3_API Z3_parser_context_add_decl(Z3_context c, Z3_parser_context pc, Z3_func_decl f) {
Z3_TRY;
LOG_Z3_parser_context_add_decl(c, pc, f);
RESET_ERROR_CODE();
auto& ctx = *to_parser_context(pc)->ctx;
func_decl* fn = to_func_decl(f);
symbol name = fn->get_name();
ctx.insert(name, fn);
Z3_CATCH;
}
Z3_ast_vector Z3_parser_context_parse_stream(Z3_context c, scoped_ptr<cmd_context>& ctx, bool owned, std::istream& is) {
Z3_TRY;
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
mk_c(c)->save_object(v);
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
try {
if (!parse_smt2_commands(*ctx, is)) {
if (owned)
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
}
catch (z3_exception& e) {
if (owned)
ctx = nullptr;
errstrm << e.msg();
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
for (expr* e : ctx->tracked_assertions())
v->m_ast_vector.push_back(e);
ctx->reset_tracked_assertions();
return of_ast_vector(v);
Z3_CATCH_RETURN(nullptr);
}
Z3_ast_vector Z3_API Z3_parser_context_from_string(Z3_context c, Z3_parser_context pc, Z3_string str) {
Z3_TRY;
LOG_Z3_parser_context_from_string(c, pc, str);
std::string s(str);
std::istringstream is(s);
auto& ctx = to_parser_context(pc)->ctx;
Z3_ast_vector r = Z3_parser_context_parse_stream(c, ctx, false, is);
RETURN_Z3(r);
Z3_CATCH_RETURN(nullptr);
}
Z3_ast_vector parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is,
unsigned num_sorts,
Z3_symbol const _sort_names[],
@ -48,70 +178,16 @@ extern "C" {
install_dl_cmds(*ctx.get());
install_opt_cmds(*ctx.get());
install_smt2_extra_cmds(*ctx.get());
ctx->register_plist();
ctx->set_ignore_check(true);
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), m);
vector<symbol> sort_names;
ptr_vector<sort> sorts;
for (unsigned i = 0; i < num_sorts; ++i) {
sorts.push_back(to_sort(_sorts[i]));
sort_names.push_back(to_symbol(_sort_names[i]));
}
mk_c(c)->save_object(v);
for (unsigned i = 0; i < num_decls; ++i) {
func_decl* d = to_func_decl(decls[i]);
ctx->insert(to_symbol(decl_names[i]), d);
sort_names.push_back(d->get_range()->get_name());
sorts.push_back(d->get_range());
for (sort* s : *d) {
sort_names.push_back(s->get_name());
sorts.push_back(s);
}
}
datatype_util dt(m);
for (unsigned i = 0; i < num_sorts; ++i) {
sort* srt = sorts[i];
symbol name = sort_names[i];
if (ctx->find_psort_decl(name)) {
continue;
}
psort* ps = ctx->pm().mk_psort_cnst(srt);
ctx->insert(ctx->pm().mk_psort_user_decl(0, name, ps));
if (!dt.is_datatype(srt)) {
continue;
}
for (unsigned i = 0; i < num_decls; ++i)
ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i]));
for (func_decl * c : *dt.get_datatype_constructors(srt)) {
ctx->insert(c->get_name(), c);
func_decl * r = dt.get_constructor_recognizer(c);
ctx->insert(r->get_name(), r);
for (func_decl * a : *dt.get_constructor_accessors(c)) {
ctx->insert(a->get_name(), a);
}
}
}
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
try {
if (!parse_smt2_commands(*ctx.get(), is)) {
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
}
catch (z3_exception& e) {
errstrm << e.msg();
ctx = nullptr;
SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str());
return of_ast_vector(v);
}
for (expr* e : ctx->tracked_assertions()) {
v->m_ast_vector.push_back(e);
}
return of_ast_vector(v);
for (unsigned i = 0; i < num_sorts; ++i)
insert_sort(m, ctx, to_symbol(_sort_names[i]), to_sort(_sorts[i]));
return Z3_parser_context_parse_stream(c, ctx, true, is);
Z3_CATCH_RETURN(nullptr);
}
@ -155,11 +231,13 @@ extern "C" {
Z3_TRY;
LOG_Z3_eval_smtlib2_string(c, str);
if (!mk_c(c)->cmd()) {
mk_c(c)->cmd() = alloc(cmd_context, false, &(mk_c(c)->m()));
install_dl_cmds(*mk_c(c)->cmd());
install_opt_cmds(*mk_c(c)->cmd());
install_smt2_extra_cmds(*mk_c(c)->cmd());
mk_c(c)->cmd()->set_solver_factory(mk_smt_strategic_solver_factory());
auto* ctx = alloc(cmd_context, false, &(mk_c(c)->m()));
mk_c(c)->cmd() = ctx;
install_dl_cmds(*ctx);
install_opt_cmds(*ctx);
install_smt2_extra_cmds(*ctx);
ctx->register_plist();
ctx->set_solver_factory(mk_smt_strategic_solver_factory());
}
scoped_ptr<cmd_context>& ctx = mk_c(c)->cmd();
std::string s(str);
@ -180,4 +258,6 @@ extern "C" {
RETURN_Z3(mk_c(c)->mk_external_string(ous.str()));
Z3_CATCH_RETURN(mk_c(c)->mk_external_string(ous.str()));
}
};

View file

@ -256,7 +256,10 @@ extern "C" {
}
void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) {
scoped_ptr<cmd_context> ctx = alloc(cmd_context, false, &(mk_c(c)->m()));
auto& solver = *to_solver(s);
if (!solver.m_cmd_context)
solver.m_cmd_context = alloc(cmd_context, false, &(mk_c(c)->m()));
auto& ctx = solver.m_cmd_context;
ctx->set_ignore_check(true);
std::stringstream errstrm;
ctx->set_regular_stream(errstrm);
@ -272,6 +275,7 @@ extern "C" {
init_solver(c, s);
for (expr* e : ctx->tracked_assertions())
to_solver(s)->assert_expr(e);
ctx->reset_tracked_assertions();
to_solver_ref(s)->set_model_converter(ctx->get_model_converter());
}
@ -387,9 +391,11 @@ extern "C" {
bool new_model = params.get_bool("model", true);
if (old_model != new_model)
to_solver_ref(s)->set_produce_models(new_model);
param_descrs r;
to_solver_ref(s)->collect_param_descrs(r);
context_params::collect_solver_param_descrs(r);
param_descrs& r = to_solver(s)->m_param_descrs;
if(r.size () == 0) {
to_solver_ref(s)->collect_param_descrs(r);
context_params::collect_solver_param_descrs(r);
}
params.validate(r);
to_solver_ref(s)->updt_params(params);
}

View file

@ -42,8 +42,10 @@ struct Z3_solver_ref : public api::object {
scoped_ptr<solver_factory> m_solver_factory;
ref<solver> m_solver;
params_ref m_params;
param_descrs m_param_descrs;
symbol m_logic;
scoped_ptr<solver2smt2_pp> m_pp;
scoped_ptr<cmd_context> m_cmd_context;
mutex m_mux;
event_handler* m_eh;

View file

@ -19,6 +19,7 @@ Revision History:
#include "util/params.h"
#include "util/lbool.h"
#include "util/mutex.h"
#include "ast/ast.h"
#define Z3_TRY try {
@ -34,7 +35,7 @@ namespace api {
// Generic wrapper for ref-count objects exposed by the API
class object {
unsigned m_ref_count;
atomic<unsigned> m_ref_count;
unsigned m_id;
context& m_context;
public:
@ -87,7 +88,6 @@ inline lbool to_lbool(Z3_lbool b) { return static_cast<lbool>(b); }
struct Z3_params_ref : public api::object {
params_ref m_params;
Z3_params_ref(api::context& c): api::object(c) {}
~Z3_params_ref() override {}
};
inline Z3_params_ref * to_params(Z3_params p) { return reinterpret_cast<Z3_params_ref *>(p); }
@ -97,7 +97,6 @@ inline params_ref& to_param_ref(Z3_params p) { return p == nullptr ? const_cast<
struct Z3_param_descrs_ref : public api::object {
param_descrs m_descrs;
Z3_param_descrs_ref(api::context& c): api::object(c) {}
~Z3_param_descrs_ref() override {}
};
inline Z3_param_descrs_ref * to_param_descrs(Z3_param_descrs p) { return reinterpret_cast<Z3_param_descrs_ref *>(p); }

View file

@ -57,6 +57,8 @@ namespace z3 {
class param_descrs;
class ast;
class sort;
class constructors;
class constructor_list;
class func_decl;
class expr;
class solver;
@ -313,6 +315,34 @@ namespace z3 {
*/
func_decl tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs);
/**
\brief Create a recursive datatype over a single sort.
\c name is the name of the recursive datatype
\c n - the numer of constructors of the datatype
\c cs - the \c n constructors used to define the datatype
References to the datatype can be created using \ref datatype_sort.
*/
sort datatype(symbol const& name, constructors const& cs);
/**
\brief Create a set of mutually recursive datatypes.
\c n - number of recursive datatypes
\c names - array of names of length n
\c cons - array of constructor lists of length n
*/
sort_vector datatypes(unsigned n, symbol const* names,
constructor_list *const* cons);
/**
\brief a reference to a recursively defined datatype.
Expect that it gets defined as a \ref datatype.
*/
sort datatype_sort(symbol const& name);
/**
\brief create an uninterpreted sort with the name given by the string or symbol.
*/
@ -2894,7 +2924,7 @@ namespace z3 {
if (n == 0)
return ctx().bool_val(true);
else if (n == 1)
return operator[](0);
return operator[](0u);
else {
array<Z3_ast> args(n);
for (unsigned i = 0; i < n; i++)
@ -3330,6 +3360,98 @@ namespace z3 {
return func_decl(*this, tuple);
}
class constructor_list {
context& ctx;
Z3_constructor_list clist;
public:
constructor_list(constructors const& cs);
~constructor_list() { Z3_del_constructor_list(ctx, clist); }
operator Z3_constructor_list() const { return clist; }
};
class constructors {
friend class constructor_list;
context& ctx;
std::vector<Z3_constructor> cons;
std::vector<unsigned> num_fields;
public:
constructors(context& ctx): ctx(ctx) {}
~constructors() {
for (auto con : cons)
Z3_del_constructor(ctx, con);
}
void add(symbol const& name, symbol const& rec, unsigned n, symbol const* names, sort const* fields) {
array<unsigned> sort_refs(n);
array<Z3_sort> sorts(n);
array<Z3_symbol> _names(n);
for (unsigned i = 0; i < n; ++i) sorts[i] = fields[i], _names[i] = names[i];
cons.push_back(Z3_mk_constructor(ctx, name, rec, n, _names.ptr(), sorts.ptr(), sort_refs.ptr()));
num_fields.push_back(n);
}
Z3_constructor operator[](unsigned i) const { return cons[i]; }
unsigned size() const { return (unsigned)cons.size(); }
void query(unsigned i, func_decl& constructor, func_decl& test, func_decl_vector& accs) {
Z3_func_decl _constructor;
Z3_func_decl _test;
array<Z3_func_decl> accessors(num_fields[i]);
accs.resize(0);
Z3_query_constructor(ctx,
cons[i],
num_fields[i],
&_constructor,
&_test,
accessors.ptr());
constructor = func_decl(ctx, _constructor);
test = func_decl(ctx, _test);
for (unsigned j = 0; j < num_fields[i]; ++j)
accs.push_back(func_decl(ctx, accessors[j]));
}
};
constructor_list::constructor_list(constructors const& cs): ctx(cs.ctx) {
array<Z3_constructor> cons(cs.size());
for (unsigned i = 0; i < cs.size(); ++i)
cons[i] = cs[i];
clist = Z3_mk_constructor_list(ctx, cs.size(), cons.ptr());
}
inline sort context::datatype(symbol const& name, constructors const& cs) {
array<Z3_constructor> _cs(cs.size());
for (unsigned i = 0; i < cs.size(); ++i) _cs[i] = cs[i];
Z3_sort s = Z3_mk_datatype(*this, name, cs.size(), _cs.ptr());
check_error();
return sort(*this, s);
}
inline sort_vector context::datatypes(
unsigned n, symbol const* names,
constructor_list *const* cons) {
sort_vector result(*this);
array<Z3_symbol> _names(n);
array<Z3_sort> _sorts(n);
array<Z3_constructor_list> _cons(n);
for (unsigned i = 0; i < n; ++i)
_names[i] = names[i], _cons[i] = *cons[i];
Z3_mk_datatypes(*this, n, _names.ptr(), _sorts.ptr(), _cons.ptr());
for (unsigned i = 0; i < n; ++i)
result.push_back(sort(*this, _sorts[i]));
return result;
}
inline sort context::datatype_sort(symbol const& name) {
Z3_sort s = Z3_mk_datatype_sort(*this, name);
check_error();
return sort(*this, s);
}
inline sort context::uninterpreted_sort(char const* name) {
Z3_symbol _name = Z3_mk_string_symbol(*this, name);
return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name));

View file

@ -4620,16 +4620,16 @@ namespace Microsoft.Z3
/// </summary>
/// <remarks>
/// Produces a term that represents the conversion of the floating-point term t into a
/// bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary,
/// bit-vector term of size sz in 2's complement format (signed when sign==true). If necessary,
/// the result will be rounded according to rounding mode rm.
/// </remarks>
/// <param name="rm">RoundingMode term.</param>
/// <param name="t">FloatingPoint term</param>
/// <param name="sz">Size of the resulting bit-vector.</param>
/// <param name="signed">Indicates whether the result is a signed or unsigned bit-vector.</param>
public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool signed)
/// <param name="sign">Indicates whether the result is a signed or unsigned bit-vector.</param>
public BitVecExpr MkFPToBV(FPRMExpr rm, FPExpr t, uint sz, bool sign)
{
if (signed)
if (sign)
return new BitVecExpr(this, Native.Z3_mk_fpa_to_sbv(this.nCtx, rm.NativeObject, t.NativeObject, sz));
else
return new BitVecExpr(this, Native.Z3_mk_fpa_to_ubv(this.nCtx, rm.NativeObject, t.NativeObject, sz));

View file

@ -168,6 +168,16 @@ namespace Microsoft.Z3
{
return (Expr)base.Translate(ctx);
}
/// <summary>
/// Create a duplicate of expression.
/// This feature is to allow extending the life-time of expressions that were passed down as arguments
/// by the user propagator callbacks. By default the life-time of arguments to callbacks is within the
/// callback only.
/// </summary>
public Expr Dup() {
return Expr.Create(Context, NativeObject);
}
/// <summary>
/// Returns a string representation of the expression.

View file

@ -100,6 +100,17 @@ namespace Microsoft.Z3
return Native.Z3_mk_mul(nCtx, (uint)(ts?.Length ?? 0), ts);
}
/// <summary>
/// Create an expression representing <c>t[0] - t[1] - ...</c>.
/// </summary>
public Z3_ast MkSub(params Z3_ast[] t)
{
Debug.Assert(t != null);
Debug.Assert(t.All(a => a != IntPtr.Zero));
var ts = t.ToArray();
return Native.Z3_mk_sub(nCtx, (uint)(ts?.Length ?? 0), ts);
}
/// <summary>
/// Create an expression representing <c>t1 / t2</c>.
/// </summary>

View file

@ -41,6 +41,9 @@ namespace Microsoft.Z3
{
/// <summary>
/// Delegate type for fixed callback
/// Note that the life-time of the term and value only applies within the scope of the callback.
/// That means the term and value cannot be stored in an array, dictionary or similar and accessed after the callback has returned.
/// Use the functionality Dup on expressions to create a duplicate copy that extends the lifetime.
/// </summary>
public delegate void FixedEh(Expr term, Expr value);
@ -64,8 +67,7 @@ namespace Microsoft.Z3
// access managed objects through a static array.
// thread safety is ignored for now.
static List<UserPropagator> propagators = new List<UserPropagator>();
int id;
GCHandle gch;
Solver solver;
Context ctx;
Z3_solver_callback callback = IntPtr.Zero;
@ -107,27 +109,27 @@ namespace Microsoft.Z3
static void _push(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.Push(), cb);
}
static void _pop(voidp ctx, Z3_solver_callback cb, uint num_scopes)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.Pop(num_scopes), cb);
}
static voidp _fresh(voidp _ctx, Z3_context new_context)
{
var prop = propagators[_ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(_ctx).Target;
var ctx = new Context(new_context);
var prop1 = prop.Fresh(prop.ctx);
return new IntPtr(prop1.id);
return GCHandle.ToIntPtr(prop1.gch);
}
static void _fixed(voidp ctx, Z3_solver_callback cb, Z3_ast _term, Z3_ast _value)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var term = Expr.Create(prop.ctx, _term);
using var value = Expr.Create(prop.ctx, _value);
prop.Callback(() => prop.fixed_eh(term, value), cb);
@ -135,13 +137,13 @@ namespace Microsoft.Z3
static void _final(voidp ctx, Z3_solver_callback cb)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
prop.Callback(() => prop.final_eh(), cb);
}
static void _eq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.eq_eh(s, t), cb);
@ -149,7 +151,7 @@ namespace Microsoft.Z3
static void _diseq(voidp ctx, Z3_solver_callback cb, Z3_ast a, Z3_ast b)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var s = Expr.Create(prop.ctx, a);
using var t = Expr.Create(prop.ctx, b);
prop.Callback(() => prop.diseq_eh(s, t), cb);
@ -157,14 +159,14 @@ namespace Microsoft.Z3
static void _created(voidp ctx, Z3_solver_callback cb, Z3_ast a)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
using var t = Expr.Create(prop.ctx, a);
prop.Callback(() => prop.created_eh(t), cb);
}
static void _decide(voidp ctx, Z3_solver_callback cb, ref Z3_ast a, ref uint idx, ref int phase)
{
var prop = propagators[ctx.ToInt32()];
var prop = (UserPropagator)GCHandle.FromIntPtr(ctx).Target;
var t = Expr.Create(prop.ctx, a);
var u = t;
prop.callback = cb;
@ -179,14 +181,13 @@ namespace Microsoft.Z3
/// </summary>
public UserPropagator(Solver s)
{
id = propagators.Count;
propagators.Add(this);
gch = GCHandle.Alloc(this);
solver = s;
ctx = solver.Context;
push_eh = _push;
pop_eh = _pop;
fresh_eh = _fresh;
Native.Z3_solver_propagate_init(ctx.nCtx, solver.NativeObject, new IntPtr(id), push_eh, pop_eh, fresh_eh);
Native.Z3_solver_propagate_init(ctx.nCtx, solver.NativeObject, GCHandle.ToIntPtr(gch), push_eh, pop_eh, fresh_eh);
}
/// <summary>
@ -194,8 +195,7 @@ namespace Microsoft.Z3
/// </summary>
public UserPropagator(Context _ctx)
{
id = propagators.Count;
propagators.Add(this);
gch = GCHandle.Alloc(this);
solver = null;
ctx = _ctx;
}
@ -205,7 +205,7 @@ namespace Microsoft.Z3
/// </summary>
~UserPropagator()
{
propagators[id] = null;
gch.Free();
if (solver == null)
ctx.Dispose();
}

View file

@ -1717,8 +1717,8 @@ public class Context implements AutoCloseable {
* {@code [domain -> range]}, and {@code i} must have the sort
* {@code domain}. The sort of the result is {@code range}.
*
* @see #mkArraySort(Sort[], Sort)
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
public <D extends Sort, R extends Sort> Expr<R> mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
{
@ -1739,8 +1739,8 @@ public class Context implements AutoCloseable {
* {@code [domains -> range]}, and {@code args} must have the sorts
* {@code domains}. The sort of the result is {@code range}.
*
* @see #mkArraySort(Sort[], Sort)
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
public <R extends Sort> Expr<R> mkSelect(Expr<ArraySort<Sort, R>> a, Expr<?>[] args)
{
@ -1763,8 +1763,8 @@ public class Context implements AutoCloseable {
* {@code select}) on all indices except for {@code i}, where it
* maps to {@code v} (and the {@code select} of {@code a}
* with respect to {@code i} may be a different value).
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
**/
public <D extends Sort, R extends Sort> ArrayExpr<D, R> mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
@ -1788,8 +1788,8 @@ public class Context implements AutoCloseable {
* {@code select}) on all indices except for {@code args}, where it
* maps to {@code v} (and the {@code select} of {@code a}
* with respect to {@code args} may be a different value).
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
**/
public <R extends Sort> ArrayExpr<Sort, R> mkStore(Expr<ArraySort<Sort, R>> a, Expr<?>[] args, Expr<R> v)
@ -1806,8 +1806,8 @@ public class Context implements AutoCloseable {
* Remarks: The resulting term is an array, such
* that a {@code select} on an arbitrary index produces the value
* {@code v}.
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
*
**/
public <D extends Sort, R extends Sort> ArrayExpr<D, R> mkConstArray(D domain, Expr<R> v)
@ -1826,9 +1826,9 @@ public class Context implements AutoCloseable {
* {@code f} must have type {@code range_1 .. range_n -> range}.
* {@code v} must have sort range. The sort of the result is
* {@code [domain_i -> range]}.
* @see #mkArraySort(Sort[], Sort)
* @see #mkSelect
* @see #mkStore
* @see #mkArraySort(Sort[], R)
* @see #mkSelect(Expr<ArraySort<D, R>> a, Expr<D> i)
* @see #mkStore(Expr<ArraySort<D, R>> a, Expr<D> i, Expr<R> v)
**/
@SafeVarargs
@ -2476,7 +2476,7 @@ public class Context implements AutoCloseable {
*
* @return A Term with value {@code num}/{@code den}
* and sort Real
* @see #mkNumeral(String,Sort)
* @see #mkNumeral(String v, R ty)
**/
public RatNum mkReal(int num, int den)
{
@ -2612,7 +2612,7 @@ public class Context implements AutoCloseable {
* 'names' of the bound variables, and {@code body} is the body
* of the quantifier. Quantifiers are associated with weights indicating the
* importance of using the quantifier during instantiation.
* Note that the bound variables are de-Bruijn indices created using {@link #mkBound}.
* Note that the bound variables are de-Bruijn indices created using {#mkBound}.
* Z3 applies the convention that the last element in {@code names} and
* {@code sorts} refers to the variable with index 0, the second to last element
* of {@code names} and {@code sorts} refers to the variable
@ -2707,7 +2707,7 @@ public class Context implements AutoCloseable {
* with the sorts of the bound variables, {@code names} is an array with the
* 'names' of the bound variables, and {@code body} is the body of the
* lambda.
* Note that the bound variables are de-Bruijn indices created using {@link #mkBound}
* Note that the bound variables are de-Bruijn indices created using {#mkBound}
* Z3 applies the convention that the last element in {@code names} and
* {@code sorts} refers to the variable with index 0, the second to last element
* of {@code names} and {@code sorts} refers to the variable

View file

@ -242,7 +242,7 @@ public class Model extends Z3Object {
* values. We say this finite set is the "universe" of the sort.
*
* @see #getNumSorts
* @see #getSortUniverse
* @see #getSortUniverse(R s)
*
* @throws Z3Exception
**/

View file

@ -1,4 +1,4 @@
// Automatically generated file
#include<jni.h>
#include<stdlib.h>
#include"z3.h"

View file

@ -161,6 +161,12 @@ public class Quantifier extends BoolExpr
/**
* Create a quantified expression.
*
* @param ctx Context to create the quantifier on.
* @param isForall Quantifier type.
* @param sorts Sorts of bound variables.
* @param names Names of bound variables
* @param body Body of quantifier
* @param weight Weight used to indicate priority for qunatifier instantiation
* @param patterns Nullable patterns
* @param noPatterns Nullable noPatterns
* @param quantifierID Nullable quantifierID

View file

@ -9,7 +9,7 @@ The readme for the bindings themselves is located in [`PUBLISHED_README.md`](./P
You'll need to have emscripten set up, along with all of its dependencies. The easiest way to do that is with [emsdk](https://github.com/emscripten-core/emsdk).
Then run `npm i` to install dependencies, `npm run build-ts` to build the TypeScript wrapper, and `npm run build-wasm` to build the wasm artifact.
Then run `npm i` to install dependencies, `npm run build:ts` to build the TypeScript wrapper, and `npm run build:wasm` to build the wasm artifact.
## Tests

View file

@ -27,7 +27,7 @@
"typescript": "^4.5.4"
},
"engines": {
"node": ">=16 <18"
"node": ">=16"
}
},
"node_modules/@ampproject/remapping": {

View file

@ -12,13 +12,13 @@
"homepage": "https://github.com/Z3Prover/z3/tree/master/src/api/js",
"repository": "github:Z3Prover/z3",
"engines": {
"node": ">=16 <18"
"node": ">=16"
},
"browser": "build/browser.js",
"main": "build/node.js",
"types": "build/node.d.ts",
"files": [
"build/*.{js,d.ts,wasm}"
"build/**/*.{js,d.ts,wasm}"
],
"scripts": {
"build:ts": "run-s -l build:ts:generate build:ts:tsc",

View file

@ -59,6 +59,7 @@ let mk_context (settings:(string * string) list) =
Z3native.del_config cfg;
Z3native.set_ast_print_mode res (Z3enums.int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT);
Z3native.set_internal_error_handler res;
Z3native.enable_concurrent_dec_ref res;
res
module Symbol =

View file

@ -49,6 +49,13 @@ type context
val mk_context : (string * string) list -> context
(** Interaction logging for Z3
Interaction logs are used to record calls into the API into a text file.
The text file can be replayed using z3. It has to be the same version of z3
to ensure that low level codes emitted in a log are compatible with the
version of z3 replaying the log. The file suffix ".log" is understood
by z3 as the format of the file being an interaction log. You can use the
optional comman-line parameter "-log" to force z3 to treat an input file
as an interaction log.
Note that this is a global, static log and if multiple Context
objects are created, it logs the interaction with all of them. *)
module Log :
@ -927,10 +934,10 @@ end
module FiniteDomain :
sig
(** Create a new finite domain sort. *)
val mk_sort : context -> Symbol.symbol -> int -> Sort.sort
val mk_sort : context -> Symbol.symbol -> int64 -> Sort.sort
(** Create a new finite domain sort. *)
val mk_sort_s : context -> string -> int -> Sort.sort
val mk_sort_s : context -> string -> int64 -> Sort.sort
(** Indicates whether the term is of an array sort. *)
val is_finite_domain : Expr.expr -> bool
@ -939,7 +946,7 @@ sig
val is_lt : Expr.expr -> bool
(** The size of the finite domain sort. *)
val get_size : Sort.sort -> int
val get_size : Sort.sort -> int64
end
@ -2078,7 +2085,7 @@ sig
val mk_numeral_i : context -> int -> Sort.sort -> Expr.expr
(** Create a numeral of FloatingPoint sort from a sign bit and two integers. *)
val mk_numeral_i_u : context -> bool -> int -> int -> Sort.sort -> Expr.expr
val mk_numeral_i_u : context -> bool -> int64 -> int64 -> Sort.sort -> Expr.expr
(** Create a numeral of FloatingPoint sort from a string *)
val mk_numeral_s : context -> string -> Sort.sort -> Expr.expr
@ -2303,7 +2310,7 @@ sig
val get_numeral_exponent_string : context -> Expr.expr -> bool -> string
(** Return the exponent value of a floating-point numeral as a signed integer *)
val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int
val get_numeral_exponent_int : context -> Expr.expr -> bool -> bool * int64
(** Return the exponent of a floating-point numeral as a bit-vector expression.
Remark: NaN's do not have a bit-vector exponent, so they are invalid arguments. *)
@ -2320,7 +2327,7 @@ sig
Remark: This function extracts the significand bits, without the
hidden bit or normalization. Throws an exception if the
significand does not fit into an int. *)
val get_numeral_significand_uint : context -> Expr.expr -> bool * int
val get_numeral_significand_uint : context -> Expr.expr -> bool * int64
(** Indicates whether a floating-point numeral is a NaN. *)
val is_numeral_nan : context -> Expr.expr -> bool

View file

@ -21,6 +21,7 @@ and solver_callback = ptr
and goal = ptr
and tactic = ptr
and params = ptr
and parser_context = ptr
and probe = ptr
and stats = ptr
and ast_vector = ptr

View file

@ -418,6 +418,7 @@ MK_PLUS_OBJ_NO_REF(constructor_list, 16)
MK_PLUS_OBJ_NO_REF(rcf_num, 16)
MK_PLUS_OBJ(params, 64)
MK_PLUS_OBJ(param_descrs, 64)
MK_PLUS_OBJ(parser_context, 64)
MK_PLUS_OBJ(model, 64)
MK_PLUS_OBJ(func_interp, 32)
MK_PLUS_OBJ(func_entry, 32)

View file

@ -129,6 +129,15 @@ def _configure_z3():
for key, val in cmake_options.items():
if type(val) is bool:
cmake_options[key] = str(val).upper()
# Allow command-line arguments to add and override Z3_ options
for i in range(len(sys.argv) - 1):
key = sys.argv[i]
if key.starts_with("Z3_"):
val = sys.argv[i + 1].upper()
if val == "TRUE" or val == "FALSE":
cmake_options[key] = val
cmake_args = [ '-D' + key + '=' + value for key,value in cmake_options.items() ]
args = [ 'cmake', *cmake_args, SRC_DIR ]
if subprocess.call(args, env=build_env, cwd=BUILD_DIR) != 0:

View file

@ -5345,6 +5345,10 @@ class DatatypeRef(ExprRef):
"""Return the datatype sort of the datatype expression `self`."""
return DatatypeSortRef(Z3_get_sort(self.ctx_ref(), self.as_ast()), self.ctx)
def DatatypeSort(name, ctx = None):
"""Create a reference to a sort that was declared, or will be declared, as a recursive datatype"""
ctx = _get_ctx(ctx)
return DatatypeSortRef(Z3_mk_datatype_sort(ctx.ref(), to_symbol(name, ctx)), ctx)
def TupleSort(name, sorts, ctx=None):
"""Create a named tuple sort base on a set of underlying sorts
@ -9192,6 +9196,25 @@ def _dict2darray(decls, ctx):
i = i + 1
return sz, _names, _decls
class ParserContext:
def __init__(self, ctx= None):
self.ctx = _get_ctx(ctx)
self.pctx = Z3_mk_parser_context(self.ctx.ref())
Z3_parser_context_inc_ref(self.ctx.ref(), self.pctx)
def __del__(self):
if self.ctx.ref() is not None and self.pctx is not None and Z3_parser_context_dec_ref is not None:
Z3_parser_context_dec_ref(self.ctx.ref(), self.pctx)
self.pctx = None
def add_sort(self, sort):
Z3_parser_context_add_sort(self.ctx.ref(), self.pctx, sort.as_ast())
def add_decl(self, decl):
Z3_parser_context_add_decl(self.ctx.ref(), self.pctx, decl.as_ast())
def from_string(self, s):
return AstVector(Z3_parser_context_from_string(self.ctx.ref(), self.pctx, s), self.ctx)
def parse_smt2_string(s, sorts={}, decls={}, ctx=None):
"""Parse a string in SMT 2.0 format using the given sorts and decls.
@ -11334,11 +11357,20 @@ def user_prop_pop(ctx, cb, num_scopes):
prop.cb = cb
prop.pop(num_scopes)
def to_ContextObj(ptr,):
ctx = ContextObj(ptr)
super(ctypes.c_void_p, ctx).__init__(ptr)
return ctx
def user_prop_fresh(id, ctx):
def user_prop_fresh(ctx, new_ctx):
_prop_closures.set_threaded()
prop = _prop_closures.get(id)
new_prop = prop.fresh()
prop = _prop_closures.get(ctx)
nctx = Context()
new_ctx = to_ContextObj(new_ctx)
nctx.ctx = new_ctx
nctx.eh = Z3_set_error_handler(new_ctx, z3_error_handler)
new_prop = prop.fresh(nctx)
_prop_closures.set(new_prop.id, new_prop)
return ctypes.c_void_p(new_prop.id)
@ -11401,6 +11433,7 @@ class UserPropagateBase:
ensure_prop_closures()
self.solver = s
self._ctx = None
self.fresh_ctx = None
self.cb = None
self.id = _prop_closures.insert(self)
self.fixed = None
@ -11408,12 +11441,7 @@ class UserPropagateBase:
self.eq = None
self.diseq = None
if ctx:
# TBD fresh is broken: ctx is not of the right type when we reach here.
self._ctx = Context()
#Z3_del_context(self._ctx.ctx)
#self._ctx.ctx = ctx
#self._ctx.eh = Z3_set_error_handler(ctx, z3_error_handler)
#Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT)
self.fresh_ctx = ctx
if s:
Z3_solver_propagate_init(self.ctx_ref(),
s.solver,
@ -11427,8 +11455,8 @@ class UserPropagateBase:
self._ctx.ctx = None
def ctx(self):
if self._ctx:
return self._ctx
if self.fresh_ctx:
return self.fresh_ctx
else:
return self.solver.ctx
@ -11438,25 +11466,29 @@ class UserPropagateBase:
def add_fixed(self, fixed):
assert not self.fixed
assert not self._ctx
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
if self.solver:
Z3_solver_propagate_fixed(self.ctx_ref(), self.solver.solver, _user_prop_fixed)
self.fixed = fixed
def add_final(self, final):
assert not self.final
assert not self._ctx
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
if self.solver:
Z3_solver_propagate_final(self.ctx_ref(), self.solver.solver, _user_prop_final)
self.final = final
def add_eq(self, eq):
assert not self.eq
assert not self._ctx
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
if self.solver:
Z3_solver_propagate_eq(self.ctx_ref(), self.solver.solver, _user_prop_eq)
self.eq = eq
def add_diseq(self, diseq):
assert not self.diseq
assert not self._ctx
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
if self.solver:
Z3_solver_propagate_diseq(self.ctx_ref(), self.solver.solver, _user_prop_diseq)
self.diseq = diseq
def push(self):
@ -11465,7 +11497,7 @@ class UserPropagateBase:
def pop(self, num_scopes):
raise Z3Exception("pop needs to be overwritten")
def fresh(self):
def fresh(self, new_ctx):
raise Z3Exception("fresh needs to be overwritten")
def add(self, e):

View file

@ -216,6 +216,13 @@ class ParamDescrs(ctypes.c_void_p):
def from_param(obj):
return obj
class ParserContextObj(ctypes.c_void_p):
def __init__(self, pc):
self._as_parameter_ = pc
def from_param(obj):
return obj
class FuncInterpObj(ctypes.c_void_p):
def __init__(self, f):

View file

@ -20,6 +20,7 @@ DEFINE_TYPE(Z3_constructor);
DEFINE_TYPE(Z3_constructor_list);
DEFINE_TYPE(Z3_params);
DEFINE_TYPE(Z3_param_descrs);
DEFINE_TYPE(Z3_parser_context);
DEFINE_TYPE(Z3_goal);
DEFINE_TYPE(Z3_tactic);
DEFINE_TYPE(Z3_probe);
@ -58,6 +59,7 @@ DEFINE_TYPE(Z3_rcf_num);
- \c Z3_constructor_list: list of constructors for a (recursive) datatype.
- \c Z3_params: parameter set used to configure many components such as: simplifiers, tactics, solvers, etc.
- \c Z3_param_descrs: provides a collection of parameter names, their types, default values and documentation strings. Solvers, tactics, and other objects accept different collection of parameters.
- \c Z3_parser_context: context for incrementally parsing strings. Declarations can be added incrementally to the parser state.
- \c Z3_model: model for the constraints asserted into the logical context.
- \c Z3_func_interp: interpretation of a function in a model.
- \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point.
@ -1413,6 +1415,7 @@ typedef enum
def_Type('CONSTRUCTOR_LIST', 'Z3_constructor_list', 'ConstructorList')
def_Type('SOLVER', 'Z3_solver', 'SolverObj')
def_Type('SOLVER_CALLBACK', 'Z3_solver_callback', 'SolverCallbackObj')
def_Type('PARSER_CONTEXT', 'Z3_parser_context', 'ParserContextObj')
def_Type('GOAL', 'Z3_goal', 'GoalObj')
def_Type('TACTIC', 'Z3_tactic', 'TacticObj')
def_Type('PARAMS', 'Z3_params', 'Params')
@ -1702,6 +1705,16 @@ extern "C" {
void Z3_API Z3_interrupt(Z3_context c);
/**
\brief use concurrency control for dec-ref.
Reference counting decrements are allowed in separate threads from the context.
If this setting is not invoked, reference counting decrements are not going to be thread safe.
def_API('Z3_enable_concurrent_dec_ref', VOID, (_in(CONTEXT),))
*/
void Z3_API Z3_enable_concurrent_dec_ref(Z3_context c);
/**@}*/
/** @name Parameters */
@ -5827,6 +5840,55 @@ extern "C" {
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
/**
\brief Create a parser context.
A parser context maintains state between calls to \c Z3_parser_context_parse_string
where the caller can pass in a set of SMTLIB2 commands.
It maintains all the declarations from previous calls together with
of sorts and function declarations (including 0-ary) that are added directly to the context.
def_API('Z3_mk_parser_context', PARSER_CONTEXT, (_in(CONTEXT),))
*/
Z3_parser_context Z3_API Z3_mk_parser_context(Z3_context c);
/**
\brief Increment the reference counter of the given \c Z3_parser_context object.
def_API('Z3_parser_context_inc_ref', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT)))
*/
void Z3_API Z3_parser_context_inc_ref(Z3_context c, Z3_parser_context pc);
/**
\brief Decrement the reference counter of the given \c Z3_parser_context object.
def_API('Z3_parser_context_dec_ref', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT)))
*/
void Z3_API Z3_parser_context_dec_ref(Z3_context c, Z3_parser_context pc);
/**
\brief Add a sort declaration.
def_API('Z3_parser_context_add_sort', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(SORT)))
*/
void Z3_API Z3_parser_context_add_sort(Z3_context c, Z3_parser_context pc, Z3_sort s);
/**
\brief Add a function declaration.
def_API('Z3_parser_context_add_decl', VOID, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(FUNC_DECL)))
*/
void Z3_API Z3_parser_context_add_decl(Z3_context c, Z3_parser_context pc, Z3_func_decl f);
/**
\brief Parse a string of SMTLIB2 commands. Return assertions.
def_API('Z3_parser_context_from_string', AST_VECTOR, (_in(CONTEXT), _in(PARSER_CONTEXT), _in(STRING)))
*/
Z3_ast_vector Z3_API Z3_parser_context_from_string(Z3_context c, Z3_parser_context pc, Z3_string s);
/**@}*/
/** @name Error Handling */
@ -6811,7 +6873,7 @@ extern "C" {
/**
\brief register a callback when a new expression with a registered function is used by the solver
The registered function appears at the top level and is created using \ref Z3_propagate_solver_declare.
The registered function appears at the top level and is created using \ref Z3_solver_propagate_declare.
def_API('Z3_solver_propagate_created', VOID, (_in(CONTEXT), _in(SOLVER), _fnptr(Z3_created_eh)))
*/
@ -6837,7 +6899,7 @@ extern "C" {
/**
Create uninterpreted function declaration for the user propagator.
When expressions using the function are created by the solver invoke a callback
to \ref \Z3_solver_progate_created with arguments
to \ref \Z3_solver_propagate_created with arguments
1. context and callback solve
2. declared_expr: expression using function that was used as the top-level symbol
3. declared_id: a unique identifier (unique within the current scope) to track the expression.

View file

@ -471,26 +471,31 @@ bool compare_nodes(ast const * n1, ast const * n2) {
return
to_var(n1)->get_idx() == to_var(n2)->get_idx() &&
to_var(n1)->get_sort() == to_var(n2)->get_sort();
case AST_QUANTIFIER:
case AST_QUANTIFIER: {
quantifier const* q1 = to_quantifier(n1);
quantifier const* q2 = to_quantifier(n2);
return
to_quantifier(n1)->get_kind() == to_quantifier(n2)->get_kind() &&
to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() &&
compare_arrays(to_quantifier(n1)->get_decl_sorts(),
to_quantifier(n2)->get_decl_sorts(),
to_quantifier(n1)->get_num_decls()) &&
compare_arrays(to_quantifier(n1)->get_decl_names(),
to_quantifier(n2)->get_decl_names(),
to_quantifier(n1)->get_num_decls()) &&
to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() &&
to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() &&
to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() &&
compare_arrays(to_quantifier(n1)->get_patterns(),
to_quantifier(n2)->get_patterns(),
to_quantifier(n1)->get_num_patterns()) &&
to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() &&
compare_arrays(to_quantifier(n1)->get_no_patterns(),
to_quantifier(n2)->get_no_patterns(),
to_quantifier(n1)->get_num_no_patterns());
q1->get_kind() == q2->get_kind() &&
q1->get_num_decls() == q2->get_num_decls() &&
compare_arrays(q1->get_decl_sorts(),
q2->get_decl_sorts(),
q1->get_num_decls()) &&
compare_arrays(q1->get_decl_names(),
q2->get_decl_names(),
q1->get_num_decls()) &&
q1->get_expr() == q2->get_expr() &&
q1->get_weight() == q2->get_weight() &&
q1->get_num_patterns() == q2->get_num_patterns() &&
((q1->get_qid().is_numerical() && q2->get_qid().is_numerical()) ||
(q1->get_qid() == q2->get_qid())) &&
compare_arrays(q1->get_patterns(),
q2->get_patterns(),
q1->get_num_patterns()) &&
q1->get_num_no_patterns() == q2->get_num_no_patterns() &&
compare_arrays(q1->get_no_patterns(),
q2->get_no_patterns(),
q1->get_num_no_patterns());
}
default:
UNREACHABLE();
}

View file

@ -67,6 +67,30 @@ namespace recfun {
m_decl = m.mk_func_decl(s, arity, domain, range, info);
}
def* def::copy(util& dst, ast_translation& tr) {
SASSERT(&dst.m() == &tr.to());
sort_ref_vector domain(tr.to());
sort_ref range(tr(m_range.get()), tr.to());
for (auto* s : m_domain)
domain.push_back(tr(s));
family_id fid = dst.get_family_id();
bool is_generated = m_decl->get_parameter(0).get_int() != 0;
def* r = alloc(def, tr.to(), fid, m_name, domain.size(), domain.data(), range, is_generated);
r->m_rhs = tr(m_rhs.get());
for (auto* v : m_vars)
r->m_vars.push_back(tr(v));
for (auto const& c1 : m_cases) {
r->m_cases.push_back(case_def(tr.to()));
auto& c2 = r->m_cases.back();
c2.m_pred = tr(c1.m_pred.get());
c2.m_guards = tr(c1.m_guards);
c2.m_rhs = tr(c1.m_rhs.get());
c2.m_def = r;
c2.m_immediate = c1.m_immediate;
}
return r;
}
bool def::contains_def(util& u, expr * e) {
struct def_find_p : public i_expr_pred {
util& u;
@ -415,6 +439,19 @@ namespace recfun {
return promise_def(&u(), d);
}
void plugin::inherit(decl_plugin* other, ast_translation& tr) {
for (auto [k, v] : static_cast<plugin*>(other)->m_defs) {
func_decl_ref f(tr(k), tr.to());
if (m_defs.contains(f))
continue;
def* d = v->copy(u(), tr);
m_defs.insert(f, d);
for (case_def & c : d->get_cases())
m_case_defs.insert(c.get_decl(), &c);
}
}
promise_def plugin::ensure_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
erase_def(d->get_decl());

View file

@ -21,6 +21,7 @@ Revision History:
#include "ast/ast.h"
#include "ast/ast_pp.h"
#include "ast/ast_translation.h"
#include "util/obj_hashtable.h"
namespace recfun {
@ -62,6 +63,12 @@ namespace recfun {
def * m_def; //<! definition this is a part of
bool m_immediate; //<! does `rhs` contain no defined_fun/case_pred?
case_def(ast_manager& m):
m_pred(m),
m_guards(m),
m_rhs(m)
{}
case_def(ast_manager & m,
family_id fid,
def * d,
@ -132,6 +139,8 @@ namespace recfun {
bool is_fun_macro() const { return m_cases.size() == 1; }
bool is_fun_defined() const { return !is_fun_macro(); }
def* copy(util& dst, ast_translation& tr);
};
// definition to be complete (missing RHS)
@ -205,6 +214,8 @@ namespace recfun {
expr_ref redirect_ite(replace& subst, unsigned n, var * const* vars, expr * e);
void inherit(decl_plugin* other, ast_translation& tr) override;
};
}

View file

@ -37,21 +37,57 @@ br_status char_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co
case OP_CHAR_CONST:
break;
case OP_CHAR_LE:
st = mk_char_le(args[0], args[1], result);
break;
case OP_CHAR_TO_INT:
st = mk_char_to_int(args[0], result);
break;
case OP_CHAR_TO_BV:
st = mk_char_to_bv(args[0], result);
break;
case OP_CHAR_FROM_BV:
st = mk_char_from_bv(args[0], result);
break;
case OP_CHAR_IS_DIGIT:
st = mk_char_is_digit(args[0], result);
break;
}
return st;
}
br_status char_rewriter::mk_char_is_digit(expr* a, expr_ref& result) {
unsigned n;
if (!m_char->is_const_char(a, n))
return BR_FAILED;
result = m.mk_bool_val('0' <= n && n <= '9');
return BR_DONE;
}
br_status char_rewriter::mk_char_to_bv(expr* a, expr_ref& result) {
return BR_FAILED;
}
br_status char_rewriter::mk_char_le(expr* a, expr* b, expr_ref& result) {
unsigned na = 0, nb = 0;
if (m_char->is_const_char(a, na)) {
if (na == 0) {
result = m.mk_true();
return BR_DONE;
}
}
if (m_char->is_const_char(b, nb)) {
if (na != 0) {
result = m.mk_bool_val(na <= nb);
return BR_DONE;
}
if (nb == m_char->max_char()) {
result = m.mk_true();
return BR_DONE;
}
}
return BR_FAILED;
}
br_status char_rewriter::mk_char_from_bv(expr* e, expr_ref& result) {
bv_util bv(m);
rational n;

View file

@ -35,6 +35,12 @@ class char_rewriter {
br_status mk_char_to_int(expr* e, expr_ref& result);
br_status mk_char_le(expr* a, expr* b, expr_ref& result);
br_status mk_char_is_digit(expr* a, expr_ref& result);
br_status mk_char_to_bv(expr* a, expr_ref& result);
public:
char_rewriter(ast_manager& m);

View file

@ -532,7 +532,12 @@ func_decl* seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p
case _OP_STRING_FROM_CHAR: {
if (!(num_parameters == 1 && parameters[0].is_int()))
m.raise_exception("character literal expects integer parameter");
zstring zs(parameters[0].get_int());
int i = parameters[0].get_int();
if (i < 0)
m.raise_exception("character literal expects a non-negative integer parameter");
if (i > (int)m_char_plugin->max_char())
m.raise_exception("character literal is out of bounds");
zstring zs(i);
parameter p(zs);
return m.mk_const_decl(m_stringc_sym, m_string,func_decl_info(m_family_id, OP_STRING_CONST, 1, &p));
}
@ -966,6 +971,22 @@ bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) c
return false;
}
bool seq_util::str::is_concat_of_units(expr* s) const {
ptr_vector<expr> todo;
todo.push_back(s);
while (!todo.empty()) {
expr* e = todo.back();
todo.pop_back();
if (is_empty(e) || is_unit(e))
continue;
if (is_concat(e))
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
else
return false;
}
return true;
}
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
zstring z;
expr* ch = nullptr;

View file

@ -375,6 +375,7 @@ public:
bool is_to_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_TO_CODE); }
bool is_len_sub(expr const* n, expr*& l, expr*& u, rational& k) const;
bool is_concat_of_units(expr* n) const;
/*
tests if s is a single character string(c) or a unit (c)

View file

@ -219,9 +219,8 @@ ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it
pr = ctx.get_check_sat_result()->get_proof();
if (pr == 0)
throw cmd_exception("proof is not available");
if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr)) {
if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr))
throw cmd_exception("proof is not well sorted");
}
context_params& params = ctx.params();
const std::string& file = params.m_dot_proof_file;
@ -235,11 +234,11 @@ static void print_core(cmd_context& ctx) {
ctx.regular_stream() << "(";
bool first = true;
for (expr* e : core) {
if (first)
first = false;
else
ctx.regular_stream() << " ";
ctx.regular_stream() << mk_ismt2_pp(e, ctx.m());
if (first)
first = false;
else
ctx.regular_stream() << " ";
ctx.regular_stream() << mk_ismt2_pp(e, ctx.m());
}
ctx.regular_stream() << ")" << std::endl;
}
@ -260,9 +259,8 @@ ATOMIC_CMD(get_unsat_assumptions_cmd, "get-unsat-assumptions", "retrieve subset
return;
if (!ctx.produce_unsat_assumptions())
throw cmd_exception("unsat assumptions construction is not enabled, use command (set-option :produce-unsat-assumptions true)");
if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat) {
if (!ctx.has_manager() || ctx.cs_state() != cmd_context::css_unsat)
throw cmd_exception("unsat assumptions is not available");
}
print_core(ctx);
});
@ -410,6 +408,15 @@ class set_option_cmd : public set_get_option_cmd {
}
}
static void check_no_assertions(cmd_context & ctx, symbol const & opt_name) {
if (ctx.has_assertions()) {
std::string msg = "error setting '";
msg += opt_name.str();
msg += "', option value cannot be modified after assertions have been added";
throw cmd_exception(std::move(msg));
}
}
void set_param(cmd_context & ctx, char const * value) {
try {
gparams::set(m_option, value);
@ -437,11 +444,11 @@ class set_option_cmd : public set_get_option_cmd {
ctx.set_interactive_mode(to_bool(value));
}
else if (m_option == m_produce_proofs) {
check_not_initialized(ctx, m_produce_proofs);
check_no_assertions(ctx, m_produce_proofs);
ctx.set_produce_proofs(to_bool(value));
}
else if (m_option == m_produce_unsat_cores) {
check_not_initialized(ctx, m_produce_unsat_cores);
check_no_assertions(ctx, m_produce_unsat_cores);
ctx.set_produce_unsat_cores(to_bool(value));
}
else if (m_option == m_produce_unsat_assumptions) {

View file

@ -538,22 +538,9 @@ public:
cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l):
m_main_ctx(main_ctx),
m_logic(l),
m_interactive_mode(false),
m_global_decls(false),
m_print_success(m_params.m_smtlib2_compliant),
m_random_seed(0),
m_produce_unsat_cores(false),
m_produce_unsat_assumptions(false),
m_produce_assignments(false),
m_status(UNKNOWN),
m_numeral_as_real(false),
m_ignore_check(false),
m_exit_on_error(false),
m_manager(m),
m_own_manager(m == nullptr),
m_manager_initialized(false),
m_pmanager(nullptr),
m_sexpr_manager(nullptr),
m_regular("stdout", std::cout),
m_diagnostic("stderr", std::cerr) {
SASSERT(m != 0 || !has_manager());
@ -607,6 +594,7 @@ void cmd_context::global_params_updated() {
m_params.updt_params();
if (m_params.m_smtlib2_compliant)
m_print_success = true;
set_produce_proofs(m_params.m_proof);
if (m_solver) {
params_ref p;
if (!m_params.m_auto_config)
@ -626,13 +614,14 @@ void cmd_context::set_produce_models(bool f) {
void cmd_context::set_produce_unsat_cores(bool f) {
// can only be set before initialization
SASSERT(!has_manager());
SASSERT(!has_assertions());
m_params.m_unsat_core |= f;
}
void cmd_context::set_produce_proofs(bool f) {
// can only be set before initialization
SASSERT(!has_manager());
SASSERT(!has_assertions() || m_params.m_proof == f);
if (has_manager())
m().toggle_proof_mode(f ? PGM_ENABLED : PGM_DISABLED);
m_params.m_proof = f;
}
@ -835,15 +824,16 @@ bool cmd_context::set_logic(symbol const & s) {
TRACE("cmd_context", tout << s << "\n";);
if (has_logic())
throw cmd_exception("the logic has already been set");
if (has_manager() && m_main_ctx)
if (has_assertions() && m_main_ctx)
throw cmd_exception("logic must be set before initialization");
if (!smt_logics::supported_logic(s)) {
if (!smt_logics::supported_logic(s))
return false;
}
m_logic = s;
if (smt_logics::logic_has_reals_only(s)) {
if (m_solver)
mk_solver();
if (smt_logics::logic_has_reals_only(s))
m_numeral_as_real = true;
}
return true;
}
@ -1837,6 +1827,10 @@ void cmd_context::add_declared_functions(model& mdl) {
}
void cmd_context::display_sat_result(lbool r) {
if (has_manager() && m().has_trace_stream()) {
m().trace_stream().flush();
}
switch (r) {
case l_true:
regular_stream() << "sat" << std::endl;
@ -2203,22 +2197,25 @@ expr_ref_vector cmd_context::tracked_assertions() {
for (unsigned i = 0; i < assertions().size(); ++i) {
expr* an = assertion_names()[i];
expr* asr = assertions()[i];
if (an) {
if (an)
result.push_back(m().mk_implies(an, asr));
}
else {
else
result.push_back(asr);
}
}
}
else {
for (expr * e : assertions()) {
for (expr * e : assertions())
result.push_back(e);
}
}
return result;
}
void cmd_context::reset_tracked_assertions() {
m_assertion_names.reset();
for (expr* a : m_assertions)
m().dec_ref(a);
m_assertions.reset();
}
void cmd_context::display_assertions() {
if (!m_interactive_mode)
@ -2254,9 +2251,8 @@ format_ns::format * cmd_context::pp(sort * s) const {
}
cmd_context::pp_env & cmd_context::get_pp_env() const {
if (m_pp_env.get() == nullptr) {
if (m_pp_env.get() == nullptr)
const_cast<cmd_context*>(this)->m_pp_env = alloc(pp_env, *const_cast<cmd_context*>(this));
}
return *(m_pp_env.get());
}
@ -2314,9 +2310,8 @@ void cmd_context::display_smt2_benchmark(std::ostream & out, unsigned num, expr
out << "(set-logic " << logic << ")" << std::endl;
// collect uninterpreted function declarations
decl_collector decls(m());
for (unsigned i = 0; i < num; i++) {
for (unsigned i = 0; i < num; i++)
decls.visit(assertions[i]);
}
// TODO: display uninterpreted sort decls, and datatype decls.
@ -2342,9 +2337,8 @@ void cmd_context::slow_progress_sample() {
svector<symbol> labels;
m_solver->get_labels(labels);
regular_stream() << "(labels";
for (symbol const& s : labels) {
for (symbol const& s : labels)
regular_stream() << " " << s;
}
regular_stream() << "))" << std::endl;
}

View file

@ -194,21 +194,21 @@ public:
protected:
ast_context_params m_params;
ast_context_params m_params;
bool m_main_ctx;
symbol m_logic;
bool m_interactive_mode;
bool m_global_decls;
bool m_interactive_mode = false;
bool m_global_decls = false;
bool m_print_success;
unsigned m_random_seed;
bool m_produce_unsat_cores;
bool m_produce_unsat_assumptions;
bool m_produce_assignments;
status m_status;
bool m_numeral_as_real;
bool m_ignore_check; // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
bool m_exit_on_error;
bool m_allow_duplicate_declarations { false };
unsigned m_random_seed = 0;
bool m_produce_unsat_cores = false;
bool m_produce_unsat_assumptions = false;
bool m_produce_assignments = false;
status m_status = UNKNOWN;
bool m_numeral_as_real = false;
bool m_ignore_check = false; // used by the API to disable check-sat() commands when parsing SMT 2.0 files.
bool m_exit_on_error = false;
bool m_allow_duplicate_declarations = false;
static std::ostringstream g_error_stream;
@ -216,9 +216,9 @@ protected:
sref_vector<generic_model_converter> m_mcs;
ast_manager * m_manager;
bool m_own_manager;
bool m_manager_initialized;
pdecl_manager * m_pmanager;
sexpr_manager * m_sexpr_manager;
bool m_manager_initialized = false;
pdecl_manager * m_pmanager = nullptr;
sexpr_manager * m_sexpr_manager = nullptr;
check_logic m_check_logic;
stream_ref m_regular;
stream_ref m_diagnostic;
@ -362,6 +362,7 @@ public:
bool produce_unsat_cores() const;
bool well_sorted_check_enabled() const;
bool validate_model_enabled() const;
bool has_assertions() const { return !m_assertions.empty(); }
void set_produce_models(bool flag);
void set_produce_unsat_cores(bool flag);
void set_produce_proofs(bool flag);
@ -485,6 +486,7 @@ public:
ptr_vector<expr> const& assertions() const { return m_assertions; }
ptr_vector<expr> const& assertion_names() const { return m_assertion_names; }
expr_ref_vector tracked_assertions();
void reset_tracked_assertions();
/**
\brief Hack: consume assertions if there are no scopes.

View file

@ -377,8 +377,8 @@ public:
};
class get_interpolant_cmd : public cmd {
expr* m_a;
expr* m_b;
scoped_ptr<expr_ref> m_a;
scoped_ptr<expr_ref> m_b;
public:
get_interpolant_cmd():cmd("get-interpolant") {}
char const * get_usage() const override { return "<expr> <expr>"; }
@ -388,17 +388,24 @@ public:
return CPK_EXPR;
}
void set_next_arg(cmd_context& ctx, expr * arg) override {
if (m_a == nullptr)
m_a = arg;
ast_manager& m = ctx.m();
if (!m.is_bool(arg))
throw default_exception("argument to interpolation is not Boolean");
if (!m_a)
m_a = alloc(expr_ref, arg, m);
else
m_b = arg;
m_b = alloc(expr_ref, arg, m);
}
void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; }
void execute(cmd_context & ctx) override {
ast_manager& m = ctx.m();
qe::interpolator mbi(m);
if (!m_a || !m_b)
throw default_exception("interpolation requires two arguments");
if (!m.is_bool(*m_a) || !m.is_bool(*m_b))
throw default_exception("interpolation requires two Boolean arguments");
expr_ref itp(m);
mbi.pogo(ctx.get_solver_factory(), m_a, m_b, itp);
mbi.pogo(ctx.get_solver_factory(), *m_a, *m_b, itp);
ctx.regular_stream() << itp << "\n";
}
};

View file

@ -1741,6 +1741,87 @@ namespace dd {
return (*this) * rational::power_of_two(n);
}
/**
* \brief substitute variable v by r.
* This base line implementation is simplistic and does not use operator caching.
*/
pdd pdd::subst_pdd(unsigned v, pdd const& r) const {
if (is_val())
return *this;
if (m.m_var2level[var()] < m.m_var2level[v])
return *this;
pdd l = lo().subst_pdd(v, r);
pdd h = hi().subst_pdd(v, r);
if (var() == v)
return r*h + l;
else if (l == lo() && h == hi())
return *this;
else
return m.mk_var(var())*h + l;
}
std::pair<unsigned_vector, pdd> pdd::var_factors() const {
if (is_val())
return { unsigned_vector(), *this };
unsigned v = var();
if (lo().is_val()) {
if (!lo().is_zero())
return { unsigned_vector(), *this };
auto [vars, p] = hi().var_factors();
vars.push_back(v);
return {vars, p};
}
auto [lo_vars, q] = lo().var_factors();
if (lo_vars.empty())
return { unsigned_vector(), *this };
unsigned_vector lo_and_hi;
auto merge = [&](unsigned_vector& lo_vars, unsigned_vector& hi_vars) {
unsigned ir = 0, jr = 0;
for (unsigned i = 0, j = 0; i < lo_vars.size() || j < hi_vars.size(); ) {
if (i == lo_vars.size())
hi_vars[jr++] = hi_vars[j++];
else if (j == hi_vars.size())
lo_vars[ir++] = lo_vars[i++];
else if (lo_vars[i] == hi_vars[j]) {
lo_and_hi.push_back(lo_vars[i]);
++i;
++j;
}
else if (m.m_var2level[lo_vars[i]] > m.m_var2level[hi_vars[j]])
hi_vars[jr++] = hi_vars[j++];
else
lo_vars[ir++] = lo_vars[i++];
}
lo_vars.shrink(ir);
hi_vars.shrink(jr);
};
auto mul = [&](unsigned_vector const& vars, pdd p) {
for (auto v : vars)
p *= m.mk_var(v);
return p;
};
auto [hi_vars, p] = hi().var_factors();
if (lo_vars.back() == v) {
lo_vars.pop_back();
merge(lo_vars, hi_vars);
lo_and_hi.push_back(v);
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
}
if (hi_vars.empty())
return { unsigned_vector(), *this };
merge(lo_vars, hi_vars);
hi_vars.push_back(v);
if (lo_and_hi.empty())
return { unsigned_vector(), *this };
else
return { lo_and_hi, mul(lo_vars, q) + mul(hi_vars, p) };
}
std::ostream& operator<<(std::ostream& out, pdd const& b) { return b.display(out); }
void pdd_iterator::next() {

View file

@ -447,11 +447,22 @@ namespace dd {
bool resolve(unsigned v, pdd const& other, pdd& result) { return m.resolve(v, *this, other, result); }
pdd reduce(unsigned v, pdd const& other) const { return m.reduce(v, *this, other); }
/**
* \brief factor out variables
*/
std::pair<unsigned_vector, pdd> var_factors() const;
pdd subst_val0(vector<std::pair<unsigned, rational>> const& s) const { return m.subst_val0(*this, s); }
pdd subst_val(pdd const& s) const { return m.subst_val(*this, s); }
pdd subst_val(vector<std::pair<unsigned, rational>> const& s) const { return m.subst_val0(*this, s); }
pdd subst_val(unsigned v, rational const& val) const { return m.subst_val(*this, v, val); }
pdd subst_add(unsigned var, rational const& val) { return m.subst_add(*this, var, val); }
/**
* \brief substitute variable v by r.
*/
pdd subst_pdd(unsigned v, pdd const& r) const;
std::ostream& display(std::ostream& out) const { return m.display(out, *this); }
bool operator==(pdd const& other) const { return root == other.root; }
bool operator!=(pdd const& other) const { return root != other.root; }

View file

@ -27,7 +27,33 @@ typedef dep_intervals::with_deps_t w_dep;
class pdd_interval {
dep_intervals& m_dep_intervals;
std::function<void (unsigned, bool, scoped_dep_interval&)> m_var2interval;
// retrieve intervals after distributing multiplication over addition.
template <w_dep wd>
void get_interval_distributed(pdd const& p, scoped_dep_interval& i, scoped_dep_interval& ret) {
bool deps = wd == w_dep::with_deps;
if (p.is_val()) {
if (deps)
m_dep_intervals.mul<dep_intervals::with_deps>(p.val(), i, ret);
else
m_dep_intervals.mul<dep_intervals::without_deps>(p.val(), i, ret);
return;
}
scoped_dep_interval hi(m()), lo(m()), t(m()), a(m());
get_interval_distributed<wd>(p.lo(), i, lo);
m_var2interval(p.var(), deps, a);
if (deps) {
m_dep_intervals.mul<dep_intervals::with_deps>(a, i, t);
get_interval_distributed<wd>(p.hi(), t, hi);
m_dep_intervals.add<dep_intervals::with_deps>(hi, lo, ret);
}
else {
m_dep_intervals.mul<dep_intervals::without_deps>(a, i, t);
get_interval_distributed<wd>(p.hi(), t, hi);
m_dep_intervals.add<dep_intervals::without_deps>(hi, lo, ret);
}
}
public:
pdd_interval(dep_intervals& d): m_dep_intervals(d) {}
@ -57,5 +83,11 @@ public:
}
}
template <w_dep wd>
void get_interval_distributed(pdd const& p, scoped_dep_interval& ret) {
scoped_dep_interval i(m());
m_dep_intervals.set_interval_for_scalar(i, rational::one());
get_interval_distributed<wd>(p, i, ret);
}
};
}

View file

@ -132,7 +132,7 @@ void grobner::display_vars(std::ostream & out, unsigned num_vars, expr * const *
}
}
void grobner::display_monomial(std::ostream & out, monomial const & m) const {
void grobner::display_monomial(std::ostream & out, monomial const & m, std::function<void(std::ostream&, expr*)>& display_var) const {
if (!m.m_coeff.is_one() || m.m_vars.empty()) {
out << m.m_coeff;
if (!m.m_vars.empty())
@ -165,7 +165,7 @@ void grobner::display_monomial(std::ostream & out, monomial const & m) const {
}
}
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
void grobner::display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials, std::function<void(std::ostream&, expr*)>& display_var) const {
bool first = true;
for (unsigned i = 0; i < num_monomials; i++) {
monomial const * m = monomials[i];
@ -173,26 +173,26 @@ void grobner::display_monomials(std::ostream & out, unsigned num_monomials, mono
first = false;
else
out << " + ";
display_monomial(out, *m);
display_monomial(out, *m, display_var);
}
}
void grobner::display_equation(std::ostream & out, equation const & eq) const {
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.data());
void grobner::display_equation(std::ostream & out, equation const & eq, std::function<void(std::ostream&, expr*)>& display_var) const {
display_monomials(out, eq.m_monomials.size(), eq.m_monomials.data(), display_var);
out << " = 0\n";
}
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header) const {
if (!v.empty()) {
out << header << "\n";
for (equation const* eq : v)
display_equation(out, *eq);
}
void grobner::display_equations(std::ostream & out, equation_set const & v, char const * header, std::function<void(std::ostream&, expr*)>& display_var) const {
if (v.empty())
return;
out << header << "\n";
for (equation const* eq : v)
display_equation(out, *eq, display_var);
}
void grobner::display(std::ostream & out) const {
display_equations(out, m_processed, "processed:");
display_equations(out, m_to_process, "to process:");
void grobner::display(std::ostream & out, std::function<void(std::ostream&, expr*)>& display_var) const {
display_equations(out, m_processed, "processed:", display_var);
display_equations(out, m_to_process, "to process:", display_var);
}
void grobner::set_weight(expr * n, int weight) {
@ -528,7 +528,7 @@ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<exp
for (; i2 < sz2; i2++)
rest.push_back(m2->m_vars[i2]);
TRACE("grobner",
tout << "monomail: "; display_monomial(tout, *m1); tout << " is a subset of ";
tout << "monomial: "; display_monomial(tout, *m1); tout << " is a subset of ";
display_monomial(tout, *m2); tout << "\n";
tout << "rest: "; display_vars(tout, rest.size(), rest.data()); tout << "\n";);
return true;
@ -552,7 +552,7 @@ bool grobner::is_subset(monomial const * m1, monomial const * m2, ptr_vector<exp
}
}
// is not subset
TRACE("grobner", tout << "monomail: "; display_monomial(tout, *m1); tout << " is not a subset of ";
TRACE("grobner", tout << "monomial: "; display_monomial(tout, *m1); tout << " is not a subset of ";
display_monomial(tout, *m2); tout << "\n";);
return false;
}

View file

@ -120,9 +120,16 @@ protected:
void display_var(std::ostream & out, expr * var) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials, std::function<void(std::ostream&, expr*)>& display_var) const;
void display_equations(std::ostream & out, equation_set const & v, char const * header) const;
void display_monomials(std::ostream & out, unsigned num_monomials, monomial * const * monomials) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_monomials(out, num_monomials, monomials, _fn);
}
void display_equations(std::ostream & out, equation_set const & v, char const * header, std::function<void(std::ostream&, expr*)>& display_var) const;
void del_equations(unsigned old_size);
@ -281,11 +288,26 @@ public:
void pop_scope(unsigned num_scopes);
void display_equation(std::ostream & out, equation const & eq) const;
void display_equation(std::ostream & out, equation const & eq) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_equation(out, eq, _fn);
}
void display_monomial(std::ostream & out, monomial const & m) const;
void display_monomial(std::ostream & out, monomial const & m) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display_monomial(out, m, _fn);
}
void display_equation(std::ostream & out, equation const & eq, std::function<void(std::ostream&, expr*)>& display_var) const;
void display(std::ostream & out) const;
void display_monomial(std::ostream & out, monomial const & m, std::function<void(std::ostream&, expr*)>& display_var) const;
void display(std::ostream & out) const {
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { display_var(out, v); };
display(out, _fn);
}
void display(std::ostream & out, std::function<void(std::ostream&, expr*)>& display_var) const;
};

View file

@ -223,6 +223,8 @@ namespace dd {
for (unsigned i = 0; i < s.m_to_simplify.size(); ++i) {
equation* e = s.m_to_simplify[i];
pdd p = e->poly();
if (p.is_val())
continue;
if (!p.hi().is_val()) {
continue;
}

View file

@ -11,9 +11,9 @@
--*/
#include "util/uint_set.h"
#include "math/grobner/pdd_solver.h"
#include "math/grobner/pdd_simplifier.h"
#include "util/uint_set.h"
#include <math.h>
@ -169,7 +169,7 @@ namespace dd {
/*
Use the given equation to simplify equations in set
*/
void solver::simplify_using(equation_vector& set, equation const& eq) {
void solver::simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier) {
struct scoped_update {
equation_vector& set;
unsigned i, j, sz;
@ -191,7 +191,7 @@ namespace dd {
equation& target = *set[sr.i];
bool changed_leading_term = false;
bool simplified = true;
simplified = !done() && try_simplify_using(target, eq, changed_leading_term);
simplified = !done() && simplifier(target, changed_leading_term);
if (simplified && is_trivial(target)) {
retire(&target);
@ -200,7 +200,6 @@ namespace dd {
// pushed to solved
}
else if (simplified && changed_leading_term) {
SASSERT(target.state() == processed);
push_equation(to_simplify, target);
if (!m_var2level.empty()) {
m_levelp1 = std::max(m_var2level[target.poly().var()]+1, m_levelp1);
@ -210,6 +209,13 @@ namespace dd {
sr.nextj();
}
}
}
void solver::simplify_using(equation_vector& set, equation const& eq) {
std::function<bool(equation&, bool&)> simplifier = [&](equation& target, bool& changed_leading_term) {
return try_simplify_using(target, eq, changed_leading_term);
};
simplify_using(set, simplifier);
}
/*
@ -342,6 +348,7 @@ namespace dd {
for (equation* e : m_solved) dealloc(e);
for (equation* e : m_to_simplify) dealloc(e);
for (equation* e : m_processed) dealloc(e);
m_subst.reset();
m_solved.reset();
m_processed.reset();
m_to_simplify.reset();
@ -352,18 +359,54 @@ namespace dd {
}
void solver::add(pdd const& p, u_dependency * dep) {
if (p.is_zero()) return;
equation * eq = alloc(equation, p, dep);
if (check_conflict(*eq)) {
if (p.is_zero())
return;
equation * eq = alloc(equation, p, dep);
if (check_conflict(*eq))
return;
}
push_equation(to_simplify, eq);
if (!m_var2level.empty()) {
if (!m_var2level.empty())
m_levelp1 = std::max(m_var2level[p.var()]+1, m_levelp1);
}
update_stats_max_degree_and_size(*eq);
}
}
void solver::add_subst(unsigned v, pdd const& p, u_dependency* dep) {
m_subst.push_back({v, p, dep});
if (!m_var2level.empty())
m_levelp1 = std::max(m_var2level[v]+1, std::max(m_var2level[p.var()]+1, m_levelp1));
std::function<bool(equation&, bool&)> simplifier = [&](equation& dst, bool& changed_leading_term) {
auto r = dst.poly().subst_pdd(v, p);
if (r == dst.poly())
return false;
if (is_too_complex(r)) {
m_too_complex = true;
return false;
}
changed_leading_term = m.different_leading_term(r, dst.poly());
dst = r;
dst = m_dep_manager.mk_join(dst.dep(), dep);
update_stats_max_degree_and_size(dst);
return true;
};
if (!done())
simplify_using(m_processed, simplifier);
if (!done())
simplify_using(m_to_simplify, simplifier);
if (!done())
simplify_using(m_solved, simplifier);
}
void solver::simplify(pdd& p, u_dependency*& d) {
for (auto const& [v, q, d2] : m_subst) {
pdd r = p.subst_pdd(v, q);
if (r != p) {
p = r;
d = m_dep_manager.mk_join(d, d2);
}
}
}
bool solver::canceled() {
return m_limit.is_canceled();
@ -446,9 +489,24 @@ namespace dd {
}
std::ostream& solver::display(std::ostream& out) const {
out << "solved\n"; for (auto e : m_solved) display(out, *e);
out << "processed\n"; for (auto e : m_processed) display(out, *e);
out << "to_simplify\n"; for (auto e : m_to_simplify) display(out, *e);
if (!m_solved.empty()) {
out << "solved\n"; for (auto e : m_solved) display(out, *e);
}
if (!m_processed.empty()) {
out << "processed\n"; for (auto e : m_processed) display(out, *e);
}
if (!m_to_simplify.empty()) {
out << "to_simplify\n"; for (auto e : m_to_simplify) display(out, *e);
}
if (!m_subst.empty()) {
out << "subst\n";
for (auto const& [v, p, d] : m_subst) {
out << "v" << v << " := " << p;
if (m_print_dep)
m_print_dep(d, out);
out << "\n";
}
}
return display_statistics(out);
}

View file

@ -118,6 +118,7 @@ private:
equation_vector m_solved; // equations with solved variables, triangular
equation_vector m_processed;
equation_vector m_to_simplify;
vector<std::tuple<unsigned, pdd, u_dependency*>> m_subst;
mutable u_dependency_manager m_dep_manager;
equation_vector m_all_eqs;
equation* m_conflict;
@ -136,6 +137,9 @@ public:
void add(pdd const& p) { add(p, nullptr); }
void add(pdd const& p, u_dependency * dep);
void simplify(pdd& p, u_dependency*& dep);
void add_subst(unsigned v, pdd const& p, u_dependency* dep);
void simplify();
void saturate();
@ -160,6 +164,7 @@ private:
void simplify_using(equation& eq, equation_vector const& eqs);
void simplify_using(equation_vector& set, equation const& eq);
void simplify_using(equation & dst, equation const& src, bool& changed_leading_term);
void simplify_using(equation_vector& set, std::function<bool(equation&, bool&)>& simplifier);
bool try_simplify_using(equation& target, equation const& source, bool& changed_leading_term);
bool is_trivial(equation const& eq) const { return eq.poly().is_zero(); }

View file

@ -222,7 +222,6 @@ public:
template <enum with_deps_t wd>
void mul(const rational& r, const interval& a, interval& b) const {
if (r.is_zero()) return;
m_imanager.mul(r.to_mpq(), a, b);
if (wd == with_deps) {
auto lower_dep = a.m_lower_dep;

View file

@ -34,6 +34,7 @@ z3_add_component(lp
nla_basics_lemmas.cpp
nla_common.cpp
nla_core.cpp
nla_grobner.cpp
nla_intervals.cpp
nla_monotone_lemmas.cpp
nla_order_lemmas.cpp

View file

@ -40,7 +40,7 @@ bool horner::row_has_monomial_to_refine(const T& row) const {
template <typename T>
bool horner::row_is_interesting(const T& row) const {
TRACE("nla_solver_details", c().print_row(row, tout););
if (row.size() > c().m_nla_settings.horner_row_length_limit()) {
if (row.size() > c().m_nla_settings.horner_row_length_limit) {
TRACE("nla_solver_details", tout << "disregard\n";);
return false;
}
@ -98,7 +98,7 @@ bool horner::lemmas_on_row(const T& row) {
}
bool horner::horner_lemmas() {
if (!c().m_nla_settings.run_horner()) {
if (!c().m_nla_settings.run_horner) {
TRACE("nla_solver", tout << "not generating horner lemmas\n";);
return false;
}

View file

@ -275,9 +275,6 @@ class lar_solver : public column_namer {
return m_column_buffer;
}
bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const;
inline unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
}
inline lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; }
void catch_up_in_updating_int_solver();
var_index to_column(unsigned ext_j) const;
@ -357,6 +354,10 @@ public:
}
void set_value_for_nbasic_column(unsigned j, const impq& new_val);
inline unsigned get_base_column_in_row(unsigned row_index) const {
return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index);
}
// lp_assert(implied_bound_is_correctly_explained(ib, explanation)); }
constraint_index mk_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side);
@ -630,6 +631,7 @@ public:
}
void round_to_integer_solution();
inline const row_strip<mpq> & get_row(unsigned i) const { return A_r().m_rows[i]; }
inline const row_strip<mpq> & basic2row(unsigned i) const { return A_r().m_rows[row_of_basic_column(i)]; }
inline const column_strip & get_column(unsigned i) const { return A_r().m_columns[i]; }
bool row_is_correct(unsigned i) const;
bool ax_is_correct() const;

View file

@ -71,11 +71,11 @@ void common::add_deps_of_fixed(lpvar j, u_dependency*& dep) {
// creates a nex expression for the coeff and var,
nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_dependency*& dep) {
SASSERT(!coeff.is_zero());
if (c().m_nla_settings.horner_subs_fixed() == 1 && c().var_is_fixed(j)) {
if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(j)) {
add_deps_of_fixed(j, dep);
return cn.mk_scalar(coeff * c().m_lar_solver.column_lower_bound(j).x);
}
if (c().m_nla_settings.horner_subs_fixed() == 2 && c().var_is_fixed_to_zero(j)) {
if (c().m_nla_settings.horner_subs_fixed == 2 && c().var_is_fixed_to_zero(j)) {
add_deps_of_fixed(j, dep);
return cn.mk_scalar(rational(0));
}
@ -89,10 +89,10 @@ nex * common::nexvar(const rational & coeff, lpvar j, nex_creator& cn, u_depende
mf *= coeff;
u_dependency * initial_dep = dep;
for (lpvar k : m.vars()) {
if (c().m_nla_settings.horner_subs_fixed() && c().var_is_fixed(k)) {
if (c().m_nla_settings.horner_subs_fixed == 1 && c().var_is_fixed(k)) {
add_deps_of_fixed(k, dep);
mf *= c().m_lar_solver.column_lower_bound(k).x;
} else if (c().m_nla_settings.horner_subs_fixed() == 2 &&
} else if (c().m_nla_settings.horner_subs_fixed == 2 &&
c().var_is_fixed_to_zero(k)) {
dep = initial_dep;
add_deps_of_fixed(k, dep);

File diff suppressed because it is too large Load diff

View file

@ -18,15 +18,18 @@
#include "math/lp/nla_basics_lemmas.h"
#include "math/lp/nla_order_lemmas.h"
#include "math/lp/nla_monotone_lemmas.h"
#include "math/lp/nla_grobner.h"
#include "math/lp/emonics.h"
#include "math/lp/nla_settings.h"
#include "math/lp/nex.h"
#include "math/lp/horner.h"
#include "math/lp/monomial_bounds.h"
#include "math/lp/nla_intervals.h"
#include "math/grobner/pdd_solver.h"
#include "nlsat/nlsat_solver.h"
namespace nra {
class solver;
}
namespace nla {
@ -139,6 +142,20 @@ struct pp_factorization {
};
class core {
friend struct common;
friend class new_lemma;
friend class grobner;
friend class order;
friend struct basics;
friend struct tangents;
friend class monotone;
friend struct nla_settings;
friend class intervals;
friend class horner;
friend class solver;
friend class monomial_bounds;
friend class nra::solver;
struct stats {
unsigned m_nla_explanations;
unsigned m_nla_lemmas;
@ -148,16 +165,18 @@ class core {
memset(this, 0, sizeof(*this));
}
};
stats m_stats;
friend class new_lemma;
unsigned m_nlsat_delay { 50 };
unsigned m_nlsat_fails { 0 };
stats m_stats;
unsigned m_nlsat_delay = 50;
unsigned m_nlsat_fails = 0;
bool should_run_bounded_nlsat();
lbool bounded_nlsat();
public:
var_eqs<emonics> m_evars;
lp::lar_solver& m_lar_solver;
reslimit& m_reslim;
vector<lemma> * m_lemma_vec;
lp::u_set m_to_refine;
tangents m_tangents;
@ -166,24 +185,21 @@ public:
monotone m_monotone;
intervals m_intervals;
monomial_bounds m_monomial_bounds;
nla_settings m_nla_settings;
horner m_horner;
nla_settings m_nla_settings;
dd::pdd_manager m_pdd_manager;
dd::solver m_pdd_grobner;
private:
grobner m_grobner;
emonics m_emons;
svector<lpvar> m_add_buffer;
mutable lp::u_set m_active_var_set;
lp::u_set m_rows;
reslimit m_nra_lim;
public:
reslimit& m_reslim;
bool m_use_nra_model;
bool m_use_nra_model = false;
nra::solver m_nra;
private:
bool m_cautious_patching;
lpvar m_patched_var;
monic const* m_patched_monic;
bool m_cautious_patching = true;
lpvar m_patched_var = 0;
monic const* m_patched_monic = nullptr;
void check_weighted(unsigned sz, std::pair<unsigned, std::function<void(void)>>* checks);
@ -205,6 +221,8 @@ public:
m_active_var_set.resize(m_lar_solver.number_of_vars());
}
unsigned get_var_weight(lpvar) const;
reslimit& reslim() { return m_reslim; }
emonics& emons() { return m_emons; }
const emonics& emons() const { return m_emons; }
@ -243,12 +261,15 @@ public:
// returns true if the combination of the Horner's schema and Grobner Basis should be called
bool need_run_horner() const {
return m_nla_settings.run_horner() && lp_settings().stats().m_nla_calls % m_nla_settings.horner_frequency() == 0;
return m_nla_settings.run_horner && lp_settings().stats().m_nla_calls % m_nla_settings.horner_frequency == 0;
}
bool need_run_grobner() const {
return m_nla_settings.run_grobner() && lp_settings().stats().m_nla_calls % m_nla_settings.grobner_frequency() == 0;
return m_nla_settings.run_grobner && lp_settings().stats().m_nla_calls % m_nla_settings.grobner_frequency == 0;
}
void set_active_vars_weights(nex_creator&);
std::unordered_set<lpvar> get_vars_of_expr_with_opening_terms(const nex* e);
void incremental_linearization(bool);
@ -450,31 +471,19 @@ public:
lpvar map_to_root(lpvar) const;
std::ostream& print_terms(std::ostream&) const;
std::ostream& print_term(const lp::lar_term&, std::ostream&) const;
template <typename T>
std::ostream& print_row(const T & row , std::ostream& out) const {
std::ostream& print_row(const T& row, std::ostream& out) const {
vector<std::pair<rational, lpvar>> v;
for (auto p : row) {
v.push_back(std::make_pair(p.coeff(), p.var()));
}
return lp::print_linear_combination_customized(v, [this](lpvar j) { return var_str(j); },
out);
return lp::print_linear_combination_customized(v, [this](lpvar j) { return var_str(j); }, out);
}
void run_grobner();
void find_nl_cluster();
void prepare_rows_and_active_vars();
void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q);
std::unordered_set<lpvar> get_vars_of_expr_with_opening_terms(const nex* e);
void display_matrix_of_m_rows(std::ostream & out) const;
void set_active_vars_weights(nex_creator&);
unsigned get_var_weight(lpvar) const;
void add_row_to_grobner(const vector<lp::row_cell<rational>> & row);
bool check_pdd_eq(const dd::solver::equation*);
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*&);
void set_level2var_for_grobner();
void configure_grobner();
bool influences_nl_var(lpvar) const;
bool is_nl_var(lpvar) const;
bool is_used_in_monic(lpvar) const;
void patch_monomials();
void patch_monomials_on_to_refine();

545
src/math/lp/nla_grobner.cpp Normal file
View file

@ -0,0 +1,545 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Module Name:
nla_grobner.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"
namespace nla {
grobner::grobner(core* c):
common(c),
m_pdd_manager(m_core.m_lar_solver.number_of_vars()),
m_solver(m_core.m_reslim, m_pdd_manager),
m_lar_solver(m_core.m_lar_solver)
{}
lp::lp_settings& grobner::lp_settings() {
return c().lp_settings();
}
void grobner::operator()() {
unsigned& quota = c().m_nla_settings.grobner_quota;
if (quota == 1)
return;
lp_settings().stats().m_grobner_calls++;
find_nl_cluster();
configure();
m_solver.saturate();
if (is_conflicting())
return;
if (propagate_bounds())
return;
if (propagate_eqs())
return;
if (propagate_factorization())
return;
if (quota > 1)
quota--;
IF_VERBOSE(2, verbose_stream() << "grobner miss, quota " << quota << "\n");
IF_VERBOSE(4, diagnose_pdd_miss(verbose_stream()));
#if 0
// diagnostics: did we miss something
vector<dd::pdd> eqs;
for (auto eq : m_solver.equations())
eqs.push_back(eq->poly());
c().m_nra.check(eqs);
#endif
}
bool grobner::is_conflicting() {
unsigned conflicts = 0;
for (auto eq : m_solver.equations())
if (is_conflicting(*eq) && ++conflicts >= m_solver.number_of_conflicts_to_report())
break;
if (conflicts > 0)
lp_settings().stats().m_grobner_conflicts++;
TRACE("grobner", m_solver.display(tout));
IF_VERBOSE(2, if (conflicts > 0) verbose_stream() << "grobner conflict\n");
return conflicts > 0;
}
bool grobner::propagate_bounds() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_bounds(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
bool grobner::propagate_eqs() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_fixed(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
bool grobner::propagate_factorization() {
unsigned changed = 0;
for (auto eq : m_solver.equations())
if (propagate_factorization(*eq) && ++changed >= m_solver.number_of_conflicts_to_report())
return true;
return changed > 0;
}
/**
\brief detect equalities
- k*x = 0, that is x = 0
- ax + b = 0
*/
typedef lp::lar_term term;
bool grobner::propagate_fixed(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
//IF_VERBOSE(0, verbose_stream() << p << "\n");
if (p.is_unary()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
ineq new_eq(v, llc::EQ, rational::zero());
if (c().ineq_holds(new_eq))
return false;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= new_eq;
return true;
}
if (p.is_offset()) {
unsigned v = p.var();
if (c().var_is_fixed(v))
return false;
rational a = p.hi().val();
rational b = -p.lo().val();
rational d = lcm(denominator(a), denominator(b));
a *= d;
b *= d;
ineq new_eq(term(a, v), llc::EQ, b);
if (c().ineq_holds(new_eq))
return false;
new_lemma lemma(c(), "pdd-eq");
add_dependencies(lemma, eq);
lemma |= new_eq;
return true;
}
return false;
}
/**
\brief detect simple factors
x*q = 0 => x = 0 or q = 0
*/
bool grobner::propagate_factorization(const dd::solver::equation& eq) {
dd::pdd const& p = eq.poly();
auto [vars, q] = p.var_factors();
if (vars.empty() || !q.is_linear())
return false;
// IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n");
term t;
while (!q.is_val()) {
t.add_monomial(q.hi().val(), q.var());
q = q.lo();
}
vector<ineq> ineqs;
for (auto v : vars)
ineqs.push_back(ineq(v, llc::EQ, rational::zero()));
ineqs.push_back(ineq(t, llc::EQ, -q.val()));
for (auto const& i : ineqs)
if (c().ineq_holds(i))
return false;
new_lemma lemma(c(), "pdd-factored");
add_dependencies(lemma, eq);
for (auto const& i : ineqs)
lemma |= i;
//lemma.display(verbose_stream());
return true;
}
void grobner::add_dependencies(new_lemma& lemma, const dd::solver::equation& eq) {
lp::explanation ex;
u_dependency_manager dm;
vector<unsigned, false> lv;
dm.linearize(eq.dep(), lv);
for (unsigned ci : lv)
ex.push_back(ci);
lemma &= ex;
}
void grobner::configure() {
m_solver.reset();
try {
set_level2var();
TRACE("grobner",
tout << "base vars: ";
for (lpvar j : c().active_var_set())
if (m_lar_solver.is_base(j))
tout << "j" << j << " ";
tout << "\n");
for (lpvar j : c().active_var_set()) {
if (m_lar_solver.is_base(j))
add_row(m_lar_solver.basic2row(j));
if (c().is_monic_var(j) && c().var_is_fixed(j))
add_fixed_monic(j);
}
}
catch (...) {
IF_VERBOSE(2, verbose_stream() << "pdd throw\n");
return;
}
TRACE("grobner", m_solver.display(tout));
#if 0
IF_VERBOSE(2, m_pdd_grobner.display(verbose_stream()));
dd::pdd_eval eval(m_pdd_manager);
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_pdd_grobner.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (p.is_linear() && !eval(p).is_zero()) {
IF_VERBOSE(0, verbose_stream() << "violated linear constraint " << p << "\n");
}
}
#endif
struct dd::solver::config cfg;
cfg.m_max_steps = m_solver.equations().size();
cfg.m_max_simplified = c().m_nla_settings.grobner_max_simplified;
cfg.m_eqs_growth = c().m_nla_settings.grobner_eqs_growth;
cfg.m_expr_size_growth = c().m_nla_settings.grobner_expr_size_growth;
cfg.m_expr_degree_growth = c().m_nla_settings.grobner_expr_degree_growth;
cfg.m_number_of_conflicts_to_report = c().m_nla_settings.grobner_number_of_conflicts_to_report;
m_solver.set(cfg);
m_solver.adjust_cfg();
m_pdd_manager.set_max_num_nodes(10000); // or something proportional to the number of initial nodes.
}
std::ostream& grobner::diagnose_pdd_miss(std::ostream& out) {
// m_pdd_grobner.display(out);
dd::pdd_eval eval;
eval.var2val() = [&](unsigned j){ return val(j); };
for (auto* e : m_solver.equations()) {
dd::pdd p = e->poly();
rational v = eval(p);
if (!v.is_zero()) {
out << p << " := " << v << "\n";
}
}
for (unsigned j = 0; j < m_lar_solver.number_of_vars(); ++j) {
if (m_lar_solver.column_has_lower_bound(j) || m_lar_solver.column_has_upper_bound(j)) {
out << j << ": [";
if (m_lar_solver.column_has_lower_bound(j)) out << m_lar_solver.get_lower_bound(j);
out << "..";
if (m_lar_solver.column_has_upper_bound(j)) out << m_lar_solver.get_upper_bound(j);
out << "]\n";
}
}
return out;
}
bool grobner::is_conflicting(const dd::solver::equation& e) {
auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
if (deps) c().m_intervals.set_var_interval<dd::w_dep::with_deps>(j, a);
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
if (!di.separated_from_zero(i)) {
TRACE("grobner", m_solver.display(tout << "not separated from 0 ", e) << "\n";
eval.get_interval_distributed<dd::w_dep::without_deps>(e.poly(), i);
tout << "separated from 0: " << di.separated_from_zero(i) << "\n";
for (auto j : e.poly().free_vars()) {
scoped_dep_interval a(di);
c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
c().m_intervals.display(tout << "j" << j << " ", a); tout << " ";
}
tout << "\n");
return false;
}
eval.get_interval<dd::w_dep::with_deps>(e.poly(), i_wd);
std::function<void (const lp::explanation&)> f = [this](const lp::explanation& e) {
new_lemma lemma(m_core, "pdd");
lemma &= e;
};
if (di.check_interval_for_conflict_on_zero(i_wd, e.dep(), f)) {
TRACE("grobner", m_solver.display(tout << "conflict ", e) << "\n");
return true;
}
else {
TRACE("grobner", m_solver.display(tout << "no conflict ", e) << "\n");
return false;
}
}
bool grobner::propagate_bounds(const dd::solver::equation& e) {
return false;
// TODO
auto& di = c().m_intervals.get_dep_intervals();
dd::pdd_interval eval(di);
eval.var2interval() = [this](lpvar j, bool deps, scoped_dep_interval& a) {
if (deps) c().m_intervals.set_var_interval<dd::w_dep::with_deps>(j, a);
else c().m_intervals.set_var_interval<dd::w_dep::without_deps>(j, a);
};
scoped_dep_interval i(di), i_wd(di);
eval.get_interval<dd::w_dep::without_deps>(e.poly(), i);
return false;
}
void grobner::add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar> & q) {
if (c().active_var_set_contains(j))
return;
c().insert_to_active_var_set(j);
if (c().is_monic_var(j)) {
const monic& m = c().emons()[j];
for (auto fcn : factorization_factory_imp(m, m_core))
for (const factor& fc: fcn)
q.push_back(var(fc));
}
if (c().var_is_fixed(j))
return;
const auto& matrix = m_lar_solver.A_r();
for (auto & s : matrix.m_columns[j]) {
unsigned row = s.var();
if (m_rows.contains(row))
continue;
m_rows.insert(row);
unsigned k = m_lar_solver.get_base_column_in_row(row);
if (m_lar_solver.column_is_free(k) && k != j)
continue;
CTRACE("grobner", matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit,
tout << "ignore the row " << row << " with the size " << matrix.m_rows[row].size() << "\n";);
if (matrix.m_rows[row].size() > c().m_nla_settings.grobner_row_length_limit)
continue;
for (auto& rc : matrix.m_rows[row])
add_var_and_its_factors_to_q_and_collect_new_rows(rc.var(), q);
}
}
const rational& grobner::val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep) {
unsigned lc, uc;
m_lar_solver.get_bound_constraint_witnesses_for_column(j, lc, uc);
dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(lc));
dep = c().m_intervals.mk_join(dep, c().m_intervals.mk_leaf(uc));
return m_lar_solver.column_lower_bound(j).x;
}
dd::pdd grobner::pdd_expr(const rational& coeff, lpvar j, u_dependency*& dep) {
dd::pdd r = m_pdd_manager.mk_val(coeff);
sbuffer<lpvar> vars;
vars.push_back(j);
u_dependency* zero_dep = dep;
while (!vars.empty()) {
j = vars.back();
vars.pop_back();
if (c().m_nla_settings.grobner_subs_fixed > 0 && c().var_is_fixed_to_zero(j)) {
r = m_pdd_manager.mk_val(val_of_fixed_var_with_deps(j, zero_dep));
dep = zero_dep;
return r;
}
if (c().m_nla_settings.grobner_subs_fixed == 1 && c().var_is_fixed(j))
r *= val_of_fixed_var_with_deps(j, dep);
else if (!c().is_monic_var(j))
r *= m_pdd_manager.mk_var(j);
else
for (lpvar k : c().emons()[j].vars())
vars.push_back(k);
}
return r;
}
/**
\brief convert p == 0 into a solved form v == r, such that
v has bounds [lo, oo) iff r has bounds [lo', oo)
v has bounds (oo,hi] iff r has bounds (oo,hi']
The solved form allows the Grobner solver identify more bounds conflicts.
A bad leading term can miss bounds conflicts.
For example for x + y + z == 0 where x, y : [0, oo) and z : (oo,0]
we prefer to solve z == -x - y instead of x == -z - y
because the solution -z - y has neither an upper, nor a lower bound.
*/
bool grobner::is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r) {
if (!p.is_linear())
return false;
r = p;
unsigned num_lo = 0, num_hi = 0;
unsigned lo = 0, hi = 0;
rational lc, hc, c;
while (!r.is_val()) {
SASSERT(r.hi().is_val());
v = r.var();
rational val = r.hi().val();
switch (m_lar_solver.get_column_type(v)) {
case lp::column_type::lower_bound:
if (val > 0) num_lo++, lo = v, lc = val; else num_hi++, hi = v, hc = val;
break;
case lp::column_type::upper_bound:
if (val < 0) num_lo++, lo = v, lc = val; else num_hi++, hi = v, hc = val;
break;
case lp::column_type::fixed:
case lp::column_type::boxed:
break;
default:
return false;
}
if (num_lo > 1 && num_hi > 1)
return false;
r = r.lo();
}
if (num_lo == 1 && num_hi > 1) {
v = lo;
c = lc;
}
else if (num_hi == 1 && num_lo > 1) {
v = hi;
c = hc;
}
else
return false;
r = c*m_pdd_manager.mk_var(v) - p;
if (c != 1)
r = r * (1/c);
return true;
}
/**
\brief add an equality to grobner solver, convert it to solved form if available.
*/
void grobner::add_eq(dd::pdd& p, u_dependency* dep) {
unsigned v;
dd::pdd q(m_pdd_manager);
m_solver.simplify(p, dep);
if (is_solved(p, v, q))
m_solver.add_subst(v, q, dep);
else
m_solver.add(p, dep);
}
void grobner::add_fixed_monic(unsigned j) {
u_dependency* dep = nullptr;
dd::pdd r = m_pdd_manager.mk_val(rational(1));
for (lpvar k : c().emons()[j].vars())
r *= pdd_expr(rational::one(), k, dep);
r -= val_of_fixed_var_with_deps(j, dep);
add_eq(r, dep);
}
void grobner::add_row(const vector<lp::row_cell<rational>> & row) {
u_dependency *dep = nullptr;
rational val;
dd::pdd sum = m_pdd_manager.mk_val(rational(0));
for (const auto &p : row)
sum += pdd_expr(p.coeff(), p.var(), dep);
TRACE("grobner", c().print_row(row, tout) << " " << sum << "\n");
add_eq(sum, dep);
}
void grobner::find_nl_cluster() {
prepare_rows_and_active_vars();
svector<lpvar> q;
TRACE("grobner", for (lpvar j : c().m_to_refine) print_monic(c().emons()[j], tout) << "\n";);
for (lpvar j : c().m_to_refine)
q.push_back(j);
while (!q.empty()) {
lpvar j = q.back();
q.pop_back();
add_var_and_its_factors_to_q_and_collect_new_rows(j, q);
}
TRACE("grobner", tout << "vars in cluster: ";
for (lpvar j : c().active_var_set()) tout << "j" << j << " "; tout << "\n";
display_matrix_of_m_rows(tout);
);
}
void grobner::prepare_rows_and_active_vars() {
m_rows.clear();
m_rows.resize(m_lar_solver.row_count());
c().clear_and_resize_active_var_set();
}
void grobner::display_matrix_of_m_rows(std::ostream & out) const {
const auto& matrix = m_lar_solver.A_r();
out << m_rows.size() << " rows" << "\n";
out << "the matrix\n";
for (const auto & r : matrix.m_rows)
c().print_row(r, out) << std::endl;
}
void grobner::set_level2var() {
unsigned n = m_lar_solver.column_count();
unsigned_vector sorted_vars(n), weighted_vars(n);
for (unsigned j = 0; j < n; j++) {
sorted_vars[j] = j;
weighted_vars[j] = c().get_var_weight(j);
}
#if 1
// potential update to weights
for (unsigned j = 0; j < n; j++) {
if (c().is_monic_var(j) && c().m_to_refine.contains(j)) {
for (lpvar k : c().m_emons[j].vars()) {
weighted_vars[k] += 6;
}
}
}
#endif
std::sort(sorted_vars.begin(), sorted_vars.end(), [&](unsigned a, unsigned b) {
unsigned wa = weighted_vars[a];
unsigned wb = weighted_vars[b];
return wa < wb || (wa == wb && a < b); });
unsigned_vector l2v(n);
for (unsigned j = 0; j < n; j++)
l2v[j] = sorted_vars[j];
m_pdd_manager.reset(l2v);
TRACE("grobner",
for (auto v : sorted_vars)
tout << "j" << v << " w:" << weighted_vars[v] << " ";
tout << "\n");
}
}

64
src/math/lp/nla_grobner.h Normal file
View file

@ -0,0 +1,64 @@
/*++
Copyright (c) 2017 Microsoft Corporation
Author:
Nikolaj Bjorner (nbjorner)
Lev Nachmanson (levnach)
--*/
#pragma once
#include "math/lp/nla_common.h"
#include "math/lp/nla_intervals.h"
#include "math/lp/nex.h"
#include "math/lp/cross_nested.h"
#include "math/lp/u_set.h"
#include "math/grobner/pdd_solver.h"
namespace nla {
class core;
class grobner : common {
dd::pdd_manager m_pdd_manager;
dd::solver m_solver;
lp::lar_solver& m_lar_solver;
lp::u_set m_rows;
lp::lp_settings& lp_settings();
// solving
bool is_conflicting();
bool is_conflicting(const dd::solver::equation& eq);
bool propagate_bounds();
bool propagate_bounds(const dd::solver::equation& eq);
bool propagate_eqs();
bool propagate_fixed(const dd::solver::equation& eq);
bool propagate_factorization();
bool propagate_factorization(const dd::solver::equation& eq);
void add_dependencies(new_lemma& lemma, const dd::solver::equation& eq);
// setup
void configure();
void set_level2var();
void find_nl_cluster();
void prepare_rows_and_active_vars();
void add_var_and_its_factors_to_q_and_collect_new_rows(lpvar j, svector<lpvar>& q);
void add_row(const vector<lp::row_cell<rational>>& row);
void add_fixed_monic(unsigned j);
bool is_solved(dd::pdd const& p, unsigned& v, dd::pdd& r);
void add_eq(dd::pdd& p, u_dependency* dep);
const rational& val_of_fixed_var_with_deps(lpvar j, u_dependency*& dep);
dd::pdd pdd_expr(const rational& c, lpvar j, u_dependency*& dep);
void display_matrix_of_m_rows(std::ostream& out) const;
std::ostream& diagnose_pdd_miss(std::ostream& out);
public:
grobner(core *core);
void operator()();
};
}

View file

@ -19,7 +19,7 @@ typedef lp::lar_term term;
// a > b && c > 0 => ac > bc
void order::order_lemma() {
TRACE("nla_solver", );
if (!c().m_nla_settings.run_order()) {
if (!c().m_nla_settings.run_order) {
TRACE("nla_solver", tout << "not generating order lemmas\n";);
return;
}

View file

@ -9,94 +9,38 @@ Author:
#pragma once
namespace nla {
class nla_settings {
bool m_run_order;
bool m_run_tangents;
bool m_run_horner;
// how often to call the horner heuristic
unsigned m_horner_frequency;
unsigned m_horner_row_length_limit;
unsigned m_horner_subs_fixed;
// grobner fields
bool m_run_grobner;
unsigned m_grobner_row_length_limit;
unsigned m_grobner_subs_fixed;
unsigned m_grobner_eqs_growth;
unsigned m_grobner_tree_size_growth;
unsigned m_grobner_expr_size_growth;
unsigned m_grobner_expr_degree_growth;
unsigned m_grobner_max_simplified;
unsigned m_grobner_number_of_conflicts_to_report;
unsigned m_grobner_quota;
unsigned m_grobner_frequency;
bool m_run_nra;
// expensive patching
bool m_expensive_patching;
public:
nla_settings() : m_run_order(true),
m_run_tangents(true),
m_run_horner(true),
m_horner_frequency(4),
m_horner_row_length_limit(10),
m_horner_subs_fixed(2),
m_run_grobner(true),
m_grobner_row_length_limit(50),
m_grobner_subs_fixed(false),
m_grobner_quota(0),
m_grobner_frequency(4),
m_run_nra(false),
m_expensive_patching(false)
{}
unsigned grobner_eqs_growth() const { return m_grobner_eqs_growth;}
unsigned& grobner_eqs_growth() { return m_grobner_eqs_growth;}
bool run_order() const { return m_run_order; }
bool& run_order() { return m_run_order; }
struct nla_settings {
bool run_order = true;
bool run_tangents = true;
// horner fields
bool run_horner = true;
unsigned horner_frequency = 4;
unsigned horner_row_length_limit = 10;
unsigned horner_subs_fixed = 2;
bool run_tangents() const { return m_run_tangents; }
bool& run_tangents() { return m_run_tangents; }
// grobner fields
bool run_grobner = true;
unsigned grobner_row_length_limit = 50;
unsigned grobner_subs_fixed = 1;
unsigned grobner_eqs_growth = 10;
unsigned grobner_tree_size_growth = 2;
unsigned grobner_expr_size_growth = 2;
unsigned grobner_expr_degree_growth = 2;
unsigned grobner_max_simplified = 10000;
unsigned grobner_number_of_conflicts_to_report = 1;
unsigned grobner_quota = 0;
unsigned grobner_frequency = 4;
bool expensive_patching() const { return m_expensive_patching; }
bool& expensive_patching() { return m_expensive_patching; }
bool run_horner() const { return m_run_horner; }
bool& run_horner() { return m_run_horner; }
unsigned horner_frequency() const { return m_horner_frequency; }
unsigned& horner_frequency() { return m_horner_frequency; }
unsigned horner_row_length_limit() const { return m_horner_row_length_limit; }
unsigned& horner_row_length_limit() { return m_horner_row_length_limit; }
unsigned horner_subs_fixed() const { return m_horner_subs_fixed; }
unsigned& horner_subs_fixed() { return m_horner_subs_fixed; }
bool run_grobner() const { return m_run_grobner; }
bool& run_grobner() { return m_run_grobner; }
unsigned grobner_frequency() const { return m_grobner_frequency; }
unsigned& grobner_frequency() { return m_grobner_frequency; }
bool run_nra() const { return m_run_nra; }
bool& run_nra() { return m_run_nra; }
unsigned grobner_row_length_limit() const { return m_grobner_row_length_limit; }
unsigned& grobner_row_length_limit() { return m_grobner_row_length_limit; }
unsigned grobner_subs_fixed() const { return m_grobner_subs_fixed; }
unsigned& grobner_subs_fixed() { return m_grobner_subs_fixed; }
unsigned grobner_tree_size_growth() const { return m_grobner_tree_size_growth; }
unsigned & grobner_tree_size_growth() { return m_grobner_tree_size_growth; }
unsigned grobner_expr_size_growth() const { return m_grobner_expr_size_growth; }
unsigned & grobner_expr_size_growth() { return m_grobner_expr_size_growth; }
unsigned grobner_expr_degree_growth() const { return m_grobner_expr_degree_growth; }
unsigned & grobner_expr_degree_growth() { return m_grobner_expr_degree_growth; }
unsigned grobner_max_simplified() const { return m_grobner_max_simplified; }
unsigned & grobner_max_simplified() { return m_grobner_max_simplified; }
unsigned grobner_number_of_conflicts_to_report() const { return m_grobner_number_of_conflicts_to_report; }
unsigned & grobner_number_of_conflicts_to_report() { return m_grobner_number_of_conflicts_to_report; }
unsigned& grobner_quota() { return m_grobner_quota; }
// nra fields
bool run_nra = false;
};
// expensive patching
bool expensive_patching = false;
nla_settings() {}
};
}

View file

@ -186,7 +186,7 @@ tangents::tangents(core * c) : common(c) {}
void tangents::tangent_lemma() {
factorization bf(nullptr);
const monic* m = nullptr;
if (c().m_nla_settings.run_tangents() && c().find_bfc_to_refine(m, bf)) {
if (c().m_nla_settings.run_tangents && c().find_bfc_to_refine(m, bf)) {
lpvar j = m->var();
tangent_imp tangent(point(val(bf[0]), val(bf[1])), c().val(j), *m, bf, *this);
tangent();

View file

@ -65,12 +65,10 @@ struct solver::imp {
}
// add polynomial definitions.
for (auto const& m : m_nla_core.emons()) {
for (auto const& m : m_nla_core.emons())
add_monic_eq(m);
}
for (unsigned i : m_term_set) {
for (unsigned i : m_term_set)
add_term(i);
}
// TBD: add variable bounds?
lbool r = l_undef;
@ -176,7 +174,103 @@ struct solver::imp {
lp_assert(false); // unreachable
}
m_nlsat->mk_clause(1, &lit, a);
}
}
lbool check(vector<dd::pdd> const& eqs) {
m_zero = nullptr;
m_nlsat = alloc(nlsat::solver, m_limit, m_params, false);
m_zero = alloc(scoped_anum, am());
m_lp2nl.reset();
m_term_set.clear();
for (auto const& eq : eqs)
add_eq(eq);
for (auto const& [v, w] : m_lp2nl) {
auto& ls = m_nla_core.m_lar_solver;
if (ls.column_has_lower_bound(v))
add_lb(ls.get_lower_bound(v), w);
if (ls.column_has_upper_bound(v))
add_ub(ls.get_upper_bound(v), w);
}
lbool r = l_undef;
try {
r = m_nlsat->check();
}
catch (z3_exception&) {
if (m_limit.is_canceled()) {
r = l_undef;
}
else {
throw;
}
}
IF_VERBOSE(0, verbose_stream() << "check-nra " << r << "\n";
m_nlsat->display(verbose_stream());
for (auto const& [v, w] : m_lp2nl) {
auto& ls = m_nla_core.m_lar_solver;
if (ls.column_has_lower_bound(v))
verbose_stream() << w << " >= " << ls.get_lower_bound(v) << "\n";
if (ls.column_has_upper_bound(v))
verbose_stream() << w << " <= " << ls.get_upper_bound(v) << "\n";
});
return r;
}
void add_eq(dd::pdd const& eq) {
dd::pdd normeq = eq;
rational lc(1);
for (auto const& [c, m] : eq)
lc = lcm(denominator(c), lc);
if (lc != 1)
normeq *= lc;
polynomial::manager& pm = m_nlsat->pm();
polynomial::polynomial_ref p(pdd2polynomial(normeq), pm);
bool is_even[1] = { false };
polynomial::polynomial* ps[1] = { p };
nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even);
m_nlsat->mk_clause(1, &lit, nullptr);
}
void add_lb(lp::impq const& b, unsigned w) {
add_bound(b.x, w, b.y <= 0, b.y > 0 ? nlsat::atom::kind::GT : nlsat::atom::kind::LT);
}
void add_ub(lp::impq const& b, unsigned w) {
add_bound(b.x, w, b.y >= 0, b.y < 0 ? nlsat::atom::kind::LT : nlsat::atom::kind::GT);
}
// w - bound < 0
// w - bound > 0
void add_bound(lp::mpq const& bound, unsigned w, bool neg, nlsat::atom::kind k) {
polynomial::manager& pm = m_nlsat->pm();
polynomial::polynomial_ref p1(pm.mk_polynomial(w), pm);
polynomial::polynomial_ref p2(pm.mk_const(bound), pm);
polynomial::polynomial_ref p(pm.sub(p1, p2), pm);
polynomial::polynomial* ps[1] = { p };
bool is_even[1] = { false };
nlsat::literal lit = m_nlsat->mk_ineq_literal(k, 1, ps, is_even);
if (neg)
lit.neg();
m_nlsat->mk_clause(1, &lit, nullptr);
}
polynomial::polynomial* pdd2polynomial(dd::pdd const& p) {
polynomial::manager& pm = m_nlsat->pm();
if (p.is_val())
return pm.mk_const(p.val());
polynomial::polynomial_ref lo(pdd2polynomial(p.lo()), pm);
polynomial::polynomial_ref hi(pdd2polynomial(p.hi()), pm);
unsigned w, v = p.var();
if (!m_lp2nl.find(v, w)) {
w = m_nlsat->mk_var(false);
m_lp2nl.insert(v, w);
}
polynomial::polynomial_ref vp(pm.mk_polynomial(w, 1), pm);
return pm.add(lo, pm.mul(vp, hi));
}
bool is_int(lp::var_index v) {
return s.var_is_int(v);
@ -265,6 +359,10 @@ lbool solver::check() {
return m_imp->check();
}
lbool solver::check(vector<dd::pdd> const& eqs) {
return m_imp->check(eqs);
}
bool solver::need_check() {
return m_imp->need_check();
}

View file

@ -9,6 +9,7 @@
#include "util/rlimit.h"
#include "util/params.h"
#include "nlsat/nlsat_solver.h"
#include "math/dd/dd_pdd.h"
namespace lp {
class lar_solver;
@ -36,6 +37,11 @@ namespace nra {
*/
lbool check();
/**
\breif Check feasibility of equalities modulo bounds constraints on their variables.
*/
lbool check(vector<dd::pdd> const& eqs);
/*
\brief determine whether nra check is needed.
*/

View file

@ -137,9 +137,6 @@ namespace opt {
m_model_fixed(),
m_objective_refs(m),
m_core(m),
m_enable_sat(false),
m_is_clausal(false),
m_pp_neat(false),
m_unknown("unknown")
{
params_ref p;
@ -196,6 +193,8 @@ namespace opt {
void context::add_hard_constraint(expr* f) {
if (m_calling_on_model) {
if (!m_incremental)
throw default_exception("Set opt.incremental = true to allow adding constraints during search");
get_solver().assert_expr(f);
for (auto const& [k, v] : m_maxsmts)
v->reset_upper();
@ -838,19 +837,14 @@ namespace opt {
}
goal_ref g(alloc(goal, m, true, !asms.empty()));
for (expr* fml : fmls) {
for (expr* fml : fmls)
g->assert_expr(fml);
}
for (expr * a : asms) {
for (expr * a : asms)
g->assert_expr(a, a);
}
tactic_ref tac0 =
and_then(mk_simplify_tactic(m, m_params),
mk_propagate_values_tactic(m),
mk_solve_eqs_tactic(m),
// NB: cannot ackermannize because max/min objectives would disappear
// mk_ackermannize_bv_tactic(m, m_params),
// NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints
m_incremental ? mk_skip_tactic() : mk_solve_eqs_tactic(m),
mk_simplify_tactic(m));
opt_params optp(m_params);
tactic_ref tac1, tac2, tac3, tac4;
@ -861,7 +855,7 @@ namespace opt {
m.linearize(core, deps);
has_dep |= !deps.empty();
}
if (optp.elim_01() && m_logic.is_null() && !has_dep) {
if (optp.elim_01() && m_logic.is_null() && !has_dep && !m_incremental) {
tac1 = mk_dt2bv_tactic(m);
tac2 = mk_lia2card_tactic(m);
tac3 = mk_eq2bv_tactic(m);
@ -1568,6 +1562,7 @@ namespace opt {
m_maxsat_engine = _p.maxsat_engine();
m_pp_neat = _p.pp_neat();
m_pp_wcnf = _p.pp_wcnf();
m_incremental = _p.incremental();
}
std::string context::to_string() {

View file

@ -194,11 +194,12 @@ namespace opt {
func_decl_ref_vector m_objective_refs;
expr_ref_vector m_core;
tactic_ref m_simplify;
bool m_enable_sat { true } ;
bool m_enable_sls { false };
bool m_is_clausal { false };
bool m_pp_neat { true };
bool m_pp_wcnf { false };
bool m_enable_sat = true;
bool m_enable_sls = false;
bool m_is_clausal = false;
bool m_pp_neat = false;
bool m_pp_wcnf = false;
bool m_incremental = false;
symbol m_maxsat_engine;
symbol m_logic;
svector<symbol> m_labels;

View file

@ -15,6 +15,7 @@ def_module_params('opt',
('enable_core_rotate', BOOL, False, 'enable core rotation to both sample cores and correction sets'),
('enable_sat', BOOL, True, 'enable the new SAT core for propositional constraints'),
('elim_01', BOOL, True, 'eliminate 01 variables'),
('incremental', BOOL, False, 'set incremental mode. It disables pre-processing and enables adding constraints in model event handler'),
('pp.neat', BOOL, True, 'use neat (as opposed to less readable, but faster) pretty printer when displaying context'),
('pb.compile_equality', BOOL, False, 'compile arithmetical equalities into pseudo-Boolean equality (instead of two inequalites)'),
('pp.wcnf', BOOL, False, 'print maxsat benchmark into wcnf format'),

View file

@ -536,6 +536,7 @@ namespace qe {
th_rewriter rewrite(m);
rewrite(a);
rewrite(b);
TRACE("interpolator", tout << a << " " << b << "\n");
solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null);
solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null);
solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null);

View file

@ -1787,6 +1787,7 @@ namespace sat {
clause& c = it.curr();
if (!c.is_learned() && !c.was_removed()) {
r.push_back(clause_wrapper(c));
SASSERT(r.back().contains(l));
SASSERT(r.back().size() == c.size());
}
}
@ -1808,9 +1809,13 @@ namespace sat {
Return false if the result is a tautology
*/
bool simplifier::resolve(clause_wrapper const & c1, clause_wrapper const & c2, literal l, literal_vector & r) {
CTRACE("resolve_bug", !c1.contains(l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";);
CTRACE("resolve_bug", !c1.contains(l) || !c2.contains(~l), tout << c1 << "\n" << c2 << "\nl: " << l << "\n";);
if (m_visited.size() <= 2*s.num_vars())
m_visited.resize(2*s.num_vars(), false);
if (c1.was_removed())
return false;
if (c2.was_removed())
return false;
SASSERT(c1.contains(l));
SASSERT(c2.contains(~l));
bool res = true;
@ -1973,7 +1978,14 @@ namespace sat {
}
}
}
TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";);
TRACE("sat_simplifier", tout << "eliminate " << v << ", before: " << before_clauses << " after: " << after_clauses << "\n";
tout << "pos\n";
for (auto & c : m_pos_cls)
tout << c << "\n";
tout << "neg\n";
for (auto & c : m_neg_cls)
tout << c << "\n";
);
m_elim_counter -= num_pos * num_neg + before_lits;
m_elim_counter -= num_pos * num_neg + before_lits;
@ -1988,6 +2000,8 @@ namespace sat {
m_elim_counter -= num_pos * num_neg + before_lits;
for (auto & c1 : m_pos_cls) {
if (c1.was_removed())
continue;
for (auto & c2 : m_neg_cls) {
m_new_cls.reset();
if (!resolve(c1, c2, pos_l, m_new_cls))

View file

@ -69,23 +69,23 @@ namespace arith {
m_nla->push();
}
smt_params_helper prms(s().params());
m_nla->settings().run_order() = prms.arith_nl_order();
m_nla->settings().run_tangents() = prms.arith_nl_tangents();
m_nla->settings().run_horner() = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed() = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency() = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit() = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner() = prms.arith_nl_grobner();
m_nla->settings().run_nra() = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed() = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth() = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth() = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth() = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified() = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota() = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching() = false;
m_nla->settings().run_order = prms.arith_nl_order();
m_nla->settings().run_tangents = prms.arith_nl_tangents();
m_nla->settings().run_horner = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner = prms.arith_nl_grobner();
m_nla->settings().run_nra = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching = false;
}
}

View file

@ -442,6 +442,7 @@ namespace bv {
SASSERT(bv.is_int2bv(n));
euf::enode* e = expr2enode(n);
mk_bits(e->get_th_var(get_id()));
get_var(e->get_arg(0));
assert_int2bv_axiom(n);
}

View file

@ -209,6 +209,22 @@ namespace bv {
if (is_bv(eq.v1())) {
m_find.merge(eq.v1(), eq.v2());
VERIFY(eq.is_eq());
return;
}
euf::enode* n1 = var2enode(eq.v1());
for (euf::enode* bv2int : euf::enode_class(n1)) {
if (!bv.is_bv2int(bv2int->get_expr()))
continue;
euf::enode* bv2int_arg = bv2int->get_arg(0);
for (euf::enode* p : euf::enode_parents(n1->get_root())) {
if (bv.is_int2bv(p->get_expr()) && p->get_sort() == bv2int_arg->get_sort() && p->get_root() != bv2int_arg->get_root()) {
euf::enode_pair_vector eqs;
eqs.push_back({ n1, p->get_arg(0) });
eqs.push_back({ n1, bv2int });
ctx.propagate(p, bv2int_arg, euf::th_explain::propagate(*this, eqs, p, bv2int_arg));
break;
}
}
}
}

View file

@ -506,7 +506,7 @@ namespace dt {
return m_nodes;
}
ptr_vector<euf::enode> const& solver::get_seq_args(enode* n) {
ptr_vector<euf::enode> const& solver::get_seq_args(enode* n, enode*& sibling) {
m_nodes.reset();
m_todo.reset();
auto add_todo = [&](enode* n) {
@ -515,9 +515,15 @@ namespace dt {
m_todo.push_back(n);
}
};
for (enode* sib : euf::enode_class(n))
add_todo(sib);
for (enode* sib : euf::enode_class(n)) {
if (m_sutil.str.is_concat_of_units(sib->get_expr())) {
add_todo(sib);
sibling = sib;
break;
}
}
for (unsigned i = 0; i < m_todo.size(); ++i) {
enode* n = m_todo[i];
@ -551,10 +557,10 @@ namespace dt {
// collect equalities on all children that may have been used.
bool found = false;
auto add = [&](enode* arg) {
if (arg->get_root() == child->get_root()) {
if (arg != child)
m_used_eqs.push_back(enode_pair(arg, child));
auto add = [&](enode* seq_arg) {
if (seq_arg->get_root() == child->get_root()) {
if (seq_arg != child)
m_used_eqs.push_back(enode_pair(seq_arg, child));
found = true;
}
};
@ -564,11 +570,14 @@ namespace dt {
if (m_autil.is_array(s) && dt.is_datatype(get_array_range(s)))
for (enode* aarg : get_array_args(arg))
add(aarg);
}
sort* se;
if (m_sutil.is_seq(child->get_sort(), se) && dt.is_datatype(se)) {
for (enode* aarg : get_seq_args(child))
add(aarg);
sort* se;
if (m_sutil.is_seq(arg->get_sort(), se) && dt.is_datatype(se)) {
enode* sibling = nullptr;
for (enode* seq_arg : get_seq_args(arg, sibling))
add(seq_arg);
if (sibling && sibling != arg)
m_used_eqs.push_back(enode_pair(arg, sibling));
}
}
VERIFY(found);
@ -636,12 +645,13 @@ namespace dt {
// explore `arg` (with parent)
expr* earg = arg->get_expr();
sort* s = earg->get_sort(), *se;
enode* sibling;
if (dt.is_datatype(s)) {
m_parent.insert(arg->get_root(), parent);
oc_push_stack(arg);
}
else if (m_sutil.is_seq(s, se) && dt.is_datatype(se)) {
for (enode* sarg : get_seq_args(arg))
for (enode* sarg : get_seq_args(arg, sibling))
if (process_arg(sarg))
return true;
}

View file

@ -112,7 +112,7 @@ namespace dt {
void oc_push_stack(enode * n);
ptr_vector<enode> m_nodes, m_todo;
ptr_vector<enode> const& get_array_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling);
void pop_core(unsigned n) override;

View file

@ -1795,9 +1795,9 @@ namespace pb {
}
if (c.lit() != sat::null_literal && value(c.lit()) != l_true) return true;
SASSERT(c.lit() == sat::null_literal || lvl(c.lit()) == 0 || (c.is_watched(*this, c.lit()) && c.is_watched(*this, ~c.lit())));
if (eval(c) == l_true) {
if (eval(c) == l_true)
return true;
}
literal_vector lits(c.literals());
for (literal l : lits) {
if (lvl(l) == 0) continue;
@ -1823,6 +1823,8 @@ namespace pb {
}
bool solver::validate_watch(pbc const& p, literal alit) const {
if (value(p.lit()) != l_true)
return true;
for (unsigned i = 0; i < p.size(); ++i) {
literal l = p[i].second;
if (l != alit && lvl(l) != 0 && p.is_watched(*this, l) != (i < p.num_watch())) {
@ -1833,9 +1835,8 @@ namespace pb {
}
}
unsigned slack = 0;
for (unsigned i = 0; i < p.num_watch(); ++i) {
slack += p[i].first;
}
for (unsigned i = 0; i < p.num_watch(); ++i)
slack += p[i].first;
if (slack != p.slack()) {
IF_VERBOSE(0, display(verbose_stream(), p, true););
UNREACHABLE();

View file

@ -270,6 +270,10 @@ namespace euf {
return mk(th, lits.size(), lits.data(), eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma) {
return mk(th, 0, nullptr, eqs.size(), eqs.data(), sat::null_literal, x, y, pma);
}
th_explain* th_explain::propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y) {
return mk(th, 1, &lit, 0, nullptr, sat::null_literal, x, y);
}

View file

@ -241,6 +241,7 @@ namespace euf {
static th_explain* conflict(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* conflict(th_euf_solver& th, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, sat::literal lit, euf::enode* x, euf::enode* y);
static th_explain* propagate(th_euf_solver& th, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, sat::literal consequent, sat::proof_hint const* pma = nullptr);
static th_explain* propagate(th_euf_solver& th, sat::literal_vector const& lits, enode_pair_vector const& eqs, euf::enode* x, euf::enode* y, sat::proof_hint const* pma = nullptr);

View file

@ -72,7 +72,7 @@ def_module_params(module_name='smt',
('arith.nl.grobner_max_simplified', UINT, 10000, 'grobner\'s maximum number of simplifications'),
('arith.nl.grobner_cnfl_to_report', UINT, 1, 'grobner\'s maximum number of conflicts to report'),
('arith.nl.gr_q', UINT, 10, 'grobner\'s quota'),
('arith.nl.grobner_subs_fixed', UINT, 2, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.grobner_subs_fixed', UINT, 1, '0 - no subs, 1 - substitute, 2 - substitute fixed zeros only'),
('arith.nl.delay', UINT, 500, 'number of calls to final check before invoking bounded nlsat check'),
('arith.propagate_eqs', BOOL, True, 'propagate (cheap) equalities'),
('arith.propagation_mode', UINT, 1, '0 - no propagation, 1 - propagate existing literals, 2 - refine finite bounds'),

View file

@ -1429,7 +1429,10 @@ namespace smt {
inc_ref(l2);
m_watches[(~l1).index()].insert_literal(l2);
m_watches[(~l2).index()].insert_literal(l1);
if (get_assignment(l2) == l_false) {
if (get_assignment(l1) == l_false) {
assign(l2, b_justification(~l1));
}
else if (get_assignment(l2) == l_false) {
assign(l1, b_justification(~l2));
}
m_clause_proof.add(l1, l2, k, j);

View file

@ -258,7 +258,7 @@ namespace smt {
bindings.set(num_decls - i - 1, sk_value);
}
TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\n" << defs << "\n";);
TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\ndefs:\n" << defs << "\n";);
if (!defs.empty()) def = mk_and(defs);
max_generation = std::max(m_qm->get_generation(q), max_generation);
add_instance(q, bindings, max_generation, def.get());
@ -453,7 +453,7 @@ namespace smt {
TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";);
m_max_cexs += m_params.m_mbqi_max_cexs;
if (num_failures == 0 && (!m_context->validate_model())) {
if (num_failures == 0 && !m_context->validate_model()) {
num_failures = 1;
// this time force expanding recursive function definitions
// that are not forced true in the current model.

View file

@ -348,6 +348,7 @@ public:
m_eq_eh = nullptr;
m_diseq_eh = nullptr;
m_created_eh = nullptr;
m_decide_eh = nullptr;
}
void user_propagate_init(
@ -385,6 +386,10 @@ public:
void user_propagate_register_created(user_propagator::created_eh_t& created_eh) override {
m_created_eh = created_eh;
}
void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override {
m_decide_eh = decide_eh;
}
};
static tactic * mk_seq_smt_tactic(ast_manager& m, params_ref const & p) {

View file

@ -492,6 +492,7 @@ namespace smt {
void theory_arith<Ext>::mk_axiom(expr * ante, expr * conseq, bool simplify_conseq) {
th_rewriter & s = ctx.get_rewriter();
expr_ref s_ante(m), s_conseq(m);
expr_ref p_ante(ante, m), p_conseq(conseq, m); // pinned versions
expr* s_conseq_n, * s_ante_n;
bool negated;
@ -562,7 +563,7 @@ namespace smt {
if (!m_util.is_zero(divisor)) {
// if divisor is zero, then idiv and mod are uninterpreted functions.
expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m);
expr_ref eqz(m), eq(m), lower(m), upper(m);
expr_ref eqz(m), eq(m), lower(m), upper(m), qr(m);
div = m_util.mk_idiv(dividend, divisor);
mod = m_util.mk_mod(dividend, divisor);
zero = m_util.mk_int(0);
@ -570,7 +571,8 @@ namespace smt {
abs_divisor = m_util.mk_sub(m.mk_ite(m_util.mk_lt(divisor, zero), m_util.mk_sub(zero, divisor), divisor), one);
s(abs_divisor);
eqz = m.mk_eq(divisor, zero);
eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend);
qr = m_util.mk_add(m_util.mk_mul(divisor, div), mod);
eq = m.mk_eq(qr, dividend);
lower = m_util.mk_ge(mod, zero);
upper = m_util.mk_le(mod, abs_divisor);
TRACE("div_axiom_bug",
@ -584,6 +586,8 @@ namespace smt {
mk_axiom(eqz, upper, !m_util.is_numeral(abs_divisor));
rational k;
m_arith_eq_adapter.mk_axioms(ensure_enode(qr), ensure_enode(mod));
if (m_util.is_zero(dividend)) {
mk_axiom(eqz, m.mk_eq(div, zero));
mk_axiom(eqz, m.mk_eq(mod, zero));
@ -591,7 +595,7 @@ namespace smt {
// (or (= y 0) (<= (* y (div x y)) x))
else if (!m_util.is_numeral(divisor)) {
expr_ref div_ge(m), div_le(m), ge(m), le(m);
expr_ref div_ge(m);
div_ge = m_util.mk_ge(m_util.mk_sub(dividend, m_util.mk_mul(divisor, div)), zero);
s(div_ge);
mk_axiom(eqz, div_ge, false);

View file

@ -80,14 +80,12 @@ void theory_arith<Ext>::mark_dependents(theory_var v, svector<theory_var> & vars
if (is_fixed(v))
return;
column & c = m_columns[v];
typename svector<col_entry>::iterator it = c.begin_entries();
typename svector<col_entry>::iterator end = c.end_entries();
for (; it != end; ++it) {
if (it->is_dead() || already_visited_rows.contains(it->m_row_id))
for (auto& ce : c) {
if (ce.is_dead() || already_visited_rows.contains(ce.m_row_id))
continue;
TRACE("non_linear_bug", tout << "visiting row: " << it->m_row_id << "\n";);
already_visited_rows.insert(it->m_row_id);
row & r = m_rows[it->m_row_id];
TRACE("non_linear_bug", tout << "visiting row: " << ce.m_row_id << "\n";);
already_visited_rows.insert(ce.m_row_id);
row & r = m_rows[ce.m_row_id];
theory_var s = r.get_base_var();
// ignore quasi base vars... actually they should not be used if the problem is non linear...
if (is_quasi_base(s))
@ -97,14 +95,10 @@ void theory_arith<Ext>::mark_dependents(theory_var v, svector<theory_var> & vars
// was eliminated by substitution.
if (s != null_theory_var && is_free(s) && s != v)
continue;
typename vector<row_entry>::const_iterator it2 = r.begin_entries();
typename vector<row_entry>::const_iterator end2 = r.end_entries();
for (; it2 != end2; ++it2) {
if (!it2->is_dead() && !is_fixed(it2->m_var))
mark_var(it2->m_var, vars, already_found);
if (!it2->is_dead() && is_fixed(it2->m_var)) {
TRACE("non_linear", tout << "skipped fixed\n";);
}
for (auto& re : r) {
if (!re.is_dead() && !is_fixed(re.m_var))
mark_var(re.m_var, vars, already_found);
CTRACE("non_linear", !re.is_dead() && is_fixed(re.m_var), tout << "skipped fixed\n");
}
}
}
@ -119,6 +113,7 @@ void theory_arith<Ext>::get_non_linear_cluster(svector<theory_var> & vars) {
return;
var_set already_found;
row_set already_visited_rows;
for (theory_var v : m_nl_monomials) {
expr * n = var2expr(v);
if (ctx.is_relevant(n))
@ -130,9 +125,9 @@ void theory_arith<Ext>::get_non_linear_cluster(svector<theory_var> & vars) {
TRACE("non_linear", tout << "marking dependents of: v" << v << "\n";);
mark_dependents(v, vars, already_found, already_visited_rows);
}
TRACE("non_linear", tout << "variables in non linear cluster:\n";
for (theory_var v : vars) tout << "v" << v << " ";
tout << "\n";);
TRACE("non_linear", tout << "variables in non linear cluster: ";
for (theory_var v : vars) tout << "v" << v << " "; tout << "\n";
for (theory_var v : m_nl_monomials) tout << "non-linear v" << v << " " << mk_pp(var2expr(v), m) << "\n";);
}
@ -1740,22 +1735,21 @@ grobner::monomial * theory_arith<Ext>::mk_gb_monomial(rational const & _coeff, e
*/
template<typename Ext>
void theory_arith<Ext>::add_row_to_gb(row const & r, grobner & gb) {
TRACE("non_linear", tout << "adding row to gb\n"; display_row(tout, r););
TRACE("grobner", tout << "adding row to gb\n"; display_row(tout, r););
ptr_buffer<grobner::monomial> monomials;
v_dependency * dep = nullptr;
m_tmp_var_set.reset();
typename vector<row_entry>::const_iterator it = r.begin_entries();
typename vector<row_entry>::const_iterator end = r.end_entries();
for (; it != end; ++it) {
if (!it->is_dead()) {
rational coeff = it->m_coeff.to_rational();
expr * m = var2expr(it->m_var);
TRACE("non_linear", tout << "monomial: " << mk_pp(m, get_manager()) << "\n";);
grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set);
TRACE("non_linear", tout << "new monomial:\n"; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";);
if (new_m)
monomials.push_back(new_m);
}
for (auto& re : r) {
if (re.is_dead())
continue;
rational coeff = re.m_coeff.to_rational();
expr * m = var2expr(re.m_var);
grobner::monomial * new_m = mk_gb_monomial(coeff, m, gb, dep, m_tmp_var_set);
if (new_m)
monomials.push_back(new_m);
TRACE("grobner",
tout << "monomial: " << mk_pp(m, get_manager()) << "\n";
tout << "new monomial: "; if (new_m) gb.display_monomial(tout, *new_m); else tout << "null"; tout << "\n";);
}
gb.assert_eq_0(monomials.size(), monomials.data(), dep);
}
@ -2158,8 +2152,9 @@ bool theory_arith<Ext>::get_gb_eqs_and_look_for_conflict(ptr_vector<grobner::equ
eqs.reset();
gb.get_equations(eqs);
TRACE("grobner", tout << "after gb\n";
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { out << "v" << expr2var(v); };
for (grobner::equation* eq : eqs)
gb.display_equation(tout, *eq);
gb.display_equation(tout, *eq, _fn);
);
for (grobner::equation* eq : eqs) {
if (is_inconsistent(eq, gb) || is_inconsistent2(eq, gb)) {
@ -2259,7 +2254,9 @@ typename theory_arith<Ext>::gb_result theory_arith<Ext>::compute_grobner(svector
ptr_vector<grobner::equation> eqs;
do {
TRACE("non_linear_gb", tout << "before:\n"; gb.display(tout););
TRACE("grobner", tout << "before grobner:\n";
std::function<void(std::ostream& out, expr* v)> _fn = [&](std::ostream& out, expr* v) { out << "v" << expr2var(v); };
gb.display(tout, _fn));
compute_basis(gb, warn);
update_statistics(gb);
TRACE("non_linear_gb", tout << "after:\n"; gb.display(tout););

View file

@ -101,7 +101,7 @@ namespace smt {
SASSERT(num_args >= 3);
sel_args.push_back(n);
for (unsigned i = 1; i < num_args - 1; ++i) {
sel_args.push_back(to_app(n->get_arg(i)));
sel_args.push_back(n->get_arg(i));
}
expr_ref sel(m);
sel = mk_select(sel_args.size(), sel_args.data());

View file

@ -594,10 +594,13 @@ namespace smt {
if (!ctx.add_fingerprint(this, m_default_lambda_fingerprint, 1, &arr))
return false;
m_stats.m_num_default_lambda_axiom++;
expr* def = mk_default(arr->get_expr());
expr* e = arr->get_expr();
expr* def = mk_default(e);
quantifier* lam = m.is_lambda_def(arr->get_decl());
expr_ref_vector args(m);
args.push_back(lam);
TRACE("array", tout << mk_pp(lam, m) << "\n" << mk_pp(e, m) << "\n");
expr_ref_vector args(m);
var_subst subst(m, false);
args.push_back(subst(lam, to_app(e)->get_num_args(), to_app(e)->get_args()));
for (unsigned i = 0; i < lam->get_num_decls(); ++i)
args.push_back(mk_epsilon(lam->get_decl_sort(i)).first);
expr_ref val(mk_select(args), m);

View file

@ -52,9 +52,8 @@ namespace smt {
bits.reset();
m_bits_expr.reset();
for (unsigned i = 0; i < bv_size; i++) {
for (unsigned i = 0; i < bv_size; i++)
m_bits_expr.push_back(mk_bit2bool(owner, i));
}
ctx.internalize(m_bits_expr.data(), bv_size, true);
for (unsigned i = 0; i < bv_size; i++) {
@ -601,9 +600,8 @@ namespace smt {
TRACE("bv", tout << mk_bounded_pp(n, m) << "\n";);
process_args(n);
mk_enode(n);
if (!ctx.relevancy()) {
if (!ctx.relevancy())
assert_bv2int_axiom(n);
}
}
@ -669,10 +667,12 @@ namespace smt {
mk_enode(n);
theory_var v = ctx.get_enode(n)->get_th_var(get_id());
mk_bits(v);
if (!ctx.relevancy()) {
enode* k = ctx.get_enode(n->get_arg(0));
if (!is_attached_to_var(k))
mk_var(k);
if (!ctx.relevancy())
assert_int2bv_axiom(n);
}
}
void theory_bv::assert_int2bv_axiom(app* n) {
@ -1497,6 +1497,26 @@ namespace smt {
unsigned sz = m_bits[v1].size();
bool changed = true;
TRACE("bv", tout << "bits size: " << sz << "\n";);
if (sz == 0) {
// int2bv(bv2int(x)) = x when int2bv(bv2int(x)) has same sort as x
enode* n1 = get_enode(r1);
for (enode* bv2int : *n1) {
if (!m_util.is_bv2int(bv2int->get_expr()))
continue;
enode* bv2int_arg = bv2int->get_arg(0);
for (enode* p : enode::parents(n1->get_root())) {
if (m_util.is_int2bv(p->get_expr()) && p->get_root() != bv2int_arg->get_root() && p->get_sort() == bv2int_arg->get_sort()) {
enode_pair_vector eqs;
eqs.push_back({n1, p->get_arg(0) });
eqs.push_back({n1, bv2int});
justification * js = ctx.mk_justification(
ext_theory_eq_propagation_justification(get_id(), ctx.get_region(), 0, nullptr, eqs.size(), eqs.data(), p, bv2int_arg));
ctx.assign_eq(p, bv2int_arg, eq_justification(js));
break;
}
}
}
}
do {
// This outerloop is necessary to avoid missing propagation steps.
// For example, let's assume that bits1 and bits2 contains the following

View file

@ -25,6 +25,7 @@ Revision History:
#include "smt/theory_datatype.h"
#include "smt/theory_array.h"
#include "smt/smt_model_generator.h"
#include <iostream>
namespace smt {
@ -519,9 +520,8 @@ namespace smt {
void theory_datatype::explain_is_child(enode* parent, enode* child) {
enode * parentc = oc_get_cstor(parent);
if (parent != parentc) {
if (parent != parentc)
m_used_eqs.push_back(enode_pair(parent, parentc));
}
// collect equalities on all children that may have been used.
bool found = false;
@ -546,14 +546,17 @@ namespace smt {
}
sort* se = nullptr;
if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* aarg : get_seq_args(arg)) {
enode* sibling;
for (enode* aarg : get_seq_args(arg, sibling)) {
if (aarg->get_root() == child->get_root()) {
if (aarg != child) {
if (aarg != child)
m_used_eqs.push_back(enode_pair(aarg, child));
}
found = true;
}
}
if (sibling && sibling != arg)
m_used_eqs.push_back(enode_pair(arg, sibling));
}
}
VERIFY(found);
@ -582,9 +585,11 @@ namespace smt {
TRACE("datatype",
tout << "occurs_check\n";
for (enode_pair const& p : m_used_eqs) {
for (enode_pair const& p : m_used_eqs)
tout << enode_eq_pp(p, ctx);
});
for (auto const& [a,b] : m_used_eqs)
tout << mk_pp(a->get_expr(), m) << " = " << mk_pp(b->get_expr(), m) << "\n";
);
}
// start exploring subgraph below `app`
@ -596,9 +601,9 @@ namespace smt {
}
v = m_find.find(v);
var_data * d = m_var_data[v];
if (!d->m_constructor) {
if (!d->m_constructor)
return false;
}
enode * parent = d->m_constructor;
oc_mark_on_stack(parent);
auto process_arg = [&](enode* aarg) {
@ -616,9 +621,8 @@ namespace smt {
};
for (enode * arg : enode::args(parent)) {
if (oc_cycle_free(arg)) {
if (oc_cycle_free(arg))
continue;
}
if (oc_on_stack(arg)) {
// arg was explored before app, and is still on the stack: cycle
occurs_check_explain(parent, arg);
@ -632,9 +636,11 @@ namespace smt {
oc_push_stack(arg);
}
else if (m_sutil.is_seq(s, se) && m_util.is_datatype(se)) {
for (enode* sarg : get_seq_args(arg))
if (process_arg(sarg))
enode* sibling;
for (enode* sarg : get_seq_args(arg, sibling)) {
if (process_arg(sarg))
return true;
}
}
else if (m_autil.is_array(s) && m_util.is_datatype(get_array_range(s))) {
for (enode* aarg : get_array_args(arg))
@ -645,7 +651,7 @@ namespace smt {
return false;
}
ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n) {
ptr_vector<enode> const& theory_datatype::get_seq_args(enode* n, enode*& sibling) {
m_args.reset();
m_todo.reset();
auto add_todo = [&](enode* n) {
@ -654,9 +660,14 @@ namespace smt {
m_todo.push_back(n);
}
};
for (enode* sib : *n)
add_todo(sib);
for (enode* sib : *n) {
if (m_sutil.str.is_concat_of_units(sib->get_expr())) {
add_todo(sib);
sibling = sib;
break;
}
}
for (unsigned i = 0; i < m_todo.size(); ++i) {
enode* n = m_todo[i];
@ -691,7 +702,7 @@ namespace smt {
a3 = cons(v3, a1)
*/
bool theory_datatype::occurs_check(enode * n) {
TRACE("datatype", tout << "occurs check: " << enode_pp(n, ctx) << "\n";);
TRACE("datatype_verbose", tout << "occurs check: " << enode_pp(n, ctx) << "\n";);
m_stats.m_occurs_check++;
bool res = false;
@ -706,7 +717,7 @@ namespace smt {
if (oc_cycle_free(app))
continue;
TRACE("datatype", tout << "occurs check loop: " << enode_pp(app, ctx) << (op==ENTER?" enter":" exit")<< "\n";);
TRACE("datatype_verbose", tout << "occurs check loop: " << enode_pp(app, ctx) << (op==ENTER?" enter":" exit")<< "\n";);
switch (op) {
case ENTER:
@ -830,15 +841,14 @@ namespace smt {
SASSERT(d->m_constructor);
func_decl * c_decl = d->m_constructor->get_decl();
datatype_value_proc * result = alloc(datatype_value_proc, c_decl);
for (enode* arg : enode::args(d->m_constructor)) {
for (enode* arg : enode::args(d->m_constructor))
result->add_dependency(arg);
}
TRACE("datatype",
tout << pp(n, m) << "\n";
tout << "depends on\n";
for (enode* arg : enode::args(d->m_constructor)) {
for (enode* arg : enode::args(d->m_constructor))
tout << " " << pp(arg, m) << "\n";
});
);
return result;
}
@ -965,12 +975,11 @@ namespace smt {
SASSERT(!lits.empty());
region & reg = ctx.get_region();
TRACE("datatype_conflict", tout << mk_ismt2_pp(recognizer->get_expr(), m) << "\n";
for (literal l : lits) {
for (literal l : lits)
ctx.display_detailed_literal(tout, l) << "\n";
}
for (auto const& p : eqs) {
for (auto const& p : eqs)
tout << enode_eq_pp(p, ctx);
});
);
ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), reg, lits.size(), lits.data(), eqs.size(), eqs.data())));
}
else if (num_unassigned == 1) {
@ -1052,9 +1061,8 @@ namespace smt {
ctx.mark_as_relevant(curr);
return;
}
else if (ctx.get_assignment(curr) != l_false) {
else if (ctx.get_assignment(curr) != l_false)
return;
}
}
if (r == nullptr)
return; // all recognizers are asserted to false... conflict will be detected...

View file

@ -94,7 +94,7 @@ namespace smt {
void oc_push_stack(enode * n);
ptr_vector<enode> m_args, m_todo;
ptr_vector<enode> const& get_array_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n);
ptr_vector<enode> const& get_seq_args(enode* n, enode*& sibling);
// class for managing state of final_check
class final_check_st {

View file

@ -276,23 +276,23 @@ class theory_lra::imp {
m_nla->push();
}
smt_params_helper prms(ctx().get_params());
m_nla->settings().run_order() = prms.arith_nl_order();
m_nla->settings().run_tangents() = prms.arith_nl_tangents();
m_nla->settings().run_horner() = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed() = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency() = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit() = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner() = prms.arith_nl_grobner();
m_nla->settings().run_nra() = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed() = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth() = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth() = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth() = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified() = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report() = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota() = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency() = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching() = false;
m_nla->settings().run_order = prms.arith_nl_order();
m_nla->settings().run_tangents = prms.arith_nl_tangents();
m_nla->settings().run_horner = prms.arith_nl_horner();
m_nla->settings().horner_subs_fixed = prms.arith_nl_horner_subs_fixed();
m_nla->settings().horner_frequency = prms.arith_nl_horner_frequency();
m_nla->settings().horner_row_length_limit = prms.arith_nl_horner_row_length_limit();
m_nla->settings().run_grobner = prms.arith_nl_grobner();
m_nla->settings().run_nra = prms.arith_nl_nra();
m_nla->settings().grobner_subs_fixed = prms.arith_nl_grobner_subs_fixed();
m_nla->settings().grobner_eqs_growth = prms.arith_nl_grobner_eqs_growth();
m_nla->settings().grobner_expr_size_growth = prms.arith_nl_grobner_expr_size_growth();
m_nla->settings().grobner_expr_degree_growth = prms.arith_nl_grobner_expr_degree_growth();
m_nla->settings().grobner_max_simplified = prms.arith_nl_grobner_max_simplified();
m_nla->settings().grobner_number_of_conflicts_to_report = prms.arith_nl_grobner_cnfl_to_report();
m_nla->settings().grobner_quota = prms.arith_nl_gr_q();
m_nla->settings().grobner_frequency = prms.arith_nl_grobner_frequency();
m_nla->settings().expensive_patching = false;
}
}
@ -1224,9 +1224,9 @@ public:
return;
}
expr_ref mod_r(a.mk_add(a.mk_mul(q, div), mod), m);
ctx().get_rewriter()(mod_r);
expr_ref eq_r(th.mk_eq_atom(mod_r, p), m);
ctx().internalize(eq_r, false);
ctx().internalize(eq_r, false);
literal eq = ctx().get_literal(eq_r);
rational k(0);
@ -1256,6 +1256,39 @@ public:
}
else {
expr_ref abs_q(m.mk_ite(a.mk_ge(q, zero), q, a.mk_uminus(q)), m);
expr_ref mone(a.mk_int(-1), m);
expr_ref modmq(a.mk_sub(mod, abs_q), m);
ctx().get_rewriter()(modmq);
literal eqz = mk_literal(m.mk_eq(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
literal mod_lt_q = mk_literal(a.mk_le(modmq, mone));
// q = 0 or p = (p mod q) + q * (p div q)
// q = 0 or (p mod q) >= 0
// q = 0 or (p mod q) < abs(q)
mk_axiom(eqz, eq);
mk_axiom(eqz, mod_ge_0);
mk_axiom(eqz, mod_lt_q);
m_arith_eq_adapter.mk_axioms(th.ensure_enode(mod_r), th.ensure_enode(p));
if (a.is_zero(p)) {
mk_axiom(eqz, mk_literal(m.mk_eq(div, zero)));
mk_axiom(eqz, mk_literal(m.mk_eq(mod, zero)));
}
// (or (= y 0) (<= (* y (div x y)) x))
else if (!a.is_numeral(q)) {
expr_ref div_ge(m);
div_ge = a.mk_ge(a.mk_sub(p, a.mk_mul(q, div)), zero);
ctx().get_rewriter()(div_ge);
mk_axiom(eqz, mk_literal(div_ge));
TRACE("arith", tout << eqz << " " << div_ge << "\n");
}
#if 0
/*literal div_ge_0 = */ mk_literal(a.mk_ge(div, zero));
/*literal div_le_0 = */ mk_literal(a.mk_le(div, zero));
/*literal p_ge_0 = */ mk_literal(a.mk_ge(p, zero));
@ -1266,7 +1299,7 @@ public:
// q >= 0 or (p mod q) >= 0
// q <= 0 or (p mod q) >= 0
// q <= 0 or (p mod q) < q
// q >= 0 or (p mod q) < -q
// q >= 0 or (p mod q) < -q
literal q_ge_0 = mk_literal(a.mk_ge(q, zero));
literal q_le_0 = mk_literal(a.mk_le(q, zero));
literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero));
@ -1277,11 +1310,11 @@ public:
mk_axiom(q_le_0, mod_ge_0);
mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero)));
mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero)));
#endif
#if 0
// seem expensive
mk_axiom(q_le_0, ~p_ge_0, div_ge_0);
mk_axiom(q_le_0, ~p_le_0, div_le_0);
mk_axiom(q_ge_0, ~p_ge_0, div_le_0);
@ -1293,19 +1326,21 @@ public:
mk_axiom(q_ge_0, p_le_0, ~div_ge_0);
#endif
#if 0
std::function<void(void)> log = [&,this]() {
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(eq.var())));
th.log_axiom_unit(m.mk_implies(m.mk_not(m.mk_eq(q, zero)), c.bool_var2expr(mod_ge_0.var())));
th.log_axiom_unit(m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_sub(mod, q), zero)));
th.log_axiom_unit(m.mk_implies(a.mk_lt(q, zero), a.mk_lt(a.mk_add(mod, q), zero)));
};
if_trace_stream _ts(m, log);
#endif
#if 0
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_ge_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_gt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_le_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_ge_0.var())), c.bool_var2expr(div_le_0.var())));
th.log_axiom_unit(m.mk_implies(m.mk_and(a.mk_lt(q, zero), c.bool_var2expr(p_le_0.var())), c.bool_var2expr(div_ge_0.var())));
#endif
};
if_trace_stream _ts(m, log);
}
if (params().m_arith_enum_const_mod && k.is_pos() && k < rational(8)) {
unsigned _k = k.get_unsigned();

View file

@ -43,32 +43,32 @@ namespace smt {
char const * theory_recfun::get_name() const { return "recfun"; }
theory* theory_recfun::mk_fresh(context* new_ctx) {
theory* theory_recfun::mk_fresh(context* new_ctx) {
return alloc(theory_recfun, *new_ctx);
}
bool theory_recfun::internalize_atom(app * atom, bool gate_ctx) {
TRACE("recfun", tout << mk_pp(atom, m) << " " << u().has_defs() << "\n");
if (!u().has_defs()) {
// if (u().is_defined(atom))
// throw default_exception("recursive atom definition is out of scope");
return false;
}
for (expr * arg : *atom) {
for (expr * arg : *atom)
ctx.internalize(arg, false);
}
if (!ctx.e_internalized(atom)) {
if (!ctx.e_internalized(atom))
ctx.mk_enode(atom, false, true, true);
}
if (!ctx.b_internalized(atom)) {
bool_var v = ctx.mk_bool_var(atom);
ctx.set_var_theory(v, get_id());
}
if (!ctx.relevancy() && u().is_defined(atom)) {
if (!ctx.b_internalized(atom))
ctx.set_var_theory(ctx.mk_bool_var(atom), get_id());
if (!ctx.relevancy() && u().is_defined(atom))
push_case_expand(atom);
}
return true;
}
bool theory_recfun::internalize_term(app * term) {
if (!u().has_defs()) {
// if (u().is_defined(term))
// throw default_exception("recursive term definition is out of scope");
return false;
}
for (expr* e : *term) {

View file

@ -2783,26 +2783,25 @@ bool theory_seq::get_length(expr* e, rational& val) {
todo.push_back(e1);
todo.push_back(e2);
}
else if (m_util.str.is_unit(c)) {
else if (m_util.str.is_unit(c))
val += rational(1);
}
else if (m_util.str.is_empty(c)) {
else if (m_util.str.is_empty(c))
continue;
}
else if (m_util.str.is_string(c, s)) {
else if (m_util.str.is_map(c, e1, e2))
todo.push_back(e2);
else if (m_util.str.is_mapi(c, e1, e2, c))
todo.push_back(c);
else if (m_util.str.is_string(c, s))
val += rational(s.length());
}
else if (!has_length(c)) {
len = mk_len(c);
add_axiom(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(0))));
TRACE("seq", tout << "literal has no length " << mk_pp(c, m) << "\n";);
return false;
}
else {
len = mk_len(c);
if (m_arith_value.get_value(len, val1) && !val1.is_neg()) {
val += val1;
if (!has_length(c)) {
add_axiom(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(0))));
TRACE("seq", tout << "literal has no length " << mk_pp(c, m) << "\n";);
return false;
}
else if (m_arith_value.get_value(len, val1) && !val1.is_neg())
val += val1;
else {
TRACE("seq", tout << "length has not been internalized " << mk_pp(c, m) << "\n";);
return false;
@ -3071,9 +3070,13 @@ void theory_seq::assign_eh(bool_var v, bool is_true) {
}
else if (m_util.str.is_is_digit(e)) {
}
else if (m_util.str.is_foldl(e) || m_util.str.is_foldli(e)) {
}
else {
TRACE("seq", tout << mk_pp(e, m) << "\n";);
IF_VERBOSE(0, verbose_stream() << mk_pp(e, m) << "\n");
UNREACHABLE();
}
}
@ -3216,18 +3219,15 @@ void theory_seq::relevant_eh(app* n) {
add_ubv_string(n);
expr* arg = nullptr;
if (m_sk.is_tail(n, arg)) {
if (m_sk.is_tail(n, arg))
add_length_limit(arg, m_max_unfolding_depth, true);
}
if (m_util.str.is_length(n, arg) && !has_length(arg) && ctx.e_internalized(arg)) {
if (m_util.str.is_length(n, arg) && !has_length(arg) && ctx.e_internalized(arg))
add_length_to_eqc(arg);
}
if (m_util.str.is_replace_all(n) ||
m_util.str.is_replace_re(n) ||
m_util.str.is_replace_re_all(n)
) {
m_util.str.is_replace_re_all(n)) {
add_unhandled_expr(n);
}
}

View file

@ -154,6 +154,7 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) {
force_push();
proof_ref in_pr(_in_pr, m), pr(_in_pr, m);
expr_ref r(e, m);
SASSERT(m.is_bool(e));
if (inconsistent())
return;

View file

@ -39,10 +39,26 @@ Notes:
#include "solver/parallel_tactic.h"
#include "solver/parallel_params.hpp"
class non_parallel_tactic : public tactic {
public:
non_parallel_tactic(solver* s, params_ref const& p) {
}
char const* name() const override { return "parallel_tactic"; }
void operator()(const goal_ref & g,goal_ref_buffer & result) override {
throw default_exception("parallel tactic is disabled in single threaded mode");
}
tactic * translate(ast_manager & m) override { return nullptr; }
void cleanup() override {}
};
#ifdef SINGLE_THREAD
tactic * mk_parallel_tactic(solver* s, params_ref const& p) {
throw default_exception("parallel tactic is disabled in single threaded mode");
return alloc(non_parallel_tactic, s, p);
}
#else
@ -97,9 +113,9 @@ class parallel_tactic : public tactic {
void shutdown() {
if (!m_shutdown) {
std::lock_guard<std::mutex> lock(m_mutex);
m_shutdown = true;
m_cond.notify_all();
std::lock_guard<std::mutex> lock(m_mutex);
for (solver_state* st : m_active) {
st->m().limit().cancel();
}
@ -131,7 +147,9 @@ class parallel_tactic : public tactic {
}
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock);
if (!m_shutdown) {
m_cond.wait(lock);
}
}
dec_wait();
}

View file

@ -116,8 +116,8 @@ public:
m_tactic->user_propagate_register_created(created_eh);
}
void user_propagate_register_decide(user_propagator::decide_eh_t& created_eh) override {
m_tactic->user_propagate_register_decide(created_eh);
void user_propagate_register_decide(user_propagator::decide_eh_t& decide_eh) override {
m_tactic->user_propagate_register_decide(decide_eh);
}
void user_propagate_clear() override {

View file

@ -110,7 +110,28 @@ public:
unsigned sz = g.size();
numeral val;
unsigned bv_sz;
expr * f, * lhs, * rhs;
expr * f, * lhs, * rhs;
auto match_bitmask = [&](expr* lhs, expr* rhs) {
unsigned lo, hi;
expr* arg;
if (!m_util.is_numeral(rhs, val, bv_sz))
return false;
if (!val.is_zero())
return false;
if (!m_util.is_extract(lhs, lo, hi, arg))
return false;
if (lo == 0)
return false;
if (hi + 1 != m_util.get_bv_size(arg))
return false;
if (!is_uninterp_const(arg))
return false;
val = rational::power_of_two(lo - 1) -1 ;
update_unsigned_upper(to_app(arg), val);
return true;
};
for (unsigned i = 0; i < sz; i++) {
bool negated = false;
f = g.form(i);
@ -152,22 +173,31 @@ public:
else update_signed_lower(to_app(rhs), val);
}
}
#if 0
#if 0
else if (m_util.is_bv_ule(f, lhs, rhs)) {
if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// v <= k
if (negated) update_unsigned_lower(to_app(lhs), val+numeral(1));
else update_unsigned_upper(to_app(lhs), val);
if (negated)
update_unsigned_lower(to_app(lhs), val+numeral(1));
else
update_unsigned_upper(to_app(lhs), val);
}
else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, val, bv_sz)) {
TRACE("bv_size_reduction", tout << (negated?"not ":"") << mk_ismt2_pp(f, m) << std::endl; );
// k <= v
if (negated) update_unsigned_upper(to_app(rhs), val-numeral(1));
else update_unsigned_lower(to_app(rhs), val);
if (negated)
update_unsigned_upper(to_app(rhs), val-numeral(1));
else
update_unsigned_lower(to_app(rhs), val);
}
}
else if (m.is_eq(f, lhs, rhs)) {
if (match_bitmask(lhs, rhs))
continue;
if (match_bitmask(rhs, lhs))
continue;
}
#endif
}
}
@ -185,33 +215,48 @@ public:
mc = nullptr;
m_mc = nullptr;
unsigned num_reduced = 0;
{
tactic_report report("reduce-bv-size", g);
collect_bounds(g);
// create substitution
expr_substitution subst(m);
auto insert_def = [&](app* k, expr* new_def, app* new_const) {
if (!new_def)
return;
subst.insert(k, new_def);
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(bv_size_reduction_mc, m, "bv_size_reduction");
m_mc->add(k, new_def);
if (!m_fmc && new_const)
m_fmc = alloc(generic_model_converter, m, "bv_size_reduction");
if (new_const)
m_fmc->hide(new_const);
}
num_reduced++;
};
if (!(m_signed_lowers.empty() || m_signed_uppers.empty())) {
TRACE("bv_size_reduction",
tout << "m_signed_lowers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_signed_lowers.begin(); it != m_signed_lowers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " >= " << it->m_value.to_string() << std::endl;
tout << "m_signed_uppers: " << std::endl;
for (obj_map<app, numeral>::iterator it = m_signed_uppers.begin(); it != m_signed_uppers.end(); it++)
tout << mk_ismt2_pp(it->m_key, m) << " <= " << it->m_value.to_string() << std::endl;
);
tout << "m_signed_lowers: " << std::endl;
for (auto const& [k, v] : m_signed_lowers)
tout << mk_ismt2_pp(k, m) << " >= " << v.to_string() << std::endl;
tout << "m_signed_uppers: " << std::endl;
for (auto const& [k, v] : m_signed_uppers)
tout << mk_ismt2_pp(k, m) << " <= " << v.to_string() << std::endl;
);
obj_map<app, numeral>::iterator it = m_signed_lowers.begin();
obj_map<app, numeral>::iterator end = m_signed_lowers.end();
for (; it != end; ++it) {
app * v = it->m_key;
unsigned bv_sz = m_util.get_bv_size(v);
numeral l = m_util.norm(it->m_value, bv_sz, true);
obj_map<app, numeral>::obj_map_entry * entry = m_signed_uppers.find_core(v);
for (auto& [k, val] : m_signed_lowers) {
unsigned bv_sz = m_util.get_bv_size(k);
numeral l = m_util.norm(val, bv_sz, true);
obj_map<app, numeral>::obj_map_entry * entry = m_signed_uppers.find_core(k);
if (entry != nullptr) {
numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true);
TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";);
TRACE("bv_size_reduction", tout << l << " <= " << k->get_decl()->get_name() << " <= " << u << "\n";);
expr * new_def = nullptr;
app * new_const = nullptr;
if (l > u) {
@ -219,19 +264,19 @@ public:
return;
}
else if (l == u) {
new_def = m_util.mk_numeral(l, v->get_sort());
new_def = m_util.mk_numeral(l, k->get_sort());
}
else {
// l < u
if (l.is_neg()) {
unsigned l_nb = (-l).get_num_bits();
unsigned v_nb = m_util.get_bv_size(v);
unsigned v_nb = m_util.get_bv_size(k);
if (u.is_neg())
{
// l <= v <= u <= 0
unsigned i_nb = l_nb;
TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= u <= 0 " << " --> " << i_nb << " bits\n";);
TRACE("bv_size_reduction", tout << " l <= " << k->get_decl()->get_name() << " <= u <= 0 " << " --> " << i_nb << " bits\n";);
if (i_nb < v_nb) {
new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb));
new_def = m_util.mk_concat(m_util.mk_numeral(numeral(-1), v_nb - i_nb), new_const);
@ -241,7 +286,7 @@ public:
// l <= v <= 0 <= u
unsigned u_nb = u.get_num_bits();
unsigned i_nb = ((l_nb > u_nb) ? l_nb : u_nb) + 1;
TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= 0 <= u " << " --> " << i_nb << " bits\n";);
TRACE("bv_size_reduction", tout << " l <= " << k->get_decl()->get_name() << " <= 0 <= u " << " --> " << i_nb << " bits\n";);
if (i_nb < v_nb) {
new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb));
new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const);
@ -251,31 +296,30 @@ public:
else {
// 0 <= l <= v <= u
unsigned u_nb = u.get_num_bits();
unsigned v_nb = m_util.get_bv_size(v);
TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << " --> " << u_nb << " bits\n";);
unsigned v_nb = m_util.get_bv_size(k);
TRACE("bv_size_reduction", tout << l << " <= " << k->get_decl()->get_name() << " <= " << u << " --> " << u_nb << " bits\n";);
if (u_nb < v_nb) {
new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(u_nb));
new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const);
}
}
}
if (new_def) {
subst.insert(v, new_def);
if (m_produce_models) {
if (!m_mc)
m_mc = alloc(bv_size_reduction_mc, m, "bv_size_reduction");
m_mc->add(v, new_def);
if (!m_fmc && new_const)
m_fmc = alloc(generic_model_converter, m, "bv_size_reduction");
if (new_const)
m_fmc->hide(new_const);
}
num_reduced++;
}
insert_def(k, new_def, new_const);
}
}
}
for (auto const& [k, v] : m_unsigned_uppers) {
unsigned shift;
unsigned bv_sz = m_util.get_bv_size(k);
numeral u = m_util.norm(v, bv_sz, true) + 1;
if (u.is_power_of_two(shift) && shift < bv_sz) {
app* new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(shift));
expr* new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), bv_sz - shift), new_const);
insert_def(k, new_def, new_const);
}
}
#if 0
if (!(m_unsigned_lowers.empty() && m_unsigned_uppers.empty())) {

View file

@ -424,10 +424,20 @@ tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5
return or_else(10, ts);
}
class no_par_tactical : public tactic {
public:
char const* name() const override { return "par"; }
void operator()(goal_ref const & in, goal_ref_buffer& result) override {
throw default_exception("par_tactical is unavailable in single threaded mode");
}
tactic * translate(ast_manager & m) override { return nullptr; }
void cleanup() override {}
};
#ifdef SINGLE_THREAD
tactic * par(unsigned num, tactic * const * ts) {
throw default_exception("par_tactical is unavailable in single threaded mode");
return alloc(no_par_tactical);
}
#else
@ -576,11 +586,23 @@ tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4) {
return par(4, ts);
}
class no_par_and_then_tactical : public tactic {
public:
char const* name() const override { return "par_then"; }
void operator()(goal_ref const & in, goal_ref_buffer& result) override {
throw default_exception("par_and_then is not available in single threaded mode");
}
tactic * translate(ast_manager & m) override { return nullptr; }
void cleanup() override {}
};
#ifdef SINGLE_THREAD
tactic * par_and_then(tactic * t1, tactic * t2) {
throw default_exception("par_and_then is not available in single threaded mode");
return alloc(no_par_and_then_tactical);
}
#else
class par_and_then_tactical : public and_then_tactical {
public:

View file

@ -593,6 +593,63 @@ public :
SASSERT_EQ(coeff[2], 0);
SASSERT_EQ(coeff[3], 3);
}
static void factors() {
pdd_manager m(3);
pdd v0 = m.mk_var(0);
pdd v1 = m.mk_var(1);
pdd v2 = m.mk_var(2);
pdd v3 = m.mk_var(3);
pdd v4 = m.mk_var(4);
pdd c1 = v0 * v1 * v2 + v2 * v0 + v1 + 1;
{
auto [vars, p] = c1.var_factors();
VERIFY(p == c1 && vars.empty());
}
{
auto q = c1 * v4;
auto [vars, p] = q.var_factors();
std::cout << p << " " << vars << "\n";
VERIFY(p == c1 && vars.size() == 1 && vars[0] == 4);
}
for (unsigned i = 0; i < 5; ++i) {
auto v = m.mk_var(i);
auto q = c1 * v;
std::cout << i << ": " << q << "\n";
auto [vars, p] = q.var_factors();
std::cout << p << " " << vars << "\n";
VERIFY(p == c1 && vars.size() == 1 && vars[0] == i);
}
for (unsigned i = 0; i < 5; ++i) {
for (unsigned j = 0; j < 5; ++j) {
auto vi = m.mk_var(i);
auto vj = m.mk_var(j);
auto q = c1 * vi * vj;
auto [vars, p] = q.var_factors();
std::cout << p << " " << vars << "\n";
VERIFY(p == c1 && vars.size() == 2);
VERIFY(vars[0] == i || vars[1] == i);
VERIFY(vars[0] == j || vars[1] == j);
}
}
for (unsigned i = 0; i < 5; ++i) {
for (unsigned j = i; j < 5; ++j) {
for (unsigned k = j; k < 5; ++k) {
auto vi = m.mk_var(i);
auto vj = m.mk_var(j);
auto vk = m.mk_var(k);
auto q = c1 * vi * vj * vk;
auto [vars, p] = q.var_factors();
std::cout << p << " " << vars << "\n";
VERIFY(p == c1 && vars.size() == 3);
VERIFY(vars[0] == i || vars[1] == i || vars[2] == i);
VERIFY(vars[0] == j || vars[1] == j || vars[2] == j);
VERIFY(vars[0] == k || vars[1] == k || vars[2] == k);
}
}
}
}
};
}
@ -615,4 +672,5 @@ void tst_pdd() {
dd::test::pow();
dd::test::subst_val();
dd::test::univariate();
dd::test::factors();
}

View file

@ -91,7 +91,7 @@ private:
}
void dec_ref(unsigned sz, value * vs) {
if (C::ref_count)
if (C::ref_count)
for (unsigned i = 0; i < sz; i++)
m_vmanager.dec_ref(vs[i]);
}
@ -151,7 +151,7 @@ private:
size_t new_capacity = curr_capacity == 0 ? 2 : (3 * curr_capacity + 1) >> 1;
value * new_vs = allocate_values(new_capacity);
if (curr_capacity > 0) {
for (size_t i = 0; i < curr_capacity; i++)
for (size_t i = 0; i < curr_capacity; i++)
new_vs[i] = vs[i];
deallocate_values(vs);
}
@ -177,7 +177,7 @@ private:
inc_ref(v);
vs[sz] = v;
sz++;
}
}
void rpush_back(cell * c, value const & v) {
SASSERT(c->kind() == ROOT);
@ -269,7 +269,7 @@ public:
}
value_manager & manager() { return m_vmanager; }
void mk(ref & r) {
dec_ref(r.m_ref);
cell * new_c = mk(ROOT);
@ -283,12 +283,12 @@ public:
r.m_ref = nullptr;
r.m_updt_counter = 0;
}
void copy(ref const & s, ref & t) {
inc_ref(s.m_ref);
dec_ref(t.m_ref);
t.m_ref = s.m_ref;
t.m_updt_counter = 0;
t.m_updt_counter = 0;
}
unsigned size(ref const & r) const {
@ -310,17 +310,15 @@ public:
}
void check_size(cell* c) const {
unsigned r;
while (c) {
switch (c->kind()) {
case SET:
break;
case PUSH_BACK:
r = size(c->next());
// ? SASSERT(c->idx() == size(c->next()));
break;
case POP_BACK:
r = size(c->next());
SASSERT(c->idx() == r);
SASSERT(c->idx() == size(c->next()));
break;
case ROOT:
return;
@ -333,7 +331,7 @@ public:
value const & get(ref const & r, unsigned i) const {
SASSERT(i < size(r));
unsigned trail_sz = 0;
cell * c = r.m_ref;
@ -451,7 +449,7 @@ public:
inc_ref(v);
new_c->m_elem = v;
new_c->m_next = r.m_ref;
r.m_ref = new_c;
r.m_ref = new_c;
SASSERT(new_c->m_ref_count == 1);
}
@ -536,7 +534,7 @@ public:
r.m_updt_counter = 0;
SASSERT(r.root());
}
void reroot(ref & r) {
if (r.root())
return;
@ -545,7 +543,7 @@ public:
unsigned r_sz = size(r);
unsigned trail_split_idx = r_sz / C::factor;
unsigned i = 0;
cell * c = r.m_ref;
cell * c = r.m_ref;
while (c->kind() != ROOT && i < trail_split_idx) {
cs.push_back(c);
c = c->next();
@ -556,7 +554,7 @@ public:
unfold(c);
}
DEBUG_CODE(check_size(c););
SASSERT(c->kind() == ROOT);
SASSERT(c->kind() == ROOT);
for (i = cs.size(); i-- > 0; ) {
cell * p = cs[i];
SASSERT(c->m_kind == ROOT);
@ -574,7 +572,7 @@ public:
case PUSH_BACK:
c->m_kind = POP_BACK;
if (sz == capacity(vs))
expand(vs);
expand(vs);
vs[sz] = p->m_elem;
++sz;
c->m_idx = sz;