From f39773f15b5937f634855ae0adb7b6b7e948c876 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 30 Aug 2015 15:23:31 -0400 Subject: [PATCH 01/91] update readme --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index 22fc58c7a..afd4876ca 100644 --- a/README +++ b/README @@ -2,6 +2,8 @@ Z3 is a theorem prover from Microsoft Research. Z3 is licensed under the MIT license. Z3 can be built using Visual Studio Command Prompt and make/g++. +This fork of Z3 adds support for solving equations in the theory of strings. + 1) Building Z3 on Windows using Visual Studio Command Prompt 32-bit builds, start with: From b30d4f757db057253dc64f61b84f12b7f820a7ef Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Sep 2015 18:08:58 -0400 Subject: [PATCH 02/91] ignore Z3-str source --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 93194fc1b..037d9abd7 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,6 @@ src/api/ml/z3.mllib *.bak doc/api doc/code +# reference code for z3str2 +Z3-str/** + From 1f96e19211eece215d6deaec3d53018d9c53579f Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Sep 2015 18:55:45 -0400 Subject: [PATCH 03/91] failing test case: SMT2 parse string constants --- src/test/smt2print_parse.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 39543d141..349a3c9c8 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -98,6 +98,12 @@ void tst_smt2print_parse() { test_parseprint(spec5); + // Test strings + char const* spec6 = + "(assert (= \"abc\" \"abc\"))"; + + test_parseprint(spec6); + // Test ? } From e48ac4a97af1150ac3ce72c454dd2102f35bb32f Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Sep 2015 21:12:03 -0400 Subject: [PATCH 04/91] create and register string theory plugin the parser gets a little bit further now! rejects input with "unexpected character" --- .gitignore | 1 + src/ast/ast.cpp | 5 +++ src/ast/ast.h | 6 +++ src/ast/reg_decl_plugins.cpp | 4 ++ src/ast/str_decl_plugin.cpp | 74 ++++++++++++++++++++++++++++++++ src/ast/str_decl_plugin.h | 75 +++++++++++++++++++++++++++++++++ src/parsers/smt2/smt2parser.cpp | 18 ++++++++ 7 files changed, 183 insertions(+) create mode 100644 src/ast/str_decl_plugin.cpp create mode 100644 src/ast/str_decl_plugin.h diff --git a/.gitignore b/.gitignore index 037d9abd7..97ca67cf4 100644 --- a/.gitignore +++ b/.gitignore @@ -75,5 +75,6 @@ src/api/ml/z3.mllib doc/api doc/code # reference code for z3str2 +Z3-str Z3-str/** diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 3a6275e33..5a2dc4a52 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include +#include #include"ast.h" #include"ast_pp.h" #include"ast_ll_pp.h" @@ -58,6 +59,7 @@ parameter& parameter::operator=(parameter const& other) { case PARAM_SYMBOL: new (m_symbol) symbol(other.get_symbol()); break; case PARAM_RATIONAL: new (m_rational) rational(other.get_rational()); break; case PARAM_DOUBLE: m_dval = other.m_dval; break; + case PARAM_STRING: m_string = other.m_string; break; case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; default: UNREACHABLE(); @@ -90,6 +92,7 @@ bool parameter::operator==(parameter const & p) const { case PARAM_SYMBOL: return get_symbol() == p.get_symbol(); case PARAM_RATIONAL: return get_rational() == p.get_rational(); case PARAM_DOUBLE: return m_dval == p.m_dval; + case PARAM_STRING: return (m_string == NULL && p.m_string == NULL) || strcmp(m_string, p.m_string)==0; case PARAM_EXTERNAL: return m_ext_id == p.m_ext_id; default: UNREACHABLE(); return false; } @@ -103,6 +106,7 @@ unsigned parameter::hash() const { case PARAM_SYMBOL: b = get_symbol().hash(); break; case PARAM_RATIONAL: b = get_rational().hash(); break; case PARAM_DOUBLE: b = static_cast(m_dval); break; + case PARAM_STRING: /* TODO */ b = 42; break; case PARAM_EXTERNAL: b = m_ext_id; break; } return (b << 2) | m_kind; @@ -115,6 +119,7 @@ std::ostream& parameter::display(std::ostream& out) const { case PARAM_RATIONAL: return out << get_rational(); case PARAM_AST: return out << "#" << get_ast()->get_id(); case PARAM_DOUBLE: return out << m_dval; + case PARAM_STRING: return out << m_string; case PARAM_EXTERNAL: return out << "@" << m_ext_id; default: UNREACHABLE(); diff --git a/src/ast/ast.h b/src/ast/ast.h index a5f5c286f..9c1044ec7 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -86,6 +86,7 @@ public: PARAM_SYMBOL, PARAM_RATIONAL, PARAM_DOUBLE, + PARAM_STRING, // PARAM_EXTERNAL is used for handling decl_plugin specific parameters. // For example, it is used for handling mpf numbers in float_decl_plugin, // and irrational algebraic numbers in arith_decl_plugin. @@ -104,6 +105,7 @@ private: char m_symbol[sizeof(symbol)]; // for PARAM_SYMBOL char m_rational[sizeof(rational)]; // for PARAM_RATIONAL double m_dval; // for PARAM_DOUBLE (remark: this is not used in float_decl_plugin) + const char* m_string; // for PARAM_STRING unsigned m_ext_id; // for PARAM_EXTERNAL }; @@ -116,6 +118,7 @@ public: explicit parameter(symbol const & s): m_kind(PARAM_SYMBOL) { new (m_symbol) symbol(s); } explicit parameter(rational const & r): m_kind(PARAM_RATIONAL) { new (m_rational) rational(r); } explicit parameter(double d):m_kind(PARAM_DOUBLE), m_dval(d) {} + explicit parameter(const char *s):m_kind(PARAM_STRING), m_string(s) {} explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); @@ -129,6 +132,7 @@ public: bool is_symbol() const { return m_kind == PARAM_SYMBOL; } bool is_rational() const { return m_kind == PARAM_RATIONAL; } bool is_double() const { return m_kind == PARAM_DOUBLE; } + bool is_string() const { return m_kind == PARAM_STRING; } bool is_external() const { return m_kind == PARAM_EXTERNAL; } bool is_int(int & i) const { return is_int() && (i = get_int(), true); } @@ -136,6 +140,7 @@ public: bool is_symbol(symbol & s) const { return is_symbol() && (s = get_symbol(), true); } bool is_rational(rational & r) const { return is_rational() && (r = get_rational(), true); } bool is_double(double & d) const { return is_double() && (d = get_double(), true); } + // TODO is_string(char*) bool is_external(unsigned & id) const { return is_external() && (id = get_ext_id(), true); } /** @@ -155,6 +160,7 @@ public: symbol const & get_symbol() const { SASSERT(is_symbol()); return *(reinterpret_cast(m_symbol)); } rational const & get_rational() const { SASSERT(is_rational()); return *(reinterpret_cast(m_rational)); } double get_double() const { SASSERT(is_double()); return m_dval; } + const char * get_string() const { SASSERT(is_string()); return m_string; } unsigned get_ext_id() const { SASSERT(is_external()); return m_ext_id; } bool operator==(parameter const & p) const; diff --git a/src/ast/reg_decl_plugins.cpp b/src/ast/reg_decl_plugins.cpp index f46dd76d4..6a7e7b30c 100644 --- a/src/ast/reg_decl_plugins.cpp +++ b/src/ast/reg_decl_plugins.cpp @@ -25,6 +25,7 @@ Revision History: #include"dl_decl_plugin.h" #include"seq_decl_plugin.h" #include"fpa_decl_plugin.h" +#include"str_decl_plugin.h" void reg_decl_plugins(ast_manager & m) { if (!m.get_plugin(m.mk_family_id(symbol("arith")))) { @@ -48,4 +49,7 @@ void reg_decl_plugins(ast_manager & m) { if (!m.get_plugin(m.mk_family_id(symbol("fpa")))) { m.register_plugin(symbol("fpa"), alloc(fpa_decl_plugin)); } + if (!m.get_plugin(m.mk_family_id(symbol("str")))) { + m.register_plugin(symbol("str"), alloc(str_decl_plugin)); + } } diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp new file mode 100644 index 000000000..540cd89c0 --- /dev/null +++ b/src/ast/str_decl_plugin.cpp @@ -0,0 +1,74 @@ +/*++ +Module Name: + + str_decl_plugin.h + +Abstract: + + + +Author: + + Murphy Berzish (mtrberzi) 2015-09-02. + +Revision History: + +--*/ +#include +#include"str_decl_plugin.h" +#include"string_buffer.h" +#include"warning.h" +#include"ast_pp.h" +#include"ast_smt2_pp.h" + +str_decl_plugin::str_decl_plugin(): + m_strv_sym("String"), + m_str_decl(0){ +} + +str_decl_plugin::~str_decl_plugin(){ +} + +void str_decl_plugin::finalize(void) { + #define DEC_REF(decl) if (decl) { m_manager->dec_ref(decl); } ((void) 0) + DEC_REF(m_str_decl); +} + +void str_decl_plugin::set_manager(ast_manager * m, family_id id) { + decl_plugin::set_manager(m, id); + m_str_decl = m->mk_sort(symbol("String"), sort_info(id, STRING_SORT)); + m->inc_ref(m_str_decl); + sort * s = m_str_decl; + /* TODO mk_pred, etc. */ +} + +decl_plugin * str_decl_plugin::mk_fresh() { + return alloc(str_decl_plugin); +} + +sort * str_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { + switch (k) { + case STRING_SORT: return m_str_decl; + default: return 0; + } +} + +func_decl * str_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) { + /* TODO */ + m_manager->raise_exception("str_decl_plugin::mk_func_decl() not yet implemented"); return 0; +} + +app * str_decl_plugin::mk_string(const char * val) { + parameter p[1] = {parameter(val)}; + func_decl * d; + d = m_manager->mk_const_decl(m_strv_sym, m_str_decl, func_decl_info(m_family_id, OP_STR, 1, p)); + return m_manager->mk_const(d); +} + +str_util::str_util(ast_manager &m) : + str_recognizers(m.mk_family_id(symbol("str"))), + m_manager(m) { + SASSERT(m.has_plugin(symbol("str"))); + m_plugin = static_cast(m.get_plugin(m.mk_family_id(symbol("str")))); +} diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h new file mode 100644 index 000000000..2fd1db022 --- /dev/null +++ b/src/ast/str_decl_plugin.h @@ -0,0 +1,75 @@ +/*++ +Module Name: + + str_decl_plugin.h + +Abstract: + + + +Author: + + Murphy Berzish (mtrberzi) 2015-09-02. + +Revision History: + +--*/ +#ifndef _STR_DECL_PLUGIN_H_ +#define _STR_DECL_PLUGIN_H_ + +#include"ast.h" + +enum str_sort_kind { + STRING_SORT, +}; + +enum str_op_kind { + OP_STR, /* string constants */ + + LAST_STR_OP +}; + +class str_decl_plugin : public decl_plugin { +protected: + symbol m_strv_sym; + sort * m_str_decl; + + virtual void set_manager(ast_manager * m, family_id id); +public: + str_decl_plugin(); + virtual ~str_decl_plugin(); + virtual void finalize(); + + virtual decl_plugin * mk_fresh(); + virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range); + + app * mk_string(const char * val); + // TODO +}; + +class str_recognizers { + family_id m_afid; +public: + str_recognizers(family_id fid):m_afid(fid) {} + family_id get_fid() const { return m_afid; } + family_id get_family_id() const { return get_fid(); } + // TODO +}; + +class str_util : public str_recognizers { + ast_manager & m_manager; + str_decl_plugin * m_plugin; +public: + str_util(ast_manager & m); + ast_manager & get_manager() const { return m_manager; } + str_decl_plugin & plugin() { return *m_plugin; } + + app * mk_string(const char * val) { + return m_plugin->mk_string(val); + } + // TODO +}; + +#endif /* _STR_DECL_PLUGIN_H_ */ diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 785f578f3..af752c82d 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -22,6 +22,7 @@ Revision History: #include"datatype_decl_plugin.h" #include"bv_decl_plugin.h" #include"arith_decl_plugin.h" +#include"str_decl_plugin.h" #include"ast_pp.h" #include"well_sorted.h" #include"pattern_validation.h" @@ -64,6 +65,7 @@ namespace smt2 { scoped_ptr m_bv_util; scoped_ptr m_arith_util; + scoped_ptr m_str_util; scoped_ptr m_pattern_validator; scoped_ptr m_var_shifter; @@ -272,6 +274,12 @@ namespace smt2 { return *(m_bv_util.get()); } + str_util & strutil() { + if (m_str_util.get() == 0) + m_str_util = alloc(str_util, m()); + return *(m_str_util.get()); + } + pattern_validator & pat_validator() { if (m_pattern_validator.get() == 0) { m_pattern_validator = alloc(pattern_validator, m()); @@ -1054,6 +1062,13 @@ namespace smt2 { next(); } + void parse_string() { + SASSERT(curr() == scanner::STRING_TOKEN); + TRACE("parse_string", tout << "new string constant: " << m_scanner.get_string() << "\n";); + expr_stack().push_back(strutil().mk_string(m_scanner.get_string())); + next(); + } + void push_pattern_frame() { // TODO: It seems the only reliable way to parse patterns is: // Parse as an S-Expr, then try to convert it to an useful pattern. @@ -1713,6 +1728,9 @@ namespace smt2 { case scanner::BV_TOKEN: parse_bv_numeral(); break; + case scanner::STRING_TOKEN: + parse_string(); + break; case scanner::LEFT_PAREN: push_expr_frame(m_num_expr_frames == 0 ? 0 : static_cast(m_stack.top())); break; From 02345ee5f190d7033373523743329ec5de016b78 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 3 Sep 2015 00:17:05 -0400 Subject: [PATCH 05/91] fix string constant representation in parser spec1 loopback OK --- src/ast/ast.h | 4 +++- src/ast/ast_smt_pp.cpp | 9 +++++++++ src/ast/str_decl_plugin.cpp | 8 ++++++++ src/ast/str_decl_plugin.h | 2 ++ src/parsers/smt2/smt2parser.cpp | 9 +++++++-- src/test/smt2print_parse.cpp | 13 +++++++++---- 6 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 9c1044ec7..14b869e51 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -118,7 +118,9 @@ public: explicit parameter(symbol const & s): m_kind(PARAM_SYMBOL) { new (m_symbol) symbol(s); } explicit parameter(rational const & r): m_kind(PARAM_RATIONAL) { new (m_rational) rational(r); } explicit parameter(double d):m_kind(PARAM_DOUBLE), m_dval(d) {} - explicit parameter(const char *s):m_kind(PARAM_STRING), m_string(s) {} + explicit parameter(const char *s):m_kind(PARAM_STRING), m_string(s) { + TRACE("parse_string", tout << "parameter(const char *): " << s << "\n";); + } explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 805f3070f..0785c7bfc 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -24,6 +24,7 @@ Revision History: #include"ast_smt_pp.h" #include"arith_decl_plugin.h" #include"bv_decl_plugin.h" +#include"str_decl_plugin.h" #include"array_decl_plugin.h" #include"datatype_decl_plugin.h" #include"vector.h" @@ -160,8 +161,10 @@ class smt_printer { unsigned m_num_lets; arith_util m_autil; bv_util m_bvutil; + str_util m_strutil; family_id m_basic_fid; family_id m_bv_fid; + family_id m_str_fid; family_id m_arith_fid; family_id m_array_fid; family_id m_dt_fid; @@ -394,6 +397,7 @@ class smt_printer { void visit_app(app* n) { rational val; + const char *str; bool is_int, pos; buffer names; unsigned bv_size; @@ -436,6 +440,9 @@ class smt_printer { m_out << ") bv1[1])"; } } + else if (m_strutil.is_string(n, &str)) { + m_out << "\"" << str << "\""; + } else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { if (m_is_smt2) { m_out << "(! "; @@ -797,6 +804,7 @@ public: m_num_lets(0), m_autil(m), m_bvutil(m), + m_strutil(m), m_logic(logic), m_AUFLIRA("AUFLIRA"), // It's much easier to read those testcases with that. @@ -809,6 +817,7 @@ public: m_bv_fid = m.mk_family_id("bv"); m_arith_fid = m.mk_family_id("arith"); m_array_fid = m.mk_family_id("array"); + m_str_fid = m.mk_family_id("str"); m_dt_fid = m.mk_family_id("datatype"); } diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 540cd89c0..66b6c23fc 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -66,6 +66,14 @@ app * str_decl_plugin::mk_string(const char * val) { return m_manager->mk_const(d); } +bool str_recognizers::is_string(expr const * n, const char ** val) const { + if (!is_app_of(n, m_afid, OP_STR)) + return false; + func_decl * decl = to_app(n)->get_decl(); + *val = decl->get_parameter(0).get_string(); + return true; +} + str_util::str_util(ast_manager &m) : str_recognizers(m.mk_family_id(symbol("str"))), m_manager(m) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 2fd1db022..2d629e006 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -55,6 +55,8 @@ public: str_recognizers(family_id fid):m_afid(fid) {} family_id get_fid() const { return m_afid; } family_id get_family_id() const { return get_fid(); } + + bool is_string(expr const * n, const char ** val) const; // TODO }; diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index af752c82d..5c8d60700 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -1064,8 +1064,13 @@ namespace smt2 { void parse_string() { SASSERT(curr() == scanner::STRING_TOKEN); - TRACE("parse_string", tout << "new string constant: " << m_scanner.get_string() << "\n";); - expr_stack().push_back(strutil().mk_string(m_scanner.get_string())); + char const *original_token = m_scanner.get_string(); + size_t bufsize = strlen(original_token); + char * buf = alloc_svect(char, bufsize + 1); + strncpy(buf, original_token, bufsize); + buf[bufsize] = '\0'; + TRACE("parse_string", tout << "new string constant: " << buf << " length=" << bufsize << "\n";); + expr_stack().push_back(strutil().mk_string(buf)); next(); } diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 349a3c9c8..1b491a022 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -7,8 +7,9 @@ void test_print(Z3_context ctx, Z3_ast a) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", 0, 0, 0, 0, 0, a); - std::cout << spec1 << "\n"; + std::cout << "spec1: benchmark->string\n" << spec1 << "\n"; + std::cout << "attempting to parse spec1...\n"; Z3_ast b = Z3_parse_smtlib2_string(ctx, spec1, @@ -18,14 +19,14 @@ void test_print(Z3_context ctx, Z3_ast a) { 0, 0, 0); - + std::cout << "parse successful, converting ast->string\n"; char const* spec2 = Z3_ast_to_string(ctx, b); - std::cout << spec2 << "\n"; + std::cout << "spec2: string->ast->string\n" << spec2 << "\n"; } void test_parseprint(char const* spec) { Z3_context ctx = Z3_mk_context(0); - std::cout << spec << "\n"; + std::cout << "spec:\n" << spec << "\n"; Z3_ast a = Z3_parse_smtlib2_string(ctx, @@ -37,8 +38,12 @@ void test_parseprint(char const* spec) { 0, 0); + std::cout << "done parsing\n"; + test_print(ctx, a); + std::cout << "done printing\n"; + Z3_del_context(ctx); } From 744d2e3c9ca7b2407fb66b27ad9f150fe02dbd29 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 3 Sep 2015 01:12:08 -0400 Subject: [PATCH 06/91] pretty-printing of string constants in AST spec2 looks good now --- src/ast/ast_smt2_pp.cpp | 15 +++++++++++++++ src/ast/ast_smt2_pp.h | 7 ++++++- src/ast/str_decl_plugin.cpp | 5 +++++ src/ast/str_decl_plugin.h | 1 + src/cmd_context/cmd_context.cpp | 5 ++++- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 035e228fb..0006d508c 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -298,6 +298,18 @@ format * smt2_pp_environment::mk_float(rational const & val) const { return mk_string(get_manager(), s.c_str()); } +format * smt2_pp_environment::pp_str_literal(app * t) { + TRACE("parse_string", tout << "pp_str_literal\n";); + str_util & u = get_strutil(); + SASSERT(u.is_string(t)); + const char * val; + u.is_string(t, &val); + ast_manager & m = get_manager(); + string_buffer<> buf; + buf << "\"" << val << "\""; + return mk_string(m, buf.c_str()); +} + format * smt2_pp_environment::pp_arith_literal(app * t, bool decimal, unsigned decimal_prec) { arith_util & u = get_autil(); SASSERT(u.is_numeral(t) || u.is_irrational_algebraic_numeral(t)); @@ -581,6 +593,9 @@ class smt2_printer { else if (m_env.get_dlutil().is_numeral(c)) { f = m_env.pp_datalog_literal(c); } + else if (m_env.get_strutil().is_string(c)) { + f = m_env.pp_str_literal(c); + } else { buffer names; if (m().is_label_lit(c, names)) { diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 8aac71b8c..17bc322bc 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -29,6 +29,7 @@ Revision History: #include"array_decl_plugin.h" #include"fpa_decl_plugin.h" #include"dl_decl_plugin.h" +#include"str_decl_plugin.h" #include"smt2_util.h" class smt2_pp_environment { @@ -47,12 +48,14 @@ public: virtual bv_util & get_bvutil() = 0; virtual array_util & get_arutil() = 0; virtual fpa_util & get_futil() = 0; + virtual str_util & get_strutil() = 0; virtual datalog::dl_decl_util& get_dlutil() = 0; virtual bool uses(symbol const & s) const = 0; virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len); virtual format_ns::format * pp_bv_literal(app * t, bool use_bv_lits, bool bv_neg); virtual format_ns::format * pp_arith_literal(app * t, bool decimal, unsigned prec); virtual format_ns::format * pp_float_literal(app * t, bool use_bv_lits, bool use_float_real_lits); + virtual format_ns::format * pp_str_literal(app * t); virtual format_ns::format * pp_datalog_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); @@ -70,14 +73,16 @@ class smt2_pp_environment_dbg : public smt2_pp_environment { bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; + str_util m_strutil; datalog::dl_decl_util m_dlutil; public: - smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_dlutil(m) {} + smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_strutil(m), m_dlutil(m) {} virtual ast_manager & get_manager() const { return m_manager; } virtual arith_util & get_autil() { return m_autil; } virtual bv_util & get_bvutil() { return m_bvutil; } virtual array_util & get_arutil() { return m_arutil; } virtual fpa_util & get_futil() { return m_futil; } + virtual str_util & get_strutil() { return m_strutil; } virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } virtual bool uses(symbol const & s) const { return false; } }; diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 66b6c23fc..3bd81970d 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -74,6 +74,11 @@ bool str_recognizers::is_string(expr const * n, const char ** val) const { return true; } +bool str_recognizers::is_string(expr const * n) const { + const char * tmp = 0; + return is_string(n, & tmp); +} + str_util::str_util(ast_manager &m) : str_recognizers(m.mk_family_id(symbol("str"))), m_manager(m) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 2d629e006..57829d542 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -57,6 +57,7 @@ public: family_id get_family_id() const { return get_fid(); } bool is_string(expr const * n, const char ** val) const; + bool is_string(expr const * n) const; // TODO }; diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 0c60d876b..77cbfe132 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -25,6 +25,7 @@ Notes: #include"datatype_decl_plugin.h" #include"seq_decl_plugin.h" #include"fpa_decl_plugin.h" +#include"str_decl_plugin.h" #include"ast_pp.h" #include"var_subst.h" #include"pp.h" @@ -241,6 +242,7 @@ protected: bv_util m_bvutil; array_util m_arutil; fpa_util m_futil; + str_util m_strutil; datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { @@ -261,13 +263,14 @@ protected: } public: - pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_dlutil(o.m()) {} + pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_strutil(o.m()), m_dlutil(o.m()) {} virtual ~pp_env() {} virtual ast_manager & get_manager() const { return m_owner.m(); } virtual arith_util & get_autil() { return m_autil; } virtual bv_util & get_bvutil() { return m_bvutil; } virtual array_util & get_arutil() { return m_arutil; } virtual fpa_util & get_futil() { return m_futil; } + virtual str_util & get_strutil() { return m_strutil; } virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } virtual bool uses(symbol const & s) const { return From 8137e022e3dc27e579521d4bc32fa944462d473c Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 6 Sep 2015 20:53:08 -0400 Subject: [PATCH 07/91] load str decl plugin; recognize String sorted constants --- src/ast/str_decl_plugin.cpp | 10 ++++ src/ast/str_decl_plugin.h | 4 ++ src/cmd_context/cmd_context.cpp | 9 ++- src/cmd_context/cmd_context.h | 1 + src/smt/smt_setup.cpp | 18 ++++++ src/smt/smt_setup.h | 2 + src/smt/theory_str.cpp | 97 +++++++++++++++++++++++++++++++++ src/smt/theory_str.h | 50 +++++++++++++++++ 8 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/smt/theory_str.cpp create mode 100644 src/smt/theory_str.h diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 3bd81970d..b6ec25c46 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -66,6 +66,16 @@ app * str_decl_plugin::mk_string(const char * val) { return m_manager->mk_const(d); } +void str_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { + // TODO + // we would do something like: + // op_names.push_back(builtin_name("<=",OP_LE)); +} + +void str_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { + sort_names.push_back(builtin_name("String", STRING_SORT)); +} + bool str_recognizers::is_string(expr const * n, const char ** val) const { if (!is_app_of(n, m_afid, OP_STR)) return false; diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 57829d542..854431366 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -46,6 +46,10 @@ public: unsigned arity, sort * const * domain, sort * range); app * mk_string(const char * val); + + virtual void get_op_names(svector & op_names, symbol const & logic); + + virtual void get_sort_names(svector & sort_names, symbol const & logic); // TODO }; diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 77cbfe132..a7db2f16c 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -563,6 +563,10 @@ bool cmd_context::logic_has_fpa() const { return !has_logic() || m_logic == "QF_FP" || m_logic == "QF_FPBV"; } +bool cmd_context::logic_has_str() const { + return !has_logic() || m_logic == "QF_S"; +} + bool cmd_context::logic_has_array_core(symbol const & s) const { return s == "QF_AX" || @@ -605,6 +609,7 @@ void cmd_context::init_manager_core(bool new_manager) { register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype()); register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq()); register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa()); + register_plugin(symbol("str"), alloc(str_decl_plugin), logic_has_str()); } else { // the manager was created by an external module @@ -618,6 +623,7 @@ void cmd_context::init_manager_core(bool new_manager) { load_plugin(symbol("datatype"), logic_has_datatype(), fids); load_plugin(symbol("seq"), logic_has_seq(), fids); load_plugin(symbol("fpa"), logic_has_fpa(), fids); + load_plugin(symbol("str"), logic_has_str(), fids); svector::iterator it = fids.begin(); svector::iterator end = fids.end(); @@ -671,7 +677,8 @@ bool cmd_context::supported_logic(symbol const & s) const { logic_has_arith_core(s) || logic_has_bv_core(s) || logic_has_array_core(s) || logic_has_seq_core(s) || logic_has_horn(s) || - s == "QF_FP" || s == "QF_FPBV"; + s == "QF_FP" || s == "QF_FPBV" || + s == "QF_S"; } bool cmd_context::set_logic(symbol const & s) { diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index f9e50e611..37dccab8a 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -242,6 +242,7 @@ protected: bool logic_has_array() const; bool logic_has_datatype() const; bool logic_has_fpa() const; + bool logic_has_str() const; bool supported_logic(symbol const & s) const; void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; } diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index e6d21a1e2..5e4af91fd 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -31,6 +31,7 @@ Revision History: #include"theory_dl.h" #include"theory_seq_empty.h" #include"theory_fpa.h" +#include"theory_str.h" namespace smt { @@ -117,6 +118,8 @@ namespace smt { setup_QF_FP(); else if (m_logic == "QF_FPBV") setup_QF_FPBV(); + else if (m_logic == "QF_S") + setup_QF_S(); else setup_unknown(); } @@ -158,6 +161,8 @@ namespace smt { setup_QF_BVRE(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(st); + else if (m_logic == "QF_S") + setup_QF_S(); else if (m_logic == "AUFLIA") setup_AUFLIA(st); else if (m_logic == "AUFLIRA") @@ -694,6 +699,11 @@ namespace smt { m_context.register_plugin(alloc(smt::theory_fpa, m_manager)); } + void setup::setup_QF_S() { + setup_QF_LIA(); + m_context.register_plugin(alloc(smt::theory_str, m_manager)); + } + bool is_arith(static_features const & st) { return st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0; } @@ -800,6 +810,11 @@ namespace smt { m_context.register_plugin(alloc(theory_fpa, m_manager)); } + void setup::setup_str() { + setup_arith(); + m_context.register_plugin(alloc(theory_str, m_manager)); + } + void setup::setup_unknown() { setup_arith(); setup_arrays(); @@ -808,6 +823,7 @@ namespace smt { setup_dl(); setup_seq(); setup_fpa(); + setup_str(); } void setup::setup_unknown(static_features & st) { @@ -906,6 +922,8 @@ namespace smt { return; } + // TODO setup_str() by features + setup_unknown(); } diff --git a/src/smt/smt_setup.h b/src/smt/smt_setup.h index 6cbcb9602..6beb0b239 100644 --- a/src/smt/smt_setup.h +++ b/src/smt/smt_setup.h @@ -77,6 +77,7 @@ namespace smt { void setup_QF_AUFLIA(static_features const & st); void setup_QF_FP(); void setup_QF_FPBV(); + void setup_QF_S(); void setup_LRA(); void setup_AUFLIA(bool simple_array = true); void setup_AUFLIA(static_features const & st); @@ -98,6 +99,7 @@ namespace smt { void setup_i_arith(); void setup_mi_arith(); void setup_fpa(); + void setup_str(); public: setup(context & c, smt_params & params); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp new file mode 100644 index 000000000..bc1b59551 --- /dev/null +++ b/src/smt/theory_str.cpp @@ -0,0 +1,97 @@ +/*++ +Module Name: + + theory_str.cpp + +Abstract: + + String Theory Plugin + +Author: + + Murphy Berzish (mtrberzi) 2015-09-03 + +Revision History: + +--*/ +#include"ast_smt2_pp.h" +#include"smt_context.h" +#include"theory_str.h" +#include"smt_model_generator.h" + +namespace smt { + +theory_str::theory_str(ast_manager &m): + theory(m.mk_family_id("str")) +{ +} + +theory_str::~theory_str() { +} + +bool theory_str::internalize_atom(app * atom, bool gate_ctx) { + // TODO I have no idea if this is correct. + TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << "\n";); + SASSERT(atom->get_family_id() == get_family_id()); + + ast_manager & m = get_manager(); + context & ctx = get_context(); + + if (ctx.b_internalized(atom)) + return true; + + unsigned num_args = atom->get_num_args(); + for (unsigned i = 0; i < num_args; i++) + ctx.internalize(atom->get_arg(i), false); + + literal l(ctx.mk_bool_var(atom)); + ctx.set_var_theory(l.var(), get_id()); + + return true; +} + +bool theory_str::internalize_term(app * term) { + // TODO I have no idea if this is correct either. + ast_manager & m = get_manager(); + context & ctx = get_context(); + TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << "\n";); + SASSERT(term->get_family_id() == get_family_id()); + SASSERT(!ctx.e_internalized(term)); + + unsigned num_args = term->get_num_args(); + for (unsigned i = 0; i < num_args; i++) + ctx.internalize(term->get_arg(i), false); + + enode * e = (ctx.e_internalized(term)) ? ctx.get_enode(term) : + ctx.mk_enode(term, false, false, true); + + if (is_attached_to_var(e)) + return false; + + attach_new_th_var(e); + + return true; +} + +void theory_str::attach_new_th_var(enode * n) { + context & ctx = get_context(); + theory_var v = mk_var(n); + ctx.attach_th_var(n, this, v); + TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := " << v << "\n";); +} + +void theory_str::new_eq_eh(theory_var x, theory_var y) { + // TODO + TRACE("t_str", tout << "new eq: " << x << " = " << y << std::endl;); + TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << + mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); +} + +void theory_str::new_diseq_eh(theory_var x, theory_var y) { + // TODO + TRACE("t_str", tout << "new diseq: " << x << " != " << y << std::endl;); + TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << + mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); +} + +}; /* namespace smt */ diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h new file mode 100644 index 000000000..7bb5b5148 --- /dev/null +++ b/src/smt/theory_str.h @@ -0,0 +1,50 @@ +/*++ +Module Name: + + theory_str.h + +Abstract: + + String Theory Plugin + +Author: + + Murphy Berzish (mtrberzi) 2015-09-03 + +Revision History: + +--*/ +#ifndef _THEORY_STR_H_ +#define _THEORY_STR_H_ + +#include"smt_theory.h" +#include"trail.h" +#include"th_rewriter.h" +#include"value_factory.h" +#include"smt_model_generator.h" + +namespace smt { + + class str_value_factory : public value_factory { + // TODO + }; + + class theory_str : public theory { + // TODO + protected: + virtual bool internalize_atom(app * atom, bool gate_ctx); + virtual bool internalize_term(app * term); + + virtual void new_eq_eh(theory_var, theory_var); + virtual void new_diseq_eh(theory_var, theory_var); + virtual theory* mk_fresh(context*) { return alloc(theory_str, get_manager()); } + public: + theory_str(ast_manager& m); + virtual ~theory_str(); + protected: + void attach_new_th_var(enode * n); + }; + +}; + +#endif /* _THEORY_STR_H_ */ From f0c301e920affab0ae2586a762a74280b488412f Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 6 Sep 2015 21:05:32 -0400 Subject: [PATCH 08/91] register Concat function now reaches str_decl_plugin::mk_func_decl() --- src/ast/str_decl_plugin.cpp | 19 ++++++++++++++----- src/ast/str_decl_plugin.h | 5 ++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index b6ec25c46..eb309ecf0 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -23,7 +23,8 @@ Revision History: str_decl_plugin::str_decl_plugin(): m_strv_sym("String"), - m_str_decl(0){ + m_str_decl(0), + m_concat_decl(0){ } str_decl_plugin::~str_decl_plugin(){ @@ -39,7 +40,17 @@ void str_decl_plugin::set_manager(ast_manager * m, family_id id) { m_str_decl = m->mk_sort(symbol("String"), sort_info(id, STRING_SORT)); m->inc_ref(m_str_decl); sort * s = m_str_decl; - /* TODO mk_pred, etc. */ + +#define MK_AC_OP(FIELD, NAME, KIND, SORT) { \ + func_decl_info info(id, KIND); \ + info.set_associative(); \ + info.set_flat_associative(); \ + info.set_commutative(); \ + FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ + m->inc_ref(FIELD); \ + } + + MK_AC_OP(m_concat_decl, "Concat", OP_STRCAT, s); } decl_plugin * str_decl_plugin::mk_fresh() { @@ -67,9 +78,7 @@ app * str_decl_plugin::mk_string(const char * val) { } void str_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { - // TODO - // we would do something like: - // op_names.push_back(builtin_name("<=",OP_LE)); + op_names.push_back(builtin_name("Concat", OP_STRCAT)); } void str_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 854431366..16e1ef4a3 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -25,7 +25,8 @@ enum str_sort_kind { enum str_op_kind { OP_STR, /* string constants */ - + // + OP_STRCAT, LAST_STR_OP }; @@ -34,6 +35,8 @@ protected: symbol m_strv_sym; sort * m_str_decl; + func_decl * m_concat_decl; + virtual void set_manager(ast_manager * m, family_id id); public: str_decl_plugin(); From 7f0d9157ac9ec470f958902401e957e72871f177 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 6 Sep 2015 21:47:57 -0400 Subject: [PATCH 09/91] at least for now, Concat is no longer associative this means that we'll always have (Concat a b) instead of variadic forms --- src/ast/str_decl_plugin.cpp | 31 ++++++++++++++++++++----------- src/ast/str_decl_plugin.h | 2 ++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index eb309ecf0..70c8a6ebe 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -41,16 +41,11 @@ void str_decl_plugin::set_manager(ast_manager * m, family_id id) { m->inc_ref(m_str_decl); sort * s = m_str_decl; -#define MK_AC_OP(FIELD, NAME, KIND, SORT) { \ - func_decl_info info(id, KIND); \ - info.set_associative(); \ - info.set_flat_associative(); \ - info.set_commutative(); \ - FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, info); \ - m->inc_ref(FIELD); \ - } +#define MK_OP(FIELD, NAME, KIND, SORT) \ + FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, func_decl_info(id, KIND)); \ + m->inc_ref(FIELD) - MK_AC_OP(m_concat_decl, "Concat", OP_STRCAT, s); + MK_OP(m_concat_decl, "Concat", OP_STRCAT, s); } decl_plugin * str_decl_plugin::mk_fresh() { @@ -64,10 +59,24 @@ sort * str_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter } } +func_decl * str_decl_plugin::mk_func_decl(decl_kind k) { + switch(k) { + case OP_STRCAT: return m_concat_decl; + default: return 0; + } +} + func_decl * str_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - /* TODO */ - m_manager->raise_exception("str_decl_plugin::mk_func_decl() not yet implemented"); return 0; + if (k == OP_STR) { + m_manager->raise_exception("OP_STR not yet implemented in mk_func_decl!"); + return 0; + } + if (arity == 0) { + m_manager->raise_exception("no arguments supplied to string operator"); + return 0; + } + return mk_func_decl(k); } app * str_decl_plugin::mk_string(const char * val) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 16e1ef4a3..d190e9ff7 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -38,6 +38,8 @@ protected: func_decl * m_concat_decl; virtual void set_manager(ast_manager * m, family_id id); + + func_decl * mk_func_decl(decl_kind k); public: str_decl_plugin(); virtual ~str_decl_plugin(); From dc86385e7fb635484b596fc9fdd7ab17274e0e55 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 7 Sep 2015 16:13:48 -0400 Subject: [PATCH 10/91] add Length function to theory of strings --- src/ast/str_decl_plugin.cpp | 13 +++++++++++++ src/ast/str_decl_plugin.h | 6 ++++++ src/smt/theory_str.cpp | 37 +++++++++++++++++++++++++++++++++++-- src/smt/theory_str.h | 8 ++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 70c8a6ebe..fa0a26f25 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -33,6 +33,9 @@ str_decl_plugin::~str_decl_plugin(){ void str_decl_plugin::finalize(void) { #define DEC_REF(decl) if (decl) { m_manager->dec_ref(decl); } ((void) 0) DEC_REF(m_str_decl); + DEC_REF(m_concat_decl); + DEC_REF(m_length_decl); + DEC_REF(m_int_sort); } void str_decl_plugin::set_manager(ast_manager * m, family_id id) { @@ -41,11 +44,19 @@ void str_decl_plugin::set_manager(ast_manager * m, family_id id) { m->inc_ref(m_str_decl); sort * s = m_str_decl; + m_arith_fid = m_manager->mk_family_id("arith"); + m_int_sort = m_manager->mk_sort(m_arith_fid, INT_SORT); + SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before str_decl_plugin. + m_manager->inc_ref(m_int_sort); + sort * i = m_int_sort; + #define MK_OP(FIELD, NAME, KIND, SORT) \ FIELD = m->mk_func_decl(symbol(NAME), SORT, SORT, SORT, func_decl_info(id, KIND)); \ m->inc_ref(FIELD) MK_OP(m_concat_decl, "Concat", OP_STRCAT, s); + + m_length_decl = m->mk_func_decl(symbol("Length"), s, i); m_manager->inc_ref(m_length_decl); } decl_plugin * str_decl_plugin::mk_fresh() { @@ -62,6 +73,7 @@ sort * str_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter func_decl * str_decl_plugin::mk_func_decl(decl_kind k) { switch(k) { case OP_STRCAT: return m_concat_decl; + case OP_STRLEN: return m_length_decl; default: return 0; } } @@ -88,6 +100,7 @@ app * str_decl_plugin::mk_string(const char * val) { void str_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("Concat", OP_STRCAT)); + op_names.push_back(builtin_name("Length", OP_STRLEN)); } void str_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index d190e9ff7..3fd5fb7e6 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -18,6 +18,7 @@ Revision History: #define _STR_DECL_PLUGIN_H_ #include"ast.h" +#include"arith_decl_plugin.h" enum str_sort_kind { STRING_SORT, @@ -27,6 +28,7 @@ enum str_op_kind { OP_STR, /* string constants */ // OP_STRCAT, + OP_STRLEN, LAST_STR_OP }; @@ -35,7 +37,11 @@ protected: symbol m_strv_sym; sort * m_str_decl; + sort * m_int_sort; + family_id m_arith_fid; + func_decl * m_concat_decl; + func_decl * m_length_decl; virtual void set_manager(ast_manager * m, family_id id); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index bc1b59551..2bf67ed81 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -31,7 +31,7 @@ theory_str::~theory_str() { bool theory_str::internalize_atom(app * atom, bool gate_ctx) { // TODO I have no idea if this is correct. - TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << "\n";); + TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << std::endl;); SASSERT(atom->get_family_id() == get_family_id()); ast_manager & m = get_manager(); @@ -54,7 +54,7 @@ bool theory_str::internalize_term(app * term) { // TODO I have no idea if this is correct either. ast_manager & m = get_manager(); context & ctx = get_context(); - TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << "\n";); + TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); SASSERT(term->get_family_id() == get_family_id()); SASSERT(!ctx.e_internalized(term)); @@ -80,6 +80,20 @@ void theory_str::attach_new_th_var(enode * n) { TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := " << v << "\n";); } +void theory_str::init_search_eh() { + ast_manager & m = get_manager(); + context & ctx = get_context(); + TRACE("t_str", + tout << "search started, assignments are:" << std::endl; + expr_ref_vector assignment(m); + ctx.get_assignments(assignment); + for (expr_ref_vector::iterator i = assignment.begin(); i != assignment.end(); ++i) { + expr * ex = *i; + tout << mk_ismt2_pp(ex, m) << std::endl; + } + ); +} + void theory_str::new_eq_eh(theory_var x, theory_var y) { // TODO TRACE("t_str", tout << "new eq: " << x << " = " << y << std::endl;); @@ -94,4 +108,23 @@ void theory_str::new_diseq_eh(theory_var x, theory_var y) { mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); } +void theory_str::relevant_eh(app * n) { + TRACE("t_str", tout << "relevant: " << mk_ismt2_pp(n, get_manager()) << "\n";); +} + +void theory_str::assign_eh(bool_var v, bool is_true) { + context & ctx = get_context(); + TRACE("t_str", tout << "assert: v" << v << " #" << ctx.bool_var2expr(v)->get_id() << " is_true: " << is_true << "\n";); +} + +void theory_str::push_scope_eh() { + TRACE("t_str", tout << "push" << std::endl;); +} + +final_check_status theory_str::final_check_eh() { + // TODO + TRACE("t_str", tout << "final check" << std::endl;); + return FC_DONE; +} + }; /* namespace smt */ diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 7bb5b5148..5ee5502de 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -38,6 +38,14 @@ namespace smt { virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); virtual theory* mk_fresh(context*) { return alloc(theory_str, get_manager()); } + + virtual void init_search_eh(); + + virtual void relevant_eh(app * n); + virtual void assign_eh(bool_var v, bool is_true); + virtual void push_scope_eh(); + + virtual final_check_status final_check_eh(); public: theory_str(ast_manager& m); virtual ~theory_str(); From 9b04f1570f45b55d409f981bd389c46dc14825e0 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 7 Sep 2015 19:40:25 -0400 Subject: [PATCH 11/91] instantiate length axiom for concatenation --- src/ast/str_decl_plugin.cpp | 3 +- src/smt/theory_str.cpp | 80 ++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 15 ++++++- 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index fa0a26f25..0e74493ff 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -24,7 +24,8 @@ Revision History: str_decl_plugin::str_decl_plugin(): m_strv_sym("String"), m_str_decl(0), - m_concat_decl(0){ + m_concat_decl(0), + m_length_decl(0){ } str_decl_plugin::~str_decl_plugin(){ diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 2bf67ed81..c6d51b1a4 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -18,17 +18,39 @@ Revision History: #include"smt_context.h" #include"theory_str.h" #include"smt_model_generator.h" +#include"ast_pp.h" +#include"ast_ll_pp.h" namespace smt { -theory_str::theory_str(ast_manager &m): - theory(m.mk_family_id("str")) +theory_str::theory_str(ast_manager & m): + theory(m.mk_family_id("str")), + search_started(false), + m_autil(m) { } theory_str::~theory_str() { } +void theory_str::assert_axiom(unsigned num_lits, literal * lits) { + context & ctx = get_context(); + TRACE("t_str_detail", + tout << "assert_axiom: literals:\n"; + for (unsigned i = 0; i < num_lits; ++i) { + expr * e = ctx.bool_var2expr(lits[i].var()); + if (lits[i].sign()) + tout << "not "; + tout << mk_pp(e, get_manager()) << " "; + tout << "\n"; + }); + ctx.mk_th_axiom(get_id(), num_lits, lits); +} + +void theory_str::assert_axiom(literal l) { + assert_axiom(1, &l); +} + bool theory_str::internalize_atom(app * atom, bool gate_ctx) { // TODO I have no idea if this is correct. TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << std::endl;); @@ -70,9 +92,62 @@ bool theory_str::internalize_term(app * term) { attach_new_th_var(e); + if (is_concat(term)) { + instantiate_concat_axiom(e); + } + return true; } +app * theory_str::mk_strlen(app * e) { + expr * args[1] = {e}; + return get_manager().mk_app(get_id(), OP_STRLEN, 0, 0, 1, args); +} + +/* + * Instantiate an axiom of the following form: + * Length(Concat(x, y)) = Length(x) + Length(y) + */ +void theory_str::instantiate_concat_axiom(enode * cat) { + SASSERT(is_concat(cat)); + app * a_cat = cat->get_owner(); + + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // build LHS + expr_ref len_xy(m); + // TODO re-use ASTs for length subexpressions, like in old Z3-str? + // TODO should we use str_util for these and other expressions? + len_xy = mk_strlen(a_cat); + SASSERT(len_xy); + + // build RHS: start by extracting x and y from Concat(x, y) + unsigned nArgs = a_cat->get_num_args(); + SASSERT(nArgs == 2); + app * a_x = to_app(a_cat->get_arg(0)); + app * a_y = to_app(a_cat->get_arg(1)); + + expr_ref len_x(m); + len_x = mk_strlen(a_x); + SASSERT(len_x); + + expr_ref len_y(m); + len_y = mk_strlen(a_y); + SASSERT(len_y); + + // now build len_x + len_y + app * len_x_plus_len_y = m_autil.mk_add(len_x, len_y); + SASSERT(len_x_plus_len_y); + + TRACE("t_str", tout << mk_bounded_pp(len_xy, m) << " = " << mk_bounded_pp(len_x_plus_len_y, m) << "\n";); + + // finally assert equality between the two subexpressions + literal l(mk_eq(len_xy, len_x_plus_len_y, true)); + ctx.mark_as_relevant(l); + assert_axiom(l); +} + void theory_str::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); @@ -92,6 +167,7 @@ void theory_str::init_search_eh() { tout << mk_ismt2_pp(ex, m) << std::endl; } ); + search_started = true; } void theory_str::new_eq_eh(theory_var x, theory_var y) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 5ee5502de..867c4316b 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -22,6 +22,7 @@ Revision History: #include"th_rewriter.h" #include"value_factory.h" #include"smt_model_generator.h" +#include"arith_decl_plugin.h" namespace smt { @@ -31,6 +32,8 @@ namespace smt { class theory_str : public theory { // TODO + protected: + bool search_started; protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); @@ -46,9 +49,19 @@ namespace smt { virtual void push_scope_eh(); virtual final_check_status final_check_eh(); + + void assert_axiom(unsigned num_lits, literal * lits); + void assert_axiom(literal l); + + app * mk_strlen(app * e); + + bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } + bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } + void instantiate_concat_axiom(enode * cat); public: - theory_str(ast_manager& m); + theory_str(ast_manager & m); virtual ~theory_str(); + arith_util m_autil; protected: void attach_new_th_var(enode * n); }; From 799fd07c85f8ec89552454334fe8a3e4c3f0273e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 7 Sep 2015 19:51:52 -0400 Subject: [PATCH 12/91] optimization: return integer consts for strlen() over constant strings --- src/smt/theory_str.cpp | 14 +++++++++++--- src/smt/theory_str.h | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index c6d51b1a4..568e6b5ae 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -26,7 +26,8 @@ namespace smt { theory_str::theory_str(ast_manager & m): theory(m.mk_family_id("str")), search_started(false), - m_autil(m) + m_autil(m), + m_strutil(m) { } @@ -100,8 +101,15 @@ bool theory_str::internalize_term(app * term) { } app * theory_str::mk_strlen(app * e) { - expr * args[1] = {e}; - return get_manager().mk_app(get_id(), OP_STRLEN, 0, 0, 1, args); + if (m_strutil.is_string(e)) { + const char * strval = 0; + m_strutil.is_string(e, &strval); + int len = strlen(strval); + return m_autil.mk_numeral(rational(len), true); + } else { + expr * args[1] = {e}; + return get_manager().mk_app(get_id(), OP_STRLEN, 0, 0, 1, args); + } } /* diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 867c4316b..a583a106e 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -34,6 +34,8 @@ namespace smt { // TODO protected: bool search_started; + arith_util m_autil; + str_util m_strutil; protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); @@ -61,7 +63,6 @@ namespace smt { public: theory_str(ast_manager & m); virtual ~theory_str(); - arith_util m_autil; protected: void attach_new_th_var(enode * n); }; From 992fff8ba8c882b0724aafca2620bd8dd151365a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 10 Sep 2015 18:43:14 -0400 Subject: [PATCH 13/91] set up theory of arithmetic correctly closes #1 --- src/cmd_context/cmd_context.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index a7db2f16c..394350879 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -517,7 +517,8 @@ bool cmd_context::logic_has_arith_core(symbol const & s) const { s == "LRA" || s == "QF_FP" || s == "QF_FPBV" || - s == "HORN"; + s == "HORN" || + s == "QF_S"; } bool cmd_context::logic_has_arith() const { From 4d5a0ea53f42258e375be664aea15bbcbbc66a12 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 26 Sep 2015 18:51:02 -0400 Subject: [PATCH 14/91] WIP add axioms --- src/ast/str_decl_plugin.cpp | 9 ++- src/ast/str_decl_plugin.h | 1 + src/smt/theory_str.cpp | 125 ++++++++++++++++++++++++++++-------- src/smt/theory_str.h | 25 ++++---- 4 files changed, 120 insertions(+), 40 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 0e74493ff..1502e3d3a 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -25,7 +25,10 @@ str_decl_plugin::str_decl_plugin(): m_strv_sym("String"), m_str_decl(0), m_concat_decl(0), - m_length_decl(0){ + m_length_decl(0), + m_arith_plugin(0), + m_arith_fid(0), + m_int_sort(0){ } str_decl_plugin::~str_decl_plugin(){ @@ -45,7 +48,11 @@ void str_decl_plugin::set_manager(ast_manager * m, family_id id) { m->inc_ref(m_str_decl); sort * s = m_str_decl; + SASSERT(m_manager->has_plugin(symbol("arith"))); m_arith_fid = m_manager->mk_family_id("arith"); + m_arith_plugin = static_cast(m_manager->get_plugin(m_arith_fid)); + SASSERT(m_arith_plugin); + m_int_sort = m_manager->mk_sort(m_arith_fid, INT_SORT); SASSERT(m_int_sort != 0); // arith_decl_plugin must be installed before str_decl_plugin. m_manager->inc_ref(m_int_sort); diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 3fd5fb7e6..7e75fbaf0 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -37,6 +37,7 @@ protected: symbol m_strv_sym; sort * m_str_decl; + arith_decl_plugin * m_arith_plugin; sort * m_int_sort; family_id m_arith_fid; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 568e6b5ae..ff4b3dd76 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -34,22 +34,25 @@ theory_str::theory_str(ast_manager & m): theory_str::~theory_str() { } -void theory_str::assert_axiom(unsigned num_lits, literal * lits) { +void theory_str::assert_axiom(ast * a) { + /* + if (search_started) { + // effectively Z3_theory_assert_axiom + NOT_IMPLEMENTED_YET(); + } else { + // effectively Z3_assert_cnstr + context & ctx = get_context(); + ctx.assert_expr(to_expr(a)); + } + */ + TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); + expr * e = to_expr(a); context & ctx = get_context(); - TRACE("t_str_detail", - tout << "assert_axiom: literals:\n"; - for (unsigned i = 0; i < num_lits; ++i) { - expr * e = ctx.bool_var2expr(lits[i].var()); - if (lits[i].sign()) - tout << "not "; - tout << mk_pp(e, get_manager()) << " "; - tout << "\n"; - }); - ctx.mk_th_axiom(get_id(), num_lits, lits); -} - -void theory_str::assert_axiom(literal l) { - assert_axiom(1, &l); + ctx.internalize(e, false); + literal lit(ctx.get_literal(e)); + ctx.mark_as_relevant(lit); + ctx.mk_th_axiom(get_id(), 1, &lit); + TRACE("t_str_detail", tout << "done asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); } bool theory_str::internalize_atom(app * atom, bool gate_ctx) { @@ -93,15 +96,17 @@ bool theory_str::internalize_term(app * term) { attach_new_th_var(e); + /* if (is_concat(term)) { instantiate_concat_axiom(e); } + */ return true; } app * theory_str::mk_strlen(app * e) { - if (m_strutil.is_string(e)) { + /*if (m_strutil.is_string(e)) {*/ if (false) { const char * strval = 0; m_strutil.is_string(e, &strval); int len = strlen(strval); @@ -145,22 +150,90 @@ void theory_str::instantiate_concat_axiom(enode * cat) { SASSERT(len_y); // now build len_x + len_y - app * len_x_plus_len_y = m_autil.mk_add(len_x, len_y); + expr_ref len_x_plus_len_y(m); + len_x_plus_len_y = m_autil.mk_add(len_x, len_y); SASSERT(len_x_plus_len_y); - TRACE("t_str", tout << mk_bounded_pp(len_xy, m) << " = " << mk_bounded_pp(len_x_plus_len_y, m) << "\n";); - // finally assert equality between the two subexpressions - literal l(mk_eq(len_xy, len_x_plus_len_y, true)); - ctx.mark_as_relevant(l); - assert_axiom(l); + app * eq = m.mk_eq(len_xy, len_x_plus_len_y); + SASSERT(eq); + TRACE("t_str", tout << mk_bounded_pp(eq, m) << std::endl;); + assert_axiom(eq); +} + +/* + * Add axioms that are true for any string variable: + * 1. Length(x) >= 0 + * 2. Length(x) == 0 <=> x == "" + */ +void theory_str::instantiate_basic_string_axioms(enode * str) { + // generate a stronger axiom for constant strings + if (m_strutil.is_string(str->get_owner())) { + // TODO + } else { + // TODO keep track of which enodes we have added axioms for, so we don't add the same ones twice? + app * a_str = str->get_owner(); + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // TODO find out why these are crashing the SMT solver + + // build axiom 1: Length(a_str) >= 0 + { + // build LHS + expr_ref len_str(m); + len_str = mk_strlen(a_str); + 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_ge_rhs = m_autil.mk_ge(len_str, zero); + SASSERT(lhs_ge_rhs); + // TODO verify that this works + TRACE("t_str_detail", tout << "string axiom 1: " << mk_bounded_pp(lhs_ge_rhs, m) << std::endl;); + assert_axiom(lhs_ge_rhs); + } + + /* + // build axiom 2: Length(a_str) == 0 <=> a_str == "" + { + // build LHS of iff + expr_ref len_str(m); + len_str = mk_strlen(a_str); + SASSERT(len_str); + expr_ref zero(m); + zero = m_autil.mk_numeral(rational(0), true); + SASSERT(zero); + expr_ref lhs(m); + lhs = ctx.mk_eq_atom(len_str, zero); + SASSERT(lhs); + // build RHS of iff + expr_ref empty_str(m); + empty_str = m_strutil.mk_string(""); + SASSERT(empty_str); + expr_ref rhs(m); + rhs = ctx.mk_eq_atom(a_str, empty_str); + SASSERT(rhs); + // build LHS <=> RHS and assert + TRACE("t_str_detail", tout << "string axiom 2: " << mk_bounded_pp(lhs, m) << " <=> " << mk_bounded_pp(rhs, m) << std::endl;); + // TODO this is kind of a hack, maybe just ctx.assert_expr() will be enough? + literal l(mk_eq(lhs, rhs, true)); + ctx.mark_as_relevant(l); + assert_axiom(l); + } + */ + } } void theory_str::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); - TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := " << v << "\n";); + TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := v#" << v << std::endl;); + // probably okay...note however that this seems to miss constants and functions + //instantiate_basic_string_axioms(n); } void theory_str::init_search_eh() { @@ -180,14 +253,14 @@ void theory_str::init_search_eh() { void theory_str::new_eq_eh(theory_var x, theory_var y) { // TODO - TRACE("t_str", tout << "new eq: " << x << " = " << y << std::endl;); + TRACE("t_str", tout << "new eq: v#" << x << " = v#" << y << std::endl;); TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { // TODO - TRACE("t_str", tout << "new diseq: " << x << " != " << y << std::endl;); + TRACE("t_str", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); } @@ -198,7 +271,7 @@ void theory_str::relevant_eh(app * n) { void theory_str::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); - TRACE("t_str", tout << "assert: v" << v << " #" << ctx.bool_var2expr(v)->get_id() << " is_true: " << is_true << "\n";); + TRACE("t_str", tout << "assert: v" << v << " #" << ctx.bool_var2expr(v)->get_id() << " is_true: " << is_true << std::endl;); } void theory_str::push_scope_eh() { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index a583a106e..0e7b0bcc8 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -36,6 +36,18 @@ namespace smt { bool search_started; arith_util m_autil; str_util m_strutil; + protected: + void assert_axiom(ast * e); + + app * mk_strlen(app * e); + + bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } + bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } + void instantiate_concat_axiom(enode * cat); + void instantiate_basic_string_axioms(enode * str); + public: + theory_str(ast_manager & m); + virtual ~theory_str(); protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); @@ -51,19 +63,6 @@ namespace smt { virtual void push_scope_eh(); virtual final_check_status final_check_eh(); - - void assert_axiom(unsigned num_lits, literal * lits); - void assert_axiom(literal l); - - app * mk_strlen(app * e); - - bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } - bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } - void instantiate_concat_axiom(enode * cat); - public: - theory_str(ast_manager & m); - virtual ~theory_str(); - protected: void attach_new_th_var(enode * n); }; From f6affe64d0cb72f1e434e05d69d4d7845600fb0a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 26 Sep 2015 21:02:56 -0400 Subject: [PATCH 15/91] deferred addition of basic string axioms no longer crashes the solver and got our first correct UNSAT! --- src/smt/theory_str.cpp | 53 +++++++++++++++++++++++++++++++++++------- src/smt/theory_str.h | 7 ++++++ 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index ff4b3dd76..b99087f29 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -117,6 +117,20 @@ app * theory_str::mk_strlen(app * e) { } } +bool theory_str::can_propagate() { + return !m_basicstr_axiom_todo.empty(); +} + +void theory_str::propagate() { + TRACE("t_str_detail", tout << "trying to propagate..." << std::endl;); + while (can_propagate()) { + for (unsigned i = 0; i < m_basicstr_axiom_todo.size(); ++i) { + instantiate_basic_string_axioms(m_basicstr_axiom_todo[i]); + } + m_basicstr_axiom_todo.reset(); + } +} + /* * Instantiate an axiom of the following form: * Length(Concat(x, y)) = Length(x) + Length(y) @@ -165,17 +179,32 @@ void theory_str::instantiate_concat_axiom(enode * cat) { * Add axioms that are true for any string variable: * 1. Length(x) >= 0 * 2. Length(x) == 0 <=> x == "" + * If the term is a string constant, we can assert something stronger: + * Length(x) == strlen(x) */ void theory_str::instantiate_basic_string_axioms(enode * str) { - // generate a stronger axiom for constant strings - if (m_strutil.is_string(str->get_owner())) { - // TODO - } else { - // TODO keep track of which enodes we have added axioms for, so we don't add the same ones twice? - app * a_str = str->get_owner(); - context & ctx = get_context(); - ast_manager & m = get_manager(); + // TODO keep track of which enodes we have added axioms for, so we don't add the same ones twice? + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // generate a stronger axiom for constant strings + app * a_str = str->get_owner(); + if (m_strutil.is_string(str->get_owner())) { + expr_ref len_str(m); + len_str = mk_strlen(a_str); + SASSERT(len_str); + + const char * strconst = 0; + m_strutil.is_string(str->get_owner(), & strconst); + TRACE("t_str_detail", tout << "instantiating constant string axioms for \"" << strconst << "\"" << std::endl;); + int l = strlen(strconst); + expr_ref len(m_autil.mk_numeral(rational(l), true), m); + + literal lit(mk_eq(len_str, len, false)); + ctx.mark_as_relevant(lit); + ctx.mk_th_axiom(get_id(), 1, &lit); + } else { // TODO find out why these are crashing the SMT solver // build axiom 1: Length(a_str) >= 0 @@ -233,7 +262,13 @@ void theory_str::attach_new_th_var(enode * n) { ctx.attach_th_var(n, this, v); TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := v#" << v << std::endl;); // probably okay...note however that this seems to miss constants and functions - //instantiate_basic_string_axioms(n); + m_basicstr_axiom_todo.push_back(n); +} + +void theory_str::reset_eh() { + TRACE("t_str", tout << "resetting" << std::endl;); + m_basicstr_axiom_todo.reset(); + pop_scope_eh(0); } void theory_str::init_search_eh() { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 0e7b0bcc8..a336ec649 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -36,6 +36,8 @@ namespace smt { bool search_started; arith_util m_autil; str_util m_strutil; + + ptr_vector m_basicstr_axiom_todo; protected: void assert_axiom(ast * e); @@ -62,6 +64,11 @@ namespace smt { virtual void assign_eh(bool_var v, bool is_true); virtual void push_scope_eh(); + virtual void reset_eh(); + + virtual bool can_propagate(); + virtual void propagate(); + virtual final_check_status final_check_eh(); void attach_new_th_var(enode * n); }; From 4085db99906b01321921132d458c736ef1ea4239 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 26 Sep 2015 23:35:23 -0400 Subject: [PATCH 16/91] recursive descent through all assertions to discover all String terms set up axioms on these terms to be asserted during propagation --- src/smt/theory_str.cpp | 56 +++++++++++++++++++++++++++++++++--------- src/smt/theory_str.h | 2 ++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index b99087f29..63378a700 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -122,13 +122,14 @@ bool theory_str::can_propagate() { } void theory_str::propagate() { - TRACE("t_str_detail", tout << "trying to propagate..." << std::endl;); while (can_propagate()) { + TRACE("t_str_detail", tout << "propagating..." << std::endl;); for (unsigned i = 0; i < m_basicstr_axiom_todo.size(); ++i) { instantiate_basic_string_axioms(m_basicstr_axiom_todo[i]); } m_basicstr_axiom_todo.reset(); } + TRACE("t_str_detail", tout << "done propagating" << std::endl;); } /* @@ -205,8 +206,6 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); } else { - // TODO find out why these are crashing the SMT solver - // build axiom 1: Length(a_str) >= 0 { // build LHS @@ -261,8 +260,6 @@ void theory_str::attach_new_th_var(enode * n) { theory_var v = mk_var(n); ctx.attach_th_var(n, this, v); TRACE("t_str_detail", tout << "new theory var: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " := v#" << v << std::endl;); - // probably okay...note however that this seems to miss constants and functions - m_basicstr_axiom_todo.push_back(n); } void theory_str::reset_eh() { @@ -271,18 +268,55 @@ void theory_str::reset_eh() { pop_scope_eh(0); } +void theory_str::set_up_axioms(expr * ex) { + // TODO check to make sure we don't set up axioms on the same term twice + TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << std::endl;); + + ast_manager & m = get_manager(); + context & ctx = get_context(); + + sort * ex_sort = m.get_sort(ex); + sort * str_sort = m.mk_sort(get_family_id(), STRING_SORT); + + if (ex_sort == str_sort) { + TRACE("t_str_detail", tout << "expr is of sort String" << std::endl;); + // set up basic string axioms + enode * n = ctx.get_enode(ex); + SASSERT(n); + m_basicstr_axiom_todo.push_back(n); + } else { + TRACE("t_str_detail", tout << "expr is of wrong sort, ignoring" << std::endl;); + } + + // if expr is an application, recursively inspect all arguments + if (is_app(ex)) { + app * term = (app*)ex; + unsigned num_args = term->get_num_args(); + for (unsigned i = 0; i < num_args; i++) { + set_up_axioms(term->get_arg(i)); + } + } +} + void theory_str::init_search_eh() { ast_manager & m = get_manager(); context & ctx = get_context(); - TRACE("t_str", - tout << "search started, assignments are:" << std::endl; - expr_ref_vector assignment(m); - ctx.get_assignments(assignment); - for (expr_ref_vector::iterator i = assignment.begin(); i != assignment.end(); ++i) { - expr * ex = *i; + + TRACE("t_str_detail", + tout << "dumping all asserted formulas:" << std::endl; + unsigned nFormulas = ctx.get_num_asserted_formulas(); + for (unsigned i = 0; i < nFormulas; ++i) { + expr * ex = ctx.get_asserted_formula(i); tout << mk_ismt2_pp(ex, m) << std::endl; } ); + // recursive descent through all asserted formulas to set up axioms + unsigned nFormulas = ctx.get_num_asserted_formulas(); + for (unsigned i = 0; i < nFormulas; ++i) { + expr * ex = ctx.get_asserted_formula(i); + set_up_axioms(ex); + } + search_started = true; } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index a336ec649..23abc3c9d 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -47,6 +47,8 @@ namespace smt { bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } void instantiate_concat_axiom(enode * cat); void instantiate_basic_string_axioms(enode * str); + + void set_up_axioms(expr * ex); public: theory_str(ast_manager & m); virtual ~theory_str(); From 91e9cf272a58dd87f8f523c1f26320953479584a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 00:12:04 -0400 Subject: [PATCH 17/91] assert string axiom 2 --- src/smt/theory_str.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 63378a700..ee230d027 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -224,7 +224,6 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { assert_axiom(lhs_ge_rhs); } - /* // build axiom 2: Length(a_str) == 0 <=> a_str == "" { // build LHS of iff @@ -246,12 +245,11 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { SASSERT(rhs); // build LHS <=> RHS and assert TRACE("t_str_detail", tout << "string axiom 2: " << mk_bounded_pp(lhs, m) << " <=> " << mk_bounded_pp(rhs, m) << std::endl;); - // TODO this is kind of a hack, maybe just ctx.assert_expr() will be enough? literal l(mk_eq(lhs, rhs, true)); ctx.mark_as_relevant(l); - assert_axiom(l); + ctx.mk_th_axiom(get_id(), 1, &l); } - */ + } } From 114b51dec89319412031c18c71e43f6b63ccc8f2 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 17:26:52 -0400 Subject: [PATCH 18/91] only handle equalities in assignments during init_search_eh --- src/smt/theory_str.cpp | 34 +++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 5 ++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index ee230d027..ab0324a57 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -266,6 +266,10 @@ void theory_str::reset_eh() { pop_scope_eh(0); } +void theory_str::handle_equality(expr * lhs, expr * rhs) { + +} + void theory_str::set_up_axioms(expr * ex) { // TODO check to make sure we don't set up axioms on the same term twice TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << std::endl;); @@ -308,13 +312,40 @@ void theory_str::init_search_eh() { tout << mk_ismt2_pp(ex, m) << std::endl; } ); - // recursive descent through all asserted formulas to set up axioms + /* + * Recursive descent through all asserted formulas to set up axioms. + * Note that this is just the input structure and not necessarily things + * that we know to be true or false. We're just doing this to see + * which terms are explicitly mentioned. + */ unsigned nFormulas = ctx.get_num_asserted_formulas(); for (unsigned i = 0; i < nFormulas; ++i) { expr * ex = ctx.get_asserted_formula(i); set_up_axioms(ex); } + /* + * Similar recursive descent, except over all initially assigned terms. + * This is done to find equalities between terms, etc. that we otherwise + * wouldn't get a chance to see. + */ + expr_ref_vector assignments(m); + ctx.get_assignments(assignments); + for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { + expr * ex = *i; + TRACE("t_str_detail", tout << "processing assignment " << mk_ismt2_pp(ex, m) << std::endl;); + if (m.is_eq(ex)) { + TRACE("t_str_detail", tout << "expr is equality" << std::endl;); + app * eq = (app*)ex; + SASSERT(eq->get_num_args() == 2); + expr * lhs = eq->get_arg(0); + expr * rhs = eq->get_arg(1); + handle_equality(lhs, rhs); + } else { + TRACE("t_str_detail", tout << "expr ignored" << std::endl;); + } + } + search_started = true; } @@ -323,6 +354,7 @@ void theory_str::new_eq_eh(theory_var x, theory_var y) { TRACE("t_str", tout << "new eq: v#" << x << " = v#" << y << std::endl;); TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); + handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 23abc3c9d..f58ddea91 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -49,6 +49,7 @@ namespace smt { void instantiate_basic_string_axioms(enode * str); void set_up_axioms(expr * ex); + void handle_equality(expr * lhs, expr * rhs); public: theory_str(ast_manager & m); virtual ~theory_str(); @@ -58,14 +59,12 @@ namespace smt { virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); + virtual theory* mk_fresh(context*) { return alloc(theory_str, get_manager()); } - virtual void init_search_eh(); - virtual void relevant_eh(app * n); virtual void assign_eh(bool_var v, bool is_true); virtual void push_scope_eh(); - virtual void reset_eh(); virtual bool can_propagate(); From 6481fe941ae853e87b462edc0e84b95287c5d37d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 17:48:53 -0400 Subject: [PATCH 19/91] instantiate string-eq length-eq axiom --- src/smt/theory_str.cpp | 66 +++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 2 ++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index ab0324a57..3b9054132 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -118,7 +118,7 @@ app * theory_str::mk_strlen(app * e) { } bool theory_str::can_propagate() { - return !m_basicstr_axiom_todo.empty(); + return !m_basicstr_axiom_todo.empty() || !m_str_eq_length_axiom_todo.empty(); } void theory_str::propagate() { @@ -128,6 +128,14 @@ void theory_str::propagate() { instantiate_basic_string_axioms(m_basicstr_axiom_todo[i]); } m_basicstr_axiom_todo.reset(); + + for (unsigned i = 0; i < m_str_eq_length_axiom_todo.size(); ++i) { + std::pair pair = m_str_eq_length_axiom_todo[i]; + enode * lhs = pair.first; + enode * rhs = pair.second; + instantiate_str_eq_length_axiom(lhs, rhs); + } + m_str_eq_length_axiom_todo.reset(); } TRACE("t_str_detail", tout << "done propagating" << std::endl;); } @@ -253,6 +261,33 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { } } +/* + * Add an axiom of the form: + * (lhs == rhs) -> ( Length(lhs) == Length(rhs) ) + */ +void theory_str::instantiate_str_eq_length_axiom(enode * lhs, enode * rhs) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + app * a_lhs = lhs->get_owner(); + app * a_rhs = rhs->get_owner(); + + // build premise: (lhs == rhs) + expr_ref premise(ctx.mk_eq_atom(a_lhs, a_rhs), m); + + // build conclusion: ( Length(lhs) == Length(rhs) ) + expr_ref len_lhs(mk_strlen(a_lhs), m); + SASSERT(len_lhs); + expr_ref len_rhs(mk_strlen(a_rhs), m); + SASSERT(len_rhs); + expr_ref conclusion(ctx.mk_eq_atom(len_lhs, len_rhs), m); + + // build (premise -> conclusion) and assert + expr_ref axiom(m.mk_implies(premise, conclusion), m); + TRACE("t_str_detail", tout << "string-eq length-eq axiom: " << mk_bounded_pp(axiom, m) << std::endl;); + assert_axiom(axiom); +} + void theory_str::attach_new_th_var(enode * n) { context & ctx = get_context(); theory_var v = mk_var(n); @@ -263,11 +298,40 @@ void theory_str::attach_new_th_var(enode * n) { void theory_str::reset_eh() { TRACE("t_str", tout << "resetting" << std::endl;); m_basicstr_axiom_todo.reset(); + m_str_eq_length_axiom_todo.reset(); pop_scope_eh(0); } void theory_str::handle_equality(expr * lhs, expr * rhs) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + // both terms must be of sort String + sort * lhs_sort = m.get_sort(lhs); + sort * rhs_sort = m.get_sort(rhs); + sort * str_sort = m.mk_sort(get_family_id(), STRING_SORT); + if (lhs_sort != str_sort || rhs_sort != str_sort) { + TRACE("t_str_detail", tout << "skip equality: not String sort" << std::endl;); + return; + } + + // TODO freeVarAttempt()? + + // TODO simplify concat? + + // TODO newEqCheck()? + + // BEGIN new_eq_handler() in strTheory + + // TODO there's some setup with getLenValue() that I don't think is necessary + // because we should already be generating the string length axioms for all string terms + + // set up string length axiom: + // (lhs == rhs) -> (Length(lhs) == Length(rhs)) + enode * e_lhs = ctx.get_enode(lhs); + enode * e_rhs = ctx.get_enode(rhs); + std::pair eq_pair(e_lhs, e_rhs); + m_str_eq_length_axiom_todo.push_back(eq_pair); } void theory_str::set_up_axioms(expr * ex) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index f58ddea91..b9c11c2f0 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -38,6 +38,7 @@ namespace smt { str_util m_strutil; ptr_vector m_basicstr_axiom_todo; + svector > m_str_eq_length_axiom_todo; protected: void assert_axiom(ast * e); @@ -47,6 +48,7 @@ namespace smt { bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } void instantiate_concat_axiom(enode * cat); void instantiate_basic_string_axioms(enode * str); + void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs); void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); From 86e60877187049d3a8800fa27fda8c07a565e4c7 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 21:30:45 -0400 Subject: [PATCH 20/91] starting solve_concat_eq_str(); currently there is an unsoundness bug --- src/smt/theory_str.cpp | 206 ++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 9 ++ 2 files changed, 212 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3b9054132..faaa7fb70 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -144,6 +144,7 @@ void theory_str::propagate() { * Instantiate an axiom of the following form: * Length(Concat(x, y)) = Length(x) + Length(y) */ +// TODO this isn't used yet void theory_str::instantiate_concat_axiom(enode * cat) { SASSERT(is_concat(cat)); app * a_cat = cat->get_owner(); @@ -302,6 +303,99 @@ void theory_str::reset_eh() { pop_scope_eh(0); } +/* + * Check equality among equivalence class members of LHS and RHS + * to discover an incorrect LHS == RHS. + * For example, if we have y2 == "str3" + * and the equivalence classes are + * { y2, (Concat ce m2) } + * { "str3", (Concat abc x2) } + * then y2 can't be equal to "str3". + * Then add an assertion: (y2 == (Concat ce m2)) AND ("str3" == (Concat abc x2)) -> (y2 != "str3") + */ +bool theory_str::new_eq_check(expr * lhs, expr * rhs) { + // TODO this involves messing around with enodes and equivalence classes + return true; +} + +void theory_str::group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + enode * nNode = ctx.get_enode(n); + enode * eqcNode = nNode; + do { + app * ast = eqcNode->get_owner(); + if (is_concat(eqcNode)) { + // TODO simplify_concat + /* + Z3_ast simConcat = simplifyConcat(t, eqcNode); + if (simConcat != eqcNode) { + if (isConcatFunc(t, simConcat)) { + concats.insert(simConcat); + } else { + if (isConstStr(t, simConcat)) { + constStrs.insert(simConcat); + } else { + vars.insert(simConcat); + } + } + } else { + concats.insert(simConcat); + } + */ + concats.insert(ast); + } else if (is_string(eqcNode)) { + consts.insert(ast); + } else { + vars.insert(ast); + } + eqcNode = eqcNode->get_next(); + } while (eqcNode != nNode); +} + +void theory_str::simplify_concat_equality(expr * lhs, expr * rhs) { + // TODO strArgmt::simplifyConcatEq() +} + +/* + * strArgmt::solve_concat_eq_str() + * Solve concatenations of the form: + * const == Concat(const, X) + * const == Concat(X, const) + */ +void theory_str::solve_concat_eq_str(expr * concat, expr * str) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + + TRACE("t_str_detail", tout << mk_ismt2_pp(concat, m) << " == " << mk_ismt2_pp(str, m) << std::endl;); + + if (is_concat(to_app(concat)) && is_string(to_app(str))) { + const char * tmp = 0; + m_strutil.is_string(str, & tmp); + std::string const_str(tmp); + app * a_concat = to_app(concat); + SASSERT(a_concat->get_num_args() == 2); + expr * a1 = a_concat->get_arg(0); + expr * a2 = a_concat->get_arg(1); + + if (const_str == "") { + TRACE("t_str", tout << "quick path: concat == \"\"" << std::endl;); + // assert the following axiom: + // ( (Concat a1 a2) == str ) -> ( (a1 == "") AND (a2 == "") ) + expr_ref premise(ctx.mk_eq_atom(concat, str), m); + expr_ref empty_str(m_strutil.mk_string(""), m); + expr_ref c1(ctx.mk_eq_atom(a1, empty_str), m); + expr_ref c2(ctx.mk_eq_atom(a2, empty_str), m); + expr_ref conclusion(m.mk_and(c1, c2), m); + expr_ref axiom(m.mk_implies(premise, conclusion), m); + TRACE("t_str_detail", tout << "learn " << mk_ismt2_pp(axiom, m) << std::endl;); + assert_axiom(axiom); + return; + } + // TODO the rest... + } +} + void theory_str::handle_equality(expr * lhs, expr * rhs) { ast_manager & m = get_manager(); context & ctx = get_context(); @@ -319,7 +413,10 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { // TODO simplify concat? - // TODO newEqCheck()? + // newEqCheck() -- check consistency wrt. existing equivalence classes + if (!new_eq_check(lhs, rhs)) { + return; + } // BEGIN new_eq_handler() in strTheory @@ -332,6 +429,94 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { enode * e_rhs = ctx.get_enode(rhs); std::pair eq_pair(e_lhs, e_rhs); m_str_eq_length_axiom_todo.push_back(eq_pair); + + // group terms by equivalence class (groupNodeInEqc()) + std::set eqc_lhs_concat; + std::set eqc_lhs_var; + std::set eqc_lhs_const; + group_terms_by_eqc(lhs, eqc_lhs_concat, eqc_lhs_var, eqc_lhs_const); + + TRACE("t_str_detail", + tout << "eqc[lhs]:" << std::endl; + tout << "Concats:" << std::endl; + for (std::set::iterator it = eqc_lhs_concat.begin(); it != eqc_lhs_concat.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + tout << "Variables:" << std::endl; + for (std::set::iterator it = eqc_lhs_var.begin(); it != eqc_lhs_var.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + tout << "Constants:" << std::endl; + for (std::set::iterator it = eqc_lhs_const.begin(); it != eqc_lhs_const.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + ); + + std::set eqc_rhs_concat; + std::set eqc_rhs_var; + std::set eqc_rhs_const; + group_terms_by_eqc(rhs, eqc_rhs_concat, eqc_rhs_var, eqc_rhs_const); + + TRACE("t_str_detail", + tout << "eqc[rhs]:" << std::endl; + tout << "Concats:" << std::endl; + for (std::set::iterator it = eqc_rhs_concat.begin(); it != eqc_rhs_concat.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + tout << "Variables:" << std::endl; + for (std::set::iterator it = eqc_rhs_var.begin(); it != eqc_rhs_var.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + tout << "Constants:" << std::endl; + for (std::set::iterator it = eqc_rhs_const.begin(); it != eqc_rhs_const.end(); ++it) { + expr * ex = *it; + tout << mk_ismt2_pp(ex, get_manager()) << std::endl; + } + ); + + // step 1: Concat == Concat + bool hasCommon = false; + if (eqc_lhs_concat.size() != 0 && eqc_rhs_concat.size() != 0) { + std::set::iterator itor1 = eqc_lhs_concat.begin(); + std::set::iterator itor2 = eqc_rhs_concat.begin(); + for (; itor1 != eqc_lhs_concat.end(); ++itor1) { + if (eqc_rhs_concat.find(*itor1) != eqc_rhs_concat.end()) { + hasCommon = true; + break; + } + } + for (; !hasCommon && itor2 != eqc_rhs_concat.end(); ++itor2) { + if (eqc_lhs_concat.find(*itor2) != eqc_lhs_concat.end()) { + hasCommon = true; + break; + } + } + if (!hasCommon) { + simplify_concat_equality(*(eqc_lhs_concat.begin()), *(eqc_rhs_concat.begin())); + } + } + + // step 2: Concat == Constant + if (eqc_lhs_const.size() != 0) { + expr * conStr = *(eqc_lhs_const.begin()); + std::set::iterator itor2 = eqc_rhs_concat.begin(); + for (; itor2 != eqc_rhs_concat.end(); ++itor2) { + solve_concat_eq_str(*itor2, conStr); + } + } else if (eqc_rhs_const.size() != 0) { + expr * conStr = *(eqc_rhs_const.begin()); + std::set::iterator itor1 = eqc_lhs_concat.begin(); + for (; itor1 != eqc_lhs_concat.end(); ++itor1) { + solve_concat_eq_str(*itor1, conStr); + } + } + + // TODO regex unroll? (much later) } void theory_str::set_up_axioms(expr * ex) { @@ -368,6 +553,9 @@ void theory_str::init_search_eh() { ast_manager & m = get_manager(); context & ctx = get_context(); + // TODO it would be better to refactor this function so that instead of deferring the axioms + // instead we defer the evaluation of the expression + TRACE("t_str_detail", tout << "dumping all asserted formulas:" << std::endl; unsigned nFormulas = ctx.get_num_asserted_formulas(); @@ -410,11 +598,11 @@ void theory_str::init_search_eh() { } } + TRACE("t_str", tout << "search started" << std::endl;); search_started = true; } void theory_str::new_eq_eh(theory_var x, theory_var y) { - // TODO TRACE("t_str", tout << "new eq: v#" << x << " = v#" << y << std::endl;); TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); @@ -422,7 +610,6 @@ void theory_str::new_eq_eh(theory_var x, theory_var y) { } void theory_str::new_diseq_eh(theory_var x, theory_var y) { - // TODO TRACE("t_str", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); @@ -442,8 +629,21 @@ void theory_str::push_scope_eh() { } final_check_status theory_str::final_check_eh() { + ast_manager & m = get_manager(); + context & ctx = get_context(); // TODO TRACE("t_str", tout << "final check" << std::endl;); + + TRACE("t_str_detail", + tout << "dumping all assignments:" << std::endl; + expr_ref_vector assignments(m); + ctx.get_assignments(assignments); + for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { + expr * ex = *i; + tout << mk_ismt2_pp(ex, m) << std::endl; + } + ); + return FC_DONE; } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index b9c11c2f0..286de818a 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -23,6 +23,7 @@ Revision History: #include"value_factory.h" #include"smt_model_generator.h" #include"arith_decl_plugin.h" +#include namespace smt { @@ -46,12 +47,20 @@ namespace smt { bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } + bool is_string(app const * a) const { return a->is_app_of(get_id(), OP_STR); } + bool is_string(enode const * n) const { return is_string(n->get_owner()); } void instantiate_concat_axiom(enode * cat); void instantiate_basic_string_axioms(enode * str); void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs); void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); + + void simplify_concat_equality(expr * lhs, expr * rhs); + void solve_concat_eq_str(expr * concat, expr * str); + + bool new_eq_check(expr * lhs, expr * rhs); + void group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts); public: theory_str(ast_manager & m); virtual ~theory_str(); From 02cb329ca5b980c02b825bb54dc163149b3f75dc Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 23:24:41 -0400 Subject: [PATCH 21/91] defer equalities uncovered during init_search --- src/smt/theory_str.cpp | 34 +++++++++++++++------------------- src/smt/theory_str.h | 2 +- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index faaa7fb70..bba37f5f0 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -118,7 +118,7 @@ app * theory_str::mk_strlen(app * e) { } bool theory_str::can_propagate() { - return !m_basicstr_axiom_todo.empty() || !m_str_eq_length_axiom_todo.empty(); + return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty(); } void theory_str::propagate() { @@ -129,13 +129,13 @@ void theory_str::propagate() { } m_basicstr_axiom_todo.reset(); - for (unsigned i = 0; i < m_str_eq_length_axiom_todo.size(); ++i) { - std::pair pair = m_str_eq_length_axiom_todo[i]; + for (unsigned i = 0; i < m_str_eq_todo.size(); ++i) { + std::pair pair = m_str_eq_todo[i]; enode * lhs = pair.first; enode * rhs = pair.second; - instantiate_str_eq_length_axiom(lhs, rhs); + handle_equality(lhs->get_owner(), rhs->get_owner()); } - m_str_eq_length_axiom_todo.reset(); + m_str_eq_todo.reset(); } TRACE("t_str_detail", tout << "done propagating" << std::endl;); } @@ -299,7 +299,7 @@ void theory_str::attach_new_th_var(enode * n) { void theory_str::reset_eh() { TRACE("t_str", tout << "resetting" << std::endl;); m_basicstr_axiom_todo.reset(); - m_str_eq_length_axiom_todo.reset(); + m_str_eq_todo.reset(); pop_scope_eh(0); } @@ -382,10 +382,10 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { TRACE("t_str", tout << "quick path: concat == \"\"" << std::endl;); // assert the following axiom: // ( (Concat a1 a2) == str ) -> ( (a1 == "") AND (a2 == "") ) - expr_ref premise(ctx.mk_eq_atom(concat, str), m); + expr_ref premise(m.mk_eq(concat, str), m); expr_ref empty_str(m_strutil.mk_string(""), m); - expr_ref c1(ctx.mk_eq_atom(a1, empty_str), m); - expr_ref c2(ctx.mk_eq_atom(a2, empty_str), m); + expr_ref c1(m.mk_eq(a1, empty_str), m); + expr_ref c2(m.mk_eq(a2, empty_str), m); expr_ref conclusion(m.mk_and(c1, c2), m); expr_ref axiom(m.mk_implies(premise, conclusion), m); TRACE("t_str_detail", tout << "learn " << mk_ismt2_pp(axiom, m) << std::endl;); @@ -423,12 +423,7 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { // TODO there's some setup with getLenValue() that I don't think is necessary // because we should already be generating the string length axioms for all string terms - // set up string length axiom: - // (lhs == rhs) -> (Length(lhs) == Length(rhs)) - enode * e_lhs = ctx.get_enode(lhs); - enode * e_rhs = ctx.get_enode(rhs); - std::pair eq_pair(e_lhs, e_rhs); - m_str_eq_length_axiom_todo.push_back(eq_pair); + instantiate_str_eq_length_axiom(ctx.get_enode(lhs), ctx.get_enode(rhs)); // group terms by equivalence class (groupNodeInEqc()) std::set eqc_lhs_concat; @@ -553,9 +548,6 @@ void theory_str::init_search_eh() { ast_manager & m = get_manager(); context & ctx = get_context(); - // TODO it would be better to refactor this function so that instead of deferring the axioms - // instead we defer the evaluation of the expression - TRACE("t_str_detail", tout << "dumping all asserted formulas:" << std::endl; unsigned nFormulas = ctx.get_num_asserted_formulas(); @@ -592,7 +584,11 @@ void theory_str::init_search_eh() { SASSERT(eq->get_num_args() == 2); expr * lhs = eq->get_arg(0); expr * rhs = eq->get_arg(1); - handle_equality(lhs, rhs); + + enode * e_lhs = ctx.get_enode(lhs); + enode * e_rhs = ctx.get_enode(rhs); + std::pair eq_pair(e_lhs, e_rhs); + m_str_eq_todo.push_back(eq_pair); } else { TRACE("t_str_detail", tout << "expr ignored" << std::endl;); } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 286de818a..76bef4561 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -39,7 +39,7 @@ namespace smt { str_util m_strutil; ptr_vector m_basicstr_axiom_todo; - svector > m_str_eq_length_axiom_todo; + svector > m_str_eq_todo; protected: void assert_axiom(ast * e); From 0d54e4e4ae99933b4330fba9df9224f9486ee361 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 27 Sep 2015 23:57:41 -0400 Subject: [PATCH 22/91] implement str_decl_plugin::is_value() and ::is_unique_value() we can now prove that (= "abc" "def") is unsatisfiable --- src/ast/str_decl_plugin.cpp | 12 ++++++++++++ src/ast/str_decl_plugin.h | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 1502e3d3a..60db88b63 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -115,6 +115,18 @@ void str_decl_plugin::get_sort_names(svector & sort_names, symbol sort_names.push_back(builtin_name("String", STRING_SORT)); } +bool str_decl_plugin::is_value(app * e) const { + if (e->get_family_id() != m_family_id) { + return false; + } + switch (e->get_decl_kind()) { + case OP_STR: + return true; + default: + return false; + } +} + bool str_recognizers::is_string(expr const * n, const char ** val) const { if (!is_app_of(n, m_afid, OP_STR)) return false; diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 7e75fbaf0..a64e0c05f 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -60,8 +60,10 @@ public: app * mk_string(const char * val); virtual void get_op_names(svector & op_names, symbol const & logic); - virtual void get_sort_names(svector & sort_names, symbol const & logic); + + virtual bool is_value(app * e) const; + virtual bool is_unique_value(app * e) const { return is_value(e); } // TODO }; From 7da3854a8b488188641b4b8c2d691e5d59df1df8 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 01:56:13 -0400 Subject: [PATCH 23/91] really lousy model-building, WIP --- src/smt/theory_str.cpp | 46 ++++++++++++++++++++++++++++-------------- src/smt/theory_str.h | 23 ++++++++++++++++++++- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index bba37f5f0..aaae3e373 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -35,18 +35,9 @@ theory_str::~theory_str() { } void theory_str::assert_axiom(ast * a) { - /* - if (search_started) { - // effectively Z3_theory_assert_axiom - NOT_IMPLEMENTED_YET(); - } else { - // effectively Z3_assert_cnstr - context & ctx = get_context(); - ctx.assert_expr(to_expr(a)); - } - */ - TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); expr * e = to_expr(a); + if (get_manager().is_true(e)) return; + TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); context & ctx = get_context(); ctx.internalize(e, false); literal lit(ctx.get_literal(e)); @@ -381,11 +372,11 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { if (const_str == "") { TRACE("t_str", tout << "quick path: concat == \"\"" << std::endl;); // assert the following axiom: - // ( (Concat a1 a2) == str ) -> ( (a1 == "") AND (a2 == "") ) - expr_ref premise(m.mk_eq(concat, str), m); + // ( (Concat a1 a2) == "" ) -> ( (a1 == "") AND (a2 == "") ) expr_ref empty_str(m_strutil.mk_string(""), m); - expr_ref c1(m.mk_eq(a1, empty_str), m); - expr_ref c2(m.mk_eq(a2, empty_str), m); + expr_ref premise(ctx.mk_eq_atom(concat, empty_str), m); + expr_ref c1(ctx.mk_eq_atom(a1, empty_str), m); + expr_ref c2(ctx.mk_eq_atom(a2, empty_str), m); expr_ref conclusion(m.mk_and(c1, c2), m); expr_ref axiom(m.mk_implies(premise, conclusion), m); TRACE("t_str_detail", tout << "learn " << mk_ismt2_pp(axiom, m) << std::endl;); @@ -643,4 +634,29 @@ final_check_status theory_str::final_check_eh() { return FC_DONE; } +void theory_str::init_model(model_generator & mg) { + TRACE("t_str", tout << "initializing model" << std::endl; display(tout);); + m_factory = alloc(str_value_factory, get_manager(), get_family_id()); + mg.register_factory(m_factory); +} + +model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { + TRACE("t_str", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << + " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")\n";); + ast_manager & m = get_manager(); + context & ctx = get_context(); + app_ref owner(m); + owner = n->get_owner(); + + // If the owner is not internalized, it doesn't have an enode associated. + SASSERT(ctx.e_internalized(owner)); + + if (m_strutil.is_string(owner)) { + return alloc(expr_wrapper_proc, owner); + } + NOT_IMPLEMENTED_YET(); // TODO +} + +void theory_str::finalize_model(model_generator & mg) {} + }; /* namespace smt */ diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 76bef4561..65a401580 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -28,7 +28,22 @@ Revision History: namespace smt { class str_value_factory : public value_factory { - // TODO + str_util m_util; + public: + str_value_factory(ast_manager & m, family_id fid) : + value_factory(m, fid), + m_util(m) {} + virtual ~str_value_factory() {} + virtual expr * get_some_value(sort * s) { + return m_util.mk_string("some value"); + } + virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { + v1 = m_util.mk_string("value 1"); + v2 = m_util.mk_string("value 2"); + return true; + } + virtual expr * get_fresh_value(sort * s) { NOT_IMPLEMENTED_YET(); } + virtual void register_value(expr * n) { /* Ignore */ } }; class theory_str : public theory { @@ -38,6 +53,8 @@ namespace smt { arith_util m_autil; str_util m_strutil; + str_value_factory * m_factory; + ptr_vector m_basicstr_axiom_todo; svector > m_str_eq_todo; protected: @@ -83,6 +100,10 @@ namespace smt { virtual final_check_status final_check_eh(); void attach_new_th_var(enode * n); + + virtual void init_model(model_generator & m); + virtual model_value_proc * mk_value(enode * n, model_generator & mg); + virtual void finalize_model(model_generator & mg); }; }; From 87b5765e3d3ef04aface95303666ed3f4daf7026 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 02:04:35 -0400 Subject: [PATCH 24/91] clean up traces and make them much easier to read --- src/smt/theory_str.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index aaae3e373..467d94c5f 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -128,7 +128,6 @@ void theory_str::propagate() { } m_str_eq_todo.reset(); } - TRACE("t_str_detail", tout << "done propagating" << std::endl;); } /* @@ -507,8 +506,6 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { void theory_str::set_up_axioms(expr * ex) { // TODO check to make sure we don't set up axioms on the same term twice - TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << std::endl;); - ast_manager & m = get_manager(); context & ctx = get_context(); @@ -516,13 +513,15 @@ void theory_str::set_up_axioms(expr * ex) { sort * str_sort = m.mk_sort(get_family_id(), STRING_SORT); if (ex_sort == str_sort) { - TRACE("t_str_detail", tout << "expr is of sort String" << std::endl;); + TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << + ": expr is of sort String" << std::endl;); // set up basic string axioms enode * n = ctx.get_enode(ex); SASSERT(n); m_basicstr_axiom_todo.push_back(n); } else { - TRACE("t_str_detail", tout << "expr is of wrong sort, ignoring" << std::endl;); + TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << + ": expr is of wrong sort, ignoring" << std::endl;); } // if expr is an application, recursively inspect all arguments @@ -568,9 +567,9 @@ void theory_str::init_search_eh() { ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { expr * ex = *i; - TRACE("t_str_detail", tout << "processing assignment " << mk_ismt2_pp(ex, m) << std::endl;); if (m.is_eq(ex)) { - TRACE("t_str_detail", tout << "expr is equality" << std::endl;); + TRACE("t_str_detail", tout << "processing assignment " << mk_ismt2_pp(ex, m) << + ": expr is equality" << std::endl;); app * eq = (app*)ex; SASSERT(eq->get_num_args() == 2); expr * lhs = eq->get_arg(0); @@ -581,7 +580,8 @@ void theory_str::init_search_eh() { std::pair eq_pair(e_lhs, e_rhs); m_str_eq_todo.push_back(eq_pair); } else { - TRACE("t_str_detail", tout << "expr ignored" << std::endl;); + TRACE("t_str_detail", tout << "processing assignment " << mk_ismt2_pp(ex, m) + << ": expr ignored" << std::endl;); } } @@ -590,15 +590,15 @@ void theory_str::init_search_eh() { } void theory_str::new_eq_eh(theory_var x, theory_var y) { - TRACE("t_str", tout << "new eq: v#" << x << " = v#" << y << std::endl;); - TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << + //TRACE("t_str_detail", tout << "new eq: v#" << x << " = v#" << y << std::endl;); + TRACE("t_str", tout << "new eq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { - TRACE("t_str", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); - TRACE("t_str_detail", tout << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << + //TRACE("t_str_detail", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); + TRACE("t_str", tout << "new diseq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); } From 5fe129b5716aabca5313adfb0f5e77f3b3ea3fc9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 02:09:35 -0400 Subject: [PATCH 25/91] use mk_ismt2_pp() instead of mk_bounded_pp() --- src/smt/theory_str.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 467d94c5f..b3780265d 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -171,7 +171,7 @@ void theory_str::instantiate_concat_axiom(enode * cat) { // finally assert equality between the two subexpressions app * eq = m.mk_eq(len_xy, len_x_plus_len_y); SASSERT(eq); - TRACE("t_str", tout << mk_bounded_pp(eq, m) << std::endl;); + TRACE("t_str", tout << mk_ismt2_pp(eq, m) << std::endl;); assert_axiom(eq); } @@ -219,7 +219,7 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { app * lhs_ge_rhs = m_autil.mk_ge(len_str, zero); SASSERT(lhs_ge_rhs); // TODO verify that this works - TRACE("t_str_detail", tout << "string axiom 1: " << mk_bounded_pp(lhs_ge_rhs, m) << std::endl;); + TRACE("t_str_detail", tout << "string axiom 1: " << mk_ismt2_pp(lhs_ge_rhs, m) << std::endl;); assert_axiom(lhs_ge_rhs); } @@ -243,7 +243,7 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { rhs = ctx.mk_eq_atom(a_str, empty_str); SASSERT(rhs); // build LHS <=> RHS and assert - TRACE("t_str_detail", tout << "string axiom 2: " << mk_bounded_pp(lhs, m) << " <=> " << mk_bounded_pp(rhs, m) << std::endl;); + TRACE("t_str_detail", tout << "string axiom 2: " << mk_ismt2_pp(lhs, m) << " <=> " << mk_ismt2_pp(rhs, m) << std::endl;); literal l(mk_eq(lhs, rhs, true)); ctx.mark_as_relevant(l); ctx.mk_th_axiom(get_id(), 1, &l); @@ -275,7 +275,7 @@ void theory_str::instantiate_str_eq_length_axiom(enode * lhs, enode * rhs) { // build (premise -> conclusion) and assert expr_ref axiom(m.mk_implies(premise, conclusion), m); - TRACE("t_str_detail", tout << "string-eq length-eq axiom: " << mk_bounded_pp(axiom, m) << std::endl;); + TRACE("t_str_detail", tout << "string-eq length-eq axiom: " << mk_ismt2_pp(axiom, m) << std::endl;); assert_axiom(axiom); } From bccadedfee53f5d1cd097300044bfc39c1a5bdac Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 03:20:13 -0400 Subject: [PATCH 26/91] instead of building axiom (=> x y), build (or (not x) y) this may be a bug in Z3 as it suggests that implications are ignored e.g. I can assert the axiom (=> true false) and Z3 is okay with this --- src/smt/theory_str.cpp | 23 +++++++++++++++-------- src/smt/theory_str.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index b3780265d..6d2284d67 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -39,7 +39,9 @@ void theory_str::assert_axiom(ast * a) { if (get_manager().is_true(e)) return; TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); context & ctx = get_context(); - ctx.internalize(e, false); + if (!ctx.b_internalized(e)) { + ctx.internalize(e, true); + } literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); @@ -372,14 +374,15 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { TRACE("t_str", tout << "quick path: concat == \"\"" << std::endl;); // assert the following axiom: // ( (Concat a1 a2) == "" ) -> ( (a1 == "") AND (a2 == "") ) - expr_ref empty_str(m_strutil.mk_string(""), m); - expr_ref premise(ctx.mk_eq_atom(concat, empty_str), m); - expr_ref c1(ctx.mk_eq_atom(a1, empty_str), m); - expr_ref c2(ctx.mk_eq_atom(a2, empty_str), m); + + + expr_ref premise(ctx.mk_eq_atom(concat, str), m); + expr_ref c1(ctx.mk_eq_atom(a1, str), m); + expr_ref c2(ctx.mk_eq_atom(a2, str), m); expr_ref conclusion(m.mk_and(c1, c2), m); - expr_ref axiom(m.mk_implies(premise, conclusion), m); - TRACE("t_str_detail", tout << "learn " << mk_ismt2_pp(axiom, m) << std::endl;); + expr_ref axiom(m.mk_or(m.mk_not(premise), conclusion), m); assert_axiom(axiom); + return; } // TODO the rest... @@ -561,7 +564,7 @@ void theory_str::init_search_eh() { /* * Similar recursive descent, except over all initially assigned terms. * This is done to find equalities between terms, etc. that we otherwise - * wouldn't get a chance to see. + * might not get a chance to see. */ expr_ref_vector assignments(m); ctx.get_assignments(assignments); @@ -615,6 +618,10 @@ void theory_str::push_scope_eh() { TRACE("t_str", tout << "push" << std::endl;); } +void theory_str::pop_scope_eh(unsigned num_scopes) { + TRACE("t_str", tout << "pop " << num_scopes << std::endl;); +} + final_check_status theory_str::final_check_eh() { ast_manager & m = get_manager(); context & ctx = get_context(); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 65a401580..afac8b7f1 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -93,6 +93,7 @@ namespace smt { virtual void relevant_eh(app * n); virtual void assign_eh(bool_var v, bool is_true); virtual void push_scope_eh(); + virtual void pop_scope_eh(unsigned num_scopes); virtual void reset_eh(); virtual bool can_propagate(); From 62cd633b63b3452f913faaec383676900ec91052 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 03:26:46 -0400 Subject: [PATCH 27/91] create helper function theory_str::assert_implication() --- src/smt/theory_str.cpp | 23 +++++++++++++---------- src/smt/theory_str.h | 3 ++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 6d2284d67..3ef2f06e5 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -34,10 +34,9 @@ theory_str::theory_str(ast_manager & m): theory_str::~theory_str() { } -void theory_str::assert_axiom(ast * a) { - expr * e = to_expr(a); +void theory_str::assert_axiom(expr * e) { if (get_manager().is_true(e)) return; - TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); + TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); context & ctx = get_context(); if (!ctx.b_internalized(e)) { ctx.internalize(e, true); @@ -45,7 +44,13 @@ void theory_str::assert_axiom(ast * a) { literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); - TRACE("t_str_detail", tout << "done asserting " << mk_ismt2_pp(a, get_manager()) << "\n";); + TRACE("t_str_detail", tout << "done asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); +} + +void theory_str::assert_implication(expr * premise, expr * conclusion) { + ast_manager & m = get_manager(); + expr_ref axiom(m.mk_or(m.mk_not(premise), conclusion), m); + assert_axiom(axiom); } bool theory_str::internalize_atom(app * atom, bool gate_ctx) { @@ -275,10 +280,9 @@ void theory_str::instantiate_str_eq_length_axiom(enode * lhs, enode * rhs) { SASSERT(len_rhs); expr_ref conclusion(ctx.mk_eq_atom(len_lhs, len_rhs), m); - // build (premise -> conclusion) and assert - expr_ref axiom(m.mk_implies(premise, conclusion), m); - TRACE("t_str_detail", tout << "string-eq length-eq axiom: " << mk_ismt2_pp(axiom, m) << std::endl;); - assert_axiom(axiom); + TRACE("t_str_detail", tout << "string-eq length-eq axiom: " + << mk_ismt2_pp(premise, m) << " -> " << mk_ismt2_pp(conclusion, m) << std::endl;); + assert_implication(premise, conclusion); } void theory_str::attach_new_th_var(enode * n) { @@ -380,8 +384,7 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { expr_ref c1(ctx.mk_eq_atom(a1, str), m); expr_ref c2(ctx.mk_eq_atom(a2, str), m); expr_ref conclusion(m.mk_and(c1, c2), m); - expr_ref axiom(m.mk_or(m.mk_not(premise), conclusion), m); - assert_axiom(axiom); + assert_implication(premise, conclusion); return; } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index afac8b7f1..c7a4a5952 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -58,7 +58,8 @@ namespace smt { ptr_vector m_basicstr_axiom_todo; svector > m_str_eq_todo; protected: - void assert_axiom(ast * e); + void assert_axiom(expr * e); + void assert_implication(expr * premise, expr * conclusion); app * mk_strlen(app * e); From 9bc685b21d86ffd815300dd77a4a996a73be71de Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 10:43:34 -0400 Subject: [PATCH 28/91] solve_concat_eq_str() for concat(const,const) == const --- src/smt/theory_str.cpp | 107 ++++++++++++++++++++++++++++++++++++++--- src/smt/theory_str.h | 3 ++ 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3ef2f06e5..f3e6496b7 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -54,11 +54,9 @@ void theory_str::assert_implication(expr * premise, expr * conclusion) { } bool theory_str::internalize_atom(app * atom, bool gate_ctx) { - // TODO I have no idea if this is correct. TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << std::endl;); SASSERT(atom->get_family_id() == get_family_id()); - ast_manager & m = get_manager(); context & ctx = get_context(); if (ctx.b_internalized(atom)) @@ -75,8 +73,6 @@ bool theory_str::internalize_atom(app * atom, bool gate_ctx) { } bool theory_str::internalize_term(app * term) { - // TODO I have no idea if this is correct either. - ast_manager & m = get_manager(); context & ctx = get_context(); TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); SASSERT(term->get_family_id() == get_family_id()); @@ -115,6 +111,16 @@ app * theory_str::mk_strlen(app * e) { } } +app * theory_str::mk_concat(app * e1, app * e2) { + ast_manager & m = get_manager(); + if (e1 == NULL || e2 == NULL) { + m.raise_exception("strings to be concatenated cannot be NULL"); + } + // TODO there's a *TON* of missing code here from strTheory::mk_concat() + expr * args[2] = {e1, e2}; + return get_manager().mk_app(get_id(), OP_STRCAT, 0, 0, 2, args); +} + bool theory_str::can_propagate() { return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty(); } @@ -225,7 +231,6 @@ void theory_str::instantiate_basic_string_axioms(enode * str) { // build LHS >= RHS and assert app * lhs_ge_rhs = m_autil.mk_ge(len_str, zero); SASSERT(lhs_ge_rhs); - // TODO verify that this works TRACE("t_str_detail", tout << "string axiom 1: " << mk_ismt2_pp(lhs_ge_rhs, m) << std::endl;); assert_axiom(lhs_ge_rhs); } @@ -315,7 +320,6 @@ bool theory_str::new_eq_check(expr * lhs, expr * rhs) { } void theory_str::group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts) { - ast_manager & m = get_manager(); context & ctx = get_context(); enode * nNode = ctx.get_enode(n); enode * eqcNode = nNode; @@ -352,6 +356,27 @@ void theory_str::group_terms_by_eqc(expr * n, std::set & concats, std::se void theory_str::simplify_concat_equality(expr * lhs, expr * rhs) { // TODO strArgmt::simplifyConcatEq() } +/* + * Look through the equivalence class of n to find a string constant. + * Return that constant if it is found, and set hasEqcValue to true. + * Otherwise, return n, and set hasEqcValue to false. + */ +expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { + context & ctx = get_context(); + enode * nNode = ctx.get_enode(n); + enode * eqcNode = nNode; + do { + app * ast = eqcNode->get_owner(); + if (is_string(eqcNode)) { + hasEqcValue = true; + return ast; + } + eqcNode = eqcNode->get_next(); + } while (eqcNode != nNode); + // not found + hasEqcValue = false; + return n; +} /* * strArgmt::solve_concat_eq_str() @@ -388,7 +413,75 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { return; } - // TODO the rest... + bool arg1_has_eqc_value = false; + bool arg2_has_eqc_value = false; + expr * arg1 = get_eqc_value(a1, arg1_has_eqc_value); + expr * arg2 = get_eqc_value(a2, arg2_has_eqc_value); + expr_ref newConcat(m); + if (arg1 != a1 || arg2 != a2) { + TRACE("t_str", tout << "resolved concat argument(s) to eqc string constants" << std::endl;); + int iPos = 0; + app * item1[2]; + if (a1 != arg1) { + item1[iPos++] = ctx.mk_eq_atom(a1, arg1); + } + if (a2 != arg2) { + item1[iPos++] = ctx.mk_eq_atom(a2, arg2); + } + expr_ref implyL1(m); + if (iPos == 1) { + implyL1 = item1[0]; + } else { + implyL1 = m.mk_and(item1[0], item1[1]); + } + newConcat = mk_concat(to_app(arg1), to_app(arg2)); + if (newConcat != str) { + expr_ref implyR1(ctx.mk_eq_atom(concat, newConcat), m); + assert_implication(implyL1, implyR1); + } + } else { + newConcat = concat; + } + if (newConcat == str) { + return; + } + if (!is_concat(to_app(newConcat))) { + return; + } + if (arg1_has_eqc_value && arg2_has_eqc_value) { + // Case 1: Concat(const, const) == const + TRACE("t_str", tout << "Case 1: Concat(const, const) == const" << std::endl;); + const char * str1; + m_strutil.is_string(arg1, & str1); + std::string arg1_str(str1); + + const char * str2; + m_strutil.is_string(arg2, & str2); + std::string arg2_str(str2); + + std::string result_str = arg1_str + arg2_str; + if (result_str != const_str) { + // Inconsistency + TRACE("t_str", tout << "inconsistency detected: \"" + << arg1_str << "\" + \"" << arg2_str << + "\" != \"" << const_str << "\"" << std::endl;); + expr_ref equality(ctx.mk_eq_atom(concat, str), m); + expr_ref diseq(m.mk_not(equality), m); + assert_axiom(diseq); + } + } else if (!arg1_has_eqc_value && arg2_has_eqc_value) { + // Case 2: Concat(var, const) == const + TRACE("t_str", tout << "Case 2: Concat(var, const) == const" << std::endl;); + NOT_IMPLEMENTED_YET(); + } else if (arg1_has_eqc_value && !arg2_has_eqc_value) { + // Case 3: Concat(const, var) == const + TRACE("t_str", tout << "Case 3: Concat(const, var) == const" << std::endl;); + NOT_IMPLEMENTED_YET(); + } else { + // Case 4: Concat(var, var) == const + TRACE("t_str", tout << "Case 4: Concat(var, var) == const" << std::endl;); + NOT_IMPLEMENTED_YET(); + } } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index c7a4a5952..4839b417b 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -62,6 +62,7 @@ namespace smt { void assert_implication(expr * premise, expr * conclusion); app * mk_strlen(app * e); + app * mk_concat(app * e1, app * e2); bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } @@ -74,6 +75,8 @@ namespace smt { void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); + expr * get_eqc_value(expr * n, bool & hasEqcValue); + void simplify_concat_equality(expr * lhs, expr * rhs); void solve_concat_eq_str(expr * concat, expr * str); From 876af399e394c1974019c897d081df76160e2177 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 14:44:25 -0400 Subject: [PATCH 29/91] probably fix duplication of mk_string() terms also implement Case 2 of solve_concat_eq_str() --- src/ast/str_decl_plugin.cpp | 22 +++++++++++++++++---- src/ast/str_decl_plugin.h | 7 +++++++ src/smt/theory_str.cpp | 39 ++++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 60db88b63..9398dbf34 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -99,11 +99,25 @@ func_decl * str_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, return mk_func_decl(k); } +app * str_decl_plugin::mk_string(std::string & val) { + std::map::iterator it = string_cache.find(val); + if (it == string_cache.end()) { + char * new_buffer = alloc_svect(char, val.length() + 1); + strcpy(new_buffer, val.c_str()); + parameter p[1] = {parameter(new_buffer)}; + func_decl * d; + d = m_manager->mk_const_decl(m_strv_sym, m_str_decl, func_decl_info(m_family_id, OP_STR, 1, p)); + app * str = m_manager->mk_const(d); + string_cache[val] = str; + return str; + } else { + return it->second; + } +} + app * str_decl_plugin::mk_string(const char * val) { - parameter p[1] = {parameter(val)}; - func_decl * d; - d = m_manager->mk_const_decl(m_strv_sym, m_str_decl, func_decl_info(m_family_id, OP_STR, 1, p)); - return m_manager->mk_const(d); + std::string key(val); + return mk_string(key); } void str_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index a64e0c05f..f84c1ec31 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -19,6 +19,7 @@ Revision History: #include"ast.h" #include"arith_decl_plugin.h" +#include enum str_sort_kind { STRING_SORT, @@ -44,6 +45,8 @@ protected: func_decl * m_concat_decl; func_decl * m_length_decl; + std::map string_cache; + virtual void set_manager(ast_manager * m, family_id id); func_decl * mk_func_decl(decl_kind k); @@ -58,6 +61,7 @@ public: unsigned arity, sort * const * domain, sort * range); app * mk_string(const char * val); + app * mk_string(std::string & val); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); @@ -90,6 +94,9 @@ public: app * mk_string(const char * val) { return m_plugin->mk_string(val); } + app * mk_string(std::string & val) { + return m_plugin->mk_string(val); + } // TODO }; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index f3e6496b7..08f83fdd3 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -468,11 +468,48 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { expr_ref equality(ctx.mk_eq_atom(concat, str), m); expr_ref diseq(m.mk_not(equality), m); assert_axiom(diseq); + return; } } else if (!arg1_has_eqc_value && arg2_has_eqc_value) { // Case 2: Concat(var, const) == const TRACE("t_str", tout << "Case 2: Concat(var, const) == const" << std::endl;); - NOT_IMPLEMENTED_YET(); + const char * str2; + m_strutil.is_string(arg2, & str2); + std::string arg2_str(str2); + int resultStrLen = const_str.length(); + int arg2StrLen = arg2_str.length(); + if (resultStrLen < arg2StrLen) { + // Inconsistency + TRACE("t_str", tout << "inconsistency detected: \"" + << arg2_str << + "\" is longer than \"" << const_str << "\"," + << " so cannot be concatenated with anything to form it" << std::endl;); + expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); + expr_ref diseq(m.mk_not(equality), m); + assert_axiom(diseq); + return; + } else { + int varStrLen = resultStrLen - arg2StrLen; + std::string firstPart = const_str.substr(0, varStrLen); + std::string secondPart = const_str.substr(varStrLen, arg2StrLen); + if (arg2_str != secondPart) { + // Inconsistency + TRACE("t_str", tout << "inconsistency detected: " + << "suffix of concatenation result expected \"" << secondPart << "\", " + << "actually \"" << arg2_str << "\"" + << std::endl;); + expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); + expr_ref diseq(m.mk_not(equality), m); + assert_axiom(diseq); + return; + } else { + expr_ref tmpStrConst(m_strutil.mk_string(firstPart), m); + expr_ref premise(ctx.mk_eq_atom(newConcat, str), m); + expr_ref conclusion(ctx.mk_eq_atom(arg1, tmpStrConst), m); + assert_implication(premise, conclusion); + return; + } + } } else if (arg1_has_eqc_value && !arg2_has_eqc_value) { // Case 3: Concat(const, var) == const TRACE("t_str", tout << "Case 3: Concat(const, var) == const" << std::endl;); From 871b08bd8cd42dd35a9ed19a8bc4e2d77fca4155 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 14:52:43 -0400 Subject: [PATCH 30/91] solve_concat_eq_str() case 3 --- src/smt/theory_str.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 08f83fdd3..d6edc2f6b 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -513,7 +513,43 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } else if (arg1_has_eqc_value && !arg2_has_eqc_value) { // Case 3: Concat(const, var) == const TRACE("t_str", tout << "Case 3: Concat(const, var) == const" << std::endl;); - NOT_IMPLEMENTED_YET(); + const char * str1; + m_strutil.is_string(arg1, & str1); + std::string arg1_str(str1); + int resultStrLen = const_str.length(); + int arg1StrLen = arg1_str.length(); + if (resultStrLen < arg1StrLen) { + // Inconsistency + TRACE("t_str", tout << "inconsistency detected: \"" + << arg1_str << + "\" is longer than \"" << const_str << "\"," + << " so cannot be concatenated with anything to form it" << std::endl;); + expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); + expr_ref diseq(m.mk_not(equality), m); + assert_axiom(diseq); + return; + } else { + int varStrLen = resultStrLen - arg1StrLen; + std::string firstPart = const_str.substr(0, arg1StrLen); + std::string secondPart = const_str.substr(arg1StrLen, varStrLen); + if (arg1_str != firstPart) { + // Inconsistency + TRACE("t_str", tout << "inconsistency detected: " + << "prefix of concatenation result expected \"" << secondPart << "\", " + << "actually \"" << arg1_str << "\"" + << std::endl;); + expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); + expr_ref diseq(m.mk_not(equality), m); + assert_axiom(diseq); + return; + } else { + expr_ref tmpStrConst(m_strutil.mk_string(secondPart), m); + expr_ref premise(ctx.mk_eq_atom(newConcat, str), m); + expr_ref conclusion(ctx.mk_eq_atom(arg2, tmpStrConst), m); + assert_implication(premise, conclusion); + return; + } + } } else { // Case 4: Concat(var, var) == const TRACE("t_str", tout << "Case 4: Concat(var, var) == const" << std::endl;); From f473b92d5c20352cb8cafcea0adb9d02fcc87f4a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 28 Sep 2015 17:41:01 -0400 Subject: [PATCH 31/91] solve_concat_eq_str() case 4 WIP --- src/smt/theory_str.cpp | 174 ++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 5 ++ 2 files changed, 177 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index d6edc2f6b..458786110 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -27,7 +27,8 @@ theory_str::theory_str(ast_manager & m): theory(m.mk_family_id("str")), search_started(false), m_autil(m), - m_strutil(m) + m_strutil(m), + tmpXorVarCount(0) { } @@ -99,6 +100,31 @@ bool theory_str::internalize_term(app * term) { return true; } +Z3_ast mk_internal_xor_var(Z3_theory t) { + Z3_context ctx = Z3_theory_get_context(t); + std::stringstream ss; + ss << tmpXorVarCount; + tmpXorVarCount++; + std::string name = "$$_xor_" + ss.str(); + return mk_int_var(ctx, name.c_str()); +} + +app * theory_str::mk_internal_xor_var() { + context & ctx = get_context(); + ast_manager & m = get_manager(); + std::stringstream ss; + ss << tmpXorVarCount; + tmpXorVarCount++; + std::string name = "$$_xor_" + ss.str(); + // Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), INT_SORT)); + sort * int_sort = m.mk_sort(m_autil.get_family_id(), INT_SORT); + symbol sym(name); + + app* a = m.mk_const(m.mk_const_decl(sym, int_sort)); + // TODO ctx.save_ast_trail(a)? + return a; +} + app * theory_str::mk_strlen(app * e) { /*if (m_strutil.is_string(e)) {*/ if (false) { const char * strval = 0; @@ -553,7 +579,151 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } else { // Case 4: Concat(var, var) == const TRACE("t_str", tout << "Case 4: Concat(var, var) == const" << std::endl;); - NOT_IMPLEMENTED_YET(); + // TODO large additions required in this section + if (true) { /* if (Concat(arg1, arg2) == NULL) { */ + int arg1Len = -1; /* = getLenValue(arg1); */ + int arg2Len = -1; /* = getLenValue(arg2); */ + if (arg1Len != -1 || arg2Len != -1) { + NOT_IMPLEMENTED_YET(); // TODO + } else { + /* + Z3_ast xorFlag = NULL; + std::pair key1(arg1, arg2); + std::pair key2(arg2, arg1); + if (varForBreakConcat.find(key1) == varForBreakConcat.end() && varForBreakConcat.find(key2) == varForBreakConcat.end()) { + xorFlag = mk_internal_xor_var(t); + varForBreakConcat[key1][0] = xorFlag; + } else { + if (varForBreakConcat.find(key1) != varForBreakConcat.end()) { + xorFlag = varForBreakConcat[key1][0]; + } else { + xorFlag = varForBreakConcat[key2][0]; + } + } + + int concatStrLen = const_str.length(); + int xor_pos = 0; + int and_count = 1; + Z3_ast * xor_items = new Z3_ast[concatStrLen + 1]; + Z3_ast * and_items = new Z3_ast[4 * (concatStrLen + 1) + 1]; + Z3_ast arg1_eq = NULL; + Z3_ast arg2_eq = NULL; + for (int i = 0; i < concatStrLen + 1; i++) { + std::string prefixStr = const_str.substr(0, i); + std::string suffixStr = const_str.substr(i, concatStrLen - i); + + // skip invalidate options + if (isConcatFunc(t, arg1) && canConcatEqStr(t, arg1, prefixStr) == 0) { + continue; + } + if (isConcatFunc(t, arg2) && canConcatEqStr(t, arg2, suffixStr) == 0) { + continue; + } + + Z3_ast xorAst = Z3_mk_eq(ctx, xorFlag, mk_int(ctx, xor_pos)); + xor_items[xor_pos++] = xorAst; + + Z3_ast prefixAst = my_mk_str_value(t, prefixStr.c_str()); + arg1_eq = Z3_mk_eq(ctx, arg1, prefixAst); + and_items[and_count++] = Z3_mk_eq(ctx, xorAst, arg1_eq); + + Z3_ast suffixAst = my_mk_str_value(t, suffixStr.c_str()); + arg2_eq = Z3_mk_eq(ctx, arg2, suffixAst); + and_items[and_count++] = Z3_mk_eq(ctx, xorAst, arg2_eq); + } + */ + expr_ref xorFlag(m); + std::pair key1(arg1, arg2); + std::pair key2(arg2, arg1); + std::map, std::map >::iterator varBreak_key1 = + varForBreakConcat.find(key1); + std::map, std::map >::iterator varBreak_key2 = + varForBreakConcat.find(key2); + if (varBreak_key1 == varForBreakConcat.end() && varBreak_key2 == varForBreakConcat.end()) { + xorFlag = mk_internal_xor_var(); + varForBreakConcat[key1][0] = xorFlag; + } else if (varBreak_key1 != varForBreakConcat.end()) { + xorFlag = varForBreakConcat[key1][0]; + } else { // varBreak_key2 != varForBreakConcat.end() + xorFlag = varForBreakConcat[key2][0]; + } + + int concatStrLen = const_str.length(); + int xor_pos = 0; + int and_count = 1; + expr * xor_items[] = new expr*[concatStrLen + 1]; + expr * and_items[] = new expr*[4 * (concatStrLen+1) + 1]; + + expr_ref arg1_eq(m); + expr_ref arg2_eq(m); + + for (int i = 0; i < concatStrLen + 1; ++i) { + std::string prefixStr = const_str.substr(0, i); + std::string suffixStr = const_str.substr(i, concatStrLen - i); + // skip invalid options + // TODO canConcatEqStr() checks: + /* + if (isConcatFunc(t, arg1) && canConcatEqStr(t, arg1, prefixStr) == 0) { + continue; + } + if (isConcatFunc(t, arg2) && canConcatEqStr(t, arg2, suffixStr) == 0) { + continue; + } + */ + expr_ref xorAst(ctx.mk_eq_atom(xorFlag, mk_int(xor_pos)), m); + xor_items[xor_pos++] = xorAst; + + expr_ref prefixAst(m_strutil.mk_string(prefixStr), m); + arg1_eq = ctx.mk_eq_atom(arg1, prefixAst); + and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg1_eq); + + expr_ref suffixAst(m_strutil.mk_string(prefixStr), m); + arg2_eq = ctx.mk_eq_atom(arg2, suffixAst); + and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg2_eq); + } + + expr_ref implyL(ctx.mk_eq_atom(concat, str), m); + expr_ref implyR(m); + if (xor_pos == 0) { + // negate + expr_ref concat_eq_str(ctx.mk_eq_atom(concat, str), m); + expr_ref negate_ast(m.mk_not(concat_eq_str), m); + assert_axiom(negate_ast); + } else { + // TODO + if (xor_pos == 1) { + + } else { + + } + } + delete[] xor_items; + delete[] and_items; + + /* + + Z3_ast implyL = Z3_mk_eq(ctx, concatAst, constStr); + Z3_ast implyR1 = NULL; + if (xor_pos == 0) { + // negate + Z3_ast negateAst = Z3_mk_not(ctx, Z3_mk_eq(ctx, concatAst, constStr)); + addAxiom(t, negateAst, __LINE__); + } else { + if (xor_pos == 1) { + and_items[0] = xor_items[0]; + implyR1 = Z3_mk_and(ctx, and_count, and_items); + } else { + and_items[0] = Z3_mk_or(ctx, xor_pos, xor_items); + implyR1 = Z3_mk_and(ctx, and_count, and_items); + } + Z3_ast implyToAssert = Z3_mk_implies(ctx, implyL, implyR1); + addAxiom(t, implyToAssert, __LINE__); + } + delete[] xor_items; + delete[] and_items; + */ + } + } } } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 4839b417b..ea6ec8551 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -57,6 +57,9 @@ namespace smt { ptr_vector m_basicstr_axiom_todo; svector > m_str_eq_todo; + + int tmpXorVarCount; + std::map, std::map > varForBreakConcat; protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); @@ -64,6 +67,8 @@ namespace smt { app * mk_strlen(app * e); app * mk_concat(app * e1, app * e2); + app * mk_internal_xor_var(); + bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } bool is_string(app const * a) const { return a->is_app_of(get_id(), OP_STR); } From 2320b6dc48106a2ccbb8748781b7820d62304e5e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 29 Sep 2015 17:46:51 -0400 Subject: [PATCH 32/91] solve_concat_eq_str() case 4: somewhat working something's wrong but it may be very simple to fix --- src/ast/str_decl_plugin.h | 8 +-- src/smt/theory_str.cpp | 120 +++++++------------------------------- 2 files changed, 26 insertions(+), 102 deletions(-) diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index f84c1ec31..61d1bc2f2 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -38,13 +38,13 @@ protected: symbol m_strv_sym; sort * m_str_decl; - arith_decl_plugin * m_arith_plugin; - sort * m_int_sort; - family_id m_arith_fid; - func_decl * m_concat_decl; func_decl * m_length_decl; + arith_decl_plugin * m_arith_plugin; + family_id m_arith_fid; + sort * m_int_sort; + std::map string_cache; virtual void set_manager(ast_manager * m, family_id id); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 458786110..e2896f4f5 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -37,7 +37,7 @@ theory_str::~theory_str() { void theory_str::assert_axiom(expr * e) { if (get_manager().is_true(e)) return; - TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); + TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << std::endl;); context & ctx = get_context(); if (!ctx.b_internalized(e)) { ctx.internalize(e, true); @@ -45,11 +45,12 @@ void theory_str::assert_axiom(expr * e) { literal lit(ctx.get_literal(e)); ctx.mark_as_relevant(lit); ctx.mk_th_axiom(get_id(), 1, &lit); - TRACE("t_str_detail", tout << "done asserting " << mk_ismt2_pp(e, get_manager()) << "\n";); + TRACE("t_str_detail", tout << "done asserting " << mk_ismt2_pp(e, get_manager()) << std::endl;); } void theory_str::assert_implication(expr * premise, expr * conclusion) { ast_manager & m = get_manager(); + TRACE("t_str_detail", tout << "asserting implication " << mk_ismt2_pp(premise, m) << " -> " << mk_ismt2_pp(conclusion, m) << std::endl;); expr_ref axiom(m.mk_or(m.mk_not(premise), conclusion), m); assert_axiom(axiom); } @@ -100,15 +101,6 @@ bool theory_str::internalize_term(app * term) { return true; } -Z3_ast mk_internal_xor_var(Z3_theory t) { - Z3_context ctx = Z3_theory_get_context(t); - std::stringstream ss; - ss << tmpXorVarCount; - tmpXorVarCount++; - std::string name = "$$_xor_" + ss.str(); - return mk_int_var(ctx, name.c_str()); -} - app * theory_str::mk_internal_xor_var() { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -118,7 +110,9 @@ app * theory_str::mk_internal_xor_var() { std::string name = "$$_xor_" + ss.str(); // Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), INT_SORT)); sort * int_sort = m.mk_sort(m_autil.get_family_id(), INT_SORT); - symbol sym(name); + 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, int_sort)); // TODO ctx.save_ast_trail(a)? @@ -585,53 +579,7 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { int arg2Len = -1; /* = getLenValue(arg2); */ if (arg1Len != -1 || arg2Len != -1) { NOT_IMPLEMENTED_YET(); // TODO - } else { - /* - Z3_ast xorFlag = NULL; - std::pair key1(arg1, arg2); - std::pair key2(arg2, arg1); - if (varForBreakConcat.find(key1) == varForBreakConcat.end() && varForBreakConcat.find(key2) == varForBreakConcat.end()) { - xorFlag = mk_internal_xor_var(t); - varForBreakConcat[key1][0] = xorFlag; - } else { - if (varForBreakConcat.find(key1) != varForBreakConcat.end()) { - xorFlag = varForBreakConcat[key1][0]; - } else { - xorFlag = varForBreakConcat[key2][0]; - } - } - - int concatStrLen = const_str.length(); - int xor_pos = 0; - int and_count = 1; - Z3_ast * xor_items = new Z3_ast[concatStrLen + 1]; - Z3_ast * and_items = new Z3_ast[4 * (concatStrLen + 1) + 1]; - Z3_ast arg1_eq = NULL; - Z3_ast arg2_eq = NULL; - for (int i = 0; i < concatStrLen + 1; i++) { - std::string prefixStr = const_str.substr(0, i); - std::string suffixStr = const_str.substr(i, concatStrLen - i); - - // skip invalidate options - if (isConcatFunc(t, arg1) && canConcatEqStr(t, arg1, prefixStr) == 0) { - continue; - } - if (isConcatFunc(t, arg2) && canConcatEqStr(t, arg2, suffixStr) == 0) { - continue; - } - - Z3_ast xorAst = Z3_mk_eq(ctx, xorFlag, mk_int(ctx, xor_pos)); - xor_items[xor_pos++] = xorAst; - - Z3_ast prefixAst = my_mk_str_value(t, prefixStr.c_str()); - arg1_eq = Z3_mk_eq(ctx, arg1, prefixAst); - and_items[and_count++] = Z3_mk_eq(ctx, xorAst, arg1_eq); - - Z3_ast suffixAst = my_mk_str_value(t, suffixStr.c_str()); - arg2_eq = Z3_mk_eq(ctx, arg2, suffixAst); - and_items[and_count++] = Z3_mk_eq(ctx, xorAst, arg2_eq); - } - */ + } else { /* ! (arg1Len != 1 || arg2Len != 1) */ expr_ref xorFlag(m); std::pair key1(arg1, arg2); std::pair key2(arg2, arg1); @@ -651,11 +599,8 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { int concatStrLen = const_str.length(); int xor_pos = 0; int and_count = 1; - expr * xor_items[] = new expr*[concatStrLen + 1]; - expr * and_items[] = new expr*[4 * (concatStrLen+1) + 1]; - - expr_ref arg1_eq(m); - expr_ref arg2_eq(m); + expr ** xor_items = new expr*[concatStrLen + 1]; + expr ** and_items = new expr*[4 * (concatStrLen+1) + 1]; for (int i = 0; i < concatStrLen + 1; ++i) { std::string prefixStr = const_str.substr(0, i); @@ -670,60 +615,39 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { continue; } */ - expr_ref xorAst(ctx.mk_eq_atom(xorFlag, mk_int(xor_pos)), m); + expr_ref xorAst(ctx.mk_eq_atom(xorFlag, m_autil.mk_numeral(rational(xor_pos), true)), m); xor_items[xor_pos++] = xorAst; expr_ref prefixAst(m_strutil.mk_string(prefixStr), m); - arg1_eq = ctx.mk_eq_atom(arg1, prefixAst); + expr_ref arg1_eq (ctx.mk_eq_atom(arg1, prefixAst), m); and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg1_eq); expr_ref suffixAst(m_strutil.mk_string(prefixStr), m); - arg2_eq = ctx.mk_eq_atom(arg2, suffixAst); + expr_ref arg2_eq (ctx.mk_eq_atom(arg2, suffixAst), m); and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg2_eq); } expr_ref implyL(ctx.mk_eq_atom(concat, str), m); - expr_ref implyR(m); + expr_ref implyR1(m); if (xor_pos == 0) { // negate expr_ref concat_eq_str(ctx.mk_eq_atom(concat, str), m); expr_ref negate_ast(m.mk_not(concat_eq_str), m); assert_axiom(negate_ast); } else { - // TODO if (xor_pos == 1) { - + and_items[0] = xor_items[0]; + implyR1 = m.mk_and(and_count, and_items); } else { - + and_items[0] = m.mk_or(xor_pos, xor_items); + implyR1 = m.mk_and(and_count, and_items); } + assert_implication(implyL, implyR1); } delete[] xor_items; delete[] and_items; - - /* - - Z3_ast implyL = Z3_mk_eq(ctx, concatAst, constStr); - Z3_ast implyR1 = NULL; - if (xor_pos == 0) { - // negate - Z3_ast negateAst = Z3_mk_not(ctx, Z3_mk_eq(ctx, concatAst, constStr)); - addAxiom(t, negateAst, __LINE__); - } else { - if (xor_pos == 1) { - and_items[0] = xor_items[0]; - implyR1 = Z3_mk_and(ctx, and_count, and_items); - } else { - and_items[0] = Z3_mk_or(ctx, xor_pos, xor_items); - implyR1 = Z3_mk_and(ctx, and_count, and_items); - } - Z3_ast implyToAssert = Z3_mk_implies(ctx, implyL, implyR1); - addAxiom(t, implyToAssert, __LINE__); - } - delete[] xor_items; - delete[] and_items; - */ - } - } + } /* (arg1Len != 1 || arg2Len != 1) */ + } /* if (Concat(arg1, arg2) == NULL) */ } } } @@ -945,7 +869,7 @@ void theory_str::new_diseq_eh(theory_var x, theory_var y) { } void theory_str::relevant_eh(app * n) { - TRACE("t_str", tout << "relevant: " << mk_ismt2_pp(n, get_manager()) << "\n";); + TRACE("t_str", tout << "relevant: " << mk_ismt2_pp(n, get_manager()) << std::endl;); } void theory_str::assign_eh(bool_var v, bool is_true) { @@ -988,7 +912,7 @@ void theory_str::init_model(model_generator & mg) { model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { TRACE("t_str", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << - " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")\n";); + " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")" << std::endl;); ast_manager & m = get_manager(); context & ctx = get_context(); app_ref owner(m); From 191c50b529310cae9f39463d371eb92b896560d5 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 29 Sep 2015 17:52:19 -0400 Subject: [PATCH 33/91] fix solve_concat_eq_str() case 4: prefixStr should have been suffixStr --- src/smt/theory_str.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index e2896f4f5..f9fb7e3a4 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -622,7 +622,7 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { expr_ref arg1_eq (ctx.mk_eq_atom(arg1, prefixAst), m); and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg1_eq); - expr_ref suffixAst(m_strutil.mk_string(prefixStr), m); + expr_ref suffixAst(m_strutil.mk_string(suffixStr), m); expr_ref arg2_eq (ctx.mk_eq_atom(arg2, suffixAst), m); and_items[and_count++] = ctx.mk_eq_atom(xorAst, arg2_eq); } From 8ed86d2f19a074dd68d10ca5832a4cfa18351cbb Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 29 Sep 2015 18:02:05 -0400 Subject: [PATCH 34/91] add concatenation axiom --- src/smt/theory_str.cpp | 16 ++++++++++++++-- src/smt/theory_str.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index f9fb7e3a4..ea7b84d62 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -142,7 +142,7 @@ app * theory_str::mk_concat(app * e1, app * e2) { } bool theory_str::can_propagate() { - return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty(); + return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty() || !m_concat_axiom_todo.empty(); } void theory_str::propagate() { @@ -160,6 +160,11 @@ void theory_str::propagate() { handle_equality(lhs->get_owner(), rhs->get_owner()); } m_str_eq_todo.reset(); + + for (unsigned i = 0; i < m_concat_axiom_todo.empty(); ++i) { + instantiate_concat_axiom(m_concat_axiom_todo[i]); + } + m_concat_axiom_todo.reset(); } } @@ -167,7 +172,6 @@ void theory_str::propagate() { * Instantiate an axiom of the following form: * Length(Concat(x, y)) = Length(x) + Length(y) */ -// TODO this isn't used yet void theory_str::instantiate_concat_axiom(enode * cat) { SASSERT(is_concat(cat)); app * a_cat = cat->get_owner(); @@ -321,6 +325,7 @@ void theory_str::reset_eh() { TRACE("t_str", tout << "resetting" << std::endl;); m_basicstr_axiom_todo.reset(); m_str_eq_todo.reset(); + m_concat_axiom_todo.reset(); pop_scope_eh(0); } @@ -767,6 +772,8 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { } } + // TODO simplify_parent over eqc + // TODO regex unroll? (much later) } @@ -785,6 +792,11 @@ void theory_str::set_up_axioms(expr * ex) { enode * n = ctx.get_enode(ex); SASSERT(n); m_basicstr_axiom_todo.push_back(n); + + // if additionally ex is a concatenation, set up concatenation axioms + if (is_app(ex) && is_concat(to_app(ex))) { + m_concat_axiom_todo.push_back(n); + } } else { TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << ": expr is of wrong sort, ignoring" << std::endl;); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index ea6ec8551..458287392 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -57,6 +57,7 @@ namespace smt { ptr_vector m_basicstr_axiom_todo; svector > m_str_eq_todo; + ptr_vector m_concat_axiom_todo; int tmpXorVarCount; std::map, std::map > varForBreakConcat; From 1cdfe159b8d23a458a5b97c8c76325c86e2fe366 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 29 Sep 2015 20:19:43 -0400 Subject: [PATCH 35/91] simplify_concat_equality() and easy cases there still WIP especially wrt. model generation but what's here does work --- src/ast/str_decl_plugin.cpp | 2 +- src/smt/theory_str.cpp | 392 +++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 12 +- 3 files changed, 401 insertions(+), 5 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 9398dbf34..c72a5dbc2 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -102,7 +102,7 @@ func_decl * str_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, app * str_decl_plugin::mk_string(std::string & val) { std::map::iterator it = string_cache.find(val); if (it == string_cache.end()) { - char * new_buffer = alloc_svect(char, val.length() + 1); + char * new_buffer = alloc_svect(char, (val.length() + 1)); strcpy(new_buffer, val.c_str()); parameter p[1] = {parameter(new_buffer)}; func_decl * d; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index ea7b84d62..92edbc22b 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -119,7 +119,7 @@ app * theory_str::mk_internal_xor_var() { return a; } -app * theory_str::mk_strlen(app * e) { +app * theory_str::mk_strlen(expr * e) { /*if (m_strutil.is_string(e)) {*/ if (false) { const char * strval = 0; m_strutil.is_string(e, &strval); @@ -378,9 +378,258 @@ void theory_str::group_terms_by_eqc(expr * n, std::set & concats, std::se } while (eqcNode != nNode); } -void theory_str::simplify_concat_equality(expr * lhs, expr * rhs) { - // TODO strArgmt::simplifyConcatEq() +void theory_str::get_nodes_in_concat(expr * node, ptr_vector & nodeList) { + app * a_node = to_app(node); + if (!is_concat(a_node)) { + nodeList.push_back(node); + return; + } else { + SASSERT(a_node->get_num_args() == 2); + expr * leftArg = a_node->get_arg(0); + expr * rightArg = a_node->get_arg(1); + get_nodes_in_concat(leftArg, nodeList); + get_nodes_in_concat(rightArg, nodeList); + } } + +/* + * The inputs: + * ~ nn: non const node + * ~ eq_str: the equivalent constant string of nn + * Iterate the parent of all eqc nodes of nn, looking for: + * ~ concat node + * to see whether some concat nodes can be simplified. + */ + +void theory_str::simplify_parent(expr * nn, expr * eq_str) { + // TODO strTheory::simplifyParent() +} + +expr * theory_str::simplify_concat(expr * node) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + std::map resolvedMap; + ptr_vector argVec; + get_nodes_in_concat(node, argVec); + + for (unsigned i = 0; i < argVec.size(); ++i) { + bool vArgHasEqcValue = false; + expr * vArg = get_eqc_value(argVec[i], vArgHasEqcValue); + if (vArg != argVec[i]) { + resolvedMap[argVec[i]] = vArg; + } + } + + if (resolvedMap.size() == 0) { + // no simplification possible + return node; + } else { + app * resultAst = m_strutil.mk_string(""); + for (unsigned i = 0; i < argVec.size(); ++i) { + bool vArgHasEqcValue = false; + expr * vArg = get_eqc_value(argVec[i], vArgHasEqcValue); + resultAst = mk_concat(to_app(resultAst), to_app(vArg)); + } + TRACE("t_str_detail", tout << mk_ismt2_pp(node, m) << " is simplified to " << mk_ismt2_pp(resultAst, m) << std::endl;); + + if (in_same_eqc(node, resultAst)) { + TRACE("t_str_detail", tout << "SKIP: both concats are already in the same equivalence class" << std::endl;); + } else { + expr ** items = alloc_svect(expr*, resolvedMap.size()); + int pos = 0; + std::map::iterator itor = resolvedMap.begin(); + for (; itor != resolvedMap.end(); ++itor) { + items[pos++] = ctx.mk_eq_atom(itor->first, itor->second); + } + expr_ref premise(m); + if (pos == 1) { + premise = items[0]; + } else { + premise = m.mk_and(pos, items); + } + expr_ref conclusion(ctx.mk_eq_atom(node, resultAst), m); + assert_implication(premise, conclusion); + } + return resultAst; + } + +} + +/* + * Handle two equivalent Concats. + */ +void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + + app * a_nn1 = to_app(nn1); + SASSERT(a_nn1->get_num_args() == 2); + app * a_nn2 = to_app(nn2); + SASSERT(a_nn2->get_num_args() == 2); + + expr * a1_arg0 = a_nn1->get_arg(0); + expr * a1_arg1 = a_nn1->get_arg(1); + expr * a2_arg0 = a_nn2->get_arg(0); + expr * a2_arg1 = a_nn2->get_arg(1); + + // TODO + /* + int a1_arg0_len = getLenValue(t, a1_arg0); + int a1_arg1_len = getLenValue(t, a1_arg1); + int a2_arg0_len = getLenValue(t, a2_arg0); + int a2_arg1_len = getLenValue(t, a2_arg1); + */ + int a1_arg0_len = -1; + int a1_arg1_len = -1; + int a2_arg0_len = -1; + int a2_arg1_len = -1; + + TRACE("t_str", tout << "nn1 = " << mk_ismt2_pp(nn1, m) << std::endl + << "nn2 = " << mk_ismt2_pp(nn2, m) << std::endl;); + + // TODO inferLenConcatEq(nn1, nn2); + + if (a1_arg0 == a2_arg0) { + if (!in_same_eqc(a1_arg1, a2_arg1)) { + expr_ref premise(ctx.mk_eq_atom(nn1, nn2), m); + expr_ref eq1(ctx.mk_eq_atom(a1_arg1, a2_arg1), m); + expr_ref eq2(ctx.mk_eq_atom(mk_strlen(a1_arg1), mk_strlen(a2_arg1)), m); + expr_ref conclusion(m.mk_and(eq1, eq2), m); + assert_implication(premise, conclusion); + } + TRACE("t_str_detail", tout << "SKIP: a1_arg0 == a2_arg0" << std::endl;); + return; + } + + if (a1_arg1 == a2_arg1) { + if (!in_same_eqc(a1_arg0, a2_arg0)) { + expr_ref premise(ctx.mk_eq_atom(nn1, nn2), m); + expr_ref eq1(ctx.mk_eq_atom(a1_arg0, a2_arg0), m); + expr_ref eq2(ctx.mk_eq_atom(mk_strlen(a1_arg0), mk_strlen(a2_arg0)), m); + expr_ref conclusion(m.mk_and(eq1, eq2), m); + assert_implication(premise, conclusion); + } + TRACE("t_str_detail", tout << "SKIP: a1_arg1 == a2_arg1" << std::endl;); + return; + } + + // quick path + + if (in_same_eqc(a1_arg0, a2_arg0)) { + if (in_same_eqc(a1_arg1, a2_arg1)) { + TRACE("t_str_detail", tout << "SKIP: a1_arg0 =~ a2_arg0 and a1_arg1 =~ a2_arg1" << std::endl;); + return; + } else { + TRACE("t_str_detail", tout << "quick path 1-1: a1_arg0 =~ a2_arg0" << std::endl;); + expr_ref premise(m.mk_and(ctx.mk_eq_atom(nn1, nn2), ctx.mk_eq_atom(a1_arg0, a2_arg0)), m); + expr_ref conclusion(m.mk_and(ctx.mk_eq_atom(a1_arg1, a2_arg1), ctx.mk_eq_atom(mk_strlen(a1_arg1), mk_strlen(a2_arg1))), m); + assert_implication(premise, conclusion); + return; + } + } else { + if (in_same_eqc(a1_arg1, a2_arg1)) { + TRACE("t_str_detail", tout << "quick path 1-2: a1_arg1 =~ a2_arg1" << std::endl;); + expr_ref premise(m.mk_and(ctx.mk_eq_atom(nn1, nn2), ctx.mk_eq_atom(a1_arg1, a2_arg1)), m); + expr_ref conclusion(m.mk_and(ctx.mk_eq_atom(a1_arg0, a2_arg0), ctx.mk_eq_atom(mk_strlen(a1_arg0), mk_strlen(a2_arg0))), m); + assert_implication(premise, conclusion); + return; + } + } + + // TODO quick path 1-2 + /* + if(a1_arg0_len != -1 && a2_arg0_len != -1 && a1_arg0_len == a2_arg0_len){ + if (! inSameEqc(t, a1_arg0, a2_arg0)) { + __debugPrint(logFile, ">> [simplifyConcatEq] Quick Path 2-1: len(nn1.arg0) == len(nn2.arg0)\n"); + Z3_ast ax_l1 = Z3_mk_eq(ctx, nn1, nn2); + Z3_ast ax_l2 = Z3_mk_eq(ctx, mk_length(t, a1_arg0), mk_length(t, a2_arg0)); + Z3_ast ax_r1 = Z3_mk_eq(ctx, a1_arg0, a2_arg0); + Z3_ast ax_r2 = Z3_mk_eq(ctx, a1_arg1, a2_arg1); + Z3_ast toAdd = Z3_mk_implies(ctx, mk_2_and(t, ax_l1, ax_l2), mk_2_and(t, ax_r1, ax_r2)); + addAxiom(t, toAdd, __LINE__); + return; + } + } + + if (a1_arg1_len != -1 && a2_arg1_len != -1 && a1_arg1_len == a2_arg1_len) + { + if (!inSameEqc(t, a1_arg1, a2_arg1)) { + __debugPrint(logFile, ">> [simplifyConcatEq] Quick Path 2-2: len(nn1.arg1) == len(nn2.arg1)\n"); + Z3_ast ax_l1 = Z3_mk_eq(ctx, nn1, nn2); + Z3_ast ax_l2 = Z3_mk_eq(ctx, mk_length(t, a1_arg1), mk_length(t, a2_arg1)); + Z3_ast ax_r1 = Z3_mk_eq(ctx, a1_arg0, a2_arg0); + Z3_ast ax_r2 = Z3_mk_eq(ctx, a1_arg1, a2_arg1); + Z3_ast toAdd = Z3_mk_implies(ctx, mk_2_and(t, ax_l1, ax_l2), mk_2_and(t, ax_r1, ax_r2)); + addAxiom(t, toAdd, __LINE__); + return; + } + } + */ + + expr * new_nn1 = simplify_concat(nn1); + expr * new_nn2 = simplify_concat(nn2); + app * a_new_nn1 = to_app(new_nn1); + app * a_new_nn2 = to_app(new_nn2); + expr * v1_arg0 = a_new_nn1->get_arg(0); + expr * v1_arg1 = a_new_nn1->get_arg(1); + expr * v2_arg0 = a_new_nn2->get_arg(0); + expr * v2_arg1 = a_new_nn2->get_arg(1); + + TRACE("t_str_detail", tout << "new_nn1 = " << mk_ismt2_pp(new_nn1, m) << std::endl + << "new_nn2 = " << mk_ismt2_pp(new_nn2, m) << std::endl;); + + if (new_nn1 == new_nn2) { + TRACE("t_str_detail", tout << "equal concats, return" << std::endl;); + return; + } + + if (!can_two_nodes_eq(new_nn1, new_nn2)) { + expr_ref detected(m.mk_not(ctx.mk_eq_atom(new_nn1, new_nn2)), m); + TRACE("t_str_detail", tout << "inconsistency detected: " << mk_ismt2_pp(detected, m) << std::endl;); + assert_axiom(detected); + return; + } + + // check whether new_nn1 and new_nn2 are still concats + + bool n1IsConcat = is_concat(a_new_nn1); + bool n2IsConcat = is_concat(a_new_nn2); + if (!n1IsConcat && n2IsConcat) { + TRACE("t_str_detail", tout << "nn1_new is not a concat" << std::endl;); + if (is_string(a_new_nn1)) { + simplify_parent(new_nn2, new_nn1); + } + return; + } else if (n1IsConcat && !n2IsConcat) { + TRACE("t_str_detail", tout << "nn2_new is not a concat" << std::endl;); + if (is_string(a_new_nn2)) { + simplify_parent(new_nn1, new_nn2); + } + return; + } + + if (!in_same_eqc(new_nn1, new_nn2) && (nn1 != new_nn1 || nn2 != new_nn2)) { + int ii4 = 0; + expr* item[3]; + if (nn1 != new_nn1) { + item[ii4++] = ctx.mk_eq_atom(nn1, new_nn1); + } + if (nn2 != new_nn2) { + item[ii4++] = ctx.mk_eq_atom(nn2, new_nn2); + } + item[ii4++] = ctx.mk_eq_atom(nn1, nn2); + expr_ref premise(m.mk_and(ii4, item), m); + expr_ref conclusion(ctx.mk_eq_atom(new_nn1, new_nn2), m); + assert_implication(premise, conclusion); + } + + // start to split both concats + + // TODO + NOT_IMPLEMENTED_YET(); + +} + /* * Look through the equivalence class of n to find a string constant. * Return that constant if it is found, and set hasEqcValue to true. @@ -403,6 +652,119 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { return n; } +/* + * Decide whether n1 and n2 are already in the same equivalence class. + * This only checks whether the core considers them to be equal; + * they may not actually be equal. + */ +bool theory_str::in_same_eqc(expr * n1, expr * n2) { + if (n1 == n2) return true; + context & ctx = get_context(); + enode * n1Node = ctx.get_enode(n1); + enode * n2Node = ctx.get_enode(n2); + + // here's what the old Z3str2 would have done; we can do something much better + /* + n1Node->get_root(); + enode * curr = n1Node->get_next(); + while (curr != n1Node) { + if (curr == n2Node) { + return true; + } + curr = curr->get_next(); + } + return false; + */ + return n1Node->get_root() == n2Node->get_root(); +} + +/* +bool canTwoNodesEq(Z3_theory t, Z3_ast n1, Z3_ast n2) { + Z3_ast n1_curr = n1; + Z3_ast n2_curr = n2; + + // case 0: n1_curr is const string, n2_curr is const string + if (isConstStr(t, n1_curr) && isConstStr(t, n2_curr)) { + if (n1_curr != n2_curr) { + return false; + } + } + // case 1: n1_curr is concat, n2_curr is const string + else if (isConcatFunc(t, n1_curr) && isConstStr(t, n2_curr)) { + std::string n2_curr_str = getConstStrValue(t, n2_curr); + if (canConcatEqStr(t, n1_curr, n2_curr_str) != 1) { + return false; + } + } + // case 2: n2_curr is concat, n1_curr is const string + else if (isConcatFunc(t, n2_curr) && isConstStr(t, n1_curr)) { + std::string n1_curr_str = getConstStrValue(t, n1_curr); + if (canConcatEqStr(t, n2_curr, n1_curr_str) != 1) { + return false; + } + } else if (isConcatFunc(t, n1_curr) && isConcatFunc(t, n2_curr)) { + if (canConcatEqConcat(t, n1_curr, n2_curr) != 1) { + return false; + } + } + + return true; +} +*/ + +bool theory_str::can_concat_eq_str(expr * concat, std::string str) { + // TODO + return true; +} + +bool theory_str::can_concat_eq_concat(expr * concat1, expr * concat2) { + // TODO + return true; +} + +/* + * Check whether n1 and n2 could be equal. + * Returns true if n1 could equal n2 (maybe), + * and false if n1 is definitely not equal to n2 (no). + */ +bool theory_str::can_two_nodes_eq(expr * n1, expr * n2) { + app * n1_curr = to_app(n1); + app * n2_curr = to_app(n2); + + // case 0: n1_curr is const string, n2_curr is const string + if (is_string(n1_curr) && is_string(n2_curr)) { + if (n1_curr != n2_curr) { + return false; + } + } + // case 1: n1_curr is concat, n2_curr is const string + else if (is_concat(n1_curr) && is_string(n2_curr)) { + const char * tmp = 0; + m_strutil.is_string(n2_curr, & tmp); + std::string n2_curr_str(tmp); + if (!can_concat_eq_str(n1_curr, n2_curr_str)) { + return false; + } + } + // case 2: n2_curr is concat, n1_curr is const string + else if (is_concat(n2_curr) && is_string(n1_curr)) { + const char * tmp = 0; + m_strutil.is_string(n1_curr, & tmp); + std::string n1_curr_str(tmp); + if (!can_concat_eq_str(n2_curr, n1_curr_str)) { + return false; + } + } + // case 3: both are concats + else if (is_concat(n1_curr) && is_concat(n2_curr)) { + if (!can_concat_eq_concat(n1_curr, n2_curr)) { + return false; + } + } + + return true; +} + /* * strArgmt::solve_concat_eq_str() * Solve concatenations of the form: @@ -604,8 +966,12 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { int concatStrLen = const_str.length(); int xor_pos = 0; int and_count = 1; + /* expr ** xor_items = new expr*[concatStrLen + 1]; expr ** and_items = new expr*[4 * (concatStrLen+1) + 1]; + */ + expr ** xor_items = alloc_svect(expr*, (concatStrLen+1)); + expr ** and_items = alloc_svect(expr*, (4 * (concatStrLen+1) + 1)); for (int i = 0; i < concatStrLen + 1; ++i) { std::string prefixStr = const_str.substr(0, i); @@ -736,6 +1102,11 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { ); // step 1: Concat == Concat + // I'm disabling this entire code block for now. It may no longer be useful. + // Z3 seems to be putting LHS and RHS into the same equivalence class extremely early. + // As a result, simplify_concat_equality() is never getting called, + // and if it were called, it would probably get called with the same element on both sides. + /* bool hasCommon = false; if (eqc_lhs_concat.size() != 0 && eqc_rhs_concat.size() != 0) { std::set::iterator itor1 = eqc_lhs_concat.begin(); @@ -756,6 +1127,21 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { simplify_concat_equality(*(eqc_lhs_concat.begin()), *(eqc_rhs_concat.begin())); } } + */ + if (eqc_lhs_concat.size() != 0 && eqc_rhs_concat.size() != 0) { + // let's pick the first concat in the LHS's eqc + // and find some concat in the RHS's eqc that is + // distinct from the first one we picked + expr * lhs = *eqc_lhs_concat.begin(); + std::set::iterator itor2 = eqc_rhs_concat.begin(); + for (; itor2 != eqc_rhs_concat.end(); ++itor2) { + expr * rhs = *itor2; + if (lhs != rhs) { + simplify_concat_equality(lhs, rhs); + break; + } + } + } // step 2: Concat == Constant if (eqc_lhs_const.size() != 0) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 458287392..c3641016f 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -65,7 +65,7 @@ namespace smt { void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); - app * mk_strlen(app * e); + app * mk_strlen(expr * e); app * mk_concat(app * e1, app * e2); app * mk_internal_xor_var(); @@ -82,6 +82,16 @@ namespace smt { void handle_equality(expr * lhs, expr * rhs); expr * get_eqc_value(expr * n, bool & hasEqcValue); + bool in_same_eqc(expr * n1, expr * n2); + + bool can_two_nodes_eq(expr * n1, expr * n2); + bool can_concat_eq_str(expr * concat, std::string str); + bool can_concat_eq_concat(expr * concat1, expr * concat2); + + void get_nodes_in_concat(expr * node, ptr_vector & nodeList); + expr * simplify_concat(expr * node); + + void simplify_parent(expr * nn, expr * eq_str); void simplify_concat_equality(expr * lhs, expr * rhs); void solve_concat_eq_str(expr * concat, expr * str); From a62d15403e434fe5358523b52cfc1442bad11917 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 29 Sep 2015 22:31:11 -0400 Subject: [PATCH 36/91] start simplify_concat_eq(), WIP but some cases OK also fix model generation for concats and nested concats --- src/smt/theory_str.cpp | 116 +++++++++++++++++++++++++++++++++++++---- src/smt/theory_str.h | 4 +- 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 92edbc22b..3f8de3d6f 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -131,14 +131,60 @@ app * theory_str::mk_strlen(expr * e) { } } -app * theory_str::mk_concat(app * e1, app * e2) { +/* + * Returns the simplified concatenation of two expressions, + * where either both expressions are constant strings + * or one expression is the empty string. + * If this precondition does not hold, the function returns NULL. + * (note: this function was strTheory::Concat()) + */ +expr * theory_str::mk_concat_const_str(expr * n1, expr * n2) { + bool n1HasEqcValue = false; + bool n2HasEqcValue = false; + expr * v1 = get_eqc_value(n1, n1HasEqcValue); + expr * v2 = get_eqc_value(n2, n2HasEqcValue); + if (n1HasEqcValue && n2HasEqcValue) { + const char * n1_str_tmp; + m_strutil.is_string(v1, & n1_str_tmp); + std::string n1_str(n1_str_tmp); + const char * n2_str_tmp; + m_strutil.is_string(v2, & n2_str_tmp); + std::string n2_str(n2_str_tmp); + std::string result = n1_str + n2_str; + return m_strutil.mk_string(result); + } else if (n1HasEqcValue && !n2HasEqcValue) { + const char * n1_str_tmp; + m_strutil.is_string(v1, & n1_str_tmp); + if (strcmp(n1_str_tmp, "") == 0) { + return n2; + } + } else if (!n1HasEqcValue && n2HasEqcValue) { + const char * n2_str_tmp; + m_strutil.is_string(v2, & n2_str_tmp); + if (strcmp(n2_str_tmp, "") == 0) { + return n1; + } + } + return NULL; +} + +expr * theory_str::mk_concat(expr * n1, expr * n2) { ast_manager & m = get_manager(); - if (e1 == NULL || e2 == NULL) { + if (n1 == NULL || n2 == NULL) { m.raise_exception("strings to be concatenated cannot be NULL"); } - // TODO there's a *TON* of missing code here from strTheory::mk_concat() - expr * args[2] = {e1, e2}; - return get_manager().mk_app(get_id(), OP_STRCAT, 0, 0, 2, args); + bool n1HasEqcValue = false; + bool n2HasEqcValue = false; + n1 = get_eqc_value(n1, n1HasEqcValue); + n2 = get_eqc_value(n2, n2HasEqcValue); + if (n1HasEqcValue && n2HasEqcValue) { + return mk_concat_const_str(n1, n2); + } else { + // TODO there's a *TON* of missing code here from strTheory::mk_concat() + // if all else fails, just build the application AST + expr * args[2] = {n1, n2}; + return get_manager().mk_app(get_id(), OP_STRCAT, 0, 0, 2, args); + } } bool theory_str::can_propagate() { @@ -424,11 +470,11 @@ expr * theory_str::simplify_concat(expr * node) { // no simplification possible return node; } else { - app * resultAst = m_strutil.mk_string(""); + expr * resultAst = m_strutil.mk_string(""); for (unsigned i = 0; i < argVec.size(); ++i) { bool vArgHasEqcValue = false; expr * vArg = get_eqc_value(argVec[i], vArgHasEqcValue); - resultAst = mk_concat(to_app(resultAst), to_app(vArg)); + resultAst = mk_concat(resultAst, vArg); } TRACE("t_str_detail", tout << mk_ismt2_pp(node, m) << " is simplified to " << mk_ismt2_pp(resultAst, m) << std::endl;); @@ -821,7 +867,7 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } else { implyL1 = m.mk_and(item1[0], item1[1]); } - newConcat = mk_concat(to_app(arg1), to_app(arg2)); + newConcat = mk_concat(arg1, arg2); if (newConcat != str) { expr_ref implyR1(ctx.mk_eq_atom(concat, newConcat), m); assert_implication(implyL1, implyR1); @@ -1308,6 +1354,52 @@ void theory_str::init_model(model_generator & mg) { mg.register_factory(m_factory); } +/* + * Helper function for mk_value(). + * Attempts to resolve the expression 'n' to a string constant. + * Stronger than get_eqc_value() in that it will perform recursive descent + * through every subexpression and attempt to resolve those to concrete values as well. + * Returns the concrete value obtained from this process, + * guaranteed to satisfy m_strutil.is_string(), + * if one could be obtained, + * or else returns NULL if no concrete value was derived. + */ +app * theory_str::mk_value_helper(app * n) { + if (m_strutil.is_string(n)) { + return n; + } else if (is_concat(n)) { + // recursively call this function on each argument + SASSERT(n->get_num_args() == 2); + expr * a0 = n->get_arg(0); + expr * a1 = n->get_arg(1); + + app * a0_conststr = mk_value_helper(to_app(a0)); + app * a1_conststr = mk_value_helper(to_app(a1)); + + if (a0_conststr != NULL && a1_conststr != NULL) { + const char * a0_str = 0; + m_strutil.is_string(a0_conststr, &a0_str); + + const char * a1_str = 0; + m_strutil.is_string(a1_conststr, &a1_str); + + std::string a0_s(a0_str); + std::string a1_s(a1_str); + std::string result = a0_s + a1_s; + return m_strutil.mk_string(result); + } + } + // fallback path + // try to find some constant string, anything, in the equivalence class of n + bool hasEqc = false; + expr * n_eqc = get_eqc_value(n, hasEqc); + if (hasEqc) { + return to_app(n_eqc); + } else { + return NULL; + } +} + model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { TRACE("t_str", tout << "mk_value for: " << mk_ismt2_pp(n->get_owner(), get_manager()) << " (sort " << mk_ismt2_pp(get_manager().get_sort(n->get_owner()), get_manager()) << ")" << std::endl;); @@ -1319,10 +1411,12 @@ model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { // If the owner is not internalized, it doesn't have an enode associated. SASSERT(ctx.e_internalized(owner)); - if (m_strutil.is_string(owner)) { - return alloc(expr_wrapper_proc, owner); + app * val = mk_value_helper(owner); + if (val != NULL) { + return alloc(expr_wrapper_proc, val); + } else { + m.raise_exception("failed to find concrete value"); return NULL; } - NOT_IMPLEMENTED_YET(); // TODO } void theory_str::finalize_model(model_generator & mg) {} diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index c3641016f..daa5656bb 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -66,7 +66,8 @@ namespace smt { void assert_implication(expr * premise, expr * conclusion); app * mk_strlen(expr * e); - app * mk_concat(app * e1, app * e2); + expr * mk_concat(expr * n1, expr * n2); + expr * mk_concat_const_str(expr * n1, expr * n2); app * mk_internal_xor_var(); @@ -81,6 +82,7 @@ namespace smt { void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); + app * mk_value_helper(app * n); expr * get_eqc_value(expr * n, bool & hasEqcValue); bool in_same_eqc(expr * n1, expr * n2); From ed7b343822e2829f63c9eb2378b454b333d01d18 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 05:15:14 -0400 Subject: [PATCH 37/91] detect and process concat eq type 1 (WIP UNTESTED) --- src/smt/theory_str.cpp | 462 ++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 37 +++- 2 files changed, 495 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3f8de3d6f..fc4548f7a 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -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 & dest, std::map & src) { + std::map::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::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::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 key1(concatAst1, concatAst2); + std::pair 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 >::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() { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index daa5656bb..b66eef4ad 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -47,7 +47,15 @@ namespace smt { }; class theory_str : public theory { - // TODO + struct T_cut + { + int level; + std::map vars; + + T_cut() { + level = -100; + } + }; protected: bool search_started; arith_util m_autil; @@ -59,8 +67,13 @@ namespace smt { svector > m_str_eq_todo; ptr_vector m_concat_axiom_todo; + int tmpStringVarCount; int tmpXorVarCount; std::map, std::map > varForBreakConcat; + + bool avoidLoopCut = true; + bool loopDetected = false; + std::map > 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 & concats, std::set & vars, std::set & consts); public: From e2901fff1ea1394bdba0a892e60d06263ff38db4 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 05:21:16 -0400 Subject: [PATCH 38/91] fix compilation errors --- src/smt/theory_str.cpp | 22 +++++++++++----------- src/smt/theory_str.h | 5 +++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index fc4548f7a..3458ec60c 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -903,8 +903,8 @@ 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; + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << std::endl; ); if (!is_concat(to_app(concatAst1))) { @@ -1041,8 +1041,8 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { option++; - add_cut_info_merge(t2, sLevel, x); - add_cut_info_merge(t2, sLevel, n); + add_cut_info_merge(t2, ctx.get_scope_level(), x); + add_cut_info_merge(t2, ctx.get_scope_level(), n); } else { loopDetected = true; TRACE("t_str", tout << "AVOID LOOP: SKIPPED" << std::endl;); @@ -1064,8 +1064,8 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { } 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); + expr_ref premise(ctx.mk_eq_atom(concatAst1, concatAst2), mgr); + expr_ref conclusion(mgr.mk_and(pos, and_item), mgr); assert_implication(premise, conclusion); } else { TRACE("t_str", tout << "STOP: no split option found for two EQ concats." << std::endl;); @@ -1078,7 +1078,7 @@ bool theory_str::is_concat_eq_type2(expr * concatAst1, expr * concatAst2) { return false; } -void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { +void theory_str::process_concat_eq_type2(expr * concatAst1, expr * concatAst2) { } @@ -1087,7 +1087,7 @@ bool theory_str::is_concat_eq_type3(expr * concatAst1, expr * concatAst2) { return false; } -void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { +void theory_str::process_concat_eq_type3(expr * concatAst1, expr * concatAst2) { } @@ -1096,7 +1096,7 @@ bool theory_str::is_concat_eq_type4(expr * concatAst1, expr * concatAst2) { return false; } -void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { +void theory_str::process_concat_eq_type4(expr * concatAst1, expr * concatAst2) { } @@ -1105,7 +1105,7 @@ bool theory_str::is_concat_eq_type5(expr * concatAst1, expr * concatAst2) { return false; } -void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { +void theory_str::process_concat_eq_type5(expr * concatAst1, expr * concatAst2) { } @@ -1114,7 +1114,7 @@ bool theory_str::is_concat_eq_type6(expr * concatAst1, expr * concatAst2) { return false; } -void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { +void theory_str::process_concat_eq_type6(expr * concatAst1, expr * concatAst2) { } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index b66eef4ad..5d0ec96db 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -24,6 +24,7 @@ Revision History: #include"smt_model_generator.h" #include"arith_decl_plugin.h" #include +#include namespace smt { @@ -71,8 +72,8 @@ namespace smt { int tmpXorVarCount; std::map, std::map > varForBreakConcat; - bool avoidLoopCut = true; - bool loopDetected = false; + bool avoidLoopCut; + bool loopDetected; std::map > cut_var_map; protected: void assert_axiom(expr * e); From ecb2116927eecd15ff2898cb03b42ffe77cfc8e9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 05:23:22 -0400 Subject: [PATCH 39/91] fix memory corruption bug caused by invalid use of delete[] --- src/smt/theory_str.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3458ec60c..071f07619 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1503,8 +1503,6 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } assert_implication(implyL, implyR1); } - delete[] xor_items; - delete[] and_items; } /* (arg1Len != 1 || arg2Len != 1) */ } /* if (Concat(arg1, arg2) == NULL) */ } From 5189c24d42c1fa8648bee09f2548e127d05ef294 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 05:45:16 -0400 Subject: [PATCH 40/91] fix theory of arithmetic complaints about wanting to write A > B "what could possibly go wrong?" --- src/smt/theory_str.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 071f07619..440aaeb9f 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -274,7 +274,8 @@ app * theory_str::mk_nonempty_str_var() { 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); + // we have to build !(LHS <= RHS) instead + app * lhs_gt_rhs = m.mk_not(m_autil.mk_le(len_str, zero)); SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } @@ -1008,8 +1009,18 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { 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))); + // TODO these are crashing the solvers because the integer theory + // expects a constant on the right-hand side. + // The things we want to assert here are len(m) > len(x) and len(y) > len(n). + // We rewrite A > B as A-B > 0 and then as not(A-B <= 0), + // and then, *because we aren't allowed to use subtraction*, + // as not(A + -1*B <= 0) + and_item[pos++] = ctx.mk_eq_atom(or_item[option], + mgr.mk_not(m_autil.mk_le(m_autil.mk_add(mk_strlen(m), + m_autil.mk_mul(mk_int(-1), mk_strlen(x))), mk_int(0))) ); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], + mgr.mk_not(m_autil.mk_le(m_autil.mk_add(mk_strlen(y), + m_autil.mk_mul(mk_int(-1), mk_strlen(n))), mk_int(0))) ); option++; From f8c13792a355f5af3505c77e6f9cf883794910ad Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 09:45:00 -0400 Subject: [PATCH 41/91] mark the position of the bug I found so I can recall it later in process_concat_eq_type1() line 1048 --- src/smt/theory_str.cpp | 35 +++++++++++++++++++++++------------ src/smt/theory_str.h | 2 ++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 440aaeb9f..aad15bec8 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1045,6 +1045,7 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { expr_ref m_plus_t2(m_autil.mk_add(mk_strlen(m), mk_strlen(t2)), mgr); + // TODO here is the bug: these EQs should be GTs 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))); @@ -1724,6 +1725,7 @@ void theory_str::init_search_eh() { * This is done to find equalities between terms, etc. that we otherwise * might not get a chance to see. */ + /* expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { @@ -1745,6 +1747,7 @@ void theory_str::init_search_eh() { << ": expr ignored" << std::endl;); } } + */ TRACE("t_str", tout << "search started" << std::endl;); search_started = true; @@ -1755,12 +1758,16 @@ void theory_str::new_eq_eh(theory_var x, theory_var y) { TRACE("t_str", tout << "new eq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); + + TRACE("t_str_detail", dump_assignments();); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { //TRACE("t_str_detail", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); TRACE("t_str", tout << "new diseq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); + + TRACE("t_str_detail", dump_assignments();); } void theory_str::relevant_eh(app * n) { @@ -1779,7 +1786,7 @@ 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(); + int sLevel = ctx.get_scope_level(); std::map >::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)) { @@ -1794,21 +1801,25 @@ void theory_str::pop_scope_eh(unsigned num_scopes) { } } +void theory_str::dump_assignments() { + ast_manager & m = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", + tout << "dumping all assignments:" << std::endl; + expr_ref_vector assignments(m); + ctx.get_assignments(assignments); + for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { + expr * ex = *i; + tout << mk_ismt2_pp(ex, m) << std::endl; + } + ); +} + final_check_status theory_str::final_check_eh() { - ast_manager & m = get_manager(); - context & ctx = get_context(); // TODO TRACE("t_str", tout << "final check" << std::endl;); - TRACE("t_str_detail", - tout << "dumping all assignments:" << std::endl; - expr_ref_vector assignments(m); - ctx.get_assignments(assignments); - for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { - expr * ex = *i; - tout << mk_ismt2_pp(ex, m) << std::endl; - } - ); + TRACE("t_str_detail", dump_assignments();); return FC_DONE; } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 5d0ec96db..930c8e9c8 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -136,6 +136,8 @@ namespace smt { bool new_eq_check(expr * lhs, expr * rhs); void group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts); + + void dump_assignments(); public: theory_str(ast_manager & m); virtual ~theory_str(); From fb5f3cbc136651a0c4113f7a857d84d3aad13dba Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 30 Sep 2015 11:41:55 -0400 Subject: [PATCH 42/91] fix greater-than bug now we just have to tweak model gen for internal variables --- src/smt/theory_str.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index aad15bec8..7514f8a23 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1016,11 +1016,13 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { // and then, *because we aren't allowed to use subtraction*, // as not(A + -1*B <= 0) and_item[pos++] = ctx.mk_eq_atom(or_item[option], - mgr.mk_not(m_autil.mk_le(m_autil.mk_add(mk_strlen(m), - m_autil.mk_mul(mk_int(-1), mk_strlen(x))), mk_int(0))) ); + mgr.mk_not(m_autil.mk_le( + m_autil.mk_add(mk_strlen(m), m_autil.mk_mul(mk_int(-1), mk_strlen(x))), + mk_int(0))) ); and_item[pos++] = ctx.mk_eq_atom(or_item[option], - mgr.mk_not(m_autil.mk_le(m_autil.mk_add(mk_strlen(y), - m_autil.mk_mul(mk_int(-1), mk_strlen(n))), mk_int(0))) ); + mgr.mk_not(m_autil.mk_le( + m_autil.mk_add(mk_strlen(y),m_autil.mk_mul(mk_int(-1), mk_strlen(n))), + mk_int(0))) ); option++; @@ -1045,10 +1047,16 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { expr_ref m_plus_t2(m_autil.mk_add(mk_strlen(m), mk_strlen(t2)), mgr); - // TODO here is the bug: these EQs should be GTs 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))); + // want len(x) > len(m) and len(n) > len(y) + and_item[pos++] = ctx.mk_eq_atom(or_item[option], + mgr.mk_not(m_autil.mk_le( + m_autil.mk_add(mk_strlen(x), m_autil.mk_mul(mk_int(-1), mk_strlen(m))), + mk_int(0))) ); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], + mgr.mk_not(m_autil.mk_le( + m_autil.mk_add(mk_strlen(n), m_autil.mk_mul(mk_int(-1), mk_strlen(y))), + mk_int(0))) ); option++; @@ -1759,7 +1767,7 @@ void theory_str::new_eq_eh(theory_var x, theory_var y) { mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); - TRACE("t_str_detail", dump_assignments();); + TRACE("t_str_dump_assign", dump_assignments();); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { @@ -1767,7 +1775,7 @@ void theory_str::new_diseq_eh(theory_var x, theory_var y) { TRACE("t_str", tout << "new diseq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); - TRACE("t_str_detail", dump_assignments();); + TRACE("t_str_dump_assign", dump_assignments();); } void theory_str::relevant_eh(app * n) { From bdf755156cd761c1247d83cbb034306455b45a28 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 1 Oct 2015 20:31:40 -0400 Subject: [PATCH 43/91] fix model generation: don't build interpretations for Length() --- src/ast/str_decl_plugin.cpp | 16 +++++++++- src/ast/str_decl_plugin.h | 4 +++ src/smt/theory_str.cpp | 60 ++++++++++++++++++++++++++++++++----- src/smt/theory_str.h | 9 +++++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index c72a5dbc2..5589db56c 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -64,7 +64,7 @@ void str_decl_plugin::set_manager(ast_manager * m, family_id id) { MK_OP(m_concat_decl, "Concat", OP_STRCAT, s); - m_length_decl = m->mk_func_decl(symbol("Length"), s, i); m_manager->inc_ref(m_length_decl); + m_length_decl = m->mk_func_decl(symbol("Length"), s, i, func_decl_info(id, OP_STRLEN)); m_manager->inc_ref(m_length_decl); } decl_plugin * str_decl_plugin::mk_fresh() { @@ -120,6 +120,20 @@ app * str_decl_plugin::mk_string(const char * val) { return mk_string(key); } +app * str_decl_plugin::mk_fresh_string() { + // cheating. + // take the longest string in the cache, append the letter "A", and call it fresh. + std::string longestString = ""; + std::map::iterator it = string_cache.begin(); + for (; it != string_cache.end(); ++it) { + if (it->first.length() > longestString.length()) { + longestString = it->first; + } + } + longestString += "A"; + return mk_string(longestString); +} + void str_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("Concat", OP_STRCAT)); op_names.push_back(builtin_name("Length", OP_STRLEN)); diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index 61d1bc2f2..f1978ab8b 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -62,6 +62,7 @@ public: app * mk_string(const char * val); app * mk_string(std::string & val); + app * mk_fresh_string(); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); @@ -97,6 +98,9 @@ public: app * mk_string(std::string & val) { return m_plugin->mk_string(val); } + app * mk_fresh_string() { + return m_plugin->mk_fresh_string(); + } // TODO }; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 7514f8a23..221f472d2 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -279,6 +279,11 @@ app * theory_str::mk_nonempty_str_var() { SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } + + // add 'a' to variable sets, so we can keep track of it + variable_set.insert(a); + internal_variable_set.insert(a); + return a; } @@ -1685,9 +1690,17 @@ void theory_str::set_up_axioms(expr * ex) { SASSERT(n); m_basicstr_axiom_todo.push_back(n); - // if additionally ex is a concatenation, set up concatenation axioms - if (is_app(ex) && is_concat(to_app(ex))) { - m_concat_axiom_todo.push_back(n); + + if (is_app(ex)) { + app * ap = to_app(ex); + if (is_concat(ap)) { + // if ex is a concat, set up concat axioms later + m_concat_axiom_todo.push_back(n); + } else if (ap->get_num_args() == 0 && !is_string(ap)) { + // if ex is a variable, add it to our list of variables + TRACE("t_str_detail", tout << "tracking variable" << std::endl;); + variable_set.insert(ex); + } } } else { TRACE("t_str_detail", tout << "setting up axioms for " << mk_ismt2_pp(ex, get_manager()) << @@ -1824,12 +1837,41 @@ void theory_str::dump_assignments() { } final_check_status theory_str::final_check_eh() { - // TODO - TRACE("t_str", tout << "final check" << std::endl;); + ast_manager & m = get_manager(); + context & ctx = get_context(); + TRACE("t_str", tout << "final check" << std::endl;); TRACE("t_str_detail", dump_assignments();); - return FC_DONE; + // Check every variable to see if it's eq. to some string constant. + // If not, mark it as free. + bool needToAssignFreeVars = false; + std::set free_variables; + for (std::set::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { + bool has_eqc_value = false; + get_eqc_value(*it, has_eqc_value); + if (!has_eqc_value) { + needToAssignFreeVars = true; + free_variables.insert(*it); + } + } + + if (!needToAssignFreeVars) { + TRACE("t_str", tout << "All variables are assigned. Done!" << std::endl;); + return FC_DONE; + } + + for (std::set::iterator it = free_variables.begin(); it != free_variables.end(); ++it) { + expr * var = *it; + if (internal_variable_set.find(var) != internal_variable_set.end()) { + TRACE("t_str", tout << "assigning arbitrary string to internal variable " << mk_ismt2_pp(var, m) << std::endl;); + app * val = m_strutil.mk_string("**unused**"); + assert_axiom(ctx.mk_eq_atom(var, val)); + } else { + NOT_IMPLEMENTED_YET(); // TODO free variable assignment from strTheory::cb_final_check() + } + } + return FC_CONTINUE; } void theory_str::init_model(model_generator & mg) { @@ -1899,7 +1941,11 @@ model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { if (val != NULL) { return alloc(expr_wrapper_proc, val); } else { - m.raise_exception("failed to find concrete value"); return NULL; + TRACE("t_str", tout << "WARNING: failed to find a concrete value, falling back" << std::endl;); + // TODO make absolutely sure the reason we can't find a concrete value is because of an unassigned temporary + // e.g. for an expression like (Concat X $$_str0) + //return alloc(expr_wrapper_proc, m_strutil.mk_string("")); + NOT_IMPLEMENTED_YET(); } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 930c8e9c8..1c2e2fbee 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -43,7 +43,11 @@ namespace smt { v2 = m_util.mk_string("value 2"); return true; } - virtual expr * get_fresh_value(sort * s) { NOT_IMPLEMENTED_YET(); } + virtual expr * get_fresh_value(sort * s) { + // TODO this may be causing crashes in model gen? investigate + //return m_util.mk_fresh_string(); + NOT_IMPLEMENTED_YET(); + } virtual void register_value(expr * n) { /* Ignore */ } }; @@ -75,6 +79,9 @@ namespace smt { bool avoidLoopCut; bool loopDetected; std::map > cut_var_map; + + std::set variable_set; + std::set internal_variable_set; protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); From 96d99dfb3888852cd1cb4521fb3513f7b9c7a817 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 2 Oct 2015 14:05:17 -0400 Subject: [PATCH 44/91] process_concat_eq_type2 implementation, not tested WIP --- src/smt/theory_str.cpp | 184 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 221f472d2..a6bdc4944 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1098,13 +1098,193 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { } // (splitType == -1) } +/************************************************************* + * Type 2: concat(x, y) = concat(m, "str") + *************************************************************/ bool theory_str::is_concat_eq_type2(expr * concatAst1, expr * concatAst2) { - // TODO - return false; + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if ((!m_strutil.is_string(v1_arg0)) && m_strutil.is_string(v1_arg1) + && (!m_strutil.is_string(v2_arg0)) && (!m_strutil.is_string(v2_arg1))) { + return true; + } else if ((!m_strutil.is_string(v2_arg0)) && m_strutil.is_string(v2_arg1) + && (!m_strutil.is_string(v1_arg0)) && (!m_strutil.is_string(v1_arg1))) { + return true; + } else { + return false; + } } void theory_str::process_concat_eq_type2(expr * concatAst1, expr * concatAst2) { + ast_manager & mgr = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", tout << "process_concat_eq TYPE 2" << std::endl + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << 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 = NULL; + expr * y = NULL; + expr * strAst = NULL; + expr * m = NULL; + + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if (m_strutil.is_string(v1_arg1) && !m_strutil.is_string(v2_arg1)) { + m = v1_arg0; + strAst = v1_arg1; + x = v2_arg0; + y = v2_arg1; + } else { + m = v2_arg0; + strAst = v2_arg1; + x = v1_arg0; + y = v1_arg1; + } + + const char * strValue_tmp = 0; + m_strutil.is_string(strAst, &strValue_tmp); + std::string strValue(strValue_tmp); + // TODO integer theory interaction + /* + int x_len = getLenValue(t, x); + int y_len = getLenValue(t, y); + int m_len = getLenValue(t, m); + int str_len = getLenValue(t, strAst); + */ + + int x_len = -1; + int y_len = -1; + int m_len = -1; + int str_len = -1; + + // setup + + expr * xorFlag = NULL; + expr * temp1 = NULL; + std::pair key1(concatAst1, concatAst2); + std::pair key2(concatAst2, concatAst1); + + if (varForBreakConcat.find(key1) == varForBreakConcat.end() + && varForBreakConcat.find(key2) == varForBreakConcat.end()) { + temp1 = mk_nonempty_str_var(); + xorFlag = mk_internal_xor_var(); + varForBreakConcat[key1][0] = temp1; + varForBreakConcat[key1][1] = xorFlag; + } else { + if (varForBreakConcat.find(key1) != varForBreakConcat.end()) { + temp1 = varForBreakConcat[key1][0]; + xorFlag = varForBreakConcat[key1][1]; + } else if (varForBreakConcat.find(key2) != varForBreakConcat.end()) { + temp1 = varForBreakConcat[key2][0]; + xorFlag = varForBreakConcat[key2][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 && str_len != -1) { + if (y_len > str_len) + splitType = 0; + else if (y_len == str_len) + splitType = 1; + else + splitType = 2; + } + + TRACE("t_str_detail", tout << "Split type " << splitType << std::endl;); + + // Provide fewer split options when 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 { + // Split type -1: no idea about the length... + int optionTotal = 2 + strValue.length(); + expr ** or_item = alloc_svect(expr*, optionTotal); + expr ** and_item = alloc_svect(expr*, (1 + 6 + 4 * (strValue.length() + 1))); + int option = 0; + int pos = 1; + + expr_ref temp1_strAst(mk_concat(temp1, strAst), mgr); // TODO assert concat axioms? + + // m cuts y + if (can_two_nodes_eq(y, temp1_strAst)) { + if (!avoidLoopCut || !has_self_cut(m, y)) { + // break down option 2-1 + // TODO + or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option)); + expr_ref x_temp1(mk_concat(x, temp1), mgr); // TODO assert concat axioms? + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(m, x_temp1)); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, temp1_strAst)); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(m), + m_autil.mk_add(mk_strlen(x), mk_strlen(temp1)))); + + ++option; + add_cut_info_merge(temp1, ctx.get_scope_level(), y); + add_cut_info_merge(temp1, ctx.get_scope_level(), m); + } else { + loopDetected = true; + TRACE("t_str", tout << "AVOID LOOP: SKIPPED" << std::endl;); + // TODO printCutVar(m, y) + } + } + + for (int i = 0; i <= (int)strValue.size(); ++i) { + std::string part1Str = strValue.substr(0, i); + std::string part2Str = strValue.substr(i, strValue.size() - i); + expr_ref prefixStr(m_strutil.mk_string(part1Str), mgr); + expr_ref x_concat(mk_concat(m, prefixStr), mgr); // TODO concat axioms? + expr_ref cropStr(m_strutil.mk_string(part2Str), mgr); + if (can_two_nodes_eq(x, x_concat) && can_two_nodes_eq(y, cropStr)) { + // break down option 2-2 + 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, x_concat)); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, cropStr)); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(y), mk_int(part2Str.length()))); + ++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 implyR(mgr.mk_and(pos, and_item), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } else { + TRACE("t_str", tout << "STOP: Should not split two EQ concats." << std::endl;); + } + } // (splitType == -1) } bool theory_str::is_concat_eq_type3(expr * concatAst1, expr * concatAst2) { From ff4706dd40b3334724d81b385ed2cad1fcc0f8ba Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 3 Oct 2015 12:07:55 -0400 Subject: [PATCH 45/91] process_concat_eq_type3 still wip because i'm just trying to get these all done --- src/smt/theory_str.cpp | 199 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index a6bdc4944..7b555d6bb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -892,6 +892,10 @@ void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) { } +/************************************************************* + * Type 1: concat(x, y) = concat(m, n) + * x, y, m and n all variables + *************************************************************/ 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); @@ -1287,12 +1291,203 @@ void theory_str::process_concat_eq_type2(expr * concatAst1, expr * concatAst2) { } // (splitType == -1) } +/************************************************************* + * Type 3: concat(x, y) = concat("str", n) + *************************************************************/ bool theory_str::is_concat_eq_type3(expr * concatAst1, expr * concatAst2) { - // TODO - return false; + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if (m_strutil.is_string(v1_arg0) && (!m_strutil.is_string(v1_arg1)) + && (!m_strutil.is_string(v2_arg0)) && (!m_strutil.is_string(v2_arg1))) { + return true; + } else if (m_strutil.is_string(v2_arg0) && (!m_strutil.is_string(v2_arg1)) + && (!m_strutil.is_string(v1_arg0)) && (!m_strutil.is_string(v1_arg1))) { + return true; + } else { + return false; + } } void theory_str::process_concat_eq_type3(expr * concatAst1, expr * concatAst2) { + ast_manager & mgr = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", tout << "process_concat_eq TYPE 3" << std::endl + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << 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 * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + expr * x = NULL; + expr * y = NULL; + expr * strAst = NULL; + expr * n = NULL; + + if (m_strutil.is_string(v1_arg0) && !m_strutil.is_string(v2_arg0)) { + strAst = v1_arg0; + n = v1_arg1; + x = v2_arg0; + y = v2_arg1; + } else { + strAst = v2_arg0; + n = v2_arg1; + x = v1_arg0; + y = v1_arg1; + } + + const char * strValue_tmp = 0; + m_strutil.is_string(strAst, &strValue_tmp); + std::string strValue(strValue_tmp); + // TODO integer theory interaction + /* + int x_len = getLenValue(t, x); + int y_len = getLenValue(t, y); + int str_len = getLenValue(t, strAst); + int n_len = getLenValue(t, n); + */ + int x_len = -1; + int y_len = -1; + int str_len = -1; + int n_len = -1; + + expr_ref xorFlag(mgr); + expr_ref temp1(mgr); + std::pair key1(concatAst1, concatAst2); + std::pair key2(concatAst2, concatAst1); + if (varForBreakConcat.find(key1) == varForBreakConcat.end() && varForBreakConcat.find(key2) == varForBreakConcat.end()) { + temp1 = mk_nonempty_str_var(); + xorFlag = mk_internal_xor_var(); + + varForBreakConcat[key1][0] = temp1; + varForBreakConcat[key1][1] = xorFlag; + } else { + if (varForBreakConcat.find(key1) != varForBreakConcat.end()) { + temp1 = varForBreakConcat[key1][0]; + xorFlag = varForBreakConcat[key1][1]; + } else if (varForBreakConcat.find(key2) != varForBreakConcat.end()) { + temp1 = varForBreakConcat[key2][0]; + xorFlag = varForBreakConcat[key2][1]; + } + } + + + + int splitType = -1; + if (x_len != -1) { + if (x_len < str_len) + splitType = 0; + else if (x_len == str_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;); + + // Provide fewer split options when 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 { + // Split type -1. We know nothing about the length... + + int optionTotal = 2 + strValue.length(); + expr ** or_item = alloc_svect(expr*, optionTotal); + int option = 0; + expr ** and_item = alloc_svect(expr*, (2 + 4 * optionTotal)); + int pos = 1; + for (int i = 0; i <= (int) strValue.size(); i++) { + std::string part1Str = strValue.substr(0, i); + std::string part2Str = strValue.substr(i, strValue.size() - i); + expr_ref cropStr(m_strutil.mk_string(part1Str), mgr); + expr_ref suffixStr(m_strutil.mk_string(part2Str), mgr); + expr_ref y_concat(mk_concat(suffixStr, n), mgr); // TODO concat axioms? + + if (can_two_nodes_eq(x, cropStr) && can_two_nodes_eq(y, y_concat)) { + // break down option 3-1 + expr_ref x_eq_str(ctx.mk_eq_atom(x, cropStr), mgr); + or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option)); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], x_eq_str); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, y_concat)); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(x), mk_strlen(cropStr))); + // and_item[pos++] = Z3_mk_eq(ctx, or_item[option], Z3_mk_eq(ctx, mk_length(t, y), mk_length(t, y_concat))); + + // adding length constraint for _ = constStr seems slowing things down. + option++; + } + } + + expr_ref strAst_temp1(mk_concat(strAst, temp1), mgr); + + + //-------------------------------------------------------- + // x cut n + //-------------------------------------------------------- + if (can_two_nodes_eq(x, strAst_temp1)) { + if (!avoidLoopCut || !(has_self_cut(x, n))) { + // break down option 3-2 + or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option)); + + expr_ref temp1_y(mk_concat(temp1, y), mgr); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(x, strAst_temp1)); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(n, temp1_y)); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(x), + m_autil.mk_add(mk_strlen(strAst), mk_strlen(temp1)) )); + option++; + + add_cut_info_merge(temp1, ctx.get_scope_level(), x); + add_cut_info_merge(temp1, ctx.get_scope_level(), n); + } else { + loopDetected = true; + TRACE("t_str", tout << "AVOID LOOP: SKIPPED." << std::endl;); + // TODO printCutVAR(x, n) + } + } + + + if (option > 0) { + if (option == 1) { + and_item[0] = or_item[0]; + } else { + and_item[0] = mgr.mk_or(option, or_item); + } + expr_ref implyR(mgr.mk_and(pos, and_item), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } else { + TRACE("t_str", tout << "STOP: should not split two eq. concats" << std::endl;); + } + } } From f7bc785a56f1bd4dade92a5812a843b634500fc5 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 3 Oct 2015 12:19:55 -0400 Subject: [PATCH 46/91] process_concat_eq_type4, still WIP not tested --- src/smt/theory_str.cpp | 82 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 7b555d6bb..5033ca978 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1491,13 +1491,91 @@ void theory_str::process_concat_eq_type3(expr * concatAst1, expr * concatAst2) { } +/************************************************************* + * Type 4: concat("str1", y) = concat("str2", n) + *************************************************************/ bool theory_str::is_concat_eq_type4(expr * concatAst1, expr * concatAst2) { - // TODO - return false; + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if (m_strutil.is_string(v1_arg0) && (!m_strutil.is_string(v1_arg1)) + && m_strutil.is_string(v2_arg0) && (!m_strutil.is_string(v2_arg1))) { + return true; + } else { + return false; + } } void theory_str::process_concat_eq_type4(expr * concatAst1, expr * concatAst2) { + ast_manager & mgr = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", tout << "process_concat_eq TYPE 4" << std::endl + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << 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 * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + expr * str1Ast = v1_arg0; + expr * y = v1_arg1; + expr * str2Ast = v2_arg0; + expr * n = v2_arg1; + + const char *tmp = 0; + m_strutil.is_string(str1Ast, &tmp); + std::string str1Value(tmp); + m_strutil.is_string(str2Ast, &tmp); + std::string str2Value(tmp); + + int str1Len = str1Value.length(); + int str2Len = str2Value.length(); + + int commonLen = (str1Len > str2Len) ? str2Len : str1Len; + if (str1Value.substr(0, commonLen) != str2Value.substr(0, commonLen)) { + TRACE("t_str_detail", tout << "Conflict: " << mk_ismt2_pp(concatAst1, mgr) + << " has no common prefix with " << mk_ismt2_pp(concatAst2, mgr) << std::endl;); + expr_ref toNegate(mgr.mk_not(ctx.mk_eq_atom(concatAst1, concatAst2)), mgr); + assert_axiom(toNegate); + return; + } else { + if (str1Len > str2Len) { + std::string deltaStr = str1Value.substr(str2Len, str1Len - str2Len); + expr_ref tmpAst(mk_concat(m_strutil.mk_string(deltaStr), y), mgr); + if (!in_same_eqc(tmpAst, n)) { + // break down option 4-1 + expr_ref implyR(ctx.mk_eq_atom(n, tmpAst), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } else if (str1Len == str2Len) { + if (!in_same_eqc(n, y)) { + //break down option 4-2 + expr_ref implyR(ctx.mk_eq_atom(n, y), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } else { + std::string deltaStr = str2Value.substr(str1Len, str2Len - str1Len); + expr_ref tmpAst(mk_concat(m_strutil.mk_string(deltaStr), n), mgr); + if (!in_same_eqc(y, tmpAst)) { + //break down option 4-3 + expr_ref implyR(ctx.mk_eq_atom(y, tmpAst), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } + } } bool theory_str::is_concat_eq_type5(expr * concatAst1, expr * concatAst2) { From be7972338224e149356632e96171cc18b74112c4 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 3 Oct 2015 12:26:30 -0400 Subject: [PATCH 47/91] process_concat_eq_type5 wip --- src/smt/theory_str.cpp | 80 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 5033ca978..f052d293a 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1578,13 +1578,89 @@ void theory_str::process_concat_eq_type4(expr * concatAst1, expr * concatAst2) { } } +/************************************************************* + * case 5: concat(x, "str1") = concat(m, "str2") + *************************************************************/ bool theory_str::is_concat_eq_type5(expr * concatAst1, expr * concatAst2) { - // TODO - return false; + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if ((!m_strutil.is_string(v1_arg0)) && m_strutil.is_string(v1_arg1) + && (!m_strutil.is_string(v2_arg0)) && m_strutil.is_string(v2_arg1)) { + return true; + } else { + return false; + } } void theory_str::process_concat_eq_type5(expr * concatAst1, expr * concatAst2) { + ast_manager & mgr = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", tout << "process_concat_eq TYPE 5" << std::endl + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << 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 * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + expr * x = v1_arg0; + expr * str1Ast = v1_arg1; + expr * m = v2_arg0; + expr * str2Ast = v2_arg1; + + const char *tmp = 0; + m_strutil.is_string(str1Ast, &tmp); + std::string str1Value(tmp); + m_strutil.is_string(str2Ast, &tmp); + std::string str2Value(tmp); + + int str1Len = str1Value.length(); + int str2Len = str2Value.length(); + + int cLen = (str1Len > str2Len) ? str2Len : str1Len; + if (str1Value.substr(str1Len - cLen, cLen) != str2Value.substr(str2Len - cLen, cLen)) { + TRACE("t_str_detail", tout << "Conflict: " << mk_ismt2_pp(concatAst1, mgr) + << " has no common suffix with " << mk_ismt2_pp(concatAst2, mgr) << std::endl;); + expr_ref toNegate(mgr.mk_not(ctx.mk_eq_atom(concatAst1, concatAst2)), mgr); + assert_axiom(toNegate); + return; + } else { + if (str1Len > str2Len) { + std::string deltaStr = str1Value.substr(0, str1Len - str2Len); + expr_ref x_deltaStr(mk_concat(x, m_strutil.mk_string(deltaStr)), mgr); + if (!in_same_eqc(m, x_deltaStr)) { + expr_ref implyR(ctx.mk_eq_atom(m, x_deltaStr), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } else if (str1Len == str2Len) { + // test + if (!in_same_eqc(x, m)) { + expr_ref implyR(ctx.mk_eq_atom(x, m), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } else { + std::string deltaStr = str2Value.substr(0, str2Len - str1Len); + expr_ref m_deltaStr(mk_concat(m, m_strutil.mk_string(deltaStr)), mgr); + if (!in_same_eqc(x, m_deltaStr)) { + expr_ref implyR(ctx.mk_eq_atom(x, m_deltaStr), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); + } + } + } } bool theory_str::is_concat_eq_type6(expr * concatAst1, expr * concatAst2) { From 6791db64c01ac7dfbac7c0c6a883be9692e513da Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 3 Oct 2015 13:34:42 -0400 Subject: [PATCH 48/91] process_concat_eq_type6 that's the last one! --- src/smt/theory_str.cpp | 162 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index f052d293a..aebaec572 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -20,6 +20,7 @@ Revision History: #include"smt_model_generator.h" #include"ast_pp.h" #include"ast_ll_pp.h" +#include namespace smt { @@ -1663,13 +1664,170 @@ void theory_str::process_concat_eq_type5(expr * concatAst1, expr * concatAst2) { } } +/************************************************************* + * case 6: concat("str1", y) = concat(m, "str2") + *************************************************************/ bool theory_str::is_concat_eq_type6(expr * concatAst1, expr * concatAst2) { - // TODO - return false; + expr * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + if (m_strutil.is_string(v1_arg0) && (!m_strutil.is_string(v1_arg1)) + && (!m_strutil.is_string(v2_arg0)) && m_strutil.is_string(v2_arg1)) { + return true; + } else if (m_strutil.is_string(v2_arg0) && (!m_strutil.is_string(v2_arg1)) + && (!m_strutil.is_string(v1_arg0)) && m_strutil.is_string(v1_arg1)) { + return true; + } else { + return false; + } } void theory_str::process_concat_eq_type6(expr * concatAst1, expr * concatAst2) { + ast_manager & mgr = get_manager(); + context & ctx = get_context(); + TRACE("t_str_detail", tout << "process_concat_eq TYPE 6" << std::endl + << "concatAst1 = " << mk_ismt2_pp(concatAst1, mgr) << std::endl + << "concatAst2 = " << mk_ismt2_pp(concatAst2, mgr) << 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 * v1_arg0 = to_app(concatAst1)->get_arg(0); + expr * v1_arg1 = to_app(concatAst1)->get_arg(1); + expr * v2_arg0 = to_app(concatAst2)->get_arg(0); + expr * v2_arg1 = to_app(concatAst2)->get_arg(1); + + + expr * str1Ast = NULL; + expr * y = NULL; + expr * m = NULL; + expr * str2Ast = NULL; + + if (m_strutil.is_string(v1_arg0)) { + str1Ast = v1_arg0; + y = v1_arg1; + m = v2_arg0; + str2Ast = v2_arg1; + } else { + str1Ast = v2_arg0; + y = v2_arg1; + m = v1_arg0; + str2Ast = v1_arg1; + } + + const char *tmp = 0; + m_strutil.is_string(str1Ast, &tmp); + std::string str1Value(tmp); + m_strutil.is_string(str2Ast, &tmp); + std::string str2Value(tmp); + + int str1Len = str1Value.length(); + int str2Len = str2Value.length(); + + //---------------------------------------- + //(a) |---str1---|----y----| + // |--m--|-----str2-----| + // + //(b) |---str1---|----y----| + // |-----m----|--str2---| + // + //(c) |---str1---|----y----| + // |------m------|-str2-| + //---------------------------------------- + + std::list overlapLen; + overlapLen.push_back(0); + + for (int i = 1; i <= str1Len && i <= str2Len; i++) { + if (str1Value.substr(str1Len - i, i) == str2Value.substr(0, i)) + overlapLen.push_back(i); + } + + //---------------------------------------------------------------- + expr * commonVar = NULL; + expr * xorFlag = NULL; + std::pair key1(concatAst1, concatAst2); + std::pair key2(concatAst2, concatAst1); + if (varForBreakConcat.find(key1) == varForBreakConcat.end() && varForBreakConcat.find(key2) == varForBreakConcat.end()) { + commonVar = mk_nonempty_str_var(); + xorFlag = mk_internal_xor_var(); + varForBreakConcat[key1][0] = commonVar; + varForBreakConcat[key1][1] = xorFlag; + } else { + if (varForBreakConcat.find(key1) != varForBreakConcat.end()) { + commonVar = varForBreakConcat[key1][0]; + xorFlag = varForBreakConcat[key1][1]; + } else { + commonVar = varForBreakConcat[key2][0]; + xorFlag = varForBreakConcat[key2][1]; + } + } + + expr ** or_item = alloc_svect(expr*, (overlapLen.size() + 1)); + int option = 0; + expr ** and_item = alloc_svect(expr*, (1 + 4 * (overlapLen.size() + 1))); + int pos = 1; + + if (!avoidLoopCut || !has_self_cut(m, y)) { + or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option)); + + expr_ref str1_commonVar(mk_concat(str1Ast, commonVar), mgr); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(m, str1_commonVar)); + + expr_ref commonVar_str2(mk_concat(commonVar, str2Ast), mgr); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(y, commonVar_str2)); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(m), + m_autil.mk_add(mk_strlen(str1Ast), mk_strlen(commonVar)) )); + + // addItems[0] = mk_length(t, commonVar); + // addItems[1] = mk_length(t, str2Ast); + // and_item[pos++] = Z3_mk_eq(ctx, or_item[option], Z3_mk_eq(ctx, mk_length(t, y), Z3_mk_add(ctx, 2, addItems))); + + option++; + } else { + loopDetected = true; + TRACE("t_str", tout << "AVOID LOOP: SKIPPED." << std::endl;); + // TODO printCutVAR(m, y) + } + + for (std::list::iterator itor = overlapLen.begin(); itor != overlapLen.end(); itor++) { + int overLen = *itor; + std::string prefix = str1Value.substr(0, str1Len - overLen); + std::string suffix = str2Value.substr(overLen, str2Len - overLen); + or_item[option] = ctx.mk_eq_atom(xorFlag, mk_int(option)); + + expr_ref prefixAst(m_strutil.mk_string(prefix), mgr); + expr_ref x_eq_prefix(ctx.mk_eq_atom(m, prefixAst), mgr); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], x_eq_prefix); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], + ctx.mk_eq_atom(mk_strlen(m), mk_strlen(prefixAst))); + + // adding length constraint for _ = constStr seems slowing things down. + + expr_ref suffixAst(m_strutil.mk_string(suffix), mgr); + expr_ref y_eq_suffix(ctx.mk_eq_atom(y, suffixAst), mgr); + and_item[pos++] = ctx.mk_eq_atom(or_item[option], y_eq_suffix); + + and_item[pos++] = ctx.mk_eq_atom(or_item[option], ctx.mk_eq_atom(mk_strlen(y), mk_strlen(suffixAst))); + + option++; + } + + // case 6: concat("str1", y) = concat(m, "str2") + and_item[0] = mgr.mk_or(option, or_item); + expr_ref implyR(mgr.mk_and(pos, and_item), mgr); + assert_implication(ctx.mk_eq_atom(concatAst1, concatAst2), implyR); } /* From b494804c9c75b083767dd1a63d75f1abf8ebc717 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 6 Oct 2015 19:31:26 -0400 Subject: [PATCH 49/91] ignore tests dir --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 97ca67cf4..b4a69f69e 100644 --- a/.gitignore +++ b/.gitignore @@ -77,4 +77,7 @@ doc/code # reference code for z3str2 Z3-str Z3-str/** +# test cases +tests +tests/** From e521ab2c3af04c7c4b4082a2ffc81b0c6caf864a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 18 Oct 2015 19:39:55 -0400 Subject: [PATCH 50/91] fix concat_axiom loop in propagate(): compare against size()...... --- src/smt/theory_str.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index aebaec572..47165997d 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -376,7 +376,7 @@ void theory_str::propagate() { } m_str_eq_todo.reset(); - for (unsigned i = 0; i < m_concat_axiom_todo.empty(); ++i) { + for (unsigned i = 0; i < m_concat_axiom_todo.size(); ++i) { instantiate_concat_axiom(m_concat_axiom_todo[i]); } m_concat_axiom_todo.reset(); From 3ee8f27588bcb88220f0ff515526ee0003716c43 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 18 Oct 2015 20:20:09 -0400 Subject: [PATCH 51/91] possibly fix internalization bug mentioned in #2 (this leads to a not-implemented-yet in final_check_eh() due to missing code surrounding free variable production) --- src/smt/theory_str.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 47165997d..15253bcfd 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1837,6 +1837,8 @@ void theory_str::process_concat_eq_type6(expr * concatAst1, expr * concatAst2) { */ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { context & ctx = get_context(); + // I hope this works + ctx.internalize(n, false); enode * nNode = ctx.get_enode(n); enode * eqcNode = nNode; do { From c08f4371f4b9b0f98001eae4c111dbe91cf53d8f Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 21 Oct 2015 21:32:38 -0400 Subject: [PATCH 52/91] begin model generation, wip --- src/smt/theory_str.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++ src/smt/theory_str.h | 5 +++ 2 files changed, 102 insertions(+) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 15253bcfd..3e6b637d1 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2525,6 +2525,88 @@ void theory_str::dump_assignments() { ); } +// NOTE: this function used to take an argument `Z3_ast node`; +// it was not used and so was removed from the signature +void theory_str::classify_ast_by_type_in_positive_context(std::map & varMap, + std::map & concatMap, std::map & unrollMap) { + + context & ctx = get_context(); + ast_manager & m = get_manager(); + expr_ref_vector assignments(m); + ctx.get_assignments(assignments); + + for (expr_ref_vector::iterator it = assignments.begin(); it != assignments.end(); ++it) { + expr * argAst = *it; + // TODO + NOT_IMPLEMENTED_YET(); + /* + * according to getNodeType(), the following things are considered "functions": + * Contains, StartsWith, EndsWith, RegexIn + * Length, Indexof, Indexof2, LastIndexof + * Concat, SubString, Replace, Unroll, CharAt + * RegexConcat, RegexStar, RegexPlus, RegexCharRange, RegexUnion, Str2Reg + * something about Z3_ARRAY_SORT? + * Z3 native functions that aren't considered "uninterpreted" + * "real" uninterpreted functions declared in the input (domainSize != 0) + */ + + /* + if (getNodeType(t, argAst) == my_Z3_Func) { + Z3_app func_app = Z3_to_app(ctx, argAst); + Z3_decl_kind func_decl = Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, func_app)); + + if (isInterestingFuncKind(func_decl)) { + classifyAstByType(t, argAst, varMap, concatMap, unrollMap); + } + } + */ + } +} + +/* + * Dependence analysis from current context assignment + * - "freeVarMap" contains a set of variables that doesn't constrained by Concats. + * But it's possible that it's bounded by unrolls + * For the case of + * (1) var1 = unroll(r1, t1) + * var1 is in the freeVarMap + * > should unroll r1 for var1 + * (2) var1 = unroll(r1, t1) /\ var1 = Concat(var2, var3) + * var2, var3 are all in freeVar + * > should split the unroll function so that var2 and var3 are bounded by new unrolls + */ +int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map & freeVarMap, + std::map > & unrollGroupMap) { + std::map concatMap; + std::map unrollMap; + std::map aliasIndexMap; + std::map var_eq_constStr_map; + std::map concat_eq_constStr_map; + std::map > var_eq_concat_map; + std::map > var_eq_unroll_map; + std::map > concat_eq_concat_map; + std::map > depMap; + + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // note that the old API concatenated these assignments into + // a massive conjunction; we may have the opportunity to avoid that here + expr_ref_vector assignments(m); + ctx.get_assignments(assignments); + + // Step 1: get variables / concat AST appearing in the context + // TODO build this map; see strTheory::checkInputVar() + // it should just be variable_set - internal_variable_set? + for(std::map::iterator it = inputVarMap.begin(); it != inputVarMap.end(); ++it) { + strVarMap[it->first] = 1; + } + classify_ast_by_type_in_positive_context(assignments, strVarMap, concatMap, unrollMap); + + // TODO the rest + NOT_IMPLEMENTED_YET(); +} + final_check_status theory_str::final_check_eh() { ast_manager & m = get_manager(); context & ctx = get_context(); @@ -2532,6 +2614,20 @@ final_check_status theory_str::final_check_eh() { TRACE("t_str", tout << "final check" << std::endl;); TRACE("t_str_detail", dump_assignments();); + // run dependence analysis to find free string variables + std::map varAppearInAssign; + std::map freeVar_map; + std::map > unrollGroup_map; + int conflictInDep = ctx_dep_analysis(varAppearInAssign, freeVar_map, unrollGroup_map); + if (conflictInDep == -1) { + // return Z3_TRUE; + return FC_DONE; + } + + // TODO the rest... + NOT_IMPLEMENTED_YET(); + + /* // Check every variable to see if it's eq. to some string constant. // If not, mark it as free. bool needToAssignFreeVars = false; @@ -2561,6 +2657,7 @@ final_check_status theory_str::final_check_eh() { } } return FC_CONTINUE; + */ } void theory_str::init_model(model_generator & mg) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 1c2e2fbee..80e321729 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -144,6 +144,11 @@ namespace smt { bool new_eq_check(expr * lhs, expr * rhs); void group_terms_by_eqc(expr * n, std::set & concats, std::set & vars, std::set & consts); + int ctx_dep_analysis(std::map & strVarMap, std::map & freeVarMap, + std::map > & unrollGroupMap); + void classify_ast_by_type_in_positive_context(std::map & varMap, + std::map & concatMap, std::map & unrollMap) + void dump_assignments(); public: theory_str(ast_manager & m); From 1f3c5cebbf660a52aebae291846a65a021c30520 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 26 Oct 2015 15:43:31 -0400 Subject: [PATCH 53/91] variable classification (WIP) --- src/smt/theory_str.cpp | 82 ++++++++++++++++++++++++++++++------------ src/smt/theory_str.h | 6 +++- 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3e6b637d1..86aaaaf44 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2525,6 +2525,57 @@ void theory_str::dump_assignments() { ); } +void theory_str::classify_ast_by_type(expr * node, std::map & varMap, + std::map & concatMap, std::map & unrollMap) { + + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // check whether the node is a non-internal string variable; + // testing set membership here bypasses several expensive checks + if (variable_set.find(node) != variable_set.end() + && internal_variable_set.find(node) == internal_variable_set.end()) { + varMap[node] = 1; + } + // check whether the node is a function that we want to inspect + else if (is_app(node)) { // TODO + app * aNode = to_app(node); + if (is_strlen(aNode)) { + // Length + return; + } else if (is_concat(aNode)) { + expr * arg0 = aNode->get_arg(0); + expr * arg1 = aNode->get_arg(1); + bool arg0HasEq = false; + bool arg1HasEq = false; + expr * arg0Val = get_eqc_value(arg0, arg0HasEq); + expr * arg1Val = get_eqc_value(arg1, arg1HasEq); + + int canskip = 0; + if (arg0HasEq && arg0Val == m_strutil.mk_string("")) { + canskip = 1; + } + if (canskip == 0 && arg1HasEq && arg1Val == m_strutil.mk_string("")) { + canskip = 1; + } + if (canskip == 0 && concatMap.find(node) == concatMap.end()) { + concatMap[node] = 1; + } + } else if (false) { // TODO is_unroll() + // Unroll + if (unrollMap.find(node) == unrollMap.end()) { + unrollMap[node] = 1; + } + } + // recursively visit all arguments + app * aNode = to_app(node); + for (unsigned i = 0; i < aNode->get_num_args(); ++i) { + expr * arg = aNode->get_arg(i); + classify_ast_by_type(arg, varMap, concatMap, unrollMap); + } + } +} + // NOTE: this function used to take an argument `Z3_ast node`; // it was not used and so was removed from the signature void theory_str::classify_ast_by_type_in_positive_context(std::map & varMap, @@ -2537,29 +2588,14 @@ void theory_str::classify_ast_by_type_in_positive_context(std::map & for (expr_ref_vector::iterator it = assignments.begin(); it != assignments.end(); ++it) { expr * argAst = *it; - // TODO - NOT_IMPLEMENTED_YET(); - /* - * according to getNodeType(), the following things are considered "functions": - * Contains, StartsWith, EndsWith, RegexIn - * Length, Indexof, Indexof2, LastIndexof - * Concat, SubString, Replace, Unroll, CharAt - * RegexConcat, RegexStar, RegexPlus, RegexCharRange, RegexUnion, Str2Reg - * something about Z3_ARRAY_SORT? - * Z3 native functions that aren't considered "uninterpreted" - * "real" uninterpreted functions declared in the input (domainSize != 0) - */ + // the original code jumped through some hoops to check whether the AST node + // is a function, then checked whether that function is "interesting". + // however, the only thing that's considered "interesting" is an equality predicate. + // so we bypass a huge amount of work by doing the following... - /* - if (getNodeType(t, argAst) == my_Z3_Func) { - Z3_app func_app = Z3_to_app(ctx, argAst); - Z3_decl_kind func_decl = Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, func_app)); - - if (isInterestingFuncKind(func_decl)) { - classifyAstByType(t, argAst, varMap, concatMap, unrollMap); - } + if (m.is_eq(argAst)) { + classify_ast_by_type(argAst, varMap, concatMap, unrollMap); } - */ } } @@ -2598,10 +2634,10 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map::iterator it = inputVarMap.begin(); it != inputVarMap.end(); ++it) { + for(std::map::iterator it = input_var_map.begin(); it != input_var_map.end(); ++it) { strVarMap[it->first] = 1; } - classify_ast_by_type_in_positive_context(assignments, strVarMap, concatMap, unrollMap); + classify_ast_by_type_in_positive_context(strVarMap, concatMap, unrollMap); // TODO the rest NOT_IMPLEMENTED_YET(); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 80e321729..684526602 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -104,6 +104,8 @@ namespace smt { bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } bool is_string(app const * a) const { return a->is_app_of(get_id(), OP_STR); } bool is_string(enode const * n) const { return is_string(n->get_owner()); } + bool is_strlen(app const * a) const { return a->is_app_of(get_id(), OP_STRLEN); } + bool is_strlen(enode const * n) const { return is_strlen(n->get_owner()); } void instantiate_concat_axiom(enode * cat); void instantiate_basic_string_axioms(enode * str); void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs); @@ -146,8 +148,10 @@ namespace smt { int ctx_dep_analysis(std::map & strVarMap, std::map & freeVarMap, std::map > & unrollGroupMap); + void classify_ast_by_type(expr * node, std::map & varMap, + std::map & concatMap, std::map & unrollMap); void classify_ast_by_type_in_positive_context(std::map & varMap, - std::map & concatMap, std::map & unrollMap) + std::map & concatMap, std::map & unrollMap); void dump_assignments(); public: From 9f01b9dc92203f8cdefa7cd9befe43da6c6565f6 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 4 Nov 2015 16:22:06 -0500 Subject: [PATCH 54/91] more progress on model gen (WIP) --- src/smt/theory_str.cpp | 203 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 86aaaaf44..c5ca630f9 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2632,13 +2632,208 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map::iterator it = input_var_map.begin(); it != input_var_map.end(); ++it) { - strVarMap[it->first] = 1; + // the thing we iterate over should just be variable_set - internal_variable_set + // so we avoid computing the set difference (but this might be slower) + for(std::set::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { + expr* var = *it; + if (internal_variable_set.find(var) == internal_variable_set.end()) { + strVarMap[*it] = 1; + } } classify_ast_by_type_in_positive_context(strVarMap, concatMap, unrollMap); + // TODO unroll() + /* + std::map aliasUnrollSet; + std::map::iterator unrollItor = unrollMap.begin(); + for (; unrollItor != unrollMap.end(); unrollItor++) { + if (aliasUnrollSet.find(unrollItor->first) != aliasUnrollSet.end()) + continue; + Z3_ast aRoot = NULL; + Z3_ast curr = unrollItor->first; + do { + if (isUnrollFunc(t, curr)) { + if (aRoot == NULL) { + aRoot = curr; + } + aliasUnrollSet[curr] = aRoot; + } + curr = Z3_theory_get_eqc_next(t, curr); + } while (curr != unrollItor->first); + } + + for (unrollItor = unrollMap.begin(); unrollItor != unrollMap.end(); unrollItor++) { + Z3_ast unrFunc = unrollItor->first; + Z3_ast urKey = aliasUnrollSet[unrFunc]; + unrollGroupMap[urKey].insert(unrFunc); + } + */ + + // Step 2: collect alias relation + // e.g. suppose we have the equivalence class {x, y, z}; + // then we set aliasIndexMap[y] = x + // and aliasIndexMap[z] = x + + std::map::iterator varItor = strVarMap.begin(); + for (; varItor != strVarMap.end(); ++varItor) { + if (aliasIndexMap.find(varItor->first) != aliasIndexMap.end()) { + continue; + } + expr * aRoot = NULL; + expr * curr = varItor->first; + do { + if (variable_set.find(curr) != variable_set.end()) { // TODO internal_variable_set? + if (aRoot == NULL) { + aRoot = curr; + } else { + aliasIndexMap[curr] = aRoot; + } + } + // curr = get_eqc_next(curr); + enode * eqcNode = ctx.get_enode(curr); + eqcNode = eqcNode->get_next(); + curr = eqcNode->get_owner(); + } while (curr != varItor->first); + } + + // Step 3: Collect interested cases + + varItor = strVarMap.begin(); + for (; varItor != strVarMap.end(); ++varItor) { + expr * deAliasNode = get_alias_index_ast(aliasIndexMap, varItor->first); + // Case 1: variable = string constant + // e.g. z = "str1" ::= var_eq_constStr_map[z] = "str1" + + if (var_eq_constStr_map.find(deAliasNode) == var_eq_constStr_map.end()) { + bool nodeHasEqcValue = false; + expr * nodeValue = get_eqc_value(deAliasNode, nodeHasEqcValue); + if (nodeHasEqcValue) { + var_eq_constStr_map[deAliasNode] = nodeValue; + } + } + + // Case 2: var_eq_concat + // e.g. z = concat("str1", b) ::= var_eq_concat[z][concat(c, "str2")] = 1 + // var_eq_unroll + // e.g. z = unroll(...) ::= var_eq_unroll[z][unroll(...)] = 1 + + if (var_eq_concat_map.find(deAliasNode) == var_eq_concat_map.end()) { + enode * e_curr = ctx.get_enode(deAliasNode); + expr * curr = e_curr->get_next()->get_owner(); + while (curr != deAliasNode) { + app * aCurr = to_app(curr); + // collect concat + if (is_concat(aCurr)) { + expr * arg0 = aCurr->get_arg(0); + expr * arg1 = aCurr->get_arg(1); + bool arg0HasEqcValue = false; + bool arg1HasEqcValue = false; + expr * arg0_value = get_eqc_value(arg0, arg0HasEqcValue); + expr * arg1_value = get_eqc_value(arg1, arg1HasEqcValue); + + bool is_arg0_emptyStr = false; + if (arg0HasEqcValue) { + const char * strval = 0; + m_strutil.is_string(arg0_value, &strval); + if (strcmp(strval, "") == 0) { + is_arg0_emptyStr = true; + } + } + + bool is_arg1_emptyStr = false; + if (arg1HasEqcValue) { + const char * strval = 0; + m_strutil.is_string(arg1_value, &strval); + if (strcmp(strval, "") == 0) { + is_arg1_emptyStr = true; + } + } + + if (!is_arg0_emptyStr && !is_arg1_emptyStr) { + var_eq_concat_map[deAliasNode][curr] = 1; + } + } + // TODO: collect unroll functions + /* + else if (isUnrollFunc(t, curr)) { + var_eq_unroll_map[deAliasNode][curr] = 1; + } + */ + + // curr = get_eqc_next(curr) + e_curr = ctx.get_enode(curr); + curr = e_curr->get_next()->get_owner(); + } + } + + } // for(varItor in strVarMap) + + // -------------------------------------------------- + // * collect aliasing relation among eq concats + // e.g EQC={concat1, concat2, concat3} + // concats_eq_Index_map[concat2] = concat1 + // concats_eq_Index_map[concat3] = concat1 + // -------------------------------------------------- + + /* + std::map concats_eq_Index_map; + std::map::iterator concatItor = concatMap.begin(); + for (; concatItor != concatMap.end(); concatItor++) { + // simplifyConcatToConst(t, concatItor->first); + + if (concats_eq_Index_map.find(concatItor->first) != concats_eq_Index_map.end()) + continue; + + Z3_ast aRoot = NULL; + Z3_ast curr = concatItor->first; + do { + if (isConcatFunc(t, curr)) { + if (aRoot == NULL) + aRoot = curr; + else + concats_eq_Index_map[curr] = aRoot; + } + curr = Z3_theory_get_eqc_next(t, curr); + } while (curr != concatItor->first); + } + + concatItor = concatMap.begin(); + for (; concatItor != concatMap.end(); concatItor++) { + Z3_ast deAliasConcat = NULL; + if (concats_eq_Index_map.find(concatItor->first) != concats_eq_Index_map.end()) + deAliasConcat = concats_eq_Index_map[concatItor->first]; + else + deAliasConcat = concatItor->first; + + // -------------------------------------------------- + // (3) concat_eq_constStr: + // e.g, concat(a,b) = "str1" + // -------------------------------------------------- + if (concat_eq_constStr_map.find(deAliasConcat) == concat_eq_constStr_map.end()) { + bool nodeHasEqcValue = false; + Z3_ast nodeValue = get_eqc_value(t, deAliasConcat, nodeHasEqcValue); + if (nodeHasEqcValue) + concat_eq_constStr_map[deAliasConcat] = nodeValue; + } + // -------------------------------------------------- + // (4) concat_eq_concat: + // e.g, concat(a,b) = concat("str1", c) /\ z = concat(a, b) /\ z = concat(e, f) + // -------------------------------------------------- + if (concat_eq_concat_map.find(deAliasConcat) == concat_eq_concat_map.end()) { + Z3_ast curr = deAliasConcat; + do { + if (isConcatFunc(t, curr)) { + // curr is not a concat that can be reduced + if (concatMap.find(curr) != concatMap.end()) { + concat_eq_concat_map[deAliasConcat][curr] = 1; + } + } + curr = Z3_theory_get_eqc_next(t, curr); + } while (curr != deAliasConcat); + } + } + */ + // TODO the rest NOT_IMPLEMENTED_YET(); } From 4a8ee88461ab27233c15a9af389bdea3743fc5bd Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 6 Nov 2015 13:43:54 -0500 Subject: [PATCH 55/91] ctx_dep_analysis() done, final_check() WIP --- src/smt/theory_str.cpp | 517 +++++++++++++++++++++++++++++++++++------ src/smt/theory_str.h | 4 + 2 files changed, 451 insertions(+), 70 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index c5ca630f9..7d9aaad7d 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2599,6 +2599,33 @@ void theory_str::classify_ast_by_type_in_positive_context(std::map & } } +inline expr * theory_str::get_alias_index_ast(std::map & aliasIndexMap, expr * node) { + if (aliasIndexMap.find(node) != aliasIndexMap.end()) + return aliasIndexMap[node]; + else + return node; +} + +inline expr * theory_str::getMostLeftNodeInConcat(expr * node) { + app * aNode = to_app(node); + if (!is_concat(aNode)) { + return node; + } else { + expr * concatArgL = aNode->get_arg(0); + return getMostLeftNodeInConcat(concatArgL); + } +} + +inline expr * theory_str::getMostRightNodeInConcat(expr * node) { + app * aNode = to_app(node); + if (!is_concat(aNode)) { + return node; + } else { + expr * concatArgR = aNode->get_arg(1); + return getMostRightNodeInConcat(concatArgR); + } +} + /* * Dependence analysis from current context assignment * - "freeVarMap" contains a set of variables that doesn't constrained by Concats. @@ -2775,67 +2802,429 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map concats_eq_Index_map; - std::map::iterator concatItor = concatMap.begin(); - for (; concatItor != concatMap.end(); concatItor++) { - // simplifyConcatToConst(t, concatItor->first); - - if (concats_eq_Index_map.find(concatItor->first) != concats_eq_Index_map.end()) - continue; - - Z3_ast aRoot = NULL; - Z3_ast curr = concatItor->first; - do { - if (isConcatFunc(t, curr)) { - if (aRoot == NULL) - aRoot = curr; - else - concats_eq_Index_map[curr] = aRoot; - } - curr = Z3_theory_get_eqc_next(t, curr); - } while (curr != concatItor->first); + std::map concats_eq_index_map; + std::map::iterator concatItor = concatMap.begin(); + for(; concatItor != concatMap.end(); ++concatItor) { + if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { + continue; + } + expr * aRoot = NULL; + expr * curr = concatItor->first; + do { + if (is_concat(to_app(curr))) { + if (aRoot == NULL) { + aRoot = curr; + } else { + concats_eq_index_map[curr] = aRoot; + } + } + // curr = get_eqc_next(curr); + enode * e_curr = ctx.get_enode(curr); + curr = e_curr->get_next()->get_owner(); + } while (curr != concatItor->first); } concatItor = concatMap.begin(); - for (; concatItor != concatMap.end(); concatItor++) { - Z3_ast deAliasConcat = NULL; - if (concats_eq_Index_map.find(concatItor->first) != concats_eq_Index_map.end()) - deAliasConcat = concats_eq_Index_map[concatItor->first]; - else - deAliasConcat = concatItor->first; + for(; concatItor != concatMap.end(); ++concatItor) { + expr * deAliasConcat = NULL; + if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { + deAliasConcat = concats_eq_index_map[concatItor->first]; + } else { + deAliasConcat = concatItor->first; + } - // -------------------------------------------------- - // (3) concat_eq_constStr: - // e.g, concat(a,b) = "str1" - // -------------------------------------------------- - if (concat_eq_constStr_map.find(deAliasConcat) == concat_eq_constStr_map.end()) { - bool nodeHasEqcValue = false; - Z3_ast nodeValue = get_eqc_value(t, deAliasConcat, nodeHasEqcValue); - if (nodeHasEqcValue) - concat_eq_constStr_map[deAliasConcat] = nodeValue; - } - // -------------------------------------------------- - // (4) concat_eq_concat: - // e.g, concat(a,b) = concat("str1", c) /\ z = concat(a, b) /\ z = concat(e, f) - // -------------------------------------------------- - if (concat_eq_concat_map.find(deAliasConcat) == concat_eq_concat_map.end()) { - Z3_ast curr = deAliasConcat; - do { - if (isConcatFunc(t, curr)) { - // curr is not a concat that can be reduced - if (concatMap.find(curr) != concatMap.end()) { - concat_eq_concat_map[deAliasConcat][curr] = 1; - } - } - curr = Z3_theory_get_eqc_next(t, curr); - } while (curr != deAliasConcat); - } + // (3) concat_eq_conststr, e.g. concat(a,b) = "str1" + if (concat_eq_constStr_map.find(deAliasConcat) == concat_eq_constStr_map.end()) { + bool nodeHasEqcValue = false; + expr * nodeValue = get_eqc_value(deAliasConcat, nodeHasEqcValue); + if (nodeHasEqcValue) { + concat_eq_constStr_map[deAliasConcat] = nodeValue; + } + } + + // (4) concat_eq_concat, e.g. + // concat(a,b) = concat("str1", c) AND z = concat(a,b) AND z = concat(e,f) + if (concat_eq_concat_map.find(deAliasConcat) == concat_eq_concat_map.end()) { + expr * curr = deAliasConcat; + do { + if (is_concat(to_app(curr))) { + // curr cannot be reduced + if (concatMap.find(curr) != concatMap.end()) { + concat_eq_concat_map[deAliasConcat][curr] = 1; + } + } + // curr = get_eqc_next(curr); + enode * e_curr = ctx.get_enode(curr); + curr = e_curr->get_next()->get_owner(); + } while (curr != deAliasConcat); + } + } + + // TODO this would be a great place to print some debugging information + + // TODO compute Contains + /* + if (containPairBoolMap.size() > 0) { + computeContains(t, aliasIndexMap, concats_eq_Index_map, var_eq_constStr_map, concat_eq_constStr_map, var_eq_concat_map); } */ - // TODO the rest - NOT_IMPLEMENTED_YET(); + // step 4: dependence analysis + + // (1) var = string constant + for (std::map::iterator itor = var_eq_constStr_map.begin(); + itor != var_eq_constStr_map.end(); ++itor) { + expr * var = get_alias_index_ast(aliasIndexMap, itor->first); + expr * strAst = itor->second; + depMap[var][strAst] = 1; + } + + // (2) var = concat + for (std::map >::iterator itor = var_eq_concat_map.begin(); + itor != var_eq_concat_map.end(); ++itor) { + expr * var = get_alias_index_ast(aliasIndexMap, itor->first); + for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); ++itor1) { + expr * concat = itor1->first; + std::map inVarMap; + std::map inConcatMap; + std::map inUnrollMap; + classify_ast_by_type(concat, inVarMap, inConcatMap, inUnrollMap); + for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); ++itor2) { + expr * varInConcat = get_alias_index_ast(aliasIndexMap, itor2->first); + if (!(depMap[var].find(varInConcat) != depMap[var].end() && depMap[var][varInConcat] == 1)) { + depMap[var][varInConcat] = 2; + } + } + } + } + + for (std::map >::iterator itor = var_eq_unroll_map.begin(); + itor != var_eq_unroll_map.end(); itor++) { + expr * var = get_alias_index_ast(aliasIndexMap, itor->first); + for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { + expr * unrollFunc = itor1->first; + std::map inVarMap; + std::map inConcatMap; + std::map inUnrollMap; + classify_ast_by_type(unrollFunc, inVarMap, inConcatMap, inUnrollMap); + for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); itor2++) { + expr * varInFunc = get_alias_index_ast(aliasIndexMap, itor2->first); + + STRACE("t_str_detail", tout << "var in unroll = " << + mk_ismt2_pp(itor2->first, m) << std::endl + << "dealiased var = " << mk_ismt2_pp(varInFunc) << std::endl;); + + // it's possible that we have both (Unroll $$_regVar_0 $$_unr_0) /\ (Unroll abcd $$_unr_0), + // while $$_regVar_0 = "abcd" + // have to exclude such cases + bool varHasValue = false; + get_eqc_value(varInFunc, varHasValue); + if (varHasValue) + continue; + + if (depMap[var].find(varInFunc) == depMap[var].end()) { + depMap[var][varInFunc] = 6; + } + } + } + } + + // (3) concat = string constant + for (std::map::iterator itor = concat_eq_constStr_map.begin(); + itor != concat_eq_constStr_map.end(); itor++) { + expr * concatAst = itor->first; + expr * constStr = itor->second; + std::map inVarMap; + std::map inConcatMap; + std::map inUnrollMap; + classify_ast_by_type(concatAst, inVarMap, inConcatMap, inUnrollMap); + for (std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); itor2++) { + expr * varInConcat = get_alias_index_ast(aliasIndexMap, itor2->first); + if (!(depMap[varInConcat].find(constStr) != depMap[varInConcat].end() && depMap[varInConcat][constStr] == 1)) + depMap[varInConcat][constStr] = 3; + } + } + + // (4) equivalent concats + // - possibility 1 : concat("str", v1) = concat(concat(v2, v3), v4) = concat(v5, v6) + // ==> v2, v5 are constrained by "str" + // - possibliity 2 : concat(v1, "str") = concat(v2, v3) = concat(v4, v5) + // ==> v2, v4 are constrained by "str" + //-------------------------------------------------------------- + + std::map mostLeftNodes; + std::map mostRightNodes; + + std::map mLIdxMap; + std::map > mLMap; + std::map mRIdxMap; + std::map > mRMap; + std::set nSet; + + for (std::map >::iterator itor = concat_eq_concat_map.begin(); + itor != concat_eq_concat_map.end(); itor++) { + mostLeftNodes.clear(); + mostRightNodes.clear(); + + expr * mLConst = NULL; + expr * mRConst = NULL; + + for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { + expr * concatNode = itor1->first; + expr * mLNode = getMostLeftNodeInConcat(concatNode); + const char * strval; + if (m_strutil.is_string(to_app(mLNode), & strval)) { + if (mLConst == NULL && strcmp(strval, "") != 0) { + mLConst = mLNode; + } + } else { + mostLeftNodes[mLNode] = concatNode; + } + + expr * mRNode = getMostRightNodeInConcat(concatNode); + if (m_strutil.is_string(to_app(mRNode), & strval)) { + if (mRConst == NULL && strcmp(strval, "") != 0) { + mRConst = mRNode; + } + } else { + mostRightNodes[mRNode] = concatNode; + } + } + + if (mLConst != NULL) { + // ------------------------------------------------------------------------------------- + // The left most variable in a concat is constrained by a constant string in eqc concat + // ------------------------------------------------------------------------------------- + // e.g. Concat(x, ...) = Concat("abc", ...) + // ------------------------------------------------------------------------------------- + for (std::map::iterator itor1 = mostLeftNodes.begin(); + itor1 != mostLeftNodes.end(); itor1++) { + expr * deVar = get_alias_index_ast(aliasIndexMap, itor1->first); + if (depMap[deVar].find(mLConst) == depMap[deVar].end() || depMap[deVar][mLConst] != 1) { + depMap[deVar][mLConst] = 4; + } + } + } + + { + // ------------------------------------------------------------------------------------- + // The left most variables in eqc concats are constrained by each other + // ------------------------------------------------------------------------------------- + // e.g. concat(x, ...) = concat(u, ...) = ... + // x and u are constrained by each other + // ------------------------------------------------------------------------------------- + nSet.clear(); + std::map::iterator itl = mostLeftNodes.begin(); + for (; itl != mostLeftNodes.end(); itl++) { + bool lfHasEqcValue = false; + get_eqc_value(itl->first, lfHasEqcValue); + if (lfHasEqcValue) + continue; + expr * deVar = get_alias_index_ast(aliasIndexMap, itl->first); + nSet.insert(deVar); + } + + if (nSet.size() > 1) { + int lId = -1; + for (std::set::iterator itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { + if (mLIdxMap.find(*itor2) != mLIdxMap.end()) { + lId = mLIdxMap[*itor2]; + break; + } + } + if (lId == -1) + lId = mLMap.size(); + for (std::set::iterator itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { + bool itorHasEqcValue = false; + get_eqc_value(*itor2, itorHasEqcValue); + if (itorHasEqcValue) + continue; + mLIdxMap[*itor2] = lId; + mLMap[lId].insert(*itor2); + } + } + } + + if (mRConst != NULL) { + for (std::map::iterator itor1 = mostRightNodes.begin(); + itor1 != mostRightNodes.end(); itor1++) { + expr * deVar = get_alias_index_ast(aliasIndexMap, itor1->first); + if (depMap[deVar].find(mRConst) == depMap[deVar].end() || depMap[deVar][mRConst] != 1) { + depMap[deVar][mRConst] = 5; + } + } + } + + { + nSet.clear(); + std::map::iterator itr = mostRightNodes.begin(); + for (; itr != mostRightNodes.end(); itr++) { + expr * deVar = get_alias_index_ast(aliasIndexMap, itr->first); + nSet.insert(deVar); + } + if (nSet.size() > 1) { + int rId = -1; + std::set::iterator itor2 = nSet.begin(); + for (; itor2 != nSet.end(); itor2++) { + if (mRIdxMap.find(*itor2) != mRIdxMap.end()) { + rId = mRIdxMap[*itor2]; + break; + } + } + if (rId == -1) + rId = mRMap.size(); + for (itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) { + bool rHasEqcValue = false; + get_eqc_value(*itor2, rHasEqcValue); + if (rHasEqcValue) + continue; + mRIdxMap[*itor2] = rId; + mRMap[rId].insert(*itor2); + } + } + } + } + + // TODO this would be a great place to print the dependence map + + // step, errr, 5: compute free variables based on the dependence map + + // the case dependence map is empty, every var in VarMap is free + //--------------------------------------------------------------- + // remove L/R most var in eq concat since they are constrained with each other + std::map > lrConstrainedMap; + for (std::map >::iterator itor = mLMap.begin(); itor != mLMap.end(); itor++) { + for (std::set::iterator it1 = itor->second.begin(); it1 != itor->second.end(); it1++) { + std::set::iterator it2 = it1; + it2++; + for (; it2 != itor->second.end(); it2++) { + expr * n1 = *it1; + expr * n2 = *it2; + lrConstrainedMap[n1][n2] = 1; + lrConstrainedMap[n2][n1] = 1; + } + } + } + for (std::map >::iterator itor = mRMap.begin(); itor != mRMap.end(); itor++) { + for (std::set::iterator it1 = itor->second.begin(); it1 != itor->second.end(); it1++) { + std::set::iterator it2 = it1; + it2++; + for (; it2 != itor->second.end(); it2++) { + expr * n1 = *it1; + expr * n2 = *it2; + lrConstrainedMap[n1][n2] = 1; + lrConstrainedMap[n2][n1] = 1; + } + } + } + + if (depMap.size() == 0) { + std::map::iterator itor = strVarMap.begin(); + for (; itor != strVarMap.end(); itor++) { + expr * var = get_alias_index_ast(aliasIndexMap, itor->first); + if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { + freeVarMap[var] = 1; + } else { + int lrConstainted = 0; + std::map::iterator lrit = freeVarMap.begin(); + for (; lrit != freeVarMap.end(); lrit++) { + if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { + lrConstainted = 1; + break; + } + } + if (lrConstainted == 0) { + freeVarMap[var] = 1; + } + } + } + } else { + // if the keys in aliasIndexMap are not contained in keys in depMap, they are free + // e.g., x= y /\ x = z /\ t = "abc" + // aliasIndexMap[y]= x, aliasIndexMap[z] = x + // depMap t ~ "abc"(1) + // x should be free + std::map::iterator itor2 = strVarMap.begin(); + for (; itor2 != strVarMap.end(); itor2++) { + if (aliasIndexMap.find(itor2->first) != aliasIndexMap.end()) { + expr * var = aliasIndexMap[itor2->first]; + if (depMap.find(var) == depMap.end()) { + if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { + freeVarMap[var] = 1; + } else { + int lrConstainted = 0; + std::map::iterator lrit = freeVarMap.begin(); + for (; lrit != freeVarMap.end(); lrit++) { + if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { + lrConstainted = 1; + break; + } + } + if (lrConstainted == 0) { + freeVarMap[var] = 1; + } + } + } + } else if (aliasIndexMap.find(itor2->first) == aliasIndexMap.end()) { + // if a variable is not in aliasIndexMap and not in depMap, it's free + if (depMap.find(itor2->first) == depMap.end()) { + expr * var = itor2->first; + if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { + freeVarMap[var] = 1; + } else { + int lrConstainted = 0; + std::map::iterator lrit = freeVarMap.begin(); + for (; lrit != freeVarMap.end(); lrit++) { + if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { + lrConstainted = 1; + break; + } + } + if (lrConstainted == 0) { + freeVarMap[var] = 1; + } + } + } + } + } + + std::map >::iterator itor = depMap.begin(); + for (; itor != depMap.end(); itor++) { + for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { + if (variable_set.find(itor1->first) != variable_set.end()) { // expr type = var + expr * var = get_alias_index_ast(aliasIndexMap, itor1->first); + // if a var is dep on itself and all dependence are type 2, it's a free variable + // e.g {y --> x(2), y(2), m --> m(2), n(2)} y,m are free + { + if (depMap.find(var) == depMap.end()) { + if (freeVarMap.find(var) == freeVarMap.end()) { + if (lrConstrainedMap.find(var) == lrConstrainedMap.end()) { + freeVarMap[var] = 1; + } else { + int lrConstainted = 0; + std::map::iterator lrit = freeVarMap.begin(); + for (; lrit != freeVarMap.end(); lrit++) { + if (lrConstrainedMap[var].find(lrit->first) != lrConstrainedMap[var].end()) { + lrConstainted = 1; + break; + } + } + if (lrConstainted == 0) { + freeVarMap[var] = 1; + } + } + + } else { + freeVarMap[var] = freeVarMap[var] + 1; + } + } + } + } + } + } + } + + return 0; } final_check_status theory_str::final_check_eh() { @@ -2855,10 +3244,6 @@ final_check_status theory_str::final_check_eh() { return FC_DONE; } - // TODO the rest... - NOT_IMPLEMENTED_YET(); - - /* // Check every variable to see if it's eq. to some string constant. // If not, mark it as free. bool needToAssignFreeVars = false; @@ -2877,18 +3262,10 @@ final_check_status theory_str::final_check_eh() { return FC_DONE; } - for (std::set::iterator it = free_variables.begin(); it != free_variables.end(); ++it) { - expr * var = *it; - if (internal_variable_set.find(var) != internal_variable_set.end()) { - TRACE("t_str", tout << "assigning arbitrary string to internal variable " << mk_ismt2_pp(var, m) << std::endl;); - app * val = m_strutil.mk_string("**unused**"); - assert_axiom(ctx.mk_eq_atom(var, val)); - } else { - NOT_IMPLEMENTED_YET(); // TODO free variable assignment from strTheory::cb_final_check() - } - } - return FC_CONTINUE; - */ + + + // TODO the rest... + NOT_IMPLEMENTED_YET(); } void theory_str::init_model(model_generator & mg) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 684526602..3d0f14ca7 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -153,6 +153,10 @@ namespace smt { void classify_ast_by_type_in_positive_context(std::map & varMap, std::map & concatMap, std::map & unrollMap); + expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); + expr * getMostLeftNodeInConcat(expr * node); + expr * getMostRightNodeInConcat(expr * node); + void dump_assignments(); public: theory_str(ast_manager & m); From ac8b5e6eae5cf97146fafd4f1cc450cda7dc4a27 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 6 Nov 2015 14:10:18 -0500 Subject: [PATCH 56/91] free variable WIP --- src/smt/theory_str.cpp | 160 ++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 3 + 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 7d9aaad7d..cb31aedde 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3262,10 +3262,166 @@ final_check_status theory_str::final_check_eh() { return FC_DONE; } + // ----------------------------------------------------------- + // variables in freeVar are those not bouned by Concats + // classify variables in freeVarMap: + // (1) freeVar = unroll(r1, t1) + // (2) vars are not bounded by either concat or unroll + // ----------------------------------------------------------- + std::map > fv_unrolls_map; + std::set tmpSet; + expr * constValue = NULL; + for (std::map::iterator fvIt2 = freeVar_map.begin(); fvIt2 != freeVar_map.end(); fvIt2++) { + expr * var = fvIt2->first; + tmpSet.clear(); + get_eqc_allUnroll(var, constValue, tmpSet); + if (tmpSet.size() > 0) { + fv_unrolls_map[var] = tmpSet; + } + } + // erase var bounded by an unroll function from freeVar_map + for (std::map >::iterator fvIt3 = fv_unrolls_map.begin(); + fvIt3 != fv_unrolls_map.end(); fvIt3++) { + expr * var = fvIt3->first; + freeVar_map.erase(var); + } + // collect the case: + // * Concat(X, Y) = unroll(r1, t1) /\ Concat(X, Y) = unroll(r2, t2) + // concatEqUnrollsMap[Concat(X, Y)] = {unroll(r1, t1), unroll(r2, t2)} - // TODO the rest... - NOT_IMPLEMENTED_YET(); + std::map > concatEqUnrollsMap; + for (std::map >::iterator urItor = unrollGroup_map.begin(); + urItor != unrollGroup_map.end(); urItor++) { + expr * unroll = urItor->first; + expr * curr = unroll; + do { + if (is_concat(to_app(curr))) { + concatEqUnrollsMap[curr].insert(unroll); + concatEqUnrollsMap[curr].insert(unrollGroup_map[unroll].begin(), unrollGroup_map[unroll].end()); + } + enode * e_curr = ctx.get_enode(curr); + curr = e_curr->get_next()->get_owner(); + // curr = get_eqc_next(curr); + } while (curr != unroll); + } + + std::map > concatFreeArgsEqUnrollsMap; + std::set fvUnrollSet; + for (std::map >::iterator concatItor = concatEqUnrollsMap.begin(); + concatItor != concatEqUnrollsMap.end(); concatItor++) { + expr * concat = concatItor->first; + expr * concatArg1 = to_app(concat)->get_arg(0); + expr * concatArg2 = to_app(concat)->get_arg(1); + bool arg1Bounded = false; + bool arg2Bounded = false; + // arg1 + if (variable_set.find(concatArg1) != variable_set.end()) { + if (freeVar_map.find(concatArg1) == freeVar_map.end()) { + arg1Bounded = true; + } else { + fvUnrollSet.insert(concatArg1); + } + } else if (is_concat(to_app(concatArg1))) { + if (concatEqUnrollsMap.find(concatArg1) == concatEqUnrollsMap.end()) { + arg1Bounded = true; + } + } + // arg2 + if (variable_set.find(concatArg2) != variable_set.end()) { + if (freeVar_map.find(concatArg2) == freeVar_map.end()) { + arg2Bounded = true; + } else { + fvUnrollSet.insert(concatArg2); + } + } else if (is_concat(to_app(concatArg2))) { + if (concatEqUnrollsMap.find(concatArg2) == concatEqUnrollsMap.end()) { + arg2Bounded = true; + } + } + if (!arg1Bounded && !arg2Bounded) { + concatFreeArgsEqUnrollsMap[concat].insert( + concatEqUnrollsMap[concat].begin(), + concatEqUnrollsMap[concat].end()); + } + } + for (std::set::iterator vItor = fvUnrollSet.begin(); vItor != fvUnrollSet.end(); vItor++) { + freeVar_map.erase(*vItor); + } + + // Assign free variables + std::set fSimpUnroll; + + constValue = NULL; + + // TODO this would be a great place to print debugging information + + // TODO process_concat_eq_unroll() + /* + for (std::map >::iterator fvIt2 = concatFreeArgsEqUnrollsMap.begin(); + fvIt2 != concatFreeArgsEqUnrollsMap.end(); fvIt2++) { + expr * concat = fvIt2->first; + for (std::set::iterator urItor = fvIt2->second.begin(); urItor != fvIt2->second.end(); urItor++) { + Z3_ast unroll = *urItor; + processConcatEqUnroll(concat, unroll); + } + } + */ + + // -------- + // experimental free variable assignment - begin + // * special handling for variables that are not used in concat + // -------- + bool testAssign = true; + if (!testAssign) { + for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { + expr * freeVar = fvIt->first; + /* + std::string vName = std::string(Z3_ast_to_string(ctx, freeVar)); + if (vName.length() >= 9 && vName.substr(0, 9) == "$$_regVar") { + continue; + } + */ + // TODO if this variable represents a regular expression, continue + expr * toAssert = gen_len_val_options_for_free_var(freeVar, NULL, ""); + if (toAssert != NULL) { + assert_axiom(toAssert); + } + } + } else { + process_free_var(freeVar_map); + } + // experimental free variable assignment - end + + // TODO more unroll stuff + /* + for (std::map >::iterator fvIt1 = fv_unrolls_map.begin(); + fvIt1 != fv_unrolls_map.end(); fvIt1++) { + Z3_ast var = fvIt1->first; + fSimpUnroll.clear(); + get_eqc_simpleUnroll(t, var, constValue, fSimpUnroll); + if (fSimpUnroll.size() == 0) { + genAssignUnrollReg(t, fv_unrolls_map[var]); + } else { + Z3_ast toAssert = genAssignUnrollStr2Reg(t, var, fSimpUnroll); + if (toAssert != NULL) { + addAxiom(t, toAssert, __LINE__); + } + } + } + */ + + return FC_CONTINUE; // since by this point we've added axioms +} + +expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue) { + // TODO + NOT_IMPLEMENTED_YET(); +} + +void theory_str::process_free_var(std::map & freeVar_map) { + // TODO this one first + NOT_IMPLEMENTED_YET(); } void theory_str::init_model(model_generator & mg) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 3d0f14ca7..60c2c3a8e 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -153,6 +153,9 @@ namespace smt { void classify_ast_by_type_in_positive_context(std::map & varMap, std::map & concatMap, std::map & unrollMap); + expr * gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue); + void process_free_var(std::map & freeVar_map); + expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); From e9b31f29954ccc2252a5d122bcbe25b9f1601bc1 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 6 Nov 2015 14:13:38 -0500 Subject: [PATCH 57/91] temporarily patched in a get_eqc_allUnroll() implementation --- src/smt/theory_str.cpp | 26 +++++++++++++++++++++++++- src/smt/theory_str.h | 4 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index cb31aedde..0965e7873 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3263,7 +3263,7 @@ final_check_status theory_str::final_check_eh() { } // ----------------------------------------------------------- - // variables in freeVar are those not bouned by Concats + // variables in freeVar are those not bounded by Concats // classify variables in freeVarMap: // (1) freeVar = unroll(r1, t1) // (2) vars are not bounded by either concat or unroll @@ -3424,6 +3424,30 @@ void theory_str::process_free_var(std::map & freeVar_map) { NOT_IMPLEMENTED_YET(); } +/* + * Collect all unroll functions + * and constant string in eqc of node n + */ +void theory_str::get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet) { + constStr = NULL; + unrollFuncSet.clear(); + context & ctx = get_context(); + + expr * curr = n; + do { + if (is_string(to_app(curr))) { + constStr = curr; + } else if (false) /*(td->Unroll == Z3_get_app_decl(ctx, Z3_to_app(ctx, curr)))*/ { // TODO + if (unrollFuncSet.find(curr) == unrollFuncSet.end()) { + unrollFuncSet.insert(curr); + } + } + enode * e_curr = ctx.get_enode(curr); + curr = e_curr->get_next()->get_owner(); + // curr = get_eqc_next(t, curr); + } while (curr != n); +} + void theory_str::init_model(model_generator & mg) { TRACE("t_str", tout << "initializing model" << std::endl; display(tout);); m_factory = alloc(str_value_factory, get_manager(), get_family_id()); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 60c2c3a8e..86f45aea0 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -160,6 +160,10 @@ namespace smt { expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); + // strRegex + + void get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet); + void dump_assignments(); public: theory_str(ast_manager & m); From a9b8707d48fb8b474a6b49ba68d1f7e9fdf7d250 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 9 Nov 2015 15:14:34 -0500 Subject: [PATCH 58/91] possibly found a way to do get_parents() --- src/smt/theory_str.cpp | 76 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 0965e7873..da5c858b8 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3420,7 +3420,81 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe } void theory_str::process_free_var(std::map & freeVar_map) { - // TODO this one first + context & ctx = get_context(); + ast_manager & m = get_manager(); + + std::set eqcRepSet; + std::set leafVarSet; + std::map > aloneVars; + + for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { + expr * freeVar = fvIt->first; + /* + std::string vName = std::string(Z3_ast_to_string(ctx, freeVar)); + if (vName.length() >= 9 && vName.substr(0, 9) == "$$_regVar") { + continue; + } + */ + // TODO skip all regular expression vars + + // Iterate the EQC of freeVar, its eqc variable should not be in the eqcRepSet. + // If found, have to filter it out + std::set eqVarSet; + get_var_in_eqc(freeVar, eqVarSet); + bool duplicated = false; + expr * dupVar = NULL; + for (std::set::iterator itorEqv = eqVarSet.begin(); itorEqv != eqVarSet.end(); itorEqv++) { + if (eqcRepSet.find(*itorEqv) != eqcRepSet.end()) { + duplicated = true; + dupVar = *itorEqv; + break; + } + } + if (duplicated && dupVar != NULL) { + STRACE("t_str_detail", tout << "Duplicated free variable found:" << mk_ismt2_pp(freeVar, m) + << " = " << mk_ismt2_pp(dupVar, m) << " (SKIP)" << std::endl;); + continue; + } else { + eqcRepSet.insert(freeVar); + } + } + + for (std::set::iterator fvIt = eqcRepSet.begin(); fvIt != eqcRepSet.end(); fvIt++) { + bool standAlone = true; + expr * freeVar = *fvIt; + // has length constraint initially + if (input_var_in_len.find(freeVar) != input_var_in_len.end()) { + standAlone = false; + } + // iterate parents + if (standAlone) { + // I hope this works! + enode * e_freeVar = ctx.get_enode(freeVar); + enode_vector::iterator it = e_freeVar->begin_parents(); + for (; it != e_freeVar->end_parents(); ++it) { + expr * parentAst = (*it)->get_owner(); + if (is_concat(to_app(parentAst))) { + standAlone = false; + break; + } + } + } + + if (standAlone) { + // TODO + // int lenValue = getLenValue(freeVar); + int lenValue = -1; + if (lenValue != -1) { + leafVarSet.insert(freeVar); + } else { + aloneVars[lenValue].insert(freeVar); + } + } else { + leafVarSet.insert(freeVar); + } + } + + // TODO the rest NOT_IMPLEMENTED_YET(); } From 0178872a19c0ad0cec339c2b9ebbe99453aa2c74 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 9 Nov 2015 15:33:52 -0500 Subject: [PATCH 59/91] completed process_free_var(), still WIP working on gen_len_val_options_for_free_var() --- src/smt/theory_str.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- src/smt/theory_str.h | 3 +++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index da5c858b8..a235936d6 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2385,6 +2385,14 @@ void theory_str::set_up_axioms(expr * ex) { if (is_concat(ap)) { // if ex is a concat, set up concat axioms later m_concat_axiom_todo.push_back(n); + } else if (is_strlen(ap)) { + // if the argument is a variable, + // keep track of this for later, we'll need it during model gen + expr * var = ap->get_arg(0); + app * aVar = to_app(var); + if (aVar->get_num_args() == 0 && !is_string(aVar)) { + input_var_in_len.insert(var); + } } else if (ap->get_num_args() == 0 && !is_string(ap)) { // if ex is a variable, add it to our list of variables TRACE("t_str_detail", tout << "tracking variable" << std::endl;); @@ -3419,6 +3427,20 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe NOT_IMPLEMENTED_YET(); } +void theory_str::get_var_in_eqc(expr * n, std::set & varSet) { + context & ctx = get_context(); + + expr * eqcNode = n; + do { + if (variable_set.find(eqcNode) != variable_set.end()) { + varSet.insert(eqcNode); + } + enode * e_eqc = ctx.get_enode(eqcNode); + eqcNode = e_eqc->get_next()->get_owner(); + // eqcNode = Z3_theory_get_eqc_next(t, eqcNode); + } while (eqcNode != n); +} + void theory_str::process_free_var(std::map & freeVar_map) { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -3494,8 +3516,22 @@ void theory_str::process_free_var(std::map & freeVar_map) { } } - // TODO the rest - NOT_IMPLEMENTED_YET(); + // TODO here's a great place for debugging info + + for(std::set::iterator itor1 = leafVarSet.begin(); + itor1 != leafVarSet.end(); ++itor1) { + expr * toAssert = gen_len_val_options_for_free_var(*itor1, NULL, ""); + assert_axiom(toAssert); + } + + for (std::map >::iterator mItor = aloneVars.begin(); + mItor != aloneVars.end(); ++mItor) { + std::set::iterator itor2 = mItor->second.begin(); + for(; itor2 != mItor->second.end(); ++itor2) { + expr * toAssert = gen_len_val_options_for_free_var(*itor2, NULL, ""); + assert_axiom(toAssert); + } + } } /* diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 86f45aea0..a4a89f947 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -82,6 +82,8 @@ namespace smt { std::set variable_set; std::set internal_variable_set; + + std::set input_var_in_len; protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); @@ -159,6 +161,7 @@ namespace smt { expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); + void get_var_in_eqc(expr * n, std::set & varSet); // strRegex From 6374d6316017ff772b2930c104215a39142917bc Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 9 Nov 2015 16:11:00 -0500 Subject: [PATCH 60/91] gen_len_val_options_for_free_var() WIP --- src/smt/theory_str.cpp | 224 ++++++++++++++++++++++++++++++++++++++--- src/smt/theory_str.h | 8 ++ 2 files changed, 219 insertions(+), 13 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index a235936d6..c485c40ff 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -221,6 +221,24 @@ app * theory_str::mk_int(int n) { return m_autil.mk_numeral(rational(n), true); } +expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + std::stringstream ss; + ss << "$$_len_" << mk_ismt2_pp(node, m) << "_" << lTries; + std::string name = ss.str(); + return mk_str_var(name); + + /* + Z3_context ctx = Z3_theory_get_context(t); + std::stringstream ss; + ss << "$$_len_" << Z3_ast_to_string(ctx, node) << "_" << lTries; + std::string name = ss.str(); + return my_mk_str_var(t, name.c_str()); + */ +} + app * theory_str::mk_internal_xor_var() { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -239,17 +257,26 @@ 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_str_var(std::string name) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + 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)); + + // I have a hunch that this may not get internalized for free... + SASSERT(ctx.get_enode(a) != NULL); + m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); + + variable_set.insert(a); + internal_variable_set.insert(a); + + return a; +} app * theory_str::mk_nonempty_str_var() { context & ctx = get_context(); @@ -3422,9 +3449,180 @@ final_check_status theory_str::final_check_eh() { return FC_CONTINUE; // since by this point we've added axioms } +inline std::string int_to_string(int i) { + std::stringstream ss; + ss << i; + return ss.str(); +} + +expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + expr * freeVarLen = mk_strlen(freeVar); + + ptr_vector orList; + ptr_vector andList; + + int distance = 3; + int l = (tries - 1) * distance; + int h = tries * distance; + + for (int i = l; i < h; ++i) { + orList.push_back(m.mk_eq(indicator, m_strutil.mk_string(int_to_string(i).c_str()))); + andList.push_back(m.mk_eq(orList[orList.size() - 1], m.mk_eq(freeVarLen, mk_int(i)))); + } + + orList.push_back(m.mk_eq(indicator, m_strutil.mk_string("more"))); + andList.push_back(m.mk_eq(orList[orList.size() - 1], m_autil.mk_ge(freeVarLen, mk_int(h)))); + + expr ** or_items = alloc_svect(expr*, orList.size()); + expr ** and_items = alloc_svect(expr*, andList.size() + 1); + + for (int i = 0; i < orList.size(); ++i) { + or_items[i] = orList[i]; + } + + and_items[0] = m.mk_or(orList.size(), or_items); + for(int i = 0; i < andList.size(); ++i) { + and_items[i+1] = andList[i]; + } + expr * lenTestAssert = m.mk_and(andList.size() + 1, and_items); + + expr * assertL = NULL; + int testerCount = tries - 1; + if (testerCount > 0) { + expr ** and_items_LHS = alloc_svect(expr*, testerCount); + expr * moreAst = m_strutil.mk_string("more"); + for (int i = 0; i < testerCount; ++i) { + and_items_LHS[i] = m.mk_eq(fvar_lenTester_map[freeVar][i], moreAst); + } + if (testerCount == 1) { + assertL = and_items_LHS[0]; + } else { + assertL = m.mk_and(testerCount, and_items_LHS); + } + } + + if (assertL != NULL) { + // return the axiom (assertL -> lenTestAssert) + // would like to use mk_implies() here but... + expr_ref lenTestAssert(m.mk_or(m.mk_not(assertL), lenTestAssert), m); + } + + return lenTestAssert; + +} + +// ----------------------------------------------------------------------------------------------------- +// True branch will be taken in final_check: +// - When we discover a variable is "free" for the first time +// lenTesterInCbEq = NULL +// lenTesterValue = "" +// False branch will be taken when invoked by new_eq_eh(). +// - After we set up length tester for a "free" var in final_check, +// when the tester is assigned to some value (e.g. "more" or "4"), +// lenTesterInCbEq != NULL, and its value will be passed by lenTesterValue +// The difference is that in new_eq_eh(), lenTesterInCbEq and its value have NOT been put into a same eqc +// ----------------------------------------------------------------------------------------------------- expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue) { - // TODO - NOT_IMPLEMENTED_YET(); + + context & ctx = get_context(); + ast_manager & m = get_manager(); + + STRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); + // no length assertions for this free variable have ever been added. + if (fvar_len_count_map.find(freeVar) == fvar_len_count_map.end()) { + fvar_len_count_map[freeVar] = 1; + unsigned int testNum = fvar_len_count_map[freeVar]; + expr * indicator = mk_internal_lenTest_var(freeVar, testNum); + fvar_lenTester_map[freeVar].push_back(indicator); + lenTester_fvar_map[indicator] = freeVar; + + expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); + return lenTestAssert; + } else { + /* + Z3_ast effectiveLenInd = NULL; + std::string effectiveLenIndiStr = ""; + int lenTesterCount = (int) fvarLenTesterMap[freeVar].size(); + + int i = 0; + for (; i < lenTesterCount; i++) { + Z3_ast len_indicator_pre = fvarLenTesterMap[freeVar][i]; + bool indicatorHasEqcValue = false; + Z3_ast len_indicator_value = get_eqc_value(t, len_indicator_pre, indicatorHasEqcValue); +#ifdef DEBUGLOG + __debugPrint(logFile, "* length indicator "); + printZ3Node(t, len_indicator_pre); + __debugPrint(logFile, " = "); + printZ3Node(t, len_indicator_value); + __debugPrint(logFile, "\n"); +#endif + if (indicatorHasEqcValue) { + std::string len_pIndiStr = getConstStrValue(t, len_indicator_value); + if (len_pIndiStr != "more") { + effectiveLenInd = len_indicator_pre; + effectiveLenIndiStr = len_pIndiStr; + break; + } + } else { + if (lenTesterInCbEq != len_indicator_pre) { +#ifdef DEBUGLOG + __debugPrint(logFile, "\n>> *Warning*: length indicator: "); + printZ3Node(t, len_indicator_pre); + __debugPrint(logFile, " doesn't have an EQC value. i = %d, lenTesterCount = %d\n", i , lenTesterCount); +#endif + if (i > 0) { + effectiveLenInd = fvarLenTesterMap[freeVar][i - 1]; + if (effectiveLenInd == lenTesterInCbEq) { + effectiveLenIndiStr = lenTesterValue; + } else { + bool effectiveHasEqcValue = false; + effectiveLenIndiStr = getConstStrValue(t, get_eqc_value(t, effectiveLenInd, effectiveHasEqcValue)); + } + } + break; + } + // lenTesterInCbEq == len_indicator_pre + else { + if (lenTesterValue != "more") { + effectiveLenInd = len_indicator_pre; + effectiveLenIndiStr = lenTesterValue; + break; + } + } + } + } + + if (effectiveLenIndiStr == "more" || effectiveLenIndiStr == "") { + Z3_ast indicator = NULL; + unsigned int testNum = 0; + + __debugPrint(logFile, "\n>> effectiveLenIndiStr = %s, i = %d, lenTesterCount = %d\n", effectiveLenIndiStr.c_str(), i, lenTesterCount); + + if (i == lenTesterCount) { + fvarLenCountMap[freeVar] = fvarLenCountMap[freeVar] + 1; + testNum = fvarLenCountMap[freeVar]; + indicator = my_mk_internal_lenTest_var(t, freeVar, testNum); + fvarLenTesterMap[freeVar].push_back(indicator); + lenTesterFvarMap[indicator] = freeVar; + } else { + indicator = fvarLenTesterMap[freeVar][i]; + testNum = i + 1; + } + Z3_ast lenTestAssert = genLenTestOptions(t, freeVar, indicator, testNum); + return lenTestAssert; + } else { + // length is fixed + Z3_ast valueAssert = genFreeVarOptions(t, freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, ""); + return valueAssert; + } + */ + + // TODO + NOT_IMPLEMENTED_YET(); + } // fVarLenCountMap.find(...) } void theory_str::get_var_in_eqc(expr * n, std::set & varSet) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index a4a89f947..b7d93ef54 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -84,6 +84,11 @@ namespace smt { std::set internal_variable_set; std::set input_var_in_len; + + std::map fvar_len_count_map; + std::map > fvar_lenTester_map; + std::map lenTester_fvar_map; + protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); @@ -99,6 +104,7 @@ namespace smt { void add_cut_info_merge(expr * destNode, int slevel, expr * srcNode); bool has_self_cut(expr * n1, expr * n2); + app * mk_str_var(std::string name); app * mk_nonempty_str_var(); app * mk_internal_xor_var(); @@ -155,8 +161,10 @@ namespace smt { void classify_ast_by_type_in_positive_context(std::map & varMap, std::map & concatMap, std::map & unrollMap); + expr * mk_internal_lenTest_var(expr * node, int lTries); expr * gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue); void process_free_var(std::map & freeVar_map); + expr * gen_len_test_options(expr * freeVar, expr * indicator, int tries); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From 3a404c248d49a700ff50ed09db42859f4c660763 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 10 Nov 2015 12:40:01 -0500 Subject: [PATCH 61/91] gen_free_var_options() WIP --- src/smt/theory_str.cpp | 124 +++++++++++++++++++++++++++++------------ src/smt/theory_str.h | 2 + 2 files changed, 90 insertions(+), 36 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index c485c40ff..2101fe7a5 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3455,6 +3455,65 @@ inline std::string int_to_string(int i) { return ss.str(); } +expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, + std::string len_valueStr, expr * valTesterInCbEq, std::string valTesterValueStr) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + int len = atoi(len_valueStr.c_str()); + + if (fvar_valueTester_map[freeVar].find(len) == fvar_valueTester_map[freeVar].end()) { + int tries = 0; + expr * val_indicator = mk_internal_valTest_var(freeVar, len, tries); + valueTester_fvar_map[val_indicator] = freeVar; + fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, val_indicator)); + print_value_tester_list(fvar_valueTester_map[freeVar][len]); + return gen_val_options(freeVar, len_indicator, val_indicator, len_valueStr, tries); + } else { + // go through all previous value testers + // If some doesn't have an eqc value, add its assertion again. + int testerTotal = fvar_valueTester_map[freeVar][len].size(); + int i = 0; + for (; i < testerTotal; i++) { + expr * aTester = fvarValueTesterMap[freeVar][len][i].second; + + if (aTester == valTesterInCbEq) { + break; + } + + bool anEqcHasValue = false; + // Z3_ast anEqc = get_eqc_value(t, aTester, anEqcHasValue); + get_eqc_value(aTester, anEqcHasValue); + if (!anEqcHasValue) { + STRACE("t_str_detail", "value tester " << mk_ismt2_pp(aTester, m) + << "doesn't have an equivalence class value." << std::endl;); + + expr_ref makeupAssert(gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i), m); + + STRACE("t_str_detail", "var: " << mk_ismt2_pp(freeVar, m) << std::endl + << mk_ismt2_pp(makeupAssert, m) << std::endl;); + assert_axiom(makeupAssert); + } + } + + if (valTesterValueStr == "more") { + expr * valTester = NULL; + if (i + 1 < testerTotal) { + valTester = fvar_valueTester_map[freeVar][len][i + 1].second; + } else { + valTester = mk_internal_valTest_var(freeVar, len, i + 1); + valueTester_fvar_map[valTester] = freeVar; + fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, valTester)); + print_value_tester_list(fvar_valueTester_map[freeVar][len]); + } + expr_ref nextAssert(gen_val_options(freeVar, len_indicator, valTester, len_valueStr, i + 1), m); + return nextAssert; + } + + return NULL; + } +} + expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -3542,25 +3601,22 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); return lenTestAssert; } else { - /* - Z3_ast effectiveLenInd = NULL; + + expr * effectiveLenInd = NULL; std::string effectiveLenIndiStr = ""; - int lenTesterCount = (int) fvarLenTesterMap[freeVar].size(); + int lenTesterCount = (int) fvar_lenTester_map[freeVar].size(); int i = 0; - for (; i < lenTesterCount; i++) { - Z3_ast len_indicator_pre = fvarLenTesterMap[freeVar][i]; + for (; i < lenTesterCount; ++i) { + expr * len_indicator_pre = fvar_lenTester_map[freeVar][i]; bool indicatorHasEqcValue = false; - Z3_ast len_indicator_value = get_eqc_value(t, len_indicator_pre, indicatorHasEqcValue); -#ifdef DEBUGLOG - __debugPrint(logFile, "* length indicator "); - printZ3Node(t, len_indicator_pre); - __debugPrint(logFile, " = "); - printZ3Node(t, len_indicator_value); - __debugPrint(logFile, "\n"); -#endif + expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); + STRACE("t_str_detail", tout << "length indicator " << mk_ismt2_pp(len_indicator_pre, m) << + " = " << mk_ismt2_pp(len_indicator_value, m) << std::endl;); if (indicatorHasEqcValue) { - std::string len_pIndiStr = getConstStrValue(t, len_indicator_value); + const char * val = 0; + m_strutil.is_string(len_indicator_value, & val); + std::string len_pIndiStr(val); if (len_pIndiStr != "more") { effectiveLenInd = len_indicator_pre; effectiveLenIndiStr = len_pIndiStr; @@ -3568,18 +3624,18 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe } } else { if (lenTesterInCbEq != len_indicator_pre) { -#ifdef DEBUGLOG - __debugPrint(logFile, "\n>> *Warning*: length indicator: "); - printZ3Node(t, len_indicator_pre); - __debugPrint(logFile, " doesn't have an EQC value. i = %d, lenTesterCount = %d\n", i , lenTesterCount); -#endif + STRACE("t_str", tout << "WARNING: length indicator " << mk_ismt2_pp(len_indicator_pre, m) + << " does not have an equivalence class value." + << " i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i > 0) { effectiveLenInd = fvarLenTesterMap[freeVar][i - 1]; if (effectiveLenInd == lenTesterInCbEq) { effectiveLenIndiStr = lenTesterValue; } else { bool effectiveHasEqcValue = false; - effectiveLenIndiStr = getConstStrValue(t, get_eqc_value(t, effectiveLenInd, effectiveHasEqcValue)); + const char * val = 0; + m_strutil.is_string(get_eqc_value(effectiveLenInd, effectiveHasEqcValue), & val); + effectiveLenIndiStr = val; } } break; @@ -3592,36 +3648,32 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe break; } } - } - } - + } // !indicatorHasEqcValue + } // for (i : [0..lenTesterCount-1]) if (effectiveLenIndiStr == "more" || effectiveLenIndiStr == "") { - Z3_ast indicator = NULL; + expr * indicator = NULL; unsigned int testNum = 0; - __debugPrint(logFile, "\n>> effectiveLenIndiStr = %s, i = %d, lenTesterCount = %d\n", effectiveLenIndiStr.c_str(), i, lenTesterCount); + STRACE("t_str", tout << "effectiveLenIndiStr = " << effectiveLenIndiStr + << ", i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i == lenTesterCount) { - fvarLenCountMap[freeVar] = fvarLenCountMap[freeVar] + 1; - testNum = fvarLenCountMap[freeVar]; - indicator = my_mk_internal_lenTest_var(t, freeVar, testNum); - fvarLenTesterMap[freeVar].push_back(indicator); - lenTesterFvarMap[indicator] = freeVar; + fvar_len_count_map[freeVar] = fvar_len_count_map[freeVar] + 1; + testNum = fvar_len_count_map[freeVar]; + indicator = mk_internal_lenTest_var(freeVar, testNum); + fvar_lenTester_map[freeVar].push_back(indicator); + lenTester_fvar_map[indicator] = freeVar; } else { indicator = fvarLenTesterMap[freeVar][i]; testNum = i + 1; } - Z3_ast lenTestAssert = genLenTestOptions(t, freeVar, indicator, testNum); + expr_ref lenTestAssert(gen_len_test_options(freeVar, indicator, testNum), m); return lenTestAssert; } else { // length is fixed - Z3_ast valueAssert = genFreeVarOptions(t, freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, ""); + expr_ref valueAssert(gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, ""), m); return valueAssert; } - */ - - // TODO - NOT_IMPLEMENTED_YET(); } // fVarLenCountMap.find(...) } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index b7d93ef54..bd26f2564 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -165,6 +165,8 @@ namespace smt { expr * gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue); void process_free_var(std::map & freeVar_map); expr * gen_len_test_options(expr * freeVar, expr * indicator, int tries); + expr * gen_free_var_options(expr * freeVar, expr * len_indicator, + std::string len_valueStr, expr * valTesterInCbEq, std::string valTesterValueStr); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From 8b538f584031907a2802551d36bcbfaf256b9c4a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 11 Nov 2015 15:34:11 -0500 Subject: [PATCH 62/91] started gen_val_options() WIP --- src/smt/theory_str.cpp | 146 ++++++++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 7 ++ 2 files changed, 152 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 2101fe7a5..f86f921de 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -239,6 +239,23 @@ expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { */ } +expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + std::stringstream ss; + ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries; + std::string name = ss.str(); + return mk_str_var(name); + + /* + Z3_context ctx = Z3_theory_get_context(t); + std::stringstream ss; + ss << "$$_val_" << Z3_ast_to_string(ctx, node) << "_" << len << "_" << vTries; + std::string name = ss.str(); + return my_mk_str_var(t, name.c_str()); + */ +} + app * theory_str::mk_internal_xor_var() { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -3455,6 +3472,133 @@ inline std::string int_to_string(int i) { return ss.str(); } +inline std::string longlong_to_string(long long i) { + std::stringstream ss; + ss << i; + return ss.str(); +} + +void theory_str::print_value_tester_list(std::vector > & testerList) { + ast_manager & m = get_manager(); + STRACE("t_str_detail", + int ss = testerList.size(); + tout << "valueTesterList = {"; + for (int i = 0; i < ss; ++i) { + if (i % 4 == 0) { + tout << std::endl; + } + tout << "(" << testerList[i].first << ", "; + tout << mk_ismt2_pp(testerList[i].second, m); + tout << "), "; + } + tout << std::endl << "}" << std::endl; + ); +} + +expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, + std::string lenStr, int tries) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + int distance = 32; + + // ---------------------------------------------------------------------------------------- + // generate value options encoding + // encoding is a vector of size (len + 1) + // e.g, len = 2, + // encoding {1, 2, 0} means the value option is "charSet[2]"."charSet[1]" + // the last item in the encoding indicates whether the whole space is covered + // for example, if the charSet = {a, b}. All valid encodings are + // {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0} + // if add 1 to the last one, we get + // {0, 0, 1} + // the last item "1" shows this is not a valid encoding, and we have covered all space + // ---------------------------------------------------------------------------------------- + int len = atoi(lenStr.c_str()); + bool coverAll = false; + std::vector > options; + std::vector base; + + if (tries == 0) { + base = std::vector(len + 1, 0); + coverAll = false; + } else { + expr * lastestValIndi = fvar_valueTester_map[freeVar][len][tries - 1].second; + STRACE("t_str_detail", tout << "last value tester = " << mk_ismt2_pp(lastestValIndi, m) << std::endl;); + coverAll = get_next_val_encode(valRangeMap[lastestValIndi], base); + } + + long long l = (tries) * distance; + long long h = l; + for (int i = 0; i < distance; i++) { + if (coverAll) + break; + options.push_back(base); + h++; + coverAll = getNextValEncode(options[options.size() - 1], base); + } + valRangeMap[val_indicator] = options[options.size() - 1]; + + STRACE("t_str_detail", tout << "value tester encoding " << printVectorInt(valRangeMap[val_indicator]) << std::endl;); + + // ---------------------------------------------------------------------------------------- + + std::vector orList; + std::vector andList; + + for (long long i = l; i < h; i++) { + orList.push_back(m.mk_eq(val_indicator, m_strutil.mk_string(longlong_to_string(i).c_str()) )); + std::string aStr = gen_val_string(len, options[i - l]); + expr_ref strAst(m_strutil.mk_string(aStr), m); + andList.push_back(m.mk_eq(orList[orList.size() - 1], m.mk_eq(freeVar, strAst))); + } + if (!coverAll) { + orList.push_back(m.mk_eq(val_indicator, m_strutil.mk_string("more"))); + } + + Z3_ast * or_items = new Z3_ast[orList.size()]; + Z3_ast * and_items = new Z3_ast[andList.size() + 1]; + for (int i = 0; i < (int) orList.size(); i++) { + or_items[i] = orList[i]; + } + if (orList.size() > 1) + and_items[0] = Z3_mk_or(ctx, orList.size(), or_items); + else + and_items[0] = or_items[0]; + + for (int i = 0; i < (int) andList.size(); i++) { + and_items[i + 1] = andList[i]; + } + Z3_ast valTestAssert = Z3_mk_and(ctx, andList.size() + 1, and_items); + delete[] or_items; + delete[] and_items; + + // --------------------------------------- + // IF the new value tester is $$_val_x_16_i + // Should add ($$_len_x_j = 16) /\ ($$_val_x_16_i = "more") + // --------------------------------------- + andList.clear(); + andList.push_back(Z3_mk_eq(ctx, len_indicator, my_mk_str_value(t, lenStr.c_str()))); + for (int i = 0; i < tries; i++) { + Z3_ast vTester = fvarValueTesterMap[freeVar][len][i].second; + if (vTester != val_indicator) + andList.push_back(Z3_mk_eq(ctx, vTester, my_mk_str_value(t, "more"))); + } + Z3_ast assertL = NULL; + if (andList.size() == 1) { + assertL = andList[0]; + } else { + Z3_ast * and_items = new Z3_ast[andList.size()]; + for (int i = 0; i < (int) andList.size(); i++) { + and_items[i] = andList[i]; + } + assertL = Z3_mk_and(ctx, andList.size(), and_items); + } + + valTestAssert = Z3_mk_implies(ctx, assertL, valTestAssert); + return valTestAssert; +} + expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, std::string len_valueStr, expr * valTesterInCbEq, std::string valTesterValueStr) { context & ctx = get_context(); @@ -3475,7 +3619,7 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, int testerTotal = fvar_valueTester_map[freeVar][len].size(); int i = 0; for (; i < testerTotal; i++) { - expr * aTester = fvarValueTesterMap[freeVar][len][i].second; + expr * aTester = fvar_valueTester_map[freeVar][len][i].second; if (aTester == valTesterInCbEq) { break; diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index bd26f2564..c9432921a 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -89,6 +89,9 @@ namespace smt { std::map > fvar_lenTester_map; std::map lenTester_fvar_map; + std::map > > > fvar_valueTester_map; + std::map valueTester_fvar_map; + protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); @@ -107,6 +110,7 @@ namespace smt { app * mk_str_var(std::string name); app * mk_nonempty_str_var(); app * mk_internal_xor_var(); + expr * mk_internal_valTest_var(expr * node, int len, int vTries); bool is_concat(app const * a) const { return a->is_app_of(get_id(), OP_STRCAT); } bool is_concat(enode const * n) const { return is_concat(n->get_owner()); } @@ -167,6 +171,9 @@ namespace smt { expr * gen_len_test_options(expr * freeVar, expr * indicator, int tries); expr * gen_free_var_options(expr * freeVar, expr * len_indicator, std::string len_valueStr, expr * valTesterInCbEq, std::string valTesterValueStr); + expr * gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, + std::string lenStr, int tries); + void print_value_tester_list(std::vector > & testerList); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From 9beeb09acf44b8a5d0dc442cf27a1132205f33e9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sun, 15 Nov 2015 15:18:14 -0500 Subject: [PATCH 63/91] model gen possibly done, but I doubt it works so WIP --- src/smt/theory_str.cpp | 76 ++++++++++++++++++++++++++++++++---------- src/smt/theory_str.h | 6 ++++ 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index f86f921de..1961a3e36 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3495,6 +3495,48 @@ void theory_str::print_value_tester_list(std::vector > & t ); } +std::string theory_str::gen_val_string(int len, std::vector & encoding) { + SASSERT(charSetSize > 0); + + std::string re = std::string(len, charSet[0]); + for (int i = 0; i < (int) encoding.size() - 1; i++) { + int idx = encoding[i]; + re[len - 1 - i] = charSet[idx]; + } + return re; +} + +/* + * The return value indicates whether we covered the search space. + * - If the next encoding is valid, return false + * - Otherwise, return true + */ +bool theory_str::get_next_val_encode(std::vector & base, std::vector & next) { + int s = 0; + int carry = 0; + next.clear(); + + for (int i = 0; i < (int) base.size(); i++) { + if (i == 0) { + s = base[i] + 1; + carry = s / charSetSize; + s = s % charSetSize; + next.push_back(s); + } else { + s = base[i] + carry; + carry = s / charSetSize; + s = s % charSetSize; + next.push_back(s); + } + } + if (next[next.size() - 1] > 0) { + next.clear(); + return true; + } else { + return false; + } +} + expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, std::string lenStr, int tries) { context & ctx = get_context(); @@ -3525,7 +3567,7 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * } else { expr * lastestValIndi = fvar_valueTester_map[freeVar][len][tries - 1].second; STRACE("t_str_detail", tout << "last value tester = " << mk_ismt2_pp(lastestValIndi, m) << std::endl;); - coverAll = get_next_val_encode(valRangeMap[lastestValIndi], base); + coverAll = get_next_val_encode(val_range_map[lastestValIndi], base); } long long l = (tries) * distance; @@ -3535,9 +3577,9 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * break; options.push_back(base); h++; - coverAll = getNextValEncode(options[options.size() - 1], base); + coverAll = get_next_val_encode(options[options.size() - 1], base); } - valRangeMap[val_indicator] = options[options.size() - 1]; + val_range_map[val_indicator] = options[options.size() - 1]; STRACE("t_str_detail", tout << "value tester encoding " << printVectorInt(valRangeMap[val_indicator]) << std::endl;); @@ -3556,46 +3598,46 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * orList.push_back(m.mk_eq(val_indicator, m_strutil.mk_string("more"))); } - Z3_ast * or_items = new Z3_ast[orList.size()]; - Z3_ast * and_items = new Z3_ast[andList.size() + 1]; + expr ** or_items = alloc_svect(expr*, orList.size()); + expr ** and_items = alloc_svect(expr*, andList.size() + 1); + for (int i = 0; i < (int) orList.size(); i++) { or_items[i] = orList[i]; } if (orList.size() > 1) - and_items[0] = Z3_mk_or(ctx, orList.size(), or_items); + and_items[0] = m.mk_or(orList.size(), or_items); else and_items[0] = or_items[0]; for (int i = 0; i < (int) andList.size(); i++) { and_items[i + 1] = andList[i]; } - Z3_ast valTestAssert = Z3_mk_and(ctx, andList.size() + 1, and_items); - delete[] or_items; - delete[] and_items; + expr * valTestAssert = m.mk_and(andList.size() + 1, and_items); // --------------------------------------- - // IF the new value tester is $$_val_x_16_i + // If the new value tester is $$_val_x_16_i // Should add ($$_len_x_j = 16) /\ ($$_val_x_16_i = "more") // --------------------------------------- andList.clear(); - andList.push_back(Z3_mk_eq(ctx, len_indicator, my_mk_str_value(t, lenStr.c_str()))); + andList.push_back(m.mk_eq(len_indicator, m_strutil.mk_string(lenStr.c_str()))); for (int i = 0; i < tries; i++) { - Z3_ast vTester = fvarValueTesterMap[freeVar][len][i].second; + expr * vTester = fvar_valueTester_map[freeVar][len][i].second; if (vTester != val_indicator) - andList.push_back(Z3_mk_eq(ctx, vTester, my_mk_str_value(t, "more"))); + andList.push_back(m.mk_eq(vTester, m_strutil.mk_string("more"))); } - Z3_ast assertL = NULL; + expr * assertL = NULL; if (andList.size() == 1) { assertL = andList[0]; } else { - Z3_ast * and_items = new Z3_ast[andList.size()]; + expr ** and_items = alloc_svect(expr*, andList.size()); for (int i = 0; i < (int) andList.size(); i++) { and_items[i] = andList[i]; } - assertL = Z3_mk_and(ctx, andList.size(), and_items); + assertL = m.mk_and(andList.size(), and_items); } - valTestAssert = Z3_mk_implies(ctx, assertL, valTestAssert); + // (assertL => valTestAssert) <=> (!assertL OR valTestAssert) + valTestAssert = m.mk_or(m.mk_not(assertL), valTestAssert); return valTestAssert; } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index c9432921a..b7a63edb3 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -92,6 +92,10 @@ namespace smt { std::map > > > fvar_valueTester_map; std::map valueTester_fvar_map; + std::map > val_range_map; + + int charSetSize = 0; + protected: void assert_axiom(expr * e); void assert_implication(expr * premise, expr * conclusion); @@ -174,6 +178,8 @@ namespace smt { expr * gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, std::string lenStr, int tries); void print_value_tester_list(std::vector > & testerList); + bool get_next_val_encode(std::vector & base, std::vector & next); + std::string gen_val_string(int len, std::vector & encoding); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From b34fc06fe95645db09b8b3eb1778c9607e0c1e94 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 Nov 2015 12:24:23 -0500 Subject: [PATCH 64/91] fix all compilation errors, now to test it --- src/smt/theory_str.cpp | 71 +++++++++++++++++++++--------------------- src/smt/theory_str.h | 13 ++++---- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 1961a3e36..068745d94 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -32,7 +32,9 @@ theory_str::theory_str(ast_manager & m): tmpStringVarCount(0), tmpXorVarCount(0), avoidLoopCut(true), - loopDetected(false) + loopDetected(false), + char_set(NULL), + charSetSize(0) { } @@ -222,7 +224,6 @@ app * theory_str::mk_int(int n) { } expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { - context & ctx = get_context(); ast_manager & m = get_manager(); std::stringstream ss; @@ -240,7 +241,6 @@ expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { } expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { - context & ctx = get_context(); ast_manager & m = get_manager(); std::stringstream ss; ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries; @@ -257,7 +257,6 @@ expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { } app * theory_str::mk_internal_xor_var() { - context & ctx = get_context(); ast_manager & m = get_manager(); std::stringstream ss; ss << tmpXorVarCount; @@ -296,7 +295,6 @@ app * theory_str::mk_str_var(std::string name) { } app * theory_str::mk_nonempty_str_var() { - context & ctx = get_context(); ast_manager & m = get_manager(); std::stringstream ss; ss << tmpStringVarCount; @@ -435,12 +433,10 @@ void theory_str::instantiate_concat_axiom(enode * cat) { SASSERT(is_concat(cat)); app * a_cat = cat->get_owner(); - context & ctx = get_context(); ast_manager & m = get_manager(); // build LHS expr_ref len_xy(m); - // TODO re-use ASTs for length subexpressions, like in old Z3-str? // TODO should we use str_util for these and other expressions? len_xy = mk_strlen(a_cat); SASSERT(len_xy); @@ -2580,9 +2576,6 @@ void theory_str::dump_assignments() { void theory_str::classify_ast_by_type(expr * node, std::map & varMap, std::map & concatMap, std::map & unrollMap) { - context & ctx = get_context(); - ast_manager & m = get_manager(); - // check whether the node is a non-internal string variable; // testing set membership here bypasses several expensive checks if (variable_set.find(node) != variable_set.end() @@ -2620,7 +2613,6 @@ void theory_str::classify_ast_by_type(expr * node, std::map & varMap } } // recursively visit all arguments - app * aNode = to_app(node); for (unsigned i = 0; i < aNode->get_num_args(); ++i) { expr * arg = aNode->get_arg(i); classify_ast_by_type(arg, varMap, concatMap, unrollMap); @@ -2964,7 +2956,7 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map & strVarMap, std::map > & testerList) { +void theory_str::print_value_tester_list(svector > & testerList) { ast_manager & m = get_manager(); STRACE("t_str_detail", int ss = testerList.size(); @@ -3495,13 +3486,14 @@ void theory_str::print_value_tester_list(std::vector > & t ); } -std::string theory_str::gen_val_string(int len, std::vector & encoding) { +std::string theory_str::gen_val_string(int len, int_vector & encoding) { SASSERT(charSetSize > 0); + SASSERT(char_set != NULL); - std::string re = std::string(len, charSet[0]); + std::string re = std::string(len, char_set[0]); for (int i = 0; i < (int) encoding.size() - 1; i++) { int idx = encoding[i]; - re[len - 1 - i] = charSet[idx]; + re[len - 1 - i] = char_set[idx]; } return re; } @@ -3511,10 +3503,10 @@ std::string theory_str::gen_val_string(int len, std::vector & encoding) { * - If the next encoding is valid, return false * - Otherwise, return true */ -bool theory_str::get_next_val_encode(std::vector & base, std::vector & next) { +bool theory_str::get_next_val_encode(int_vector & base, int_vector & next) { int s = 0; int carry = 0; - next.clear(); + next.reset(); for (int i = 0; i < (int) base.size(); i++) { if (i == 0) { @@ -3530,7 +3522,7 @@ bool theory_str::get_next_val_encode(std::vector & base, std::vector & } } if (next[next.size() - 1] > 0) { - next.clear(); + next.reset(); return true; } else { return false; @@ -3539,7 +3531,6 @@ bool theory_str::get_next_val_encode(std::vector & base, std::vector & expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, std::string lenStr, int tries) { - context & ctx = get_context(); ast_manager & m = get_manager(); int distance = 32; @@ -3558,11 +3549,11 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * // ---------------------------------------------------------------------------------------- int len = atoi(lenStr.c_str()); bool coverAll = false; - std::vector > options; - std::vector base; + svector options; + int_vector base; if (tries == 0) { - base = std::vector(len + 1, 0); + base = int_vector(len + 1, 0); coverAll = false; } else { expr * lastestValIndi = fvar_valueTester_map[freeVar][len][tries - 1].second; @@ -3581,12 +3572,20 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * } val_range_map[val_indicator] = options[options.size() - 1]; - STRACE("t_str_detail", tout << "value tester encoding " << printVectorInt(valRangeMap[val_indicator]) << std::endl;); + STRACE("t_str_detail", + tout << "value tester encoding " << "{" << std::endl; + int_vector vec = val_range_map[val_indicator]; + + for (int_vector::iterator it = vec.begin(); it != vec.end(); ++it) { + tout << *it << std::endl; + } + tout << "}" << std::endl; + ); // ---------------------------------------------------------------------------------------- - std::vector orList; - std::vector andList; + ptr_vector orList; + ptr_vector andList; for (long long i = l; i < h; i++) { orList.push_back(m.mk_eq(val_indicator, m_strutil.mk_string(longlong_to_string(i).c_str()) )); @@ -3618,7 +3617,7 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * // If the new value tester is $$_val_x_16_i // Should add ($$_len_x_j = 16) /\ ($$_val_x_16_i = "more") // --------------------------------------- - andList.clear(); + andList.reset(); andList.push_back(m.mk_eq(len_indicator, m_strutil.mk_string(lenStr.c_str()))); for (int i = 0; i < tries; i++) { expr * vTester = fvar_valueTester_map[freeVar][len][i].second; @@ -3646,6 +3645,8 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, context & ctx = get_context(); ast_manager & m = get_manager(); + int sLevel = ctx.get_scope_level(); + int len = atoi(len_valueStr.c_str()); if (fvar_valueTester_map[freeVar].find(len) == fvar_valueTester_map[freeVar].end()) { @@ -3671,12 +3672,12 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, // Z3_ast anEqc = get_eqc_value(t, aTester, anEqcHasValue); get_eqc_value(aTester, anEqcHasValue); if (!anEqcHasValue) { - STRACE("t_str_detail", "value tester " << mk_ismt2_pp(aTester, m) + STRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) << "doesn't have an equivalence class value." << std::endl;); expr_ref makeupAssert(gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i), m); - STRACE("t_str_detail", "var: " << mk_ismt2_pp(freeVar, m) << std::endl + STRACE("t_str_detail", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl << mk_ismt2_pp(makeupAssert, m) << std::endl;); assert_axiom(makeupAssert); } @@ -3701,7 +3702,6 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, } expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) { - context & ctx = get_context(); ast_manager & m = get_manager(); expr * freeVarLen = mk_strlen(freeVar); @@ -3724,12 +3724,12 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr expr ** or_items = alloc_svect(expr*, orList.size()); expr ** and_items = alloc_svect(expr*, andList.size() + 1); - for (int i = 0; i < orList.size(); ++i) { + for (unsigned i = 0; i < orList.size(); ++i) { or_items[i] = orList[i]; } and_items[0] = m.mk_or(orList.size(), or_items); - for(int i = 0; i < andList.size(); ++i) { + for(unsigned i = 0; i < andList.size(); ++i) { and_items[i+1] = andList[i]; } expr * lenTestAssert = m.mk_and(andList.size() + 1, and_items); @@ -3772,7 +3772,6 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr // ----------------------------------------------------------------------------------------------------- expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTesterInCbEq, std::string lenTesterValue) { - context & ctx = get_context(); ast_manager & m = get_manager(); STRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); @@ -3814,7 +3813,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe << " does not have an equivalence class value." << " i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i > 0) { - effectiveLenInd = fvarLenTesterMap[freeVar][i - 1]; + effectiveLenInd = fvar_lenTester_map[freeVar][i - 1]; if (effectiveLenInd == lenTesterInCbEq) { effectiveLenIndiStr = lenTesterValue; } else { @@ -3850,7 +3849,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map[indicator] = freeVar; } else { - indicator = fvarLenTesterMap[freeVar][i]; + indicator = fvar_lenTester_map[freeVar][i]; testNum = i + 1; } expr_ref lenTestAssert(gen_len_test_options(freeVar, indicator, testNum), m); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index b7a63edb3..2f23ce43e 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -89,12 +89,13 @@ namespace smt { std::map > fvar_lenTester_map; std::map lenTester_fvar_map; - std::map > > > fvar_valueTester_map; + std::map > > > fvar_valueTester_map; std::map valueTester_fvar_map; - std::map > val_range_map; + std::map val_range_map; - int charSetSize = 0; + char * char_set; + int charSetSize; protected: void assert_axiom(expr * e); @@ -177,9 +178,9 @@ namespace smt { std::string len_valueStr, expr * valTesterInCbEq, std::string valTesterValueStr); expr * gen_val_options(expr * freeVar, expr * len_indicator, expr * val_indicator, std::string lenStr, int tries); - void print_value_tester_list(std::vector > & testerList); - bool get_next_val_encode(std::vector & base, std::vector & next); - std::string gen_val_string(int len, std::vector & encoding); + void print_value_tester_list(svector > & testerList); + bool get_next_val_encode(int_vector & base, int_vector & next); + std::string gen_val_string(int len, int_vector & encoding); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From bf27d41b0824cab5c80280278d6032c4e8c97c30 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 Nov 2015 12:27:29 -0500 Subject: [PATCH 65/91] use TRACE instead of STRACE... --- src/smt/theory_str.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 068745d94..429c87e62 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2954,7 +2954,7 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map::iterator itor2 = inVarMap.begin(); itor2 != inVarMap.end(); itor2++) { expr * varInFunc = get_alias_index_ast(aliasIndexMap, itor2->first); - STRACE("t_str_detail", tout << "var in unroll = " << + TRACE("t_str_detail", tout << "var in unroll = " << mk_ismt2_pp(itor2->first, m) << std::endl << "dealiased var = " << mk_ismt2_pp(varInFunc, m) << std::endl;); @@ -3471,7 +3471,7 @@ inline std::string longlong_to_string(long long i) { void theory_str::print_value_tester_list(svector > & testerList) { ast_manager & m = get_manager(); - STRACE("t_str_detail", + TRACE("t_str_detail", int ss = testerList.size(); tout << "valueTesterList = {"; for (int i = 0; i < ss; ++i) { @@ -3557,7 +3557,7 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * coverAll = false; } else { expr * lastestValIndi = fvar_valueTester_map[freeVar][len][tries - 1].second; - STRACE("t_str_detail", tout << "last value tester = " << mk_ismt2_pp(lastestValIndi, m) << std::endl;); + TRACE("t_str_detail", tout << "last value tester = " << mk_ismt2_pp(lastestValIndi, m) << std::endl;); coverAll = get_next_val_encode(val_range_map[lastestValIndi], base); } @@ -3572,7 +3572,7 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * } val_range_map[val_indicator] = options[options.size() - 1]; - STRACE("t_str_detail", + TRACE("t_str_detail", tout << "value tester encoding " << "{" << std::endl; int_vector vec = val_range_map[val_indicator]; @@ -3672,12 +3672,12 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, // Z3_ast anEqc = get_eqc_value(t, aTester, anEqcHasValue); get_eqc_value(aTester, anEqcHasValue); if (!anEqcHasValue) { - STRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) + TRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) << "doesn't have an equivalence class value." << std::endl;); expr_ref makeupAssert(gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i), m); - STRACE("t_str_detail", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl + TRACE("t_str_detail", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl << mk_ismt2_pp(makeupAssert, m) << std::endl;); assert_axiom(makeupAssert); } @@ -3774,7 +3774,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe ast_manager & m = get_manager(); - STRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); + TRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); // no length assertions for this free variable have ever been added. if (fvar_len_count_map.find(freeVar) == fvar_len_count_map.end()) { fvar_len_count_map[freeVar] = 1; @@ -3796,7 +3796,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe expr * len_indicator_pre = fvar_lenTester_map[freeVar][i]; bool indicatorHasEqcValue = false; expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); - STRACE("t_str_detail", tout << "length indicator " << mk_ismt2_pp(len_indicator_pre, m) << + TRACE("t_str_detail", tout << "length indicator " << mk_ismt2_pp(len_indicator_pre, m) << " = " << mk_ismt2_pp(len_indicator_value, m) << std::endl;); if (indicatorHasEqcValue) { const char * val = 0; @@ -3809,7 +3809,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe } } else { if (lenTesterInCbEq != len_indicator_pre) { - STRACE("t_str", tout << "WARNING: length indicator " << mk_ismt2_pp(len_indicator_pre, m) + TRACE("t_str", tout << "WARNING: length indicator " << mk_ismt2_pp(len_indicator_pre, m) << " does not have an equivalence class value." << " i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i > 0) { @@ -3839,7 +3839,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe expr * indicator = NULL; unsigned int testNum = 0; - STRACE("t_str", tout << "effectiveLenIndiStr = " << effectiveLenIndiStr + TRACE("t_str", tout << "effectiveLenIndiStr = " << effectiveLenIndiStr << ", i = " << i << ", lenTesterCount = " << lenTesterCount << std::endl;); if (i == lenTesterCount) { @@ -3908,7 +3908,7 @@ void theory_str::process_free_var(std::map & freeVar_map) { } } if (duplicated && dupVar != NULL) { - STRACE("t_str_detail", tout << "Duplicated free variable found:" << mk_ismt2_pp(freeVar, m) + TRACE("t_str_detail", tout << "Duplicated free variable found:" << mk_ismt2_pp(freeVar, m) << " = " << mk_ismt2_pp(dupVar, m) << " (SKIP)" << std::endl;); continue; } else { From 24148bafa3df7df22afedda3a6aac1175f9fc155 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 Nov 2015 15:48:06 -0500 Subject: [PATCH 66/91] fixed several AST bugs; need to load charSet now --- src/smt/theory_str.cpp | 55 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 429c87e62..b2f79b7bc 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -285,6 +285,7 @@ app * theory_str::mk_str_var(std::string name) { app * a = m.mk_const(m.mk_const_decl(sym, string_sort)); // I have a hunch that this may not get internalized for free... + ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != NULL); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); @@ -3504,6 +3505,8 @@ std::string theory_str::gen_val_string(int len, int_vector & encoding) { * - Otherwise, return true */ bool theory_str::get_next_val_encode(int_vector & base, int_vector & next) { + SASSERT(charSetSize > 0); + int s = 0; int carry = 0; next.reset(); @@ -3552,6 +3555,14 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * svector options; int_vector base; + TRACE("t_str_detail", tout + << "freeVar = " << mk_ismt2_pp(freeVar, m) << std::endl + << "len_indicator = " << mk_ismt2_pp(len_indicator, m) << std::endl + << "val_indicator = " << mk_ismt2_pp(val_indicator, m) << std::endl + << "lenstr = " << lenStr << std::endl + << "tries = " << tries << std::endl + ;); + if (tries == 0) { base = int_vector(len + 1, 0); coverAll = false; @@ -3650,6 +3661,7 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, int len = atoi(len_valueStr.c_str()); if (fvar_valueTester_map[freeVar].find(len) == fvar_valueTester_map[freeVar].end()) { + TRACE("t_str_detail", tout << "no previous value testers" << std::endl;); int tries = 0; expr * val_indicator = mk_internal_valTest_var(freeVar, len, tries); valueTester_fvar_map[val_indicator] = freeVar; @@ -3657,6 +3669,7 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, print_value_tester_list(fvar_valueTester_map[freeVar][len]); return gen_val_options(freeVar, len_indicator, val_indicator, len_valueStr, tries); } else { + TRACE("t_str_detail", tout << "checking previous value testers" << std::endl;); // go through all previous value testers // If some doesn't have an eqc value, add its assertion again. int testerTotal = fvar_valueTester_map[freeVar][len].size(); @@ -3704,7 +3717,10 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) { ast_manager & m = get_manager(); - expr * freeVarLen = mk_strlen(freeVar); + TRACE("t_str_detail", tout << "entry" << std::endl;); + + expr_ref freeVarLen(mk_strlen(freeVar), m); + SASSERT(freeVarLen); ptr_vector orList; ptr_vector andList; @@ -3713,9 +3729,16 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr int l = (tries - 1) * distance; int h = tries * distance; + TRACE("t_str_detail", tout << "building andList and orList" << std::endl;); + for (int i = l; i < h; ++i) { - orList.push_back(m.mk_eq(indicator, m_strutil.mk_string(int_to_string(i).c_str()))); - andList.push_back(m.mk_eq(orList[orList.size() - 1], m.mk_eq(freeVarLen, mk_int(i)))); + expr * or_expr = m.mk_eq(indicator, m_strutil.mk_string(int_to_string(i).c_str())); + TRACE("t_str_detail", tout << "or_expr = " << mk_ismt2_pp(or_expr, m) << std::endl;); + orList.push_back(or_expr); + + expr * and_expr = m.mk_eq(orList[orList.size() - 1], m.mk_eq(freeVarLen, mk_int(i))); + TRACE("t_str_detail", tout << "and_expr = " << mk_ismt2_pp(and_expr, m) << std::endl;); + andList.push_back(and_expr); } orList.push_back(m.mk_eq(indicator, m_strutil.mk_string("more"))); @@ -3725,14 +3748,20 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr expr ** and_items = alloc_svect(expr*, andList.size() + 1); for (unsigned i = 0; i < orList.size(); ++i) { + SASSERT(orList[i] != NULL); or_items[i] = orList[i]; } and_items[0] = m.mk_or(orList.size(), or_items); + SASSERT(and_items[0] != NULL); for(unsigned i = 0; i < andList.size(); ++i) { + SASSERT(andList[i] != NULL); and_items[i+1] = andList[i]; } expr * lenTestAssert = m.mk_and(andList.size() + 1, and_items); + SASSERT(lenTestAssert != NULL); + + TRACE("t_str_detail", tout << "lenTestAssert = " << mk_ismt2_pp(lenTestAssert, m) << std::endl;); expr * assertL = NULL; int testerCount = tries - 1; @@ -3750,11 +3779,14 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr } if (assertL != NULL) { + TRACE("t_str_detail", tout << "assertL = " << mk_ismt2_pp(assertL, m) << std::endl;); // return the axiom (assertL -> lenTestAssert) // would like to use mk_implies() here but... expr_ref lenTestAssert(m.mk_or(m.mk_not(assertL), lenTestAssert), m); } + TRACE("t_str_detail", tout << "exit" << std::endl;); + return lenTestAssert; } @@ -3777,16 +3809,23 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe TRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); // no length assertions for this free variable have ever been added. if (fvar_len_count_map.find(freeVar) == fvar_len_count_map.end()) { + + TRACE("t_str_detail", tout << "no length assertions yet" << std::endl;); + fvar_len_count_map[freeVar] = 1; unsigned int testNum = fvar_len_count_map[freeVar]; + expr * indicator = mk_internal_lenTest_var(freeVar, testNum); + SASSERT(indicator != NULL); + fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map[indicator] = freeVar; expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); + SASSERT(lenTestAssert != NULL); return lenTestAssert; } else { - + TRACE("t_str_detail", tout << "found previous length assertions" << std::endl;); expr * effectiveLenInd = NULL; std::string effectiveLenIndiStr = ""; int lenTesterCount = (int) fvar_lenTester_map[freeVar].size(); @@ -3836,6 +3875,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe } // !indicatorHasEqcValue } // for (i : [0..lenTesterCount-1]) if (effectiveLenIndiStr == "more" || effectiveLenIndiStr == "") { + TRACE("t_str", tout << "length is not fixed; generating length tester options for free var" << std::endl;); expr * indicator = NULL; unsigned int testNum = 0; @@ -3852,11 +3892,12 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe indicator = fvar_lenTester_map[freeVar][i]; testNum = i + 1; } - expr_ref lenTestAssert(gen_len_test_options(freeVar, indicator, testNum), m); + expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); return lenTestAssert; } else { + TRACE("t_str", tout << "length is fixed; generating models for free var" << std::endl;); // length is fixed - expr_ref valueAssert(gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, ""), m); + expr * valueAssert = gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, ""); return valueAssert; } } // fVarLenCountMap.find(...) @@ -3956,6 +3997,7 @@ void theory_str::process_free_var(std::map & freeVar_map) { for(std::set::iterator itor1 = leafVarSet.begin(); itor1 != leafVarSet.end(); ++itor1) { expr * toAssert = gen_len_val_options_for_free_var(*itor1, NULL, ""); + SASSERT(toAssert != NULL); assert_axiom(toAssert); } @@ -3964,6 +4006,7 @@ void theory_str::process_free_var(std::map & freeVar_map) { std::set::iterator itor2 = mItor->second.begin(); for(; itor2 != mItor->second.end(); ++itor2) { expr * toAssert = gen_len_val_options_for_free_var(*itor2, NULL, ""); + SASSERT(toAssert != NULL); assert_axiom(toAssert); } } From 9010a5c4cf08764988113b3cf221c47e73d5963a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 Nov 2015 16:05:43 -0500 Subject: [PATCH 67/91] honest-to-goodness working model gen, i.e. it didn't crash. more testing needed --- src/smt/theory_str.cpp | 79 ++++++++++++++++++++++++++++++++++++++++-- src/smt/theory_str.h | 2 ++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index b2f79b7bc..61763b693 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -32,15 +32,88 @@ theory_str::theory_str(ast_manager & m): tmpStringVarCount(0), tmpXorVarCount(0), avoidLoopCut(true), - loopDetected(false), - char_set(NULL), - charSetSize(0) + loopDetected(false) { + initialize_charset(); } theory_str::~theory_str() { } +void theory_str::initialize_charset() { + bool defaultCharset = true; + if (defaultCharset) { + // valid C strings can't contain the null byte ('\0') + charSetSize = 255; + char_set = alloc_svect(char, charSetSize); + int idx = 0; + // small letters + for (int i = 97; i < 123; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // caps + for (int i = 65; i < 91; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // numbers + for (int i = 48; i < 58; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // printable marks - 1 + for (int i = 32; i < 48; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // printable marks - 2 + for (int i = 58; i < 65; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // printable marks - 3 + for (int i = 91; i < 97; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // printable marks - 4 + for (int i = 123; i < 127; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // non-printable - 1 + for (int i = 1; i < 32; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + // non-printable - 2 + for (int i = 127; i < 256; i++) { + char_set[idx] = (char) i; + charSetLookupTable[char_set[idx]] = idx; + idx++; + } + } else { + const char setset[] = { 'a', 'b', 'c' }; + int fSize = sizeof(setset) / sizeof(char); + + char_set = alloc_svect(char, fSize); + charSetSize = fSize; + for (int i = 0; i < charSetSize; i++) { + char_set[i] = setset[i]; + charSetLookupTable[setset[i]] = i; + } + } +} + void theory_str::assert_axiom(expr * e) { if (get_manager().is_true(e)) return; TRACE("t_str_detail", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << std::endl;); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 2f23ce43e..12898d458 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -95,6 +95,7 @@ namespace smt { std::map val_range_map; char * char_set; + std::map charSetLookupTable; int charSetSize; protected: @@ -192,6 +193,7 @@ namespace smt { void get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet); void dump_assignments(); + void initialize_charset(); public: theory_str(ast_manager & m); virtual ~theory_str(); From 07626a1e030427c9a612c3778e67ee5fda6382d8 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 28 Nov 2015 23:56:30 -0500 Subject: [PATCH 68/91] remove expr_ref stuff, start tracking variables more closely --- src/smt/theory_str.cpp | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 61763b693..9cdc53329 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -350,6 +350,8 @@ app * theory_str::mk_str_var(std::string name) { context & ctx = get_context(); ast_manager & m = get_manager(); + TRACE("t_str_detail", tout << "creating string variable " << name << std::endl;); + 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()); @@ -360,6 +362,7 @@ app * theory_str::mk_str_var(std::string name) { // I have a hunch that this may not get internalized for free... ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != NULL); + SASSERT(ctx.e_internalized(a)); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); variable_set.insert(a); @@ -369,26 +372,31 @@ app * theory_str::mk_str_var(std::string name) { } 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(); + + TRACE("t_str_detail", tout << "creating nonempty string variable " << name << std::endl;); + 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)); + ctx.internalize(a, false); + SASSERT(ctx.get_enode(a) != NULL); // 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); + expr * len_str = mk_strlen(a); SASSERT(len_str); // build RHS - expr_ref zero(m); - zero = m_autil.mk_numeral(rational(0), true); + expr * zero = m_autil.mk_numeral(rational(0), true); SASSERT(zero); // build LHS > RHS and assert // we have to build !(LHS <= RHS) instead @@ -2509,7 +2517,7 @@ void theory_str::set_up_axioms(expr * ex) { } } else if (ap->get_num_args() == 0 && !is_string(ap)) { // if ex is a variable, add it to our list of variables - TRACE("t_str_detail", tout << "tracking variable" << std::endl;); + TRACE("t_str_detail", tout << "tracking variable " << mk_ismt2_pp(ap, get_manager()) << std::endl;); variable_set.insert(ex); } } @@ -3347,6 +3355,7 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map free_variables; + TRACE("t_str_detail", tout << variable_set.size() << " variables in variable_set" << std::endl;); for (std::set::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { + TRACE("t_str_detail", tout << "checking eqc of variable " << mk_ismt2_pp(*it, m) << std::endl;); bool has_eqc_value = false; get_eqc_value(*it, has_eqc_value); if (!has_eqc_value) { @@ -3674,7 +3685,7 @@ expr * theory_str::gen_val_options(expr * freeVar, expr * len_indicator, expr * for (long long i = l; i < h; i++) { orList.push_back(m.mk_eq(val_indicator, m_strutil.mk_string(longlong_to_string(i).c_str()) )); std::string aStr = gen_val_string(len, options[i - l]); - expr_ref strAst(m_strutil.mk_string(aStr), m); + expr * strAst = m_strutil.mk_string(aStr); andList.push_back(m.mk_eq(orList[orList.size() - 1], m.mk_eq(freeVar, strAst))); } if (!coverAll) { @@ -3761,7 +3772,7 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, TRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) << "doesn't have an equivalence class value." << std::endl;); - expr_ref makeupAssert(gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i), m); + expr * makeupAssert = gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i); TRACE("t_str_detail", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl << mk_ismt2_pp(makeupAssert, m) << std::endl;); @@ -3779,7 +3790,7 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, valTester)); print_value_tester_list(fvar_valueTester_map[freeVar][len]); } - expr_ref nextAssert(gen_val_options(freeVar, len_indicator, valTester, len_valueStr, i + 1), m); + expr * nextAssert = gen_val_options(freeVar, len_indicator, valTester, len_valueStr, i + 1); return nextAssert; } @@ -3792,7 +3803,7 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr TRACE("t_str_detail", tout << "entry" << std::endl;); - expr_ref freeVarLen(mk_strlen(freeVar), m); + expr * freeVarLen = mk_strlen(freeVar); SASSERT(freeVarLen); ptr_vector orList; @@ -3855,7 +3866,7 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr TRACE("t_str_detail", tout << "assertL = " << mk_ismt2_pp(assertL, m) << std::endl;); // return the axiom (assertL -> lenTestAssert) // would like to use mk_implies() here but... - expr_ref lenTestAssert(m.mk_or(m.mk_not(assertL), lenTestAssert), m); + lenTestAssert = m.mk_or(m.mk_not(assertL), lenTestAssert); } TRACE("t_str_detail", tout << "exit" << std::endl;); From dd0bc13be720fbb3abf976d6d6c134900db74058 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 30 Nov 2015 19:22:01 -0500 Subject: [PATCH 69/91] attempt to track popped variables, still segfaults, WIP --- src/smt/theory_str.cpp | 45 ++++++++++++++++++++++++++++++++++++++---- src/smt/theory_str.h | 2 ++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 9cdc53329..8056864a4 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -329,8 +329,18 @@ expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { */ } +void theory_str::track_variable_scope(expr * var) { + context & ctx = get_context(); + int sLevel = ctx.get_scope_level(); + if (internal_variable_scope_levels.find(sLevel) == internal_variable_scope_levels.end()) { + internal_variable_scope_levels[sLevel] = std::set(); + } + internal_variable_scope_levels[sLevel].insert(var); +} + app * theory_str::mk_internal_xor_var() { ast_manager & m = get_manager(); + context & ctx = get_context(); std::stringstream ss; ss << tmpXorVarCount; tmpXorVarCount++; @@ -342,6 +352,7 @@ app * theory_str::mk_internal_xor_var() { symbol sym(new_buffer); app* a = m.mk_const(m.mk_const_decl(sym, int_sort)); + // TODO ctx.save_ast_trail(a)? return a; } @@ -350,7 +361,8 @@ app * theory_str::mk_str_var(std::string name) { context & ctx = get_context(); ast_manager & m = get_manager(); - TRACE("t_str_detail", tout << "creating string variable " << name << std::endl;); + int sLevel = ctx.get_scope_level(); + TRACE("t_str_detail", tout << "creating string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = m.mk_sort(m_strutil.get_family_id(), STRING_SORT); char * new_buffer = alloc_svect(char, name.length() + 1); @@ -367,6 +379,7 @@ app * theory_str::mk_str_var(std::string name) { variable_set.insert(a); internal_variable_set.insert(a); + track_variable_scope(a); return a; } @@ -380,7 +393,9 @@ app * theory_str::mk_nonempty_str_var() { tmpStringVarCount++; std::string name = "$$_str" + ss.str(); - TRACE("t_str_detail", tout << "creating nonempty string variable " << name << std::endl;); + int sLevel = ctx.get_scope_level(); + + TRACE("t_str_detail", tout << "creating nonempty string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = m.mk_sort(m_strutil.get_family_id(), STRING_SORT); char * new_buffer = alloc_svect(char, name.length() + 1); @@ -408,6 +423,7 @@ app * theory_str::mk_nonempty_str_var() { // add 'a' to variable sets, so we can keep track of it variable_set.insert(a); internal_variable_set.insert(a); + track_variable_scope(a); return a; } @@ -2620,13 +2636,16 @@ void theory_str::assign_eh(bool_var v, bool is_true) { } void theory_str::push_scope_eh() { - TRACE("t_str", tout << "push" << std::endl;); + context & ctx = get_context(); + int sLevel = ctx.get_scope_level(); + TRACE("t_str", tout << "push to " << sLevel << std::endl;); } void theory_str::pop_scope_eh(unsigned num_scopes) { - TRACE("t_str", tout << "pop " << num_scopes << std::endl;); context & ctx = get_context(); int sLevel = ctx.get_scope_level(); + TRACE("t_str", tout << "pop " << num_scopes << " to " << sLevel << std::endl;); + std::map >::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)) { @@ -2639,6 +2658,24 @@ void theory_str::pop_scope_eh(unsigned num_scopes) { } ++varItor; } + + // see if any internal variables went out of scope + for (int check_level = sLevel + num_scopes ; check_level > sLevel; --check_level) { + TRACE("t_str_detail", tout << "cleaning up internal variables at scope level " << check_level << std::endl;); + std::map >::iterator it = internal_variable_scope_levels.find(check_level); + if (it != internal_variable_scope_levels.end()) { + unsigned count = 0; + std::set vars = it->second; + for (std::set::iterator var_it = vars.begin(); var_it != vars.end(); ++var_it) { + variable_set.erase(*var_it); + internal_variable_set.erase(*var_it); + count += 1; + } + TRACE("t_str_detail", tout << "cleaned up " << count << " variables" << std::endl;); + vars.clear(); + } + } + } void theory_str::dump_assignments() { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 12898d458..fe2ff4625 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -82,6 +82,7 @@ namespace smt { std::set variable_set; std::set internal_variable_set; + std::map > internal_variable_scope_levels; std::set input_var_in_len; @@ -113,6 +114,7 @@ namespace smt { void add_cut_info_merge(expr * destNode, int slevel, expr * srcNode); bool has_self_cut(expr * n1, expr * n2); + void track_variable_scope(expr * var); app * mk_str_var(std::string name); app * mk_nonempty_str_var(); app * mk_internal_xor_var(); From c44d49b625661065d8bb3a6da1a0a6015100b3e4 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 1 Dec 2015 14:41:11 -0500 Subject: [PATCH 70/91] keep track of search level ourselves --- src/smt/theory_str.cpp | 11 +++-------- src/smt/theory_str.h | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 8056864a4..b69bb4ac7 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -29,6 +29,7 @@ theory_str::theory_str(ast_manager & m): search_started(false), m_autil(m), m_strutil(m), + sLevel(0), tmpStringVarCount(0), tmpXorVarCount(0), avoidLoopCut(true), @@ -331,7 +332,6 @@ expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { void theory_str::track_variable_scope(expr * var) { context & ctx = get_context(); - int sLevel = ctx.get_scope_level(); if (internal_variable_scope_levels.find(sLevel) == internal_variable_scope_levels.end()) { internal_variable_scope_levels[sLevel] = std::set(); } @@ -361,7 +361,6 @@ app * theory_str::mk_str_var(std::string name) { context & ctx = get_context(); ast_manager & m = get_manager(); - int sLevel = ctx.get_scope_level(); TRACE("t_str_detail", tout << "creating string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = m.mk_sort(m_strutil.get_family_id(), STRING_SORT); @@ -393,8 +392,6 @@ app * theory_str::mk_nonempty_str_var() { tmpStringVarCount++; std::string name = "$$_str" + ss.str(); - int sLevel = ctx.get_scope_level(); - TRACE("t_str_detail", tout << "creating nonempty string variable " << name << " at scope level " << sLevel << std::endl;); sort * string_sort = m.mk_sort(m_strutil.get_family_id(), STRING_SORT); @@ -2637,13 +2634,13 @@ void theory_str::assign_eh(bool_var v, bool is_true) { void theory_str::push_scope_eh() { context & ctx = get_context(); - int sLevel = ctx.get_scope_level(); + sLevel += 1; TRACE("t_str", tout << "push to " << sLevel << std::endl;); } void theory_str::pop_scope_eh(unsigned num_scopes) { context & ctx = get_context(); - int sLevel = ctx.get_scope_level(); + sLevel -= num_scopes; TRACE("t_str", tout << "pop " << num_scopes << " to " << sLevel << std::endl;); std::map >::iterator varItor = cut_var_map.begin(); @@ -3777,8 +3774,6 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, context & ctx = get_context(); ast_manager & m = get_manager(); - int sLevel = ctx.get_scope_level(); - int len = atoi(len_valueStr.c_str()); if (fvar_valueTester_map[freeVar].find(len) == fvar_valueTester_map[freeVar].end()) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index fe2ff4625..ce4a0cfc9 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -63,6 +63,7 @@ namespace smt { }; protected: bool search_started; + int sLevel; arith_util m_autil; str_util m_strutil; From 52f0277c99439419336bbcfb46f08d82d32e7041 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 1 Dec 2015 19:19:00 -0500 Subject: [PATCH 71/91] attempt to clean up out-of-scope variables more, still crashing --- src/smt/theory_str.cpp | 50 +++++++++++++++++++++++++++++++++++++----- src/smt/theory_str.h | 4 +++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index b69bb4ac7..3c6261243 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -32,6 +32,8 @@ theory_str::theory_str(ast_manager & m): sLevel(0), tmpStringVarCount(0), tmpXorVarCount(0), + tmpLenTestVarCount(0), + tmpValTestVarCount(0), avoidLoopCut(true), loopDetected(false) { @@ -297,11 +299,14 @@ app * theory_str::mk_int(int n) { return m_autil.mk_numeral(rational(n), true); } +// We have to work a little bit harder to ensure that all variables we create here are always fresh. + expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { ast_manager & m = get_manager(); std::stringstream ss; - ss << "$$_len_" << mk_ismt2_pp(node, m) << "_" << lTries; + ss << "$$_len_" << mk_ismt2_pp(node, m) << "_" << lTries << "_" << tmpLenTestVarCount; + tmpLenTestVarCount += 1; std::string name = ss.str(); return mk_str_var(name); @@ -317,7 +322,8 @@ expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { ast_manager & m = get_manager(); std::stringstream ss; - ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries; + ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries << "_" << tmpValTestVarCount; + tmpValTestVarCount += 1; std::string name = ss.str(); return mk_str_var(name); @@ -3923,9 +3929,35 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe ast_manager & m = get_manager(); TRACE("t_str_detail", tout << "gen for free var " << mk_ismt2_pp(freeVar, m) << std::endl;); - // no length assertions for this free variable have ever been added. - if (fvar_len_count_map.find(freeVar) == fvar_len_count_map.end()) { + bool map_effectively_empty = false; + if (fvar_len_count_map.find(freeVar) == fvar_len_count_map.end()) { + TRACE("t_str_detail", tout << "fvar_len_count_map is empty" << std::endl;); + map_effectively_empty = true; + } + + if (!map_effectively_empty) { + // check whether any entries correspond to variables that went out of scope; + // if every entry is out of scope then the map counts as being empty + // TODO: maybe remove them from the map instead? either here or in pop_scope_eh() + + // assume empty and find a counterexample + map_effectively_empty = true; + ptr_vector indicator_set = fvar_lenTester_map[freeVar]; + for (ptr_vector::iterator it = indicator_set.begin(); it != indicator_set.end(); ++it) { + expr * indicator = *it; + if (internal_variable_set.find(indicator) != internal_variable_set.end()) { + TRACE("t_str_detail", tout <<"found active internal variable " << mk_ismt2_pp(indicator, m) + << " in fvar_lenTester_map[freeVar]" << std::endl;); + map_effectively_empty = false; + break; + } + } + CTRACE("t_str_detail", map_effectively_empty, tout << "all variables in fvar_lenTester_map[freeVar] out of scope" << std::endl;); + } + + if (map_effectively_empty) { + // no length assertions for this free variable have ever been added. TRACE("t_str_detail", tout << "no length assertions yet" << std::endl;); fvar_len_count_map[freeVar] = 1; @@ -3934,6 +3966,8 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe expr * indicator = mk_internal_lenTest_var(freeVar, testNum); SASSERT(indicator != NULL); + // since the map is "effectively empty", we can remove those variables that have left scope... + fvar_lenTester_map[freeVar].shrink(0); fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map[indicator] = freeVar; @@ -3941,7 +3975,8 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe SASSERT(lenTestAssert != NULL); return lenTestAssert; } else { - TRACE("t_str_detail", tout << "found previous length assertions" << std::endl;); + TRACE("t_str_detail", tout << "found previous in-scope length assertions" << std::endl;); + expr * effectiveLenInd = NULL; std::string effectiveLenIndiStr = ""; int lenTesterCount = (int) fvar_lenTester_map[freeVar].size(); @@ -3949,6 +3984,11 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe int i = 0; for (; i < lenTesterCount; ++i) { expr * len_indicator_pre = fvar_lenTester_map[freeVar][i]; + // check whether this is in scope as well + if (internal_variable_set.find(len_indicator_pre) == internal_variable_set.end()) { + continue; + } + bool indicatorHasEqcValue = false; expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); TRACE("t_str_detail", tout << "length indicator " << mk_ismt2_pp(len_indicator_pre, m) << diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index ce4a0cfc9..f126ca019 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -63,9 +63,9 @@ namespace smt { }; protected: bool search_started; - int sLevel; arith_util m_autil; str_util m_strutil; + int sLevel; str_value_factory * m_factory; @@ -75,6 +75,8 @@ namespace smt { int tmpStringVarCount; int tmpXorVarCount; + int tmpLenTestVarCount; + int tmpValTestVarCount; std::map, std::map > varForBreakConcat; bool avoidLoopCut; From 953a4c5437c5a5d1a2be60e68991f0f15a0b49a2 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Dec 2015 20:48:15 -0500 Subject: [PATCH 72/91] add temporary variables to m_trail --- src/smt/theory_str.cpp | 8 +++++--- src/smt/theory_str.h | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3c6261243..9d0f9d689 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -30,6 +30,7 @@ theory_str::theory_str(ast_manager & m): m_autil(m), m_strutil(m), sLevel(0), + m_trail(m), tmpStringVarCount(0), tmpXorVarCount(0), tmpLenTestVarCount(0), @@ -357,9 +358,8 @@ app * theory_str::mk_internal_xor_var() { strcpy(new_buffer, name.c_str()); symbol sym(new_buffer); - app* a = m.mk_const(m.mk_const_decl(sym, int_sort)); - - // TODO ctx.save_ast_trail(a)? + app * a = m.mk_const(m.mk_const_decl(sym, int_sort)); + m_trail.push_back(a); return a; } @@ -382,6 +382,7 @@ app * theory_str::mk_str_var(std::string name) { SASSERT(ctx.e_internalized(a)); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); + m_trail.push_back(a); variable_set.insert(a); internal_variable_set.insert(a); track_variable_scope(a); @@ -424,6 +425,7 @@ app * theory_str::mk_nonempty_str_var() { } // add 'a' to variable sets, so we can keep track of it + m_trail.push_back(a); variable_set.insert(a); internal_variable_set.insert(a); track_variable_scope(a); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index f126ca019..ca985cb8f 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -67,6 +67,9 @@ namespace smt { str_util m_strutil; int sLevel; + // TODO make sure that all generated expressions are saved into the trail + expr_ref_vector m_trail; // trail for generated terms + str_value_factory * m_factory; ptr_vector m_basicstr_axiom_todo; From 23150d3b5e81064f7717835457ef2689b28aebe2 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Dec 2015 22:03:12 -0500 Subject: [PATCH 73/91] never ever ever reuse constants in mk_string(). this gets us MUCH farther --- src/ast/str_decl_plugin.cpp | 3 ++- src/smt/theory_str.cpp | 30 ++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 5589db56c..550789065 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -101,7 +101,8 @@ func_decl * str_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, app * str_decl_plugin::mk_string(std::string & val) { std::map::iterator it = string_cache.find(val); - if (it == string_cache.end()) { + //if (it == string_cache.end()) { + if (true) { char * new_buffer = alloc_svect(char, (val.length() + 1)); strcpy(new_buffer, val.c_str()); parameter p[1] = {parameter(new_buffer)}; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 9d0f9d689..9b9cc8fd9 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -300,7 +300,8 @@ app * theory_str::mk_int(int n) { return m_autil.mk_numeral(rational(n), true); } -// We have to work a little bit harder to ensure that all variables we create here are always fresh. + +// TODO refactor all of these so that they don't use variable counters, but use ast_manager::mk_fresh_const instead expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { ast_manager & m = get_manager(); @@ -369,12 +370,8 @@ app * theory_str::mk_str_var(std::string name) { TRACE("t_str_detail", tout << "creating string variable " << name << " at scope level " << sLevel << std::endl;); - 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)); + sort * string_sort = m.mk_sort(get_family_id(), STRING_SORT); + app * a = m.mk_fresh_const(name.c_str(), string_sort); // I have a hunch that this may not get internalized for free... ctx.internalize(a, false); @@ -401,12 +398,9 @@ app * theory_str::mk_nonempty_str_var() { TRACE("t_str_detail", tout << "creating nonempty string variable " << name << " at scope level " << sLevel << std::endl;); - 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); + sort * string_sort = m.mk_sort(get_family_id(), STRING_SORT); + app * a = m.mk_fresh_const(name.c_str(), string_sort); - app* a = m.mk_const(m.mk_const_decl(sym, string_sort)); ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != NULL); // assert a variation of the basic string axioms that ensures this string is nonempty @@ -3843,7 +3837,7 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr TRACE("t_str_detail", tout << "entry" << std::endl;); - expr * freeVarLen = mk_strlen(freeVar); + expr_ref freeVarLen(mk_strlen(freeVar), m); SASSERT(freeVarLen); ptr_vector orList; @@ -3856,7 +3850,10 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr TRACE("t_str_detail", tout << "building andList and orList" << std::endl;); for (int i = l; i < h; ++i) { - expr * or_expr = m.mk_eq(indicator, m_strutil.mk_string(int_to_string(i).c_str())); + std::string i_str = int_to_string(i); + expr_ref str_indicator(m_strutil.mk_string(i_str), m); + TRACE("t_str_detail", tout << "just created a string term: " << mk_ismt2_pp(str_indicator, m) << std::endl;); + expr * or_expr = m.mk_eq(indicator, str_indicator); // ARGUMENT 2 IS BOGUS! WRONG SORT TRACE("t_str_detail", tout << "or_expr = " << mk_ismt2_pp(or_expr, m) << std::endl;); orList.push_back(or_expr); @@ -3868,6 +3865,7 @@ expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tr orList.push_back(m.mk_eq(indicator, m_strutil.mk_string("more"))); andList.push_back(m.mk_eq(orList[orList.size() - 1], m_autil.mk_ge(freeVarLen, mk_int(h)))); + // TODO refactor this to use expr_ref_vector/svector/buffer instead expr ** or_items = alloc_svect(expr*, orList.size()); expr ** and_items = alloc_svect(expr*, andList.size() + 1); @@ -3965,8 +3963,8 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe fvar_len_count_map[freeVar] = 1; unsigned int testNum = fvar_len_count_map[freeVar]; - expr * indicator = mk_internal_lenTest_var(freeVar, testNum); - SASSERT(indicator != NULL); + expr_ref indicator(mk_internal_lenTest_var(freeVar, testNum), m); + SASSERT(indicator); // since the map is "effectively empty", we can remove those variables that have left scope... fvar_lenTester_map[freeVar].shrink(0); From 1a15b3937deb367adac5c64dae48e9e1298a6b5d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Dec 2015 22:09:30 -0500 Subject: [PATCH 74/91] in_same_eqc() now checks to ensure both terms are internalized before doing anything else --- src/smt/theory_str.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 9b9cc8fd9..62100cfcd 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1999,6 +1999,20 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { bool theory_str::in_same_eqc(expr * n1, expr * n2) { if (n1 == n2) return true; context & ctx = get_context(); + ast_manager & m = get_manager(); + + // similar to get_eqc_value(), make absolutely sure + // that we've set this up properly for the context + + if (!ctx.e_internalized(n1)) { + TRACE("t_str_detail", tout << "WARNING: expression " << mk_ismt2_pp(n1, m) << " was not internalized" << std::endl;); + ctx.internalize(n1, false); + } + if (!ctx.e_internalized(n2)) { + TRACE("t_str_detail", tout << "WARNING: expression " << mk_ismt2_pp(n2, m) << " was not internalized" << std::endl;); + ctx.internalize(n2, false); + } + enode * n1Node = ctx.get_enode(n1); enode * n2Node = ctx.get_enode(n2); From f5e94af784b19e021e367c688d295298abf214b8 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Dec 2015 22:15:04 -0500 Subject: [PATCH 75/91] check that both simplified expressions are concats in simplify_concat_equality() this seems to fix all the crashes but the solver takes forever to solve a really simple instance with easy model generation, so I think something is still wrong probably next I will go through and change std::map to obj_map, etc. --- src/smt/theory_str.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 62100cfcd..f6187c7c1 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -922,10 +922,6 @@ void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) { expr * new_nn2 = simplify_concat(nn2); app * a_new_nn1 = to_app(new_nn1); app * a_new_nn2 = to_app(new_nn2); - expr * v1_arg0 = a_new_nn1->get_arg(0); - expr * v1_arg1 = a_new_nn1->get_arg(1); - expr * v2_arg0 = a_new_nn2->get_arg(0); - expr * v2_arg1 = a_new_nn2->get_arg(1); TRACE("t_str_detail", tout << "new_nn1 = " << mk_ismt2_pp(new_nn1, m) << std::endl << "new_nn2 = " << mk_ismt2_pp(new_nn2, m) << std::endl;); @@ -960,6 +956,13 @@ void theory_str::simplify_concat_equality(expr * nn1, expr * nn2) { return; } + // TODO what happens if BOTH of these are simplified into non-concat terms? + + expr * v1_arg0 = a_new_nn1->get_arg(0); + expr * v1_arg1 = a_new_nn1->get_arg(1); + expr * v2_arg0 = a_new_nn2->get_arg(0); + expr * v2_arg1 = a_new_nn2->get_arg(1); + if (!in_same_eqc(new_nn1, new_nn2) && (nn1 != new_nn1 || nn2 != new_nn2)) { int ii4 = 0; expr* item[3]; From e010e7c0d606a4059bf8d4fd56777720679402f9 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 2 Dec 2015 23:35:26 -0500 Subject: [PATCH 76/91] add trace message to indicate which free variables are giving us trouble I think I'm onto the issue though --- src/smt/theory_str.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index f6187c7c1..a5584efd8 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -532,6 +532,8 @@ void theory_str::instantiate_concat_axiom(enode * cat) { ast_manager & m = get_manager(); + TRACE("t_str_detail", tout << "instantiating concat axiom for " << mk_ismt2_pp(a_cat, m) << std::endl;); + // build LHS expr_ref len_xy(m); // TODO should we use str_util for these and other expressions? @@ -560,7 +562,6 @@ void theory_str::instantiate_concat_axiom(enode * cat) { // finally assert equality between the two subexpressions app * eq = m.mk_eq(len_xy, len_x_plus_len_y); SASSERT(eq); - TRACE("t_str", tout << mk_ismt2_pp(eq, m) << std::endl;); assert_axiom(eq); } @@ -3443,6 +3444,13 @@ final_check_status theory_str::final_check_eh() { return FC_DONE; } + CTRACE("t_str", needToAssignFreeVars, + tout << "Need to assign values to the following free variables:" << std::endl; + for (std::set::iterator itx = free_variables.begin(); itx != free_variables.end(); ++itx) { + tout << mk_ismt2_pp(*itx, m) << std::endl; + } + ); + // ----------------------------------------------------------- // variables in freeVar are those not bounded by Concats // classify variables in freeVarMap: From cf5eacbf332d175bad5b46b9739016814a3991f3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 3 Dec 2015 20:58:54 -0500 Subject: [PATCH 77/91] successful run of model generation test case, after assigning all internal variables a bogus value if they are unused --- src/smt/theory_str.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index a5584efd8..fb74f4c40 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -3428,20 +3428,37 @@ final_check_status theory_str::final_check_eh() { // If not, mark it as free. bool needToAssignFreeVars = false; std::set free_variables; + std::set unused_internal_variables; TRACE("t_str_detail", tout << variable_set.size() << " variables in variable_set" << std::endl;); for (std::set::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { TRACE("t_str_detail", tout << "checking eqc of variable " << mk_ismt2_pp(*it, m) << std::endl;); bool has_eqc_value = false; get_eqc_value(*it, has_eqc_value); if (!has_eqc_value) { - needToAssignFreeVars = true; - free_variables.insert(*it); + // if this is an internal variable, it can be ignored...I think + if (internal_variable_set.find(*it) != internal_variable_set.end()) { + TRACE("t_str_detail", tout << "WARNING: free internal variable " << mk_ismt2_pp(*it, m) << std::endl;); + unused_internal_variables.insert(*it); + } else { + needToAssignFreeVars = true; + free_variables.insert(*it); + } } } if (!needToAssignFreeVars) { - TRACE("t_str", tout << "All variables are assigned. Done!" << std::endl;); - return FC_DONE; + if (unused_internal_variables.empty()) { + TRACE("t_str", tout << "All variables are assigned. Done!" << std::endl;); + return FC_DONE; + } else { + TRACE("t_str", tout << "Assigning decoy values to free internal variables." << std::endl;); + for (std::set::iterator it = unused_internal_variables.begin(); it != unused_internal_variables.end(); ++it) { + expr * var = *it; + expr_ref assignment(m.mk_eq(var, m_strutil.mk_string("**unused**")), m); + assert_axiom(assignment); + } + return FC_CONTINUE; + } } CTRACE("t_str", needToAssignFreeVars, @@ -4218,7 +4235,7 @@ void theory_str::get_eqc_allUnroll(expr * n, expr * &constStr, std::set & } void theory_str::init_model(model_generator & mg) { - TRACE("t_str", tout << "initializing model" << std::endl; display(tout);); + //TRACE("t_str", tout << "initializing model" << std::endl; display(tout);); m_factory = alloc(str_value_factory, get_manager(), get_family_id()); mg.register_factory(m_factory); } @@ -4287,7 +4304,7 @@ model_value_proc * theory_str::mk_value(enode * n, model_generator & mg) { TRACE("t_str", tout << "WARNING: failed to find a concrete value, falling back" << std::endl;); // TODO make absolutely sure the reason we can't find a concrete value is because of an unassigned temporary // e.g. for an expression like (Concat X $$_str0) - //return alloc(expr_wrapper_proc, m_strutil.mk_string("")); + //return alloc(expr_wrapper_proc, m_strutil.mk_string("**UNUSED**")); NOT_IMPLEMENTED_YET(); } } From a2d0299621c2c7856329ea32c26aa264ed8ff2c3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 7 May 2016 14:19:12 -0400 Subject: [PATCH 78/91] call super in push and pop --- src/smt/theory_str.cpp | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index fb74f4c40..61488638c 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -191,26 +191,6 @@ static void cut_vars_map_copy(std::map & dest, std::map } } -/* -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::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; @@ -2653,9 +2633,11 @@ void theory_str::assign_eh(bool_var v, bool is_true) { } void theory_str::push_scope_eh() { + theory::push_scope_eh(); context & ctx = get_context(); sLevel += 1; TRACE("t_str", tout << "push to " << sLevel << std::endl;); + TRACE("t_str_dump_assign", dump_assignments();); } void theory_str::pop_scope_eh(unsigned num_scopes) { @@ -2692,7 +2674,7 @@ void theory_str::pop_scope_eh(unsigned num_scopes) { vars.clear(); } } - + theory::pop_scope_eh(num_scopes); } void theory_str::dump_assignments() { @@ -3128,7 +3110,7 @@ int theory_str::ctx_dep_analysis(std::map & strVarMap, std::map v2, v5 are constrained by "str" - // - possibliity 2 : concat(v1, "str") = concat(v2, v3) = concat(v4, v5) + // - possibility 2 : concat(v1, "str") = concat(v2, v3) = concat(v4, v5) // ==> v2, v4 are constrained by "str" //-------------------------------------------------------------- From 1d324877cdf16e6547ef0a909a491e9e99ff3cef Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 7 May 2016 15:40:39 -0400 Subject: [PATCH 79/91] use theory_seq's internalize_term --- src/smt/theory_str.cpp | 67 +++++++++++++++++++++++++++++++++++++++--- src/smt/theory_str.h | 4 ++- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 61488638c..fc9a7f3d5 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -139,6 +139,7 @@ void theory_str::assert_implication(expr * premise, expr * conclusion) { } bool theory_str::internalize_atom(app * atom, bool gate_ctx) { + /* TRACE("t_str", tout << "internalizing atom: " << mk_ismt2_pp(atom, get_manager()) << std::endl;); SASSERT(atom->get_family_id() == get_family_id()); @@ -152,15 +153,21 @@ bool theory_str::internalize_atom(app * atom, bool gate_ctx) { ctx.internalize(atom->get_arg(i), false); literal l(ctx.mk_bool_var(atom)); + ctx.set_var_theory(l.var(), get_id()); return true; + */ + return internalize_term(atom); } bool theory_str::internalize_term(app * term) { context & ctx = get_context(); + ast_manager & m = get_manager(); TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); SASSERT(term->get_family_id() == get_family_id()); + + /* // what I had before SASSERT(!ctx.e_internalized(term)); unsigned num_args = term->get_num_args(); @@ -175,15 +182,67 @@ bool theory_str::internalize_term(app * term) { attach_new_th_var(e); - /* - if (is_concat(term)) { - instantiate_concat_axiom(e); - } + //if (is_concat(term)) { + // instantiate_concat_axiom(e); + //} */ + // from theory_seq::internalize_term() + if (ctx.e_internalized(term)) { + enode* e = ctx.get_enode(term); + mk_var(e); + return true; + } + unsigned num_args = term->get_num_args(); + expr* arg; + for (unsigned i = 0; i < num_args; i++) { + arg = term->get_arg(i); + mk_var(ensure_enode(arg)); + } + if (m.is_bool(term)) { + bool_var bv = ctx.mk_bool_var(term); + ctx.set_var_theory(bv, get_id()); + ctx.mark_as_relevant(bv); + } + + enode* e = 0; + if (ctx.e_internalized(term)) { + e = ctx.get_enode(term); + } + else { + e = ctx.mk_enode(term, false, m.is_bool(term), true); + } + mk_var(e); + return true; } +enode* theory_str::ensure_enode(expr* e) { + context& ctx = get_context(); + if (!ctx.e_internalized(e)) { + ctx.internalize(e, false); + } + enode* n = ctx.get_enode(e); + ctx.mark_as_relevant(n); + return n; +} + +theory_var theory_str::mk_var(enode* n) { + if (!m_strutil.is_string(n->get_owner())) { + return null_theory_var; + } + if (is_attached_to_var(n)) { + return n->get_th_var(get_id()); + } + else { + theory_var v = theory::mk_var(n); + // m_find.mk_var(); + get_context().attach_th_var(n, this, v); + get_context().mark_as_relevant(n); + return v; + } +} + static void cut_vars_map_copy(std::map & dest, std::map & src) { std::map::iterator itor = src.begin(); for (; itor != src.end(); itor++) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index ca985cb8f..af2ea1db6 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -208,6 +208,8 @@ namespace smt { protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); + virtual enode* ensure_enode(expr* e); + virtual theory_var mk_var(enode * n); virtual void new_eq_eh(theory_var, theory_var); virtual void new_diseq_eh(theory_var, theory_var); @@ -224,7 +226,7 @@ namespace smt { virtual void propagate(); virtual final_check_status final_check_eh(); - void attach_new_th_var(enode * n); + virtual void attach_new_th_var(enode * n); virtual void init_model(model_generator & m); virtual model_value_proc * mk_value(enode * n, model_generator & mg); From 6dfc2dd9100a39f705000c54f80930f19d65a08c Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 7 May 2016 17:16:31 -0400 Subject: [PATCH 80/91] variables of sort String should now correctly be identified as Very Relevant to the string solver --- src/smt/theory_str.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index fc9a7f3d5..37b68e48c 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -212,7 +212,8 @@ bool theory_str::internalize_term(app * term) { else { e = ctx.mk_enode(term, false, m.is_bool(term), true); } - mk_var(e); + theory_var v = mk_var(e); + TRACE("t_str_detail", tout << "term " << mk_ismt2_pp(term, get_manager()) << " = v#" << v << std::endl;); return true; } @@ -228,9 +229,16 @@ enode* theory_str::ensure_enode(expr* e) { } theory_var theory_str::mk_var(enode* n) { + /* if (!m_strutil.is_string(n->get_owner())) { return null_theory_var; } + */ + // TODO this may require an overhaul of m_strutil.is_string() if things suddenly start working after the following change: + ast_manager & m = get_manager(); + if (!(is_sort_of(m.get_sort(n->get_owner()), m_strutil.get_fid(), STRING_SORT))) { + return null_theory_var; + } if (is_attached_to_var(n)) { return n->get_th_var(get_id()); } @@ -416,6 +424,8 @@ app * theory_str::mk_str_var(std::string name) { ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != NULL); SASSERT(ctx.e_internalized(a)); + // this might help?? + mk_var(ctx.get_enode(a)); m_basicstr_axiom_todo.push_back(ctx.get_enode(a)); m_trail.push_back(a); @@ -442,6 +452,9 @@ app * theory_str::mk_nonempty_str_var() { ctx.internalize(a, false); SASSERT(ctx.get_enode(a) != NULL); + // this might help?? + mk_var(ctx.get_enode(a)); + // assert a variation of the basic string axioms that ensures this string is nonempty { // build LHS @@ -2496,11 +2509,11 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { ); // step 1: Concat == Concat - // I'm disabling this entire code block for now. It may no longer be useful. + // This code block may no longer be useful. // Z3 seems to be putting LHS and RHS into the same equivalence class extremely early. // As a result, simplify_concat_equality() is never getting called, // and if it were called, it would probably get called with the same element on both sides. - /* + bool hasCommon = false; if (eqc_lhs_concat.size() != 0 && eqc_rhs_concat.size() != 0) { std::set::iterator itor1 = eqc_lhs_concat.begin(); @@ -2521,7 +2534,7 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { simplify_concat_equality(*(eqc_lhs_concat.begin()), *(eqc_rhs_concat.begin())); } } - */ + if (eqc_lhs_concat.size() != 0 && eqc_rhs_concat.size() != 0) { // let's pick the first concat in the LHS's eqc // and find some concat in the RHS's eqc that is @@ -2591,6 +2604,10 @@ void theory_str::set_up_axioms(expr * ex) { // if ex is a variable, add it to our list of variables TRACE("t_str_detail", tout << "tracking variable " << mk_ismt2_pp(ap, get_manager()) << std::endl;); variable_set.insert(ex); + ctx.mark_as_relevant(ex); + // this might help?? + theory_var v = mk_var(n); + TRACE("t_str_detail", tout << "variable " << mk_ismt2_pp(ap, get_manager()) << " is #" << v << std::endl;); } } } else { @@ -2637,7 +2654,7 @@ void theory_str::init_search_eh() { * This is done to find equalities between terms, etc. that we otherwise * might not get a chance to see. */ - /* + expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { @@ -2659,7 +2676,6 @@ void theory_str::init_search_eh() { << ": expr ignored" << std::endl;); } } - */ TRACE("t_str", tout << "search started" << std::endl;); search_started = true; From bcaad06061b2981669c84770462192d0886791e3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Sat, 7 May 2016 17:47:50 -0400 Subject: [PATCH 81/91] add theory name; add debug info for freeVar_map --- src/smt/theory_str.cpp | 11 +++++++---- src/smt/theory_str.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 37b68e48c..3903508ea 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2655,6 +2655,7 @@ void theory_str::init_search_eh() { * might not get a chance to see. */ + /* expr_ref_vector assignments(m); ctx.get_assignments(assignments); for (expr_ref_vector::iterator i = assignments.begin(); i != assignments.end(); ++i) { @@ -2676,6 +2677,7 @@ void theory_str::init_search_eh() { << ": expr ignored" << std::endl;); } } + */ TRACE("t_str", tout << "search started" << std::endl;); search_started = true; @@ -2686,16 +2688,12 @@ void theory_str::new_eq_eh(theory_var x, theory_var y) { TRACE("t_str", tout << "new eq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " = " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); handle_equality(get_enode(x)->get_owner(), get_enode(y)->get_owner()); - - TRACE("t_str_dump_assign", dump_assignments();); } void theory_str::new_diseq_eh(theory_var x, theory_var y) { //TRACE("t_str_detail", tout << "new diseq: v#" << x << " != v#" << y << std::endl;); TRACE("t_str", tout << "new diseq: " << mk_ismt2_pp(get_enode(x)->get_owner(), get_manager()) << " != " << mk_ismt2_pp(get_enode(y)->get_owner(), get_manager()) << std::endl;); - - TRACE("t_str_dump_assign", dump_assignments();); } void theory_str::relevant_eh(app * n) { @@ -3523,6 +3521,11 @@ final_check_status theory_str::final_check_eh() { for (std::set::iterator itx = free_variables.begin(); itx != free_variables.end(); ++itx) { tout << mk_ismt2_pp(*itx, m) << std::endl; } + tout << "freeVar_map has the following entries:" << std::endl; + for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { + expr * var = fvIt->first; + tout << mk_ismt2_pp(var, m) << std::endl; + } ); // ----------------------------------------------------------- diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index af2ea1db6..9d56c01fe 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -205,6 +205,8 @@ namespace smt { public: theory_str(ast_manager & m); virtual ~theory_str(); + + virtual char const * get_name() const { return "strings"; } protected: virtual bool internalize_atom(app * atom, bool gate_ctx); virtual bool internalize_term(app * term); From f9e1ed4496a859a771bdb100a34dc2c0cf533f8a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 9 May 2016 18:12:21 -0400 Subject: [PATCH 82/91] add simplify_parent() --- src/ast/str_decl_plugin.cpp | 8 + src/ast/str_decl_plugin.h | 2 + src/smt/theory_str.cpp | 359 +++++++++++++++++++++++++++++++++++- src/smt/theory_str.h | 1 + 4 files changed, 368 insertions(+), 2 deletions(-) diff --git a/src/ast/str_decl_plugin.cpp b/src/ast/str_decl_plugin.cpp index 550789065..cd9cae5a5 100644 --- a/src/ast/str_decl_plugin.cpp +++ b/src/ast/str_decl_plugin.cpp @@ -169,6 +169,14 @@ bool str_recognizers::is_string(expr const * n) const { return is_string(n, & tmp); } +std::string str_recognizers::get_string_constant_value(expr const *n) const { + const char * cstr = 0; + bool isString = is_string(n, & cstr); + SASSERT(isString); + std::string strval(cstr); + return strval; +} + str_util::str_util(ast_manager &m) : str_recognizers(m.mk_family_id(symbol("str"))), m_manager(m) { diff --git a/src/ast/str_decl_plugin.h b/src/ast/str_decl_plugin.h index f1978ab8b..4f46fa5ac 100644 --- a/src/ast/str_decl_plugin.h +++ b/src/ast/str_decl_plugin.h @@ -81,6 +81,8 @@ public: bool is_string(expr const * n, const char ** val) const; bool is_string(expr const * n) const; + + std::string get_string_constant_value(expr const *n) const; // TODO }; diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 3903508ea..254d32141 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -797,6 +797,34 @@ void theory_str::get_nodes_in_concat(expr * node, ptr_vector & nodeList) { } } +// previously Concat() in strTheory.cpp +// Evaluates the concatenation (n1 . n2) with respect to +// the current equivalence classes of n1 and n2. +// Returns a constant string expression representing this concatenation +// if one can be determined, or NULL if this is not possible. +expr * theory_str::eval_concat(expr * n1, expr * n2) { + bool n1HasEqcValue = false; + bool n2HasEqcValue = false; + expr * v1 = get_eqc_value(n1, n1HasEqcValue); + expr * v2 = get_eqc_value(n2, n2HasEqcValue); + if (n1HasEqcValue && n2HasEqcValue) { + std::string n1_str = m_strutil.get_string_constant_value(v1); + std::string n2_str = m_strutil.get_string_constant_value(v2); + std::string result = n1_str + n2_str; + return m_strutil.mk_string(result); + } else if (n1HasEqcValue && !n2HasEqcValue) { + if (m_strutil.get_string_constant_value(v1) == "") { + return n2; + } + } else if (n2HasEqcValue && !n1HasEqcValue) { + if (m_strutil.get_string_constant_value(v2) == "") { + return n1; + } + } + // give up + return NULL; +} + /* * The inputs: * ~ nn: non const node @@ -806,8 +834,298 @@ void theory_str::get_nodes_in_concat(expr * node, ptr_vector & nodeList) { * to see whether some concat nodes can be simplified. */ +// TODO NEXT complete this method! void theory_str::simplify_parent(expr * nn, expr * eq_str) { - // TODO strTheory::simplifyParent() + ast_manager & m = get_manager(); + context & ctx = get_context(); + + TRACE("t_str", tout << "simplifying parents of " << mk_ismt2_pp(nn, m) + << " with respect to " << mk_ismt2_pp(eq_str, m) << std::endl;); + + ctx.internalize(nn, false); + enode * n_eq_enode = ctx.get_enode(nn); + enode * nn_enode = n_eq_enode; + + const char * tmp = 0; + m_strutil.is_string(eq_str, & tmp); + std::string eq_strValue(tmp); + + do { + app * n_eqNode = n_eq_enode->get_owner(); + for (enode_vector::iterator parent_it = n_eq_enode->begin_parents(); parent_it != n_eq_enode->end_parents(); parent_it++) { + enode * e_parent = *parent_it; + app * a_parent = e_parent->get_owner(); + TRACE("t_str_detail", tout << "considering parent " << mk_ismt2_pp(a_parent, m) << std::endl;); + + if (is_concat(a_parent)) { + expr * arg0 = a_parent->get_arg(0); + expr * arg1 = a_parent->get_arg(1); + + // TODO getLenValue() + // int parentLen = getLenValue(a_parent) + int parentLen = -1; + if (arg0 == n_eq_enode->get_owner()) { + // TODO getLenValue() + // int arg0Len = getLenValue(eq_str); + // int arg1Len = getLenValue(arg1); + int arg0Len = -1; + int arg1Len = -1; + + TRACE("t_str_detail", + tout << "simplify_parent #1:" << std::endl + << "* parent = " << mk_ismt2_pp(a_parent, m) << std::endl + << "* |parent| = " << parentLen << std::endl + << "* |arg0| = " << arg0Len << std::endl + << "* |arg1| = " << arg1Len << std::endl; + ); + + if (parentLen != -1 && arg1Len == -1) { + // TODO after getLenValue() above + /* + Z3_ast implyL11 = mk_2_and(t, Z3_mk_eq(ctx, mk_length(t, parent), mk_int(ctx, parentLen)), + Z3_mk_eq(ctx, mk_length(t, arg0), mk_int(ctx, arg0Len))); + int makeUpLenArg1 = parentLen - arg0Len; + Z3_ast lenAss = NULL; + if (makeUpLenArg1 >= 0) { + Z3_ast implyR11 = Z3_mk_eq(ctx, mk_length(t, arg1), mk_int(ctx, makeUpLenArg1)); + lenAss = Z3_mk_implies(ctx, implyL11, implyR11); + } else { + lenAss = Z3_mk_not(ctx, implyL11); + } + addAxiom(t, lenAss, __LINE__); + */ + } + + // (Concat n_eqNode arg1) /\ arg1 has eq const + + expr * concatResult = eval_concat(eq_str, arg1); + if (concatResult != NULL) { + bool arg1HasEqcValue = false; + expr * arg1Value = get_eqc_value(arg1, arg1HasEqcValue); + expr_ref implyL(m); + if (arg1 != arg1Value) { + expr_ref eq_ast1(m); + eq_ast1 = ctx.mk_eq_atom(n_eqNode, eq_str); + SASSERT(eq_ast1); + + expr_ref eq_ast2(m); + eq_ast2 = ctx.mk_eq_atom(arg1, arg1Value); + SASSERT(eq_ast2); + + implyL = m.mk_and(eq_ast1, eq_ast2); + } else { + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + } + + + if (!in_same_eqc(a_parent, concatResult)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, concatResult); + SASSERT(implyR); + + assert_implication(implyL, implyR); + } + } else if (is_concat(n_eqNode)) { + expr_ref simpleConcat(m); + simpleConcat = mk_concat(eq_str, arg1); + if (!in_same_eqc(a_parent, simpleConcat)) { + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + SASSERT(implyL); + + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, simpleConcat); + SASSERT(implyR); + assert_implication(implyL, implyR); + } + } + } // if (arg0 == n_eq_enode->get_owner()) + + if (arg1 == n_eq_enode->get_owner()) { + // TODO getLenValue() + // int arg0Len = getLenValue(arg0); + // int arg1Len = getLenValue(eq_str); + int arg0Len = -1; + int arg1Len = -1; + + TRACE("t_str_detail", + tout << "simplify_parent #2:" << std::endl + << "* parent = " << mk_ismt2_pp(a_parent, m) << std::endl + << "* |parent| = " << parentLen << std::endl + << "* |arg0| = " << arg0Len << std::endl + << "* |arg1| = " << arg1Len << std::endl; + ); + + if (parentLen != -1 && arg0Len == -1) { + // TODO after getLenValue() above + /* + Z3_ast implyL11 = mk_2_and(t, Z3_mk_eq(ctx, mk_length(t, parent), mk_int(ctx, parentLen)), + Z3_mk_eq(ctx, mk_length(t, arg1), mk_int(ctx, arg1Len))); + int makeUpLenArg0 = parentLen - arg1Len; + Z3_ast lenAss = NULL; + if (makeUpLenArg0 >= 0) { + Z3_ast implyR11 = Z3_mk_eq(ctx, mk_length(t, arg0), mk_int(ctx, makeUpLenArg0)); + lenAss = Z3_mk_implies(ctx, implyL11, implyR11); + } else { + lenAss = Z3_mk_not(ctx, implyL11); + } + addAxiom(t, lenAss, __LINE__); + */ + } + + // (Concat arg0 n_eqNode) /\ arg0 has eq const + + expr * concatResult = eval_concat(eq_str, arg1); + if (concatResult != NULL) { + bool arg0HasEqcValue = false; + expr * arg0Value = get_eqc_value(arg0, arg0HasEqcValue); + expr_ref implyL(m); + if (arg0 != arg0Value) { + expr_ref eq_ast1(m); + eq_ast1 = ctx.mk_eq_atom(n_eqNode, eq_str); + SASSERT(eq_ast1); + + expr_ref eq_ast2(m); + eq_ast2 = ctx.mk_eq_atom(arg0, arg0Value); + SASSERT(eq_ast2); + + implyL = m.mk_and(eq_ast1, eq_ast2); + } else { + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + } + + if (!in_same_eqc(a_parent, concatResult)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, concatResult); + SASSERT(implyR); + + assert_implication(implyL, implyR); + } + } else if (is_concat(n_eqNode)) { + expr_ref simpleConcat(m); + simpleConcat = mk_concat(arg0, eq_str); + if (!in_same_eqc(a_parent, simpleConcat)) { + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + SASSERT(implyL); + + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, simpleConcat); + SASSERT(implyR); + assert_implication(implyL, implyR); + } + } + } // if (arg1 == n_eq_enode->get_owner + + + //--------------------------------------------------------- + // Case (2-1) begin: (Concat n_eqNode (Concat str var)) + if (arg0 == n_eqNode && is_concat(to_app(arg1))) { + app * a_arg1 = to_app(arg1); + TRACE("t_str_detail", tout << "simplify_parent #3" << std::endl;); + expr * r_concat_arg0 = a_arg1->get_arg(0); + if (m_strutil.is_string(r_concat_arg0)) { + expr * combined_str = eval_concat(eq_str, r_concat_arg0); + SASSERT(combined_str); + expr * r_concat_arg1 = a_arg1->get_arg(1); + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + expr * simplifiedAst = mk_concat(combined_str, r_concat_arg1); + if (!in_same_eqc(a_parent, simplifiedAst)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, simplifiedAst); + assert_implication(implyL, implyR); + } + } + } + // Case (2-1) end: (Concat n_eqNode (Concat str var)) + //--------------------------------------------------------- + + + //--------------------------------------------------------- + // Case (2-2) begin: (Concat (Concat var str) n_eqNode) + if (is_concat(to_app(arg0)) && arg1 == n_eqNode) { + app * a_arg0 = to_app(arg0); + TRACE("t_str_detail", tout << "simplify_parent #4" << std::endl;); + expr * l_concat_arg1 = a_arg0->get_arg(1); + if (m_strutil.is_string(l_concat_arg1)) { + expr * combined_str = eval_concat(l_concat_arg1, eq_str); + SASSERT(combined_str); + expr * l_concat_arg0 = a_arg0->get_arg(0); + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + expr * simplifiedAst = mk_concat(l_concat_arg0, combined_str); + if (!in_same_eqc(a_parent, simplifiedAst)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(a_parent, simplifiedAst); + assert_implication(implyL, implyR); + } + } + } + // Case (2-2) end: (Concat (Concat var str) n_eqNode) + //--------------------------------------------------------- + + // Have to look up one more layer: if the parent of the concat is another concat + //------------------------------------------------- + // Case (3-1) begin: (Concat (Concat var n_eqNode) str ) + if (arg1 == n_eqNode) { + for (enode_vector::iterator concat_parent_it = e_parent->begin_parents(); + concat_parent_it != e_parent->end_parents(); concat_parent_it++) { + enode * e_concat_parent = *concat_parent_it; + app * concat_parent = e_concat_parent->get_owner(); + if (is_concat(concat_parent)) { + expr * concat_parent_arg0 = concat_parent->get_arg(0); + expr * concat_parent_arg1 = concat_parent->get_arg(1); + if (concat_parent_arg0 == a_parent && m_strutil.is_string(concat_parent_arg1)) { + TRACE("t_str_detail", tout << "simplify_parent #5" << std::endl;); + expr * combinedStr = eval_concat(eq_str, concat_parent_arg1); + SASSERT(combinedStr); + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + expr * simplifiedAst = mk_concat(arg0, combinedStr); + if (!in_same_eqc(concat_parent, simplifiedAst)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(concat_parent, simplifiedAst); + assert_implication(implyL, implyR); + } + } + } + } + } + // Case (3-1) end: (Concat (Concat var n_eqNode) str ) + // Case (3-2) begin: (Concat str (Concat n_eqNode var) ) + if (arg0 == n_eqNode) { + for (enode_vector::iterator concat_parent_it = e_parent->begin_parents(); + concat_parent_it != e_parent->end_parents(); concat_parent_it++) { + enode * e_concat_parent = *concat_parent_it; + app * concat_parent = e_concat_parent->get_owner(); + if (is_concat(concat_parent)) { + expr * concat_parent_arg0 = concat_parent->get_arg(0); + expr * concat_parent_arg1 = concat_parent->get_arg(1); + if (concat_parent_arg1 == a_parent && m_strutil.is_string(concat_parent_arg0)) { + TRACE("t_str_detail", tout << "simplify_parent #6" << std::endl;); + expr * combinedStr = eval_concat(concat_parent_arg0, eq_str); + SASSERT(combinedStr); + expr_ref implyL(m); + implyL = ctx.mk_eq_atom(n_eqNode, eq_str); + expr * simplifiedAst = mk_concat(combinedStr, arg1); + if (!in_same_eqc(concat_parent, simplifiedAst)) { + expr_ref implyR(m); + implyR = ctx.mk_eq_atom(concat_parent, simplifiedAst); + assert_implication(implyL, implyR); + } + } + } + } + } + // Case (3-2) end: (Concat str (Concat n_eqNode var) ) + } // if is_concat(a_parent) + } // for parent_it : n_eq_enode->begin_parents() + + + // check next EQC member + n_eq_enode = n_eq_enode->get_next(); + } while (n_eq_enode != nn_enode); } expr * theory_str::simplify_concat(expr * node) { @@ -2565,7 +2883,44 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { } } - // TODO simplify_parent over eqc + // simplify parents wrt. the equivalence class of both sides + // TODO this is slightly broken, re-enable it once some semantics have been fixed + // Briefly, Z3str2 expects that as this function is entered, + // lhs and rhs are NOT in the same equivalence class yet. + // However, newer versions of Z3 appear to behave differently, + // putting lhs and rhs into the same equivalence class + // *before* this function is called. + // Instead we do something possibly more aggressive here. + /* + bool lhs_has_eqc_value = false; + bool rhs_has_eqc_value = false; + expr * lhs_value = get_eqc_value(lhs, lhs_has_eqc_value); + expr * rhs_value = get_eqc_value(rhs, rhs_has_eqc_value); + if (lhs_has_eqc_value && !rhs_has_eqc_value) { + simplify_parent(rhs, lhs_value); + } + if (!lhs_has_eqc_value && rhs_has_eqc_value) { + simplify_parent(lhs, rhs_value); + } + */ + + bool lhs_has_eqc_value = false; + bool rhs_has_eqc_value = false; + expr * lhs_value = get_eqc_value(lhs, lhs_has_eqc_value); + expr * rhs_value = get_eqc_value(rhs, rhs_has_eqc_value); + + // TODO this depends on the old, possibly broken, semantics of is_string(). + // we explicitly want to test whether lhs/rhs is actually a string constant. + bool lhs_is_string_constant = m_strutil.is_string(lhs); + bool rhs_is_string_constant = m_strutil.is_string(rhs); + + + if (lhs_has_eqc_value && !rhs_is_string_constant) { + simplify_parent(rhs, lhs_value); + } + if (rhs_has_eqc_value && !lhs_is_string_constant) { + simplify_parent(lhs, rhs_value); + } // TODO regex unroll? (much later) } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 9d56c01fe..e167beb18 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -195,6 +195,7 @@ namespace smt { expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); void get_var_in_eqc(expr * n, std::set & varSet); + expr * eval_concat(expr * n1, expr * n2); // strRegex From 9fc1410495bb6184606d603ba2f2eda104e23a9e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 17 May 2016 14:53:17 -0400 Subject: [PATCH 83/91] remove incorrect not-null assertions for model gen --- src/smt/theory_str.cpp | 54 +++++++++++++++++++++++++++++++++++------- src/smt/theory_str.h | 2 ++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 254d32141..eace51dcb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2748,6 +2748,35 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } } +bool theory_str::free_var_attempt(expr * nn1, expr * nn2) { + /* + Z3_context ctx = Z3_theory_get_context(t); + if (getNodeType(t, nn1) == my_Z3_Str_Var) { + std::string vName = std::string(Z3_ast_to_string(ctx, nn1)); + if (vName.length() >= 6) { + std::string vPrefix = vName.substr(0, 6); + // length attempts + if (vPrefix == "$$_len") { + if (getNodeType(t, nn2) == my_Z3_ConstStr) { + moreLenTests(t, nn1, getConstStrValue(t, nn2)); + } + return 1; + } + // value attempts + else if (vPrefix == "$$_val") { + if (getNodeType(t, nn2) == my_Z3_ConstStr && "more" == getConstStrValue(t, nn2)) { + moreValueTests(t, nn1, getConstStrValue(t, nn2)); + } + return 1; + } else if (vPrefix == "$$_uRt") { + return 1; + } + } + } + return 0; + */ +} + void theory_str::handle_equality(expr * lhs, expr * rhs) { ast_manager & m = get_manager(); context & ctx = get_context(); @@ -2761,8 +2790,6 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { return; } - // TODO freeVarAttempt()? - // TODO simplify concat? // newEqCheck() -- check consistency wrt. existing equivalence classes @@ -4245,6 +4272,8 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, return gen_val_options(freeVar, len_indicator, val_indicator, len_valueStr, tries); } else { TRACE("t_str_detail", tout << "checking previous value testers" << std::endl;); + print_value_tester_list(fvar_valueTester_map[freeVar][len]); + // go through all previous value testers // If some doesn't have an eqc value, add its assertion again. int testerTotal = fvar_valueTester_map[freeVar][len].size(); @@ -4258,16 +4287,19 @@ expr * theory_str::gen_free_var_options(expr * freeVar, expr * len_indicator, bool anEqcHasValue = false; // Z3_ast anEqc = get_eqc_value(t, aTester, anEqcHasValue); - get_eqc_value(aTester, anEqcHasValue); + expr * aTester_eqc_value = get_eqc_value(aTester, anEqcHasValue); if (!anEqcHasValue) { TRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) - << "doesn't have an equivalence class value." << std::endl;); + << " doesn't have an equivalence class value." << std::endl;); expr * makeupAssert = gen_val_options(freeVar, len_indicator, aTester, len_valueStr, i); TRACE("t_str_detail", tout << "var: " << mk_ismt2_pp(freeVar, m) << std::endl << mk_ismt2_pp(makeupAssert, m) << std::endl;); assert_axiom(makeupAssert); + } else { + TRACE("t_str_detail", tout << "value tester " << mk_ismt2_pp(aTester, m) + << " == " << mk_ismt2_pp(aTester_eqc_value, m) << std::endl;); } } @@ -4506,6 +4538,7 @@ expr * theory_str::gen_len_val_options_for_free_var(expr * freeVar, expr * lenTe testNum = i + 1; } expr * lenTestAssert = gen_len_test_options(freeVar, indicator, testNum); + SASSERT(lenTestAssert != NULL); return lenTestAssert; } else { TRACE("t_str", tout << "length is fixed; generating models for free var" << std::endl;); @@ -4610,8 +4643,11 @@ void theory_str::process_free_var(std::map & freeVar_map) { for(std::set::iterator itor1 = leafVarSet.begin(); itor1 != leafVarSet.end(); ++itor1) { expr * toAssert = gen_len_val_options_for_free_var(*itor1, NULL, ""); - SASSERT(toAssert != NULL); - assert_axiom(toAssert); + // gen_len_val_options_for_free_var() can legally return NULL, + // as methods that it calls may assert their own axioms instead. + if (toAssert != NULL) { + assert_axiom(toAssert); + } } for (std::map >::iterator mItor = aloneVars.begin(); @@ -4619,8 +4655,10 @@ void theory_str::process_free_var(std::map & freeVar_map) { std::set::iterator itor2 = mItor->second.begin(); for(; itor2 != mItor->second.end(); ++itor2) { expr * toAssert = gen_len_val_options_for_free_var(*itor2, NULL, ""); - SASSERT(toAssert != NULL); - assert_axiom(toAssert); + // same deal with returning a NULL axiom here + if(toAssert != NULL) { + assert_axiom(toAssert); + } } } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index e167beb18..242b37747 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -191,6 +191,8 @@ namespace smt { bool get_next_val_encode(int_vector & base, int_vector & next); std::string gen_val_string(int len, int_vector & encoding); + bool free_var_attempt(expr * nn1, expr * nn2); + expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); expr * getMostRightNodeInConcat(expr * node); From 2f80a9d4aecd78e5c483c6f9410f2dd60d81398e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 17 May 2016 16:31:08 -0400 Subject: [PATCH 84/91] add more_len_tests, more_value_tests --- src/smt/theory_str.cpp | 110 +++++++++++++++++++++++++---------------- src/smt/theory_str.h | 5 ++ 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index eace51dcb..ff0aacfa1 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -357,15 +357,9 @@ expr * theory_str::mk_internal_lenTest_var(expr * node, int lTries) { ss << "$$_len_" << mk_ismt2_pp(node, m) << "_" << lTries << "_" << tmpLenTestVarCount; tmpLenTestVarCount += 1; std::string name = ss.str(); - return mk_str_var(name); - - /* - Z3_context ctx = Z3_theory_get_context(t); - std::stringstream ss; - ss << "$$_len_" << Z3_ast_to_string(ctx, node) << "_" << lTries; - std::string name = ss.str(); - return my_mk_str_var(t, name.c_str()); - */ + app * var = mk_str_var(name); + internal_lenTest_vars.insert(var); + return var; } expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { @@ -374,15 +368,9 @@ expr * theory_str::mk_internal_valTest_var(expr * node, int len, int vTries) { ss << "$$_val_" << mk_ismt2_pp(node, m) << "_" << len << "_" << vTries << "_" << tmpValTestVarCount; tmpValTestVarCount += 1; std::string name = ss.str(); - return mk_str_var(name); - - /* - Z3_context ctx = Z3_theory_get_context(t); - std::stringstream ss; - ss << "$$_val_" << Z3_ast_to_string(ctx, node) << "_" << len << "_" << vTries; - std::string name = ss.str(); - return my_mk_str_var(t, name.c_str()); - */ + app * var = mk_str_var(name); + internal_valTest_vars.insert(var); + return var; } void theory_str::track_variable_scope(expr * var) { @@ -2748,33 +2736,65 @@ void theory_str::solve_concat_eq_str(expr * concat, expr * str) { } } -bool theory_str::free_var_attempt(expr * nn1, expr * nn2) { - /* - Z3_context ctx = Z3_theory_get_context(t); - if (getNodeType(t, nn1) == my_Z3_Str_Var) { - std::string vName = std::string(Z3_ast_to_string(ctx, nn1)); - if (vName.length() >= 6) { - std::string vPrefix = vName.substr(0, 6); - // length attempts - if (vPrefix == "$$_len") { - if (getNodeType(t, nn2) == my_Z3_ConstStr) { - moreLenTests(t, nn1, getConstStrValue(t, nn2)); - } - return 1; +void theory_str::more_len_tests(expr * lenTester, std::string lenTesterValue) { + ast_manager & m = get_manager(); + if (lenTester_fvar_map.find(lenTester) != lenTester_fvar_map.end()) { + expr * fVar = lenTester_fvar_map[lenTester]; + expr * toAssert = gen_len_val_options_for_free_var(fVar, lenTester, lenTesterValue); + TRACE("t_str_detail", tout << "asserting more length tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); + if (toAssert != NULL) { + assert_axiom(toAssert); } - // value attempts - else if (vPrefix == "$$_val") { - if (getNodeType(t, nn2) == my_Z3_ConstStr && "more" == getConstStrValue(t, nn2)) { - moreValueTests(t, nn1, getConstStrValue(t, nn2)); - } - return 1; - } else if (vPrefix == "$$_uRt") { - return 1; - } - } } - return 0; - */ +} + +void theory_str::more_value_tests(expr * valTester, std::string valTesterValue) { + ast_manager & m = get_manager(); + + expr * fVar = valueTester_fvar_map[valTester]; + int lenTesterCount = fvar_lenTester_map[fVar].size(); + + expr * effectiveLenInd = NULL; + std::string effectiveLenIndiStr = ""; + for (int i = 0; i < lenTesterCount; ++i) { + expr * len_indicator_pre = fvar_lenTester_map[fVar][i]; + bool indicatorHasEqcValue = false; + expr * len_indicator_value = get_eqc_value(len_indicator_pre, indicatorHasEqcValue); + if (indicatorHasEqcValue) { + std::string len_pIndiStr = m_strutil.get_string_constant_value(len_indicator_value); + if (len_pIndiStr != "more") { + effectiveLenInd = len_indicator_pre; + effectiveLenIndiStr = len_pIndiStr; + break; + } + } + } + expr * valueAssert = gen_free_var_options(fVar, effectiveLenInd, effectiveLenIndiStr, valTester, valTesterValue); + TRACE("t_str_detail", tout << "asserting more value tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); + if (valueAssert != NULL) { + assert_axiom(valueAssert); + } +} + +bool theory_str::free_var_attempt(expr * nn1, expr * nn2) { + ast_manager & m = get_manager(); + + if (internal_lenTest_vars.contains(nn1) && m_strutil.is_string(nn2)) { + TRACE("t_str", tout << "acting on equivalence between length tester var " << mk_ismt2_pp(nn1, m) + << " and constant " << mk_ismt2_pp(nn2, m) << std::endl;); + more_len_tests(nn1, m_strutil.get_string_constant_value(nn2)); + return true; + } else if (internal_valTest_vars.contains(nn1) && m_strutil.is_string(nn2)) { + std::string nn2_str = m_strutil.get_string_constant_value(nn2); + if (nn2_str == "more") { + TRACE("t_str", tout << "acting on equivalence between value var " << mk_ismt2_pp(nn1, m) + << " and constant " << mk_ismt2_pp(nn2, m) << std::endl;); + more_value_tests(nn1, nn2_str); + } + return true; + } else { + return false; + } } void theory_str::handle_equality(expr * lhs, expr * rhs) { @@ -2790,6 +2810,10 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { return; } + if (free_var_attempt(lhs, rhs) || free_var_attempt(rhs, lhs)) { + return; + } + // TODO simplify concat? // newEqCheck() -- check consistency wrt. existing equivalence classes diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 242b37747..3bb3940b6 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -90,6 +90,9 @@ namespace smt { std::set internal_variable_set; std::map > internal_variable_scope_levels; + obj_hashtable internal_lenTest_vars; + obj_hashtable internal_valTest_vars; + std::set input_var_in_len; std::map fvar_len_count_map; @@ -192,6 +195,8 @@ namespace smt { std::string gen_val_string(int len, int_vector & encoding); bool free_var_attempt(expr * nn1, expr * nn2); + void more_len_tests(expr * lenTester, std::string lenTesterValue); + void more_value_tests(expr * valTester, std::string valTesterValue); expr * get_alias_index_ast(std::map & aliasIndexMap, expr * node); expr * getMostLeftNodeInConcat(expr * node); From 866d97f768b0ed0217e66b30762bf6e6aa4ff5ca Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 17 May 2016 16:45:53 -0400 Subject: [PATCH 85/91] fix eval_concat copy-and-paste error in simplify_parent; concat-eq-concat-case3_sat now passing --- src/smt/theory_str.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index ff0aacfa1..4c936dea5 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -963,7 +963,7 @@ void theory_str::simplify_parent(expr * nn, expr * eq_str) { // (Concat arg0 n_eqNode) /\ arg0 has eq const - expr * concatResult = eval_concat(eq_str, arg1); + expr * concatResult = eval_concat(arg0, eq_str); if (concatResult != NULL) { bool arg0HasEqcValue = false; expr * arg0Value = get_eqc_value(arg0, arg0HasEqcValue); From c8522c5b78a51e537bb36bd5468073be9f588c6b Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 19 May 2016 16:51:43 -0400 Subject: [PATCH 86/91] cleanup before attempting to fix the null enode parent bug --- src/smt/theory_str.cpp | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 4c936dea5..7581baf8d 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -821,8 +821,6 @@ expr * theory_str::eval_concat(expr * n1, expr * n2) { * ~ concat node * to see whether some concat nodes can be simplified. */ - -// TODO NEXT complete this method! void theory_str::simplify_parent(expr * nn, expr * eq_str) { ast_manager & m = get_manager(); context & ctx = get_context(); @@ -834,14 +832,17 @@ void theory_str::simplify_parent(expr * nn, expr * eq_str) { enode * n_eq_enode = ctx.get_enode(nn); enode * nn_enode = n_eq_enode; - const char * tmp = 0; - m_strutil.is_string(eq_str, & tmp); - std::string eq_strValue(tmp); + std::string eq_strValue = m_strutil.get_string_constant_value(eq_str); do { app * n_eqNode = n_eq_enode->get_owner(); + TRACE("t_str_detail", tout << "considering all parents of " << mk_ismt2_pp(n_eqNode, m) << std::endl + << "associated n_eq_enode has " << n_eq_enode->get_num_parents() << " parents" << std::endl;); for (enode_vector::iterator parent_it = n_eq_enode->begin_parents(); parent_it != n_eq_enode->end_parents(); parent_it++) { enode * e_parent = *parent_it; + // TODO deeper bug hiding here + SASSERT(e_parent != NULL); + app * a_parent = e_parent->get_owner(); TRACE("t_str_detail", tout << "considering parent " << mk_ismt2_pp(a_parent, m) << std::endl;); @@ -2814,7 +2815,32 @@ void theory_str::handle_equality(expr * lhs, expr * rhs) { return; } - // TODO simplify concat? + if (is_concat(to_app(lhs)) && is_concat(to_app(rhs))) { + bool nn1HasEqcValue = false; + bool nn2HasEqcValue = false; + expr * nn1_value = get_eqc_value(lhs, nn1HasEqcValue); + expr * nn2_value = get_eqc_value(rhs, nn2HasEqcValue); + if (nn1HasEqcValue && !nn2HasEqcValue) { + simplify_parent(rhs, nn1_value); + } + if (!nn1HasEqcValue && nn2HasEqcValue) { + simplify_parent(lhs, nn2_value); + } + + expr * nn1_arg0 = to_app(lhs)->get_arg(0); + expr * nn1_arg1 = to_app(lhs)->get_arg(1); + expr * nn2_arg0 = to_app(rhs)->get_arg(0); + expr * nn2_arg1 = to_app(rhs)->get_arg(1); + if (nn1_arg0 == nn2_arg0 && in_same_eqc(nn1_arg1, nn2_arg1)) { + TRACE("t_str_detail", tout << "skip: lhs arg0 == rhs arg0" << std::endl;); + return; + } + + if (nn1_arg1 == nn2_arg1 && in_same_eqc(nn1_arg0, nn2_arg0)) { + TRACE("t_str_detail", tout << "skip: lhs arg1 == rhs arg1" << std::endl;); + return; + } + } // newEqCheck() -- check consistency wrt. existing equivalence classes if (!new_eq_check(lhs, rhs)) { From 2f494a96119732443e4d41321a6f97508a16a4ce Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Thu, 19 May 2016 16:57:01 -0400 Subject: [PATCH 87/91] fix null parent bug by making a copy of n_eq_enode->m_parents in simplify_parent --- src/smt/theory_str.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 7581baf8d..bc32e14eb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -838,9 +838,18 @@ void theory_str::simplify_parent(expr * nn, expr * eq_str) { app * n_eqNode = n_eq_enode->get_owner(); TRACE("t_str_detail", tout << "considering all parents of " << mk_ismt2_pp(n_eqNode, m) << std::endl << "associated n_eq_enode has " << n_eq_enode->get_num_parents() << " parents" << std::endl;); - for (enode_vector::iterator parent_it = n_eq_enode->begin_parents(); parent_it != n_eq_enode->end_parents(); parent_it++) { + + // the goal of this next bit is to avoid dereferencing a bogus e_parent in the following loop. + // what I image is causing this bug is that, for example, we examine some parent, we add an axiom that involves it, + // and the parent_it iterator becomes invalidated, because we indirectly modified the container that we're iterating over. + + enode_vector current_parents; + for (enode_vector::const_iterator parent_it = n_eq_enode->begin_parents(); parent_it != n_eq_enode->end_parents(); parent_it++) { + current_parents.insert(*parent_it); + } + + for (enode_vector::iterator parent_it = current_parents.begin(); parent_it != current_parents.end(); ++parent_it) { enode * e_parent = *parent_it; - // TODO deeper bug hiding here SASSERT(e_parent != NULL); app * a_parent = e_parent->get_owner(); From 2522e35c5e90d2a47f5bf4b2a4ad16f300678c6d Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 May 2016 10:22:19 -0400 Subject: [PATCH 88/91] start work on string-integer integration --- src/smt/theory_str.cpp | 33 +++++++++++++++++++++++++++++++++ src/smt/theory_str.h | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index bc32e14eb..05425c61b 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -2363,6 +2363,39 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { return n; } +/* + * Look through the equivalence class of n to find an integer constant. + * Return that constant if it is found. Otherwise, return -1. + * Note that a return value of -1 should not normally be possible, as + * string length cannot be negative. + */ + +rational theory_str::get_len_value(expr * n) { + ast_manager & m = get_manager(); + context & ctx = get_context(); + ctx.internalize(n, false); + + TRACE("t_str_detail", tout << "checking eqc of " << mk_ismt2_pp(n, m) << " for an integer constant" << std::endl;); + + enode * nNode = ctx.get_enode(n); + enode * eqcNode = nNode; + do { + app * ast = eqcNode->get_owner(); + rational val; + bool is_int; + if (m_autil.is_numeral(n, val, is_int)) { + if (is_int) { + TRACE("t_str_detail", tout << "eqc contains integer constant " << val << std::endl;); + SASSERT(!val.is_neg()); + return val; + } + } + } while (eqcNode != nNode); + // not found + TRACE("t_str_detail", tout << "eqc contains no integer constants" << std::endl;); + return rational(-1); +} + /* * Decide whether n1 and n2 are already in the same equivalence class. * This only checks whether the core considers them to be equal; diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 3bb3940b6..cf7ef0060 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -146,6 +146,8 @@ namespace smt { expr * get_eqc_value(expr * n, bool & hasEqcValue); bool in_same_eqc(expr * n1, expr * n2); + rational get_len_value(expr * n); + bool can_two_nodes_eq(expr * n1, expr * n2); bool can_concat_eq_str(expr * concat, std::string str); bool can_concat_eq_concat(expr * concat1, expr * concat2); From ecb069b7018f52b7621a8ac7445d4ce7968db770 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Fri, 20 May 2016 16:34:11 -0400 Subject: [PATCH 89/91] non-fixes to string length code, plus the get_length() code from new Z3 --- src/smt/theory_str.cpp | 135 ++++++++++++++++++++++++++++++++++------- src/smt/theory_str.h | 2 +- 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 05425c61b..d3e842fed 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1440,37 +1440,39 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { 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; + rational x_len = get_len_value(x); + rational y_len = get_len_value(y); + rational m_len = get_len_value(m); + rational n_len = get_len_value(n); int splitType = -1; - if (x_len != -1 && m_len != -1) { - if (x_len < m_len) + if (x_len != rational(-1) && m_len != rational(-1)) { + if (x_len < m_len) { splitType = 0; - else if (x_len == m_len) + } else if (x_len == m_len) { splitType = 1; - else + } else { splitType = 2; + } } - if (splitType == -1 && y_len != -1 && n_len != -1) { - if (y_len > n_len) + if (splitType == -1 && y_len != rational(-1) && n_len != rational(-1)) { + if (y_len > n_len) { splitType = 0; - else if (y_len == n_len) + } else if (y_len == n_len) { splitType = 1; - else + } else { splitType = 2; + } } - TRACE("t_str_detail", tout << "split type " << splitType << std::endl;); + TRACE("t_str_detail", tout + << "len(x) = " << x_len << std::endl + << "len(y) = " << y_len << std::endl + << "len(m) = " << m_len << std::endl + << "len(n) = " << n_len << std::endl + << "split type " << splitType << std::endl; + ); expr * t1 = NULL; expr * t2 = NULL; @@ -2363,6 +2365,93 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { return n; } +// from Z3: theory_seq.cpp + +/* +static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { + theory* th = ctx.get_theory(afid); + if (th && ctx.e_internalized(e)) { + return dynamic_cast(th); + } + else { + return 0; + } +} + +bool theory_seq::get_value(expr* e, rational& val) const { + context& ctx = get_context(); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _val(m); + if (!tha || !tha->get_value(ctx.get_enode(e), _val)) return false; + return m_autil.is_numeral(_val, val) && val.is_int(); +} + +bool theory_seq::lower_bound(expr* _e, rational& lo) const { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _lo(m); + if (!tha || !tha->get_lower(ctx.get_enode(e), _lo)) return false; + return m_autil.is_numeral(_lo, lo) && lo.is_int(); +} + +bool theory_seq::upper_bound(expr* _e, rational& hi) const { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _hi(m); + if (!tha || !tha->get_upper(ctx.get_enode(e), _hi)) return false; + return m_autil.is_numeral(_hi, hi) && hi.is_int(); +} + +bool theory_seq::get_length(expr* e, rational& val) const { + context& ctx = get_context(); + theory* th = ctx.get_theory(m_autil.get_family_id()); + if (!th) return false; + theory_mi_arith* tha = dynamic_cast(th); + if (!tha) return false; + rational val1; + expr_ref len(m), len_val(m); + expr* e1, *e2; + ptr_vector todo; + todo.push_back(e); + val.reset(); + zstring s; + while (!todo.empty()) { + expr* c = todo.back(); + todo.pop_back(); + if (m_util.str.is_concat(c, e1, e2)) { + todo.push_back(e1); + todo.push_back(e2); + } + else if (m_util.str.is_unit(c)) { + val += rational(1); + } + else if (m_util.str.is_empty(c)) { + continue; + } + else if (m_util.str.is_string(c, s)) { + val += rational(s.length()); + } + else if (!has_length(c)) { + return false; + } + else { + len = m_util.str.mk_length(c); + if (ctx.e_internalized(len) && + tha->get_value(ctx.get_enode(len), len_val) && + m_autil.is_numeral(len_val, val1)) { + val += val1; + } + else { + return false; + } + } + } + return val.is_int(); +} +*/ + /* * Look through the equivalence class of n to find an integer constant. * Return that constant if it is found. Otherwise, return -1. @@ -2370,9 +2459,11 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { * string length cannot be negative. */ -rational theory_str::get_len_value(expr * n) { +rational theory_str::get_len_value(expr * x) { ast_manager & m = get_manager(); context & ctx = get_context(); + ctx.internalize(x, false); + expr * n = mk_strlen(x); ctx.internalize(n, false); TRACE("t_str_detail", tout << "checking eqc of " << mk_ismt2_pp(n, m) << " for an integer constant" << std::endl;); @@ -2383,13 +2474,15 @@ rational theory_str::get_len_value(expr * n) { app * ast = eqcNode->get_owner(); rational val; bool is_int; - if (m_autil.is_numeral(n, val, is_int)) { + TRACE("t_str_detail", tout << "eqc member: " << mk_ismt2_pp(ast, m) << std::endl;); + if (m_autil.is_numeral(ast, val, is_int)) { if (is_int) { TRACE("t_str_detail", tout << "eqc contains integer constant " << val << std::endl;); SASSERT(!val.is_neg()); return val; } } + eqcNode = eqcNode->get_next(); } while (eqcNode != nNode); // not found TRACE("t_str_detail", tout << "eqc contains no integer constants" << std::endl;); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index cf7ef0060..946340366 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -146,7 +146,7 @@ namespace smt { expr * get_eqc_value(expr * n, bool & hasEqcValue); bool in_same_eqc(expr * n1, expr * n2); - rational get_len_value(expr * n); + rational get_len_value(expr * x); bool can_two_nodes_eq(expr * n1, expr * n2); bool can_concat_eq_str(expr * concat, std::string str); From f8f7014a1855d40931a3b2b9202f8c23ca617bc3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 1 Jun 2016 16:34:48 -0400 Subject: [PATCH 90/91] use LRA instead of LIA in strings setup, so that the theory_seq integer value code works --- src/smt/smt_setup.cpp | 2 +- src/smt/theory_str.cpp | 68 +++++++++++++++++++++++++----------------- src/smt/theory_str.h | 3 +- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 5e4af91fd..acb03a954 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -700,7 +700,7 @@ namespace smt { } void setup::setup_QF_S() { - setup_QF_LIA(); + setup_QF_LRA(); m_context.register_plugin(alloc(smt::theory_str, m_manager)); } diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index d3e842fed..e2e2f55d1 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -21,6 +21,7 @@ Revision History: #include"ast_pp.h" #include"ast_ll_pp.h" #include +#include"theory_arith.h" namespace smt { @@ -1440,13 +1441,14 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { expr * m = to_app(concatAst2)->get_arg(0); expr * n = to_app(concatAst2)->get_arg(1); - rational x_len = get_len_value(x); - rational y_len = get_len_value(y); - rational m_len = get_len_value(m); - rational n_len = get_len_value(n); + rational x_len, y_len, m_len, n_len; + bool x_len_exists = get_len_value(x, x_len); + bool y_len_exists = get_len_value(y, y_len); + bool m_len_exists = get_len_value(m, m_len); + bool n_len_exists = get_len_value(n, n_len); int splitType = -1; - if (x_len != rational(-1) && m_len != rational(-1)) { + if (x_len_exists && m_len_exists) { if (x_len < m_len) { splitType = 0; } else if (x_len == m_len) { @@ -1456,7 +1458,7 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { } } - if (splitType == -1 && y_len != rational(-1) && n_len != rational(-1)) { + if (splitType == -1 && y_len_exists && n_len_exists) { if (y_len > n_len) { splitType = 0; } else if (y_len == n_len) { @@ -2367,7 +2369,6 @@ expr * theory_str::get_eqc_value(expr * n, bool & hasEqcValue) { // from Z3: theory_seq.cpp -/* static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { theory* th = ctx.get_theory(afid); if (th && ctx.e_internalized(e)) { @@ -2378,15 +2379,18 @@ static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { } } -bool theory_seq::get_value(expr* e, rational& val) const { +bool theory_str::get_value(expr* e, rational& val) const { context& ctx = get_context(); + ast_manager & m = get_manager(); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); expr_ref _val(m); if (!tha || !tha->get_value(ctx.get_enode(e), _val)) return false; return m_autil.is_numeral(_val, val) && val.is_int(); } -bool theory_seq::lower_bound(expr* _e, rational& lo) const { +// TODO bring these in as well +/* +bool theory_str::lower_bound(expr* _e, rational& lo) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); @@ -2395,7 +2399,7 @@ bool theory_seq::lower_bound(expr* _e, rational& lo) const { return m_autil.is_numeral(_lo, lo) && lo.is_int(); } -bool theory_seq::upper_bound(expr* _e, rational& hi) const { +bool theory_str::upper_bound(expr* _e, rational& hi) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); @@ -2403,54 +2407,60 @@ bool theory_seq::upper_bound(expr* _e, rational& hi) const { if (!tha || !tha->get_upper(ctx.get_enode(e), _hi)) return false; return m_autil.is_numeral(_hi, hi) && hi.is_int(); } +*/ -bool theory_seq::get_length(expr* e, rational& val) const { +bool theory_str::get_len_value(expr* e, rational& val) { context& ctx = get_context(); + ast_manager & m = get_manager(); theory* th = ctx.get_theory(m_autil.get_family_id()); - if (!th) return false; + if (!th) { + TRACE("t_str_int", tout << "oops, can't get m_autil's theory" << std::endl;); + return false; + } theory_mi_arith* tha = dynamic_cast(th); - if (!tha) return false; + if (!tha) { + TRACE("t_str_int", tout << "oops, can't cast to theory_mi_arith" << std::endl;); + return false; + } + + TRACE("t_str_int", tout << "checking len value of " << mk_ismt2_pp(e, m) << std::endl;); + rational val1; expr_ref len(m), len_val(m); expr* e1, *e2; ptr_vector todo; todo.push_back(e); val.reset(); - zstring s; while (!todo.empty()) { expr* c = todo.back(); todo.pop_back(); - if (m_util.str.is_concat(c, e1, e2)) { + if (is_concat(to_app(c))) { + e1 = to_app(c)->get_arg(0); + e2 = to_app(c)->get_arg(1); todo.push_back(e1); todo.push_back(e2); } - else if (m_util.str.is_unit(c)) { - val += rational(1); - } - else if (m_util.str.is_empty(c)) { - continue; - } - else if (m_util.str.is_string(c, s)) { - val += rational(s.length()); - } - else if (!has_length(c)) { - return false; + else if (is_string(to_app(c))) { + int sl = m_strutil.get_string_constant_value(c).length(); + val += rational(sl); } else { - len = m_util.str.mk_length(c); + len = mk_strlen(c); if (ctx.e_internalized(len) && tha->get_value(ctx.get_enode(len), len_val) && m_autil.is_numeral(len_val, val1)) { val += val1; + TRACE("t_str_int", tout << "subexpression " << mk_ismt2_pp(len, m) << " has length " << val1 << std::endl;); } else { + TRACE("t_str_int", tout << "subexpression " << mk_ismt2_pp(len, m) << " has no length assignment; bailing out" << std::endl;); return false; } } } + TRACE("t_str_int", tout << "length of " << mk_ismt2_pp(e, m) << " is " << val << std::endl;); return val.is_int(); } -*/ /* * Look through the equivalence class of n to find an integer constant. @@ -2459,6 +2469,7 @@ bool theory_seq::get_length(expr* e, rational& val) const { * string length cannot be negative. */ +/* rational theory_str::get_len_value(expr * x) { ast_manager & m = get_manager(); context & ctx = get_context(); @@ -2488,6 +2499,7 @@ rational theory_str::get_len_value(expr * x) { TRACE("t_str_detail", tout << "eqc contains no integer constants" << std::endl;); return rational(-1); } +*/ /* * Decide whether n1 and n2 are already in the same equivalence class. diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 946340366..da950713f 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -146,7 +146,8 @@ namespace smt { expr * get_eqc_value(expr * n, bool & hasEqcValue); bool in_same_eqc(expr * n1, expr * n2); - rational get_len_value(expr * x); + bool get_value(expr* e, rational& val) const; + bool get_len_value(expr* e, rational& val); bool can_two_nodes_eq(expr * n1, expr * n2); bool can_concat_eq_str(expr * concat, std::string str); From bc79a73779f0b28e10bb98ca22e266362c0c2687 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 1 Jun 2016 17:23:47 -0400 Subject: [PATCH 91/91] lower/upper bound WIP --- src/smt/theory_str.cpp | 21 +++++++++++---------- src/smt/theory_str.h | 2 ++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index e2e2f55d1..f7d31a80b 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -165,7 +165,6 @@ bool theory_str::internalize_atom(app * atom, bool gate_ctx) { bool theory_str::internalize_term(app * term) { context & ctx = get_context(); ast_manager & m = get_manager(); - TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); SASSERT(term->get_family_id() == get_family_id()); /* // what I had before @@ -194,6 +193,7 @@ bool theory_str::internalize_term(app * term) { mk_var(e); return true; } + TRACE("t_str", tout << "internalizing term: " << mk_ismt2_pp(term, get_manager()) << std::endl;); unsigned num_args = term->get_num_args(); expr* arg; for (unsigned i = 0; i < num_args; i++) { @@ -1447,6 +1447,8 @@ void theory_str::process_concat_eq_type1(expr * concatAst1, expr * concatAst2) { bool m_len_exists = get_len_value(m, m_len); bool n_len_exists = get_len_value(n, n_len); + // debugging + int splitType = -1; if (x_len_exists && m_len_exists) { if (x_len < m_len) { @@ -2388,26 +2390,25 @@ bool theory_str::get_value(expr* e, rational& val) const { return m_autil.is_numeral(_val, val) && val.is_int(); } -// TODO bring these in as well -/* -bool theory_str::lower_bound(expr* _e, rational& lo) const { +bool theory_str::lower_bound(expr* _e, rational& lo) { context& ctx = get_context(); - expr_ref e(m_util.str.mk_length(_e), m); + ast_manager & m = get_manager(); + expr_ref e(mk_strlen(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); expr_ref _lo(m); if (!tha || !tha->get_lower(ctx.get_enode(e), _lo)) return false; return m_autil.is_numeral(_lo, lo) && lo.is_int(); } -bool theory_str::upper_bound(expr* _e, rational& hi) const { +bool theory_str::upper_bound(expr* _e, rational& hi) { context& ctx = get_context(); - expr_ref e(m_util.str.mk_length(_e), m); + ast_manager & m = get_manager(); + expr_ref e(mk_strlen(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); expr_ref _hi(m); if (!tha || !tha->get_upper(ctx.get_enode(e), _hi)) return false; return m_autil.is_numeral(_hi, hi) && hi.is_int(); } -*/ bool theory_str::get_len_value(expr* e, rational& val) { context& ctx = get_context(); @@ -2450,10 +2451,10 @@ bool theory_str::get_len_value(expr* e, rational& val) { tha->get_value(ctx.get_enode(len), len_val) && m_autil.is_numeral(len_val, val1)) { val += val1; - TRACE("t_str_int", tout << "subexpression " << mk_ismt2_pp(len, m) << " has length " << val1 << std::endl;); + TRACE("t_str_int", tout << "integer theory: subexpression " << mk_ismt2_pp(len, m) << " has length " << val1 << std::endl;); } else { - TRACE("t_str_int", tout << "subexpression " << mk_ismt2_pp(len, m) << " has no length assignment; bailing out" << std::endl;); + TRACE("t_str_int", tout << "integer theory: subexpression " << mk_ismt2_pp(len, m) << " has no length assignment; bailing out" << std::endl;); return false; } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index da950713f..45c5f3e06 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -148,6 +148,8 @@ namespace smt { bool get_value(expr* e, rational& val) const; bool get_len_value(expr* e, rational& val); + bool lower_bound(expr* _e, rational& lo); + bool upper_bound(expr* _e, rational& hi); bool can_two_nodes_eq(expr * n1, expr * n2); bool can_concat_eq_str(expr * concat, std::string str);