mirror of
https://github.com/Z3Prover/z3
synced 2025-06-25 07:13:41 +00:00
sketch possible AC functionality
This commit is contained in:
parent
f932d480a0
commit
20ddfc7795
1 changed files with 98 additions and 19 deletions
|
@ -134,7 +134,7 @@ namespace euf {
|
||||||
//
|
//
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
typedef enum {
|
typedef enum {
|
||||||
INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN,
|
INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN, INITAC,
|
||||||
BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN,
|
BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN,
|
||||||
YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
|
YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
|
||||||
COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE,
|
COMPARE, CHECK, FILTER, CFILTER, PFILTER, CHOOSE, NOOP, CONTINUE,
|
||||||
|
@ -150,7 +150,7 @@ namespace euf {
|
||||||
unsigned m_counter; // how often it was executed
|
unsigned m_counter; // how often it was executed
|
||||||
#endif
|
#endif
|
||||||
bool is_init() const {
|
bool is_init() const {
|
||||||
return m_opcode >= INIT1 && m_opcode <= INITN;
|
return m_opcode >= INIT1 && m_opcode <= INITAC;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -332,12 +332,14 @@ namespace euf {
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & out, const instruction & instr) {
|
std::ostream & operator<<(std::ostream & out, const instruction & instr) {
|
||||||
switch (instr.m_opcode) {
|
switch (instr.m_opcode) {
|
||||||
case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
|
case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: case INITAC:
|
||||||
out << "(INIT";
|
out << "(INIT";
|
||||||
if (instr.m_opcode <= INIT6)
|
if (instr.m_opcode <= INIT6)
|
||||||
out << (instr.m_opcode - INIT1 + 1);
|
out << (instr.m_opcode - INIT1 + 1);
|
||||||
else
|
else if (instr.m_opcode == INITN)
|
||||||
out << "N";
|
out << "N";
|
||||||
|
else
|
||||||
|
out << "AC";
|
||||||
out << ")";
|
out << ")";
|
||||||
break;
|
break;
|
||||||
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
|
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
|
||||||
|
@ -519,6 +521,10 @@ namespace euf {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool arg_compatible(app* f) const {
|
||||||
|
return expected_num_args() == f->get_num_args();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned expected_num_args() const {
|
unsigned expected_num_args() const {
|
||||||
return m_num_args;
|
return m_num_args;
|
||||||
}
|
}
|
||||||
|
@ -626,24 +632,34 @@ namespace euf {
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
|
|
||||||
class code_tree_manager {
|
class code_tree_manager {
|
||||||
euf::mam_solver & ctx;
|
euf::mam_solver& ctx;
|
||||||
label_hasher & m_lbl_hasher;
|
label_hasher& m_lbl_hasher;
|
||||||
region & m_region;
|
region& m_region;
|
||||||
|
|
||||||
template<typename OP>
|
template<typename OP>
|
||||||
OP * mk_instr(opcode op, unsigned size) {
|
OP* mk_instr(opcode op, unsigned size) {
|
||||||
void * mem = m_region.allocate(size);
|
void* mem = m_region.allocate(size);
|
||||||
OP * r = new (mem) OP;
|
OP* r = new (mem) OP;
|
||||||
r->m_opcode = op;
|
r->m_opcode = op;
|
||||||
r->m_next = nullptr;
|
r->m_next = nullptr;
|
||||||
#ifdef _PROFILE_MAM
|
#ifdef _PROFILE_MAM
|
||||||
r->m_counter = 0;
|
r->m_counter = 0;
|
||||||
#endif
|
#endif
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
instruction * mk_init(unsigned n) {
|
bool is_ac(func_decl* f) const {
|
||||||
|
return false && f->is_associative() && f->is_commutative();
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction * mk_init(func_decl* f, unsigned n) {
|
||||||
SASSERT(n >= 1);
|
SASSERT(n >= 1);
|
||||||
|
if (is_ac(f)) {
|
||||||
|
auto* r = mk_instr<initn>(INITAC, sizeof(initn));
|
||||||
|
r->m_num_args = n;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
opcode op = n <= 6 ? static_cast<opcode>(INIT1 + n - 1) : INITN;
|
opcode op = n <= 6 ? static_cast<opcode>(INIT1 + n - 1) : INITN;
|
||||||
if (op == INITN) {
|
if (op == INITN) {
|
||||||
// We store the actual number of arguments for INITN.
|
// We store the actual number of arguments for INITN.
|
||||||
|
@ -668,7 +684,7 @@ namespace euf {
|
||||||
|
|
||||||
code_tree * mk_code_tree(func_decl * lbl, unsigned short num_args, bool filter_candidates) {
|
code_tree * mk_code_tree(func_decl * lbl, unsigned short num_args, bool filter_candidates) {
|
||||||
code_tree * r = alloc(code_tree,m_lbl_hasher, lbl, num_args, filter_candidates);
|
code_tree * r = alloc(code_tree,m_lbl_hasher, lbl, num_args, filter_candidates);
|
||||||
r->m_root = mk_init(num_args);
|
r->m_root = mk_init(lbl, num_args);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1238,7 +1254,7 @@ namespace euf {
|
||||||
m_matched_exprs.reset();
|
m_matched_exprs.reset();
|
||||||
while (!m_todo.empty())
|
while (!m_todo.empty())
|
||||||
linearise_core();
|
linearise_core();
|
||||||
|
|
||||||
if (m_mp->get_num_args() > 1) {
|
if (m_mp->get_num_args() > 1) {
|
||||||
m_mp_already_processed.reset();
|
m_mp_already_processed.reset();
|
||||||
m_mp_already_processed.resize(m_mp->get_num_args());
|
m_mp_already_processed.resize(m_mp->get_num_args());
|
||||||
|
@ -1786,7 +1802,7 @@ namespace euf {
|
||||||
- is_tmp_tree: trail for update operations is created if is_tmp_tree = false.
|
- is_tmp_tree: trail for update operations is created if is_tmp_tree = false.
|
||||||
*/
|
*/
|
||||||
void insert(code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) {
|
void insert(code_tree * tree, quantifier * qa, app * mp, unsigned first_idx, bool is_tmp_tree) {
|
||||||
if (tree->expected_num_args() != to_app(mp->get_arg(first_idx))->get_num_args()) {
|
if (!tree->arg_compatible(to_app(mp->get_arg(first_idx)))) {
|
||||||
// We have to check the number of arguments because of nary + and * operators.
|
// We have to check the number of arguments because of nary + and * operators.
|
||||||
// The E-matching engine that was built when all + and * applications were binary.
|
// The E-matching engine that was built when all + and * applications were binary.
|
||||||
// We ignore the pattern if it does not have the expected number of arguments.
|
// We ignore the pattern if it does not have the expected number of arguments.
|
||||||
|
@ -1845,6 +1861,9 @@ namespace euf {
|
||||||
unsigned m_old_max_generation;
|
unsigned m_old_max_generation;
|
||||||
union {
|
union {
|
||||||
enode * m_curr;
|
enode * m_curr;
|
||||||
|
struct {
|
||||||
|
unsigned m_next_pattern;
|
||||||
|
};
|
||||||
struct {
|
struct {
|
||||||
enode_vector * m_to_recycle;
|
enode_vector * m_to_recycle;
|
||||||
enode * const * m_it;
|
enode * const * m_it;
|
||||||
|
@ -1883,6 +1902,9 @@ namespace euf {
|
||||||
unsigned_vector m_min_top_generation, m_max_top_generation;
|
unsigned_vector m_min_top_generation, m_max_top_generation;
|
||||||
|
|
||||||
pool<enode_vector> m_pool;
|
pool<enode_vector> m_pool;
|
||||||
|
ptr_buffer<enode> m_acargs;
|
||||||
|
bool_vector m_acbitset;
|
||||||
|
unsigned_vector m_acpatarg;
|
||||||
|
|
||||||
enode_vector * mk_enode_vector() {
|
enode_vector * mk_enode_vector() {
|
||||||
enode_vector * r = m_pool.mk();
|
enode_vector * r = m_pool.mk();
|
||||||
|
@ -1987,6 +2009,8 @@ namespace euf {
|
||||||
|
|
||||||
void display_pc_info(std::ostream & out);
|
void display_pc_info(std::ostream & out);
|
||||||
|
|
||||||
|
bool match_ac(initn const* pc);
|
||||||
|
|
||||||
#define INIT_ARGS_SIZE 16
|
#define INIT_ARGS_SIZE 16
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -2219,7 +2243,7 @@ namespace euf {
|
||||||
|
|
||||||
void interpreter::display_instr_input_reg(std::ostream & out, const instruction * instr) {
|
void interpreter::display_instr_input_reg(std::ostream & out, const instruction * instr) {
|
||||||
switch (instr->m_opcode) {
|
switch (instr->m_opcode) {
|
||||||
case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN:
|
case INIT1: case INIT2: case INIT3: case INIT4: case INIT5: case INIT6: case INITN: case INITAC:
|
||||||
display_reg(out, 0);
|
display_reg(out, 0);
|
||||||
break;
|
break;
|
||||||
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
|
case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN:
|
||||||
|
@ -2254,6 +2278,25 @@ namespace euf {
|
||||||
display_instr_input_reg(out, m_pc);
|
display_instr_input_reg(out, m_pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// plan:
|
||||||
|
// - bit-set of matched elements in m_acargs (m_acbitset)
|
||||||
|
// - for each pattern index an index into m_acargs that it matches (m_acpatarg)
|
||||||
|
// when backtracking, take previous pattern index and clear bit-set at position
|
||||||
|
// of pattern_index: try binding the next available position not in the bit-index
|
||||||
|
//
|
||||||
|
// If pattern argument is a variable it can bind to multiple m_acargs
|
||||||
|
// Initially: simply punt. Dont consider these as matches
|
||||||
|
// Naive: iterate over all subsets not in current bitset and use a sequence binding.
|
||||||
|
// Established: use Diophantine equations to capture matchability.
|
||||||
|
//
|
||||||
|
|
||||||
|
bool interpreter::match_ac(initn const* pc) {
|
||||||
|
unsigned f_args = pc->m_num_args;
|
||||||
|
SASSERT(f_args <= m_acargs.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool interpreter::execute_core(code_tree * t, enode * n) {
|
bool interpreter::execute_core(code_tree * t, enode * n) {
|
||||||
TRACE(trigger_bug, tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_expr(), m) << "\n";);
|
TRACE(trigger_bug, tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_expr(), m) << "\n";);
|
||||||
unsigned since_last_check = 0;
|
unsigned since_last_check = 0;
|
||||||
|
@ -2364,6 +2407,37 @@ namespace euf {
|
||||||
m_pc = m_pc->m_next;
|
m_pc = m_pc->m_next;
|
||||||
goto main_loop;
|
goto main_loop;
|
||||||
|
|
||||||
|
case INITAC: {
|
||||||
|
m_app = m_registers[0];
|
||||||
|
m_acargs.reset();
|
||||||
|
m_acargs.push_back(m_app);
|
||||||
|
auto* f = m_app->get_decl();
|
||||||
|
for (unsigned i = 0; i < m_acargs.size(); ++i) {
|
||||||
|
auto* arg = m_acargs[i];
|
||||||
|
if (is_app(arg->get_expr()) && f == arg->get_decl()) {
|
||||||
|
m_acargs.append(arg->num_args(), arg->args());
|
||||||
|
m_acargs[i] = m_acargs.back();
|
||||||
|
m_acargs.pop_back();
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (static_cast<const initn*>(m_pc)->m_num_args > m_acargs.size())
|
||||||
|
goto backtrack;
|
||||||
|
m_acbitset.reset();
|
||||||
|
m_acbitset.reserve(m_acargs.size(), false);
|
||||||
|
m_acpatarg.reset();
|
||||||
|
m_acpatarg.reserve(m_acargs.size(), 0);
|
||||||
|
m_backtrack_stack[m_top].m_instr = m_pc;
|
||||||
|
m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation;
|
||||||
|
m_backtrack_stack[m_top].m_next_pattern = 0;
|
||||||
|
++m_top;
|
||||||
|
// perform the match relative index
|
||||||
|
if (!match_ac(static_cast<initn const*>(m_pc)))
|
||||||
|
goto backtrack;
|
||||||
|
m_pc = m_pc->m_next;
|
||||||
|
goto main_loop;
|
||||||
|
}
|
||||||
|
|
||||||
case COMPARE:
|
case COMPARE:
|
||||||
m_n1 = m_registers[static_cast<const compare *>(m_pc)->m_reg1];
|
m_n1 = m_registers[static_cast<const compare *>(m_pc)->m_reg1];
|
||||||
m_n2 = m_registers[static_cast<const compare *>(m_pc)->m_reg2];
|
m_n2 = m_registers[static_cast<const compare *>(m_pc)->m_reg2];
|
||||||
|
@ -2756,6 +2830,11 @@ namespace euf {
|
||||||
m_pc = m_b->m_next;
|
m_pc = m_b->m_next;
|
||||||
goto main_loop;
|
goto main_loop;
|
||||||
|
|
||||||
|
case INITAC:
|
||||||
|
// this is a backtracking point.
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
goto main_loop;
|
||||||
|
|
||||||
case CONTINUE:
|
case CONTINUE:
|
||||||
++bp.m_it;
|
++bp.m_it;
|
||||||
for (; bp.m_it != bp.m_end; ++bp.m_it) {
|
for (; bp.m_it != bp.m_end; ++bp.m_it) {
|
||||||
|
@ -2867,7 +2946,7 @@ namespace euf {
|
||||||
m_trees.reserve(lbl_id+1, nullptr);
|
m_trees.reserve(lbl_id+1, nullptr);
|
||||||
if (m_trees[lbl_id] == nullptr) {
|
if (m_trees[lbl_id] == nullptr) {
|
||||||
m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false);
|
m_trees[lbl_id] = m_compiler.mk_tree(qa, mp, first_idx, false);
|
||||||
SASSERT(m_trees[lbl_id]->expected_num_args() == p->get_num_args());
|
SASSERT(m_trees[lbl_id]->arg_compatible(p));
|
||||||
DEBUG_CODE(m_trees[lbl_id]->set_egraph(m_egraph););
|
DEBUG_CODE(m_trees[lbl_id]->set_egraph(m_egraph););
|
||||||
ctx.get_trail().push(mk_tree_trail(m_trees, lbl_id));
|
ctx.get_trail().push(mk_tree_trail(m_trees, lbl_id));
|
||||||
}
|
}
|
||||||
|
@ -2877,7 +2956,7 @@ namespace euf {
|
||||||
// The E-matching engine that was built when all + and * applications were binary.
|
// The E-matching engine that was built when all + and * applications were binary.
|
||||||
// We ignore the pattern if it does not have the expected number of arguments.
|
// We ignore the pattern if it does not have the expected number of arguments.
|
||||||
// This is not the ideal solution, but it avoids possible crashes.
|
// This is not the ideal solution, but it avoids possible crashes.
|
||||||
if (tree->expected_num_args() == p->get_num_args())
|
if (tree->arg_compatible(p))
|
||||||
m_compiler.insert(tree, qa, mp, first_idx, false);
|
m_compiler.insert(tree, qa, mp, first_idx, false);
|
||||||
}
|
}
|
||||||
DEBUG_CODE(if (first_idx == 0) {
|
DEBUG_CODE(if (first_idx == 0) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue