3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2021-11-10 03:03:23 -08:00
commit 733f44d141
151 changed files with 3249 additions and 1504 deletions

View file

@ -58,7 +58,7 @@ extern "C" {
Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned sz, Z3_string str) {
Z3_TRY;
LOG_Z3_mk_string(c, str);
LOG_Z3_mk_lstring(c, sz, str);
RESET_ERROR_CODE();
unsigned_vector chs;
for (unsigned i = 0; i < sz; ++i) chs.push_back((unsigned char)str[i]);
@ -69,6 +69,17 @@ extern "C" {
Z3_CATCH_RETURN(nullptr);
}
Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned sz, unsigned const chars[]) {
Z3_TRY;
LOG_Z3_mk_u32string(c, sz, chars);
RESET_ERROR_CODE();
zstring s(sz, chars);
app* a = mk_c(c)->sutil().str.mk_string(s);
mk_c(c)->save_ast_trail(a);
RETURN_Z3(of_ast(a));
Z3_CATCH_RETURN(nullptr);
}
Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) {
Z3_TRY;
LOG_Z3_mk_string_sort(c);
@ -187,10 +198,9 @@ extern "C" {
svector<char> buff;
for (unsigned i = 0; i < str.length(); ++i) {
unsigned ch = str[i];
if (ch <= 32 || ch >= 127) {
if (ch == 0 || ch >= 256 || (ch == '\\' && i + 1 < str.length() && str[i+1] == 'u')) {
buff.reset();
buffer.push_back('\\');
// buffer.push_back('\\'); // possibly replace by native non-escaped version?
buffer.push_back('u');
buffer.push_back('{');
while (ch > 0) {
@ -215,6 +225,38 @@ extern "C" {
Z3_CATCH_RETURN("");
}
unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s) {
Z3_TRY;
LOG_Z3_get_string_length(c, s);
RESET_ERROR_CODE();
zstring str;
if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
}
return str.length();
Z3_CATCH_RETURN(0);
}
void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]) {
Z3_TRY;
LOG_Z3_get_string_contents(c, s, length, contents);
RESET_ERROR_CODE();
zstring str;
if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) {
SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal");
return;
}
if (str.length() != length) {
SET_ERROR_CODE(Z3_INVALID_ARG, "string size disagrees with supplied buffer length");
return;
}
for (unsigned i = 0; i < length; ++i)
contents[i] = str[i];
Z3_CATCH;
}
#define MK_SORTED(NAME, FN ) \
Z3_ast Z3_API NAME(Z3_context c, Z3_sort s) { \
Z3_TRY; \

View file

@ -875,7 +875,7 @@ extern "C" {
init_solver(c, s);
solver::push_eh_t _push = push_eh;
solver::pop_eh_t _pop = pop_eh;
solver::fresh_eh_t _fresh = [&](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) {
solver::fresh_eh_t _fresh = [=](void * user_ctx, ast_manager& m, solver::context_obj*& _ctx) {
ast_context_params params;
params.set_foreign_manager(&m);
auto* ctx = alloc(api::context, &params, false);

View file

@ -36,12 +36,12 @@ Notes:
\defgroup cppapi C++ API
*/
/*@{*/
/**@{*/
/**
@name C++ API classes and functions
*/
/*@{*/
/**@{*/
/**
\brief Z3 C++ namespace
@ -379,6 +379,7 @@ namespace z3 {
expr string_val(char const* s);
expr string_val(char const* s, unsigned n);
expr string_val(std::string const& s);
expr string_val(std::u32string const& s);
expr num_val(int n, sort const & s);
@ -1100,23 +1101,29 @@ namespace z3 {
bool is_string_value() const { return Z3_is_string(ctx(), m_ast); }
/**
\brief for a string value expression return an escaped or unescaped string value.
\brief for a string value expression return an escaped string value.
\pre expression is for a string value.
*/
std::string get_escaped_string() const {
std::string get_string() const {
assert(is_string_value());
char const* s = Z3_get_string(ctx(), m_ast);
check_error();
return std::string(s);
}
std::string get_string() const {
/**
\brief for a string value expression return an unespaced string value.
\pre expression is for a string value.
*/
std::u32string get_u32string() const {
assert(is_string_value());
unsigned n;
char const* s = Z3_get_lstring(ctx(), m_ast, &n);
check_error();
return std::string(s, n);
unsigned n = Z3_get_string_length(ctx(), m_ast);
std::u32string s;
s.resize(n);
Z3_get_string_contents(ctx(), m_ast, n, (unsigned*)s.data());
return s;
}
operator Z3_app() const { assert(is_app()); return reinterpret_cast<Z3_app>(m_ast); }
@ -1516,7 +1523,7 @@ namespace z3 {
expr substitute(expr_vector const& dst);
class iterator {
class iterator {
expr& e;
unsigned i;
public:
@ -1912,14 +1919,14 @@ namespace z3 {
Z3_ast r;
if (a.is_int()) {
expr zero = a.ctx().int_val(0);
expr ge = a >= zero;
expr na = -a;
expr ge = a >= zero;
expr na = -a;
r = Z3_mk_ite(a.ctx(), ge, a, na);
}
else if (a.is_real()) {
expr zero = a.ctx().real_val(0);
expr ge = a >= zero;
expr na = -a;
expr ge = a >= zero;
expr na = -a;
r = Z3_mk_ite(a.ctx(), ge, a, na);
}
else {
@ -3480,6 +3487,7 @@ namespace z3 {
inline expr context::string_val(char const* s, unsigned n) { Z3_ast r = Z3_mk_lstring(m_ctx, n, s); check_error(); return expr(*this, r); }
inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); }
inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); }
inline expr context::string_val(std::u32string const& s) { Z3_ast r = Z3_mk_u32string(m_ctx, (unsigned)s.size(), (unsigned const*)s.c_str()); check_error(); return expr(*this, r); }
inline expr context::num_val(int n, sort const & s) { Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); }
@ -3932,6 +3940,8 @@ namespace z3 {
virtual void push() = 0;
virtual void pop(unsigned num_scopes) = 0;
virtual ~user_propagator_base() = default;
/**
\brief user_propagators created using \c fresh() are created during
search and their lifetimes are restricted to search time. They should
@ -3954,12 +3964,28 @@ namespace z3 {
Z3_solver_propagate_fixed(ctx(), *s, fixed_eh);
}
void register_fixed() {
assert(s);
m_fixed_eh = [this](unsigned id, expr const& e) {
fixed(id, e);
};
Z3_solver_propagate_fixed(ctx(), *s, fixed_eh);
}
void register_eq(eq_eh_t& f) {
assert(s);
m_eq_eh = f;
Z3_solver_propagate_eq(ctx(), *s, eq_eh);
}
void register_eq() {
assert(s);
m_eq_eh = [this](unsigned x, unsigned y) {
eq(x, y);
};
Z3_solver_propagate_eq(ctx(), *s, eq_eh);
}
/**
\brief register a callback on final-check.
During the final check stage, all propagations have been processed.
@ -3973,6 +3999,21 @@ namespace z3 {
m_final_eh = f;
Z3_solver_propagate_final(ctx(), *s, final_eh);
}
void register_final() {
assert(s);
m_final_eh = [this]() {
final();
};
Z3_solver_propagate_final(ctx(), *s, final_eh);
}
virtual void fixed(unsigned /*id*/, expr const& /*e*/) { }
virtual void eq(unsigned /*x*/, unsigned /*y*/) { }
virtual void final() { }
/**
\brief tracks \c e by a unique identifier that is returned by the call.
@ -4020,7 +4061,7 @@ namespace z3 {
}
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#undef Z3_THROW

View file

@ -214,7 +214,6 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(expr, numerator)
.MM(expr, denominator)
.MM(expr, is_string_value)
.MM(expr, get_escaped_string)
.MM(expr, get_string)
.MM(expr, decl)
.MM(expr, num_args)

View file

@ -76,14 +76,14 @@ int compare_pointers(void* pt1, void* pt2) {
return +1;
}
#define MK_CTX_OF(X) \
#define MK_CTX_OF(X, USED) \
CAMLprim DLL_PUBLIC value n_context_of_ ## X(value v) { \
CAMLparam1(v); \
CAMLlocal1(result); \
Z3_context_plus cp; \
Z3_ ## X ## _plus * p = (Z3_ ## X ## _plus *) Data_custom_val(v); \
cp = p->cp; \
result = caml_alloc_custom(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), 0, 1); \
result = caml_alloc_custom_mem(&Z3_context_plus_custom_ops, sizeof(Z3_context_plus), USED); \
*(Z3_context_plus *)Data_custom_val(result) = cp; \
/* We increment the usage counter of the context, as we just \
created a second custom block holding that context */ \
@ -102,7 +102,7 @@ int compare_pointers(void* pt1, void* pt2) {
CAMLlocal1(result); \
Z3_context_plus cp = *(Z3_context_plus*)(Data_custom_val(v)); \
Z3_ ## X ## _plus a = Z3_ ## X ## _plus_mk(cp, NULL); \
result = caml_alloc_custom(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), 0, 1); \
result = caml_alloc_custom_mem(&Z3_ ## X ## _plus_custom_ops, sizeof(Z3_ ## X ## _plus), USED); \
*(Z3_ ## X ## _plus*)(Data_custom_val(result)) = a; \
CAMLreturn(result); \
}
@ -294,9 +294,9 @@ static struct custom_operations Z3_ast_plus_custom_ops = {
Z3_ast_compare_ext
};
MK_CTX_OF(ast)
MK_CTX_OF(ast, 16) // let's say 16 bytes per ast
#define MK_PLUS_OBJ_NO_REF(X) \
#define MK_PLUS_OBJ_NO_REF(X, USED) \
typedef struct { \
Z3_context_plus cp; \
Z3_ ## X p; \
@ -349,9 +349,9 @@ MK_CTX_OF(ast)
Z3_ ## X ## _compare_ext \
}; \
\
MK_CTX_OF(X)
MK_CTX_OF(X, USED)
#define MK_PLUS_OBJ(X) \
#define MK_PLUS_OBJ(X, USED) \
typedef struct { \
Z3_context_plus cp; \
Z3_ ## X p; \
@ -408,27 +408,27 @@ MK_CTX_OF(ast)
Z3_ ## X ## _compare_ext \
}; \
\
MK_CTX_OF(X)
MK_CTX_OF(X, USED)
MK_PLUS_OBJ_NO_REF(symbol)
MK_PLUS_OBJ_NO_REF(constructor)
MK_PLUS_OBJ_NO_REF(constructor_list)
MK_PLUS_OBJ_NO_REF(rcf_num)
MK_PLUS_OBJ(params)
MK_PLUS_OBJ(param_descrs)
MK_PLUS_OBJ(model)
MK_PLUS_OBJ(func_interp)
MK_PLUS_OBJ(func_entry)
MK_PLUS_OBJ(goal)
MK_PLUS_OBJ(tactic)
MK_PLUS_OBJ(probe)
MK_PLUS_OBJ(apply_result)
MK_PLUS_OBJ(solver)
MK_PLUS_OBJ(stats)
MK_PLUS_OBJ(ast_map)
MK_PLUS_OBJ(ast_vector)
MK_PLUS_OBJ(fixedpoint)
MK_PLUS_OBJ(optimize)
MK_PLUS_OBJ_NO_REF(symbol, 32)
MK_PLUS_OBJ_NO_REF(constructor, 32)
MK_PLUS_OBJ_NO_REF(constructor_list, 32)
MK_PLUS_OBJ_NO_REF(rcf_num, 32)
MK_PLUS_OBJ(params, 128)
MK_PLUS_OBJ(param_descrs, 128)
MK_PLUS_OBJ(model, 512)
MK_PLUS_OBJ(func_interp, 128)
MK_PLUS_OBJ(func_entry, 128)
MK_PLUS_OBJ(goal, 128)
MK_PLUS_OBJ(tactic, 128)
MK_PLUS_OBJ(probe, 128)
MK_PLUS_OBJ(apply_result, 128)
MK_PLUS_OBJ(solver, 20 * 1000 * 1000) // pretend a solver is 20MB
MK_PLUS_OBJ(stats, 128)
MK_PLUS_OBJ(ast_map, 1024 * 2)
MK_PLUS_OBJ(ast_vector, 128)
MK_PLUS_OBJ(fixedpoint, 20 * 1000 * 1000)
MK_PLUS_OBJ(optimize, 20 * 1000 * 1000)
#ifdef __cplusplus
extern "C" {

View file

@ -11102,33 +11102,34 @@ class PropClosures:
self.bases = {}
self.lock = None
def set_threaded():
def set_threaded(self):
if self.lock is None:
import threading
self.lock = threading.thread.Lock()
self.lock = threading.Lock()
def get(self, ctx):
if self.lock:
self.lock.acquire()
r = self.bases[ctx]
if self.lock:
self.lock.release()
with self.lock:
r = self.bases[ctx]
else:
r = self.bases[ctx]
return r
def set(self, ctx, r):
if self.lock:
self.lock.acquire()
self.bases[ctx] = r
if self.lock:
self.lock.release()
with self.lock:
self.bases[ctx] = r
else:
self.bases[ctx] = r
def insert(self, r):
if self.lock:
self.lock.acquire()
id = len(self.bases) + 3
self.bases[id] = r
if self.lock:
self.lock.release()
with self.lock:
id = len(self.bases) + 3
self.bases[id] = r
else:
id = len(self.bases) + 3
self.bases[id] = r
return id
@ -11151,8 +11152,9 @@ def user_prop_pop(ctx, num_scopes):
def user_prop_fresh(id, ctx):
_prop_closures.set_threaded()
new_prop = UsePropagateBase(None, ctx)
_prop_closures.set(new_prop.id, new_prop.fresh())
prop = _prop_closures.get(id)
new_prop = prop.fresh()
_prop_closures.set(new_prop.id, new_prop)
return ctypes.c_void_p(new_prop.id)
@ -11214,11 +11216,12 @@ 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)
#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)
if s:
Z3_solver_propagate_init(self.ctx_ref(),
s.solver,

View file

@ -25,10 +25,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Algebraic Numbers */
/*@{*/
/**@{*/
/**
\brief Return \c true if \c a can be used as value in the Z3 real algebraic
number package.
@ -240,8 +240,8 @@ extern "C" {
*/
unsigned Z3_API Z3_algebraic_get_i(Z3_context c, Z3_ast a);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -37,11 +37,11 @@ DEFINE_TYPE(Z3_optimize);
DEFINE_TYPE(Z3_rcf_num);
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Types */
///@{
/**@{*/
/**
Most of the types in the C API are opaque pointers.
@ -1449,7 +1449,7 @@ typedef enum
Z3_GOAL_UNDER_OVER
} Z3_goal_prec;
///@}
/**@}*/
#ifdef __cplusplus
extern "C" {
@ -1514,7 +1514,7 @@ extern "C" {
/**@}*/
/** @name Create configuration */
/*@{*/
/**@{*/
/**
\brief Create a configuration object for the Z3 context object.
@ -1569,10 +1569,10 @@ extern "C" {
*/
void Z3_API Z3_set_param_value(Z3_config c, Z3_string param_id, Z3_string param_value);
/*@}*/
/**@}*/
/** @name Context and AST Reference Counting */
/*@{*/
/**@{*/
/**
\brief Create a context using the given configuration.
@ -1678,10 +1678,10 @@ extern "C" {
void Z3_API Z3_interrupt(Z3_context c);
/*@}*/
/**@}*/
/** @name Parameters */
/*@{*/
/**@{*/
/**
\brief Create a Z3 (empty) parameter set.
@ -1754,10 +1754,10 @@ extern "C" {
*/
void Z3_API Z3_params_validate(Z3_context c, Z3_params p, Z3_param_descrs d);
/*@}*/
/**@}*/
/** @name Parameter Descriptions */
/*@{*/
/**@{*/
/**
\brief Increment the reference counter of the given parameter description set.
@ -1811,10 +1811,10 @@ extern "C" {
*/
Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p);
/*@}*/
/**@}*/
/** @name Symbols */
/*@{*/
/**@{*/
/**
\brief Create a Z3 symbol using an integer.
@ -1843,10 +1843,10 @@ extern "C" {
*/
Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, Z3_string s);
/*@}*/
/**@}*/
/** @name Sorts */
/*@{*/
/**@{*/
/**
\brief Create a free (uninterpreted) type using the given name (symbol).
@ -2150,10 +2150,10 @@ extern "C" {
Z3_func_decl* tester,
Z3_func_decl accessors[]);
/*@}*/
/**@}*/
/** @name Constants and Applications */
/*@{*/
/**@{*/
/**
\brief Declare a constant or function.
@ -2287,10 +2287,10 @@ extern "C" {
*/
void Z3_API Z3_add_rec_def(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast args[], Z3_ast body);
/*@}*/
/**@}*/
/** @name Propositional Logic and Equality */
/*@{*/
/**@{*/
/**
\brief Create an AST node representing \c true.
@ -2397,10 +2397,10 @@ extern "C" {
def_API('Z3_mk_or', AST, (_in(CONTEXT), _in(UINT), _in_array(1, AST)))
*/
Z3_ast Z3_API Z3_mk_or(Z3_context c, unsigned num_args, Z3_ast const args[]);
/*@}*/
/**@}*/
/** @name Integers and Reals */
/*@{*/
/**@{*/
/**
\brief Create an AST node representing \ccode{args[0] + ... + args[num_args-1]}.
@ -2573,10 +2573,10 @@ extern "C" {
def_API('Z3_mk_is_int', AST, (_in(CONTEXT), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_is_int(Z3_context c, Z3_ast t1);
/*@}*/
/**@}*/
/** @name Bit-vectors */
/*@{*/
/**@{*/
/**
\brief Bitwise negation.
@ -3097,10 +3097,10 @@ extern "C" {
def_API('Z3_mk_bvmul_no_underflow', AST, (_in(CONTEXT), _in(AST), _in(AST)))
*/
Z3_ast Z3_API Z3_mk_bvmul_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2);
/*@}*/
/**@}*/
/** @name Arrays */
/*@{*/
/**@{*/
/**
\brief Array read.
The argument \c a is the array and \c i is the index of the array that gets read.
@ -3212,10 +3212,10 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_set_has_size(Z3_context c, Z3_ast set, Z3_ast k);
/*@}*/
/**@}*/
/** @name Sets */
/*@{*/
/**@{*/
/**
\brief Create Set type.
@ -3308,10 +3308,10 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_array_ext(Z3_context c, Z3_ast arg1, Z3_ast arg2);
/*@}*/
/**@}*/
/** @name Numerals */
/*@{*/
/**@{*/
/**
\brief Create a numeral of a given sort.
@ -3400,10 +3400,10 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_bv_numeral(Z3_context c, unsigned sz, bool const* bits);
/*@}*/
/**@}*/
/** @name Sequences and regular expressions */
/*@{*/
/**@{*/
/**
\brief Create a sequence sort out of the sort for the elements.
@ -3480,6 +3480,11 @@ extern "C" {
/**
\brief Create a string constant out of the string that is passed in
The string may contain escape encoding for non-printable characters
or characters outside of the basic printable ASCII range. For example,
the escape encoding \u{0} represents the character 0 and the encoding
\u{100} represents the character 256.
def_API('Z3_mk_string', AST, (_in(CONTEXT), _in(STRING)))
*/
Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string s);
@ -3487,12 +3492,22 @@ extern "C" {
/**
\brief Create a string constant out of the string that is passed in
It takes the length of the string as well to take into account
0 characters. The string is unescaped.
0 characters. The string is treated as if it is unescaped so a sequence
of characters \u{0} is treated as 5 characters and not the character 0.
def_API('Z3_mk_lstring', AST, (_in(CONTEXT), _in(UINT), _in(STRING)))
*/
Z3_ast Z3_API Z3_mk_lstring(Z3_context c, unsigned len, Z3_string s);
/**
\brief Create a string constant out of the string that is passed in
It takes the length of the string as well to take into account
0 characters. The string is unescaped.
def_API('Z3_mk_u32string', AST, (_in(CONTEXT), _in(UINT), _in_array(1, UINT)))
*/
Z3_ast Z3_API Z3_mk_u32string(Z3_context c, unsigned len, unsigned const chars[]);
/**
\brief Determine if \c s is a string constant.
@ -3502,6 +3517,7 @@ extern "C" {
/**
\brief Retrieve the string constant stored in \c s.
Characters outside the basic printiable ASCII range are escaped.
\pre Z3_is_string(c, s)
@ -3510,14 +3526,36 @@ extern "C" {
Z3_string Z3_API Z3_get_string(Z3_context c, Z3_ast s);
/**
\brief Retrieve the unescaped string constant stored in \c s.
\brief Retrieve the string constant stored in \c s. The string can contain escape sequences.
Characters in the range 1 to 255 are literal.
Characters in the range 0, and 256 above are escaped.
\pre Z3_is_string(c, s)
def_API('Z3_get_lstring', CHAR_PTR, (_in(CONTEXT), _in(AST), _out(UINT)))
*/
Z3_char_ptr Z3_API Z3_get_lstring(Z3_context c, Z3_ast s, unsigned* length);
/**
\brief Retrieve the length of the unescaped string constant stored in \c s.
\pre Z3_is_string(c, s)
def_API('Z3_get_string_length', UINT, (_in(CONTEXT), _in(AST)))
*/
unsigned Z3_API Z3_get_string_length(Z3_context c, Z3_ast s);
/**
\brief Retrieve the unescaped string constant stored in \c s.
\pre Z3_is_string(c, s)
\pre length contains the number of characters in s
def_API('Z3_get_string_contents', VOID, (_in(CONTEXT), _in(AST), _in(UINT), _out_array(2, UINT)))
*/
void Z3_API Z3_get_string_contents(Z3_context c, Z3_ast s, unsigned length, unsigned contents[]);
/**
\brief Create an empty sequence of the sequence sort \c seq.
@ -3821,11 +3859,11 @@ extern "C" {
*/
Z3_ast Z3_API Z3_mk_char_is_digit(Z3_context c, Z3_ast ch);
/*@}*/
/**@}*/
/** @name Special relations */
/*@{*/
/**@{*/
/**
\brief create a linear ordering relation over signature \c a.
The relation is identified by the index \c id.
@ -3866,10 +3904,10 @@ extern "C" {
*/
Z3_func_decl Z3_API Z3_mk_transitive_closure(Z3_context c, Z3_func_decl f);
/*@}*/
/**@}*/
/** @name Quantifiers */
/*@{*/
/**@{*/
/**
\brief Create a pattern for quantifier instantiation.
@ -4174,10 +4212,10 @@ extern "C" {
Z3_ast body);
/*@}*/
/**@}*/
/** @name Accessors */
/*@{*/
/**@{*/
/**
\brief Return \c Z3_INT_SYMBOL if the symbol was constructed
using #Z3_mk_int_symbol, and \c Z3_STRING_SYMBOL if the symbol
@ -5112,10 +5150,10 @@ extern "C" {
def_API('Z3_simplify_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT),))
*/
Z3_param_descrs Z3_API Z3_simplify_get_param_descrs(Z3_context c);
/*@}*/
/**@}*/
/** @name Modifiers */
/*@{*/
/**@{*/
/**
\brief Update the arguments of term \c a using the arguments \c args.
The number of arguments \c num_args should coincide
@ -5158,10 +5196,10 @@ extern "C" {
def_API('Z3_translate', AST, (_in(CONTEXT), _in(AST), _in(CONTEXT)))
*/
Z3_ast Z3_API Z3_translate(Z3_context source, Z3_ast a, Z3_context target);
/*@}*/
/**@}*/
/** @name Models */
/*@{*/
/**@{*/
/**
\brief Create a fresh model object. It has reference count 0.
@ -5496,10 +5534,10 @@ extern "C" {
def_API('Z3_func_entry_get_arg', AST, (_in(CONTEXT), _in(FUNC_ENTRY), _in(UINT)))
*/
Z3_ast Z3_API Z3_func_entry_get_arg(Z3_context c, Z3_func_entry e, unsigned i);
/*@}*/
/**@}*/
/** @name Interaction logging */
/*@{*/
/**@{*/
/**
\brief Log interaction to a file.
@ -5534,10 +5572,10 @@ extern "C" {
def_API('Z3_toggle_warning_messages', VOID, (_in(BOOL),))
*/
void Z3_API Z3_toggle_warning_messages(bool enabled);
/*@}*/
/**@}*/
/** @name String conversion */
/*@{*/
/**@{*/
/**
\brief Select mode for the format used for pretty-printing AST nodes.
@ -5624,10 +5662,10 @@ extern "C" {
Z3_ast const assumptions[],
Z3_ast formula);
/*@}*/
/**@}*/
/** @name Parser interface */
/*@{*/
/**@{*/
/**
\brief Parse the given string using the SMT-LIB2 parser.
@ -5671,10 +5709,10 @@ extern "C" {
Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str);
/*@}*/
/**@}*/
/** @name Error Handling */
/*@{*/
/**@{*/
#ifndef SAFE_ERRORS
/**
\brief Return the error code for the last API call.
@ -5717,10 +5755,10 @@ extern "C" {
*/
Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err);
/*@}*/
/**@}*/
/** @name Miscellaneous */
/*@{*/
/**@{*/
/**
\brief Return Z3 version number information.
@ -5781,10 +5819,10 @@ extern "C" {
def_API('Z3_finalize_memory', VOID, ())
*/
void Z3_API Z3_finalize_memory(void);
/*@}*/
/**@}*/
/** @name Goals */
/*@{*/
/**@{*/
/**
\brief Create a goal (aka problem). A goal is essentially a set
of formulas, that can be solved and/or transformed using
@ -5934,10 +5972,10 @@ extern "C" {
*/
Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g, bool include_names);
/*@}*/
/**@}*/
/** @name Tactics and Probes */
/*@{*/
/**@{*/
/**
\brief Return a tactic associated with the given name.
The complete list of tactics may be obtained using the procedures #Z3_get_num_tactics and #Z3_get_tactic_name.
@ -6286,10 +6324,10 @@ extern "C" {
*/
Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i);
/*@}*/
/**@}*/
/** @name Solvers*/
/*@{*/
/**@{*/
/**
\brief Create a new solver. This solver is a "combined solver" (see
combined_solver module) that internally uses a non-incremental (solver1) and an
@ -6812,10 +6850,10 @@ extern "C" {
*/
Z3_string Z3_API Z3_solver_to_dimacs_string(Z3_context c, Z3_solver s, bool include_names);
/*@}*/
/**@}*/
/** @name Statistics */
/*@{*/
/**@{*/
/**
\brief Convert a statistics into a string.
@ -6897,11 +6935,11 @@ extern "C" {
*/
uint64_t Z3_API Z3_get_estimated_alloc_size(void);
/*@}*/
/**@}*/
#ifdef __cplusplus
}
#endif // __cplusplus
/*@}*/
/**@}*/

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name AST vectors */
/*@{*/
/**@{*/
/**
\brief Return an empty AST vector.
@ -104,10 +104,10 @@ extern "C" {
*/
Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v);
/*@}*/
/**@}*/
/** @name AST maps */
/*@{*/
/**@{*/
/**
\brief Return an empty mapping from AST to AST
@ -189,8 +189,8 @@ extern "C" {
def_API('Z3_ast_map_to_string', STRING, (_in(CONTEXT), _in(AST_MAP)))
*/
Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Fixedpoint facilities */
/*@{*/
/**@{*/
/**
\brief Create a new fixedpoint context.
@ -373,8 +373,8 @@ extern "C" {
void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Floating-Point Arithmetic */
/*@{*/
/**@{*/
/**
\brief Create the RoundingMode sort.
@ -841,7 +841,7 @@ extern "C" {
/** @name Z3-specific floating-point extensions */
/*@{*/
/**@{*/
/**
\brief Retrieves the number of bits reserved for the exponent in a FloatingPoint sort.
@ -1080,9 +1080,9 @@ extern "C" {
def_API('Z3_mk_fpa_to_fp_int_real', AST, (_in(CONTEXT),_in(AST),_in(AST),_in(AST),_in(SORT)))
*/
Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s);
/*@}*/
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -28,10 +28,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Optimization facilities */
/*@{*/
/**@{*/
/**
\brief Create a new optimize context.
@ -368,8 +368,8 @@ extern "C" {
Z3_model_eh model_eh);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -24,11 +24,11 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Polynomials */
/*@{*/
/**@{*/
/**
\brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x.
@ -43,8 +43,8 @@ extern "C" {
Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -26,10 +26,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Real Closed Fields */
/*@{*/
/**@{*/
/**
\brief Delete a RCF numeral created using the RCF API.
@ -196,8 +196,8 @@ extern "C" {
*/
void Z3_API Z3_rcf_get_numerator_denominator(Z3_context c, Z3_rcf_num a, Z3_rcf_num * n, Z3_rcf_num * d);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -23,10 +23,10 @@ extern "C" {
#endif // __cplusplus
/** \defgroup capi C API */
/*@{*/
/**@{*/
/** @name Spacer facilities */
/*@{*/
/**@{*/
/**
\brief Pose a query against the asserted rules at the given level.
@ -132,8 +132,8 @@ extern "C" {
Z3_ast_vector vars,
Z3_ast body);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
#ifdef __cplusplus
}

View file

@ -1399,6 +1399,7 @@ inline bool has_labels(expr const * n) {
class some_value_proc {
public:
virtual expr * operator()(sort * s) = 0;
virtual ~some_value_proc() = default;
};
// -----------------------------------

View file

@ -46,6 +46,7 @@ public:
public:
virtual bool operator()(func_decl* d) const { return false; }
virtual bool operator()(sort* s) const { return false; }
virtual ~is_declared() = default;
};
private:
ast_manager& m_manager;

View file

@ -406,7 +406,7 @@ namespace datatype {
VALIDATE_PARAM(arity == 1 && num_parameters == 1 && parameters[0].is_ast() && is_func_decl(parameters[0].get_ast()));
VALIDATE_PARAM(u().is_datatype(domain[0]));
VALIDATE_PARAM_PP(domain[0] == to_func_decl(parameters[0].get_ast())->get_range(), "invalid sort argument passed to recognizer");
// blindly trust that parameter is a constructor
VALIDATE_PARAM_PP(u().is_constructor(to_func_decl(parameters[0].get_ast())), "expecting constructor argument to recognizer");
sort* range = m_manager->mk_bool_sort();
func_decl_info info(m_family_id, OP_DT_IS, num_parameters, parameters);
info.m_private_parameters = true;

View file

@ -681,6 +681,7 @@ namespace euf {
void egraph::begin_explain() {
SASSERT(m_todo.empty());
m_uses_congruence = false;
DEBUG_CODE(for (enode* n : m_nodes) SASSERT(!n->is_marked1()););
}
void egraph::end_explain() {

View file

@ -102,7 +102,7 @@ expr_ref bv2fpa_converter::convert_bv2fp(sort * s, expr * sgn, expr * exp, expr
rational exp_unbiased_q;
exp_unbiased_q = exp_q - m_fpa_util.fm().m_powers2.m1(ebits - 1);
scoped_mpz sig_z(mpzm);
scoped_mpz sig_z(mpzm);
mpf_exp_t exp_z;
mpzm.set(sig_z, sig_q.to_mpq().numerator());
exp_z = mpzm.get_int64(exp_unbiased_q.to_mpq().numerator());
@ -346,7 +346,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
app * a0 = to_app(val->get_arg(0));
expr_ref v0(m), v1(m), v2(m);
#ifdef Z3DEBUG
#ifdef Z3DEBUG_FPA2BV_NAMES
app * a1 = to_app(val->get_arg(1));
app * a2 = to_app(val->get_arg(2));
v0 = mc->get_const_interp(a0->get_decl());
@ -378,7 +378,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
SASSERT(val->is_app_of(m_fpa_util.get_family_id(), OP_FPA_FP));
#ifdef Z3DEBUG
#ifdef Z3DEBUG_FPA2BV_NAMES
SASSERT(to_app(val->get_arg(0))->get_decl()->get_arity() == 0);
SASSERT(to_app(val->get_arg(1))->get_decl()->get_arity() == 0);
SASSERT(to_app(val->get_arg(2))->get_decl()->get_arity() == 0);
@ -386,9 +386,10 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model
seen.insert(to_app(val->get_arg(1))->get_decl());
seen.insert(to_app(val->get_arg(2))->get_decl());
#else
SASSERT(a->get_arg(0)->get_kind() == OP_EXTRACT);
SASSERT(to_app(a->get_arg(0))->get_arg(0)->get_kind() == OP_EXTRACT);
SASSERT(is_app(val->get_arg(0)));
SASSERT(m_bv_util.is_extract(val->get_arg(0)));
seen.insert(to_app(to_app(val->get_arg(0))->get_arg(0))->get_decl());
#endif
if (!sgn && !sig && !exp)

View file

@ -192,7 +192,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) {
app_ref sgn(m), s(m), e(m);
#ifdef Z3DEBUG
#ifdef Z3DEBUG_FPA2BV_NAMES
std::string p("fpa2bv");
std::string name = f->get_name().str();
@ -326,7 +326,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) {
expr_ref bv3(m);
bv3 = m.mk_fresh_const(
#ifdef Z3DEBUG
#ifdef Z3DEBUG_FPA2BV_NAMES
"fpa2bv_rm"
#else
nullptr
@ -465,7 +465,7 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits,
SASSERT(is_well_sorted(m, big_d_sig));
if (ebits > sbits)
throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
throw default_exception("addition/subtract with ebits > sbits not supported");
expr_ref shifted_big(m), shifted_d_sig(m), sticky_raw(m), sticky(m);
@ -950,7 +950,7 @@ void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref &
unsigned ebits = m_util.get_ebits(s);
unsigned sbits = m_util.get_sbits(s);
if (ebits > sbits)
throw default_exception("there is no floating point support for division for representations with non-standard bit representations");
throw default_exception("division with ebits > sbits not supported");
SASSERT(ebits <= sbits);
expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m);
@ -2561,9 +2561,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
res_sig = sig;
res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder.
unsigned sig_sz = m_bv_util.get_bv_size(res_sig);
(void) sig_sz;
SASSERT(sig_sz == to_sbits + 4);
SASSERT(m_bv_util.get_bv_size(res_sig) == to_sbits + 4);
expr_ref exponent_overflow(m), exponent_underflow(m);
exponent_overflow = m.mk_false();
@ -2577,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
lz_ext = m_bv_util.mk_zero_extend(to_ebits - from_ebits + 2, lz);
res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext);
}
else if (from_ebits > (to_ebits + 2)) {
else if (from_ebits >= (to_ebits + 2)) {
unsigned ebits_diff = from_ebits - (to_ebits + 2);
// subtract lz for subnormal numbers.
@ -2617,9 +2615,6 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r
res_exp = m.mk_ite(ovf_cond, max_exp, res_exp);
res_exp = m.mk_ite(udf_cond, min_exp, res_exp);
}
else { // from_ebits == (to_ebits + 2)
res_exp = m_bv_util.mk_bv_sub(exp, lz);
}
SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits + 2);
SASSERT(is_well_sorted(m, res_exp));
@ -3839,7 +3834,7 @@ void fpa2bv_converter::mk_rounding_mode(decl_kind k, expr_ref & result)
}
void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) {
#ifdef Z3DEBUG
#ifdef Z3DEBUG_FPA2BV_NAMES
return;
// CMW: This works only for quantifier-free formulas.
if (m_util.is_fp(e)) {

View file

@ -61,7 +61,7 @@ protected:
public:
fpa2bv_converter(ast_manager & m);
~fpa2bv_converter();
virtual ~fpa2bv_converter();
fpa_util & fu() { return m_util; }
bv_util & bu() { return m_bv_util; }

View file

@ -207,7 +207,7 @@ sort * fpa_decl_plugin::mk_float_sort(unsigned ebits, unsigned sbits) {
m_manager->raise_exception("minimum number of exponent bits is 2");
if (ebits > 63)
m_manager->raise_exception("maximum number of exponent bits is 63");
parameter p1(ebits), p2(sbits);
parameter ps[2] = { p1, p2 };
sort_size sz;
@ -929,16 +929,22 @@ bool fpa_decl_plugin::is_unique_value(app* e) const {
case OP_FPA_RM_TOWARD_NEGATIVE:
case OP_FPA_RM_TOWARD_ZERO:
return true;
case OP_FPA_PLUS_INF: /* No; +oo == fp(#b0 #b11 #b00) */
case OP_FPA_MINUS_INF: /* No; -oo == fp #b1 #b11 #b00) */
case OP_FPA_PLUS_ZERO: /* No; +zero == fp #b0 #b00 #b000) */
case OP_FPA_MINUS_ZERO: /* No; -zero == fp #b1 #b00 #b000) */
case OP_FPA_PLUS_INF: /* No; +oo == (fp #b0 #b11 #b00) */
case OP_FPA_MINUS_INF: /* No; -oo == (fp #b1 #b11 #b00) */
case OP_FPA_PLUS_ZERO: /* No; +zero == (fp #b0 #b00 #b000) */
case OP_FPA_MINUS_ZERO: /* No; -zero == (fp #b1 #b00 #b000) */
case OP_FPA_NAN: /* No; NaN == (fp #b0 #b111111 #b0000001) */
case OP_FPA_NUM: /* see NaN */
return false;
case OP_FPA_FP:
return false; /*No; generally not because of clashes with +oo, -oo, +zero, -zero, NaN */
// a refinement would require to return true only if there is no clash with these cases.
case OP_FPA_FP: {
if (m_manager->is_value(e->get_arg(0)) &&
m_manager->is_value(e->get_arg(1)) &&
m_manager->is_value(e->get_arg(2))) {
bv_util bu(*m_manager);
return !bu.is_allone(e->get_arg(1)) && !bu.is_zero(e->get_arg(1));
}
return false;
}
default:
return false;
}

View file

@ -23,6 +23,7 @@ Revision History:
class is_variable_proc {
public:
virtual ~is_variable_proc() = default;
virtual bool operator()(const expr* e) const = 0;
};

View file

@ -23,6 +23,7 @@ Notes:
class expr_predicate {
public:
virtual ~expr_predicate() = default;
virtual bool operator()(expr * t) = 0;
};

View file

@ -37,6 +37,8 @@ public:
m_ignore_quantifiers(ignore_quantifiers) {
}
virtual ~num_occurs() = default;
void validate();
virtual void reset() { m_num_occurs.reset(); }

View file

@ -93,6 +93,7 @@ namespace recfun {
// closure for computing whether a `rhs` expression is immediate
struct is_immediate_pred {
virtual bool operator()(expr * rhs) = 0;
virtual ~is_immediate_pred() = default;
};
class def {

View file

@ -703,8 +703,60 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
result = m().update_quantifier(lam, quantifier_kind::forall_k, e);
return BR_REWRITE2;
}
expr_ref lh1(m()), rh1(m());
expr_ref_vector fmls(m());
auto has_large_domain = [&](sort* s, unsigned num_stores) {
unsigned sz = get_array_arity(s);
uint64_t dsz = 1;
for (unsigned i = 0; i < sz; ++i) {
sort* d = get_array_domain(s, i);
if (d->is_infinite() || d->is_very_big())
return true;
auto const& n = d->get_num_elements();
if (n.size() > num_stores)
return true;
dsz *= n.size();
if (dsz > num_stores)
return true;
}
return false;
};
if (m_expand_store_eq) {
expr* lhs1 = lhs;
expr* rhs1 = rhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
fmls.push_back(m().mk_eq(v, w));
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
}
if (m_expand_nested_stores) {
expr_ref lh1(m()), rh1(m());
if (is_expandable_store(lhs)) {
lh1 = expand_store(lhs);
}
@ -719,10 +771,6 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
}
}
if (!m_expand_store_eq) {
return BR_FAILED;
}
expr_ref_vector fmls(m());
#if 0
// lambda friendly version of array equality rewriting.
@ -744,46 +792,5 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
}
#endif
expr* lhs1 = lhs;
unsigned num_lhs = 0, num_rhs = 0;
while (m_util.is_store(lhs1)) {
lhs1 = to_app(lhs1)->get_arg(0);
++num_lhs;
}
expr* rhs1 = rhs;
while (m_util.is_store(rhs1)) {
rhs1 = to_app(rhs1)->get_arg(0);
++num_rhs;
}
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
auto has_large_domain = [&](sort* s, unsigned num_stores) {
unsigned sz = get_array_arity(s);
uint64_t dsz = 1;
for (unsigned i = 0; i < sz; ++i) {
sort* d = get_array_domain(s, i);
if (d->is_infinite() || d->is_very_big())
return true;
auto const& n = d->get_num_elements();
if (n.size() > num_stores)
return true;
dsz *= n.size();
if (dsz > num_stores)
return true;
}
return false;
};
if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
has_large_domain(lhs->get_sort(), std::max(num_lhs, num_rhs))) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
fmls.push_back(m().mk_eq(v, w));
result = m().mk_and(fmls);
return BR_REWRITE_FULL;
}
return BR_FAILED;
}

View file

@ -33,6 +33,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg {
virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args);
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr);
push_app_ite_cfg(ast_manager& m): m(m), m_conservative(true) {}
virtual ~push_app_ite_cfg() = default;
void set_conservative(bool c) { m_conservative = c; }
bool rewrite_patterns() const { return false; }
};

View file

@ -22,10 +22,9 @@ Author:
br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
if (m_rec.is_defined(f) && num_args > 0) {
for (unsigned i = 0; i < num_args; ++i) {
for (unsigned i = 0; i < num_args; ++i)
if (!m.is_value(args[i]))
return BR_FAILED;
}
if (!m_rec.has_def(f))
return BR_FAILED;
recfun::def const& d = m_rec.get_def(f);
@ -35,9 +34,8 @@ br_status recfun_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr *
result = sub(d.get_rhs(), num_args, args);
return BR_REWRITE_FULL;
}
else {
else
return BR_FAILED;
}
}

View file

@ -850,7 +850,7 @@ namespace seq {
add_clause(~eq, ge10k);
for (unsigned i = 0; i < k; ++i) {
expr* ch = seq.str.mk_nth_i(ubvs, i);
expr* ch = seq.str.mk_nth_c(ubvs, i);
is_digit = seq.mk_char_is_digit(ch);
add_clause(~ge_len, is_digit);
}
@ -1142,8 +1142,8 @@ namespace seq {
/**
~contains(a, b) => ~prefix(b, a)
~contains(a, b) => ~contains(tail(a), b) or a = empty
~contains(a, b) & a = empty => b != empty
~contains(a, b) => ~contains(tail(a), b)
a = empty => tail(a) = empty
~(a = empty) => a = head + tail
*/
void axioms::unroll_not_contains(expr* e) {
@ -1165,7 +1165,7 @@ namespace seq {
expr_ref bound_tracker = m_sk.mk_length_limit(s, k);
expr* s0 = nullptr;
if (seq.str.is_stoi(s, s0))
s = s0;
s = s0;
add_clause(~bound_tracker, mk_le(mk_len(s), k));
return bound_tracker;
}

View file

@ -15,6 +15,7 @@ Author:
--*/
#include "ast/ast_pp.h"
#include "ast/rewriter/seq_eq_solver.h"
#include "ast/bv_decl_plugin.h"
@ -675,7 +676,7 @@ namespace seq {
if (rs.size() > i) {
unsigned diff = rs.size() - (i + 1);
for (unsigned j = 0; same && j < i; ++j)
same = !m.are_distinct(ls[j], rs[diff + j]);
same = !m.are_distinct(ls[j], rs[diff + j]);
}
// ls = x ++ rs ++ y, diff = |x|
else {
@ -704,8 +705,9 @@ namespace seq {
bool same = true;
// ls = x ++ rs' && rs = rs' ++ y, diff = |x|
if (rs.size() > i) {
for (unsigned j = 1; same && j <= i; ++j)
same = !m.are_distinct(ls[diff + j], rs[j]);
for (unsigned j = 1; same && j <= i; ++j) {
same = !m.are_distinct(ls[diff + j], rs[j]);
}
}
// ls = x ++ rs ++ y, diff = |x|
else {
@ -715,6 +717,7 @@ namespace seq {
if (same)
return true;
}
return false;
}

View file

@ -39,6 +39,7 @@ namespace seq {
class eq_solver_context {
public:
virtual ~eq_solver_context() = default;
virtual void add_consequence(bool uses_dep, expr_ref_vector const& clause) = 0;
virtual void add_solution(expr* var, expr* term) = 0;
virtual expr* expr2rep(expr* e) = 0;

View file

@ -859,13 +859,12 @@ br_status seq_rewriter::mk_seq_length(expr* a, expr_ref& result) {
// elif offset >= len(s) then 0
// elif offset + length > len(s) then len(s) - offset
// else length
expr_ref zero(m_autil.mk_int(0), m());
result = length;
result = m().mk_ite(m_autil.mk_gt(m_autil.mk_add(offset, length), len_s),
m_autil.mk_sub(len_s, offset),
result);
result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero), m_autil.mk_lt(offset, zero)),
zero,
result = m().mk_ite(m().mk_or(m_autil.mk_le(len_s, offset), m_autil.mk_le(length, zero()), m_autil.mk_lt(offset, zero())),
zero(),
result);
return BR_REWRITE_FULL;
}
@ -883,52 +882,75 @@ expr_ref seq_rewriter::mk_seq_first(expr* t) {
if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, j);
else
result = str().mk_nth_i(t, m_autil.mk_int(0));
result = str().mk_nth_c(t, 0);
return result;
}
expr_ref seq_rewriter::mk_sub(expr* a, rational const& n) {
expr* a1, *a2;
SASSERT(n.is_int());
rational k;
if (m_autil.is_sub(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_sub(a1, m_autil.mk_int(k + n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a2, k))
return expr_ref(m_autil.mk_add(a1, m_autil.mk_int(k - n)), m());
if (m_autil.is_add(a, a1, a2) && m_autil.is_numeral(a1, k))
return expr_ref(m_autil.mk_add(a2, m_autil.mk_int(k - n)), m());
return expr_ref(m_autil.mk_sub(a, m_autil.mk_int(n)), m());
}
/*
* In general constructs substring(t,1,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j+1,k-1)
* This method assumes that |t| > 0.
*/
expr_ref seq_rewriter::mk_seq_rest(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_substr(s, m_autil.mk_add(j, one), m_autil.mk_sub(k, one));
else
result = str().mk_substr(t, one, m_autil.mk_sub(str().mk_length(t), one));
expr* s, * j, * k;
rational jv;
if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, jv) && jv >= 0)
result = str().mk_substr(s, m_autil.mk_int(jv + 1), mk_sub(k, 1));
else
result = str().mk_substr(t, one(), mk_sub(str().mk_length(t), 1));
return result;
}
/*
* In general constructs nth(t,|t|-1) but if t = substring(s,j,k) then simplifies to nth(s,j+k-1)
* In general constructs nth(t,|t|-1) but if t = substring(s,j,|s|-j) j >= 0, then simplifies to nth(s,|s|-1)
* This method assumes that |t| > 0.
*/
expr_ref seq_rewriter::mk_seq_last(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_nth_i(s, m_autil.mk_sub(m_autil.mk_add(j, k), one));
expr* s, * j, * k, * s_, * len_s;
rational jv, i;
if (str().is_extract(t, s, j, k) &&
m_autil.is_numeral(j, jv) && jv >= 0 &&
str().is_len_sub(k, len_s, s_, i) &&
s == s_ && jv == i) {
expr_ref lastpos = mk_sub(len_s, 1);
result = str().mk_nth_i(s, lastpos);
}
else
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one));
result = str().mk_nth_i(t, m_autil.mk_sub(str().mk_length(t), one()));
return result;
}
/*
* In general constructs substring(t,0,|t|-1) but if t = substring(s,j,k) then simplifies to substring(s,j,k-1)
* This method assumes that |t| > 0 holds.
* In general constructs substring(t,0,|t|-1) but if t = substring(s,0,k) then simplifies to substring(s,0,k-1)
* This method assumes that |t| > 0, thus, if t = substring(s,0,k) then k > 0 so substring(s,0,k-1) is correct.
*/
expr_ref seq_rewriter::mk_seq_butlast(expr* t) {
expr_ref result(m());
expr* s, * j, * k;
expr_ref one(m_autil.mk_int(1), m());
if (str().is_extract(t, s, j, k))
result = str().mk_substr(s, j, m_autil.mk_sub(k, one));
rational v;
if (str().is_extract(t, s, j, k) && m_autil.is_numeral(j, v) && v.is_zero()) {
expr_ref_vector k_min_1(m());
k_min_1.push_back(k);
k_min_1.push_back(minus_one());
result = str().mk_substr(s, j, m_autil.mk_add_simplify(k_min_1));
}
else
result = str().mk_substr(t, m_autil.mk_int(0), m_autil.mk_sub(str().mk_length(t), one));
result = str().mk_substr(t, zero(), m_autil.mk_sub(str().mk_length(t), one()));
return result;
}
@ -1582,23 +1604,33 @@ br_status seq_rewriter::mk_seq_nth(expr* a, expr* b, expr_ref& result) {
result = s;
return BR_DONE;
}
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1)) {
if (str().is_extract(a, s, p, len) && m_autil.is_numeral(p, pos1) && pos1 > 0) {
expr_ref_vector lens(m());
rational pos2;
/*
* nth(s[k, |s| - k], b) =
* b < 0 -> nth_u(a, b)
* b + k < |s| -> nth(s, b + k)
* k >= |s| -> nth_u(empty, b)
* k < |s| <= b + k -> nth_u(a, b)
*/
if (get_lengths(len, lens, pos2) && (pos1 == -pos2) && (lens.size() == 1) && (lens.get(0) == s)) {
expr_ref idx(m_autil.mk_int(pos1), m());
idx = m_autil.mk_add(b, idx);
expr* es[2] = { s, idx };
result = m().mk_app(m_util.get_family_id(), OP_SEQ_NTH, 2, es);
expr_ref k(m_autil.mk_int(pos1), m());
expr_ref case1(str().mk_nth_i(s, m_autil.mk_add(b, k)), m());
expr_ref case2(str().mk_nth_u(str().mk_empty(s->get_sort()), b), m());
expr_ref case3(str().mk_nth_u(a, b), m());
result = case3;
result = m().mk_ite(m_autil.mk_lt(m_autil.mk_add(k, b), str().mk_length(s)), case1, result);
result = m().mk_ite(m_autil.mk_ge(k, str().mk_length(s)), case2, result);
result = m().mk_ite(m_autil.mk_lt(b, zero()), case3, result);
return BR_REWRITE_FULL;
}
}
expr* es[2] = { a, b};
expr* la = str().mk_length(a);
result = m().mk_ite(m().mk_and(m_autil.mk_ge(b, zero()), m().mk_not(m_autil.mk_le(la, b))),
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_I, 2, es),
m().mk_app(m_util.get_family_id(), OP_SEQ_NTH_U, 2, es));
str().mk_nth_i(a, b),
str().mk_nth_u(a, b));
return BR_REWRITE_FULL;
}
@ -1678,7 +1710,7 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
return BR_DONE;
}
if (m_autil.is_numeral(c, r) && r.is_neg()) {
result = m_autil.mk_int(-1);
result = minus_one();
return BR_DONE;
}
@ -1688,10 +1720,10 @@ br_status seq_rewriter::mk_seq_index(expr* a, expr* b, expr* c, expr_ref& result
}
if (str().is_empty(b)) {
result = m().mk_ite(m().mk_and(m_autil.mk_le(m_autil.mk_int(0), c),
result = m().mk_ite(m().mk_and(m_autil.mk_le(zero(), c),
m_autil.mk_le(c, str().mk_length(a))),
c,
m_autil.mk_int(-1));
minus_one());
return BR_REWRITE2;
}
@ -2307,7 +2339,7 @@ br_status seq_rewriter::mk_str_to_code(expr* a, expr_ref& result) {
if (s.length() == 1)
result = m_autil.mk_int(s[0]);
else
result = m_autil.mk_int(-1);
result = minus_one();
return BR_DONE;
}
return BR_FAILED;
@ -2448,7 +2480,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
result = m_autil.mk_int(ch - '0');
}
else {
result = m_autil.mk_int(-1);
result = minus_one();
}
return BR_DONE;
}
@ -2456,7 +2488,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
expr_ref_vector as(m());
str().get_concat_units(a, as);
if (as.empty()) {
result = m_autil.mk_int(-1);
result = minus_one();
return BR_DONE;
}
if (str().is_unit(as.back())) {
@ -2466,11 +2498,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
expr_ref tail(str().mk_stoi(as.back()), m());
expr_ref head(str().mk_concat(as.size() - 1, as.data(), a->get_sort()), m());
expr_ref stoi_head(str().mk_stoi(head), m());
result = m().mk_ite(m_autil.mk_ge(stoi_head, m_autil.mk_int(0)),
result = m().mk_ite(m_autil.mk_ge(stoi_head, zero()),
m_autil.mk_add(m_autil.mk_mul(m_autil.mk_int(10), stoi_head), tail),
m_autil.mk_int(-1));
minus_one());
result = m().mk_ite(m_autil.mk_ge(tail, m_autil.mk_int(0)),
result = m().mk_ite(m_autil.mk_ge(tail, zero()),
result,
tail);
result = m().mk_ite(str().mk_is_empty(head),
@ -2481,7 +2513,7 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) {
if (str().is_unit(as.get(0), u) && m_util.is_const_char(u, ch) && '0' == ch) {
result = str().mk_concat(as.size() - 1, as.data() + 1, as[0]->get_sort());
result = m().mk_ite(str().mk_is_empty(result),
m_autil.mk_int(0),
zero(),
str().mk_stoi(result));
return BR_REWRITE_FULL;
}
@ -2573,7 +2605,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) {
}
/*
s = head + tail where |head| = 1
s = [head] + tail where head is the first element of s
*/
bool seq_rewriter::get_head_tail(expr* s, expr_ref& head, expr_ref& tail) {
expr* h = nullptr, *t = nullptr;
@ -2670,10 +2702,10 @@ expr_ref seq_rewriter::re_predicate(expr* cond, sort* seq_sort) {
expr_ref seq_rewriter::is_nullable(expr* r) {
STRACE("seq_verbose", tout << "is_nullable: "
<< mk_pp(r, m()) << std::endl;);
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr), m());
expr_ref result(m_op_cache.find(_OP_RE_IS_NULLABLE, r, nullptr, nullptr), m());
if (!result) {
result = is_nullable_rec(r);
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, result);
m_op_cache.insert(_OP_RE_IS_NULLABLE, r, nullptr, nullptr, result);
}
STRACE("seq_verbose", tout << "is_nullable result: "
<< result << std::endl;);
@ -2691,7 +2723,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
re().is_intersection(r, r1, r2)) {
m_br.mk_and(is_nullable(r1), is_nullable(r2), result);
}
else if (re().is_union(r, r1, r2)) {
else if (re().is_union(r, r1, r2) || re().is_antimorov_union(r, r1, r2)) {
m_br.mk_or(is_nullable(r1), is_nullable(r2), result);
}
else if (re().is_diff(r, r1, r2)) {
@ -2701,6 +2733,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
else if (re().is_star(r) ||
re().is_opt(r) ||
re().is_full_seq(r) ||
re().is_epsilon(r) ||
(re().is_loop(r, r1, lo) && lo == 0) ||
(re().is_loop(r, r1, lo, hi) && lo == 0)) {
result = m().mk_true();
@ -2724,7 +2757,7 @@ expr_ref seq_rewriter::is_nullable_rec(expr* r) {
result = is_nullable(r1);
}
else if (m().is_ite(r, cond, r1, r2)) {
result = m().mk_ite(cond, is_nullable(r1), is_nullable(r2));
m_br.mk_ite(cond, is_nullable(r1), is_nullable(r2), result);
}
else if (m_util.is_re(r, seq_sort)) {
result = is_nullable_symbolic_regex(r, seq_sort);
@ -2881,7 +2914,8 @@ br_status seq_rewriter::mk_re_reverse(expr* r, expr_ref& result) {
br_status seq_rewriter::mk_re_derivative(expr* ele, expr* r, expr_ref& result) {
result = mk_derivative(ele, r);
// TBD: we may even declare BR_DONE here and potentially miss some simplifications
return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
// return re().is_derivative(result) ? BR_DONE : BR_REWRITE_FULL;
return BR_DONE;
}
/*
@ -2976,29 +3010,406 @@ bool seq_rewriter::check_deriv_normal_form(expr* r, int level) {
}
#endif
/*
Memoized, recursive implementation of the symbolic derivative such that
the result is in normal form.
expr_ref seq_rewriter::mk_derivative(expr* r) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
expr_ref v(m().mk_var(0, ele_sort), m());
return mk_antimirov_deriv(v, r, m().mk_true());
}
Functions without _rec are memoized wrappers, which call the _rec
version if lookup fails.
The main logic is in mk_der_op_rec for combining normal forms.
*/
expr_ref seq_rewriter::mk_derivative(expr* ele, expr* r) {
STRACE("seq_verbose", tout << "derivative: " << mk_pp(ele, m())
<< "," << mk_pp(r, m()) << std::endl;);
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, ele, r), m());
return mk_antimirov_deriv(ele, r, m().mk_true());
}
expr_ref seq_rewriter::mk_antimirov_deriv(expr* e, expr* r, expr* path) {
// Ensure references are owned
expr_ref _e(e, m()), _path(path, m()), _r(r, m());
expr_ref result(m_op_cache.find(OP_RE_DERIVATIVE, e, r, path), m());
if (!result) {
result = mk_derivative_rec(ele, r);
m_op_cache.insert(OP_RE_DERIVATIVE, ele, r, result);
mk_antimirov_deriv_rec(e, r, path, result);
m_op_cache.insert(OP_RE_DERIVATIVE, e, r, path, result);
STRACE("seq_regex", tout << "D(" << mk_pp(e, m()) << "," << mk_pp(r, m()) << "," << mk_pp(path, m()) << ")" << std::endl;);
STRACE("seq_regex", tout << "= " << mk_pp(result, m()) << std::endl;);
}
STRACE("seq_verbose", tout << "derivative result: "
<< mk_pp(result, m()) << std::endl;);
CASSERT("seq_regex", check_deriv_normal_form(r));
return result;
}
void seq_rewriter::mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
SASSERT(ele_sort == e->get_sort());
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
expr_ref c1(m());
expr_ref c2(m());
auto nothing = [&]() { return expr_ref(re().mk_empty(r->get_sort()), m()); };
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(r->get_sort()), m()); };
unsigned lo = 0, hi = 0;
if (re().is_empty(r) || re().is_epsilon(r))
// D(e,[]) = D(e,()) = []
result = nothing();
else if (re().is_full_seq(r) || re().is_dot_plus(r))
// D(e,.*) = D(e,.+) = .*
result = dotstar();
else if (re().is_full_char(r))
// D(e,.) = ()
result = epsilon();
else if (re().is_to_re(r, r1)) {
expr_ref h(m());
expr_ref t(m());
// here r1 is a sequence
if (get_head_tail(r1, h, t)) {
if (eq_char(e, h))
result = re().mk_to_re(t);
else if (neq_char(e, h))
result = nothing();
else
result = re().mk_ite_simplify(m().mk_eq(e, h), re().mk_to_re(t), nothing());
}
else {
// observe that the precondition |r1|>0 is is implied by c1 for use of mk_seq_first
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_first(r1), e), c1);
m_br.mk_and(path, c1, c2);
if (m().is_false(c2))
result = nothing();
else
// observe that the precondition |r1|>0 is implied by c1 for use of mk_seq_rest
result = m().mk_ite(c1, re().mk_to_re(mk_seq_rest(r1)), nothing());
}
}
else if (re().is_reverse(r, r2))
if (re().is_to_re(r2, r1)) {
// here r1 is a sequence
// observe that the precondition |r1|>0 of mk_seq_last is implied by c1
m_br.mk_and(m().mk_not(m().mk_eq(r1, str().mk_empty(seq_sort))), m().mk_eq(mk_seq_last(r1), e), c1);
m_br.mk_and(path, c1, c2);
if (m().is_false(c2))
result = nothing();
else
// observe that the precondition |r1|>0 of mk_seq_rest is implied by c1
result = re().mk_ite_simplify(c1, re().mk_reverse(re().mk_to_re(mk_seq_butlast(r1))), nothing());
}
else {
result = mk_regex_reverse(r2);
if (result.get() == r)
//r2 is an uninterpreted regex that is stuck
//for example if r = (re.reverse R) where R is a regex variable then
//here result.get() == r
result = re().mk_derivative(e, result);
else
result = mk_antimirov_deriv(e, result, path);
}
else if (re().is_concat(r, r1, r2)) {
expr_ref r1nullable(is_nullable(r1), m());
c1 = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), r2);
expr_ref r1nullable_and_path(m());
m_br.mk_and(r1nullable, path, r1nullable_and_path);
if (m().is_false(r1nullable_and_path))
// D(e,r1)r2
result = c1;
else
// D(e,r1)r2|(ite (r1nullable) (D(e,r2)) [])
// observe that (mk_ite_simplify(true, D(e,r2), []) = D(e,r2)
result = mk_antimirov_deriv_union(c1, re().mk_ite_simplify(r1nullable, mk_antimirov_deriv(e, r2, path), nothing()));
}
else if (m().is_ite(r, c, r1, r2)) {
c1 = simplify_path(m().mk_and(c, path));
c2 = simplify_path(m().mk_and(m().mk_not(c), path));
if (m().is_false(c1))
result = mk_antimirov_deriv(e, r2, c2);
else if (m().is_false(c2))
result = mk_antimirov_deriv(e, r1, c1);
else
result = re().mk_ite_simplify(c, mk_antimirov_deriv(e, r1, c1), mk_antimirov_deriv(e, r2, c2));
}
else if (re().is_range(r, r1, r2)) {
expr_ref range(m());
expr_ref psi(m().mk_false(), m());
if (str().is_unit_string(r1, c1) && str().is_unit_string(r2, c2)) {
SASSERT(u().is_char(c1));
SASSERT(u().is_char(c2));
// case: c1 <= e <= c2
range = simplify_path(m().mk_and(u().mk_le(c1, e), u().mk_le(e, c2)));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r1) && str().is_unit_string(r2, c2)) {
SASSERT(u().is_char(c2));
// r1 nonground: |r1|=1 & r1[0] <= e <= c2
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, c2))));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r2) && str().is_unit_string(r1, c1)) {
SASSERT(u().is_char(c1));
// r2 nonground: |r2|=1 & c1 <= e <= r2_0
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
range = simplify_path(m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(c1, e), u().mk_le(e, r2_0))));
psi = simplify_path(m().mk_and(path, range));
}
else if (!str().is_string(r1) && !str().is_string(r2)) {
// both r1 and r2 nonground: |r1|=1 & |r2|=1 & r1[0] <= e <= r2[0]
expr_ref one(m_autil.mk_int(1), m());
expr_ref zero(m_autil.mk_int(0), m());
expr_ref r1_length_eq_one(m().mk_eq(str().mk_length(r1), one), m());
expr_ref r1_0(str().mk_nth_i(r1, zero), m());
expr_ref r2_length_eq_one(m().mk_eq(str().mk_length(r2), one), m());
expr_ref r2_0(str().mk_nth_i(r2, zero), m());
range = simplify_path(m().mk_and(r1_length_eq_one, m().mk_and(r2_length_eq_one, m().mk_and(u().mk_le(r1_0, e), u().mk_le(e, r2_0)))));
psi = simplify_path(m().mk_and(path, range));
}
if (m().is_false(psi))
result = nothing();
else
result = re().mk_ite_simplify(range, epsilon(), nothing());
}
else if (re().is_union(r, r1, r2))
result = mk_antimirov_deriv_union(mk_antimirov_deriv(e, r1, path), mk_antimirov_deriv(e, r2, path));
else if (re().is_intersection(r, r1, r2))
result = mk_antimirov_deriv_intersection(
mk_antimirov_deriv(e, r1, path),
mk_antimirov_deriv(e, r2, path), m().mk_true());
else if (re().is_star(r, r1) || re().is_plus(r, r1) || (re().is_loop(r, r1, lo) && 0 <= lo && lo <= 1))
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_star(r1));
else if (re().is_loop(r, r1, lo))
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, lo - 1));
else if (re().is_loop(r, r1, lo, hi)) {
if ((lo == 0 && hi == 0) || hi < lo)
result = nothing();
else
result = mk_antimirov_deriv_concat(mk_antimirov_deriv(e, r1, path), re().mk_loop(r1, (lo == 0 ? 0 : lo - 1), hi - 1));
}
else if (re().is_opt(r, r1))
result = mk_antimirov_deriv(e, r1, path);
else if (re().is_complement(r, r1))
// D(e,~r1) = ~D(e,r1)
result = mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r1, path));
else if (re().is_diff(r, r1, r2))
result = mk_antimirov_deriv_intersection(
mk_antimirov_deriv(e, r1, path),
mk_antimirov_deriv_negate(mk_antimirov_deriv(e, r2, path)), m().mk_true());
else if (re().is_of_pred(r, r1)) {
array_util array(m());
expr* args[2] = { r1, e };
result = array.mk_select(2, args);
// Use mk_der_cond to normalize
result = mk_der_cond(result, e, seq_sort);
}
else
// stuck cases
result = re().mk_derivative(e, r);
}
expr_ref seq_rewriter::mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path) {
sort* seq_sort = nullptr, * ele_sort = nullptr;
VERIFY(m_util.is_re(d1, seq_sort));
VERIFY(m_util.is_seq(seq_sort, ele_sort));
expr_ref result(m());
expr* c, * a, * b;
if (d1 == d2 || re().is_full_seq(d2) || re().is_empty(d1))
result = d1;
else if (re().is_full_seq(d1) || re().is_empty(d2))
result = d2;
else if (m().is_ite(d1, c, a, b)) {
expr_ref path_and_c(simplify_path(m().mk_and(path, c)), m());
expr_ref path_and_notc(simplify_path(m().mk_and(path, m().mk_not(c))), m());
if (m().is_false(path_and_c))
result = mk_antimirov_deriv_intersection(b, d2, path);
else if (m().is_false(path_and_notc))
result = mk_antimirov_deriv_intersection(a, d2, path);
else
result = m().mk_ite(c, mk_antimirov_deriv_intersection(a, d2, path_and_c),
mk_antimirov_deriv_intersection(b, d2, path_and_notc));
}
else if (m().is_ite(d2))
// swap d1 and d2
result = mk_antimirov_deriv_intersection(d2, d1, path);
else if (re().is_union(d1, a, b))
// distribute intersection over the union in d1
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(a, d2, path), mk_antimirov_deriv_intersection(b, d2, path));
else if (re().is_union(d2, a, b))
// distribute intersection over the union in d2
result = mk_antimirov_deriv_union(mk_antimirov_deriv_intersection(d1, a, path), mk_antimirov_deriv_intersection(d1, b, path));
else
// in all other cases create the intersection regex
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
result = (d1->get_id() <= d2->get_id() ? re().mk_inter(d1, d2) : re().mk_inter(d2, d1));
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_concat(expr* d, expr* r) {
expr_ref result(m());
// Take reference count of r and d
expr_ref _r(r, m()), _d(d, m());
expr* c, * t, * e;
if (m().is_ite(d, c, t, e))
result = m().mk_ite(c, mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else if (re().is_union(d, t, e))
result = re().mk_union(mk_antimirov_deriv_concat(t, r), mk_antimirov_deriv_concat(e, r));
else
result = mk_re_append(d, r);
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_negate(expr* d) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(d, seq_sort));
auto nothing = [&]() { return expr_ref(re().mk_empty(d->get_sort()), m()); };
auto epsilon = [&]() { return expr_ref(re().mk_epsilon(seq_sort), m()); };
auto dotstar = [&]() { return expr_ref(re().mk_full_seq(d->get_sort()), m()); };
auto dotplus = [&]() { return expr_ref(re().mk_plus(re().mk_full_char(d->get_sort())), m()); };
expr_ref result(m());
expr* c, * t, * e;
if (re().is_empty(d))
result = dotstar();
else if (re().is_epsilon(d))
result = dotplus();
else if (re().is_full_seq(d))
result = nothing();
else if (re().is_dot_plus(d))
result = epsilon();
else if (m().is_ite(d, c, t, e))
result = m().mk_ite(c, mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_union(d, t, e))
result = re().mk_inter(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_intersection(d, t, e))
result = re().mk_union(mk_antimirov_deriv_negate(t), mk_antimirov_deriv_negate(e));
else if (re().is_complement(d, t))
result = t;
else
result = re().mk_complement(d);
return result;
}
expr_ref seq_rewriter::mk_antimirov_deriv_union(expr* d1, expr* d2) {
expr_ref result(m());
if (re().is_empty(d1) || re().is_full_seq(d2))
result = d2;
else if (re().is_empty(d2) || re().is_full_seq(d1))
result = d1;
else if (re().is_dot_plus(d1) && re().get_info(d2).min_length > 0)
result = d1;
else if (re().is_dot_plus(d2) && re().get_info(d1).min_length > 0)
result = d2;
else
// TODO: flatten, order and merge d1 and d2 to maintain equality under similarity
result = (d1->get_id() <= d2->get_id() ? re().mk_union(d1, d2) : re().mk_union(d2, d1));
return result;
}
expr_ref seq_rewriter::mk_regex_reverse(expr* r) {
expr* r1 = nullptr, * r2 = nullptr, * c = nullptr;
unsigned lo = 0, hi = 0;
expr_ref result(m());
if (re().is_empty(r) || re().is_range(r) || re().is_epsilon(r) || re().is_full_seq(r) ||
re().is_full_char(r) || re().is_dot_plus(r) || re().is_of_pred(r))
result = r;
else if (re().is_to_re(r))
result = re().mk_reverse(r);
else if (re().is_reverse(r, r1))
result = r1;
else if (re().is_concat(r, r1, r2))
result = mk_regex_concat(mk_regex_reverse(r2), mk_regex_reverse(r1));
else if (m().is_ite(r, c, r1, r2))
result = m().mk_ite(c, mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_union(r, r1, r2))
result = re().mk_union(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_intersection(r, r1, r2))
result = re().mk_inter(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_diff(r, r1, r2))
result = re().mk_diff(mk_regex_reverse(r1), mk_regex_reverse(r2));
else if (re().is_star(r, r1))
result = re().mk_star(mk_regex_reverse(r1));
else if (re().is_plus(r, r1))
result = re().mk_plus(mk_regex_reverse(r1));
else if (re().is_loop(r, r1, lo))
result = re().mk_loop(mk_regex_reverse(r1), lo);
else if (re().is_loop(r, r1, lo, hi))
result = re().mk_loop(mk_regex_reverse(r1), lo, hi);
else if (re().is_opt(r, r1))
result = re().mk_opt(mk_regex_reverse(r1));
else if (re().is_complement(r, r1))
result = re().mk_complement(mk_regex_reverse(r1));
else
//stuck cases: such as r being a regex variable
//observe that re().mk_reverse(to_re(s)) is not a stuck case
result = re().mk_reverse(r);
return result;
}
expr_ref seq_rewriter::mk_regex_concat(expr* r, expr* s) {
sort* seq_sort = nullptr;
VERIFY(m_util.is_re(r, seq_sort));
SASSERT(r->get_sort() == s->get_sort());
expr_ref result(m());
expr* r1, * r2;
if (re().is_epsilon(r) || re().is_empty(s))
result = s;
else if (re().is_epsilon(s) || re().is_empty(r))
result = r;
else if (re().is_full_seq(r) && re().is_full_seq(s))
result = r;
else if (re().is_concat(r, r1, r2))
//create the resulting concatenation in right-associative form
result = mk_regex_concat(r1, mk_regex_concat(r2, s));
else {
//TODO: perhaps simplifiy some further cases such as .*. = ..* = .*.+ = .+.* = .+
result = re().mk_concat(r, s);
}
return result;
}
expr_ref seq_rewriter::mk_in_antimirov(expr* s, expr* d){
expr_ref result(mk_in_antimirov_rec(s, d), m());
return result;
}
expr_ref seq_rewriter::mk_in_antimirov_rec(expr* s, expr* d) {
expr* c, * d1, * d2;
expr_ref result(m());
if (re().is_full_seq(d) || (str().min_length(s) > 0 && re().is_dot_plus(d)))
// s in .* <==> true, also: s in .+ <==> true when |s|>0
result = m().mk_true();
else if (re().is_empty(d) || (str().min_length(s) > 0 && re().is_epsilon(d)))
// s in [] <==> false, also: s in () <==> false when |s|>0
result = m().mk_false();
else if (m().is_ite(d, c, d1, d2))
result = re().mk_ite_simplify(c, mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2));
else if (re().is_union(d, d1, d2))
m_br.mk_or(mk_in_antimirov_rec(s, d1), mk_in_antimirov_rec(s, d2), result);
else
result = re().mk_in_re(s, d);
return result;
}
/*
path is typically a conjunction of (negated) character equations or constraints that can potentially be simplified
the first element of each equation is assumed to be the element parameter, for example x = (:var 0),
for example a constraint x='a' & x='b' is simplified to false
*/
expr_ref seq_rewriter::simplify_path(expr* path) {
//TODO: more systematic simplifications
expr_ref result(path, m());
expr* h = nullptr, * t = nullptr, * lhs = nullptr, * rhs = nullptr, * h1 = nullptr;
if (m().is_and(path, h, t)) {
if (m().is_true(h))
result = simplify_path(t);
else if (m().is_true(t))
result = simplify_path(h);
else if (m().is_eq(h, lhs, rhs) || (m().is_not(h, h1) && m().is_eq(h1, lhs, rhs)))
elim_condition(lhs, result);
}
return result;
}
expr_ref seq_rewriter::mk_der_antimorov_union(expr* r1, expr* r2) {
return mk_der_op(_OP_RE_ANTIMOROV_UNION, r1, r2);
}
@ -3016,7 +3427,7 @@ expr_ref seq_rewriter::mk_der_concat(expr* r1, expr* r2) {
}
/*
Utility functions to decide char <, ==, and <=.
Utility functions to decide char <, ==, !=, and <=.
Return true if deduced, false if unknown.
*/
bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
@ -3027,6 +3438,11 @@ bool seq_rewriter::lt_char(expr* ch1, expr* ch2) {
bool seq_rewriter::eq_char(expr* ch1, expr* ch2) {
return ch1 == ch2;
}
bool seq_rewriter::neq_char(expr* ch1, expr* ch2) {
unsigned u1, u2;
return u().is_const_char(ch1, u1) &&
u().is_const_char(ch2, u2) && (u1 != u2);
}
bool seq_rewriter::le_char(expr* ch1, expr* ch2) {
return eq_char(ch1, ch2) || lt_char(ch1, ch2);
}
@ -3257,10 +3673,10 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
default:
break;
}
result = m_op_cache.find(k, a, b);
result = m_op_cache.find(k, a, b, nullptr);
if (!result) {
result = mk_der_op_rec(k, a, b);
m_op_cache.insert(k, a, b, result);
m_op_cache.insert(k, a, b, nullptr, result);
}
CASSERT("seq_regex", check_deriv_normal_form(result));
return result;
@ -3269,7 +3685,7 @@ expr_ref seq_rewriter::mk_der_op(decl_kind k, expr* a, expr* b) {
expr_ref seq_rewriter::mk_der_compl(expr* r) {
STRACE("seq_verbose", tout << "mk_der_compl: " << mk_pp(r, m())
<< std::endl;);
expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr), m());
expr_ref result(m_op_cache.find(OP_RE_COMPLEMENT, r, nullptr, nullptr), m());
if (!result) {
expr* c = nullptr, * r1 = nullptr, * r2 = nullptr;
if (re().is_antimorov_union(r, r1, r2)) {
@ -3285,7 +3701,7 @@ expr_ref seq_rewriter::mk_der_compl(expr* r) {
}
else if (BR_FAILED == mk_re_complement(r, result))
result = re().mk_complement(r);
m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, result);
m_op_cache.insert(OP_RE_COMPLEMENT, r, nullptr, nullptr, result);
}
CASSERT("seq_regex", check_deriv_normal_form(result));
return result;
@ -3509,7 +3925,7 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
// construct the term (if (r2 != () and (ele = (last r2)) then reverse(to_re (butlast r2)) else []))
// hd = first of reverse(r2) i.e. last of r2
// tl = rest of reverse(r2) i.e. butlast of r2
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), m_autil.mk_int(1)));
//hd = str().mk_nth_i(r2, m_autil.mk_sub(str().mk_length(r2), one()));
hd = mk_seq_last(r2);
m_br.mk_and(m().mk_not(m().mk_eq(r2, str().mk_empty(seq_sort))), m().mk_eq(hd, ele), result);
tl = re().mk_to_re(mk_seq_butlast(r2));
@ -3537,9 +3953,9 @@ expr_ref seq_rewriter::mk_derivative_rec(expr* ele, expr* r) {
return mk_empty();
}
}
expr* e1 = nullptr, *e2 = nullptr;
expr* e1 = nullptr, * e2 = nullptr;
if (str().is_unit(r1, e1) && str().is_unit(r2, e2)) {
SASSERT(u().is_char(e1));
SASSERT(u().is_char(e1));
// Use mk_der_cond to normalize
STRACE("seq_verbose", tout << "deriv range str" << std::endl;);
expr_ref p1(u().mk_le(e1, ele), m());
@ -3760,7 +4176,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
(re().is_union(b, b1, eps) && re().is_epsilon(eps)) ||
(re().is_union(b, eps, b1) && re().is_epsilon(eps)))
{
result = m().mk_ite(m().mk_eq(str().mk_length(a), m_autil.mk_int(0)),
result = m().mk_ite(m().mk_eq(str().mk_length(a), zero()),
m().mk_true(),
re().mk_in_re(a, b1));
return BR_REWRITE_FULL;
@ -3775,8 +4191,10 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref hd(m()), tl(m());
if (get_head_tail(a, hd, tl)) {
result = re().mk_in_re(tl, re().mk_derivative(hd, b));
return BR_REWRITE2;
//result = re().mk_in_re(tl, re().mk_derivative(hd, b));
//result = re().mk_in_re(tl, mk_derivative(hd, b));
result = mk_in_antimirov(tl, mk_antimirov_deriv(hd, b, m().mk_true()));
return BR_REWRITE_FULL;
}
if (get_head_tail_reversed(a, hd, tl)) {
@ -3791,7 +4209,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref len_a(str().mk_length(a), m());
expr_ref len_tl(m_autil.mk_sub(len_a, len_hd), m());
result = m().mk_and(m_autil.mk_ge(len_a, len_hd),
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl));
return BR_REWRITE_FULL;
}
@ -3802,7 +4220,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) {
expr_ref len_hd(m_autil.mk_sub(len_a, len_tl), m());
expr* s = nullptr;
result = m().mk_and(m_autil.mk_ge(len_a, len_tl),
re().mk_in_re(str().mk_substr(a, m_autil.mk_int(0), len_hd), hd),
re().mk_in_re(str().mk_substr(a, zero(), len_hd), hd),
(re().is_to_re(tl, s) ? m().mk_eq(s, str().mk_substr(a, len_hd, len_tl)) :
re().mk_in_re(str().mk_substr(a, len_hd, len_tl), tl)));
return BR_REWRITE_FULL;
@ -3912,6 +4330,10 @@ br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) {
return BR_REWRITE2;
}
expr* a1 = nullptr, *b1 = nullptr;
if (re().is_to_re(a, a1) && re().is_to_re(b, b1)) {
result = re().mk_to_re(str().mk_concat(a1, b1));
return BR_DONE;
}
if (re().is_star(a, a1) && re().is_star(b, b1) && a1 == b1) {
result = a;
return BR_DONE;
@ -5151,15 +5573,15 @@ bool seq_rewriter::reduce_eq_empty(expr* l, expr* r, expr_ref& result) {
if (str().is_extract(r, s, offset, len)) {
expr_ref len_s(str().mk_length(s), m());
expr_ref_vector fmls(m());
fmls.push_back(m_autil.mk_lt(offset, m_autil.mk_int(0)));
fmls.push_back(m_autil.mk_lt(offset, zero()));
fmls.push_back(m().mk_eq(s, l));
fmls.push_back(m_autil.mk_le(len, m_autil.mk_int(0)));
fmls.push_back(m_autil.mk_le(len, zero()));
fmls.push_back(m_autil.mk_le(len_s, offset));
result = m().mk_or(fmls);
return true;
}
if (str().is_itos(r, s)) {
result = m_autil.mk_lt(s, m_autil.mk_int(0));
result = m_autil.mk_lt(s, zero());
return true;
}
return false;
@ -5275,19 +5697,20 @@ seq_rewriter::op_cache::op_cache(ast_manager& m):
m_trail(m)
{}
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b) {
op_entry e(op, a, b, nullptr);
expr* seq_rewriter::op_cache::find(decl_kind op, expr* a, expr* b, expr* c) {
op_entry e(op, a, b, c, nullptr);
m_table.find(e, e);
return e.r;
}
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* r) {
void seq_rewriter::op_cache::insert(decl_kind op, expr* a, expr* b, expr* c, expr* r) {
cleanup();
if (a) m_trail.push_back(a);
if (b) m_trail.push_back(b);
if (c) m_trail.push_back(c);
if (r) m_trail.push_back(r);
m_table.insert(op_entry(op, a, b, r));
m_table.insert(op_entry(op, a, b, c, r));
}
void seq_rewriter::op_cache::cleanup() {

View file

@ -117,20 +117,20 @@ class seq_rewriter {
class op_cache {
struct op_entry {
decl_kind k;
expr* a, *b, *r;
op_entry(decl_kind k, expr* a, expr* b, expr* r): k(k), a(a), b(b), r(r) {}
op_entry():k(0), a(nullptr), b(nullptr), r(nullptr) {}
expr* a, *b, *c, *r;
op_entry(decl_kind k, expr* a, expr* b, expr* c, expr* r): k(k), a(a), b(b), c(c), r(r) {}
op_entry():k(0), a(nullptr), b(nullptr), c(nullptr), r(nullptr) {}
};
struct hash_entry {
unsigned operator()(op_entry const& e) const {
return mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0);
return combine_hash(mk_mix(e.k, e.a ? e.a->get_id() : 0, e.b ? e.b->get_id() : 0), e.c ? e.c->get_id() : 0);
}
};
struct eq_entry {
bool operator()(op_entry const& a, op_entry const& b) const {
return a.k == b.k && a.a == b.a && a.b == b.b;
bool operator()(op_entry const& a, op_entry const& b) const {
return a.k == b.k && a.a == b.a && a.b == b.b && a.c == b.c;
}
};
@ -143,8 +143,8 @@ class seq_rewriter {
public:
op_cache(ast_manager& m);
expr* find(decl_kind op, expr* a, expr* b);
void insert(decl_kind op, expr* a, expr* b, expr* r);
expr* find(decl_kind op, expr* a, expr* b, expr* c);
void insert(decl_kind op, expr* a, expr* b, expr* c, expr* r);
};
seq_util m_util;
@ -208,8 +208,24 @@ class seq_rewriter {
bool check_deriv_normal_form(expr* r, int level = 3);
#endif
void mk_antimirov_deriv_rec(expr* e, expr* r, expr* path, expr_ref& result);
expr_ref mk_antimirov_deriv(expr* e, expr* r, expr* path);
expr_ref mk_in_antimirov_rec(expr* s, expr* d);
expr_ref mk_in_antimirov(expr* s, expr* d);
expr_ref mk_antimirov_deriv_intersection(expr* d1, expr* d2, expr* path);
expr_ref mk_antimirov_deriv_concat(expr* d, expr* r);
expr_ref mk_antimirov_deriv_negate(expr* d);
expr_ref mk_antimirov_deriv_union(expr* d1, expr* d2);
expr_ref mk_regex_reverse(expr* r);
expr_ref mk_regex_concat(expr* r1, expr* r2);
expr_ref simplify_path(expr* path);
bool lt_char(expr* ch1, expr* ch2);
bool eq_char(expr* ch1, expr* ch2);
bool neq_char(expr* ch1, expr* ch2);
bool le_char(expr* ch1, expr* ch2);
bool pred_implies(expr* a, expr* b);
bool are_complements(expr* r1, expr* r2) const;
@ -286,6 +302,8 @@ class seq_rewriter {
expr_ref zero() { return expr_ref(m_autil.mk_int(0), m()); }
expr_ref one() { return expr_ref(m_autil.mk_int(1), m()); }
expr_ref minus_one() { return expr_ref(m_autil.mk_int(-1), m()); }
expr_ref mk_sub(expr* a, rational const& n);
expr_ref mk_sub(expr* a, unsigned n) { return mk_sub(a, rational(n)); }
bool is_suffix(expr* s, expr* offset, expr* len);
bool is_prefix(expr* s, expr* offset, expr* len);
@ -379,9 +397,19 @@ public:
void add_seqs(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_pair_vector& new_eqs);
// Expose derivative and nullability check
/*
create the nullability check for r
*/
expr_ref is_nullable(expr* r);
/*
make the derivative of r wrt the given element ele
*/
expr_ref mk_derivative(expr* ele, expr* r);
/*
make the derivative of r wrt the canonical variable v0 = (:var 0),
for example mk_derivative(a+) = (if (v0 = 'a') then a* else [])
*/
expr_ref mk_derivative(expr* r);
// heuristic elimination of element from condition that comes form a derivative.
// special case optimization for conjunctions of equalities, disequalities and ranges.

View file

@ -28,8 +28,8 @@ skolem::skolem(ast_manager& m, th_rewriter& rw):
m_tail = "seq.tail";
m_seq_first = "seq.first";
m_seq_last = "seq.last";
m_indexof_left = "seq.idx.left";
m_indexof_right = "seq.idx.right";
m_indexof_left = "seq.idx.l";
m_indexof_right = "seq.idx.r";
m_aut_step = "aut.step";
m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l
m_post = "seq.post"; // (seq.post s l): suffix of string s of length k, based on extract starting at index i of length l

View file

@ -77,8 +77,10 @@ namespace seq {
expr_ref mk_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_left, t, s, offset); }
expr_ref mk_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk(m_indexof_right, t, s, offset); }
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_left", t, s, offset); }
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.last_indexof_right", t, s, offset); }
expr_ref mk_contains_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.l", t, s, offset); }
expr_ref mk_contains_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.cnt.r", t, s, offset); }
expr_ref mk_last_indexof_left(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.l", t, s, offset); }
expr_ref mk_last_indexof_right(expr* t, expr* s, expr* offset = nullptr) { return mk("seq.lidx.r", t, s, offset); }
expr_ref mk_tail(expr* s, expr* i) { return mk(m_tail, s, i); }
expr_ref mk_post(expr* s, expr* i) { return mk(m_post, s, i); }

View file

@ -839,7 +839,7 @@ bool seq_util::str::is_nth_i(expr const* n, expr*& s, unsigned& idx) const {
return arith_util(m).is_unsigned(i, idx);
}
app* seq_util::str::mk_nth_i(expr* s, unsigned i) const {
app* seq_util::str::mk_nth_c(expr* s, unsigned i) const {
return mk_nth_i(s, arith_util(m).mk_int(i));
}
@ -854,6 +854,48 @@ void seq_util::str::get_concat(expr* e, expr_ref_vector& es) const {
}
}
/*
Returns true if s is an expression of the form (l = |u|) |u|-k or (-k)+|u| or |u|+(-k).
Also returns true and assigns k=0 and l=s if s is |u|.
*/
bool seq_util::str::is_len_sub(expr const* s, expr*& l, expr*& u, rational& k) const {
expr* x;
rational v;
arith_util a(m);
if (is_length(s, l)) {
k = 0;
return true;
}
else if (a.is_sub(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonneg()) {
k = v;
return true;
}
else if (a.is_add(s, l, x) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
k = - v;
return true;
}
else if (a.is_add(s, x, l) && is_length(l, u) && a.is_numeral(x, v) && v.is_nonpos()) {
k = - v;
return true;
}
else
return false;
}
bool seq_util::str::is_unit_string(expr const* s, expr_ref& c) const {
zstring z;
expr* ch = nullptr;
if (is_string(s, z) && z.length() == 1) {
c = mk_char(z[0]);
return true;
}
else if (is_unit(s, ch)) {
c = ch;
return true;
}
return false;
}
void seq_util::str::get_concat_units(expr* e, expr_ref_vector& es) const {
expr* e1, *e2;
while (is_concat(e, e1, e2)) {
@ -876,8 +918,6 @@ app* seq_util::str::mk_is_empty(expr* s) const {
return m.mk_eq(s, mk_empty(s->get_sort()));
}
unsigned seq_util::str::min_length(expr* s) const {
SASSERT(u.is_seq(s));
unsigned result = 0;
@ -892,7 +932,10 @@ unsigned seq_util::str::min_length(expr* s) const {
return 0u;
};
while (is_concat(s, s1, s2)) {
result += get_length(s1);
if (is_concat(s1))
result += min_length(s1);
else
result += get_length(s1);
s = s2;
}
result += get_length(s);
@ -920,7 +963,10 @@ unsigned seq_util::str::max_length(expr* s) const {
return UINT_MAX;
};
while (is_concat(s, s1, s2)) {
result = u.max_plus(get_length(s1), result);
if (is_concat(s1))
result = u.max_plus(max_length(s1), result);
else
result = u.max_plus(get_length(s1), result);
s = s2;
}
result = u.max_plus(get_length(s), result);
@ -1065,38 +1111,70 @@ app* seq_util::rex::mk_epsilon(sort* seq_sort) {
/*
Produces compact view of concrete concatenations such as (abcd).
*/
std::ostream& seq_util::rex::pp::compact_helper_seq(std::ostream& out, expr* s) const {
SASSERT(re.u.is_seq(s));
bool seq_util::rex::pp::print_seq(std::ostream& out, expr* s) const {
zstring z;
expr* x, * j, * k, * l, * i, * x_;
if (re.u.str.is_empty(s))
out << "()";
else if (re.u.str.is_unit(s))
seq_unit(out, s);
else if (re.u.str.is_concat(s)) {
expr_ref_vector es(re.m);
re.u.str.get_concat(s, es);
for (expr* e : es)
compact_helper_seq(out, e);
print(out, e);
}
else if (re.u.str.is_string(s, z)) {
for (unsigned i = 0; i < z.length(); i++)
out << (char)z[i];
}
//using braces to indicate 'full' output
//for example an uninterpreted constant X will be printed as {X}
//while a unit sequence "X" will be printed as X
//thus for example (concat "X" "Y" Z "W") where Z is uninterpreted is printed as XY{Z}W
else out << "{" << mk_pp(s, re.m) << "}";
return out;
else if (re.u.str.is_at(s, x, i))
print(out, x) << "@", print(out, i);
else if (re.u.str.is_extract(s, x, j, k)) {
rational jv, iv;
print(out, x);
if (arith_util(re.m).is_numeral(j, jv)) {
if (arith_util(re.m).is_numeral(k, iv)) {
// output X[j,k]
out << "[" << jv.get_int32() << "," << jv.get_int32() << "]";
}
else if (arith_util(re.m).is_sub(k, l, i) && re.u.str.is_length(l, x_) && x == x_ &&
arith_util(re.m).is_numeral(i, iv) && iv == jv) {
// case X[j,|X|-j] is denoted by X[j..]
out << "[" << jv.get_int32() << "..]";
}
else if (((arith_util(re.m).is_add(k, l, i) && re.u.str.is_length(l, x_)) ||
(arith_util(re.m).is_add(k, i, l) && re.u.str.is_length(l, x_))) && x == x_ &&
arith_util(re.m).is_numeral(i, iv) && iv.get_int32() + jv.get_int32() == 0) {
// case X[j,|X|-j] is denoted by X[j..]
out << "[" << jv.get_int32() << "..]";
}
else {
out << "[" << jv.get_int32() << ",";
print(out, k);
out << "]";
}
}
else {
out << "[";
print(out, j);
out << ",";
print(out, k);
out << "]";
}
}
else
return false;
return true;
}
/*
Produces output such as [a-z] for a range.
*/
std::ostream& seq_util::rex::pp::compact_helper_range(std::ostream& out, expr* s1, expr* s2) const {
std::ostream& seq_util::rex::pp::print_range(std::ostream& out, expr* s1, expr* s2) const {
out << "[";
seq_unit(out, s1) << "-";
seq_unit(out, s2) << "]";
print(out, s1);
out << "-";
print(out, s2);
out << "]";
return out;
}
@ -1111,9 +1189,10 @@ bool seq_util::rex::pp::can_skip_parenth(expr* r) const {
/*
Specialize output for a unit sequence converting to visible ASCII characters if possible.
*/
std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
expr* e;
bool seq_util::rex::pp::print_unit(std::ostream& out, expr* s) const {
expr* e, * i;
unsigned n = 0;
if ((re.u.str.is_unit(s, e) && re.u.is_const_char(e, n)) || re.u.is_const_char(s, n)) {
char c = (char)n;
if (c == '\n')
@ -1122,22 +1201,21 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
out << "\\r";
else if (c == '\f')
out << "\\f";
else if (c == ' ')
out << "\\s";
else if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '.' || c == '\\')
out << "\\" << c;
else if (32 < n && n < 127) {
else if (32 <= n && n < 127 && n != '\"' && n != ' '
&& n != '\\' && n != '\'' && n != '?' && n != '.' && n != '(' && n != ')' && n != '[' && n != ']'
&& n != '{' && n != '}' && n != '&') {
if (html_encode) {
if (c == '<')
out << "&lt;";
else if (c == '>')
out << "&gt;";
else if (c == '&')
out << "&amp;";
else if (c == '\"')
out << "&quot;";
//else if (c == '&')
// out << "&amp;";
//else if (c == '\"')
// out << "&quot;";
else
out << "\\x" << std::hex << n;
//out << "\\x" << std::hex << n;
out << c;
}
else
out << c;
@ -1148,95 +1226,193 @@ std::ostream& seq_util::rex::pp::seq_unit(std::ostream& out, expr* s) const {
out << "\\x" << std::hex << n;
else if (n <= 0xFFF)
out << "\\u0" << std::hex << n;
else
else
out << "\\u" << std::hex << n;
}
else if (re.u.str.is_nth_i(s, e, i)) {
print(out, e) << "[";
print(out, i) << "]";
}
else if (re.u.str.is_length(s, e))
print(out << "|", e) << "|";
else
out << "{" << mk_pp(s, re.m) << "}";
return out;
return false;
return true;
}
/*
Pretty prints the regex r into the out stream
Pretty prints the regex r into the ostream out
*/
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
std::ostream& seq_util::rex::pp::print(std::ostream& out, expr* e) const {
expr* r1 = nullptr, * r2 = nullptr, * s = nullptr, * s2 = nullptr;
unsigned lo = 0, hi = 0;
if (re.u.is_char(e))
return seq_unit(out, e);
else if (re.u.is_seq(e))
return compact_helper_seq(out, e);
arith_util a(re.m);
rational v;
if (!e)
out << "null";
else if (print_unit(out, e))
;
else if (print_seq(out, e))
;
else if (re.is_full_char(e))
return out << ".";
out << ".";
else if (re.is_full_seq(e))
return out << ".*";
out << ".*";
else if (re.is_to_re(e, s))
return compact_helper_seq(out, s);
else if (re.is_range(e, s, s2))
return compact_helper_range(out, s, s2);
print(out, s);
else if (re.is_range(e, s, s2))
print_range(out, s, s2);
else if (re.is_epsilon(e))
return out << "()";
// &#x03B5; = epsilon
out << (html_encode ? "&#x03B5;" : "()");
else if (re.is_empty(e))
return out << "[]";
else if (re.is_concat(e, r1, r2))
return out << pp(re, r1) << pp(re, r2);
else if (re.is_union(e, r1, r2))
return out << "(" << pp(re, r1) << "|" << pp(re, r2) << ")";
else if (re.is_intersection(e, r1, r2))
return out << "(" << pp(re, r1) << "&amp;" /*(html_encode ? ")&amp;(" : ")&(")*/ << pp(re, r2) << ")";
// &#x2205; = emptyset
out << (html_encode ? "&#x2205;" : "[]");
else if (re.is_concat(e, r1, r2)) {
print(out, r1);
print(out, r2);
}
else if (re.is_antimorov_union(e, r1, r2) || re.is_union(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C3;" : "|");
print(out, r2);
out << ")";
}
else if (re.is_intersection(e, r1, r2)) {
out << "(";
print(out, r1);
out << (html_encode ? "&#x22C2;" : "&");
print(out, r2);
out << ")";
}
else if (re.is_complement(e, r1)) {
out << "~";
if (can_skip_parenth(r1))
return out << "~" << pp(re, r1);
else
return out << "~(" << pp(re, r1) << ")";
print(out, r1);
else {
out << "(";
print(out, r1);
out << ")";
}
}
else if (re.is_plus(e, r1)) {
if (can_skip_parenth(r1))
return out << pp(re, r1) << "+";
else
return out << "(" << pp(re, r1) << ")+";
if (can_skip_parenth(r1)) {
print(out, r1);
out << "+";
}
else {
out << "(";
print(out, r1);
out << ")+";
}
}
else if (re.is_star(e, r1)) {
if (can_skip_parenth(r1))
return out << pp(re, r1) << "*";
else
return out << "(" << pp(re, r1) << ")*";
if (can_skip_parenth(r1)) {
print(out, r1);
out << "*";
}
else {
out << "(";
print(out, r1);
out << ")*";
}
}
else if (re.is_loop(e, r1, lo)) {
if (can_skip_parenth(r1))
return out << pp(re, r1) << "{" << lo << ",}";
else
return out << "(" << pp(re, r1) << "){" << lo << ",}";
if (can_skip_parenth(r1))
print(out, r1) << "{" << lo << ",}";
else {
out << "(";
print(out, r1);
out << "){" << lo << ",}";
}
}
else if (re.is_loop(e, r1, lo, hi)) {
if (can_skip_parenth(r1)) {
print(out, r1);
if (lo == hi)
return out << pp(re, r1) << "{" << lo << "}";
else
return out << pp(re, r1) << "{" << lo << "," << hi << "}";
out << "{" << lo << "}";
else
out << "{" << lo << "," << hi << "}";
}
else {
out << "(";
print(out, r1);
if (lo == hi)
return out << "(" << pp(re, r1) << "){" << lo << "}";
out << "){" << lo << "}";
else
return out << "(" << pp(re, r1) << "){" << lo << "," << hi << "}";
out << "){" << lo << "," << hi << "}";
}
}
else if (re.is_diff(e, r1, r2))
return out << "(" << pp(re, r1) << ")\\(" << pp(re, r2) << ")";
else if (re.m.is_ite(e, s, r1, r2))
return out << "if(" << mk_pp(s, re.m) << "," << pp(re, r1) << "," << pp(re, r2) << ")";
else if (re.is_diff(e, r1, r2)) {
out << "(";
print(out, r1);
out << ")\\(";
print(out, r2);
out << ")";
}
else if (re.m.is_ite(e, s, r1, r2)) {
out << (html_encode ? "(&#x1D422;&#x1D41F; " : "(if ");
print(out, s);
out << (html_encode ? " &#x1D42D;&#x1D5F5;&#x1D41E;&#x1D427; " : " then ");
print(out, r1);
out << (html_encode ? " &#x1D41E;&#x1D425;&#x1D600;&#x1D41E; " : " else ");
print(out, r2);
out << ")";
}
else if (re.is_opt(e, r1)) {
if (can_skip_parenth(r1))
return out << pp(re, r1) << "?";
else
return out << "(" << pp(re, r1) << ")?";
print(out, r1) << "?";
else {
out << "(";
print(out, r1);
out << ")?";
}
}
else if (re.is_reverse(e, r1)) {
out << "(reverse ";
print(out, r1);
out << ")";
}
else if (re.m.is_eq(e, r1, r2)) {
out << "(";
print(out, r1);
out << " = ";
print(out, r2);
out << ")";
}
else if (re.m.is_not(e, r1)) {
out << "!";
print(out, r1);
}
else if (a.is_add(e, s, s2) && a.is_numeral(s, v) && v < 0)
print(out, s2) << " - " << -v;
else if (a.is_add(e, s, s2) && a.is_numeral(s2, v) && v < 0)
print(out, s) << " - " << -v;
else if (a.is_add(e, s, s2))
print(out, s) << " + ", print(out, s2);
else if (a.is_sub(e, s, s2) && a.is_numeral(s2, v) && v > 0)
print(out, s) << " - " << v;
else if (a.is_le(e, s, s2))
print(out << "(", s) << " <= ", print(out, s2) << ")";
else if (re.m.is_value(e))
out << mk_pp(e, re.m);
else if (is_app(e) && to_app(e)->get_num_args() == 0)
out << mk_pp(e, re.m);
else if (is_app(e)) {
out << "(" << to_app(e)->get_decl()->get_name();
for (expr* arg : *to_app(e))
print(out << " ", arg);
out << ")";
}
else if (re.is_reverse(e, r1))
return out << "reverse(" << pp(re, r1) << ")";
else
// Else: derivative or is_of_pred
return out << "{" << mk_pp(e, re.m) << "}";
// for all remaining cases use the default pretty printer
out << mk_pp(e, re.m);
return out;
}
std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
return print(out, ex);
}
/*
@ -1244,7 +1420,16 @@ std::ostream& seq_util::rex::pp::display(std::ostream& out) const {
*/
std::string seq_util::rex::to_str(expr* r) const {
std::ostringstream out;
out << pp(u.re, r);
pp(u.re, r, false).display(out);
return out.str();
}
/*
Pretty prints the regex r into the output string that is htmlencoded
*/
std::string seq_util::rex::to_strh(expr* r) const {
std::ostringstream out;
pp(u.re, r, true).display(out);
return out.str();
}
@ -1290,7 +1475,7 @@ seq_util::rex::info seq_util::rex::get_info_rec(expr* e) const {
else
result = mk_info_rec(to_app(e));
m_infos.setx(e->get_id(), result, invalid_info);
STRACE("re_info", tout << "compute_info(" << pp(u.re, e) << ")=" << result << std::endl;);
STRACE("re_info", tout << "compute_info(" << pp(u.re, e, false) << ")=" << result << std::endl;);
return result;
}
@ -1518,7 +1703,13 @@ seq_util::rex::info seq_util::rex::info::orelse(seq_util::rex::info const& i) co
// unsigned ite_min_length = std::min(min_length, i.min_length);
// lbool ite_nullable = (nullable == i.nullable ? nullable : l_undef);
// TBD: whether ite is interpreted or not depends on whether the condition is interpreted and both branches are interpreted
return info(false, false, false, false, normalized && i.normalized, monadic && i.monadic, singleton && i.singleton, nullable, std::min(min_length, i.min_length), std::max(star_height, i.star_height));
return info(false, false, false, false,
normalized && i.normalized,
monadic && i.monadic,
singleton && i.singleton,
((nullable == l_true && i.nullable == l_true) ? l_true : ((nullable == l_false && i.nullable == l_false) ? l_false : l_undef)),
std::min(min_length, i.min_length),
std::max(star_height, i.star_height));
}
else
return i;

View file

@ -286,7 +286,8 @@ public:
app* mk_at(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_AT, 2, es); }
app* mk_nth(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH, 2, es); }
app* mk_nth_i(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_I, 2, es); }
app* mk_nth_i(expr* s, unsigned i) const;
app* mk_nth_u(expr* s, expr* i) const { expr* es[2] = { s, i }; return m.mk_app(m_fid, OP_SEQ_NTH_U, 2, es); }
app* mk_nth_c(expr* s, unsigned i) const;
app* mk_substr(expr* a, expr* b, expr* c) const { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); }
app* mk_contains(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); }
@ -350,6 +351,13 @@ public:
bool is_from_code(expr const* n) const { return is_app_of(n, m_fid, OP_STRING_FROM_CODE); }
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;
/*
tests if s is a single character string(c) or a unit (c)
*/
bool is_unit_string(expr const* s, expr_ref& c) const;
bool is_string_term(expr const * n) const {
return u.is_string(n->get_sort());
}
@ -530,7 +538,20 @@ public:
bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); }
bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); }
bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); }
bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); }
bool is_full_seq(expr const* n) const {
expr* s;
return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET) || (is_star(n, s) && is_full_char(s));
}
bool is_dot_plus(expr const* n) const {
expr* s, * t;
if (is_plus(n, s) && is_full_char(s))
return true;
if (is_concat(n, s, t)) {
if ((is_full_char(s) && is_full_seq(t)) || (is_full_char(t) && is_full_seq(s)))
return true;
}
return false;
}
bool is_of_pred(expr const* n) const { return is_app_of(n, m_fid, OP_RE_OF_PRED); }
bool is_reverse(expr const* n) const { return is_app_of(n, m_fid, OP_RE_REVERSE); }
bool is_derivative(expr const* n) const { return is_app_of(n, m_fid, OP_RE_DERIVATIVE); }
@ -559,18 +580,32 @@ public:
app* mk_epsilon(sort* seq_sort);
info get_info(expr* r) const;
std::string to_str(expr* r) const;
std::string to_strh(expr* r) const;
expr_ref mk_ite_simplify(expr* c, expr* t, expr* e)
{
expr_ref result(m);
if (m.is_true(c) || t == e)
result = t;
else if (m.is_false(c))
result = e;
else
result = m.mk_ite(c, t, e);
return result;
}
class pp {
seq_util::rex& re;
expr* e;
expr* ex;
bool html_encode;
bool can_skip_parenth(expr* r) const;
std::ostream& seq_unit(std::ostream& out, expr* s) const;
std::ostream& compact_helper_seq(std::ostream& out, expr* s) const;
std::ostream& compact_helper_range(std::ostream& out, expr* s1, expr* s2) const;
bool print_unit(std::ostream& out, expr* s) const;
bool print_seq(std::ostream& out, expr* s) const;
std::ostream& print_range(std::ostream& out, expr* s1, expr* s2) const;
std::ostream& print(std::ostream& out, expr* e) const;
public:
pp(seq_util::rex& r, expr* e, bool html = false) : re(r), e(e), html_encode(html) {}
pp(seq_util::rex& re, expr* ex, bool html) : re(re), ex(ex), html_encode(html) {}
std::ostream& display(std::ostream&) const;
};
};

View file

@ -87,6 +87,7 @@ public:
bool is_special_relation(app* e) const { return is_special_relation(e->get_decl()); }
sr_property get_property(func_decl* f) const;
sr_property get_property(app* e) const { return get_property(e->get_decl()); }
func_decl* get_relation(func_decl* f) const { SASSERT(is_special_relation(f)); return to_func_decl(f->get_parameter(0).get_ast()); }
func_decl* mk_to_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_TO); }
func_decl* mk_po_decl(func_decl* f) { return mk_rel_decl(f, OP_SPECIAL_RELATION_PO); }

View file

@ -1828,9 +1828,8 @@ void cmd_context::add_declared_functions(model& mdl) {
mdl.register_decl(f, fi);
}
}
mdl.add_rec_funs();
}
mdl.add_rec_funs();
}
void cmd_context::display_sat_result(lbool r) {

View file

@ -270,6 +270,7 @@ public:
class check_value {
public:
virtual bool operator()(Value const& v) = 0;
virtual ~check_value() = default;
};
bool find_le(Key const* keys, check_value& check) {

View file

@ -23,6 +23,7 @@ Revision History:
namespace lp {
class column_namer {
public:
virtual ~column_namer() = default;
virtual std::string get_variable_name(unsigned j) const = 0;
template <typename T>
std::ostream & print_row(const row_strip<T> & row, std::ostream & out) const {

View file

@ -120,6 +120,8 @@ struct factorization_factory {
m_vars(vars), m_monic(m) {
}
virtual ~factorization_factory() = default;
bool_vector get_mask() const {
// we keep the last element always in the first factor to avoid
// repeating a pair twice, that is why m_mask is shorter by one then m_vars

View file

@ -16,6 +16,9 @@ namespace lp {
lp_settings const& lar_solver::settings() const { return m_settings; }
statistics& lar_solver::stats() { return m_settings.stats(); }
void lar_solver::updt_params(params_ref const& _p) {
smt_params_helper p(_p);
set_track_pivoted_rows(p.arith_bprop_on_pivoted_rows());
@ -23,17 +26,9 @@ namespace lp {
m_settings.updt_params(_p);
}
void clear() {
lp_assert(false); // not implemented
}
lar_solver::lar_solver() :
m_status(lp_status::UNKNOWN),
m_crossed_bounds_column(-1),
m_mpq_lar_core_solver(m_settings, *this),
m_int_solver(nullptr),
m_need_register_terms(false),
m_var_register(false),
m_term_register(true),
m_constraints(*this) {}
@ -197,11 +192,11 @@ namespace lp {
void lar_solver::set_status(lp_status s) { m_status = s; }
lp_status lar_solver::find_feasible_solution() {
m_settings.stats().m_make_feasible++;
if (A_r().column_count() > m_settings.stats().m_max_cols)
m_settings.stats().m_max_cols = A_r().column_count();
if (A_r().row_count() > m_settings.stats().m_max_rows)
m_settings.stats().m_max_rows = A_r().row_count();
stats().m_make_feasible++;
if (A_r().column_count() > stats().m_max_cols)
stats().m_max_cols = A_r().column_count();
if (A_r().row_count() > stats().m_max_rows)
stats().m_max_rows = A_r().row_count();
if (strategy_is_undecided())
decide_on_strategy_and_adjust_initial_state();
@ -248,7 +243,7 @@ namespace lp {
m_constraints.push();
m_usage_in_terms.push();
}
void lar_solver::clean_popped_elements(unsigned n, u_set& set) {
vector<int> to_remove;
for (unsigned j : set)
@ -269,9 +264,8 @@ namespace lp {
m_crossed_bounds_column.pop(k);
unsigned n = m_columns_to_ul_pairs.peek_size(k);
m_var_register.shrink(n);
if (m_settings.use_tableau()) {
if (m_settings.use_tableau())
pop_tableau();
}
lp_assert(A_r().column_count() == n);
TRACE("lar_solver_details",
for (unsigned j = 0; j < n; j++) {
@ -285,6 +279,10 @@ namespace lp {
clean_popped_elements(n, m_columns_with_changed_bounds);
clean_popped_elements(n, m_incorrect_columns);
for (auto rid : m_row_bounds_to_replay)
insert_row_with_changed_bounds(rid);
m_row_bounds_to_replay.reset();
unsigned m = A_r().row_count();
clean_popped_elements(m, m_rows_with_changed_bounds);
clean_inf_set_of_r_solver_after_pop();
@ -633,6 +631,9 @@ namespace lp {
left_side.push_back(std::make_pair(p.second, p.first));
}
void lar_solver::insert_row_with_changed_bounds(unsigned rid) {
m_rows_with_changed_bounds.insert(rid);
}
void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) {
if (A_r().row_count() != m_column_buffer.data_size())
@ -643,14 +644,14 @@ namespace lp {
m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer);
for (unsigned i : m_column_buffer.m_index)
m_rows_with_changed_bounds.insert(i);
insert_row_with_changed_bounds(i);
}
void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) {
for (auto& rc : m_mpq_lar_core_solver.m_r_A.m_columns[j])
m_rows_with_changed_bounds.insert(rc.var());
insert_row_with_changed_bounds(rc.var());
}
bool lar_solver::use_tableau() const { return m_settings.use_tableau(); }
@ -743,7 +744,7 @@ namespace lp {
void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) {
if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) {
m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]);
insert_row_with_changed_bounds(m_mpq_lar_core_solver.m_r_heading[j]);
return;
}
@ -793,7 +794,7 @@ namespace lp {
update_x_and_inf_costs_for_columns_with_changed_bounds();
m_mpq_lar_core_solver.solve();
set_status(m_mpq_lar_core_solver.m_r_solver.get_status());
lp_assert(((m_settings.stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold());
lp_assert(((stats().m_make_feasible% 100) != 0) || m_status != lp_status::OPTIMAL || all_constraints_hold());
}
@ -974,9 +975,7 @@ namespace lp {
bool lar_solver::the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>>& evidence) const {
std::unordered_map<var_index, mpq> coeff_map;
for (auto& it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
for (auto const & [coeff, con_ind] : evidence) {
lp_assert(m_constraints.valid_index(con_ind));
register_in_map(coeff_map, m_constraints[con_ind], coeff);
}
@ -1337,7 +1336,7 @@ namespace lp {
void lar_solver::mark_rows_for_bound_prop(lpvar j) {
auto& column = A_r().m_columns[j];
for (auto const& r : column)
m_rows_with_changed_bounds.insert(r.var());
insert_row_with_changed_bounds(r.var());
}
@ -1659,7 +1658,7 @@ namespace lp {
m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size());
m_mpq_lar_core_solver.m_r_basis.push_back(j);
if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
insert_row_with_changed_bounds(A_r().row_count() - 1);
}
else {
m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1);
@ -1755,7 +1754,7 @@ namespace lp {
if (use_tableau() && !coeffs.empty()) {
add_row_from_term_no_constraint(m_terms.back(), ret);
if (m_settings.bound_propagation())
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
insert_row_with_changed_bounds(A_r().row_count() - 1);
}
lp_assert(m_var_register.size() == A_r().column_count());
if (m_need_register_terms) {

View file

@ -76,13 +76,13 @@ class lar_solver : public column_namer {
//////////////////// fields //////////////////////////
lp_settings m_settings;
lp_status m_status;
lp_status m_status = lp_status::UNKNOWN;
stacked_value<simplex_strategy_enum> m_simplex_strategy;
// such can be found at the initialization step: u < l
stacked_value<int> m_crossed_bounds_column;
lar_core_solver m_mpq_lar_core_solver;
int_solver * m_int_solver;
bool m_need_register_terms;
int_solver * m_int_solver = nullptr;
bool m_need_register_terms = false;
var_register m_var_register;
var_register m_term_register;
stacked_vector<ul_pair> m_columns_to_ul_pairs;
@ -90,6 +90,8 @@ class lar_solver : public column_namer {
// the set of column indices j such that bounds have changed for j
u_set m_columns_with_changed_bounds;
u_set m_rows_with_changed_bounds;
unsigned_vector m_row_bounds_to_replay;
u_set m_basic_columns_with_changed_cost;
// these are basic columns with the value changed, so the the corresponding row in the tableau
// does not sum to zero anymore
@ -164,7 +166,6 @@ class lar_solver : public column_namer {
void adjust_initial_state_for_lu();
void adjust_initial_state_for_tableau_rows();
void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls);
void clear();
bool use_lu() const;
bool sizes_are_correct() const;
bool implied_bound_is_correctly_explained(implied_bound const & be, const vector<std::pair<mpq, unsigned>> & explanation) const;
@ -219,6 +220,7 @@ class lar_solver : public column_namer {
void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq> & delta);
void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j);
unsigned num_changed_bounds() const { return m_rows_with_changed_bounds.size(); }
void insert_row_with_changed_bounds(unsigned rid);
void detect_rows_with_changed_bounds_for_column(unsigned j);
void detect_rows_with_changed_bounds();
void set_value_for_nbasic_column(unsigned j, const impq & new_val);
@ -368,20 +370,19 @@ public:
// these two loops should be run sequentially
// since the first loop might change column bounds
// and add fixed columns this way
if (settings().cheap_eqs()) {
if (settings().propagate_eqs()) {
bp.clear_for_eq();
for (unsigned i : m_rows_with_changed_bounds) {
calculate_cheap_eqs_for_row(i, bp);
unsigned offset_eqs = stats().m_offset_eqs;
bp.cheap_eq_tree(i);
if (settings().get_cancel_flag())
return;
if (stats().m_offset_eqs > offset_eqs)
m_row_bounds_to_replay.push_back(i);
}
}
m_rows_with_changed_bounds.clear();
}
template <typename T>
void calculate_cheap_eqs_for_row(unsigned i, lp_bound_propagator<T> & bp) {
bp.cheap_eq_tree(i);
}
bool is_fixed(column_index const& j) const { return column_is_fixed(j); }
inline column_index to_column_index(unsigned v) const { return column_index(external_to_column_index(v)); }
@ -515,6 +516,8 @@ public:
unsigned column_to_reported_index(unsigned j) const;
lp_settings & settings();
lp_settings const & settings() const;
statistics& stats();
void updt_params(params_ref const& p);
column_type get_column_type(unsigned j) const { return m_mpq_lar_core_solver.m_column_types()[j]; }
const impq & get_lower_bound(unsigned j) const { return m_mpq_lar_core_solver.m_r_lower_bounds()[j]; }

View file

@ -155,7 +155,7 @@ public:
};
class const_iterator {
u_map< mpq>::iterator m_it;
u_map<mpq>::iterator m_it;
public:
ival operator*() const { return ival(m_it->m_key, m_it->m_value); }
const_iterator operator++() { const_iterator i = *this; m_it++; return i; }

View file

@ -72,14 +72,15 @@ class lp_bound_propagator {
static int other(int x, int y, int z) { SASSERT(x == z || y == z); return x == z ? y : x; }
std::ostream& print_vert(std::ostream & out, const vertex* v) const {
out << "(c = " << v->column() << ", parent = {";
if (v->parent()) { out << "(" << v->parent()->column() << ")";}
else { out << "null"; }
if (v->parent())
out << "(" << v->parent()->column() << ")";
else
out << "null";
out << "} , lvl = " << v->level();
if (m_pol.contains(v->column())) {
if (m_pol.contains(v->column()))
out << (pol(v) == -1? " -":" +");
} else {
else
out << " not in m_pol";
}
out << ')';
return out;
}
@ -87,13 +88,13 @@ class lp_bound_propagator {
hashtable<unsigned, u_hash, u_eq> m_visited_rows;
hashtable<unsigned, u_hash, u_eq> m_visited_columns;
u_map<vertex*> m_vertices;
vertex* m_root;
vertex* m_root = nullptr;
// At some point we can find a row with a single vertex non fixed vertex
// then we can fix the whole tree,
// by adjusting the vertices offsets, so they become absolute.
// If the tree is fixed then in addition to checking with the m_vals_to_verts
// we are going to check with the m_fixed_var_tables.
const vertex* m_fixed_vertex;
const vertex* m_fixed_vertex = nullptr;
explanation m_fixed_vertex_explanation;
// a pair (o, j) belongs to m_vals_to_verts iff x[j] = x[m_root->column()] + o
map<mpq, const vertex*, obj_hash<mpq>, default_eq<mpq>> m_vals_to_verts;
@ -111,19 +112,199 @@ class lp_bound_propagator {
T& m_imp;
vector<implied_bound> m_ibounds;
map<mpq, unsigned, obj_hash<mpq>, default_eq<mpq>> m_val2fixed_row;
void try_add_equation_with_internal_fixed_tables(unsigned r1, vertex const* v) {
SASSERT(m_fixed_vertex);
if (v != m_root)
return;
unsigned v1 = v->column();
unsigned r2 = UINT_MAX;
if (!m_val2fixed_row.find(val(v1), r2) || r2 >= lp().row_count()) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
unsigned v2, v3;
int polarity;
if (!is_tree_offset_row(r2, v2, v3, polarity) || !not_set(v3) ||
is_int(v1) != is_int(v2) || val(v1) != val(v2)) {
m_val2fixed_row.insert(val(v1), r1);
return;
}
explanation ex;
explain_fixed_in_row(r1, ex);
explain_fixed_in_row(r2, ex);
add_eq_on_columns(ex, v1, v2, true);
}
void try_add_equation_with_lp_fixed_tables(unsigned row_index, const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
unsigned j = null_lpvar;
if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j)) {
// try_add_equation_with_internal_fixed_tables(row_index, v);
return;
}
TRACE("cheap_eq",
tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;
tout << "v = "; print_vert(tout, v) << std::endl;
tout << "found j " << j << std::endl; lp().print_column_info(j, tout)<< std::endl;
tout << "found j = " << j << std::endl;);
vector<edge> path = connect_in_tree(v, m_fixed_vertex);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
explain_fixed_column(j, ex);
add_eq_on_columns(ex, j, v_j, true);
}
void try_add_equation_with_val_table(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
const vertex *u = nullptr;
if (!m_vals_to_verts.find(val(v_j), u)) {
m_vals_to_verts.insert(val(v_j), v);
return;
}
unsigned j = u->column();
if (j == v_j || is_int(j) != is_int(v_j))
return;
TRACE("cheap_eq", tout << "found j=" << j << " for v=";
print_vert(tout, v) << "\n in m_vals_to_verts\n";);
vector<edge> path = connect_in_tree(u, v);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
add_eq_on_columns(ex, j, v_j, true);
}
static bool not_set(unsigned j) { return j == UINT_MAX; }
static bool is_set(unsigned j) { return j != UINT_MAX; }
void create_root(unsigned row_index) {
SASSERT(!m_root && !m_fixed_vertex);
unsigned x, y;
int polarity;
TRACE("cheap_eq_det", print_row(tout, row_index););
if (!is_tree_offset_row(row_index, x, y, polarity)) {
TRACE("cheap_eq_det", tout << "not an offset row\n";);
return;
}
TRACE("cheap_eq", print_row(tout, row_index););
m_root = alloc_v(x);
set_polarity(m_root, 1); // keep m_root in the positive table
if (not_set(y)) {
set_fixed_vertex(m_root);
explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
}
else {
vertex *v = add_child_with_check(row_index, y, m_root, polarity);
if (v)
explore_under(v);
}
explore_under(m_root);
}
void explore_under(vertex * v) {
check_for_eq_and_add_to_val_tables(v);
go_over_vertex_column(v);
}
// In case of only one non fixed column, and the function returns true,
// this column would be represened by x.
bool is_tree_offset_row(unsigned row_index, unsigned & x, unsigned & y, int & polarity) const {
x = y = UINT_MAX;
const row_cell<mpq>* x_cell = nullptr;
const row_cell<mpq>* y_cell = nullptr;
const auto & row = lp().get_row(row_index);
for (unsigned k = 0; k < row.size(); k++) {
const auto& c = row[k];
if (column_is_fixed(c.var()))
continue;
if (not_set(x)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
x = c.var();
x_cell = & c;
}
else
return false;
}
else if (not_set(y)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
y = c.var();
y_cell = & c;
}
else
return false;
}
else
return false;
}
if (is_set(x)) {
if (is_set(y))
polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
else
polarity = 1;
return true;
}
return false;
}
void go_over_vertex_column(vertex * v) {
lpvar j = v->column();
if (!check_insert(m_visited_columns, j))
return;
for (const auto & c : lp().get_column(j)) {
unsigned row_index = c.var();
if (!check_insert(m_visited_rows, row_index))
continue;
vertex* u = get_child_from_row(row_index, v);
if (u)
explore_under(u);
}
}
void reset_cheap_eq_eh() {
if (!m_root)
return;
delete_tree(m_root);
m_root = nullptr;
set_fixed_vertex(nullptr);
m_fixed_vertex_explanation.clear();
m_vals_to_verts.reset();
m_vals_to_verts_neg.reset();
m_pol.reset();
m_vertices.reset();
}
struct reset_cheap_eq {
lp_bound_propagator& p;
reset_cheap_eq(lp_bound_propagator& p):p(p) {}
~reset_cheap_eq() { p.reset_cheap_eq_eh(); }
};
public:
lp_bound_propagator(T& imp):
m_imp(imp) {}
const vector<implied_bound>& ibounds() const { return m_ibounds; }
void init() {
m_improved_upper_bounds.clear();
m_improved_lower_bounds.clear();
m_ibounds.reset();
}
lp_bound_propagator(T& imp): m_root(nullptr),
m_fixed_vertex(nullptr),
m_imp(imp) {}
const lar_solver& lp() const { return m_imp.lp(); }
lar_solver& lp() { return m_imp.lp(); }
column_type get_column_type(unsigned j) const {
return m_imp.lp().get_column_type(j);
}
@ -133,9 +314,8 @@ public:
}
const mpq & get_lower_bound_rational(unsigned j) const {
return m_imp.lp().get_lower_bound(j).x;
return m_imp.lp().get_lower_bound(j).x;
}
const impq & get_upper_bound(unsigned j) const {
return m_imp.lp().get_upper_bound(j);
@ -167,19 +347,22 @@ public:
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
}
} else {
}
else {
m_improved_lower_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
}
} else { // the upper bound case
}
else { // the upper bound case
if (try_get_value(m_improved_upper_bounds, j, k)) {
auto & found_bound = m_ibounds[k];
if (v < found_bound.m_bound || (v == found_bound.m_bound && !found_bound.m_strict && strict)) {
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
TRACE("try_add_bound", m_imp.lp().print_implied_bound(found_bound, tout););
}
} else {
}
else {
m_improved_upper_bounds[j] = m_ibounds.size();
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
TRACE("try_add_bound", m_imp.lp().print_implied_bound(m_ibounds.back(), tout););
@ -199,54 +382,12 @@ public:
return val(v->column());
}
void try_add_equation_with_lp_fixed_tables(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
unsigned j = null_lpvar;
if (!lp().find_in_fixed_tables(val(v_j), is_int(v_j), j))
return;
TRACE("cheap_eq", tout << "v_j = "; lp().print_column_info(v_j, tout) << std::endl;);
TRACE("cheap_eq", tout << "v = "; print_vert(tout, v) << std::endl;);
TRACE("cheap_eq", tout << "found j " << j << std::endl;
lp().print_column_info(j, tout)<< std::endl;);
TRACE("cheap_eq", tout << "found j = " << j << std::endl;);
vector<edge> path = connect_in_tree(v, m_fixed_vertex);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
explain_fixed_column(j, ex);
add_eq_on_columns(ex, j, v->column());
}
void try_add_equation_with_val_table(const vertex *v) {
SASSERT(m_fixed_vertex);
unsigned v_j = v->column();
const vertex *u = nullptr;
if (!m_vals_to_verts.find(val(v_j), u)) {
m_vals_to_verts.insert(val(v_j), v);
return;
}
unsigned j = u->column();
if (j == v_j || is_int(j) != is_int(v_j))
return;
TRACE("cheap_eq", tout << "found j=" << j << " for v=";
print_vert(tout, v) << "\n in m_vals_to_verts\n";);
vector<edge> path = connect_in_tree(u, v);
explanation ex = get_explanation_from_path(path);
ex.add_expl(m_fixed_vertex_explanation);
add_eq_on_columns(ex, j, v_j);
}
bool tree_contains_r(vertex* root, vertex *v) const {
if (*root == *v)
return true;
for (auto e : root->edges()) {
for (auto e : root->edges())
if (tree_contains_r(e.target(), v))
return true;
}
return false;
}
@ -294,38 +435,12 @@ public:
return v;
}
static bool not_set(unsigned j) { return j == UINT_MAX; }
static bool is_set(unsigned j) { return j != UINT_MAX; }
void create_root(unsigned row_index) {
SASSERT(!m_root && !m_fixed_vertex);
unsigned x, y;
int polarity;
TRACE("cheap_eq_det", print_row(tout, row_index););
if (!is_tree_offset_row(row_index, x, y, polarity)) {
TRACE("cheap_eq_det", tout << "not an offset row\n";);
return;
}
TRACE("cheap_eq", print_row(tout, row_index););
m_root = alloc_v(x);
set_polarity(m_root, 1); // keep m_root in the positive table
if (not_set(y)) {
set_fixed_vertex(m_root);
explain_fixed_in_row(row_index, m_fixed_vertex_explanation);
} else {
vertex *v = add_child_with_check(row_index, y, m_root, polarity);
if (v)
explore_under(v);
}
explore_under(m_root);
}
unsigned column(unsigned row, unsigned index) {
return lp().get_row(row)[index].var();
}
bool fixed_phase() const { return m_fixed_vertex; }
// Returns the vertex to start exploration from, or nullptr.
@ -379,10 +494,12 @@ public:
is_int(k->column()) == is_int(v->column()) &&
!is_equal(k->column(), v->column())) {
report_eq(k, v);
} else {
}
else {
TRACE("cheap_eq", tout << "no report\n";);
}
} else {
}
else {
TRACE("cheap_eq", tout << "registered: " << val(v) << " -> { "; print_vert(tout, v) << "} \n";);
table.insert(val(v), v);
}
@ -411,37 +528,31 @@ public:
std::ostream& print_path(const vector<edge>& path, std::ostream& out) const {
out << "path = \n";
for (const edge& k : path) {
for (const edge& k : path)
print_edge(k, out) << "\n";
}
return out;
}
// we have v_i and v_j, indices of vertices at the same offsets
void report_eq(const vertex* v_i, const vertex* v_j) {
SASSERT(v_i != v_j);
SASSERT(lp().get_column_value(v_i->column()) == lp().get_column_value(v_j->column()));
TRACE("cheap_eq", tout << v_i->column() << " = " << v_j->column() << "\nu = ";
print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n";
);
print_vert(tout, v_i) << "\nv = "; print_vert(tout, v_j) <<"\n");
vector<edge> path = connect_in_tree(v_i, v_j);
lp::explanation exp = get_explanation_from_path(path);
add_eq_on_columns(exp, v_i->column(), v_j->column());
add_eq_on_columns(exp, v_i->column(), v_j->column(), false);
}
std::ostream& print_expl(std::ostream & out, const explanation& exp) const {
for (auto p : exp) {
for (auto p : exp)
lp().constraints().display(out, [this](lpvar j) { return lp().get_variable_name(j);}, p.ci());
}
return out;
}
void add_eq_on_columns(const explanation& exp, lpvar j, lpvar k) {
bool add_eq_on_columns(const explanation& exp, lpvar j, lpvar k, bool is_fixed) {
SASSERT(j != k);
unsigned je = lp().column_to_reported_index(j);
unsigned ke = lp().column_to_reported_index(k);
@ -452,8 +563,10 @@ public:
tout << "theory_vars v" << lp().local_to_external(je) << " == v" << lp().local_to_external(ke) << "\n";
);
m_imp.add_eq(je, ke, exp);
lp().settings().stats().m_cheap_eqs++;
bool added = m_imp.add_eq(je, ke, exp, is_fixed);
if (added)
lp().stats().m_offset_eqs++;
return added;
}
// column to theory_var
@ -478,14 +591,10 @@ public:
}
void explain_fixed_in_row(unsigned row, explanation& ex) const {
TRACE("cheap_eq",
tout << lp().get_row(row) << std::endl;
);
for (const auto & c : lp().get_row(row)) {
if (lp().is_fixed(c.var())) {
TRACE("cheap_eq", tout << lp().get_row(row) << std::endl);
for (const auto & c : lp().get_row(row))
if (lp().is_fixed(c.var()))
explain_fixed_column(c.var(), ex);
}
}
}
void explain_fixed_column(unsigned j, explanation & ex) const {
@ -536,10 +645,9 @@ public:
if (visited_verts.find(v->column()) != visited_verts.end())
return false;
visited_verts.insert(v->column());
for (auto e : v->edges()) {
for (auto e : v->edges())
if (!tree_is_correct(e.target(), visited_verts))
return false;
}
return true;
}
std::ostream& print_tree(std::ostream & out, vertex* v) const {
@ -553,43 +661,37 @@ public:
return out;
}
void try_add_equation_with_fixed_tables(const vertex* v) {
try_add_equation_with_lp_fixed_tables(v);
void try_add_equation_with_fixed_tables(unsigned row_index, const vertex* v) {
try_add_equation_with_lp_fixed_tables(row_index, v);
try_add_equation_with_val_table(v);
}
void create_fixed_eqs(const vertex* v) {
try_add_equation_with_fixed_tables(v);
void handle_fixed_phase(unsigned row_index) {
if (!fixed_phase())
return;
const vertex* v = m_root;
try_add_equation_with_fixed_tables(row_index, v);
for (auto e: v->edges())
try_add_equation_with_fixed_tables(e.target());
try_add_equation_with_fixed_tables(row_index, e.target());
}
void handle_fixed_phase() {
create_fixed_eqs(m_root);
}
void cheap_eq_tree(unsigned row_index) {
TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";);
if (!check_insert(m_visited_rows, row_index))
return; // already explored
create_root(row_index);
if (m_root == nullptr) {
reset_cheap_eq _reset(*this);
TRACE("cheap_eq_det", tout << "row_index = " << row_index << "\n";);
if (!check_insert(m_visited_rows, row_index))
return;
}
TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";);
create_root(row_index);
if (!m_root)
return;
TRACE("cheap_eq", tout << "tree = "; print_tree(tout, m_root) << "\n";);
SASSERT(tree_is_correct());
if (fixed_phase())
handle_fixed_phase();
TRACE("cheap_eq", tout << "done for row_index " << row_index << "\n";);
TRACE("cheap_eq", tout << "tree size = " << verts_size(););
delete_tree(m_root);
m_root = nullptr;
set_fixed_vertex(nullptr);
m_fixed_vertex_explanation.clear();
m_vals_to_verts.reset();
m_vals_to_verts_neg.reset();
m_pol.reset();
m_vertices.reset();
handle_fixed_phase(row_index);
TRACE("cheap_eq",
tout << "done for row_index " << row_index << "\n";
tout << "tree size = " << verts_size(););
}
std::ostream& print_row(std::ostream & out, unsigned row_index) const {
@ -643,71 +745,7 @@ public:
return false;
table.insert(j);
return true;
}
void go_over_vertex_column(vertex * v) {
lpvar j = v->column();
if (!check_insert(m_visited_columns, j))
return;
for (const auto & c : lp().get_column(j)) {
unsigned row_index = c.var();
if (!check_insert(m_visited_rows, row_index))
continue;
vertex *u = get_child_from_row(row_index, v);
if (u) {
// debug
// if (verts_size() > 3) {
// std::cout << "big tree\n";
// TRACE("cheap_eq", print_tree(tout, m_root););
// exit(1);
// } // end debug
explore_under(u);
}
}
}
void explore_under(vertex * v) {
check_for_eq_and_add_to_val_tables(v);
go_over_vertex_column(v);
}
}
// In case of only one non fixed column, and the function returns true,
// this column would be represened by x.
bool is_tree_offset_row( unsigned row_index,
unsigned & x, unsigned & y, int & polarity ) const {
x = y = UINT_MAX;
const row_cell<mpq>* x_cell = nullptr;
const row_cell<mpq>* y_cell = nullptr;
const auto & row = lp().get_row(row_index);
for (unsigned k = 0; k < row.size(); k++) {
const auto& c = row[k];
if (column_is_fixed(c.var()))
continue;
if (not_set(x)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
x = c.var();
x_cell = & c;
} else {
return false;
}
} else if (not_set(y)) {
if (c.coeff().is_one() || c.coeff().is_minus_one()) {
y = c.var();
y_cell = & c;
} else
return false;
} else
return false;
}
if (is_set(x)) {
if (is_set(y))
polarity = x_cell->coeff().is_pos() == y_cell->coeff().is_pos()? -1 : 1;
else
polarity = 1;
return true;
}
return false;
}
};
}

View file

@ -27,7 +27,7 @@ template bool lp::vectors_are_equal<lp::mpq>(vector<lp::mpq > const&, vector<lp:
void lp::lp_settings::updt_params(params_ref const& _p) {
smt_params_helper p(_p);
m_enable_hnf = p.arith_enable_hnf();
m_cheap_eqs = p.arith_propagate_eqs();
m_propagate_eqs = p.arith_propagate_eqs();
print_statistics = p.arith_print_stats();
m_print_external_var_name = p.arith_print_ext_var_names();
report_frequency = p.arith_rep_freq();

View file

@ -99,6 +99,7 @@ template <typename X> bool is_epsilon_small(const X & v, const double& eps);
class lp_resource_limit {
public:
virtual ~lp_resource_limit() = default;
virtual bool get_cancel_flag() = 0;
};
@ -125,7 +126,7 @@ struct statistics {
unsigned m_cross_nested_forms;
unsigned m_grobner_calls;
unsigned m_grobner_conflicts;
unsigned m_cheap_eqs;
unsigned m_offset_eqs;
statistics() { reset(); }
void reset() { memset(this, 0, sizeof(*this)); }
void collect_statistics(::statistics& st) const {
@ -146,7 +147,7 @@ struct statistics {
st.update("arith-horner-cross-nested-forms", m_cross_nested_forms);
st.update("arith-grobner-calls", m_grobner_calls);
st.update("arith-grobner-conflicts", m_grobner_conflicts);
st.update("arith-cheap-eqs", m_cheap_eqs);
st.update("arith-offset-eqs", m_offset_eqs);
}
};
@ -241,10 +242,10 @@ private:
unsigned m_nlsat_delay;
bool m_enable_hnf { true };
bool m_print_external_var_name { false };
bool m_cheap_eqs { false };
bool m_propagate_eqs { false };
public:
bool print_external_var_name() const { return m_print_external_var_name; }
bool cheap_eqs() const { return m_cheap_eqs;}
bool propagate_eqs() const { return m_propagate_eqs;}
unsigned hnf_cut_period() const { return m_hnf_cut_period; }
void set_hnf_cut_period(unsigned period) { m_hnf_cut_period = period; }
unsigned random_next() { return m_rand(); }

View file

@ -71,6 +71,7 @@ namespace polynomial {
template<typename ValManager, typename Value = typename ValManager::numeral>
class var2value {
public:
virtual ~var2value() = default;
virtual ValManager & m() const = 0;
virtual bool contains(var x) const = 0;
virtual Value const & operator()(var x) const = 0;
@ -100,6 +101,7 @@ namespace polynomial {
struct display_var_proc {
virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; }
virtual ~display_var_proc() = default;
};
class polynomial;
@ -228,6 +230,7 @@ namespace polynomial {
del_eh * m_next;
public:
del_eh():m_next(nullptr) {}
virtual ~del_eh() = default;
virtual void operator()(polynomial * p) = 0;
};

View file

@ -175,6 +175,8 @@ namespace upolynomial {
m_current_size = 0;
}
virtual ~factorization_combination_iterator_base() = default;
/**
\brief Returns the factors we are enumerating through.
*/

View file

@ -37,6 +37,7 @@ namespace realclosure {
class mk_interval {
public:
virtual ~mk_interval() = default;
virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) = 0;
};

View file

@ -15,6 +15,7 @@ Notes:
#include "math/simplex/bit_matrix.h"
#include "util/stopwatch.h"
#include "util/trace.h"
#include <cstring>

View file

@ -42,6 +42,7 @@ public:
};
struct display_var_proc {
virtual ~display_var_proc() = default;
virtual void operator()(std::ostream & out, var x) const { out << "x" << x; }
};

View file

@ -38,7 +38,7 @@ array_factory::array_factory(ast_manager & m, model_core & md):
}
/**
\brieft Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s.
\brief Return as-array[f] where f is a fresh function symbol with the right domain and range for the array sort s.
Store in fi the function interpretation for f.
*/
expr * array_factory::mk_array_interp(sort * s, func_interp * & fi) {

View file

@ -280,6 +280,9 @@ void func_interp::compress() {
}
// other compression, if else is a default branch.
// or function encode identity.
#if 0
// breaks array interpretations
// #5604
if (m().is_false(m_else)) {
expr_ref new_else(get_interp(), m());
for (func_entry * curr : m_entries) {
@ -291,7 +294,9 @@ void func_interp::compress() {
m().dec_ref(m_else);
m_else = new_else;
}
else if (!m_entries.empty() && is_identity()) {
else
#endif
if (!m_entries.empty() && is_identity()) {
for (func_entry * curr : m_entries) {
curr->deallocate(m(), m_arity);
}
@ -335,14 +340,11 @@ expr * func_interp::get_interp_core() const {
expr * r = m_else;
ptr_buffer<expr> vars;
for (func_entry * curr : m_entries) {
if (m_else == curr->get_result()) {
if (m_else == curr->get_result())
continue;
}
if (vars.empty()) {
for (unsigned i = 0; i < m_arity; i++) {
if (vars.empty())
for (unsigned i = 0; i < m_arity; i++)
vars.push_back(m().mk_var(i, curr->get_arg(i)->get_sort()));
}
}
ptr_buffer<expr> eqs;
for (unsigned i = 0; i < m_arity; i++) {
eqs.push_back(m().mk_eq(vars[i], curr->get_arg(i)));

View file

@ -238,15 +238,13 @@ void model::compress(bool force_inline) {
top_sort ts(m);
collect_deps(ts);
ts.topological_sort();
for (func_decl * f : ts.top_sorted()) {
for (func_decl * f : ts.top_sorted())
cleanup_interp(ts, f, force_inline);
}
func_decl_set removed;
ts.m_occur_count.reset();
for (func_decl * f : ts.top_sorted()) {
for (func_decl * f : ts.top_sorted())
collect_occs(ts, f);
}
// remove auxiliary declarations that are not used.
for (func_decl * f : ts.top_sorted()) {
@ -256,7 +254,8 @@ void model::compress(bool force_inline) {
removed.insert(f);
}
}
if (removed.empty()) break;
if (removed.empty())
break;
TRACE("model", tout << "remove\n"; for (func_decl* f : removed) tout << f->get_name() << "\n";);
remove_decls(m_decls, removed);
remove_decls(m_func_decls, removed);
@ -268,12 +267,14 @@ void model::compress(bool force_inline) {
void model::collect_deps(top_sort& ts) {
for (auto const& kv : m_finterp) {
ts.insert(kv.m_key, collect_deps(ts, kv.m_value));
}
for (auto const& kv : m_interp) {
ts.insert(kv.m_key, collect_deps(ts, kv.m_value.second));
}
recfun::util u(m);
for (auto const& [f, v] : m_finterp)
if (!u.has_def(f))
ts.insert(f, collect_deps(ts, v));
for (auto const& [f,v] : m_interp)
if (!u.has_def(f))
ts.insert(f, collect_deps(ts, v.second));
}
struct model::deps_collector {
@ -334,6 +335,7 @@ model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) {
*/
void model::cleanup_interp(top_sort& ts, func_decl* f, bool force_inline) {
unsigned pid = ts.partition_id(f);
expr * e1 = get_const_interp(f);
if (e1) {

View file

@ -162,7 +162,6 @@ struct evaluator_cfg : public default_rewriter_cfg {
result_pr = nullptr;
family_id fid = f->get_family_id();
bool _is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f);
func_decl* g = nullptr;
br_status st = BR_FAILED;
#if 0
struct pp {

View file

@ -122,6 +122,7 @@ namespace datalog {
class register_engine_base {
public:
virtual ~register_engine_base() = default;
virtual engine_base* mk_engine(DL_ENGINE engine_type) = 0;
virtual void set_context(context* ctx) = 0;
};

View file

@ -42,7 +42,7 @@ namespace datalog {
/**
\brief Number of rules longer than two that contain this pair.
This number is being updated by \c add_rule and \remove rule. Even though between
This number is being updated by \c add_rule and \c remove_rule. Even though between
adding a rule and removing it, the length of a rule can decrease without this pair
being notified about it, it will surely see the decrease from length 3 to 2 which
the threshold for rule being counted in this counter.

View file

@ -34,7 +34,7 @@ public:
typedef obj_map<func_decl, unsigned> partition_map;
/**
\Brief Model Based Cartesian projection of lits
\brief Model Based Cartesian projection of lits
*/
void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl,
vector<expr_ref_vector> &res);

View file

@ -32,6 +32,7 @@ namespace nlsat {
class display_assumption_proc {
public:
virtual ~display_assumption_proc() = default;
virtual std::ostream& operator()(std::ostream& out, assumption a) const = 0;
};

View file

@ -131,16 +131,18 @@ namespace opt {
soft.set_value(l_undef);
}
model_ref mdl;
s().get_model(mdl);
s().get_model(mdl);
if (mdl) {
TRACE("opt", tout << *mdl << "\n";);
for (auto & soft : m_soft) {
if (!mdl->is_true(soft.s)) {
break;
update_bounds();
return;
}
soft.set_value(l_true);
assert_value(soft);
}
update_bounds();
update_assignment();
}
}
@ -151,9 +153,8 @@ namespace opt {
unsigned sz = m_soft.size();
for (unsigned i = 0; i < sz; ++i) {
auto& soft = m_soft[i];
if (soft.value != l_undef) {
if (soft.value != l_undef)
continue;
}
expr_ref_vector asms(m);
asms.push_back(soft.s);
lbool is_sat = s().check_sat(asms);

View file

@ -24,6 +24,7 @@ Notes:
#include "ast/pb_decl_plugin.h"
#include "ast/ast_smt_pp.h"
#include "ast/ast_pp_util.h"
#include "ast/ast_ll_pp.h"
#include "ast/display_dimacs.h"
#include "model/model_smt2_pp.h"
#include "tactic/goal.h"
@ -966,12 +967,12 @@ namespace opt {
tout << "Convert minimization " << orig_term << "\n";
tout << "to maxsat: " << term << "\n";
for (unsigned i = 0; i < weights.size(); ++i) {
tout << mk_pp(terms[i].get(), m) << ": " << weights[i] << "\n";
tout << mk_pp(terms.get(i), m) << ": " << weights[i] << "\n";
}
tout << "offset: " << offset << "\n";
);
std::ostringstream out;
out << orig_term << ':' << index;
out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
id = symbol(out.str());
return true;
}
@ -994,7 +995,7 @@ namespace opt {
}
neg = true;
std::ostringstream out;
out << orig_term << ':' << index;
out << mk_bounded_pp(orig_term, m) << ':' << index;
id = symbol(out.str());
return true;
}
@ -1013,7 +1014,7 @@ namespace opt {
}
neg = is_max;
std::ostringstream out;
out << orig_term << ':' << index;
out << mk_bounded_pp(orig_term, m, 2) << ':' << index;
id = symbol(out.str());
return true;
}
@ -1200,7 +1201,7 @@ namespace opt {
app* context::purify(generic_model_converter_ref& fm, expr* term) {
std::ostringstream out;
out << mk_pp(term, m);
out << mk_bounded_pp(term, m, 3);
app* q = m.mk_fresh_const(out.str(), term->get_sort());
if (!fm) fm = alloc(generic_model_converter, m, "opt");
if (m_arith.is_int_real(term)) {

View file

@ -45,6 +45,7 @@ namespace opt {
class maxsat_context {
public:
virtual ~maxsat_context() = default;
virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification.
virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled?
virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver)

View file

@ -26,6 +26,7 @@ namespace opt {
class pareto_callback {
public:
virtual ~pareto_callback() = default;
virtual unsigned num_objectives() = 0;
virtual expr_ref mk_gt(unsigned i, model_ref& model) = 0;
virtual expr_ref mk_ge(unsigned i, model_ref& model) = 0;

View file

@ -989,6 +989,7 @@ namespace nlarith {
imp& m_imp;
public:
isubst(imp& i) : m_imp(i) {}
virtual ~isubst() = default;
virtual void mk_lt(poly const& p, app_ref& r) = 0;
virtual void mk_eq(poly const& p, app_ref& r) = 0;
virtual void mk_le(poly const& p, app_ref& r) {

View file

@ -96,8 +96,6 @@ namespace nlarith {
bool create_branches(app* x, unsigned nl, expr* const* lits, branch_conditions& bc);
/**
\brief Extract non-linear variables from ground formula.
\requires a ground formula.
*/
void extract_non_linear(expr* e, ptr_vector<app>& nl_vars);

View file

@ -34,6 +34,7 @@ namespace qe {
class i_nnf_atom {
public:
virtual ~i_nnf_atom() = default;
virtual void operator()(expr* e, bool pol, expr_ref& result) = 0;
};

View file

@ -40,6 +40,7 @@ namespace sat {
class literal_occs_fun {
public:
virtual double operator()(literal l) = 0;
virtual ~literal_occs_fun() = default;
};

View file

@ -1893,7 +1893,6 @@ namespace sat {
void solver::init_ext_assumptions() {
if (m_ext && m_ext->tracking_assumptions()) {
m_ext_assumption_set.reset();
unsigned trail_size = m_trail.size();
if (!inconsistent())
m_ext->add_assumptions(m_ext_assumption_set);
}

View file

@ -301,26 +301,27 @@ namespace arith {
m_explanation.add_pair(j, v);
}
void solver::add_eq(lpvar u, lpvar v, lp::explanation const& e) {
bool solver::add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) {
if (s().inconsistent())
return;
return false;
theory_var uv = lp().local_to_external(u); // variables that are returned should have external representations
theory_var vv = lp().local_to_external(v); // so maybe better to have them already transformed to external form
if (is_equal(uv, vv))
return;
return false;
enode* n1 = var2enode(uv);
enode* n2 = var2enode(vv);
expr* e1 = n1->get_expr();
expr* e2 = n2->get_expr();
if (m.is_ite(e1) || m.is_ite(e2))
return;
if (!is_fixed && !a.is_numeral(e1) && !a.is_numeral(e2) && (m.is_ite(e1) || m.is_ite(e2)))
return false;
if (e1->get_sort() != e2->get_sort())
return;
return false;
reset_evidence();
for (auto ev : e)
set_evidence(ev.ci(), m_core, m_eqs);
auto* jst = euf::th_explain::propagate(*this, m_core, m_eqs, n1, n2);
ctx.propagate(n1, n2, jst->to_index());
return true;
}
bool solver::bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const {
@ -627,7 +628,7 @@ namespace arith {
anum const& an = nl_value(v, *m_a1);
if (a.is_int(o) && !m_nla->am().is_int(an))
value = a.mk_numeral(rational::zero(), a.is_int(o));
value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o));
//value = a.mk_numeral(m_nla->am(), nl_value(v, *m_a1), a.is_int(o));
}
else if (v != euf::null_theory_var) {
rational r = get_value(v);

View file

@ -450,7 +450,7 @@ namespace arith {
lp::lar_solver& lp() { return *m_solver; }
lp::lar_solver const& lp() const { return *m_solver; }
bool is_equal(theory_var x, theory_var y) const;
void add_eq(lpvar u, lpvar v, lp::explanation const& e);
bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed);
void consume(rational const& v, lp::constraint_index j);
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const;
};

View file

@ -621,8 +621,7 @@ namespace array {
continue;
// arrays used as indices in other arrays have to be treated as shared issue #3532, #3529
if (ctx.is_shared(r) || is_shared_arg(r))
roots.push_back(r->get_th_var(get_id()));
roots.push_back(r->get_th_var(get_id()));
r->mark1();
to_unmark.push_back(r);
}

View file

@ -120,8 +120,8 @@ namespace array {
SASSERT(!n || !n->is_attached_to(get_id()));
if (!n)
n = mk_enode(e, false);
SASSERT(!n->is_attached_to(get_id()));
mk_var(n);
if (!n->is_attached_to(get_id()))
mk_var(n);
for (auto* arg : euf::enode_args(n))
ensure_var(arg);
switch (a->get_decl_kind()) {

View file

@ -119,7 +119,7 @@ namespace array {
bool solver::must_have_different_model_values(theory_var v1, theory_var v2) {
euf::enode* else1 = nullptr, * else2 = nullptr;
euf::enode* n1 = var2enode(v1), *n2 = var2enode(v2);
euf::enode* n1 = var2enode(v1);
expr* e1 = n1->get_expr();
if (!a.is_array(e1))
return true;

View file

@ -46,6 +46,7 @@ namespace euf {
value = r->get_expr();
else
value = factory.get_fresh_value(srt);
(void)s;
TRACE("model", tout << s.bpp(r) << " := " << value << "\n";);
values.set(id, value);
expr_ref_vector* vals = nullptr;

View file

@ -22,6 +22,32 @@ Author:
namespace euf {
void solver::add_auto_relevant(expr* e) {
if (!relevancy_enabled())
return;
for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes)
m_auto_relevant_lim.push_back(m_auto_relevant.size());
// std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
m_auto_relevant.push_back(e);
}
void solver::pop_relevant(unsigned n) {
if (m_auto_relevant_scopes >= n) {
m_auto_relevant_scopes -= n;
return;
}
n -= m_auto_relevant_scopes;
m_auto_relevant_scopes = 0;
unsigned top = m_auto_relevant_lim.size() - n;
unsigned lim = m_auto_relevant_lim[top];
m_auto_relevant_lim.shrink(top);
m_auto_relevant.shrink(lim);
}
void solver::push_relevant() {
++m_auto_relevant_scopes;
}
bool solver::is_relevant(expr* e) const {
return m_relevant_expr_ids.get(e->get_id(), true);
}
@ -31,11 +57,11 @@ namespace euf {
}
void solver::ensure_dual_solver() {
if (!m_dual_solver) {
m_dual_solver = alloc(sat::dual_solver, s().rlimit());
for (unsigned i = s().num_user_scopes(); i-- > 0; )
m_dual_solver->push();
}
if (m_dual_solver)
return;
m_dual_solver = alloc(sat::dual_solver, s().rlimit());
for (unsigned i = s().num_user_scopes(); i-- > 0; )
m_dual_solver->push();
}
/**
@ -65,8 +91,6 @@ namespace euf {
bool solver::init_relevancy() {
m_relevant_expr_ids.reset();
bool_vector visited;
ptr_vector<expr> todo;
if (!relevancy_enabled())
return true;
if (!m_dual_solver)
@ -77,12 +101,21 @@ namespace euf {
for (enode* n : m_egraph.nodes())
max_id = std::max(max_id, n->get_expr_id());
m_relevant_expr_ids.resize(max_id + 1, false);
ptr_vector<expr>& todo = m_relevant_todo;
bool_vector& visited = m_relevant_visited;
auto const& core = m_dual_solver->core();
todo.reset();
for (auto lit : core) {
expr* e = m_bool_var2expr.get(lit.var(), nullptr);
if (e)
todo.push_back(e);
}
#if 0
std::cout << "init-relevant\n";
for (expr* e : m_auto_relevant)
std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
#endif
todo.append(m_auto_relevant);
for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i];
if (visited.get(e->get_id(), false))
@ -114,6 +147,9 @@ namespace euf {
todo.push_back(arg);
}
for (auto * e : todo)
visited[e->get_id()] = false;
TRACE("euf",
for (enode* n : m_egraph.nodes())
if (is_relevant(n))

View file

@ -183,6 +183,7 @@ namespace euf {
}
void solver::propagate(literal lit, ext_justification_idx idx) {
add_auto_relevant(bool_var2expr(lit.var()));
s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx));
}
@ -527,6 +528,7 @@ namespace euf {
m_egraph.push();
if (m_dual_solver)
m_dual_solver->push();
push_relevant();
}
void solver::pop(unsigned n) {
@ -536,6 +538,7 @@ namespace euf {
e->pop(n);
si.pop(n);
m_egraph.pop(n);
pop_relevant(n);
scope const & sc = m_scopes[m_scopes.size() - n];
for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) {
bool_var v = m_var_trail[i];
@ -994,7 +997,7 @@ namespace euf {
::solver::push_eh_t& push_eh,
::solver::pop_eh_t& pop_eh,
::solver::fresh_eh_t& fresh_eh) {
m_user_propagator = alloc(user::solver, *this);
m_user_propagator = alloc(user_solver::solver, *this);
m_user_propagator->add(ctx, push_eh, pop_eh, fresh_eh);
for (unsigned i = m_scopes.size(); i-- > 0; )
m_user_propagator->push();

View file

@ -99,9 +99,8 @@ namespace euf {
sat::lookahead* m_lookahead = nullptr;
ast_manager* m_to_m;
sat::sat_internalizer* m_to_si;
scoped_ptr<euf::ackerman> m_ackerman;
scoped_ptr<sat::dual_solver> m_dual_solver;
user::solver* m_user_propagator = nullptr;
scoped_ptr<euf::ackerman> m_ackerman;
user_solver::solver* m_user_propagator = nullptr;
th_solver* m_qsolver = nullptr;
unsigned m_generation = 0;
mutable ptr_vector<expr> m_todo;
@ -182,6 +181,8 @@ namespace euf {
// relevancy
bool_vector m_relevant_expr_ids;
bool_vector m_relevant_visited;
ptr_vector<expr> m_relevant_todo;
void ensure_dual_solver();
bool init_relevancy();
@ -363,6 +364,11 @@ namespace euf {
// relevancy
bool m_relevancy = true;
scoped_ptr<sat::dual_solver> m_dual_solver;
ptr_vector<expr> m_auto_relevant;
unsigned_vector m_auto_relevant_lim;
unsigned m_auto_relevant_scopes = 0;
bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; }
void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false; }
void add_root(unsigned n, sat::literal const* lits);
@ -377,6 +383,9 @@ namespace euf {
void track_relevancy(sat::bool_var v);
bool is_relevant(expr* e) const;
bool is_relevant(enode* n) const;
void add_auto_relevant(expr* e);
void pop_relevant(unsigned n);
void push_relevant();
// model construction

View file

@ -52,6 +52,9 @@ namespace pb {
constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k):
m_tag(t), m_lit(l), m_size(sz), m_obj_size(osz), m_id(id), m_k(k) {
}
virtual ~constraint() = default;
sat::ext_constraint_idx cindex() const { return sat::constraint_base::mem2base(this); }
void deallocate(small_object_allocator& a) { a.deallocate(obj_size(), sat::constraint_base::mem2base_ptr(this)); }
unsigned id() const { return m_id; }

View file

@ -35,6 +35,7 @@ namespace pb {
class solver_interface {
public:
virtual ~solver_interface() = default;
virtual lbool value(bool_var v) const = 0;
virtual lbool value(literal lit) const = 0;
virtual bool is_false(literal lit) const = 0;

View file

@ -32,8 +32,8 @@ namespace q {
<< mk_bounded_pp(rhs, m, 2);
}
std::ostream& binding::display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const {
for (unsigned i = 0; i < num_nodes; ++i)
std::ostream& binding::display(euf::solver& ctx, std::ostream& out) const {
for (unsigned i = 0; i < size(); ++i)
out << ctx.bpp((*this)[i]) << " ";
return out;
}
@ -46,7 +46,7 @@ namespace q {
if (!b)
return out;
do {
b->display(ctx, num_decls(), out) << "\n";
b->display(ctx, out) << "\n";
b = b->next();
}
while (b != m_bindings);

View file

@ -22,6 +22,7 @@ Author:
#include "ast/euf/euf_enode.h"
#include "sat/smt/euf_solver.h"
namespace q {
struct lit {
@ -35,25 +36,7 @@ namespace q {
std::ostream& display(std::ostream& out) const;
};
struct binding : public dll_base<binding> {
app* m_pattern;
unsigned m_max_generation;
unsigned m_min_top_generation;
unsigned m_max_top_generation;
euf::enode* m_nodes[0];
binding(app* pat, unsigned max_generation, unsigned min_top, unsigned max_top):
m_pattern(pat),
m_max_generation(max_generation),
m_min_top_generation(min_top),
m_max_top_generation(max_top) {}
euf::enode* const* nodes() { return m_nodes; }
euf::enode* operator[](unsigned i) const { return m_nodes[i]; }
std::ostream& display(euf::solver& ctx, unsigned num_nodes, std::ostream& out) const;
};
struct binding;
struct clause {
unsigned m_index;
@ -62,10 +45,10 @@ namespace q {
unsigned m_watch = 0;
sat::literal m_literal = sat::null_literal;
q::quantifier_stat* m_stat = nullptr;
binding* m_bindings = nullptr;
binding* m_bindings = nullptr;
clause(ast_manager& m, unsigned idx): m_index(idx), m_q(m) {}
clause(ast_manager& m, unsigned idx) : m_index(idx), m_q(m) {}
std::ostream& display(euf::solver& ctx, std::ostream& out) const;
lit const& operator[](unsigned i) const { return m_lits[i]; }
@ -76,6 +59,70 @@ namespace q {
quantifier* q() const { return m_q; }
};
struct binding : public dll_base<binding> {
clause* c;
app* m_pattern;
unsigned m_max_generation;
unsigned m_min_top_generation;
unsigned m_max_top_generation;
euf::enode* m_nodes[0];
binding(clause& c, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top):
c(&c),
m_pattern(pat),
m_max_generation(max_generation),
m_min_top_generation(min_top),
m_max_top_generation(max_top) {}
euf::enode* const* nodes() { return m_nodes; }
euf::enode* operator[](unsigned i) const { return m_nodes[i]; }
std::ostream& display(euf::solver& ctx, std::ostream& out) const;
unsigned size() const { return c->num_decls(); }
quantifier* q() const { return c->m_q; }
bool eq(binding const& other) const {
if (q() != other.q())
return false;
for (unsigned i = size(); i-- > 0; )
if ((*this)[i] != other[i])
return false;
return true;
}
};
struct binding_khasher {
unsigned operator()(binding const* f) const { return f->q()->get_id(); }
};
struct binding_chasher {
unsigned operator()(binding const* f, unsigned idx) const { return f->m_nodes[idx]->hash(); }
};
struct binding_hash_proc {
unsigned operator()(binding const* f) const {
return get_composite_hash<binding*, binding_khasher, binding_chasher>(const_cast<binding*>(f), f->size());
}
};
struct binding_eq_proc {
bool operator()(binding const* a, binding const* b) const { return a->eq(*b); }
};
typedef ptr_hashtable<binding, binding_hash_proc, binding_eq_proc> bindings;
inline std::ostream& operator<<(std::ostream& out, binding const& f) {
out << "[fp " << f.q()->get_id() << ":";
for (unsigned i = 0; i < f.size(); ++i)
out << " " << f[i]->get_expr_id();
return out << "]";
}
struct justification {
expr* m_lhs, *m_rhs;
bool m_sign;

View file

@ -54,7 +54,11 @@ namespace q {
m_eval(ctx),
m_qstat_gen(m, ctx.get_region()),
m_inst_queue(*this, ctx),
m_infer_patterns(m, ctx.get_config())
m_infer_patterns(m, ctx.get_config()),
m_new_defs(m),
m_new_proofs(m),
m_dn(m),
m_nnf(m, m_dn)
{
std::function<void(euf::enode*, euf::enode*)> _on_merge =
[&](euf::enode* root, euf::enode* other) {
@ -80,7 +84,7 @@ namespace q {
unsigned num_patterns = q->get_num_patterns();
for (unsigned i = 0; i < num_patterns; i++)
ensure_ground_enodes(q->get_pattern(i));
for (auto lit : c.m_lits) {
for (auto const& lit : c.m_lits) {
ensure_ground_enodes(lit.lhs);
ensure_ground_enodes(lit.rhs);
}
@ -105,6 +109,20 @@ namespace q {
m_eval.explain(l, justification::from_index(idx), r, probing);
}
quantifier_ref ematch::nnf_skolem(quantifier* q) {
expr_ref r(m);
proof_ref p(m);
m_new_defs.reset();
m_new_proofs.reset();
m_nnf(q, m_new_defs, m_new_proofs, r, p);
SASSERT(is_quantifier(r));
for (expr* d : m_new_defs)
m_qs.add_unit(m_qs.mk_literal(d));
CTRACE("q", r != q, tout << mk_pp(q, m) << " -->\n" << r << "\n" << m_new_defs << "\n";);
return quantifier_ref(to_quantifier(r), m);
}
std::ostream& ematch::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const {
auto& j = justification::from_index(idx);
auto& c = j.m_clause;
@ -186,7 +204,7 @@ namespace q {
void ematch::init_watch(clause& c) {
unsigned idx = c.index();
for (auto lit : c.m_lits) {
for (auto const& lit : c.m_lits) {
if (!is_ground(lit.lhs))
init_watch(lit.lhs, idx);
if (!is_ground(lit.rhs))
@ -218,38 +236,71 @@ namespace q {
}
};
binding* ematch::alloc_binding(unsigned n, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top) {
unsigned sz = sizeof(binding) + sizeof(euf::enode* const*)*n;
void* mem = ctx.get_region().allocate(sz);
return new (mem) binding(pat, max_generation, min_top, max_top);
}
binding* ematch::tmp_binding(clause& c, app* pat, euf::enode* const* b) {
if (c.num_decls() > m_tmp_binding_capacity) {
void* mem = memory::allocate(sizeof(binding) + c.num_decls() * sizeof(euf::enode*));
m_tmp_binding = new (mem) binding(c, pat, 0, 0, 0);
m_tmp_binding_capacity = c.num_decls();
}
euf::enode* const* ematch::alloc_binding(clause& c, euf::enode* const* _binding) {
unsigned sz = sizeof(euf::enode* const*) * c.num_decls();
euf::enode** binding = (euf::enode**)ctx.get_region().allocate(sz);
for (unsigned i = 0; i < c.num_decls(); ++i)
binding[i] = _binding[i];
return binding;
for (unsigned i = c.num_decls(); i-- > 0; )
m_tmp_binding->m_nodes[i] = b[i];
m_tmp_binding->m_pattern = pat;
m_tmp_binding->c = &c;
return m_tmp_binding.get();
}
void ematch::add_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) {
binding* ematch::alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top) {
binding* b = tmp_binding(c, pat, _binding);
if (m_bindings.contains(b))
return nullptr;
for (unsigned i = c.num_decls(); i-- > 0; )
b->m_nodes[i] = b->m_nodes[i]->get_root();
if (m_bindings.contains(b))
return nullptr;
unsigned n = c.num_decls();
binding* b = alloc_binding(n, pat, max_generation, min_top, max_top);
unsigned sz = sizeof(binding) + sizeof(euf::enode* const*) * n;
void* mem = ctx.get_region().allocate(sz);
b = new (mem) binding(c, pat, max_generation, min_top, max_top);
b->init(b);
for (unsigned i = 0; i < n; ++i)
b->m_nodes[i] = _binding[i];
binding::push_to_front(c.m_bindings, b);
ctx.push(remove_binding(ctx, c, b));
++m_stats.m_num_delayed_bindings;
b->m_nodes[i] = _binding[i];
m_bindings.insert(b);
ctx.push(insert_map<bindings, binding*>(m_bindings, b));
return b;
}
euf::enode* const* ematch::copy_nodes(clause& c, euf::enode* const* nodes) {
unsigned sz = sizeof(euf::enode* const*) * c.num_decls();
euf::enode** new_nodes = (euf::enode**)ctx.get_region().allocate(sz);
for (unsigned i = 0; i < c.num_decls(); ++i)
new_nodes[i] = nodes[i];
return new_nodes;
}
void ematch::on_binding(quantifier* q, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_gen, unsigned max_gen) {
TRACE("q", tout << "on-binding " << mk_pp(q, m) << "\n";);
unsigned idx = m_q2clauses[q];
clause& c = *m_clauses[idx];
bool new_propagation = false;
if (!propagate(false, _binding, max_generation, c, new_propagation))
add_binding(c, pat, _binding, max_generation, min_gen, max_gen);
binding* b = alloc_binding(c, pat, _binding, max_generation, min_gen, max_gen);
if (!b)
return;
TRACE("q", b->display(ctx, tout << "on-binding " << mk_pp(q, m) << "\n") << "\n";);
if (false && propagate(false, _binding, max_generation, c, new_propagation))
return;
binding::push_to_front(c.m_bindings, b);
ctx.push(remove_binding(ctx, c, b));
++m_stats.m_num_delayed_bindings;
}
bool ematch::propagate(bool is_owned, euf::enode* const* binding, unsigned max_generation, clause& c, bool& propagated) {
@ -272,7 +323,7 @@ namespace q {
if (ev == l_undef && max_generation > m_generation_propagation_threshold)
return false;
if (!is_owned)
binding = alloc_binding(c, binding);
binding = copy_nodes(c, binding);
auto j_idx = mk_justification(idx, c, binding);
@ -308,17 +359,14 @@ namespace q {
return true;
}
void ematch::instantiate(binding& b, clause& c) {
void ematch::instantiate(binding& b) {
if (m_stats.m_num_instantiations > ctx.get_config().m_qi_max_instances)
return;
unsigned max_generation = b.m_max_generation;
max_generation = std::max(max_generation, c.m_stat->get_generation());
c.m_stat->update_max_generation(max_generation);
fingerprint * f = add_fingerprint(c, b, max_generation);
if (!f)
return;
m_inst_queue.insert(f);
m_stats.m_num_instantiations++;
max_generation = std::max(max_generation, b.c->m_stat->get_generation());
b.c->m_stat->update_max_generation(max_generation);
m_stats.m_num_instantiations++;
m_inst_queue.insert(&b);
}
void ematch::add_instantiation(clause& c, binding& b, sat::literal lit) {
@ -326,35 +374,6 @@ namespace q {
ctx.propagate(lit, mk_justification(UINT_MAX, c, b.nodes()));
}
void ematch::set_tmp_binding(fingerprint& fp) {
binding& b = *fp.b;
clause& c = *fp.c;
if (c.num_decls() > m_tmp_binding_capacity) {
void* mem = memory::allocate(sizeof(binding) + c.num_decls()*sizeof(euf::enode*));
m_tmp_binding = new (mem) binding(b.m_pattern, 0, 0, 0);
m_tmp_binding_capacity = c.num_decls();
}
fp.b = m_tmp_binding.get();
for (unsigned i = c.num_decls(); i-- > 0; )
fp.b->m_nodes[i] = b[i];
}
fingerprint* ematch::add_fingerprint(clause& c, binding& b, unsigned max_generation) {
fingerprint fp(c, b, max_generation);
if (m_fingerprints.contains(&fp))
return nullptr;
set_tmp_binding(fp);
for (unsigned i = c.num_decls(); i-- > 0; )
fp.b->m_nodes[i] = fp.b->m_nodes[i]->get_root();
if (m_fingerprints.contains(&fp))
return nullptr;
fingerprint* f = new (ctx.get_region()) fingerprint(c, b, max_generation);
m_fingerprints.insert(f);
ctx.push(insert_map<fingerprints, fingerprint*>(m_fingerprints, f));
return f;
}
sat::literal ematch::instantiate(clause& c, euf::enode* const* binding, lit const& l) {
expr_ref_vector _binding(m);
for (unsigned i = 0; i < c.num_decls(); ++i)
@ -427,7 +446,10 @@ namespace q {
cl->m_literal.neg();
expr_ref body(mk_not(m, q->get_expr()), m);
q = m.update_quantifier(q, forall_k, body);
}
}
q = nnf_skolem(q);
expr_ref_vector ors(m);
flatten_or(q->get_expr(), ors);
for (expr* arg : ors)
@ -547,11 +569,16 @@ namespace q {
}
bool ematch::unit_propagate() {
return false;
return ctx.get_config().m_ematching && propagate(false);
}
bool ematch::propagate(bool flush) {
m_mam->propagate();
bool propagated = flush_prop_queue();
if (m_qhead >= m_clause_queue.size())
return m_inst_queue.propagate();
return m_inst_queue.propagate() || propagated;
ctx.push(value_trail<unsigned>(m_qhead));
ptr_buffer<binding> to_remove;
for (; m_qhead < m_clause_queue.size(); ++m_qhead) {
@ -561,12 +588,13 @@ namespace q {
if (!b)
continue;
do {
if (propagate(true, b->m_nodes, b->m_max_generation, c, propagated))
do {
if (false && propagate(true, b->m_nodes, b->m_max_generation, c, propagated))
to_remove.push_back(b);
else if (flush) {
instantiate(*b, c);
instantiate(*b);
to_remove.push_back(b);
propagated = true;
}
b = b->next();
}
@ -592,21 +620,22 @@ namespace q {
TRACE("q", m_mam->display(tout););
if (propagate(false))
return true;
if (m_lazy_mam) {
if (m_lazy_mam)
m_lazy_mam->propagate();
if (propagate(false))
return true;
}
unsigned idx = 0;
for (clause* c : m_clauses) {
if (c->m_bindings)
insert_clause_in_queue(idx);
idx++;
}
if (propagate(false))
return true;
for (unsigned i = 0; i < m_clauses.size(); ++i)
if (m_clauses[i]->m_bindings)
insert_clause_in_queue(i);
if (propagate(true))
return true;
if (m_inst_queue.lazy_propagate())
return true;
for (unsigned i = 0; i < m_clauses.size(); ++i)
if (m_clauses[i]->m_bindings)
IF_VERBOSE(0, verbose_stream() << "missed propagation " << i << "\n");
TRACE("q", tout << "no more propagation\n";);
return false;
}

View file

@ -19,11 +19,11 @@ Author:
#include "util/nat_set.h"
#include "ast/quantifier_stat.h"
#include "ast/pattern/pattern_inference.h"
#include "ast/normal_forms/nnf.h"
#include "solver/solver.h"
#include "sat/smt/sat_th.h"
#include "sat/smt/q_mam.h"
#include "sat/smt/q_clause.h"
#include "sat/smt/q_fingerprint.h"
#include "sat/smt/q_queue.h"
#include "sat/smt/q_eval.h"
@ -69,7 +69,7 @@ namespace q {
ast_manager& m;
eval m_eval;
quantifier_stat_gen m_qstat_gen;
fingerprints m_fingerprints;
bindings m_bindings;
scoped_ptr<binding> m_tmp_binding;
unsigned m_tmp_binding_capacity = 0;
queue m_inst_queue;
@ -90,16 +90,16 @@ namespace q {
unsigned_vector m_clause_queue;
euf::enode_pair_vector m_evidence;
binding* alloc_binding(unsigned n, app* pat, unsigned max_generation, unsigned min_top, unsigned max_top);
euf::enode* const* alloc_binding(clause& c, euf::enode* const* _binding);
void add_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top);
euf::enode* const* copy_nodes(clause& c, euf::enode* const* _binding);
binding* tmp_binding(clause& c, app* pat, euf::enode* const* _binding);
binding* alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top);
sat::ext_justification_idx mk_justification(unsigned idx, clause& c, euf::enode* const* b);
void ensure_ground_enodes(expr* e);
void ensure_ground_enodes(clause const& c);
void instantiate(binding& b, clause& c);
void instantiate(binding& b);
sat::literal instantiate(clause& c, euf::enode* const* binding, lit const& l);
// register as callback into egraph.
@ -115,21 +115,26 @@ namespace q {
clause* clausify(quantifier* q);
lit clausify_literal(expr* arg);
fingerprint* add_fingerprint(clause& c, binding& b, unsigned max_generation);
void set_tmp_binding(fingerprint& fp);
bool flush_prop_queue();
void propagate(bool is_conflict, unsigned idx, sat::ext_justification_idx j_idx);
bool propagate(bool flush);
expr_ref_vector m_new_defs;
proof_ref_vector m_new_proofs;
defined_names m_dn;
nnf m_nnf;
quantifier_ref nnf_skolem(quantifier* q);
public:
ematch(euf::solver& ctx, solver& s);
bool operator()();
bool propagate(bool flush);
bool unit_propagate();
// void init_search();
void add(quantifier* q);

View file

@ -1,77 +0,0 @@
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
q_fingerprint.h
Abstract:
Fingerprint summary of a quantifier instantiation
Author:
Nikolaj Bjorner (nbjorner) 2021-01-24
--*/
#pragma once
#include "util/hashtable.h"
#include "ast/ast.h"
#include "ast/quantifier_stat.h"
#include "ast/euf/euf_enode.h"
#include "sat/smt/q_clause.h"
namespace q {
struct fingerprint {
clause* c;
binding* b;
unsigned m_max_generation;
unsigned size() const { return c->num_decls(); }
euf::enode* const* nodes() const { return b->nodes(); }
quantifier* q() const { return c->m_q; }
fingerprint(clause& _c, binding& _b, unsigned mg) :
c(&_c), b(&_b), m_max_generation(mg) {}
bool eq(fingerprint const& other) const {
if (c->m_q != other.c->m_q)
return false;
for (unsigned i = size(); i--> 0; )
if ((*b)[i] != (*other.b)[i])
return false;
return true;
}
};
struct fingerprint_khasher {
unsigned operator()(fingerprint const * f) const { return f->c->m_q->get_id(); }
};
struct fingerprint_chasher {
unsigned operator()(fingerprint const * f, unsigned idx) const { return f->b->m_nodes[idx]->hash(); }
};
struct fingerprint_hash_proc {
unsigned operator()(fingerprint const * f) const {
return get_composite_hash<fingerprint *, fingerprint_khasher, fingerprint_chasher>(const_cast<fingerprint*>(f), f->size());
}
};
struct fingerprint_eq_proc {
bool operator()(fingerprint const* a, fingerprint const* b) const { return a->eq(*b); }
};
typedef ptr_hashtable<fingerprint, fingerprint_hash_proc, fingerprint_eq_proc> fingerprints;
inline std::ostream& operator<<(std::ostream& out, fingerprint const& f) {
out << "[fp " << f.q()->get_id() << ":";
for (unsigned i = 0; i < f.size(); ++i)
out << " " << (*f.b)[i]->get_expr_id();
return out << "]";
}
}

View file

@ -409,16 +409,17 @@ namespace q {
unsigned m_num_args; //!< we need this information to avoid the nary *,+ crash bug
bool m_filter_candidates;
unsigned m_num_regs;
unsigned m_num_choices;
instruction * m_root;
unsigned m_num_choices = 0;
instruction * m_root = nullptr;
enode_vector m_candidates;
unsigned m_qhead = 0;
#ifdef Z3DEBUG
egraph * m_egraph;
egraph * m_egraph = nullptr;
svector<std::pair<quantifier*, app*>> m_patterns;
#endif
#ifdef _PROFILE_MAM
stopwatch m_watch;
unsigned m_counter;
unsigned m_counter = 0;
#endif
friend class compiler;
friend class code_tree_manager;
@ -492,13 +493,7 @@ namespace q {
m_root_lbl(lbl),
m_num_args(num_args),
m_filter_candidates(filter_candidates),
m_num_regs(num_args + 1),
m_num_choices(0),
m_root(nullptr) {
DEBUG_CODE(m_egraph = nullptr;);
#ifdef _PROFILE_MAM
m_counter = 0;
#endif
m_num_regs(num_args + 1) {
(void)m_lbl_hasher;
}
@ -546,16 +541,40 @@ namespace q {
return m_root;
}
void add_candidate(enode * n) {
void add_candidate(euf::solver& ctx, enode * n) {
m_candidates.push_back(n);
ctx.push(push_back_trail<enode*, false>(m_candidates));
}
void unmark(unsigned head) {
for (unsigned i = m_candidates.size(); i-- > head; ) {
enode* app = m_candidates[i];
if (app->is_marked1())
app->unmark1();
}
}
struct scoped_unmark {
unsigned m_qhead;
code_tree* t;
scoped_unmark(code_tree* t) : m_qhead(t->m_qhead), t(t) {}
~scoped_unmark() { t->unmark(m_qhead); }
};
bool has_candidates() const {
return !m_candidates.empty();
return m_qhead < m_candidates.size();
}
void reset_candidates() {
m_candidates.reset();
void save_qhead(euf::solver& ctx) {
ctx.push(value_trail<unsigned>(m_qhead));
}
enode* next_candidate() {
if (m_qhead < m_candidates.size())
return m_candidates[m_qhead++];
else
return nullptr;
}
enode_vector const & get_candidates() const {
@ -1987,33 +2006,29 @@ namespace q {
m_backtrack_stack.resize(t->get_num_choices());
}
void execute(code_tree * t) {
if (!t->has_candidates())
return;
TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout););
init(t);
if (t->filter_candidates()) {
for (unsigned i = 0; i < t->get_candidates().size(); ++i) {
enode* app = t->get_candidates()[i];
TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";);
t->save_qhead(ctx);
enode* app;
if (t->filter_candidates()) {
code_tree::scoped_unmark _unmark(t);
while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) {
TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";);
if (!app->is_marked1() && app->is_cgr()) {
if (ctx.resource_limits_exceeded() || !execute_core(t, app))
return;
execute_core(t, app);
app->mark1();
}
}
for (enode* app : t->get_candidates()) {
if (app->is_marked1())
app->unmark1();
}
}
else {
for (unsigned i = 0; i < t->get_candidates().size(); ++i) {
enode* app = t->get_candidates()[i];
TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_expr(), m) << "\n";);
if (app->is_cgr()) {
TRACE("trigger_bug", tout << "is_cgr\n";);
if (ctx.resource_limits_exceeded() || !execute_core(t, app))
return;
}
while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) {
TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";);
if (app->is_cgr())
execute_core(t, app);
}
}
}
@ -2474,17 +2489,18 @@ namespace q {
case YIELD1:
m_bindings[0] = m_registers[static_cast<const yield *>(m_pc)->m_bindings[0]];
#define ON_MATCH(NUM) \
#define ON_MATCH(NUM) \
m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \
if (!m.inc()) { \
return false; \
} \
if (!m.inc()) \
return false; \
\
m_mam.on_match(static_cast<const yield *>(m_pc)->m_qa, \
static_cast<const yield *>(m_pc)->m_pat, \
NUM, \
m_bindings.begin(), \
m_max_generation)
SASSERT(static_cast<const yield *>(m_pc)->m_qa->get_decl_sort(0) == m_bindings[0]->get_expr()->get_sort());
ON_MATCH(1);
goto backtrack;
@ -3056,29 +3072,9 @@ namespace q {
// temporary field used to collect candidates
ptr_vector<path_tree> m_todo;
enode * m_root { nullptr }; // temp field
enode * m_other { nullptr }; // temp field
bool m_check_missing_instances { false };
class pop_to_match : public trail {
mam_impl& i;
public:
pop_to_match(mam_impl& i):i(i) {}
void undo() override {
code_tree* t = i.m_to_match.back();
t->reset_candidates();
i.m_to_match.pop_back();
}
};
class reset_new_patterns : public trail {
mam_impl& i;
public:
reset_new_patterns(mam_impl& i):i(i) {}
void undo() override {
i.m_new_patterns.reset();
}
};
enode * m_root = nullptr; // temp field
enode * m_other = nullptr; // temp field
bool m_check_missing_instances = false;
enode_vector * mk_tmp_vector() {
enode_vector * r = m_pool.mk();
@ -3091,14 +3087,14 @@ namespace q {
}
void add_candidate(code_tree * t, enode * app) {
if (t) {
TRACE("mam_candidate", tout << "adding candidate:\n" << mk_ll_pp(app->get_expr(), m););
if (!t->has_candidates()) {
ctx.push(pop_to_match(*this));
m_to_match.push_back(t);
}
t->add_candidate(app);
if (!t)
return;
TRACE("q", tout << "candidate " << t << " " << ctx.bpp(app) << "\n";);
if (!t->has_candidates()) {
ctx.push(push_back_trail<code_tree*, false>(m_to_match));
m_to_match.push_back(t);
}
t->add_candidate(ctx, app);
}
void add_candidate(enode * app) {
@ -3675,14 +3671,20 @@ namespace q {
}
}
void match_new_patterns() {
unsigned m_new_patterns_qhead = 0;
void propagate_new_patterns() {
if (m_new_patterns_qhead >= m_new_patterns.size())
return;
ctx.push(value_trail<unsigned>(m_new_patterns_qhead));
TRACE("mam_new_pat", tout << "matching new patterns:\n";);
m_tmp_trees_to_delete.reset();
for (auto const& kv : m_new_patterns) {
for (; m_new_patterns_qhead < m_new_patterns.size(); ++m_new_patterns_qhead) {
if (!m.inc())
break;
quantifier * qa = kv.first;
app * mp = kv.second;
auto [qa, mp] = m_new_patterns[m_new_patterns_qhead];
SASSERT(m.is_pattern(mp));
app * p = to_app(mp->get_arg(0));
func_decl * lbl = p->get_decl();
@ -3714,7 +3716,6 @@ namespace q {
m_tmp_trees[lbl_id] = nullptr;
dealloc(tmp_tree);
}
m_new_patterns.reset();
}
public:
@ -3750,7 +3751,7 @@ namespace q {
return; // ignore multi-pattern containing ground pattern.
update_filters(qa, mp);
m_new_patterns.push_back(qp_pair(qa, mp));
ctx.push(reset_new_patterns(*this));
ctx.push(push_back_trail<qp_pair, false>(m_new_patterns));
// The matching abstract machine implements incremental
// e-matching. So, for a multi-pattern [ p_1, ..., p_n ],
// we have to make n insertions. In the i-th insertion,
@ -3761,7 +3762,6 @@ namespace q {
void reset() override {
m_trees.reset();
m_new_patterns.reset();
m_is_plbl.reset();
m_is_clbl.reset();
reset_pp_pc();
@ -3776,22 +3776,18 @@ namespace q {
return out;
}
void propagate() override {
TRACE("trigger_bug", tout << "match\n"; display(tout););
if (m_to_match_head >= m_to_match.size())
void propagate_to_match() {
if (m_to_match_head >= m_to_match.size())
return;
ctx.push(value_trail<unsigned>(m_to_match_head));
for (; m_to_match_head < m_to_match.size(); ++m_to_match_head) {
code_tree* t = m_to_match[m_to_match_head];
if (t->has_candidates()) {
m_interpreter.execute(t);
t->reset_candidates();
}
}
if (!m_new_patterns.empty()) {
match_new_patterns();
m_new_patterns.reset();
}
for (; m_to_match_head < m_to_match.size(); ++m_to_match_head)
m_interpreter.execute(m_to_match[m_to_match_head]);
}
void propagate() override {
TRACE("trigger_bug", tout << "match\n"; display(tout););
propagate_to_match();
propagate_new_patterns();
}
void rematch(bool use_irrelevant) override {

View file

@ -86,13 +86,13 @@ namespace q {
m_parser.add_var("cs_factor");
}
void queue::set_values(fingerprint& f, float cost) {
void queue::set_values(binding& f, float cost) {
quantifier_stat * stat = f.c->m_stat;
quantifier* q = f.q();
app* pat = f.b->m_pattern;
app* pat = f.m_pattern;
m_vals[COST] = cost;
m_vals[MIN_TOP_GENERATION] = static_cast<float>(f.b->m_min_top_generation);
m_vals[MAX_TOP_GENERATION] = static_cast<float>(f.b->m_max_top_generation);
m_vals[MIN_TOP_GENERATION] = static_cast<float>(f.m_min_top_generation);
m_vals[MAX_TOP_GENERATION] = static_cast<float>(f.m_max_top_generation);
m_vals[INSTANCES] = static_cast<float>(stat->get_num_instances_curr_branch());
m_vals[SIZE] = static_cast<float>(stat->get_size());
m_vals[DEPTH] = static_cast<float>(stat->get_depth());
@ -108,14 +108,14 @@ namespace q {
TRACE("q_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";);
}
float queue::get_cost(fingerprint& f) {
float queue::get_cost(binding& f) {
set_values(f, 0);
float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.data());
f.c->m_stat->update_max_cost(r);
return r;
}
unsigned queue::get_new_gen(fingerprint& f, float cost) {
unsigned queue::get_new_gen(binding& f, float cost) {
set_values(f, cost);
float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.data());
return std::max(f.m_max_generation + 1, static_cast<unsigned>(r));
@ -129,7 +129,7 @@ namespace q {
}
};
void queue::insert(fingerprint* f) {
void queue::insert(binding* f) {
float cost = get_cost(*f);
if (m_new_entries.empty())
ctx.push(reset_new_entries(m_new_entries));
@ -137,7 +137,7 @@ namespace q {
}
void queue::instantiate(entry& ent) {
fingerprint & f = *ent.m_qb;
binding& f = *ent.m_qb;
quantifier * q = f.q();
unsigned num_bindings = f.size();
quantifier_stat * stat = f.c->m_stat;
@ -146,12 +146,18 @@ namespace q {
unsigned gen = get_new_gen(f, ent.m_cost);
bool new_propagation = false;
if (em.propagate(true, f.nodes(), gen, *f.c, new_propagation))
if (false && em.propagate(true, f.nodes(), gen, *f.c, new_propagation))
return;
#if 0
std::cout << mk_pp(q, m) << "\n";
std::cout << num_bindings << "\n";
for (unsigned i = 0; i < num_bindings; ++i)
std::cout << mk_pp(f[i]->get_expr(), m) << " " << mk_pp(f[i]->get_sort(), m) << "\n";
#endif
auto* ebindings = m_subst(q, num_bindings);
for (unsigned i = 0; i < num_bindings; ++i)
ebindings[i] = f.nodes()[i]->get_expr();
ebindings[i] = f[i]->get_expr();
expr_ref instance = m_subst();
ctx.get_rewriter()(instance);
if (m.is_true(instance)) {
@ -161,10 +167,13 @@ namespace q {
stat->inc_num_instances();
m_stats.m_num_instances++;
// f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") << "\n";
euf::solver::scoped_generation _sg(ctx, gen);
sat::literal result_l = ctx.mk_literal(instance);
em.add_instantiation(*f.c, *f.b, result_l);
em.add_instantiation(*f.c, f, result_l);
}
bool queue::propagate() {
@ -178,7 +187,7 @@ namespace q {
if (0 == since_last_check && ctx.resource_limits_exceeded())
break;
fingerprint& f = *curr.m_qb;
binding& f = *curr.m_qb;
if (curr.m_cost <= m_eager_cost_threshold)
instantiate(curr);
@ -223,15 +232,14 @@ namespace q {
}
}
bool instantiated = false;
unsigned idx = 0;
for (entry & e : m_delayed_entries) {
for (unsigned idx = 0; idx < m_delayed_entries.size(); ++idx) {
entry & e = m_delayed_entries[idx];
if (!e.m_instantiated && e.m_cost <= cost_limit) {
instantiated = true;
ctx.push(reset_instantiated(*this, idx));
m_stats.m_num_lazy_instances++;
instantiate(e);
}
++idx;
}
return instantiated;
}

View file

@ -20,7 +20,7 @@ Author:
#include "ast/cost_evaluator.h"
#include "ast/rewriter/cached_var_subst.h"
#include "parsers/util/cost_parser.h"
#include "sat/smt/q_fingerprint.h"
#include "sat/smt/q_clause.h"
@ -51,12 +51,12 @@ namespace q {
cost_evaluator m_evaluator;
cached_var_subst m_subst;
svector<float> m_vals;
double m_eager_cost_threshold { 0 };
double m_eager_cost_threshold = 0;
struct entry {
fingerprint * m_qb;
binding * m_qb;
float m_cost;
bool m_instantiated{ false };
entry(fingerprint * f, float c):m_qb(f), m_cost(c) {}
bool m_instantiated = false;
entry(binding * f, float c):m_qb(f), m_cost(c) {}
};
struct reset_new_entries;
struct reset_instantiated;
@ -64,18 +64,18 @@ namespace q {
svector<entry> m_new_entries;
svector<entry> m_delayed_entries;
float get_cost(fingerprint& f);
void set_values(fingerprint& f, float cost);
float get_cost(binding& f);
void set_values(binding& f, float cost);
void init_parser_vars();
void setup();
unsigned get_new_gen(fingerprint& f, float cost);
unsigned get_new_gen(binding& f, float cost);
void instantiate(entry& e);
public:
queue(ematch& em, euf::solver& ctx);
void insert(fingerprint* f);
void insert(binding* f);
bool propagate();

View file

@ -100,7 +100,7 @@ namespace q {
}
bool solver::unit_propagate() {
return ctx.get_config().m_ematching && m_ematch.propagate(false);
return m_ematch.unit_propagate();
}
euf::theory_var solver::mk_var(euf::enode* n) {

View file

@ -52,7 +52,7 @@ namespace euf {
virtual void apply_sort_cnstr(enode* n, sort* s) {}
/**
\record that an equality has been internalized.
\brief Record that an equality has been internalized.
*/
virtual void eq_internalized(enode* n) {}

View file

@ -18,7 +18,7 @@ Author:
#include "sat/smt/user_solver.h"
#include "sat/smt/euf_solver.h"
namespace user {
namespace user_solver {
solver::solver(euf::solver& ctx) :
th_euf_solver(ctx, symbol("user"), ctx.get_manager().mk_family_id("user"))

View file

@ -23,7 +23,7 @@ Author:
#include "solver/solver.h"
namespace user {
namespace user_solver {
class solver : public euf::th_euf_solver, public ::solver::propagate_callback {

View file

@ -2091,7 +2091,6 @@ namespace {
enode * n = m_registers[j2->m_reg]->get_root();
if (n->get_num_parents() == 0)
return nullptr;
unsigned num_args = n->get_num_args();
enode_vector * v = mk_enode_vector();
enode_vector::const_iterator it1 = n->begin_parents();
enode_vector::const_iterator end1 = n->end_parents();

View file

@ -684,11 +684,12 @@ bool theory_seq::branch_quat_variable(depeq const& e) {
cond = true;
}
// xs and ys cannot align
else if (!can_align_from_lhs(xs, ys) && !can_align_from_rhs(xs, ys))
else if (!can_align_from_lhs(xs, ys) && !can_align_from_rhs(xs, ys) && !can_align_from_lhs(ys, xs) && !can_align_from_rhs(ys, xs))
cond = true;
if (!cond)
return false;
literal_vector lits;
if (xs == ys) {
@ -724,7 +725,7 @@ bool theory_seq::branch_quat_variable(depeq const& e) {
}
}
else {
TRACE("seq", tout << mk_pp(x1, m) << " > " << mk_pp(y1, m) << "\n";);
TRACE("seq", tout << mk_pp(x1, m) << " >\n" << mk_pp(y1, m) << "\n";);
if (ctx.get_assignment(lit3) == l_undef) {
ctx.mark_as_relevant(lit3);
return true;
@ -1144,7 +1145,7 @@ bool theory_seq::solve_nth_eq(expr_ref_vector const& ls, expr_ref_vector const&
m.inc_ref(rhs);
m.inc_ref(ls[0]);
m_nth_eq2_cache.insert(std::make_pair(rhs, ls[0]));
ctx.push_trail(remove_obj_pair_map(m, m_nth_eq2_cache, rhs, ls[0]));
get_trail_stack().push(remove_obj_pair_map(m, m_nth_eq2_cache, rhs, ls[0]));
ls1.push_back(s);
if (!idx_is_zero) rs1.push_back(m_sk.mk_pre(s, idx));
rs1.push_back(m_util.str.mk_unit(rhs));

View file

@ -42,7 +42,7 @@ namespace smt {
seq_offset_eq(theory& th, ast_manager& m);
bool empty() const { return m_offset_equalities.empty(); }
/**
\breif determine if r1 = r2 + offset
\brief determine if r1 = r2 + offset
*/
bool find(enode* r1, enode* r2, int& offset) const;
bool contains(enode* r1, enode* r2) const { int offset = 0; return find(r1, r2, offset); }

View file

@ -12,6 +12,7 @@ Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2020-5-22
Margus Veanes 2021
--*/
@ -253,10 +254,8 @@ namespace smt {
* (accept s (i + 1) (derivative s[i] r)
*
* Acceptance of a derivative is unfolded into a disjunction over
* all derivatives. Effectively, this implements the following rule,
* but all in one step:
* (accept s i (ite c r1 r2)) =>
* c & (accept s i r1) \/ ~c & (accept s i r2)
* all derivatives. Effectively, this implements the following rule:
* (accept s i (ite c r1 r2)) => (ite c (accept s i r1) (accept s i r2))
*/
void seq_regex::propagate_accept(literal lit) {
SASSERT(!lit.sign());
@ -302,6 +301,7 @@ namespace smt {
unsigned min_len = re().min_length(r);
unsigned min_len_plus_i = u().max_plus(min_len, idx);
literal len_s_ge_min = th.m_ax.mk_ge(th.mk_len(s), min_len_plus_i);
// Acc(s,i,r) ==> |s| >= i + minlength(r)
th.propagate_lit(nullptr, 1, &lit, len_s_ge_min);
// Axiom equivalent to the above: th.add_axiom(~lit, len_s_ge_min);
@ -316,6 +316,8 @@ namespace smt {
STRACE("seq_regex_brief", tout
<< " (Warning: min_length returned 0 for"
<< " non-nullable regex)";);
// since nullable(r) = false:
// Acc(s,i,r) ==> |s|>i
th.propagate_lit(nullptr, 1, &lit, ~len_s_le_i);
}
else if (!m.is_true(is_nullable)) {
@ -327,7 +329,9 @@ namespace smt {
<< " (Warning: is_nullable did not simplify)";);
literal is_nullable_lit = th.mk_literal(is_nullable);
ctx.mark_as_relevant(is_nullable_lit);
// Acc(s,i,r) & |s|<=i ==> nullable(r)
th.add_axiom(~lit, ~len_s_le_i, is_nullable_lit);
//TODO: what if is_nullable contains an in_re
if (str().is_in_re(is_nullable))
th.add_unhandled_expr(is_nullable);
}
@ -335,14 +339,19 @@ namespace smt {
// Rule 3: derivative unfolding
literal_vector accept_next;
expr_ref hd = th.mk_nth(s, i);
expr_ref s_i = th.mk_nth(s, i);
expr_ref deriv(m);
deriv = derivative_wrapper(hd, r);
deriv = mk_derivative_wrapper(s_i, r);
STRACE("seq_regex", tout
<< "mk_derivative_wrapper: " << re().to_str(deriv) << std::endl;);
expr_ref accept_deriv(m);
accept_deriv = mk_deriv_accept(s, idx + 1, deriv);
accept_next.push_back(~lit);
accept_next.push_back(len_s_le_i);
accept_next.push_back(th.mk_literal(accept_deriv));
// Acc(s, i, r) => (|s|<=i or Acc(s, i+1, D(s_i,r)))
// where Acc(s, i+1, ite(c, t, f)) = ite(c, Acc(s, i+1, t), Acc(s, i+1, t))
// and Acc(s, i+1, r U s) = Acc(s, i+1, r) or Acc(s, i+1, s)
th.add_axiom(accept_next);
}
@ -418,20 +427,21 @@ namespace smt {
/*
Wrapper around calls to is_nullable from the seq rewriter.
Note: the nullable wrapper and derivative wrapper actually use
TODO: clean up the following:
Note: the is_nullable_wrapper and mk_derivative_wrapper actually use
different sequence rewriters; these are at:
m_seq_rewrite
(returned by seq_rw())
th.m_rewrite.m_imp->m_cfg.m_seq_rw
(private, can't be accessed directly)
As a result operations are cached separately for the nullable
and derivative calls. TBD if caching them using the same rewriter
makes any difference.
and derivative calls.
*/
expr_ref seq_regex::is_nullable_wrapper(expr* r) {
STRACE("seq_regex", tout << "nullable: " << mk_pp(r, m) << std::endl;);
expr_ref result = seq_rw().is_nullable(r);
//TODO: rewrite seems unnecessary here
rewrite(result);
STRACE("seq_regex", tout << "nullable result: " << mk_pp(result, m) << std::endl;);
@ -442,39 +452,28 @@ namespace smt {
}
/*
Wrapper around the regex symbolic derivative from the seq rewriter.
Ensures that the derivative is written in a normalized BDD form
with optimizations for if-then-else expressions involving the head.
Note: the nullable wrapper and derivative wrapper actually use
different sequence rewriters; these are at:
m_seq_rewrite
(returned by seq_rw())
th.m_rewrite.m_imp->m_cfg.m_seq_rw
(private, can't be accessed directly)
As a result operations are cached separately for the nullable
and derivative calls. TBD if caching them using the same rewriter
makes any difference.
First creates a derivatrive of r wrt x=(:var 0) and then replaces x by ele.
This will create a cached entry for the generic derivative of r that is independent of ele.
*/
expr_ref seq_regex::derivative_wrapper(expr* hd, expr* r) {
STRACE("seq_regex", tout << "derivative(" << mk_pp(hd, m) << "): " << mk_pp(r, m) << std::endl;);
expr_ref seq_regex::mk_derivative_wrapper(expr* ele, expr* r) {
STRACE("seq_regex", tout << "derivative(" << mk_pp(ele, m) << "): " << mk_pp(r, m) << std::endl;);
// Use canonical variable for head
expr_ref hd_canon(m.mk_var(0, hd->get_sort()), m);
expr_ref result(re().mk_derivative(hd_canon, r), m);
rewrite(result);
// Uses canonical variable (:var 0) for the derivative element
expr_ref der(seq_rw().mk_derivative(r), m);
// Substitute with real head
// Substitute (:var 0) with the actual element
var_subst subst(m);
expr_ref_vector sub(m);
sub.push_back(hd);
result = subst(result, sub);
sub.push_back(ele);
der = subst(der, sub);
STRACE("seq_regex", tout << "derivative result: " << mk_pp(result, m) << std::endl;);
STRACE("seq_regex", tout << "derivative result: " << mk_pp(der, m) << std::endl;);
STRACE("seq_regex_brief", tout << "d(" << state_str(r) << ")="
<< state_str(result) << " ";);
<< state_str(der) << " ";);
return result;
//TODO: simplify der further, if ele implies further simplifications
//e.g. if ele='b' then de(ite (x='a') t f) simplifies to t
return der;
}
void seq_regex::propagate_eq(expr* r1, expr* r2) {
@ -557,7 +556,7 @@ namespace smt {
literal null_lit = th.mk_literal(is_nullable);
expr_ref hd = mk_first(r, n);
expr_ref d(m);
d = derivative_wrapper(hd, r);
d = mk_derivative_wrapper(hd, r);
literal_vector lits;
lits.push_back(~lit);
@ -584,11 +583,10 @@ namespace smt {
}
/*
Given a string s, index i, and a derivative regex d, return an
Given a string s, index i, and a derivative r, return an
expression that is equivalent to
accept s i r
but which pushes accept s i r into the leaves (next derivatives to
explore).
but which pushes accept s i r into the leaves
Input r is of type regex; output is of type bool.
@ -600,14 +598,18 @@ namespace smt {
expr_ref seq_regex::mk_deriv_accept(expr* s, unsigned i, expr* r) {
vector<expr*> to_visit;
to_visit.push_back(r);
obj_map<expr, expr*> re_to_bool;
obj_map<expr, expr*> re_to_accept;
expr_ref_vector _temp_bool_owner(m); // temp owner for bools we create
// DFS
bool s_is_longer_than_i = str().min_length(s) > i;
expr* i_int = a().mk_int(i);
_temp_bool_owner.push_back(i_int);
// DFS, avoids duplicating derivative construction that has already been done
while (to_visit.size() > 0) {
expr* e = to_visit.back();
expr* econd = nullptr, *e1 = nullptr, *e2 = nullptr;
if (!re_to_bool.contains(e)) {
if (!re_to_accept.contains(e)) {
// First visit: add children
STRACE("seq_regex_verbose", tout << "1";);
if (m.is_ite(e, econd, e1, e2) ||
@ -616,36 +618,40 @@ namespace smt {
to_visit.push_back(e2);
}
// Mark first visit by adding nullptr to the map
re_to_bool.insert(e, nullptr);
re_to_accept.insert(e, nullptr);
}
else if (re_to_bool.find(e) == nullptr) {
else if (re_to_accept.find(e) == nullptr) {
// Second visit: set value
STRACE("seq_regex_verbose", tout << "2";);
to_visit.pop_back();
if (m.is_ite(e, econd, e1, e2)) {
expr* b1 = re_to_bool.find(e1);
expr* b2 = re_to_bool.find(e2);
expr* b = m.mk_ite(econd, b1, b2);
expr* b1 = re_to_accept.find(e1);
expr* b2 = re_to_accept.find(e2);
expr* b = m.is_true(econd) || b1 == b2 ? b1 : m.is_false(econd) ? b2 : m.mk_ite(econd, b1, b2);
_temp_bool_owner.push_back(b);
re_to_bool.find(e) = b;
re_to_accept.find(e) = b;
}
else if (re().is_empty(e) || (s_is_longer_than_i && re().is_epsilon(e)))
{
// s[i..] in [] <==> false, also: s[i..] in () <==> false when |s|>i
re_to_accept.find(e) = m.mk_false();
}
else if (re().is_full_seq(e) || (s_is_longer_than_i && re().is_dot_plus(e)))
{
// s[i..] in .* <==> true, also: s[i..] in .+ <==> true when |s|>i
re_to_accept.find(e) = m.mk_true();
}
/*
else if (re().is_empty(e))
{
re_to_bool.find(e) = m.mk_false();
}
else if (re().is_epsilon(e))
{
expr* iplus1 = a().mk_int(i);
expr* one = a().mk_int(1);
_temp_bool_owner.push_back(iplus1);
_temp_bool_owner.push_back(one);
//the substring starting after position iplus1 must be empty
expr* s_end = str().mk_substr(s, iplus1, one);
//the substring starting after position i must be empty
expr* s_end = str().mk_substr(s, i_int, one);
expr* s_end_is_epsilon = m.mk_eq(s_end, str().mk_empty(m.get_sort(s)));
_temp_bool_owner.push_back(s_end_is_epsilon);
re_to_bool.find(e) = s_end_is_epsilon;
re_to_accept.find(e) = s_end_is_epsilon;
STRACE("seq_regex_verbose", tout
<< "added empty sequence leaf: "
@ -653,18 +659,16 @@ namespace smt {
}
*/
else if (re().is_union(e, e1, e2)) {
expr* b1 = re_to_bool.find(e1);
expr* b2 = re_to_bool.find(e2);
expr* b = m.mk_or(b1, b2);
expr* b1 = re_to_accept.find(e1);
expr* b2 = re_to_accept.find(e2);
expr* b = m.is_false(b1) || b1 == b2 ? b2 : m.is_false(b2) ? b1 : m.mk_or(b1, b2);
_temp_bool_owner.push_back(b);
re_to_bool.find(e) = b;
re_to_accept.find(e) = b;
}
else {
expr* iplus1 = a().mk_int(i);
_temp_bool_owner.push_back(iplus1);
expr_ref acc_leaf = sk().mk_accept(s, iplus1, e);
expr_ref acc_leaf = sk().mk_accept(s, i_int, e);
_temp_bool_owner.push_back(acc_leaf);
re_to_bool.find(e) = acc_leaf;
re_to_accept.find(e) = acc_leaf;
STRACE("seq_regex_verbose", tout
<< "mk_deriv_accept: added accept leaf: "
@ -680,59 +684,44 @@ namespace smt {
// Finalize
expr_ref result(m);
result = re_to_bool.find(r); // Assigns ownership of all exprs in
// re_to_bool for after this completes
result = re_to_accept.find(r); // Assigns ownership of all exprs in
// re_to_accept for after this completes
rewrite(result);
return result;
}
/*
Return a list of all leaves in the derivative of a regex r,
Return a list of all target regexes in the derivative of a regex r,
ignoring the conditions along each path.
Warning: Although the derivative
normal form tries to eliminate unsat condition paths, one cannot
assume that the path to each leaf is satisfiable in general
(e.g. when regexes are created using re.pred).
So not all results may correspond to satisfiable predicates.
It is OK to rely on the results being satisfiable for completeness,
but not soundness.
The derivative construction uses (:var 0) and tries
to eliminate unsat condition paths but it does not perform
full satisfiability checks and it is not guaranteed
that all targets are actually reachable
*/
void seq_regex::get_all_derivatives(expr* r, expr_ref_vector& results) {
// Get derivative
sort* seq_sort = nullptr;
VERIFY(u().is_re(r, seq_sort));
expr_ref n(m.mk_fresh_const("re.char", seq_sort), m);
expr_ref hd = mk_first(r, n);
expr_ref d(m);
d = derivative_wrapper(hd, r);
void seq_regex::get_derivative_targets(expr* r, expr_ref_vector& targets) {
// constructs the derivative wrt (:var 0)
expr_ref d(seq_rw().mk_derivative(r), m);
// DFS
vector<expr*> to_visit;
to_visit.push_back(d);
obj_map<expr, bool> visited; // set<expr> (bool is used as a unit type)
while (to_visit.size() > 0) {
expr* e = to_visit.back();
to_visit.pop_back();
if (visited.contains(e)) continue;
visited.insert(e, true);
expr* econd = nullptr, *e1 = nullptr, *e2 = nullptr;
if (m.is_ite(e, econd, e1, e2) ||
re().is_union(e, e1, e2)) {
to_visit.push_back(e1);
to_visit.push_back(e2);
}
else if (!re().is_empty(e)) {
results.push_back(e);
STRACE("seq_regex_verbose", tout
<< "get_all_derivatives: added deriv: "
<< mk_pp(e, m) << std::endl;);
// use DFS to collect all the targets (leaf regexes) in d.
expr* _1 = nullptr, * e1 = nullptr, * e2 = nullptr;
obj_hashtable<expr>::entry* _2 = nullptr;
vector<expr*> workset;
workset.push_back(d);
obj_hashtable<expr> done;
done.insert(d);
while (workset.size() > 0) {
expr* e = workset.back();
workset.pop_back();
if (m.is_ite(e, _1, e1, e2) || re().is_union(e, e1, e2)) {
if (done.insert_if_not_there_core(e1, _2))
workset.push_back(e1);
if (done.insert_if_not_there_core(e2, _2))
workset.push_back(e2);
}
else if (!re().is_empty(e))
targets.push_back(e);
}
STRACE("seq_regex", tout << "Number of derivatives: "
<< results.size() << std::endl;);
STRACE("seq_regex_brief", tout << "#derivs=" << results.size() << " ";);
}
/*
@ -800,7 +789,7 @@ namespace smt {
th.add_axiom(~lit, ~th.mk_literal(is_nullable));
expr_ref hd = mk_first(r, n);
expr_ref d(m);
d = derivative_wrapper(hd, r);
d = mk_derivative_wrapper(hd, r);
literal_vector lits;
expr_ref_pair_vector cofactors(m);
get_cofactors(d, cofactors);
@ -884,7 +873,7 @@ namespace smt {
STRACE("state_graph",
if (!m_state_graph.is_seen(r_id))
tout << std::endl << "state(" << r_id << ") = " << seq_util::rex::pp(re(), r) << std::endl << "info(" << r_id << ") = " << re().get_info(r) << std::endl;);
tout << std::endl << "state(" << r_id << ") = " << re().to_str(r) << std::endl << "info(" << r_id << ") = " << re().get_info(r) << std::endl;);
// Add state
m_state_graph.add_state(r_id);
STRACE("seq_regex", tout << "Updating state graph for regex "
@ -900,14 +889,14 @@ namespace smt {
expr_ref_vector derivatives(m);
STRACE("seq_regex_verbose", tout
<< "getting all derivs: " << r_id << " " << std::endl;);
get_all_derivatives(r, derivatives);
get_derivative_targets(r, derivatives);
for (auto const& dr: derivatives) {
unsigned dr_id = get_state_id(dr);
STRACE("seq_regex_verbose", tout
<< std::endl << " traversing deriv: " << dr_id << " ";);
STRACE("state_graph",
if (!m_state_graph.is_seen(dr_id))
tout << "state(" << dr_id << ") = " << seq_util::rex::pp(re(), dr) << std::endl << "info(" << dr_id << ") = " << re().get_info(dr) << std::endl;);
tout << "state(" << dr_id << ") = " << re().to_str(dr) << std::endl << "info(" << dr_id << ") = " << re().get_info(dr) << std::endl;);
// Add state
m_state_graph.add_state(dr_id);
bool maybecycle = can_be_in_cycle(r, dr);

View file

@ -158,12 +158,12 @@ namespace smt {
expr_ref symmetric_diff(expr* r1, expr* r2);
expr_ref is_nullable_wrapper(expr* r);
expr_ref derivative_wrapper(expr* hd, expr* r);
expr_ref mk_derivative_wrapper(expr* hd, expr* r);
// Various support for unfolding derivative expressions that are
// returned by derivative_wrapper
expr_ref mk_deriv_accept(expr* s, unsigned i, expr* r);
void get_all_derivatives(expr* r, expr_ref_vector& results);
void get_derivative_targets(expr* r, expr_ref_vector& targets);
void get_cofactors(expr* r, expr_ref_pair_vector& result);
void get_cofactors_rec(expr* r, expr_ref_vector& conds,
expr_ref_pair_vector& result);

Some files were not shown because too many files have changed in this diff Show more