mirror of
https://github.com/Z3Prover/z3
synced 2025-04-29 11:55:51 +00:00
add priority queue to instantiation
This commit is contained in:
parent
22b0c3aa70
commit
46f754c43d
19 changed files with 1138 additions and 541 deletions
256
src/sat/smt/q_queue.cpp
Normal file
256
src/sat/smt/q_queue.cpp
Normal file
|
@ -0,0 +1,256 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
q_queue.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Instantiation queue for quantifiers
|
||||
|
||||
Based on smt/qi_queue
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2021-01-24
|
||||
|
||||
--*/
|
||||
|
||||
#include "sat/smt/euf_solver.h"
|
||||
#include "sat/smt/q_queue.h"
|
||||
#include "sat/smt/q_ematch.h"
|
||||
|
||||
|
||||
namespace q {
|
||||
|
||||
queue::queue(ematch& em, euf::solver& ctx):
|
||||
em(em),
|
||||
ctx(ctx),
|
||||
m(ctx.get_manager()),
|
||||
m_params(ctx.get_config()),
|
||||
m_cost_function(m),
|
||||
m_new_gen_function(m),
|
||||
m_parser(m),
|
||||
m_evaluator(m),
|
||||
m_subst(m)
|
||||
{}
|
||||
|
||||
void queue::setup() {
|
||||
TRACE("q", tout << "qi_cost: " << m_params.m_qi_cost << "\n";);
|
||||
if (!m_parser.parse_string(m_params.m_qi_cost.c_str(), m_cost_function)) {
|
||||
warning_msg("invalid cost function '%s', switching to default one", m_params.m_qi_cost.c_str());
|
||||
VERIFY(m_parser.parse_string("(+ weight generation)", m_cost_function));
|
||||
}
|
||||
if (!m_parser.parse_string(m_params.m_qi_new_gen.c_str(), m_new_gen_function)) {
|
||||
warning_msg("invalid new_gen function '%s', switching to default one", m_params.m_qi_new_gen.c_str());
|
||||
VERIFY(m_parser.parse_string("cost", m_new_gen_function));
|
||||
}
|
||||
m_eager_cost_threshold = m_params.m_qi_eager_threshold;
|
||||
}
|
||||
|
||||
void queue::init_parser_vars() {
|
||||
#define COST 14
|
||||
m_parser.add_var("cost");
|
||||
#define MIN_TOP_GENERATION 13
|
||||
m_parser.add_var("min_top_generation");
|
||||
#define MAX_TOP_GENERATION 12
|
||||
m_parser.add_var("max_top_generation");
|
||||
#define INSTANCES 11
|
||||
m_parser.add_var("instances");
|
||||
#define SIZE 10
|
||||
m_parser.add_var("size");
|
||||
#define DEPTH 9
|
||||
m_parser.add_var("depth");
|
||||
#define GENERATION 8
|
||||
m_parser.add_var("generation");
|
||||
#define QUANT_GENERATION 7
|
||||
m_parser.add_var("quant_generation");
|
||||
#define WEIGHT 6
|
||||
m_parser.add_var("weight");
|
||||
#define VARS 5
|
||||
m_parser.add_var("vars");
|
||||
#define PATTERN_WIDTH 4
|
||||
m_parser.add_var("pattern_width");
|
||||
#define TOTAL_INSTANCES 3
|
||||
m_parser.add_var("total_instances");
|
||||
#define SCOPE 2
|
||||
m_parser.add_var("scope");
|
||||
#define NESTED_QUANTIFIERS 1
|
||||
m_parser.add_var("nested_quantifiers");
|
||||
#define CS_FACTOR 0
|
||||
m_parser.add_var("cs_factor");
|
||||
}
|
||||
|
||||
void queue::set_values(fingerprint& f, float cost) {
|
||||
quantifier_stat * stat = f.c->m_stat;
|
||||
quantifier* q = f.q();
|
||||
app* pat = f.b->m_pattern;
|
||||
unsigned min_top_generation = f.b->m_min_top_generation;
|
||||
unsigned max_top_generation = f.b->m_max_top_generation;
|
||||
m_vals[COST] = cost;
|
||||
m_vals[MIN_TOP_GENERATION] = static_cast<float>(min_top_generation);
|
||||
m_vals[MAX_TOP_GENERATION] = static_cast<float>(max_top_generation);
|
||||
m_vals[INSTANCES] = static_cast<float>(stat->get_num_instances_curr_branch());
|
||||
m_vals[SIZE] = static_cast<float>(stat->get_size());
|
||||
m_vals[DEPTH] = static_cast<float>(stat->get_depth());
|
||||
m_vals[GENERATION] = static_cast<float>(f.m_max_generation);
|
||||
m_vals[QUANT_GENERATION] = static_cast<float>(stat->get_generation());
|
||||
m_vals[WEIGHT] = static_cast<float>(q->get_weight());
|
||||
m_vals[VARS] = static_cast<float>(q->get_num_decls());
|
||||
m_vals[PATTERN_WIDTH] = pat ? static_cast<float>(pat->get_num_args()) : 1.0f;
|
||||
m_vals[TOTAL_INSTANCES] = static_cast<float>(stat->get_num_instances_curr_search());
|
||||
m_vals[SCOPE] = static_cast<float>(ctx.s().num_scopes());
|
||||
m_vals[NESTED_QUANTIFIERS] = static_cast<float>(stat->get_num_nested_quantifiers());
|
||||
m_vals[CS_FACTOR] = static_cast<float>(stat->get_case_split_factor());
|
||||
TRACE("q_detail", for (unsigned i = 0; i < m_vals.size(); i++) { tout << m_vals[i] << " "; } tout << "\n";);
|
||||
}
|
||||
|
||||
float queue::get_cost(fingerprint& f) {
|
||||
set_values(f, 0);
|
||||
float r = m_evaluator(m_cost_function, m_vals.size(), m_vals.c_ptr());
|
||||
f.c->m_stat->update_max_cost(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned queue::get_new_gen(fingerprint& f, float cost) {
|
||||
set_values(f, cost);
|
||||
float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.c_ptr());
|
||||
return std::max(f.m_max_generation + 1, static_cast<unsigned>(r));
|
||||
}
|
||||
|
||||
struct queue::reset_new_entries : public trail<euf::solver> {
|
||||
svector<entry>& m_entries;
|
||||
reset_new_entries(svector<entry>& e): m_entries(e) {}
|
||||
void undo(euf::solver& ctx) override {
|
||||
m_entries.reset();
|
||||
}
|
||||
};
|
||||
|
||||
void queue::insert(fingerprint* f) {
|
||||
float cost = get_cost(*f);
|
||||
if (m_new_entries.empty())
|
||||
ctx.push(reset_new_entries(m_new_entries));
|
||||
m_new_entries.push_back(entry(f, cost));
|
||||
}
|
||||
|
||||
void queue::instantiate(entry& ent) {
|
||||
fingerprint & f = *ent.m_qb;
|
||||
quantifier * q = f.q();
|
||||
unsigned generation = f.m_max_generation;
|
||||
unsigned num_bindings = f.size();
|
||||
euf::enode * const * bindings = f.nodes();
|
||||
q::quantifier_stat * stat = f.c->m_stat;
|
||||
|
||||
ent.m_instantiated = true;
|
||||
|
||||
unsigned gen = get_new_gen(f, ent.m_cost);
|
||||
if (em.propagate(bindings, gen, *f.c))
|
||||
return;
|
||||
|
||||
auto* ebindings = m_subst(q, num_bindings);
|
||||
for (unsigned i = 0; i < num_bindings; ++i)
|
||||
ebindings[i] = bindings[i]->get_expr();
|
||||
expr_ref instance = m_subst();
|
||||
ctx.get_rewriter()(instance);
|
||||
if (m.is_true(instance)) {
|
||||
stat->inc_num_instances_simplify_true();
|
||||
return;
|
||||
}
|
||||
stat->inc_num_instances();
|
||||
|
||||
m_stats.m_num_instances++;
|
||||
|
||||
euf::solver::scoped_generation _sg(ctx, gen);
|
||||
sat::literal result_l = ctx.mk_literal(instance);
|
||||
em.add_instantiation(*f.c, *f.b, result_l);
|
||||
}
|
||||
|
||||
bool queue::propagate() {
|
||||
if (m_new_entries.empty())
|
||||
return false;
|
||||
unsigned since_last_check = 0;
|
||||
for (entry & curr : m_new_entries) {
|
||||
since_last_check = (1 + since_last_check) & 0xFF;
|
||||
if (!m.inc())
|
||||
break;
|
||||
if (0 == since_last_check && ctx.resource_limits_exceeded())
|
||||
break;
|
||||
|
||||
fingerprint& f = *curr.m_qb;
|
||||
|
||||
if (curr.m_cost <= m_eager_cost_threshold)
|
||||
instantiate(curr);
|
||||
else if (m_params.m_qi_promote_unsat && l_false == em.eval(f.nodes(), *f.c)) {
|
||||
// do not delay instances that produce a conflict.
|
||||
TRACE("q", tout << "promoting instance that produces a conflict\n" << mk_pp(f.q(), m) << "\n";);
|
||||
instantiate(curr);
|
||||
}
|
||||
else {
|
||||
TRACE("q", tout << "delaying quantifier instantiation... " << f << "\n" << mk_pp(f.q(), m) << "\ncost: " << curr.m_cost << "\n";);
|
||||
m_delayed_entries.push_back(curr);
|
||||
ctx.push(push_back_vector<euf::solver,svector<entry>>(m_delayed_entries));
|
||||
}
|
||||
}
|
||||
m_new_entries.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
struct queue::reset_instantiated : public trail<euf::solver> {
|
||||
queue& q;
|
||||
unsigned idx;
|
||||
reset_instantiated(queue& q, unsigned idx): q(q), idx(idx) {}
|
||||
void undo(euf::solver& ctx) override {
|
||||
q.m_delayed_entries[idx].m_instantiated = false;
|
||||
}
|
||||
};
|
||||
|
||||
bool queue::lazy_propagate() {
|
||||
if (m_delayed_entries.empty())
|
||||
return false;
|
||||
|
||||
double cost_limit = m_params.m_qi_lazy_threshold;
|
||||
if (m_params.m_qi_conservative_final_check) {
|
||||
bool init = false;
|
||||
cost_limit = 0.0;
|
||||
for (entry & e : m_delayed_entries) {
|
||||
TRACE("q", tout << e.m_qb << ", cost: " << e.m_cost << ", instantiated: " << e.m_instantiated << "\n";);
|
||||
if (!e.m_instantiated && e.m_cost <= m_params.m_qi_lazy_threshold && (!init || e.m_cost < cost_limit)) {
|
||||
init = true;
|
||||
cost_limit = e.m_cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool instantiated = false;
|
||||
unsigned idx = 0;
|
||||
for (entry & e : m_delayed_entries) {
|
||||
if (!e.m_instantiated && e.m_cost <= cost_limit) {
|
||||
instantiated = true;
|
||||
ctx.push(reset_instantiated(*this, idx));
|
||||
m_stats.m_num_lazy_instances++;
|
||||
instantiate(e);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return instantiated;
|
||||
}
|
||||
|
||||
void queue::collect_statistics(::statistics & st) const {
|
||||
float fmin = 0.0f, fmax = 0.0f;
|
||||
bool found = false;
|
||||
for (auto const& e : m_delayed_entries) {
|
||||
if (!e.m_instantiated) {
|
||||
if (found)
|
||||
fmin = std::min(fmin, e.m_cost), fmax = std::max(fmax, e.m_cost);
|
||||
else
|
||||
fmin = e.m_cost, fmax = e.m_cost, found = true;
|
||||
}
|
||||
}
|
||||
st.update("q instantiations", m_stats.m_num_instances);
|
||||
st.update("q lazy instantiations", m_stats.m_num_lazy_instances);
|
||||
st.update("q missed instantiations", m_delayed_entries.size());
|
||||
st.update("q min missed cost", fmin);
|
||||
st.update("q max missed cost", fmax);
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue