3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

detect and process concat eq type 1 (WIP UNTESTED)

This commit is contained in:
Murphy Berzish 2015-09-30 05:15:14 -04:00
parent a62d15403e
commit ed7b343822
2 changed files with 495 additions and 4 deletions

View file

@ -28,7 +28,10 @@ theory_str::theory_str(ast_manager & m):
search_started(false),
m_autil(m),
m_strutil(m),
tmpXorVarCount(0)
tmpStringVarCount(0),
tmpXorVarCount(0),
avoidLoopCut(true),
loopDetected(false)
{
}
@ -101,6 +104,122 @@ bool theory_str::internalize_term(app * term) {
return true;
}
static void cut_vars_map_copy(std::map<expr*, int> & dest, std::map<expr*, int> & src) {
std::map<expr*, int>::iterator itor = src.begin();
for (; itor != src.end(); itor++) {
dest[itor->first] = 1;
}
}
/*
bool hasSelfCut(Z3_ast n1, Z3_ast n2) {
if (cut_VARMap.find(n1) == cut_VARMap.end())
return false;
if (cut_VARMap.find(n2) == cut_VARMap.end())
return false;
if (cut_VARMap[n1].empty() || cut_VARMap[n2].empty())
return false;
std::map<Z3_ast, int>::iterator itor = cut_VARMap[n1].top()->vars.begin();
for (; itor != cut_VARMap[n1].top()->vars.end(); itor++) {
if (cut_VARMap[n2].top()->vars.find(itor->first) != cut_VARMap[n2].top()->vars.end())
return true;
}
return false;
}
*/
bool theory_str::has_self_cut(expr * n1, expr * n2) {
if (cut_var_map.find(n1) == cut_var_map.end()) {
return false;
}
if (cut_var_map.find(n2) == cut_var_map.end()) {
return false;
}
if (cut_var_map[n1].empty() || cut_var_map[n2].empty()) {
return false;
}
std::map<expr*, int>::iterator itor = cut_var_map[n1].top()->vars.begin();
for (; itor != cut_var_map[n1].top()->vars.end(); ++itor) {
if (cut_var_map[n2].top()->vars.find(itor->first) != cut_var_map[n2].top()->vars.end()) {
return true;
}
}
return false;
}
void theory_str::add_cut_info_one_node(expr * baseNode, int slevel, expr * node) {
if (cut_var_map.find(baseNode) == cut_var_map.end()) {
T_cut * varInfo = alloc(T_cut);
varInfo->level = slevel;
varInfo->vars[node] = 1;
cut_var_map[baseNode].push(varInfo);
} else {
if (cut_var_map[baseNode].empty()) {
T_cut * varInfo = alloc(T_cut);
varInfo->level = slevel;
varInfo->vars[node] = 1;
cut_var_map[baseNode].push(varInfo);
} else {
if (cut_var_map[baseNode].top()->level < slevel) {
T_cut * varInfo = alloc(T_cut);
varInfo->level = slevel;
cut_vars_map_copy(varInfo->vars, cut_var_map[baseNode].top()->vars);
varInfo->vars[node] = 1;
cut_var_map[baseNode].push(varInfo);
} else if (cut_var_map[baseNode].top()->level == slevel) {
cut_var_map[baseNode].top()->vars[node] = 1;
} else {
get_manager().raise_exception("entered illegal state during add_cut_info_one_node()");
}
}
}
}
void theory_str::add_cut_info_merge(expr * destNode, int slevel, expr * srcNode) {
if (cut_var_map.find(srcNode) == cut_var_map.end()) {
get_manager().raise_exception("illegal state in add_cut_info_merge(): cut_var_map doesn't contain srcNode");
}
if (cut_var_map[srcNode].empty()) {
get_manager().raise_exception("illegal state in add_cut_info_merge(): cut_var_map[srcNode] is empty");
}
if (cut_var_map.find(destNode) == cut_var_map.end()) {
T_cut * varInfo = alloc(T_cut);
varInfo->level = slevel;
cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars);
cut_var_map[destNode].push(varInfo);
} else {
if (cut_var_map[destNode].empty() || cut_var_map[destNode].top()->level < slevel) {
T_cut * varInfo = alloc(T_cut);
varInfo->level = slevel;
cut_vars_map_copy(varInfo->vars, cut_var_map[destNode].top()->vars);
cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars);
cut_var_map[destNode].push(varInfo);
} else if (cut_var_map[destNode].top()->level == slevel) {
cut_vars_map_copy(cut_var_map[destNode].top()->vars, cut_var_map[srcNode].top()->vars);
} else {
get_manager().raise_exception("illegal state in add_cut_info_merge(): inconsistent slevels");
}
}
}
void theory_str::check_and_init_cut_var(expr * node) {
if (cut_var_map.find(node) != cut_var_map.end()) {
return;
} else if (!m_strutil.is_string(node)) {
add_cut_info_one_node(node, -1, node);
}
}
app * theory_str::mk_int(int n) {
return m_autil.mk_numeral(rational(n), true);
}
app * theory_str::mk_internal_xor_var() {
context & ctx = get_context();
ast_manager & m = get_manager();
@ -119,6 +238,49 @@ app * theory_str::mk_internal_xor_var() {
return a;
}
/*
Z3_context ctx = Z3_theory_get_context(t);
PATheoryData * td = (PATheoryData *) Z3_theory_get_ext_data(t);
std::stringstream ss;
ss << tmpStringVarCount;
tmpStringVarCount++;
std::string name = "$$_str" + ss.str();
Z3_ast varAst = mk_var(ctx, name.c_str(), td->String);
nonEmptyStrVarAxiom(t, varAst, __LINE__);
return varAst;
*/
app * theory_str::mk_nonempty_str_var() {
context & ctx = get_context();
ast_manager & m = get_manager();
std::stringstream ss;
ss << tmpStringVarCount;
tmpStringVarCount++;
std::string name = "$$_str" + ss.str();
sort * string_sort = m.mk_sort(m_strutil.get_family_id(), STRING_SORT);
char * new_buffer = alloc_svect(char, name.length() + 1);
strcpy(new_buffer, name.c_str());
symbol sym(new_buffer);
app* a = m.mk_const(m.mk_const_decl(sym, string_sort));
// assert a variation of the basic string axioms that ensures this string is nonempty
{
// build LHS
expr_ref len_str(m);
len_str = mk_strlen(a);
SASSERT(len_str);
// build RHS
expr_ref zero(m);
zero = m_autil.mk_numeral(rational(0), true);
SASSERT(zero);
// build LHS > RHS and assert
app * lhs_gt_rhs = m_autil.mk_gt(len_str, zero);
SASSERT(lhs_gt_rhs);
assert_axiom(lhs_gt_rhs);
}
return a;
}
app * theory_str::mk_strlen(expr * e) {
/*if (m_strutil.is_string(e)) {*/ if (false) {
const char * strval = 0;
@ -372,7 +534,7 @@ void theory_str::reset_eh() {
m_basicstr_axiom_todo.reset();
m_str_eq_todo.reset();
m_concat_axiom_todo.reset();
pop_scope_eh(0);
pop_scope_eh(get_context().get_scope_level());
}
/*
@ -670,9 +832,289 @@ void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) {
}
// start to split both concats
check_and_init_cut_var(v1_arg0);
check_and_init_cut_var(v1_arg1);
check_and_init_cut_var(v2_arg0);
check_and_init_cut_var(v2_arg1);
//*************************************************************
// case 1: concat(x, y) = concat(m, n)
//*************************************************************
if (is_concat_eq_type1(new_nn1, new_nn2)) {
process_concat_eq_type1(new_nn1, new_nn2);
return;
}
//*************************************************************
// case 2: concat(x, y) = concat(m, "str")
//*************************************************************
if (is_concat_eq_type2(new_nn1, new_nn2)) {
process_concat_eq_type2(new_nn1, new_nn2);
return;
}
//*************************************************************
// case 3: concat(x, y) = concat("str", n)
//*************************************************************
if (is_concat_eq_type3(new_nn1, new_nn2)) {
process_concat_eq_type3(new_nn1, new_nn2);
return;
}
//*************************************************************
// case 4: concat("str1", y) = concat("str2", n)
//*************************************************************
if (is_concat_eq_type4(new_nn1, new_nn2)) {
process_concat_eq_type4(new_nn1, new_nn2);
return;
}
//*************************************************************
// case 5: concat(x, "str1") = concat(m, "str2")
//*************************************************************
if (is_concat_eq_type5(new_nn1, new_nn2)) {
process_concat_eq_type5(new_nn1, new_nn2);
return;
}
//*************************************************************
// case 6: concat("str1", y) = concat(m, "str2")
//*************************************************************
if (is_concat_eq_type6(new_nn1, new_nn2)) {
process_concat_eq_type6(new_nn1, new_nn2);
return;
}
}
bool theory_str::is_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
expr * x = to_app(concatAst1)->get_arg(0);
expr * y = to_app(concatAst1)->get_arg(1);
expr * m = to_app(concatAst2)->get_arg(0);
expr * n = to_app(concatAst2)->get_arg(1);
if (!m_strutil.is_string(x) && !m_strutil.is_string(y) && !m_strutil.is_string(m) && !m_strutil.is_string(n)) {
return true;
} else {
return false;
}
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
ast_manager & mgr = get_manager();
context & ctx = get_context();
TRACE("t_str_detail", tout << "process_concat_eq TYPE 1" << std::endl
<< "concatAst1 = " << mk_ismt2_pp(concatAst1, m) << std::endl
<< "concatAst2 = " << mk_ismt2_pp(concatAst2, m) << std::endl;
);
if (!is_concat(to_app(concatAst1))) {
TRACE("t_str_detail", tout << "concatAst1 is not a concat function" << std::endl;);
return;
}
if (!is_concat(to_app(concatAst2))) {
TRACE("t_str_detail", tout << "concatAst2 is not a concat function" << std::endl;);
return;
}
expr * x = to_app(concatAst1)->get_arg(0);
expr * y = to_app(concatAst1)->get_arg(1);
expr * m = to_app(concatAst2)->get_arg(0);
expr * n = to_app(concatAst2)->get_arg(1);
/* TODO query the integer theory:
int x_len = getLenValue(t, x);
int y_len = getLenValue(t, y);
int m_len = getLenValue(t, m);
int n_len = getLenValue(t, n);
*/
int x_len = -1;
int y_len = -1;
int m_len = -1;
int n_len = -1;
int splitType = -1;
if (x_len != -1 && m_len != -1) {
if (x_len < m_len)
splitType = 0;
else if (x_len == m_len)
splitType = 1;
else
splitType = 2;
}
if (splitType == -1 && y_len != -1 && n_len != -1) {
if (y_len > n_len)
splitType = 0;
else if (y_len == n_len)
splitType = 1;
else
splitType = 2;
}
TRACE("t_str_detail", tout << "split type " << splitType << std::endl;);
expr * t1 = NULL;
expr * t2 = NULL;
expr * xorFlag = NULL;
std::pair<expr*, expr*> key1(concatAst1, concatAst2);
std::pair<expr*, expr*> key2(concatAst2, concatAst1);
if (varForBreakConcat.find(key1) == varForBreakConcat.end() && varForBreakConcat.find(key2) == varForBreakConcat.end()) {
t1 = mk_nonempty_str_var();
t2 = mk_nonempty_str_var();
xorFlag = mk_internal_xor_var();
check_and_init_cut_var(t1);
check_and_init_cut_var(t2);
varForBreakConcat[key1][0] = t1;
varForBreakConcat[key1][1] = t2;
varForBreakConcat[key1][2] = xorFlag;
} else {
// match found
if (varForBreakConcat.find(key1) != varForBreakConcat.end()) {
t1 = varForBreakConcat[key1][0];
t2 = varForBreakConcat[key1][1];
xorFlag = varForBreakConcat[key1][2];
} else {
t1 = varForBreakConcat[key2][0];
t2 = varForBreakConcat[key2][1];
xorFlag = varForBreakConcat[key2][2];
}
}
// For split types 0 through 2, we can get away with providing
// fewer split options since more length information is available.
if (splitType == 0) {
NOT_IMPLEMENTED_YET(); // TODO
} else if (splitType == 1) {
NOT_IMPLEMENTED_YET(); // TODO
} else if (splitType == 2) {
NOT_IMPLEMENTED_YET(); // TODO
} else if (splitType == -1) {
// Here we don't really have a choice. We have no length information at all...
expr ** or_item = alloc_svect(expr*, 3);
expr ** and_item = alloc_svect(expr*, 20);
int option = 0;
int pos = 1;
// break option 1: m cuts y
// len(x) < len(m) || len(y) > len(n)
if (!avoidLoopCut || !has_self_cut(m, y)) {
// break down option 1-1
expr * x_t1 = mk_concat(x, t1);
expr * t1_n = mk_concat(t1, n);
or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(m, x_t1));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, t1_n));
expr_ref x_plus_t1(m_autil.mk_add(mk_strlen(x), mk_strlen(t1)), mgr);
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(m), x_plus_t1));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], m_autil.mk_gt(mk_strlen(m), mk_strlen(x)));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], m_autil.mk_gt(mk_strlen(y), mk_strlen(n)));
option++;
add_cut_info_merge(t1, ctx.get_scope_level(), m);
add_cut_info_merge(t1, ctx.get_scope_level(), y);
} else {
loopDetected = true;
TRACE("t_str", tout << "AVOID LOOP: SKIPPED" << std::endl;);
// TODO printCutVar(m, y);
}
// break option 2:
// x = m || y = n
if (!avoidLoopCut || !has_self_cut(x, n)) {
// break down option 1-2
expr * m_t2 = mk_concat(m, t2);
expr * t2_y = mk_concat(t2, y);
or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(x, m_t2));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(n, t2_y));
expr_ref m_plus_t2(m_autil.mk_add(mk_strlen(m), mk_strlen(t2)), mgr);
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(x), m_plus_t2));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(x), mk_strlen(m)));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(n), mk_strlen(y)));
option++;
add_cut_info_merge(t2, sLevel, x);
add_cut_info_merge(t2, sLevel, n);
} else {
loopDetected = true;
TRACE("t_str", tout << "AVOID LOOP: SKIPPED" << std::endl;);
// TODO printCutVar(x, n);
}
if (can_two_nodes_eq(x, m) && can_two_nodes_eq(y, n)) {
or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(x, m));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, n));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(x), mk_strlen(m)));
and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(y), mk_strlen(n)));
++option;
}
if (option > 0) {
if (option == 1) {
and_item[0] = or_item[0];
} else {
and_item[0] = mgr.mk_or(option, or_item);
}
expr_ref premise(ctx.mk_eq_atom(concatAst1, concatAst2), m);
expr_ref conclusion(mgr.mk_and(pos, and_item), m);
assert_implication(premise, conclusion);
} else {
TRACE("t_str", tout << "STOP: no split option found for two EQ concats." << std::endl;);
}
} // (splitType == -1)
}
bool theory_str::is_concat_eq_type2(expr * concatAst1, expr * concatAst2) {
// TODO
NOT_IMPLEMENTED_YET();
return false;
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
}
bool theory_str::is_concat_eq_type3(expr * concatAst1, expr * concatAst2) {
// TODO
return false;
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
}
bool theory_str::is_concat_eq_type4(expr * concatAst1, expr * concatAst2) {
// TODO
return false;
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
}
bool theory_str::is_concat_eq_type5(expr * concatAst1, expr * concatAst2) {
// TODO
return false;
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
}
bool theory_str::is_concat_eq_type6(expr * concatAst1, expr * concatAst2) {
// TODO
return false;
}
void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) {
}
@ -1327,6 +1769,20 @@ void theory_str::push_scope_eh() {
void theory_str::pop_scope_eh(unsigned num_scopes) {
TRACE("t_str", tout << "pop " << num_scopes << std::endl;);
context & ctx = get_context();
unsigned sLevel = ctx.get_scope_level();
std::map<expr*, std::stack<T_cut *> >::iterator varItor = cut_var_map.begin();
while (varItor != cut_var_map.end()) {
while ((varItor->second.size() > 0) && (varItor->second.top()->level != 0) && (varItor->second.top()->level >= sLevel)) {
T_cut * aCut = varItor->second.top();
varItor->second.pop();
dealloc(aCut);
}
if (varItor->second.size() == 0) {
cut_var_map.erase(varItor);
}
++varItor;
}
}
final_check_status theory_str::final_check_eh() {

View file

@ -47,7 +47,15 @@ namespace smt {
};
class theory_str : public theory {
// TODO
struct T_cut
{
int level;
std::map<expr*, int> vars;
T_cut() {
level = -100;
}
};
protected:
bool search_started;
arith_util m_autil;
@ -59,8 +67,13 @@ namespace smt {
svector<std::pair<enode*,enode*> > m_str_eq_todo;
ptr_vector<enode> m_concat_axiom_todo;
int tmpStringVarCount;
int tmpXorVarCount;
std::map<std::pair<expr*, expr*>, std::map<int, expr*> > varForBreakConcat;
bool avoidLoopCut = true;
bool loopDetected = false;
std::map<expr*, std::stack<T_cut *> > cut_var_map;
protected:
void assert_axiom(expr * e);
void assert_implication(expr * premise, expr * conclusion);
@ -69,6 +82,14 @@ namespace smt {
expr * mk_concat(expr * n1, expr * n2);
expr * mk_concat_const_str(expr * n1, expr * n2);
app * mk_int(int n);
void check_and_init_cut_var(expr * node);
void add_cut_info_one_node(expr * baseNode, int slevel, expr * node);
void add_cut_info_merge(expr * destNode, int slevel, expr * srcNode);
bool has_self_cut(expr * n1, expr * n2);
app * mk_nonempty_str_var();
app * mk_internal_xor_var();
bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); }
@ -98,6 +119,20 @@ namespace smt {
void simplify_concat_equality(expr * lhs, expr * rhs);
void solve_concat_eq_str(expr * concat, expr * str);
bool is_concat_eq_type1(expr * concatAst1, expr * concatAst2);
bool is_concat_eq_type2(expr * concatAst1, expr * concatAst2);
bool is_concat_eq_type3(expr * concatAst1, expr * concatAst2);
bool is_concat_eq_type4(expr * concatAst1, expr * concatAst2);
bool is_concat_eq_type5(expr * concatAst1, expr * concatAst2);
bool is_concat_eq_type6(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type1(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type2(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type3(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type4(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type5(expr * concatAst1, expr * concatAst2);
void process_concat_eq_type6(expr * concatAst1, expr * concatAst2);
bool new_eq_check(expr * lhs, expr * rhs);
void group_terms_by_eqc(expr * n, std::set<expr*> & concats, std::set<expr*> & vars, std::set<expr*> & consts);
public: