mirror of
https://github.com/Z3Prover/z3
synced 2025-12-08 12:53:25 +00:00
merge with master branch
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
commit
651587ce01
1602 changed files with 40496 additions and 27837 deletions
33
src/muz/spacer/CMakeLists.txt
Normal file
33
src/muz/spacer/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
z3_add_component(spacer
|
||||
SOURCES
|
||||
spacer_legacy_mev.cpp
|
||||
spacer_legacy_frames.cpp
|
||||
spacer_context.cpp
|
||||
spacer_dl_interface.cpp
|
||||
spacer_farkas_learner.cpp
|
||||
spacer_generalizers.cpp
|
||||
spacer_manager.cpp
|
||||
spacer_marshal.cpp
|
||||
spacer_prop_solver.cpp
|
||||
spacer_smt_context_manager.cpp
|
||||
spacer_sym_mux.cpp
|
||||
spacer_util.cpp
|
||||
spacer_itp_solver.cpp
|
||||
spacer_virtual_solver.cpp
|
||||
spacer_legacy_mbp.cpp
|
||||
spacer_proof_utils.cpp
|
||||
spacer_unsat_core_learner.cpp
|
||||
spacer_unsat_core_plugin.cpp
|
||||
spacer_matrix.cpp
|
||||
spacer_min_cut.cpp
|
||||
spacer_antiunify.cpp
|
||||
spacer_mev_array.cpp
|
||||
spacer_qe_project.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
arith_tactics
|
||||
core_tactics
|
||||
muz
|
||||
qe
|
||||
smt_tactic
|
||||
transforms
|
||||
)
|
||||
459
src/muz/spacer/spacer_antiunify.cpp
Normal file
459
src/muz/spacer/spacer_antiunify.cpp
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_antiunify.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Antiunification utilities
|
||||
|
||||
Author:
|
||||
|
||||
Bernhard Gleiss
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include"muz/spacer/spacer_antiunify.h"
|
||||
#include"ast/ast.h"
|
||||
#include"ast/rewriter/rewriter.h"
|
||||
#include"ast/rewriter/rewriter_def.h"
|
||||
#include"ast/arith_decl_plugin.h"
|
||||
#include"ast/ast_util.h"
|
||||
#include"ast/expr_abstract.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
// Abstracts numeric values by variables
|
||||
struct var_abs_rewriter : public default_rewriter_cfg {
|
||||
ast_manager &m;
|
||||
arith_util m_util;
|
||||
ast_mark m_seen;
|
||||
ast_mark m_has_num;
|
||||
unsigned m_var_index;
|
||||
expr_ref_vector m_pinned;
|
||||
obj_map<expr, expr*>& m_substitution;
|
||||
ptr_vector<expr> m_stack;
|
||||
|
||||
var_abs_rewriter (ast_manager &manager, obj_map<expr, expr*>& substitution,
|
||||
unsigned k = 0) :
|
||||
m(manager), m_util(m), m_var_index(k),
|
||||
m_pinned(m), m_substitution(substitution) {}
|
||||
|
||||
void reset(unsigned k = 0) {
|
||||
m_pinned.reset();
|
||||
m_var_index = k;
|
||||
}
|
||||
|
||||
bool pre_visit(expr * t) {
|
||||
bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t));
|
||||
// only unify if convex closure will not contain non-linear multiplication
|
||||
if (m_util.is_mul(t))
|
||||
{
|
||||
bool contains_const_child = false;
|
||||
app* a = to_app(t);
|
||||
for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) {
|
||||
if (m_util.is_numeral(a->get_arg(i))) {
|
||||
contains_const_child = true;
|
||||
}
|
||||
}
|
||||
if (!contains_const_child) {r = false;}
|
||||
}
|
||||
if (r) {m_stack.push_back (t);}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
br_status reduce_app (func_decl * f, unsigned num, expr * const * args,
|
||||
expr_ref & result, proof_ref & result_pr) {
|
||||
expr *s;
|
||||
s = m_stack.back();
|
||||
m_stack.pop_back();
|
||||
if (is_app(s)) {
|
||||
app *a = to_app(s);
|
||||
for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) {
|
||||
if (m_has_num.is_marked(a->get_arg(i))) {
|
||||
m_has_num.mark(a,true);
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool cache_all_results() const { return false; }
|
||||
bool cache_results() const { return false; }
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if (m_util.is_numeral(s)) {
|
||||
t = m.mk_var(m_var_index++, m.get_sort(s));
|
||||
m_substitution.insert(t, s);
|
||||
m_pinned.push_back(t);
|
||||
m_has_num.mark(s, true);
|
||||
m_seen.mark(t, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* construct m_g, which is a generalization of t, where every constant
|
||||
* is replaced by a variable for any variable in m_g, remember the
|
||||
* substitution to get back t and save it in m_substitutions
|
||||
*/
|
||||
anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m)
|
||||
{
|
||||
m_pinned.push_back(t);
|
||||
|
||||
obj_map<expr, expr*> substitution;
|
||||
|
||||
var_abs_rewriter var_abs_cfg(m, substitution);
|
||||
rewriter_tpl<var_abs_rewriter> var_abs_rw (m, false, var_abs_cfg);
|
||||
var_abs_rw (t, m_g);
|
||||
|
||||
m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k
|
||||
}
|
||||
|
||||
/* traverses m_g and t in parallel. if they only differ in constants
|
||||
* (i.e. m_g contains a variable, where t contains a constant), then
|
||||
* add the substitutions, which need to be applied to m_g to get t, to
|
||||
* m_substitutions.
|
||||
*/
|
||||
bool anti_unifier::add_term(expr* t) {
|
||||
m_pinned.push_back(t);
|
||||
|
||||
ptr_vector<expr> todo;
|
||||
ptr_vector<expr> todo2;
|
||||
todo.push_back(m_g);
|
||||
todo2.push_back(t);
|
||||
|
||||
ast_mark visited;
|
||||
|
||||
arith_util util(m);
|
||||
|
||||
obj_map<expr, expr*> substitution;
|
||||
|
||||
while (!todo.empty()) {
|
||||
expr* current = todo.back();
|
||||
todo.pop_back();
|
||||
expr* current2 = todo2.back();
|
||||
todo2.pop_back();
|
||||
|
||||
if (!visited.is_marked(current)) {
|
||||
visited.mark(current, true);
|
||||
|
||||
if (is_var(current)) {
|
||||
// TODO: for now we don't allow variables in the terms we want to antiunify
|
||||
SASSERT(m_substitutions[0].contains(current));
|
||||
if (util.is_numeral(current2)) {
|
||||
substitution.insert(current, current2);
|
||||
}
|
||||
else {return false;}
|
||||
}
|
||||
else {
|
||||
SASSERT(is_app(current));
|
||||
|
||||
if (is_app(current2) &&
|
||||
to_app(current)->get_decl() == to_app(current2)->get_decl() &&
|
||||
to_app(current)->get_num_args() == to_app(current2)->get_num_args()) {
|
||||
// TODO: what to do for numerals here? E.g. if we
|
||||
// have 1 and 2, do they have the same decl or are
|
||||
// the decls already different?
|
||||
SASSERT (!util.is_numeral(current) || current == current2);
|
||||
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
|
||||
i < num_args; ++i) {
|
||||
todo.push_back(to_app(current)->get_arg(i));
|
||||
todo2.push_back(to_app(current2)->get_arg(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we now know that the terms can be anti-unified, so add the cached substitution
|
||||
m_substitutions.push_back(substitution);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns m_g, where additionally any variable, which has only equal
|
||||
* substitutions, is substituted with that substitution
|
||||
*/
|
||||
void anti_unifier::finalize() {
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(m_g);
|
||||
|
||||
ast_mark visited;
|
||||
|
||||
obj_map<expr, expr*> generalization;
|
||||
|
||||
arith_util util(m);
|
||||
|
||||
// post-order traversel which ignores constants and handles them
|
||||
// directly when the enclosing term of the constant is handled
|
||||
while (!todo.empty()) {
|
||||
expr* current = todo.back();
|
||||
SASSERT(is_app(current));
|
||||
|
||||
// if we haven't already visited current
|
||||
if (!visited.is_marked(current)) {
|
||||
bool existsUnvisitedParent = false;
|
||||
|
||||
for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) {
|
||||
expr* argument = to_app(current)->get_arg(i);
|
||||
|
||||
if (!is_var(argument)) {
|
||||
SASSERT(is_app(argument));
|
||||
// if we haven't visited the current parent yet
|
||||
if(!visited.is_marked(argument)) {
|
||||
// add it to the stack
|
||||
todo.push_back(argument);
|
||||
existsUnvisitedParent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we already visited all parents, we can visit current too
|
||||
if (!existsUnvisitedParent) {
|
||||
visited.mark(current, true);
|
||||
todo.pop_back();
|
||||
|
||||
ptr_buffer<expr> arg_list;
|
||||
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
|
||||
i < num_args; ++i) {
|
||||
expr* argument = to_app(current)->get_arg(i);
|
||||
|
||||
if (is_var(argument)) {
|
||||
// compute whether there are different
|
||||
// substitutions for argument
|
||||
bool containsDifferentSubstitutions = false;
|
||||
|
||||
for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) {
|
||||
SASSERT(m_substitutions[i].contains(argument));
|
||||
SASSERT(m_substitutions[i+1].contains(argument));
|
||||
|
||||
// TODO: how to check equality?
|
||||
if (m_substitutions[i][argument] !=
|
||||
m_substitutions[i+1][argument])
|
||||
{
|
||||
containsDifferentSubstitutions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if yes, use the variable
|
||||
if (containsDifferentSubstitutions) {
|
||||
arg_list.push_back(argument);
|
||||
}
|
||||
// otherwise use the concrete value instead
|
||||
// and remove the substitutions
|
||||
else
|
||||
{
|
||||
arg_list.push_back(m_substitutions[0][argument]);
|
||||
|
||||
for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) {
|
||||
SASSERT(m_substitutions[i].contains(argument));
|
||||
m_substitutions[i].remove(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(generalization.contains(argument));
|
||||
arg_list.push_back(generalization[argument]);
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(to_app(current)->get_num_args() == arg_list.size());
|
||||
expr_ref application(m.mk_app(to_app(current)->get_decl(),
|
||||
to_app(current)->get_num_args(),
|
||||
arg_list.c_ptr()), m);
|
||||
m_pinned.push_back(application);
|
||||
generalization.insert(current, application);
|
||||
}
|
||||
}
|
||||
else {
|
||||
todo.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
m_g = generalization[m_g];
|
||||
}
|
||||
|
||||
|
||||
class ncc_less_than_key
|
||||
{
|
||||
public:
|
||||
ncc_less_than_key(arith_util& util) : m_util(util) {}
|
||||
|
||||
bool operator() (const expr*& e1, const expr*& e2) {
|
||||
rational val1;
|
||||
rational val2;
|
||||
|
||||
if (m_util.is_numeral(e1, val1) && m_util.is_numeral(e2, val2))
|
||||
{
|
||||
return val1 < val2;
|
||||
}
|
||||
else
|
||||
{
|
||||
SASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
arith_util m_util;
|
||||
};
|
||||
|
||||
/*
|
||||
* if there is a single interval which exactly captures each of the
|
||||
* substitutions, return the corresponding closure, otherwise do
|
||||
* nothing
|
||||
*/
|
||||
bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m,
|
||||
expr_ref& result) {
|
||||
arith_util util(m);
|
||||
|
||||
SASSERT(au.get_num_substitutions() > 0);
|
||||
if (au.get_substitution(0).size() == 0) {
|
||||
result = au.get_generalization();
|
||||
return true;
|
||||
}
|
||||
|
||||
// check that all substitutions have the same size
|
||||
for (unsigned i=0, sz = au.get_num_substitutions(); i+1 < sz; ++i) {
|
||||
if (au.get_substitution(i).size() != au.get_substitution(i+1).size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// for each substitution entry
|
||||
bool is_first_key = true;
|
||||
unsigned lower_bound = 0;
|
||||
unsigned upper_bound = 0;
|
||||
for (const auto& pair : au.get_substitution(0)) {
|
||||
// construct vector
|
||||
expr* key = &pair.get_key();
|
||||
vector<unsigned> entries;
|
||||
|
||||
rational val;
|
||||
for (unsigned i=0, sz = au.get_num_substitutions(); i < sz; ++i)
|
||||
{
|
||||
if (util.is_numeral(au.get_substitution(i)[key], val) &&
|
||||
val.is_unsigned()) {
|
||||
entries.push_back(val.get_unsigned());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check whether vector represents interval
|
||||
unsigned current_lower_bound = 0;
|
||||
unsigned current_upper_bound = 0;
|
||||
|
||||
// if vector represents interval
|
||||
if (get_range(entries, current_lower_bound, current_upper_bound)) {
|
||||
// if interval is the same as previous interval
|
||||
if (is_first_key) {
|
||||
is_first_key = false;
|
||||
lower_bound = current_lower_bound;
|
||||
upper_bound = current_upper_bound;
|
||||
}
|
||||
else {
|
||||
if (current_lower_bound != lower_bound ||
|
||||
current_upper_bound != upper_bound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise we don't do a convex closure
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// we finally know that we can express the substitutions using a
|
||||
// single interval, so build the expression 1. construct const
|
||||
expr_ref const_ref(m.mk_const(symbol("scti!0"), util.mk_int()), m);
|
||||
|
||||
// 2. construct body with const
|
||||
expr_ref lit1(util.mk_le(util.mk_int(lower_bound), const_ref), m);
|
||||
expr_ref lit2(util.mk_le(const_ref, util.mk_int(upper_bound)), m);
|
||||
expr_ref lit3(m);
|
||||
substitute_vars_by_const(m, au.get_generalization(), const_ref, lit3);
|
||||
|
||||
expr_ref_vector args(m);
|
||||
args.push_back(lit1);
|
||||
args.push_back(lit2);
|
||||
args.push_back(lit3);
|
||||
expr_ref body_with_consts = mk_and(args);
|
||||
|
||||
// 3. replace const by var
|
||||
ptr_vector<expr> vars;
|
||||
vars.push_back(const_ref);
|
||||
|
||||
expr_ref body(m);
|
||||
expr_abstract(m, 0, vars.size(), (expr*const*)vars.c_ptr(), body_with_consts, body);
|
||||
|
||||
// 4. introduce quantifier
|
||||
ptr_vector<sort> sorts;
|
||||
sorts.push_back(util.mk_int());
|
||||
svector<symbol> names;
|
||||
names.push_back(symbol("scti!0"));
|
||||
|
||||
result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool naive_convex_closure::get_range(vector<unsigned int>& v,
|
||||
unsigned int& lower_bound, unsigned int& upper_bound)
|
||||
{
|
||||
// sort substitutions
|
||||
std::sort(v.begin(), v.end());
|
||||
|
||||
// check that numbers are consecutive
|
||||
for (unsigned i=0; i+1 < v.size(); ++i) {
|
||||
if (v[i] + 1 != v[i+1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(v.size() > 0);
|
||||
lower_bound = v[0];
|
||||
upper_bound = v.back();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct subs_rewriter_cfg : public default_rewriter_cfg {
|
||||
ast_manager &m;
|
||||
expr_ref m_c;
|
||||
|
||||
subs_rewriter_cfg (ast_manager &manager, expr* c) : m(manager), m_c(c, m) {}
|
||||
|
||||
bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) {
|
||||
result = m_c;
|
||||
result_pr = 0;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t,
|
||||
expr* c, expr_ref& res) {
|
||||
subs_rewriter_cfg subs_cfg(m, c);
|
||||
rewriter_tpl<subs_rewriter_cfg> subs_rw (m, false, subs_cfg);
|
||||
subs_rw (t, res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template class rewriter_tpl<spacer::var_abs_rewriter>;
|
||||
template class rewriter_tpl<spacer::subs_rewriter_cfg>;
|
||||
67
src/muz/spacer/spacer_antiunify.h
Normal file
67
src/muz/spacer/spacer_antiunify.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_antiunify.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Antiunification utilities
|
||||
|
||||
Author:
|
||||
|
||||
Bernhard Gleiss
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_ANTIUNIFY_H_
|
||||
#define _SPACER_ANTIUNIFY_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
|
||||
namespace spacer {
|
||||
class anti_unifier
|
||||
{
|
||||
public:
|
||||
anti_unifier(expr* t, ast_manager& m);
|
||||
~anti_unifier() {}
|
||||
|
||||
bool add_term(expr* t);
|
||||
void finalize();
|
||||
|
||||
expr* get_generalization() {return m_g;}
|
||||
unsigned get_num_substitutions() {return m_substitutions.size();}
|
||||
obj_map<expr, expr*> get_substitution(unsigned index){
|
||||
SASSERT(index < m_substitutions.size());
|
||||
return m_substitutions[index];
|
||||
}
|
||||
|
||||
private:
|
||||
ast_manager& m;
|
||||
// tracking all created expressions
|
||||
expr_ref_vector m_pinned;
|
||||
|
||||
expr_ref m_g;
|
||||
|
||||
vector<obj_map<expr, expr*>> m_substitutions;
|
||||
};
|
||||
|
||||
class naive_convex_closure
|
||||
{
|
||||
public:
|
||||
static bool compute_closure(anti_unifier& au, ast_manager& m,
|
||||
expr_ref& result);
|
||||
|
||||
private:
|
||||
static bool get_range(vector<unsigned>& v, unsigned& lower_bound,
|
||||
unsigned& upper_bound);
|
||||
static void substitute_vars_by_const(ast_manager& m, expr* t, expr* c,
|
||||
expr_ref& res);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
3508
src/muz/spacer/spacer_context.cpp
Normal file
3508
src/muz/spacer/spacer_context.cpp
Normal file
File diff suppressed because it is too large
Load diff
842
src/muz/spacer/spacer_context.h
Normal file
842
src/muz/spacer/spacer_context.h
Normal file
|
|
@ -0,0 +1,842 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_context.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SPACER predicate transformers and search context.
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
Anvesh Komuravelli
|
||||
|
||||
Based on muz/pdr/pdr_context.h by Nikolaj Bjorner (nbjorner)
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_CONTEXT_H_
|
||||
#define _SPACER_CONTEXT_H_
|
||||
|
||||
#ifdef _CYGWIN
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#include <queue>
|
||||
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "muz/spacer/spacer_prop_solver.h"
|
||||
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
|
||||
namespace datalog {
|
||||
class rule_set;
|
||||
class context;
|
||||
};
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class pred_transformer;
|
||||
class derivation;
|
||||
class pob_queue;
|
||||
class context;
|
||||
|
||||
typedef obj_map<datalog::rule const, app_ref_vector*> rule2inst;
|
||||
typedef obj_map<func_decl, pred_transformer*> decl2rel;
|
||||
|
||||
class pob;
|
||||
typedef ref<pob> pob_ref;
|
||||
typedef sref_vector<pob> pob_ref_vector;
|
||||
|
||||
class reach_fact;
|
||||
typedef ref<reach_fact> reach_fact_ref;
|
||||
typedef sref_vector<reach_fact> reach_fact_ref_vector;
|
||||
|
||||
class reach_fact {
|
||||
unsigned m_ref_count;
|
||||
|
||||
expr_ref m_fact;
|
||||
ptr_vector<app> m_aux_vars;
|
||||
|
||||
const datalog::rule &m_rule;
|
||||
reach_fact_ref_vector m_justification;
|
||||
|
||||
bool m_init;
|
||||
|
||||
public:
|
||||
reach_fact (ast_manager &m, const datalog::rule &rule,
|
||||
expr* fact, const ptr_vector<app> &aux_vars,
|
||||
bool init = false) :
|
||||
m_ref_count (0), m_fact (fact, m), m_aux_vars (aux_vars),
|
||||
m_rule(rule), m_init (init) {}
|
||||
reach_fact (ast_manager &m, const datalog::rule &rule,
|
||||
expr* fact, bool init = false) :
|
||||
m_ref_count (0), m_fact (fact, m), m_rule(rule), m_init (init) {}
|
||||
|
||||
bool is_init () {return m_init;}
|
||||
const datalog::rule& get_rule () {return m_rule;}
|
||||
|
||||
void add_justification (reach_fact *f) {m_justification.push_back (f);}
|
||||
const reach_fact_ref_vector& get_justifications () {return m_justification;}
|
||||
|
||||
expr *get () {return m_fact.get ();}
|
||||
const ptr_vector<app> &aux_vars () {return m_aux_vars;}
|
||||
|
||||
void inc_ref () {++m_ref_count;}
|
||||
void dec_ref ()
|
||||
{
|
||||
SASSERT (m_ref_count > 0);
|
||||
--m_ref_count;
|
||||
if(m_ref_count == 0) { dealloc(this); }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class lemma;
|
||||
typedef ref<lemma> lemma_ref;
|
||||
typedef sref_vector<lemma> lemma_ref_vector;
|
||||
|
||||
typedef pob pob;
|
||||
|
||||
// a lemma
|
||||
class lemma {
|
||||
unsigned m_ref_count;
|
||||
|
||||
ast_manager &m;
|
||||
expr_ref m_body;
|
||||
expr_ref_vector m_cube;
|
||||
app_ref_vector m_bindings;
|
||||
unsigned m_lvl;
|
||||
pob_ref m_pob;
|
||||
bool m_new_pob;
|
||||
|
||||
void mk_expr_core();
|
||||
void mk_cube_core();
|
||||
public:
|
||||
lemma(ast_manager &manager, expr * fml, unsigned lvl);
|
||||
lemma(pob_ref const &p);
|
||||
lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl);
|
||||
// lemma(const lemma &other) = delete;
|
||||
|
||||
ast_manager &get_ast_manager() {return m;}
|
||||
expr *get_expr();
|
||||
bool is_false();
|
||||
expr_ref_vector const &get_cube();
|
||||
void update_cube(pob_ref const &p, expr_ref_vector &cube);
|
||||
|
||||
bool has_pob() {return m_pob;}
|
||||
pob_ref &get_pob() {return m_pob;}
|
||||
|
||||
unsigned level () const {return m_lvl;}
|
||||
void set_level (unsigned lvl) {m_lvl = lvl;}
|
||||
app_ref_vector& get_bindings() {return m_bindings;}
|
||||
void add_binding(app_ref_vector const &binding) {m_bindings.append(binding);}
|
||||
void mk_insts(expr_ref_vector& inst, expr* e = nullptr);
|
||||
bool is_ground () {return !is_quantifier (get_expr());}
|
||||
|
||||
void inc_ref () {++m_ref_count;}
|
||||
void dec_ref ()
|
||||
{
|
||||
SASSERT (m_ref_count > 0);
|
||||
--m_ref_count;
|
||||
if(m_ref_count == 0) { dealloc(this); }
|
||||
}
|
||||
};
|
||||
|
||||
struct lemma_lt_proc : public std::binary_function<lemma*, lemma *, bool> {
|
||||
bool operator() (lemma *a, lemma *b) {
|
||||
return (a->level () < b->level ()) ||
|
||||
(a->level () == b->level () &&
|
||||
ast_lt_proc() (a->get_expr (), b->get_expr ()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Predicate transformer state.
|
||||
// A predicate transformer corresponds to the
|
||||
// set of rules that have the same head predicates.
|
||||
//
|
||||
|
||||
class pred_transformer {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_propagations;
|
||||
unsigned m_num_invariants;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
/// manager of the lemmas in all the frames
|
||||
#include "muz/spacer/spacer_legacy_frames.h"
|
||||
class frames {
|
||||
private:
|
||||
pred_transformer &m_pt;
|
||||
lemma_ref_vector m_lemmas;
|
||||
unsigned m_size;
|
||||
|
||||
bool m_sorted;
|
||||
lemma_lt_proc m_lt;
|
||||
|
||||
void sort ();
|
||||
|
||||
public:
|
||||
frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {}
|
||||
~frames() {}
|
||||
void simplify_formulas ();
|
||||
|
||||
pred_transformer& pt () {return m_pt;}
|
||||
|
||||
|
||||
void get_frame_lemmas (unsigned level, expr_ref_vector &out) {
|
||||
for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i)
|
||||
if(m_lemmas[i]->level() == level) {
|
||||
out.push_back(m_lemmas[i]->get_expr());
|
||||
}
|
||||
}
|
||||
void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) {
|
||||
for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i)
|
||||
if(m_lemmas [i]->level() >= level) {
|
||||
out.push_back(m_lemmas[i]->get_expr());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned size () const {return m_size;}
|
||||
unsigned lemma_size () const {return m_lemmas.size ();}
|
||||
void add_frame () {m_size++;}
|
||||
void inherit_frames (frames &other) {
|
||||
for (unsigned i = 0, sz = other.m_lemmas.size (); i < sz; ++i) {
|
||||
lemma_ref lem = alloc(lemma, m_pt.get_ast_manager(),
|
||||
other.m_lemmas[i]->get_expr (),
|
||||
other.m_lemmas[i]->level());
|
||||
lem->add_binding(other.m_lemmas[i]->get_bindings());
|
||||
add_lemma(lem.get());
|
||||
}
|
||||
m_sorted = false;
|
||||
}
|
||||
|
||||
bool add_lemma (lemma *lem);
|
||||
void propagate_to_infinity (unsigned level);
|
||||
bool propagate_to_next_level (unsigned level);
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
manager of proof-obligations (pobs)
|
||||
*/
|
||||
class pobs {
|
||||
typedef ptr_buffer<pob, 1> pob_buffer;
|
||||
typedef obj_map<expr, pob_buffer > expr2pob_buffer;
|
||||
|
||||
pred_transformer &m_pt;
|
||||
|
||||
expr2pob_buffer m_pobs;
|
||||
pob_ref_vector m_pinned;
|
||||
public:
|
||||
pobs(pred_transformer &pt) : m_pt(pt) {}
|
||||
pob* mk_pob(pob *parent, unsigned level, unsigned depth,
|
||||
expr *post, app_ref_vector const &b);
|
||||
|
||||
pob* mk_pob(pob *parent, unsigned level, unsigned depth,
|
||||
expr *post) {
|
||||
app_ref_vector b(m_pt.get_ast_manager());
|
||||
return mk_pob (parent, level, depth, post, b);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
typedef obj_map<datalog::rule const, expr*> rule2expr;
|
||||
typedef obj_map<datalog::rule const, ptr_vector<app> > rule2apps;
|
||||
|
||||
manager& pm; // spacer-manager
|
||||
ast_manager& m; // manager
|
||||
context& ctx;
|
||||
|
||||
func_decl_ref m_head; // predicate
|
||||
func_decl_ref_vector m_sig; // signature
|
||||
ptr_vector<pred_transformer> m_use; // places where 'this' is referenced.
|
||||
ptr_vector<datalog::rule> m_rules; // rules used to derive transformer
|
||||
prop_solver m_solver; // solver context
|
||||
solver* m_reach_ctx; // context for reachability facts
|
||||
pobs m_pobs;
|
||||
frames m_frames;
|
||||
reach_fact_ref_vector m_reach_facts; // reach facts
|
||||
/// Number of initial reachability facts
|
||||
unsigned m_rf_init_sz;
|
||||
obj_map<expr, datalog::rule const*> m_tag2rule; // map tag predicate to rule.
|
||||
rule2expr m_rule2tag; // map rule to predicate tag.
|
||||
rule2inst m_rule2inst; // map rules to instantiations of indices
|
||||
rule2expr m_rule2transition; // map rules to transition
|
||||
rule2apps m_rule2vars; // map rule to auxiliary variables
|
||||
expr_ref m_transition; // transition relation.
|
||||
expr_ref m_initial_state; // initial state.
|
||||
app_ref m_extend_lit; // literal to extend initial state
|
||||
bool m_all_init; // true if the pt has no uninterpreted body in any rule
|
||||
ptr_vector<func_decl> m_predicates;
|
||||
stats m_stats;
|
||||
stopwatch m_initialize_watch;
|
||||
stopwatch m_must_reachable_watch;
|
||||
|
||||
|
||||
|
||||
/// Auxiliary variables to represent different disjunctive
|
||||
/// cases of must summaries. Stored over 'n' (a.k.a. new)
|
||||
/// versions of the variables
|
||||
expr_ref_vector m_reach_case_vars;
|
||||
|
||||
void init_sig();
|
||||
void ensure_level(unsigned level);
|
||||
void add_lemma_core (lemma *lemma);
|
||||
void add_lemma_from_child (pred_transformer &child, lemma *lemma, unsigned lvl);
|
||||
|
||||
void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result);
|
||||
|
||||
// Initialization
|
||||
void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition);
|
||||
void init_rule(decl2rel const& pts, datalog::rule const& rule, vector<bool>& is_init,
|
||||
ptr_vector<datalog::rule const>& rules, expr_ref_vector& transition);
|
||||
void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx);
|
||||
|
||||
void simplify_formulas(tactic& tac, expr_ref_vector& fmls);
|
||||
|
||||
// Debugging
|
||||
bool check_filled(app_ref_vector const& v) const;
|
||||
|
||||
void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r);
|
||||
|
||||
expr* mk_fresh_reach_case_var ();
|
||||
|
||||
public:
|
||||
pred_transformer(context& ctx, manager& pm, func_decl* head);
|
||||
~pred_transformer();
|
||||
|
||||
inline bool use_native_mbp ();
|
||||
reach_fact *get_reach_fact (expr *v)
|
||||
{
|
||||
for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i)
|
||||
if(v == m_reach_facts [i]->get()) { return m_reach_facts[i]; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void add_rule(datalog::rule* r) { m_rules.push_back(r); }
|
||||
void add_use(pred_transformer* pt) { if(!m_use.contains(pt)) { m_use.insert(pt); } }
|
||||
void initialize(decl2rel const& pts);
|
||||
|
||||
func_decl* head() const { return m_head; }
|
||||
ptr_vector<datalog::rule> const& rules() const { return m_rules; }
|
||||
func_decl* sig(unsigned i) const { return m_sig[i]; } // signature
|
||||
func_decl* const* sig() { return m_sig.c_ptr(); }
|
||||
unsigned sig_size() const { return m_sig.size(); }
|
||||
expr* transition() const { return m_transition; }
|
||||
expr* initial_state() const { return m_initial_state; }
|
||||
expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); }
|
||||
unsigned get_num_levels() { return m_frames.size (); }
|
||||
expr_ref get_cover_delta(func_decl* p_orig, int level);
|
||||
void add_cover(unsigned level, expr* property);
|
||||
expr_ref get_reachable ();
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
bool is_must_reachable (expr* state, model_ref* model = 0);
|
||||
/// \brief Returns reachability fact active in the given model
|
||||
/// all determines whether initial reachability facts are included as well
|
||||
reach_fact *get_used_reach_fact (model_evaluator_util& mev, bool all = true);
|
||||
/// \brief Returns reachability fact active in the origin of the given model
|
||||
reach_fact* get_used_origin_reach_fact (model_evaluator_util &mev, unsigned oidx);
|
||||
expr_ref get_origin_summary (model_evaluator_util &mev,
|
||||
unsigned level, unsigned oidx, bool must,
|
||||
const ptr_vector<app> **aux);
|
||||
|
||||
void remove_predecessors(expr_ref_vector& literals);
|
||||
void find_predecessors(datalog::rule const& r, ptr_vector<func_decl>& predicates) const;
|
||||
void find_predecessors(vector<std::pair<func_decl*, unsigned> >& predicates) const;
|
||||
datalog::rule const* find_rule(model &mev, bool& is_concrete,
|
||||
vector<bool>& reach_pred_used,
|
||||
unsigned& num_reuse_reach);
|
||||
expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); }
|
||||
ptr_vector<app>& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); }
|
||||
|
||||
bool propagate_to_next_level(unsigned level);
|
||||
void propagate_to_infinity(unsigned level);
|
||||
/// \brief Add a lemma to the current context and all users
|
||||
bool add_lemma(expr * lemma, unsigned lvl);
|
||||
bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);}
|
||||
expr* get_reach_case_var (unsigned idx) const;
|
||||
bool has_reach_facts () const { return !m_reach_facts.empty () ;}
|
||||
|
||||
/// initialize reachability facts using initial rules
|
||||
void init_reach_facts ();
|
||||
void add_reach_fact (reach_fact *fact); // add reachability fact
|
||||
reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); }
|
||||
expr* get_last_reach_case_var () const;
|
||||
|
||||
pob* mk_pob(pob *parent, unsigned level, unsigned depth,
|
||||
expr *post, app_ref_vector const &b){
|
||||
return m_pobs.mk_pob(parent, level, depth, post, b);
|
||||
}
|
||||
|
||||
pob* mk_pob(pob *parent, unsigned level, unsigned depth,
|
||||
expr *post) {
|
||||
return m_pobs.mk_pob(parent, level, depth, post);
|
||||
}
|
||||
|
||||
lbool is_reachable(pob& n, expr_ref_vector* core, model_ref *model,
|
||||
unsigned& uses_level, bool& is_concrete,
|
||||
datalog::rule const*& r,
|
||||
vector<bool>& reach_pred_used,
|
||||
unsigned& num_reuse_reach);
|
||||
bool is_invariant(unsigned level, expr* lemma,
|
||||
unsigned& solver_level, expr_ref_vector* core = 0);
|
||||
bool check_inductive(unsigned level, expr_ref_vector& state,
|
||||
unsigned& assumes_level);
|
||||
|
||||
expr_ref get_formulas(unsigned level, bool add_axioms);
|
||||
|
||||
void simplify_formulas();
|
||||
|
||||
context& get_context () const {return ctx;}
|
||||
manager& get_manager() const { return pm; }
|
||||
ast_manager& get_ast_manager() const { return m; }
|
||||
|
||||
void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r);
|
||||
|
||||
void inherit_properties(pred_transformer& other);
|
||||
|
||||
void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector<app>& aux_vars,
|
||||
bool is_init);
|
||||
|
||||
/// \brief Adds a given expression to the set of initial rules
|
||||
app* extend_initial (expr *e);
|
||||
|
||||
/// \brief Returns true if the obligation is already blocked by current lemmas
|
||||
bool is_blocked (pob &n, unsigned &uses_level);
|
||||
/// \brief Returns true if the obligation is already blocked by current quantified lemmas
|
||||
bool is_qblocked (pob &n);
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A proof obligation.
|
||||
*/
|
||||
class pob {
|
||||
friend class context;
|
||||
unsigned m_ref_count;
|
||||
/// parent node
|
||||
pob_ref m_parent;
|
||||
/// predicate transformer
|
||||
pred_transformer& m_pt;
|
||||
/// post-condition decided by this node
|
||||
expr_ref m_post;
|
||||
// if m_post is not ground, then m_binding is an instantiation for
|
||||
// all quantified variables
|
||||
app_ref_vector m_binding;
|
||||
/// new post to be swapped in for m_post
|
||||
expr_ref m_new_post;
|
||||
/// level at which to decide the post
|
||||
unsigned m_level;
|
||||
|
||||
unsigned m_depth;
|
||||
|
||||
/// whether a concrete answer to the post is found
|
||||
bool m_open;
|
||||
/// whether to use farkas generalizer to construct a lemma blocking this node
|
||||
bool m_use_farkas;
|
||||
|
||||
unsigned m_weakness;
|
||||
/// derivation representing the position of this node in the parent's rule
|
||||
scoped_ptr<derivation> m_derivation;
|
||||
|
||||
ptr_vector<pob> m_kids;
|
||||
public:
|
||||
pob (pob* parent, pred_transformer& pt,
|
||||
unsigned level, unsigned depth=0, bool add_to_parent=true);
|
||||
|
||||
~pob() {if(m_parent) { m_parent->erase_child(*this); }}
|
||||
|
||||
unsigned weakness() {return m_weakness;}
|
||||
void bump_weakness() {m_weakness++;}
|
||||
void reset_weakness() {m_weakness=0;}
|
||||
|
||||
void inc_level () {m_level++; m_depth++;reset_weakness();}
|
||||
|
||||
void inherit(pob const &p);
|
||||
void set_derivation (derivation *d) {m_derivation = d;}
|
||||
bool has_derivation () const {return (bool)m_derivation;}
|
||||
derivation &get_derivation() const {return *m_derivation.get ();}
|
||||
void reset_derivation () {set_derivation (NULL);}
|
||||
/// detaches derivation from the node without deallocating
|
||||
derivation* detach_derivation () {return m_derivation.detach ();}
|
||||
|
||||
pob* parent () const { return m_parent.get (); }
|
||||
|
||||
pred_transformer& pt () const { return m_pt; }
|
||||
ast_manager& get_ast_manager () const { return m_pt.get_ast_manager (); }
|
||||
manager& get_manager () const { return m_pt.get_manager (); }
|
||||
context& get_context () const {return m_pt.get_context ();}
|
||||
|
||||
unsigned level () const { return m_level; }
|
||||
unsigned depth () const {return m_depth;}
|
||||
|
||||
bool use_farkas_generalizer () const {return m_use_farkas;}
|
||||
void set_farkas_generalizer (bool v) {m_use_farkas = v;}
|
||||
|
||||
expr* post() const { return m_post.get (); }
|
||||
void set_post(expr *post);
|
||||
void set_post(expr *post, app_ref_vector const &b);
|
||||
|
||||
/// indicate that a new post should be set for the node
|
||||
void new_post(expr *post) {if(post != m_post) {m_new_post = post;}}
|
||||
/// true if the node needs to be updated outside of the priority queue
|
||||
bool is_dirty () {return m_new_post;}
|
||||
/// clean a dirty node
|
||||
void clean();
|
||||
|
||||
void reset () {clean (); m_derivation = NULL; m_open = true;}
|
||||
|
||||
bool is_closed () const { return !m_open; }
|
||||
void close();
|
||||
|
||||
void add_child (pob &v) {m_kids.push_back (&v);}
|
||||
void erase_child (pob &v) {m_kids.erase (&v);}
|
||||
|
||||
bool is_ground () { return m_binding.empty (); }
|
||||
app_ref_vector const &get_binding() const {return m_binding;}
|
||||
/*
|
||||
* Return skolem variables that appear in post
|
||||
*/
|
||||
void get_skolems(app_ref_vector& v);
|
||||
|
||||
void inc_ref () {++m_ref_count;}
|
||||
void dec_ref ()
|
||||
{
|
||||
--m_ref_count;
|
||||
if(m_ref_count == 0) { dealloc(this); }
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct pob_lt :
|
||||
public std::binary_function<const pob*, const pob*, bool>
|
||||
{bool operator() (const pob *pn1, const pob *pn2) const;};
|
||||
|
||||
struct pob_gt :
|
||||
public std::binary_function<const pob*, const pob*, bool> {
|
||||
pob_lt lt;
|
||||
bool operator() (const pob *n1, const pob *n2) const
|
||||
{return lt(n2, n1);}
|
||||
};
|
||||
|
||||
struct pob_ref_gt :
|
||||
public std::binary_function<const pob_ref&, const model_ref &, bool> {
|
||||
pob_gt gt;
|
||||
bool operator() (const pob_ref &n1, const pob_ref &n2) const
|
||||
{return gt (n1.get (), n2.get ());}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
class derivation {
|
||||
/// a single premise of a derivation
|
||||
class premise {
|
||||
pred_transformer &m_pt;
|
||||
/// origin order in the rule
|
||||
unsigned m_oidx;
|
||||
/// summary fact corresponding to the premise
|
||||
expr_ref m_summary;
|
||||
/// whether this is a must or may premise
|
||||
bool m_must;
|
||||
app_ref_vector m_ovars;
|
||||
|
||||
public:
|
||||
premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must,
|
||||
const ptr_vector<app> *aux_vars = NULL);
|
||||
premise (const premise &p);
|
||||
|
||||
bool is_must () {return m_must;}
|
||||
expr * get_summary () {return m_summary.get ();}
|
||||
app_ref_vector &get_ovars () {return m_ovars;}
|
||||
unsigned get_oidx () {return m_oidx;}
|
||||
pred_transformer &pt () {return m_pt;}
|
||||
|
||||
/// \brief Updated the summary.
|
||||
/// The new summary is over n-variables.
|
||||
void set_summary (expr * summary, bool must,
|
||||
const ptr_vector<app> *aux_vars = NULL);
|
||||
};
|
||||
|
||||
|
||||
/// parent model node
|
||||
pob& m_parent;
|
||||
|
||||
/// the rule corresponding to this derivation
|
||||
const datalog::rule &m_rule;
|
||||
|
||||
/// the premises
|
||||
vector<premise> m_premises;
|
||||
/// pointer to the active premise
|
||||
unsigned m_active;
|
||||
// transition relation over origin variables
|
||||
expr_ref m_trans;
|
||||
// implicitly existentially quantified variables in m_trans
|
||||
app_ref_vector m_evars;
|
||||
/// -- create next child using given model as the guide
|
||||
/// -- returns NULL if there is no next child
|
||||
pob* create_next_child (model_evaluator_util &mev);
|
||||
public:
|
||||
derivation (pob& parent, datalog::rule const& rule,
|
||||
expr *trans, app_ref_vector const &evars);
|
||||
void add_premise (pred_transformer &pt, unsigned oidx,
|
||||
expr * summary, bool must, const ptr_vector<app> *aux_vars = NULL);
|
||||
|
||||
/// creates the first child. Must be called after all the premises
|
||||
/// are added. The model must be valid for the premises
|
||||
/// Returns NULL if no child exits
|
||||
pob *create_first_child (model_evaluator_util &mev);
|
||||
|
||||
/// Create the next child. Must summary of the currently active
|
||||
/// premise must be consistent with the transition relation
|
||||
pob *create_next_child ();
|
||||
|
||||
datalog::rule const& get_rule () const { return m_rule; }
|
||||
pob& get_parent () const { return m_parent; }
|
||||
ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();}
|
||||
manager &get_manager () const {return m_parent.get_manager ();}
|
||||
context &get_context() const {return m_parent.get_context();}
|
||||
};
|
||||
|
||||
|
||||
class pob_queue {
|
||||
pob_ref m_root;
|
||||
unsigned m_max_level;
|
||||
unsigned m_min_depth;
|
||||
|
||||
std::priority_queue<pob_ref, std::vector<pob_ref>,
|
||||
pob_ref_gt> m_obligations;
|
||||
|
||||
public:
|
||||
pob_queue(): m_root(NULL), m_max_level(0), m_min_depth(0) {}
|
||||
~pob_queue();
|
||||
|
||||
void reset();
|
||||
pob * top ();
|
||||
void pop () {m_obligations.pop ();}
|
||||
void push (pob &n) {m_obligations.push (&n);}
|
||||
|
||||
void inc_level ()
|
||||
{
|
||||
SASSERT (!m_obligations.empty () || m_root);
|
||||
m_max_level++;
|
||||
m_min_depth++;
|
||||
if(m_root && m_obligations.empty()) { m_obligations.push(m_root); }
|
||||
}
|
||||
|
||||
pob& get_root() const { return *m_root.get (); }
|
||||
void set_root(pob& n);
|
||||
bool is_root (pob& n) const {return m_root.get () == &n;}
|
||||
|
||||
unsigned max_level () {return m_max_level;}
|
||||
unsigned min_depth () {return m_min_depth;}
|
||||
size_t size () {return m_obligations.size ();}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generalizers (strengthens) a lemma
|
||||
*/
|
||||
class lemma_generalizer {
|
||||
protected:
|
||||
context& m_ctx;
|
||||
public:
|
||||
lemma_generalizer(context& ctx): m_ctx(ctx) {}
|
||||
virtual ~lemma_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma) = 0;
|
||||
virtual void collect_statistics(statistics& st) const {}
|
||||
virtual void reset_statistics() {}
|
||||
};
|
||||
|
||||
|
||||
class context {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_queries;
|
||||
unsigned m_num_reach_queries;
|
||||
unsigned m_num_reuse_reach;
|
||||
unsigned m_max_query_lvl;
|
||||
unsigned m_max_depth;
|
||||
unsigned m_cex_depth;
|
||||
unsigned m_expand_node_undef;
|
||||
unsigned m_num_lemmas;
|
||||
unsigned m_num_restarts;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// stat watches
|
||||
stopwatch m_solve_watch;
|
||||
stopwatch m_propagate_watch;
|
||||
stopwatch m_reach_watch;
|
||||
stopwatch m_is_reach_watch;
|
||||
stopwatch m_create_children_watch;
|
||||
stopwatch m_init_rules_watch;
|
||||
|
||||
fixedpoint_params const& m_params;
|
||||
ast_manager& m;
|
||||
datalog::context* m_context;
|
||||
manager m_pm;
|
||||
decl2rel m_rels; // Map from relation predicate to fp-operator.
|
||||
func_decl_ref m_query_pred;
|
||||
pred_transformer* m_query;
|
||||
mutable pob_queue m_pob_queue;
|
||||
lbool m_last_result;
|
||||
unsigned m_inductive_lvl;
|
||||
unsigned m_expanded_lvl;
|
||||
ptr_buffer<lemma_generalizer> m_lemma_generalizers;
|
||||
stats m_stats;
|
||||
model_converter_ref m_mc;
|
||||
proof_converter_ref m_pc;
|
||||
bool m_use_native_mbp;
|
||||
bool m_ground_cti;
|
||||
bool m_instantiate;
|
||||
bool m_use_qlemmas;
|
||||
bool m_weak_abs;
|
||||
bool m_use_restarts;
|
||||
unsigned m_restart_initial_threshold;
|
||||
|
||||
// Functions used by search.
|
||||
lbool solve_core (unsigned from_lvl = 0);
|
||||
bool check_reachability ();
|
||||
bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl,
|
||||
unsigned full_prop_lvl);
|
||||
bool is_reachable(pob &n);
|
||||
lbool expand_node(pob& n);
|
||||
reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev,
|
||||
datalog::rule const& r);
|
||||
bool create_children(pob& n, datalog::rule const& r,
|
||||
model_evaluator_util &model,
|
||||
const vector<bool>& reach_pred_used);
|
||||
expr_ref mk_sat_answer();
|
||||
expr_ref mk_unsat_answer() const;
|
||||
|
||||
// Generate inductive property
|
||||
void get_level_property(unsigned lvl, expr_ref_vector& res,
|
||||
vector<relation_info> & rs) const;
|
||||
|
||||
|
||||
// Initialization
|
||||
void init_lemma_generalizers(datalog::rule_set& rules);
|
||||
|
||||
bool check_invariant(unsigned lvl);
|
||||
bool check_invariant(unsigned lvl, func_decl* fn);
|
||||
|
||||
void checkpoint();
|
||||
|
||||
void init_rules(datalog::rule_set& rules, decl2rel& transformers);
|
||||
|
||||
void simplify_formulas();
|
||||
|
||||
void reset_lemma_generalizers();
|
||||
|
||||
bool validate();
|
||||
|
||||
unsigned get_cex_depth ();
|
||||
|
||||
public:
|
||||
/**
|
||||
Initial values of predicates are stored in corresponding relations in dctx.
|
||||
|
||||
We check whether there is some reachable state of the relation checked_relation.
|
||||
*/
|
||||
context(
|
||||
fixedpoint_params const& params,
|
||||
ast_manager& m);
|
||||
|
||||
~context();
|
||||
|
||||
fixedpoint_params const& get_params() const { return m_params; }
|
||||
bool use_native_mbp () {return m_use_native_mbp;}
|
||||
bool use_ground_cti () {return m_ground_cti;}
|
||||
bool use_instantiate () { return m_instantiate; }
|
||||
bool use_qlemmas () {return m_use_qlemmas; }
|
||||
|
||||
ast_manager& get_ast_manager() const { return m; }
|
||||
manager& get_manager() { return m_pm; }
|
||||
decl2rel const& get_pred_transformers() const { return m_rels; }
|
||||
pred_transformer& get_pred_transformer(func_decl* p) const
|
||||
{ return *m_rels.find(p); }
|
||||
datalog::context& get_datalog_context() const
|
||||
{ SASSERT(m_context); return *m_context; }
|
||||
expr_ref get_answer();
|
||||
/**
|
||||
* get bottom-up (from query) sequence of ground predicate instances
|
||||
* (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query
|
||||
*/
|
||||
expr_ref get_ground_sat_answer ();
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void display_certificate(std::ostream& strm) const {}
|
||||
|
||||
lbool solve(unsigned from_lvl = 0);
|
||||
|
||||
lbool solve_from_lvl (unsigned from_lvl);
|
||||
|
||||
void reset();
|
||||
|
||||
void set_query(func_decl* q) { m_query_pred = q; }
|
||||
|
||||
void set_unsat() { m_last_result = l_false; }
|
||||
|
||||
void set_model_converter(model_converter_ref& mc) { m_mc = mc; }
|
||||
|
||||
void get_rules_along_trace (datalog::rule_ref_vector& rules);
|
||||
|
||||
model_converter_ref get_model_converter() { return m_mc; }
|
||||
|
||||
void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; }
|
||||
|
||||
void update_rules(datalog::rule_set& rules);
|
||||
|
||||
void set_axioms(expr* axioms) { m_pm.set_background(axioms); }
|
||||
|
||||
unsigned get_num_levels(func_decl* p);
|
||||
|
||||
expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p);
|
||||
|
||||
void add_cover(int level, func_decl* pred, expr* property);
|
||||
|
||||
expr_ref get_reachable (func_decl* p);
|
||||
|
||||
void add_invariant (func_decl *pred, expr* property);
|
||||
|
||||
model_ref get_model();
|
||||
|
||||
proof_ref get_proof() const;
|
||||
|
||||
pob& get_root() const { return m_pob_queue.get_root(); }
|
||||
|
||||
expr_ref get_constraints (unsigned lvl);
|
||||
void add_constraints (unsigned lvl, expr_ref c);
|
||||
};
|
||||
|
||||
inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();}
|
||||
}
|
||||
|
||||
#endif
|
||||
354
src/muz/spacer/spacer_dl_interface.cpp
Normal file
354
src/muz/spacer/spacer_dl_interface.cpp
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_dl.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog SPACER
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/base/dl_context.h"
|
||||
#include "muz/transforms/dl_mk_coi_filter.h"
|
||||
#include "muz/transforms/dl_mk_interp_tail_simplifier.h"
|
||||
#include "muz/transforms/dl_mk_subsumption_checker.h"
|
||||
#include "muz/transforms/dl_mk_rule_inliner.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_transformer.h"
|
||||
#include "parsers/smt2/smt2parser.h"
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/spacer/spacer_dl_interface.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "muz/transforms/dl_mk_slice.h"
|
||||
#include "muz/transforms/dl_mk_unfold.h"
|
||||
#include "muz/transforms/dl_mk_coalesce.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "muz/transforms/dl_transforms.h"
|
||||
|
||||
using namespace spacer;
|
||||
|
||||
dl_interface::dl_interface(datalog::context& ctx) :
|
||||
engine_base(ctx.get_manager(), "spacer"),
|
||||
m_ctx(ctx),
|
||||
m_spacer_rules(ctx),
|
||||
m_old_rules(ctx),
|
||||
m_context(0),
|
||||
m_refs(ctx.get_manager())
|
||||
{
|
||||
m_context = alloc(spacer::context, ctx.get_params(), ctx.get_manager());
|
||||
}
|
||||
|
||||
|
||||
dl_interface::~dl_interface()
|
||||
{
|
||||
dealloc(m_context);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Check if the new rules are weaker so that we can
|
||||
// re-use existing context.
|
||||
//
|
||||
void dl_interface::check_reset()
|
||||
{
|
||||
datalog::rule_set const& new_rules = m_ctx.get_rules();
|
||||
datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules();
|
||||
bool is_subsumed = !old_rules.empty();
|
||||
for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) {
|
||||
is_subsumed = false;
|
||||
for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) {
|
||||
if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) {
|
||||
is_subsumed = true;
|
||||
}
|
||||
}
|
||||
if (!is_subsumed) {
|
||||
TRACE("spacer", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule "););
|
||||
m_context->reset();
|
||||
}
|
||||
}
|
||||
m_old_rules.replace_rules(new_rules);
|
||||
}
|
||||
|
||||
|
||||
lbool dl_interface::query(expr * query)
|
||||
{
|
||||
//we restore the initial state in the datalog context
|
||||
m_ctx.ensure_opened();
|
||||
m_refs.reset();
|
||||
m_pred2slice.reset();
|
||||
ast_manager& m = m_ctx.get_manager();
|
||||
datalog::rule_manager& rm = m_ctx.get_rule_manager();
|
||||
datalog::rule_set& rules0 = m_ctx.get_rules();
|
||||
datalog::rule_set old_rules(rules0);
|
||||
func_decl_ref query_pred(m);
|
||||
rm.mk_query(query, m_ctx.get_rules());
|
||||
expr_ref bg_assertion = m_ctx.get_background_assertion();
|
||||
|
||||
check_reset();
|
||||
|
||||
TRACE("spacer",
|
||||
if (!m.is_true(bg_assertion)) {
|
||||
tout << "axioms:\n";
|
||||
tout << mk_pp(bg_assertion, m) << "\n";
|
||||
}
|
||||
tout << "query: " << mk_pp(query, m) << "\n";
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
);
|
||||
|
||||
|
||||
apply_default_transformation(m_ctx);
|
||||
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
datalog::rule_transformer transformer(m_ctx);
|
||||
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
|
||||
transformer.register_plugin(slice);
|
||||
m_ctx.transform_rules(transformer);
|
||||
|
||||
// track sliced predicates.
|
||||
obj_map<func_decl, func_decl*> const& preds = slice->get_predicates();
|
||||
obj_map<func_decl, func_decl*>::iterator it = preds.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = preds.end();
|
||||
for (; it != end; ++it) {
|
||||
m_pred2slice.insert(it->m_key, it->m_value);
|
||||
m_refs.push_back(it->m_key);
|
||||
m_refs.push_back(it->m_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ctx.get_params().xform_unfold_rules() > 0) {
|
||||
unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules();
|
||||
datalog::rule_transformer transf1(m_ctx), transf2(m_ctx);
|
||||
transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx));
|
||||
transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx));
|
||||
if (m_ctx.get_params().xform_coalesce_rules()) {
|
||||
m_ctx.transform_rules(transf1);
|
||||
}
|
||||
while (num_unfolds > 0) {
|
||||
m_ctx.transform_rules(transf2);
|
||||
--num_unfolds;
|
||||
}
|
||||
}
|
||||
|
||||
const datalog::rule_set& rules = m_ctx.get_rules();
|
||||
if (rules.get_output_predicates().empty()) {
|
||||
m_context->set_unsat();
|
||||
return l_false;
|
||||
}
|
||||
|
||||
query_pred = rules.get_output_predicate();
|
||||
|
||||
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
|
||||
m_spacer_rules.replace_rules(rules);
|
||||
m_spacer_rules.close();
|
||||
m_ctx.record_transformed_rules();
|
||||
m_ctx.reopen();
|
||||
m_ctx.replace_rules(old_rules);
|
||||
|
||||
scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
|
||||
|
||||
m_context->set_proof_converter(m_ctx.get_proof_converter());
|
||||
m_context->set_model_converter(m_ctx.get_model_converter());
|
||||
m_context->set_query(query_pred);
|
||||
m_context->set_axioms(bg_assertion);
|
||||
m_context->update_rules(m_spacer_rules);
|
||||
|
||||
if (m_spacer_rules.get_rules().empty()) {
|
||||
m_context->set_unsat();
|
||||
IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0););
|
||||
return l_false;
|
||||
}
|
||||
|
||||
return m_context->solve();
|
||||
|
||||
}
|
||||
|
||||
lbool dl_interface::query_from_lvl(expr * query, unsigned lvl)
|
||||
{
|
||||
//we restore the initial state in the datalog context
|
||||
m_ctx.ensure_opened();
|
||||
m_refs.reset();
|
||||
m_pred2slice.reset();
|
||||
ast_manager& m = m_ctx.get_manager();
|
||||
datalog::rule_manager& rm = m_ctx.get_rule_manager();
|
||||
datalog::rule_set& rules0 = m_ctx.get_rules();
|
||||
datalog::rule_set old_rules(rules0);
|
||||
func_decl_ref query_pred(m);
|
||||
rm.mk_query(query, m_ctx.get_rules());
|
||||
expr_ref bg_assertion = m_ctx.get_background_assertion();
|
||||
|
||||
check_reset();
|
||||
|
||||
TRACE("spacer",
|
||||
if (!m.is_true(bg_assertion)) {
|
||||
tout << "axioms:\n";
|
||||
tout << mk_pp(bg_assertion, m) << "\n";
|
||||
}
|
||||
tout << "query: " << mk_pp(query, m) << "\n";
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
);
|
||||
|
||||
|
||||
apply_default_transformation(m_ctx);
|
||||
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
datalog::rule_transformer transformer(m_ctx);
|
||||
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
|
||||
transformer.register_plugin(slice);
|
||||
m_ctx.transform_rules(transformer);
|
||||
|
||||
// track sliced predicates.
|
||||
obj_map<func_decl, func_decl*> const& preds = slice->get_predicates();
|
||||
obj_map<func_decl, func_decl*>::iterator it = preds.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = preds.end();
|
||||
for (; it != end; ++it) {
|
||||
m_pred2slice.insert(it->m_key, it->m_value);
|
||||
m_refs.push_back(it->m_key);
|
||||
m_refs.push_back(it->m_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ctx.get_params().xform_unfold_rules() > 0) {
|
||||
unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules();
|
||||
datalog::rule_transformer transf1(m_ctx), transf2(m_ctx);
|
||||
transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx));
|
||||
transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx));
|
||||
if (m_ctx.get_params().xform_coalesce_rules()) {
|
||||
m_ctx.transform_rules(transf1);
|
||||
}
|
||||
while (num_unfolds > 0) {
|
||||
m_ctx.transform_rules(transf2);
|
||||
--num_unfolds;
|
||||
}
|
||||
}
|
||||
|
||||
const datalog::rule_set& rules = m_ctx.get_rules();
|
||||
if (rules.get_output_predicates().empty()) {
|
||||
|
||||
m_context->set_unsat();
|
||||
return l_false;
|
||||
}
|
||||
|
||||
query_pred = rules.get_output_predicate();
|
||||
|
||||
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
|
||||
m_spacer_rules.replace_rules(rules);
|
||||
m_spacer_rules.close();
|
||||
m_ctx.record_transformed_rules();
|
||||
m_ctx.reopen();
|
||||
m_ctx.replace_rules(old_rules);
|
||||
|
||||
scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
|
||||
|
||||
m_context->set_proof_converter(m_ctx.get_proof_converter());
|
||||
m_context->set_model_converter(m_ctx.get_model_converter());
|
||||
m_context->set_query(query_pred);
|
||||
m_context->set_axioms(bg_assertion);
|
||||
m_context->update_rules(m_spacer_rules);
|
||||
|
||||
if (m_spacer_rules.get_rules().empty()) {
|
||||
m_context->set_unsat();
|
||||
IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(), 0););
|
||||
return l_false;
|
||||
}
|
||||
|
||||
return m_context->solve(lvl);
|
||||
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig)
|
||||
{
|
||||
func_decl* pred = pred_orig;
|
||||
m_pred2slice.find(pred_orig, pred);
|
||||
SASSERT(pred);
|
||||
return m_context->get_cover_delta(level, pred_orig, pred);
|
||||
}
|
||||
|
||||
void dl_interface::add_cover(int level, func_decl* pred, expr* property)
|
||||
{
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers");
|
||||
}
|
||||
m_context->add_cover(level, pred, property);
|
||||
}
|
||||
|
||||
void dl_interface::add_invariant(func_decl* pred, expr* property)
|
||||
{
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
throw default_exception("Invariants are incompatible with slicing. Disable slicing before using invariants");
|
||||
}
|
||||
m_context->add_invariant(pred, property);
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_reachable(func_decl* pred)
|
||||
{
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
throw default_exception("Invariants are incompatible with slicing. "
|
||||
"Disable slicing before using invariants");
|
||||
}
|
||||
return m_context->get_reachable(pred);
|
||||
}
|
||||
|
||||
unsigned dl_interface::get_num_levels(func_decl* pred)
|
||||
{
|
||||
m_pred2slice.find(pred, pred);
|
||||
SASSERT(pred);
|
||||
return m_context->get_num_levels(pred);
|
||||
}
|
||||
|
||||
void dl_interface::collect_statistics(statistics& st) const
|
||||
{
|
||||
m_context->collect_statistics(st);
|
||||
}
|
||||
|
||||
void dl_interface::reset_statistics()
|
||||
{
|
||||
m_context->reset_statistics();
|
||||
}
|
||||
|
||||
void dl_interface::display_certificate(std::ostream& out) const
|
||||
{
|
||||
m_context->display_certificate(out);
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_answer()
|
||||
{
|
||||
return m_context->get_answer();
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_ground_sat_answer()
|
||||
{
|
||||
return m_context->get_ground_sat_answer();
|
||||
}
|
||||
|
||||
void dl_interface::get_rules_along_trace(datalog::rule_ref_vector& rules)
|
||||
{
|
||||
m_context->get_rules_along_trace(rules);
|
||||
}
|
||||
|
||||
void dl_interface::updt_params()
|
||||
{
|
||||
dealloc(m_context);
|
||||
m_context = alloc(spacer::context, m_ctx.get_params(), m_ctx.get_manager());
|
||||
}
|
||||
|
||||
model_ref dl_interface::get_model()
|
||||
{
|
||||
return m_context->get_model();
|
||||
}
|
||||
|
||||
proof_ref dl_interface::get_proof()
|
||||
{
|
||||
return m_context->get_proof();
|
||||
}
|
||||
86
src/muz/spacer/spacer_dl_interface.h
Normal file
86
src/muz/spacer/spacer_dl_interface.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_dl_interface.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog SPACER
|
||||
|
||||
Author:
|
||||
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_DL_INTERFACE_H_
|
||||
#define _SPACER_DL_INTERFACE_H_
|
||||
|
||||
#include "util/lbool.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "muz/base/dl_engine_base.h"
|
||||
#include "util/statistics.h"
|
||||
|
||||
namespace datalog {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class context;
|
||||
|
||||
class dl_interface : public datalog::engine_base {
|
||||
datalog::context& m_ctx;
|
||||
datalog::rule_set m_spacer_rules;
|
||||
datalog::rule_set m_old_rules;
|
||||
context* m_context;
|
||||
obj_map<func_decl, func_decl*> m_pred2slice;
|
||||
ast_ref_vector m_refs;
|
||||
|
||||
void check_reset();
|
||||
|
||||
public:
|
||||
dl_interface(datalog::context& ctx);
|
||||
~dl_interface();
|
||||
|
||||
lbool query(expr* query);
|
||||
|
||||
lbool query_from_lvl(expr* query, unsigned lvl);
|
||||
|
||||
void display_certificate(std::ostream& out) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
void reset_statistics();
|
||||
|
||||
expr_ref get_answer();
|
||||
|
||||
expr_ref get_ground_sat_answer();
|
||||
|
||||
void get_rules_along_trace(datalog::rule_ref_vector& rules);
|
||||
|
||||
unsigned get_num_levels(func_decl* pred);
|
||||
|
||||
expr_ref get_cover_delta(int level, func_decl* pred);
|
||||
|
||||
void add_cover(int level, func_decl* pred, expr* property);
|
||||
|
||||
void add_invariant(func_decl* pred, expr* property);
|
||||
|
||||
expr_ref get_reachable(func_decl *pred);
|
||||
|
||||
void updt_params();
|
||||
|
||||
model_ref get_model();
|
||||
|
||||
proof_ref get_proof();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
439
src/muz/spacer/spacer_farkas_learner.cpp
Normal file
439
src/muz/spacer/spacer_farkas_learner.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_farkas_learner.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Proviced abstract interface and some inplementations of algorithms
|
||||
for strenghtning lemmas
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-11-1.
|
||||
|
||||
Revision History:
|
||||
// TODO: what to write here
|
||||
--*/
|
||||
|
||||
//TODO: reorder, delete unnecessary includes
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/dl_decl_plugin.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_farkas_learner.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "muz/base/proof_utils.h"
|
||||
#include "ast/reg_decl_plugins.h"
|
||||
#include "smt/smt_farkas_util.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class collect_pure_proc {
|
||||
func_decl_set& m_symbs;
|
||||
public:
|
||||
collect_pure_proc(func_decl_set& s): m_symbs(s) {}
|
||||
|
||||
void operator()(app* a)
|
||||
{
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
m_symbs.insert(a->get_decl());
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res)
|
||||
{
|
||||
ast_manager& m = res.get_manager();
|
||||
smt::farkas_util res_c(m);
|
||||
res_c.set_split_literals(m_split_literals);
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
res_c.add(coeffs[i], lits[i]);
|
||||
}
|
||||
res = res_c.get();
|
||||
}
|
||||
|
||||
// every uninterpreted symbol is in symbs
|
||||
class is_pure_expr_proc {
|
||||
func_decl_set const& m_symbs;
|
||||
array_util m_au;
|
||||
public:
|
||||
struct non_pure {};
|
||||
|
||||
is_pure_expr_proc(func_decl_set const& s, ast_manager& m):
|
||||
m_symbs(s),
|
||||
m_au(m)
|
||||
{}
|
||||
|
||||
void operator()(app* a)
|
||||
{
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
if (!m_symbs.contains(a->get_decl())) {
|
||||
throw non_pure();
|
||||
}
|
||||
} else if (a->get_family_id() == m_au.get_family_id() &&
|
||||
a->is_app_of(a->get_family_id(), OP_ARRAY_EXT)) {
|
||||
throw non_pure();
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const
|
||||
{
|
||||
is_pure_expr_proc proc(symbs, m);
|
||||
try {
|
||||
for_each_expr(proc, e);
|
||||
} catch (is_pure_expr_proc::non_pure) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Revised version of Farkas strengthener.
|
||||
1. Mark B-pure nodes as derivations that depend only on B.
|
||||
2. Collect B-influenced nodes
|
||||
3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B.
|
||||
4. Weaken B-pure units for resolution with Farkas Clauses.
|
||||
5. Add B-pure units elsewhere.
|
||||
|
||||
Rules:
|
||||
- hypothesis h |- h
|
||||
|
||||
H |- false
|
||||
- lemma ----------
|
||||
|- not H
|
||||
|
||||
Th |- L \/ C H |- not L
|
||||
- th-lemma -------------------------
|
||||
H |- C
|
||||
|
||||
Note: C is false for theory axioms, C is unit literal for propagation.
|
||||
|
||||
- rewrite |- t = s
|
||||
|
||||
H |- t = s
|
||||
- monotonicity ----------------
|
||||
H |- f(t) = f(s)
|
||||
|
||||
H |- t = s H' |- s = u
|
||||
- trans ----------------------
|
||||
H, H' |- t = u
|
||||
|
||||
H |- C \/ L H' |- not L
|
||||
- unit_resolve ------------------------
|
||||
H, H' |- C
|
||||
|
||||
H |- a ~ b H' |- a
|
||||
- mp --------------------
|
||||
H, H' |- b
|
||||
|
||||
- def-axiom |- C
|
||||
|
||||
- asserted |- f
|
||||
|
||||
Mark nodes by:
|
||||
- Hypotheses
|
||||
- Dependency on bs
|
||||
- Dependency on A
|
||||
|
||||
A node is unit derivable from bs if:
|
||||
- It has no hypotheses.
|
||||
- It depends on bs.
|
||||
- It does not depend on A.
|
||||
|
||||
NB: currently unit derivable is not symmetric: A clause can be
|
||||
unit derivable, but a unit literal with hypotheses is not.
|
||||
This is clearly wrong, because hypotheses are just additional literals
|
||||
in a clausal version.
|
||||
|
||||
NB: the routine is not interpolating, though an interpolating variant would
|
||||
be preferrable because then we can also use it for model propagation.
|
||||
|
||||
We collect the unit derivable nodes from bs.
|
||||
These are the weakenings of bs, besides the
|
||||
units under Farkas.
|
||||
|
||||
*/
|
||||
|
||||
#define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); }
|
||||
|
||||
void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas)
|
||||
{
|
||||
ast_manager& m = lemmas.get_manager();
|
||||
bool_rewriter brwr(m);
|
||||
func_decl_set Bsymbs;
|
||||
collect_pure_proc collect_proc(Bsymbs);
|
||||
expr_set::iterator it = bs.begin(), end = bs.end();
|
||||
for (; it != end; ++it) {
|
||||
for_each_expr(collect_proc, *it);
|
||||
}
|
||||
|
||||
proof_ref pr(root, m);
|
||||
proof_utils::reduce_hypotheses(pr);
|
||||
proof_utils::permute_unit_resolution(pr);
|
||||
IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";);
|
||||
|
||||
ptr_vector<expr_set> hyprefs;
|
||||
obj_map<expr, expr_set*> hypmap;
|
||||
obj_hashtable<expr> lemma_set;
|
||||
ast_mark b_depend, a_depend, visited, b_closed;
|
||||
expr_set* empty_set = alloc(expr_set);
|
||||
hyprefs.push_back(empty_set);
|
||||
ptr_vector<proof> todo;
|
||||
TRACE("spacer_verbose", tout << mk_pp(pr, m) << "\n";);
|
||||
todo.push_back(pr);
|
||||
while (!todo.empty()) {
|
||||
proof* p = todo.back();
|
||||
SASSERT(m.is_proof(p));
|
||||
if (visited.is_marked(p)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
bool all_visit = true;
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
expr* arg = p->get_arg(i);
|
||||
SASSERT(m.is_proof(arg));
|
||||
if (!visited.is_marked(arg)) {
|
||||
all_visit = false;
|
||||
todo.push_back(to_app(arg));
|
||||
}
|
||||
}
|
||||
if (!all_visit) {
|
||||
continue;
|
||||
}
|
||||
visited.mark(p, true);
|
||||
todo.pop_back();
|
||||
|
||||
// retrieve hypotheses and dependencies on A, bs.
|
||||
bool b_dep = false, a_dep = false;
|
||||
expr_set* hyps = empty_set;
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
expr* arg = p->get_arg(i);
|
||||
a_dep = a_dep || a_depend.is_marked(arg);
|
||||
b_dep = b_dep || b_depend.is_marked(arg);
|
||||
expr_set* hyps2 = hypmap.find(arg);
|
||||
if (hyps != hyps2 && !hyps2->empty()) {
|
||||
if (hyps->empty()) {
|
||||
hyps = hyps2;
|
||||
} else {
|
||||
expr_set* hyps3 = alloc(expr_set);
|
||||
datalog::set_union(*hyps3, *hyps);
|
||||
datalog::set_union(*hyps3, *hyps2);
|
||||
hyps = hyps3;
|
||||
hyprefs.push_back(hyps);
|
||||
}
|
||||
}
|
||||
}
|
||||
hypmap.insert(p, hyps);
|
||||
a_depend.mark(p, a_dep);
|
||||
b_depend.mark(p, b_dep);
|
||||
|
||||
#define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty())
|
||||
|
||||
|
||||
// Add lemmas that depend on bs, have no hypotheses, don't depend on A.
|
||||
if ((!hyps->empty() || a_depend.is_marked(p)) &&
|
||||
b_depend.is_marked(p) && !is_farkas_lemma(m, p)) {
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
app* arg = to_app(p->get_arg(i));
|
||||
if (IS_B_PURE(arg)) {
|
||||
expr* fact = m.get_fact(arg);
|
||||
if (is_pure_expr(Bsymbs, fact, m)) {
|
||||
TRACE("farkas_learner2",
|
||||
tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n";
|
||||
tout << mk_pp(arg, m) << "\n";
|
||||
);
|
||||
INSERT(fact);
|
||||
} else {
|
||||
get_asserted(p, bs, b_closed, lemma_set, lemmas);
|
||||
b_closed.mark(p, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (p->get_decl_kind()) {
|
||||
case PR_ASSERTED:
|
||||
if (bs.contains(m.get_fact(p))) {
|
||||
b_depend.mark(p, true);
|
||||
} else {
|
||||
a_depend.mark(p, true);
|
||||
}
|
||||
break;
|
||||
case PR_HYPOTHESIS: {
|
||||
SASSERT(hyps == empty_set);
|
||||
hyps = alloc(expr_set);
|
||||
hyps->insert(m.get_fact(p));
|
||||
hyprefs.push_back(hyps);
|
||||
hypmap.insert(p, hyps);
|
||||
break;
|
||||
}
|
||||
case PR_DEF_AXIOM: {
|
||||
if (!is_pure_expr(Bsymbs, m.get_fact(p), m)) {
|
||||
a_depend.mark(p, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PR_LEMMA: {
|
||||
expr_set* hyps2 = alloc(expr_set);
|
||||
hyprefs.push_back(hyps2);
|
||||
datalog::set_union(*hyps2, *hyps);
|
||||
hyps = hyps2;
|
||||
expr* fml = m.get_fact(p);
|
||||
hyps->remove(fml);
|
||||
if (m.is_or(fml)) {
|
||||
for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) {
|
||||
expr* f = to_app(fml)->get_arg(i);
|
||||
expr_ref hyp(m);
|
||||
brwr.mk_not(f, hyp);
|
||||
hyps->remove(hyp);
|
||||
}
|
||||
}
|
||||
hypmap.insert(p, hyps);
|
||||
break;
|
||||
}
|
||||
case PR_TH_LEMMA: {
|
||||
if (!is_farkas_lemma(m, p)) { break; }
|
||||
|
||||
SASSERT(m.has_fact(p));
|
||||
unsigned prem_cnt = m.get_num_parents(p);
|
||||
func_decl * d = p->get_decl();
|
||||
SASSERT(d->get_num_parameters() >= prem_cnt + 2);
|
||||
SASSERT(d->get_parameter(0).get_symbol() == "arith");
|
||||
SASSERT(d->get_parameter(1).get_symbol() == "farkas");
|
||||
parameter const* params = d->get_parameters() + 2;
|
||||
|
||||
app_ref_vector lits(m);
|
||||
expr_ref tmp(m);
|
||||
unsigned num_b_pures = 0;
|
||||
rational coef;
|
||||
vector<rational> coeffs;
|
||||
|
||||
TRACE("farkas_learner2",
|
||||
for (unsigned i = 0; i < prem_cnt; ++i) {
|
||||
VERIFY(params[i].is_rational(coef));
|
||||
proof* prem = to_app(p->get_arg(i));
|
||||
bool b_pure = IS_B_PURE(prem);
|
||||
tout << (b_pure ? "B" : "A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n";
|
||||
}
|
||||
tout << mk_pp(m.get_fact(p), m) << "\n";
|
||||
);
|
||||
|
||||
// NB. Taking 'abs' of coefficients is a workaround.
|
||||
// The Farkas coefficient extraction in arith_core must be wrong.
|
||||
// The coefficients would be always positive relative to the theory lemma.
|
||||
|
||||
for (unsigned i = 0; i < prem_cnt; ++i) {
|
||||
expr * prem_e = p->get_arg(i);
|
||||
SASSERT(is_app(prem_e));
|
||||
proof * prem = to_app(prem_e);
|
||||
|
||||
if (IS_B_PURE(prem)) {
|
||||
++num_b_pures;
|
||||
} else {
|
||||
VERIFY(params[i].is_rational(coef));
|
||||
lits.push_back(to_app(m.get_fact(prem)));
|
||||
coeffs.push_back(abs(coef));
|
||||
}
|
||||
}
|
||||
params += prem_cnt;
|
||||
if (prem_cnt + 2 < d->get_num_parameters()) {
|
||||
unsigned num_args = 1;
|
||||
expr* fact = m.get_fact(p);
|
||||
expr* const* args = &fact;
|
||||
if (m.is_or(fact)) {
|
||||
app* _or = to_app(fact);
|
||||
num_args = _or->get_num_args();
|
||||
args = _or->get_args();
|
||||
}
|
||||
SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters());
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
expr* prem_e = args[i];
|
||||
brwr.mk_not(prem_e, tmp);
|
||||
VERIFY(params[i].is_rational(coef));
|
||||
SASSERT(is_app(tmp));
|
||||
lits.push_back(to_app(tmp));
|
||||
coeffs.push_back(abs(coef));
|
||||
}
|
||||
|
||||
}
|
||||
SASSERT(coeffs.size() == lits.size());
|
||||
if (num_b_pures > 0) {
|
||||
expr_ref res(m);
|
||||
combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res);
|
||||
TRACE("farkas_learner2", tout << "Add: " << mk_pp(res, m) << "\n";);
|
||||
INSERT(res);
|
||||
b_closed.mark(p, true);
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc<expr_set>());
|
||||
simplify_bounds(lemmas);
|
||||
}
|
||||
|
||||
void farkas_learner::get_asserted(proof* p0, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas)
|
||||
{
|
||||
ast_manager& m = lemmas.get_manager();
|
||||
ast_mark visited;
|
||||
proof* p = p0;
|
||||
ptr_vector<proof> todo;
|
||||
todo.push_back(p);
|
||||
|
||||
while (!todo.empty()) {
|
||||
p = todo.back();
|
||||
todo.pop_back();
|
||||
if (visited.is_marked(p) || b_closed.is_marked(p)) {
|
||||
continue;
|
||||
}
|
||||
visited.mark(p, true);
|
||||
for (unsigned i = 0; i < m.get_num_parents(p); ++i) {
|
||||
expr* arg = p->get_arg(i);
|
||||
SASSERT(m.is_proof(arg));
|
||||
todo.push_back(to_app(arg));
|
||||
}
|
||||
if (p->get_decl_kind() == PR_ASSERTED &&
|
||||
bs.contains(m.get_fact(p))) {
|
||||
expr* fact = m.get_fact(p);
|
||||
TRACE("farkas_learner2",
|
||||
tout << mk_ll_pp(p0, m) << "\n";
|
||||
tout << "Add: " << mk_pp(p, m) << "\n";);
|
||||
INSERT(fact);
|
||||
b_closed.mark(p, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e)
|
||||
{
|
||||
app * a;
|
||||
func_decl* d;
|
||||
symbol sym;
|
||||
return
|
||||
is_app(e) &&
|
||||
(a = to_app(e), d = a->get_decl(), true) &&
|
||||
PR_TH_LEMMA == a->get_decl_kind() &&
|
||||
d->get_num_parameters() >= 2 &&
|
||||
d->get_parameter(0).is_symbol(sym) && sym == "arith" &&
|
||||
d->get_parameter(1).is_symbol(sym) && sym == "farkas" &&
|
||||
d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2;
|
||||
}
|
||||
}
|
||||
66
src/muz/spacer/spacer_farkas_learner.h
Normal file
66
src/muz/spacer/spacer_farkas_learner.h
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_farkas_learner.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog SPACER
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-11-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_FARKAS_LEARNER_H_
|
||||
#define _SPACER_FARKAS_LEARNER_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class farkas_learner {
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
|
||||
bool m_split_literals;
|
||||
|
||||
void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res);
|
||||
|
||||
bool is_farkas_lemma(ast_manager& m, expr* e);
|
||||
|
||||
void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas);
|
||||
|
||||
bool is_pure_expr(func_decl_set const& symbs, expr* e, ast_manager& m) const;
|
||||
|
||||
public:
|
||||
farkas_learner(): m_split_literals(false) {}
|
||||
|
||||
/**
|
||||
Traverse a proof and retrieve lemmas using the vocabulary from bs.
|
||||
*/
|
||||
void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas);
|
||||
|
||||
void collect_statistics(statistics& st) const {}
|
||||
void reset_statistics() {}
|
||||
|
||||
|
||||
/** \brief see smt::farkas_util::set_split_literals */
|
||||
void set_split_literals(bool v) {m_split_literals = v;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
293
src/muz/spacer/spacer_generalizers.cpp
Normal file
293
src/muz/spacer/spacer_generalizers.cpp
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_generalizers.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Lemma generalizers.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-20.
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/spacer/spacer_generalizers.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/factor_equivs.h"
|
||||
|
||||
|
||||
namespace spacer {
|
||||
void lemma_sanity_checker::operator()(lemma_ref &lemma) {
|
||||
unsigned uses_level;
|
||||
expr_ref_vector cube(lemma->get_ast_manager());
|
||||
cube.append(lemma->get_cube());
|
||||
ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(),
|
||||
cube, uses_level));
|
||||
}
|
||||
|
||||
|
||||
// ------------------------
|
||||
// lemma_bool_inductive_generalizer
|
||||
/// Inductive generalization by dropping and expanding literals
|
||||
void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) {
|
||||
if (lemma->get_cube().empty()) return;
|
||||
|
||||
m_st.count++;
|
||||
scoped_watch _w_(m_st.watch);
|
||||
|
||||
unsigned uses_level;
|
||||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
ast_manager &m = pt.get_ast_manager();
|
||||
|
||||
expr_ref_vector cube(m);
|
||||
cube.append(lemma->get_cube());
|
||||
|
||||
bool dirty = false;
|
||||
expr_ref true_expr(m.mk_true(), m);
|
||||
ptr_vector<expr> processed;
|
||||
expr_ref_vector extra_lits(m);
|
||||
|
||||
unsigned i = 0, num_failures = 0;
|
||||
while (i < cube.size() &&
|
||||
(!m_failure_limit || num_failures < m_failure_limit)) {
|
||||
expr_ref lit(m);
|
||||
lit = cube.get(i);
|
||||
cube[i] = true_expr;
|
||||
if (cube.size() > 1 &&
|
||||
pt.check_inductive(lemma->level(), cube, uses_level)) {
|
||||
num_failures = 0;
|
||||
dirty = true;
|
||||
for (i = 0; i < cube.size() &&
|
||||
processed.contains(cube.get(i)); ++i);
|
||||
} else {
|
||||
// check if the literal can be expanded and any single
|
||||
// literal in the expansion can replace it
|
||||
extra_lits.reset();
|
||||
extra_lits.push_back(lit);
|
||||
expand_literals(m, extra_lits);
|
||||
SASSERT(extra_lits.size() > 0);
|
||||
bool found = false;
|
||||
if (extra_lits.get(0) != lit) {
|
||||
SASSERT(extra_lits.size() > 1);
|
||||
for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) {
|
||||
cube[i] = extra_lits.get(j);
|
||||
if (pt.check_inductive(lemma->level(), cube, uses_level)) {
|
||||
num_failures = 0;
|
||||
dirty = true;
|
||||
found = true;
|
||||
processed.push_back(extra_lits.get(j));
|
||||
for (i = 0; i < cube.size() &&
|
||||
processed.contains(cube.get(i)); ++i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
cube[i] = lit;
|
||||
processed.push_back(lit);
|
||||
++num_failures;
|
||||
++m_st.num_failures;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
TRACE("spacer",
|
||||
tout << "Generalized from:\n" << mk_and(lemma->get_cube())
|
||||
<< "\ninto\n" << mk_and(cube) << "\n";);
|
||||
|
||||
lemma->update_cube(lemma->get_pob(), cube);
|
||||
SASSERT(uses_level >= lemma->level());
|
||||
lemma->set_level(uses_level);
|
||||
}
|
||||
}
|
||||
|
||||
void lemma_bool_inductive_generalizer::collect_statistics(statistics &st) const
|
||||
{
|
||||
st.update("time.spacer.solve.reach.gen.bool_ind", m_st.watch.get_seconds());
|
||||
st.update("bool inductive gen", m_st.count);
|
||||
st.update("bool inductive gen failures", m_st.num_failures);
|
||||
}
|
||||
|
||||
void unsat_core_generalizer::operator()(lemma_ref &lemma)
|
||||
{
|
||||
m_st.count++;
|
||||
scoped_watch _w_(m_st.watch);
|
||||
ast_manager &m = lemma->get_ast_manager();
|
||||
|
||||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
|
||||
unsigned old_sz = lemma->get_cube().size();
|
||||
unsigned old_level = lemma->level();
|
||||
|
||||
unsigned uses_level;
|
||||
expr_ref_vector core(m);
|
||||
VERIFY(pt.is_invariant(old_level, lemma->get_expr(), uses_level, &core));
|
||||
|
||||
CTRACE("spacer", old_sz > core.size(),
|
||||
tout << "unsat core reduced lemma from: "
|
||||
<< old_sz << " to " << core.size() << "\n";);
|
||||
CTRACE("spacer", old_level < uses_level,
|
||||
tout << "unsat core moved lemma up from: "
|
||||
<< old_level << " to " << uses_level << "\n";);
|
||||
if (old_sz > core.size()) {
|
||||
lemma->update_cube(lemma->get_pob(), core);
|
||||
lemma->set_level(uses_level);
|
||||
}
|
||||
}
|
||||
|
||||
void unsat_core_generalizer::collect_statistics(statistics &st) const
|
||||
{
|
||||
st.update("time.spacer.solve.reach.gen.unsat_core", m_st.watch.get_seconds());
|
||||
st.update("gen.unsat_core.cnt", m_st.count);
|
||||
st.update("gen.unsat_core.fail", m_st.num_failures);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class collect_array_proc {
|
||||
array_util m_au;
|
||||
func_decl_set &m_symbs;
|
||||
sort *m_sort;
|
||||
public:
|
||||
collect_array_proc(ast_manager &m, func_decl_set& s) :
|
||||
m_au(m), m_symbs(s), m_sort(NULL) {}
|
||||
|
||||
void operator()(app* a)
|
||||
{
|
||||
if (a->get_family_id() == null_family_id && m_au.is_array(a)) {
|
||||
if (m_sort && m_sort != get_sort(a)) { return; }
|
||||
if (!m_sort) { m_sort = get_sort(a); }
|
||||
m_symbs.insert(a->get_decl());
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
}
|
||||
|
||||
void lemma_array_eq_generalizer::operator() (lemma_ref &lemma)
|
||||
{
|
||||
TRACE("core_array_eq", tout << "Looking for equalities\n";);
|
||||
|
||||
// -- find array constants
|
||||
ast_manager &m = lemma->get_ast_manager();
|
||||
manager &pm = m_ctx.get_manager();
|
||||
(void)pm;
|
||||
|
||||
expr_ref_vector core(m);
|
||||
expr_ref v(m);
|
||||
func_decl_set symb;
|
||||
collect_array_proc cap(m, symb);
|
||||
|
||||
core.append (lemma->get_cube());
|
||||
v = mk_and(core);
|
||||
for_each_expr(cap, v);
|
||||
|
||||
TRACE("core_array_eq",
|
||||
tout << "found " << symb.size() << " array variables in: \n"
|
||||
<< mk_pp(v, m) << "\n";);
|
||||
|
||||
// too few constants
|
||||
if (symb.size() <= 1) { return; }
|
||||
// too many constants, skip this
|
||||
if (symb.size() >= 8) { return; }
|
||||
|
||||
|
||||
// -- for every pair of variables, try an equality
|
||||
typedef func_decl_set::iterator iterator;
|
||||
ptr_vector<func_decl> vsymbs;
|
||||
for (iterator it = symb.begin(), end = symb.end();
|
||||
it != end; ++it)
|
||||
{ vsymbs.push_back(*it); }
|
||||
|
||||
expr_ref_vector eqs(m);
|
||||
|
||||
for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i)
|
||||
for (unsigned j = i + 1; j < sz; ++j)
|
||||
{ eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)),
|
||||
m.mk_const(vsymbs.get(j)))); }
|
||||
|
||||
smt::kernel solver(m, m_ctx.get_manager().fparams2());
|
||||
expr_ref_vector lits(m);
|
||||
for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) {
|
||||
SASSERT(lits.size() == i);
|
||||
solver.push();
|
||||
solver.assert_expr(core.get(i));
|
||||
for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) {
|
||||
solver.push();
|
||||
solver.assert_expr(eqs.get(j));
|
||||
lbool res = solver.check();
|
||||
solver.pop(1);
|
||||
|
||||
if (res == l_false) {
|
||||
TRACE("core_array_eq",
|
||||
tout << "strengthened " << mk_pp(core.get(i), m)
|
||||
<< " with " << mk_pp(m.mk_not(eqs.get(j)), m) << "\n";);
|
||||
lits.push_back(m.mk_not(eqs.get(j)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
solver.pop(1);
|
||||
if (lits.size() == i) { lits.push_back(core.get(i)); }
|
||||
}
|
||||
|
||||
/**
|
||||
HACK: if the first 3 arguments of pt are boolean, assume
|
||||
they correspond to SeaHorn encoding and condition the equality on them.
|
||||
*/
|
||||
// pred_transformer &pt = n.pt ();
|
||||
// if (pt.sig_size () >= 3 &&
|
||||
// m.is_bool (pt.sig (0)->get_range ()) &&
|
||||
// m.is_bool (pt.sig (1)->get_range ()) &&
|
||||
// m.is_bool (pt.sig (2)->get_range ()))
|
||||
// {
|
||||
// lits.push_back (m.mk_const (pm.o2n(pt.sig (0), 0)));
|
||||
// lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (1), 0))));
|
||||
// lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (2), 0))));
|
||||
// }
|
||||
|
||||
TRACE("core_array_eq", tout << "new possible core "
|
||||
<< mk_pp(pm.mk_and(lits), m) << "\n";);
|
||||
|
||||
|
||||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
// -- check if it is consistent with the transition relation
|
||||
unsigned uses_level1;
|
||||
if (pt.check_inductive(lemma->level(), lits, uses_level1)) {
|
||||
TRACE("core_array_eq", tout << "Inductive!\n";);
|
||||
lemma->update_cube(lemma->get_pob(),lits);
|
||||
lemma->set_level(uses_level1);
|
||||
return;
|
||||
} else
|
||||
{ TRACE("core_array_eq", tout << "Not-Inductive!\n";);}
|
||||
}
|
||||
|
||||
void lemma_eq_generalizer::operator() (lemma_ref &lemma)
|
||||
{
|
||||
TRACE("core_eq", tout << "Transforming equivalence classes\n";);
|
||||
|
||||
ast_manager &m = m_ctx.get_ast_manager();
|
||||
expr_ref_vector core(m);
|
||||
core.append (lemma->get_cube());
|
||||
|
||||
bool dirty;
|
||||
expr_equiv_class eq_classes(m);
|
||||
factor_eqs(core, eq_classes);
|
||||
// create all possible equalities to allow for simple inductive generalization
|
||||
dirty = equiv_to_expr_full(eq_classes, core);
|
||||
if (dirty) {
|
||||
lemma->update_cube(lemma->get_pob(), core);
|
||||
}
|
||||
}
|
||||
};
|
||||
99
src/muz/spacer/spacer_generalizers.h
Normal file
99
src/muz/spacer/spacer_generalizers.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_generalizers.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Generalizer plugins.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-22.
|
||||
Arie Gurfinkel
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_GENERALIZERS_H_
|
||||
#define _SPACER_GENERALIZERS_H_
|
||||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
// can be used to check whether produced core is really implied by
|
||||
// frame and therefore valid TODO: or negation?
|
||||
class lemma_sanity_checker : public lemma_generalizer {
|
||||
public:
|
||||
lemma_sanity_checker(context& ctx) : lemma_generalizer(ctx) {}
|
||||
virtual ~lemma_sanity_checker() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
};
|
||||
|
||||
/**
|
||||
* Boolean inductive generalization by dropping literals
|
||||
*/
|
||||
class lemma_bool_inductive_generalizer : public lemma_generalizer {
|
||||
|
||||
struct stats {
|
||||
unsigned count;
|
||||
unsigned num_failures;
|
||||
stopwatch watch;
|
||||
stats() {reset();}
|
||||
void reset() {count = 0; num_failures = 0; watch.reset();}
|
||||
};
|
||||
|
||||
unsigned m_failure_limit;
|
||||
stats m_st;
|
||||
|
||||
public:
|
||||
lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit) :
|
||||
lemma_generalizer(ctx), m_failure_limit(failure_limit) {}
|
||||
virtual ~lemma_bool_inductive_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
|
||||
virtual void collect_statistics(statistics& st) const;
|
||||
virtual void reset_statistics() {m_st.reset();}
|
||||
};
|
||||
|
||||
class unsat_core_generalizer : public lemma_generalizer {
|
||||
struct stats {
|
||||
unsigned count;
|
||||
unsigned num_failures;
|
||||
stopwatch watch;
|
||||
stats() { reset(); }
|
||||
void reset() {count = 0; num_failures = 0; watch.reset();}
|
||||
};
|
||||
|
||||
stats m_st;
|
||||
public:
|
||||
unsat_core_generalizer(context &ctx) : lemma_generalizer(ctx) {}
|
||||
virtual ~unsat_core_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
|
||||
virtual void collect_statistics(statistics &st) const;
|
||||
virtual void reset_statistics() {m_st.reset();}
|
||||
};
|
||||
|
||||
class lemma_array_eq_generalizer : public lemma_generalizer {
|
||||
public:
|
||||
lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {}
|
||||
virtual ~lemma_array_eq_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
|
||||
};
|
||||
|
||||
class lemma_eq_generalizer : public lemma_generalizer {
|
||||
public:
|
||||
lemma_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {}
|
||||
virtual ~lemma_eq_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
#endif
|
||||
355
src/muz/spacer/spacer_itp_solver.cpp
Normal file
355
src/muz/spacer/spacer_itp_solver.cpp
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_itp_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"muz/spacer/spacer_itp_solver.h"
|
||||
#include"ast/ast.h"
|
||||
#include"muz/spacer/spacer_util.h"
|
||||
#include"muz/spacer/spacer_farkas_learner.h"
|
||||
#include"ast/rewriter/expr_replacer.h"
|
||||
#include"muz/spacer/spacer_unsat_core_learner.h"
|
||||
#include"muz/spacer/spacer_unsat_core_plugin.h"
|
||||
|
||||
namespace spacer {
|
||||
void itp_solver::push ()
|
||||
{
|
||||
m_defs.push_back (def_manager (*this));
|
||||
m_solver.push ();
|
||||
}
|
||||
|
||||
void itp_solver::pop (unsigned n)
|
||||
{
|
||||
m_solver.pop (n);
|
||||
unsigned lvl = m_defs.size ();
|
||||
SASSERT (n <= lvl);
|
||||
unsigned new_lvl = lvl-n;
|
||||
while (m_defs.size() > new_lvl) {
|
||||
m_num_proxies -= m_defs.back ().m_defs.size ();
|
||||
m_defs.pop_back ();
|
||||
}
|
||||
}
|
||||
|
||||
app* itp_solver::fresh_proxy ()
|
||||
{
|
||||
if (m_num_proxies == m_proxies.size()) {
|
||||
std::stringstream name;
|
||||
name << "spacer_proxy!" << m_proxies.size ();
|
||||
app_ref res(m);
|
||||
res = m.mk_const (symbol (name.str ().c_str ()),
|
||||
m.mk_bool_sort ());
|
||||
m_proxies.push_back (res);
|
||||
|
||||
// -- add the new proxy to proxy eliminator
|
||||
proof_ref pr(m);
|
||||
pr = m.mk_asserted (m.mk_true ());
|
||||
m_elim_proxies_sub.insert (res, m.mk_true (), pr);
|
||||
|
||||
}
|
||||
return m_proxies.get (m_num_proxies++);
|
||||
}
|
||||
|
||||
app* itp_solver::mk_proxy (expr *v)
|
||||
{
|
||||
{
|
||||
expr *e = v;
|
||||
m.is_not (v, e);
|
||||
if (is_uninterp_const(e)) { return to_app(v); }
|
||||
}
|
||||
|
||||
def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs;
|
||||
return def.mk_proxy (v);
|
||||
}
|
||||
|
||||
bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (unsigned i = from, sz = v.size(); i < sz; ++i) {
|
||||
app *p = mk_proxy (v.get (i));
|
||||
dirty |= (v.get (i) != p);
|
||||
v[i] = p;
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void itp_solver::push_bg (expr *e)
|
||||
{
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
m_assumptions.push_back (e);
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
}
|
||||
|
||||
void itp_solver::pop_bg (unsigned n)
|
||||
{
|
||||
if (n == 0) { return; }
|
||||
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0;
|
||||
m_assumptions.shrink (m_first_assumption);
|
||||
}
|
||||
|
||||
unsigned itp_solver::get_num_bg () {return m_first_assumption;}
|
||||
|
||||
lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions)
|
||||
{
|
||||
// -- remove any old assumptions
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
|
||||
// -- replace theory literals in background assumptions with proxies
|
||||
mk_proxies (m_assumptions);
|
||||
// -- in case mk_proxies added new literals, they are all background
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
|
||||
m_assumptions.append (num_assumptions, assumptions);
|
||||
m_is_proxied = mk_proxies (m_assumptions, m_first_assumption);
|
||||
|
||||
lbool res;
|
||||
res = m_solver.check_sat (m_assumptions.size (), m_assumptions.c_ptr ());
|
||||
set_status (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
app* itp_solver::def_manager::mk_proxy (expr *v)
|
||||
{
|
||||
app* r;
|
||||
if (m_expr2proxy.find(v, r)) { return r; }
|
||||
|
||||
ast_manager &m = m_parent.m;
|
||||
app_ref proxy(m);
|
||||
app_ref def(m);
|
||||
proxy = m_parent.fresh_proxy ();
|
||||
def = m.mk_or (m.mk_not (proxy), v);
|
||||
m_defs.push_back (def);
|
||||
m_expr2proxy.insert (v, proxy);
|
||||
m_proxy2def.insert (proxy, def);
|
||||
|
||||
m_parent.assert_expr (def.get ());
|
||||
return proxy;
|
||||
}
|
||||
|
||||
bool itp_solver::def_manager::is_proxy (app *k, app_ref &def)
|
||||
{
|
||||
app *r = NULL;
|
||||
bool found = m_proxy2def.find (k, r);
|
||||
def = r;
|
||||
return found;
|
||||
}
|
||||
|
||||
void itp_solver::def_manager::reset ()
|
||||
{
|
||||
m_expr2proxy.reset ();
|
||||
m_proxy2def.reset ();
|
||||
m_defs.reset ();
|
||||
}
|
||||
|
||||
bool itp_solver::def_manager::is_proxy_def (expr *v)
|
||||
{
|
||||
// XXX This might not be the most robust way to check
|
||||
return m_defs.contains (v);
|
||||
}
|
||||
|
||||
bool itp_solver::is_proxy(expr *e, app_ref &def)
|
||||
{
|
||||
if (!is_uninterp_const(e)) { return false; }
|
||||
|
||||
app *a = to_app (e);
|
||||
|
||||
for (int i = m_defs.size (); i > 0; --i)
|
||||
if (m_defs[i-1].is_proxy (a, def))
|
||||
{ return true; }
|
||||
|
||||
if (m_base_defs.is_proxy (a, def))
|
||||
{ return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void itp_solver::collect_statistics (statistics &st) const
|
||||
{
|
||||
m_solver.collect_statistics (st);
|
||||
st.update ("time.itp_solver.itp_core", m_itp_watch.get_seconds ());
|
||||
}
|
||||
|
||||
void itp_solver::reset_statistics ()
|
||||
{
|
||||
m_itp_watch.reset ();
|
||||
}
|
||||
|
||||
void itp_solver::get_unsat_core (ptr_vector<expr> &core)
|
||||
{
|
||||
m_solver.get_unsat_core (core);
|
||||
undo_proxies_in_core (core);
|
||||
}
|
||||
void itp_solver::undo_proxies_in_core (ptr_vector<expr> &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
expr_fast_mark1 bg;
|
||||
for (unsigned i = 0; i < m_first_assumption; ++i)
|
||||
{ bg.mark(m_assumptions.get(i)); }
|
||||
|
||||
// expand proxies
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0, sz = r.size(); i < sz; ++i) {
|
||||
// skip background assumptions
|
||||
if (bg.is_marked(r[i])) { continue; }
|
||||
|
||||
// -- undo proxies, but only if they were introduced in check_sat
|
||||
if (m_is_proxied && is_proxy(r[i], e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[j] = e->get_arg (1);
|
||||
} else if (i != j) { r[j] = r[i]; }
|
||||
j++;
|
||||
}
|
||||
r.shrink (j);
|
||||
}
|
||||
|
||||
void itp_solver::undo_proxies (expr_ref_vector &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
// expand proxies
|
||||
for (unsigned i = 0, sz = r.size (); i < sz; ++i)
|
||||
if (is_proxy(r.get(i), e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[i] = e->get_arg (1);
|
||||
}
|
||||
}
|
||||
|
||||
void itp_solver::get_unsat_core (expr_ref_vector &_core)
|
||||
{
|
||||
ptr_vector<expr> core;
|
||||
get_unsat_core (core);
|
||||
_core.append (core.size (), core.c_ptr ());
|
||||
}
|
||||
|
||||
void itp_solver::elim_proxies (expr_ref_vector &v)
|
||||
{
|
||||
expr_ref f = mk_and (v);
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer (m);
|
||||
rep->set_substitution (&m_elim_proxies_sub);
|
||||
(*rep) (f);
|
||||
v.reset ();
|
||||
flatten_and (f, v);
|
||||
}
|
||||
|
||||
void itp_solver::get_itp_core (expr_ref_vector &core)
|
||||
{
|
||||
scoped_watch _t_ (m_itp_watch);
|
||||
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
expr_set B;
|
||||
for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) {
|
||||
expr *a = m_assumptions.get (i);
|
||||
app_ref def(m);
|
||||
if (is_proxy(a, def)) { B.insert(def.get()); }
|
||||
B.insert (a);
|
||||
}
|
||||
|
||||
proof_ref pr(m);
|
||||
pr = get_proof ();
|
||||
|
||||
if (!m_new_unsat_core) {
|
||||
// old code
|
||||
farkas_learner learner_old;
|
||||
learner_old.set_split_literals(m_split_literals);
|
||||
|
||||
learner_old.get_lemmas (pr, B, core);
|
||||
elim_proxies (core);
|
||||
simplify_bounds (core); // XXX potentially redundant
|
||||
} else {
|
||||
// new code
|
||||
unsat_core_learner learner(m);
|
||||
|
||||
if (m_farkas_optimized) {
|
||||
if (true) // TODO: proper options
|
||||
{
|
||||
unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m);
|
||||
learner.register_plugin(plugin_farkas_lemma_optimized);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m);
|
||||
learner.register_plugin(plugin_farkas_lemma_bounded);
|
||||
}
|
||||
|
||||
} else {
|
||||
unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, m_farkas_a_const);
|
||||
learner.register_plugin(plugin_farkas_lemma);
|
||||
}
|
||||
|
||||
if (m_minimize_unsat_core) {
|
||||
unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m);
|
||||
learner.register_plugin(plugin_min_cut);
|
||||
} else {
|
||||
unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner);
|
||||
learner.register_plugin(plugin_lemma);
|
||||
}
|
||||
|
||||
learner.compute_unsat_core(pr, B, core);
|
||||
|
||||
elim_proxies (core);
|
||||
simplify_bounds (core); // XXX potentially redundant
|
||||
|
||||
// // debug
|
||||
// expr_ref_vector core2(m);
|
||||
// unsat_core_learner learner2(m);
|
||||
//
|
||||
// unsat_core_plugin_farkas_lemma* plugin_farkas_lemma2 = alloc(unsat_core_plugin_farkas_lemma, learner2, m_split_literals);
|
||||
// learner2.register_plugin(plugin_farkas_lemma2);
|
||||
// unsat_core_plugin_lemma* plugin_lemma2 = alloc(unsat_core_plugin_lemma, learner2);
|
||||
// learner2.register_plugin(plugin_lemma2);
|
||||
// learner2.compute_unsat_core(pr, B, core2);
|
||||
//
|
||||
// elim_proxies (core2);
|
||||
// simplify_bounds (core2);
|
||||
//
|
||||
// IF_VERBOSE(2,
|
||||
// verbose_stream () << "Itp Core:\n"
|
||||
// << mk_pp (mk_and (core), m) << "\n";);
|
||||
// IF_VERBOSE(2,
|
||||
// verbose_stream () << "Itp Core2:\n"
|
||||
// << mk_pp (mk_and (core2), m) << "\n";);
|
||||
//SASSERT(mk_and (core) == mk_and (core2));
|
||||
}
|
||||
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream () << "Itp Core:\n"
|
||||
<< mk_pp (mk_and (core), m) << "\n";);
|
||||
|
||||
}
|
||||
|
||||
void itp_solver::refresh ()
|
||||
{
|
||||
// only refresh in non-pushed state
|
||||
SASSERT (m_defs.size () == 0);
|
||||
expr_ref_vector assertions (m);
|
||||
for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) {
|
||||
expr* a = m_solver.get_assertion (i);
|
||||
if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); }
|
||||
|
||||
}
|
||||
m_base_defs.reset ();
|
||||
NOT_IMPLEMENTED_YET ();
|
||||
// solver interface does not have a reset method. need to introduce it somewhere.
|
||||
// m_solver.reset ();
|
||||
for (unsigned i = 0, e = assertions.size (); i < e; ++i)
|
||||
{ m_solver.assert_expr(assertions.get(i)); }
|
||||
}
|
||||
|
||||
}
|
||||
179
src/muz/spacer/spacer_itp_solver.h
Normal file
179
src/muz/spacer/spacer_itp_solver.h
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_itp_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef SPACER_ITP_SOLVER_H_
|
||||
#define SPACER_ITP_SOLVER_H_
|
||||
|
||||
#include"solver/solver.h"
|
||||
#include"ast/expr_substitution.h"
|
||||
#include"util/stopwatch.h"
|
||||
namespace spacer {
|
||||
class itp_solver : public solver {
|
||||
private:
|
||||
struct def_manager {
|
||||
itp_solver &m_parent;
|
||||
obj_map<expr, app*> m_expr2proxy;
|
||||
obj_map<app, app*> m_proxy2def;
|
||||
|
||||
expr_ref_vector m_defs;
|
||||
|
||||
def_manager(itp_solver &parent) :
|
||||
m_parent(parent), m_defs(m_parent.m)
|
||||
{}
|
||||
|
||||
bool is_proxy(app *k, app_ref &v);
|
||||
app* mk_proxy(expr *v);
|
||||
void reset();
|
||||
bool is_proxy_def(expr *v);
|
||||
|
||||
};
|
||||
|
||||
friend struct def_manager;
|
||||
ast_manager &m;
|
||||
solver &m_solver;
|
||||
app_ref_vector m_proxies;
|
||||
unsigned m_num_proxies;
|
||||
vector<def_manager> m_defs;
|
||||
def_manager m_base_defs;
|
||||
expr_ref_vector m_assumptions;
|
||||
unsigned m_first_assumption;
|
||||
bool m_is_proxied;
|
||||
|
||||
stopwatch m_itp_watch;
|
||||
|
||||
expr_substitution m_elim_proxies_sub;
|
||||
bool m_split_literals;
|
||||
bool m_new_unsat_core;
|
||||
bool m_minimize_unsat_core;
|
||||
bool m_farkas_optimized;
|
||||
bool m_farkas_a_const;
|
||||
|
||||
bool is_proxy(expr *e, app_ref &def);
|
||||
void undo_proxies_in_core(ptr_vector<expr> &v);
|
||||
app* mk_proxy(expr *v);
|
||||
app* fresh_proxy();
|
||||
void elim_proxies(expr_ref_vector &v);
|
||||
public:
|
||||
itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool split_literals = false) :
|
||||
m(solver.get_manager()),
|
||||
m_solver(solver),
|
||||
m_proxies(m),
|
||||
m_num_proxies(0),
|
||||
m_base_defs(*this),
|
||||
m_assumptions(m),
|
||||
m_first_assumption(0),
|
||||
m_is_proxied(false),
|
||||
m_elim_proxies_sub(m, false, true),
|
||||
m_split_literals(split_literals),
|
||||
m_new_unsat_core(new_unsat_core),
|
||||
m_minimize_unsat_core(minimize_unsat_core),
|
||||
m_farkas_optimized(farkas_optimized),
|
||||
m_farkas_a_const(farkas_a_const)
|
||||
{}
|
||||
|
||||
virtual ~itp_solver() {}
|
||||
|
||||
/* itp solver specific */
|
||||
virtual void get_unsat_core(expr_ref_vector &core);
|
||||
virtual void get_itp_core(expr_ref_vector &core);
|
||||
void set_split_literals(bool v) {m_split_literals = v;}
|
||||
bool mk_proxies(expr_ref_vector &v, unsigned from = 0);
|
||||
void undo_proxies(expr_ref_vector &v);
|
||||
|
||||
void push_bg(expr *e);
|
||||
void pop_bg(unsigned n);
|
||||
unsigned get_num_bg();
|
||||
|
||||
void get_full_unsat_core(ptr_vector<expr> &core)
|
||||
{m_solver.get_unsat_core(core);}
|
||||
|
||||
/* solver interface */
|
||||
|
||||
virtual solver* translate(ast_manager &m, params_ref const &p)
|
||||
{return m_solver.translate(m, p);}
|
||||
virtual void updt_params(params_ref const &p)
|
||||
{m_solver.updt_params(p);}
|
||||
virtual void collect_param_descrs(param_descrs &r)
|
||||
{m_solver.collect_param_descrs(r);}
|
||||
virtual void set_produce_models(bool f)
|
||||
{m_solver.set_produce_models(f);}
|
||||
virtual void assert_expr(expr *t)
|
||||
{m_solver.assert_expr(t);}
|
||||
|
||||
virtual void assert_expr(expr *t, expr *a)
|
||||
{NOT_IMPLEMENTED_YET();}
|
||||
virtual void assert_lemma(expr* e) { NOT_IMPLEMENTED_YET(); }
|
||||
virtual expr_ref lookahead(const expr_ref_vector &,const expr_ref_vector &) { return expr_ref(m.mk_true(), m); }
|
||||
|
||||
virtual void push();
|
||||
virtual void pop(unsigned n);
|
||||
virtual unsigned get_scope_level() const
|
||||
{return m_solver.get_scope_level();}
|
||||
|
||||
virtual lbool check_sat(unsigned num_assumptions, expr * const *assumptions);
|
||||
virtual void set_progress_callback(progress_callback *callback)
|
||||
{m_solver.set_progress_callback(callback);}
|
||||
virtual unsigned get_num_assertions() const
|
||||
{return m_solver.get_num_assertions();}
|
||||
virtual expr * get_assertion(unsigned idx) const
|
||||
{return m_solver.get_assertion(idx);}
|
||||
virtual unsigned get_num_assumptions() const
|
||||
{return m_solver.get_num_assumptions();}
|
||||
virtual expr * get_assumption(unsigned idx) const
|
||||
{return m_solver.get_assumption(idx);}
|
||||
virtual std::ostream &display(std::ostream &out) const
|
||||
{m_solver.display(out); return out;}
|
||||
|
||||
/* check_sat_result interface */
|
||||
|
||||
virtual void collect_statistics(statistics &st) const ;
|
||||
virtual void reset_statistics();
|
||||
virtual void get_unsat_core(ptr_vector<expr> &r);
|
||||
virtual void get_model(model_ref &m) {m_solver.get_model(m);}
|
||||
virtual proof *get_proof() {return m_solver.get_proof();}
|
||||
virtual std::string reason_unknown() const
|
||||
{return m_solver.reason_unknown();}
|
||||
virtual void set_reason_unknown(char const* msg)
|
||||
{m_solver.set_reason_unknown(msg);}
|
||||
virtual void get_labels(svector<symbol> &r)
|
||||
{m_solver.get_labels(r);}
|
||||
virtual ast_manager &get_manager() const {return m;}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
class scoped_mk_proxy {
|
||||
itp_solver &m_s;
|
||||
expr_ref_vector &m_v;
|
||||
public:
|
||||
scoped_mk_proxy(itp_solver &s, expr_ref_vector &v) : m_s(s), m_v(v)
|
||||
{m_s.mk_proxies(m_v);}
|
||||
~scoped_mk_proxy()
|
||||
{m_s.undo_proxies(m_v);}
|
||||
};
|
||||
|
||||
class scoped_bg {
|
||||
itp_solver &m_s;
|
||||
unsigned m_bg_sz;
|
||||
public:
|
||||
scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {}
|
||||
~scoped_bg()
|
||||
{if(m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }}
|
||||
};
|
||||
};
|
||||
}
|
||||
#endif
|
||||
171
src/muz/spacer/spacer_legacy_frames.cpp
Normal file
171
src/muz/spacer/spacer_legacy_frames.cpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Legacy implementations of frames. To be removed.
|
||||
*/
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "util/util.h"
|
||||
#include "muz/spacer/spacer_prop_solver.h"
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/spacer/spacer_generalizers.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "smt/tactic/unit_subsumption_tactic.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "muz/transforms/dl_mk_rule_inliner.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/proof_checker/proof_checker.h"
|
||||
#include "smt/smt_value_sort.h"
|
||||
#include "muz/base/proof_utils.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "muz/spacer/spacer_qe_project.h"
|
||||
#include "tactic/core/blast_term_ite_tactic.h"
|
||||
|
||||
#include "util/timeit.h"
|
||||
#include "util/luby.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/factor_equivs.h"
|
||||
|
||||
|
||||
namespace spacer {
|
||||
// ------------------
|
||||
// legacy_frames
|
||||
void pred_transformer::legacy_frames::simplify_formulas(tactic& tac,
|
||||
expr_ref_vector& v)
|
||||
{
|
||||
ast_manager &m = m_pt.get_ast_manager();
|
||||
goal_ref g(alloc(goal, m, false, false, false));
|
||||
for (unsigned j = 0; j < v.size(); ++j) { g->assert_expr(v[j].get()); }
|
||||
model_converter_ref mc;
|
||||
proof_converter_ref pc;
|
||||
expr_dependency_ref core(m);
|
||||
goal_ref_buffer result;
|
||||
tac(g, result, mc, pc, core);
|
||||
SASSERT(result.size() == 1);
|
||||
goal* r = result[0];
|
||||
v.reset();
|
||||
for (unsigned j = 0; j < r->size(); ++j) { v.push_back(r->form(j)); }
|
||||
}
|
||||
|
||||
void pred_transformer::legacy_frames::simplify_formulas()
|
||||
{
|
||||
ast_manager &m = m_pt.get_ast_manager();
|
||||
tactic_ref us = mk_unit_subsumption_tactic(m);
|
||||
simplify_formulas(*us, m_invariants);
|
||||
for (unsigned i = 0; i < m_levels.size(); ++i) {
|
||||
simplify_formulas(*us, m_levels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void pred_transformer::legacy_frames::get_frame_geq_lemmas(unsigned lvl,
|
||||
expr_ref_vector &out)
|
||||
{
|
||||
get_frame_lemmas(infty_level(), out);
|
||||
for (unsigned i = lvl, sz = m_levels.size(); i < sz; ++i)
|
||||
{ get_frame_lemmas(i, out); }
|
||||
}
|
||||
|
||||
bool pred_transformer::legacy_frames::propagate_to_next_level(unsigned src_level)
|
||||
{
|
||||
|
||||
ast_manager &m = m_pt.get_ast_manager();
|
||||
(void) m;
|
||||
if (m_levels.size() <= src_level) { return true; }
|
||||
if (m_levels [src_level].empty()) { return true; }
|
||||
|
||||
unsigned tgt_level = next_level(src_level);
|
||||
m_pt.ensure_level(next_level(tgt_level));
|
||||
|
||||
TRACE("spacer",
|
||||
tout << "propagating " << src_level << " to " << tgt_level;
|
||||
tout << " for relation " << m_pt.head()->get_name() << "\n";);
|
||||
|
||||
for (unsigned i = 0; i < m_levels[src_level].size();) {
|
||||
expr_ref_vector &src = m_levels[src_level];
|
||||
expr * curr = src[i].get();
|
||||
unsigned stored_lvl;
|
||||
VERIFY(m_prop2level.find(curr, stored_lvl));
|
||||
SASSERT(stored_lvl >= src_level);
|
||||
unsigned solver_level;
|
||||
if (stored_lvl > src_level) {
|
||||
TRACE("spacer", tout << "at level: " << stored_lvl << " " << mk_pp(curr, m) << "\n";);
|
||||
src[i] = src.back();
|
||||
src.pop_back();
|
||||
} else if (m_pt.is_invariant(tgt_level, curr, solver_level)) {
|
||||
// -- might invalidate src reference
|
||||
add_lemma(curr, solver_level);
|
||||
TRACE("spacer", tout << "is invariant: " << pp_level(solver_level) << " " << mk_pp(curr, m) << "\n";);
|
||||
// shadow higher-level src
|
||||
expr_ref_vector &src = m_levels[src_level];
|
||||
src[i] = src.back();
|
||||
src.pop_back();
|
||||
++m_pt.m_stats.m_num_propagations;
|
||||
} else {
|
||||
TRACE("spacer", tout << "not propagated: " << mk_pp(curr, m) << "\n";);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
CTRACE("spacer", m_levels[src_level].empty(),
|
||||
tout << "Fully propagated level "
|
||||
<< src_level << " of " << m_pt.head()->get_name() << "\n";);
|
||||
|
||||
return m_levels[src_level].empty();
|
||||
}
|
||||
|
||||
bool pred_transformer::legacy_frames::add_lemma(expr * lemma, unsigned lvl)
|
||||
{
|
||||
if (is_infty_level(lvl)) {
|
||||
if (!m_invariants.contains(lemma)) {
|
||||
m_invariants.push_back(lemma);
|
||||
m_prop2level.insert(lemma, lvl);
|
||||
//m_pt.add_lemma_core (lemma, lvl);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_level;
|
||||
if (!m_prop2level.find(lemma, old_level) || old_level < lvl) {
|
||||
m_levels[lvl].push_back(lemma);
|
||||
m_prop2level.insert(lemma, lvl);
|
||||
//m_pt.add_lemma_core (lemma, lvl);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void pred_transformer::legacy_frames::propagate_to_infinity(unsigned level)
|
||||
{
|
||||
TRACE("spacer", tout << "propagating to oo from lvl " << level
|
||||
<< " of " << m_pt.m_head->get_name() << "\n";);
|
||||
|
||||
if (m_levels.empty()) { return; }
|
||||
|
||||
for (unsigned i = m_levels.size(); i > level; --i) {
|
||||
expr_ref_vector &lemmas = m_levels [i - 1];
|
||||
for (unsigned j = 0; j < lemmas.size(); ++j)
|
||||
{ add_lemma(lemmas.get(j), infty_level()); }
|
||||
lemmas.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void pred_transformer::legacy_frames::inherit_frames(legacy_frames& other)
|
||||
{
|
||||
|
||||
SASSERT(m_pt.m_head == other.m_pt.m_head);
|
||||
obj_map<expr, unsigned>::iterator it = other.m_prop2level.begin();
|
||||
obj_map<expr, unsigned>::iterator end = other.m_prop2level.end();
|
||||
for (; it != end; ++it) { add_lemma(it->m_key, it->m_value); }
|
||||
}
|
||||
}
|
||||
47
src/muz/spacer/spacer_legacy_frames.h
Normal file
47
src/muz/spacer/spacer_legacy_frames.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Legacy implementations of frames. To be removed.
|
||||
|
||||
Notes: this file is included from the middle of spacer_context.h
|
||||
*/
|
||||
|
||||
class legacy_frames
|
||||
{
|
||||
pred_transformer &m_pt;
|
||||
|
||||
/// level formulas
|
||||
vector<expr_ref_vector> m_levels;
|
||||
/// map property to level where it occurs.
|
||||
obj_map<expr, unsigned> m_prop2level;
|
||||
/// properties that are invariant.
|
||||
expr_ref_vector m_invariants;
|
||||
|
||||
void simplify_formulas (tactic& tac, expr_ref_vector& v);
|
||||
|
||||
public:
|
||||
legacy_frames (pred_transformer &pt) :
|
||||
m_pt(pt), m_invariants (m_pt.get_ast_manager ()) {}
|
||||
pred_transformer& pt () const {return m_pt;}
|
||||
bool add_lemma (expr * lemma, unsigned level);
|
||||
void get_frame_lemmas (unsigned level, expr_ref_vector &out)
|
||||
{
|
||||
if(is_infty_level(level)) { out.append(m_invariants); }
|
||||
else if(level < m_levels.size()) { out.append(m_levels [level]); }
|
||||
}
|
||||
|
||||
void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out);
|
||||
void add_frame () {m_levels.push_back (expr_ref_vector (m_pt.get_ast_manager ()));}
|
||||
|
||||
unsigned size () const {return m_levels.size ();}
|
||||
unsigned lemma_size () const {return m_prop2level.size ();}
|
||||
|
||||
|
||||
void propagate_to_infinity (unsigned level);
|
||||
bool propagate_to_next_level (unsigned level);
|
||||
|
||||
void simplify_formulas ();
|
||||
|
||||
void inherit_frames (legacy_frames& other);
|
||||
|
||||
};
|
||||
114
src/muz/spacer/spacer_legacy_mbp.cpp
Normal file
114
src/muz/spacer/spacer_legacy_mbp.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_legacy_mbp.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Legacy Model Based Projection. Used by Grigory Fedyukovich
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
Anvesh Komuravelli
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include <sstream>
|
||||
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "model/model.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "util/util.h"
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "qe/qe_lite.h"
|
||||
#include "muz/spacer/spacer_qe_project.h"
|
||||
#include "model/model_pp.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
#include "muz/spacer/spacer_legacy_mev.h"
|
||||
|
||||
namespace spacer {
|
||||
void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map)
|
||||
{
|
||||
th_rewriter rw(m);
|
||||
// qe-lite; TODO: use qe_lite aggressively
|
||||
params_ref p;
|
||||
qe_lite qe(m, p, true);
|
||||
qe(vars, fml);
|
||||
rw(fml);
|
||||
|
||||
TRACE("spacer",
|
||||
tout << "After qe_lite:\n";
|
||||
tout << mk_pp(fml, m) << "\n";
|
||||
tout << "Vars:\n";
|
||||
for (unsigned i = 0; i < vars.size(); ++i) {
|
||||
tout << mk_pp(vars.get(i), m) << "\n";
|
||||
}
|
||||
);
|
||||
|
||||
// substitute model values for booleans and
|
||||
// use LW projection for arithmetic variables
|
||||
if (!vars.empty()) {
|
||||
app_ref_vector arith_vars(m);
|
||||
expr_substitution sub(m);
|
||||
proof_ref pr(m.mk_asserted(m.mk_true()), m);
|
||||
expr_ref bval(m);
|
||||
for (unsigned i = 0; i < vars.size(); i++) {
|
||||
if (m.is_bool(vars.get(i))) {
|
||||
// obtain the interpretation of the ith var using model completion
|
||||
VERIFY(M->eval(vars.get(i), bval, true));
|
||||
sub.insert(vars.get(i), bval, pr);
|
||||
} else {
|
||||
arith_vars.push_back(vars.get(i));
|
||||
}
|
||||
}
|
||||
if (!sub.empty()) {
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer(m);
|
||||
rep->set_substitution(&sub);
|
||||
(*rep)(fml);
|
||||
rw(fml);
|
||||
TRACE("spacer",
|
||||
tout << "Projected Boolean vars:\n" << mk_pp(fml, m) << "\n";
|
||||
);
|
||||
}
|
||||
// model based projection
|
||||
if (!arith_vars.empty()) {
|
||||
TRACE("spacer",
|
||||
tout << "Arith vars:\n";
|
||||
for (unsigned i = 0; i < arith_vars.size(); ++i) {
|
||||
tout << mk_pp(arith_vars.get(i), m) << "\n";
|
||||
}
|
||||
);
|
||||
{
|
||||
scoped_no_proof _sp(m);
|
||||
qe::arith_project(*M, arith_vars, fml, map);
|
||||
}
|
||||
SASSERT(arith_vars.empty());
|
||||
TRACE("spacer",
|
||||
tout << "Projected arith vars:\n" << mk_pp(fml, m) << "\n";
|
||||
);
|
||||
}
|
||||
SASSERT(M->eval(fml, bval, true) && m.is_true(bval)); // M |= fml
|
||||
vars.reset();
|
||||
vars.append(arith_vars);
|
||||
}
|
||||
}
|
||||
}
|
||||
834
src/muz/spacer/spacer_legacy_mev.cpp
Normal file
834
src/muz/spacer/spacer_legacy_mev.cpp
Normal file
|
|
@ -0,0 +1,834 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Deprecated implementation of model evaluator. To be removed.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "model/model.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "util/util.h"
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "muz/spacer/spacer_legacy_mev.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "qe/qe_lite.h"
|
||||
#include "muz/spacer/spacer_qe_project.h"
|
||||
#include "model/model_pp.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
namespace old {
|
||||
|
||||
/////////////////////////
|
||||
// model_evaluator
|
||||
//
|
||||
|
||||
|
||||
void model_evaluator::assign_value(expr* e, expr* val)
|
||||
{
|
||||
rational r;
|
||||
if (m.is_true(val)) {
|
||||
set_true(e);
|
||||
} else if (m.is_false(val)) {
|
||||
set_false(e);
|
||||
} else if (m_arith.is_numeral(val, r)) {
|
||||
set_number(e, r);
|
||||
} else if (m.is_value(val)) {
|
||||
set_value(e, val);
|
||||
} else {
|
||||
IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << "\n";);
|
||||
TRACE("old_spacer", tout << "Variable is not tracked: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::setup_model(const model_ref& model)
|
||||
{
|
||||
m_numbers.reset();
|
||||
m_values.reset();
|
||||
m_model = model.get();
|
||||
rational r;
|
||||
unsigned sz = model->get_num_constants();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
func_decl * d = model->get_constant(i);
|
||||
expr* val = model->get_const_interp(d);
|
||||
expr* e = m.mk_const(d);
|
||||
m_refs.push_back(e);
|
||||
assign_value(e, val);
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::reset()
|
||||
{
|
||||
m1.reset();
|
||||
m2.reset();
|
||||
m_values.reset();
|
||||
m_visited.reset();
|
||||
m_numbers.reset();
|
||||
m_refs.reset();
|
||||
m_model = 0;
|
||||
}
|
||||
|
||||
|
||||
void model_evaluator::minimize_literals(ptr_vector<expr> const& formulas,
|
||||
const model_ref& mdl, expr_ref_vector& result)
|
||||
{
|
||||
|
||||
TRACE("old_spacer",
|
||||
tout << "formulas:\n";
|
||||
for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n";
|
||||
);
|
||||
|
||||
expr_ref tmp(m);
|
||||
ptr_vector<expr> tocollect;
|
||||
|
||||
setup_model(mdl);
|
||||
collect(formulas, tocollect);
|
||||
for (unsigned i = 0; i < tocollect.size(); ++i) {
|
||||
expr* e = tocollect[i];
|
||||
expr* e1, *e2;
|
||||
SASSERT(m.is_bool(e));
|
||||
SASSERT(is_true(e) || is_false(e));
|
||||
if (is_true(e)) {
|
||||
result.push_back(e);
|
||||
}
|
||||
// hack to break disequalities for arithmetic variables.
|
||||
else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) {
|
||||
if (get_number(e1) < get_number(e2)) {
|
||||
result.push_back(m_arith.mk_lt(e1, e2));
|
||||
} else {
|
||||
result.push_back(m_arith.mk_lt(e2, e1));
|
||||
}
|
||||
} else {
|
||||
result.push_back(m.mk_not(e));
|
||||
}
|
||||
}
|
||||
reset();
|
||||
TRACE("old_spacer",
|
||||
tout << "minimized model:\n";
|
||||
for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
void model_evaluator::process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect)
|
||||
{
|
||||
SASSERT(m.is_bool(e));
|
||||
SASSERT(is_true(e) || is_false(e));
|
||||
unsigned v = is_true(e);
|
||||
unsigned sz = e->get_num_args();
|
||||
expr* const* args = e->get_args();
|
||||
if (e->get_family_id() == m.get_basic_family_id()) {
|
||||
switch (e->get_decl_kind()) {
|
||||
case OP_TRUE:
|
||||
break;
|
||||
case OP_FALSE:
|
||||
break;
|
||||
case OP_EQ:
|
||||
case OP_IFF:
|
||||
if (args[0] == args[1]) {
|
||||
SASSERT(v);
|
||||
// no-op
|
||||
} else if (m.is_bool(args[0])) {
|
||||
todo.append(sz, args);
|
||||
} else {
|
||||
tocollect.push_back(e);
|
||||
}
|
||||
break;
|
||||
case OP_DISTINCT:
|
||||
tocollect.push_back(e);
|
||||
break;
|
||||
case OP_ITE:
|
||||
if (args[1] == args[2]) {
|
||||
tocollect.push_back(args[1]);
|
||||
} else if (is_true(args[1]) && is_true(args[2])) {
|
||||
todo.append(2, args + 1);
|
||||
} else if (is_false(args[1]) && is_false(args[2])) {
|
||||
todo.append(2, args + 1);
|
||||
} else if (is_true(args[0])) {
|
||||
todo.append(2, args);
|
||||
} else {
|
||||
SASSERT(is_false(args[0]));
|
||||
todo.push_back(args[0]);
|
||||
todo.push_back(args[2]);
|
||||
}
|
||||
break;
|
||||
case OP_AND:
|
||||
if (v) {
|
||||
todo.append(sz, args);
|
||||
} else {
|
||||
unsigned i = 0;
|
||||
for (; !is_false(args[i]) && i < sz; ++i);
|
||||
if (i == sz) {
|
||||
fatal_error(1);
|
||||
}
|
||||
VERIFY(i < sz);
|
||||
todo.push_back(args[i]);
|
||||
}
|
||||
break;
|
||||
case OP_OR:
|
||||
if (v) {
|
||||
unsigned i = 0;
|
||||
for (; !is_true(args[i]) && i < sz; ++i);
|
||||
if (i == sz) {
|
||||
fatal_error(1);
|
||||
}
|
||||
VERIFY(i < sz);
|
||||
todo.push_back(args[i]);
|
||||
} else {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
break;
|
||||
case OP_XOR:
|
||||
case OP_NOT:
|
||||
todo.append(sz, args);
|
||||
break;
|
||||
case OP_IMPLIES:
|
||||
if (v) {
|
||||
if (is_true(args[1])) {
|
||||
todo.push_back(args[1]);
|
||||
} else if (is_false(args[0])) {
|
||||
todo.push_back(args[0]);
|
||||
} else {
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
tocollect.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect)
|
||||
{
|
||||
ptr_vector<expr> todo;
|
||||
todo.append(formulas);
|
||||
m_visited.reset();
|
||||
|
||||
VERIFY(check_model(formulas));
|
||||
|
||||
while (!todo.empty()) {
|
||||
app* e = to_app(todo.back());
|
||||
todo.pop_back();
|
||||
if (!m_visited.is_marked(e)) {
|
||||
process_formula(e, todo, tocollect);
|
||||
m_visited.mark(e, true);
|
||||
}
|
||||
}
|
||||
m_visited.reset();
|
||||
}
|
||||
|
||||
void model_evaluator::eval_arith(app* e)
|
||||
{
|
||||
rational r, r2;
|
||||
|
||||
#define ARG1 e->get_arg(0)
|
||||
#define ARG2 e->get_arg(1)
|
||||
|
||||
unsigned arity = e->get_num_args();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* arg = e->get_arg(i);
|
||||
if (is_x(arg)) {
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
SASSERT(!is_unknown(arg));
|
||||
}
|
||||
switch (e->get_decl_kind()) {
|
||||
case OP_NUM:
|
||||
VERIFY(m_arith.is_numeral(e, r));
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_IRRATIONAL_ALGEBRAIC_NUM:
|
||||
set_x(e);
|
||||
break;
|
||||
case OP_LE:
|
||||
set_bool(e, get_number(ARG1) <= get_number(ARG2));
|
||||
break;
|
||||
case OP_GE:
|
||||
set_bool(e, get_number(ARG1) >= get_number(ARG2));
|
||||
break;
|
||||
case OP_LT:
|
||||
set_bool(e, get_number(ARG1) < get_number(ARG2));
|
||||
break;
|
||||
case OP_GT:
|
||||
set_bool(e, get_number(ARG1) > get_number(ARG2));
|
||||
break;
|
||||
case OP_ADD:
|
||||
r = rational::zero();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
r += get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_SUB:
|
||||
r = get_number(e->get_arg(0));
|
||||
for (unsigned i = 1; i < arity; ++i) {
|
||||
r -= get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_UMINUS:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, -get_number(e->get_arg(0)));
|
||||
break;
|
||||
case OP_MUL:
|
||||
r = rational::one();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
r *= get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_DIV:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
} else {
|
||||
set_number(e, get_number(ARG1) / r);
|
||||
}
|
||||
break;
|
||||
case OP_IDIV:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
} else {
|
||||
set_number(e, div(get_number(ARG1), r));
|
||||
}
|
||||
break;
|
||||
case OP_REM:
|
||||
// rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
} else {
|
||||
r2 = mod(get_number(ARG1), r);
|
||||
if (r.is_neg()) { r2.neg(); }
|
||||
set_number(e, r2);
|
||||
}
|
||||
break;
|
||||
case OP_MOD:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
} else {
|
||||
set_number(e, mod(get_number(ARG1), r));
|
||||
}
|
||||
break;
|
||||
case OP_TO_REAL:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, get_number(ARG1));
|
||||
break;
|
||||
case OP_TO_INT:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, floor(get_number(ARG1)));
|
||||
break;
|
||||
case OP_IS_INT:
|
||||
SASSERT(arity == 1);
|
||||
set_bool(e, get_number(ARG1).is_int());
|
||||
break;
|
||||
case OP_POWER:
|
||||
set_x(e);
|
||||
break;
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::inherit_value(expr* e, expr* v)
|
||||
{
|
||||
expr* w;
|
||||
SASSERT(!is_unknown(v));
|
||||
SASSERT(m.get_sort(e) == m.get_sort(v));
|
||||
if (is_x(v)) {
|
||||
set_x(e);
|
||||
} else if (m.is_bool(e)) {
|
||||
SASSERT(m.is_bool(v));
|
||||
if (is_true(v)) { set_true(e); }
|
||||
else if (is_false(v)) { set_false(e); }
|
||||
else {
|
||||
TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
} else if (m_arith.is_int_real(e)) {
|
||||
set_number(e, get_number(v));
|
||||
} else if (m.is_value(v)) {
|
||||
set_value(e, v);
|
||||
} else if (m_values.find(v, w)) {
|
||||
set_value(e, w);
|
||||
} else {
|
||||
TRACE("old_spacer", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::eval_exprs(expr_ref_vector& es)
|
||||
{
|
||||
model_ref mr(m_model);
|
||||
for (unsigned j = 0; j < es.size(); ++j) {
|
||||
if (m_array.is_as_array(es[j].get())) {
|
||||
es[j] = eval(mr, es[j].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool model_evaluator::extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case)
|
||||
{
|
||||
SASSERT(m_array.is_array(a));
|
||||
|
||||
TRACE("old_spacer", tout << mk_pp(a, m) << "\n";);
|
||||
while (m_array.is_store(a)) {
|
||||
expr_ref_vector store(m);
|
||||
store.append(to_app(a)->get_num_args() - 1, to_app(a)->get_args() + 1);
|
||||
eval_exprs(store);
|
||||
stores.push_back(store);
|
||||
a = to_app(a)->get_arg(0);
|
||||
}
|
||||
|
||||
if (m_array.is_const(a)) {
|
||||
else_case = to_app(a)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (m_array.is_as_array(a)) {
|
||||
func_decl* f = m_array.get_as_array_func_decl(to_app(a));
|
||||
func_interp* g = m_model->get_func_interp(f);
|
||||
unsigned sz = g->num_entries();
|
||||
unsigned arity = f->get_arity();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr_ref_vector store(m);
|
||||
func_entry const* fe = g->get_entry(i);
|
||||
store.append(arity, fe->get_args());
|
||||
store.push_back(fe->get_result());
|
||||
for (unsigned j = 0; j < store.size(); ++j) {
|
||||
if (!is_ground(store[j].get())) {
|
||||
TRACE("old_spacer", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
eval_exprs(store);
|
||||
stores.push_back(store);
|
||||
}
|
||||
else_case = g->get_else();
|
||||
if (!else_case) {
|
||||
TRACE("old_spacer", tout << "no else case " << mk_pp(a, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (!is_ground(else_case)) {
|
||||
TRACE("old_spacer", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (m_array.is_as_array(else_case)) {
|
||||
model_ref mr(m_model);
|
||||
else_case = eval(mr, else_case);
|
||||
}
|
||||
TRACE("old_spacer", tout << "else case: " << mk_pp(else_case, m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
TRACE("old_spacer", tout << "no translation: " << mk_pp(a, m) << "\n";);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
best effort evaluator of extensional array equality.
|
||||
*/
|
||||
void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2)
|
||||
{
|
||||
TRACE("old_spacer", tout << "array equality: " << mk_pp(e, m) << "\n";);
|
||||
expr_ref v1(m), v2(m);
|
||||
m_model->eval(arg1, v1);
|
||||
m_model->eval(arg2, v2);
|
||||
if (v1 == v2) {
|
||||
set_true(e);
|
||||
return;
|
||||
}
|
||||
sort* s = m.get_sort(arg1);
|
||||
sort* r = get_array_range(s);
|
||||
// give up evaluating finite domain/range arrays
|
||||
if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) {
|
||||
TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
vector<expr_ref_vector> store;
|
||||
expr_ref else1(m), else2(m);
|
||||
if (!extract_array_func_interp(v1, store, else1) ||
|
||||
!extract_array_func_interp(v2, store, else2)) {
|
||||
TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (else1 != else2) {
|
||||
if (m.is_value(else1) && m.is_value(else2)) {
|
||||
TRACE("old_spacer", tout
|
||||
<< "defaults are different: " << mk_pp(e, m) << " "
|
||||
<< mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";);
|
||||
set_false(e);
|
||||
} else if (m_array.is_array(else1)) {
|
||||
eval_array_eq(e, else1, else2);
|
||||
} else {
|
||||
TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref s1(m), s2(m), w1(m), w2(m);
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
args1.push_back(v1);
|
||||
args2.push_back(v2);
|
||||
for (unsigned i = 0; i < store.size(); ++i) {
|
||||
args1.resize(1);
|
||||
args2.resize(1);
|
||||
args1.append(store[i].size() - 1, store[i].c_ptr());
|
||||
args2.append(store[i].size() - 1, store[i].c_ptr());
|
||||
s1 = m_array.mk_select(args1.size(), args1.c_ptr());
|
||||
s2 = m_array.mk_select(args2.size(), args2.c_ptr());
|
||||
m_model->eval(s1, w1);
|
||||
m_model->eval(s2, w2);
|
||||
if (w1 == w2) {
|
||||
continue;
|
||||
}
|
||||
if (m.is_value(w1) && m.is_value(w2)) {
|
||||
TRACE("old_spacer", tout << "Equality evaluation: " << mk_pp(e, m) << "\n";
|
||||
tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n";
|
||||
tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";);
|
||||
set_false(e);
|
||||
} else if (m_array.is_array(w1)) {
|
||||
eval_array_eq(e, w1, w2);
|
||||
if (is_true(e)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
TRACE("old_spacer", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
set_true(e);
|
||||
}
|
||||
|
||||
void model_evaluator::eval_eq(app* e, expr* arg1, expr* arg2)
|
||||
{
|
||||
if (arg1 == arg2) {
|
||||
set_true(e);
|
||||
} else if (m_array.is_array(arg1)) {
|
||||
eval_array_eq(e, arg1, arg2);
|
||||
} else if (is_x(arg1) || is_x(arg2)) {
|
||||
set_x(e);
|
||||
} else if (m.is_bool(arg1)) {
|
||||
bool val = is_true(arg1) == is_true(arg2);
|
||||
SASSERT(val == (is_false(arg1) == is_false(arg2)));
|
||||
if (val) {
|
||||
set_true(e);
|
||||
} else {
|
||||
set_false(e);
|
||||
}
|
||||
} else if (m_arith.is_int_real(arg1)) {
|
||||
set_bool(e, get_number(arg1) == get_number(arg2));
|
||||
} else {
|
||||
expr* e1 = get_value(arg1);
|
||||
expr* e2 = get_value(arg2);
|
||||
if (m.is_value(e1) && m.is_value(e2)) {
|
||||
set_bool(e, e1 == e2);
|
||||
} else if (e1 == e2) {
|
||||
set_bool(e, true);
|
||||
} else {
|
||||
TRACE("old_spacer", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::eval_basic(app* e)
|
||||
{
|
||||
expr* arg1, *arg2;
|
||||
expr *argCond, *argThen, *argElse, *arg;
|
||||
bool has_x = false;
|
||||
unsigned arity = e->get_num_args();
|
||||
switch (e->get_decl_kind()) {
|
||||
case OP_AND:
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
expr * arg = e->get_arg(j);
|
||||
if (is_false(arg)) {
|
||||
set_false(e);
|
||||
return;
|
||||
} else if (is_x(arg)) {
|
||||
has_x = true;
|
||||
} else {
|
||||
SASSERT(is_true(arg));
|
||||
}
|
||||
}
|
||||
if (has_x) {
|
||||
set_x(e);
|
||||
} else {
|
||||
set_true(e);
|
||||
}
|
||||
break;
|
||||
case OP_OR:
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
expr * arg = e->get_arg(j);
|
||||
if (is_true(arg)) {
|
||||
set_true(e);
|
||||
return;
|
||||
} else if (is_x(arg)) {
|
||||
has_x = true;
|
||||
} else {
|
||||
SASSERT(is_false(arg));
|
||||
}
|
||||
}
|
||||
if (has_x) {
|
||||
set_x(e);
|
||||
} else {
|
||||
set_false(e);
|
||||
}
|
||||
break;
|
||||
case OP_NOT:
|
||||
VERIFY(m.is_not(e, arg));
|
||||
if (is_true(arg)) {
|
||||
set_false(e);
|
||||
} else if (is_false(arg)) {
|
||||
set_true(e);
|
||||
} else {
|
||||
SASSERT(is_x(arg));
|
||||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_IMPLIES:
|
||||
VERIFY(m.is_implies(e, arg1, arg2));
|
||||
if (is_false(arg1) || is_true(arg2)) {
|
||||
set_true(e);
|
||||
} else if (arg1 == arg2) {
|
||||
set_true(e);
|
||||
} else if (is_true(arg1) && is_false(arg2)) {
|
||||
set_false(e);
|
||||
} else {
|
||||
SASSERT(is_x(arg1) || is_x(arg2));
|
||||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_IFF:
|
||||
VERIFY(m.is_iff(e, arg1, arg2));
|
||||
eval_eq(e, arg1, arg2);
|
||||
break;
|
||||
case OP_XOR:
|
||||
VERIFY(m.is_xor(e, arg1, arg2));
|
||||
eval_eq(e, arg1, arg2);
|
||||
if (is_false(e)) { set_true(e); }
|
||||
else if (is_true(e)) { set_false(e); }
|
||||
break;
|
||||
case OP_ITE:
|
||||
VERIFY(m.is_ite(e, argCond, argThen, argElse));
|
||||
if (is_true(argCond)) {
|
||||
inherit_value(e, argThen);
|
||||
} else if (is_false(argCond)) {
|
||||
inherit_value(e, argElse);
|
||||
} else if (argThen == argElse) {
|
||||
inherit_value(e, argThen);
|
||||
} else if (m.is_bool(e)) {
|
||||
SASSERT(is_x(argCond));
|
||||
if (is_x(argThen) || is_x(argElse)) {
|
||||
set_x(e);
|
||||
} else if (is_true(argThen) == is_true(argElse)) {
|
||||
inherit_value(e, argThen);
|
||||
} else {
|
||||
set_x(e);
|
||||
}
|
||||
} else {
|
||||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_TRUE:
|
||||
set_true(e);
|
||||
break;
|
||||
case OP_FALSE:
|
||||
set_false(e);
|
||||
break;
|
||||
case OP_EQ:
|
||||
VERIFY(m.is_eq(e, arg1, arg2));
|
||||
eval_eq(e, arg1, arg2);
|
||||
break;
|
||||
case OP_DISTINCT: {
|
||||
vector<rational> values;
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* arg = e->get_arg(i);
|
||||
if (is_x(arg)) {
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
values.push_back(get_number(arg));
|
||||
}
|
||||
std::sort(values.begin(), values.end());
|
||||
for (unsigned i = 0; i + 1 < values.size(); ++i) {
|
||||
if (values[i] == values[i + 1]) {
|
||||
set_false(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_true(e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void model_evaluator::eval_fmls(ptr_vector<expr> const& formulas)
|
||||
{
|
||||
ptr_vector<expr> todo(formulas);
|
||||
|
||||
while (!todo.empty()) {
|
||||
expr * curr_e = todo.back();
|
||||
|
||||
if (!is_app(curr_e)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
app * curr = to_app(curr_e);
|
||||
|
||||
if (!is_unknown(curr)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
unsigned arity = curr->get_num_args();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (is_unknown(curr->get_arg(i))) {
|
||||
todo.push_back(curr->get_arg(i));
|
||||
}
|
||||
}
|
||||
if (todo.back() != curr) {
|
||||
continue;
|
||||
}
|
||||
todo.pop_back();
|
||||
if (curr->get_family_id() == m_arith.get_family_id()) {
|
||||
eval_arith(curr);
|
||||
} else if (curr->get_family_id() == m.get_basic_family_id()) {
|
||||
eval_basic(curr);
|
||||
} else {
|
||||
expr_ref vl(m);
|
||||
m_model->eval(curr, vl);
|
||||
assign_value(curr, vl);
|
||||
}
|
||||
|
||||
IF_VERBOSE(35, verbose_stream() << "assigned " << mk_pp(curr_e, m)
|
||||
<< (is_true(curr_e) ? "true" : is_false(curr_e) ? "false" : "unknown") << "\n";);
|
||||
SASSERT(!is_unknown(curr));
|
||||
}
|
||||
}
|
||||
|
||||
bool model_evaluator::check_model(ptr_vector<expr> const& formulas)
|
||||
{
|
||||
eval_fmls(formulas);
|
||||
bool has_x = false;
|
||||
for (unsigned i = 0; i < formulas.size(); ++i) {
|
||||
expr * form = formulas[i];
|
||||
SASSERT(!is_unknown(form));
|
||||
TRACE("spacer_verbose",
|
||||
tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" << mk_pp(form, m) << "\n";);
|
||||
|
||||
if (is_false(form)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "formula false in model: " << mk_pp(form, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (is_x(form)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";);
|
||||
TRACE("old_spacer", model_smt2_pp(tout, m, *m_model, 0););
|
||||
has_x = true;
|
||||
}
|
||||
}
|
||||
return !has_x;
|
||||
}
|
||||
|
||||
expr_ref model_evaluator::eval_heavy(const model_ref& model, expr* fml)
|
||||
{
|
||||
expr_ref result(model->get_manager());
|
||||
|
||||
setup_model(model);
|
||||
ptr_vector<expr> fmls;
|
||||
fmls.push_back(fml);
|
||||
eval_fmls(fmls);
|
||||
if (is_false(fml)) {
|
||||
result = m.mk_false();
|
||||
} else if (is_true(fml) || is_x(fml)) {
|
||||
result = m.mk_true();
|
||||
} else if (m_arith.is_int_real(fml)) {
|
||||
result = m_arith.mk_numeral(get_number(fml), m_arith.is_int(fml));
|
||||
} else {
|
||||
result = get_value(fml);
|
||||
}
|
||||
reset();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref model_evaluator::eval(const model_ref& model, func_decl* d)
|
||||
{
|
||||
SASSERT(d->get_arity() == 0);
|
||||
expr_ref result(m);
|
||||
if (m_array.is_array(d->get_range())) {
|
||||
expr_ref e(m);
|
||||
e = m.mk_const(d);
|
||||
result = eval(model, e);
|
||||
} else {
|
||||
result = model->get_const_interp(d);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref model_evaluator::eval(const model_ref& model, expr* e)
|
||||
{
|
||||
expr_ref result(m);
|
||||
m_model = model.get();
|
||||
VERIFY(m_model->eval(e, result, true));
|
||||
if (m_array.is_array(e)) {
|
||||
vector<expr_ref_vector> stores;
|
||||
expr_ref_vector args(m);
|
||||
expr_ref else_case(m);
|
||||
if (extract_array_func_interp(result, stores, else_case)) {
|
||||
result = m_array.mk_const_array(m.get_sort(e), else_case);
|
||||
while (!stores.empty() && stores.back().back() == else_case) {
|
||||
stores.pop_back();
|
||||
}
|
||||
for (unsigned i = stores.size(); i > 0;) {
|
||||
--i;
|
||||
args.resize(1);
|
||||
args[0] = result;
|
||||
args.append(stores[i]);
|
||||
result = m_array.mk_store(args.size(), args.c_ptr());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
116
src/muz/spacer/spacer_legacy_mev.h
Normal file
116
src/muz/spacer/spacer_legacy_mev.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Deprecated implementation of model evaluator. To be removed.
|
||||
*/
|
||||
#ifndef OLD_MEV_H
|
||||
#define OLD_MEV_H
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "util/trace.h"
|
||||
#include "util/vector.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
namespace old {
|
||||
class model_evaluator {
|
||||
ast_manager& m;
|
||||
arith_util m_arith;
|
||||
array_util m_array;
|
||||
obj_map<expr, rational> m_numbers;
|
||||
expr_ref_vector m_refs;
|
||||
obj_map<expr, expr*> m_values;
|
||||
model_ref m_model;
|
||||
|
||||
//00 -- non-visited
|
||||
//01 -- X
|
||||
//10 -- false
|
||||
//11 -- true
|
||||
expr_mark m1;
|
||||
expr_mark m2;
|
||||
|
||||
/// used by collect()
|
||||
expr_mark m_visited;
|
||||
|
||||
|
||||
|
||||
void reset();
|
||||
|
||||
/// caches the values of all constants in the given model
|
||||
void setup_model(const model_ref& model);
|
||||
/// caches the value of an expression
|
||||
void assign_value(expr* e, expr* v);
|
||||
|
||||
/// extracts an implicant of the conjunction of formulas
|
||||
void collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect);
|
||||
|
||||
/// one-round of extracting an implicant of e. The implicant
|
||||
/// literals are stored in tocollect. The worklist is stored in todo
|
||||
void process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect);
|
||||
void eval_arith(app* e);
|
||||
void eval_basic(app* e);
|
||||
void eval_eq(app* e, expr* arg1, expr* arg2);
|
||||
void eval_array_eq(app* e, expr* arg1, expr* arg2);
|
||||
void inherit_value(expr* e, expr* v);
|
||||
|
||||
bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); }
|
||||
void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); }
|
||||
bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); }
|
||||
bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); }
|
||||
bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); }
|
||||
void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); }
|
||||
void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
|
||||
void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
|
||||
void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); }
|
||||
void set_bool(expr* x, bool v) { if(v) { set_true(x); } else { set_false(x); } }
|
||||
rational const& get_number(expr* x) const { return m_numbers.find(x); }
|
||||
void set_number(expr* x, rational const& v)
|
||||
{
|
||||
set_v(x);
|
||||
m_numbers.insert(x, v);
|
||||
TRACE("spacer_verbose", tout << mk_pp(x, m) << " " << v << "\n";);
|
||||
}
|
||||
expr* get_value(expr* x) { return m_values.find(x); }
|
||||
void set_value(expr* x, expr* v)
|
||||
{ set_v(x); m_refs.push_back(v); m_values.insert(x, v); }
|
||||
|
||||
|
||||
/// evaluates all sub-formulas and terms of the input in the current model.
|
||||
/// Caches the result
|
||||
void eval_fmls(ptr_vector<expr> const & formulas);
|
||||
|
||||
/// calls eval_fmls(). Then checks whether all formulas are
|
||||
/// TRUE. Returns false if at lest one formula is unknown (X)
|
||||
bool check_model(ptr_vector<expr> const & formulas);
|
||||
|
||||
bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores,
|
||||
expr_ref& else_case);
|
||||
|
||||
void eval_exprs(expr_ref_vector& es);
|
||||
|
||||
public:
|
||||
model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {}
|
||||
|
||||
|
||||
/**
|
||||
\brief extract literals from formulas that satisfy formulas.
|
||||
|
||||
\pre model satisfies formulas
|
||||
*/
|
||||
void minimize_literals(ptr_vector<expr> const & formulas, const model_ref& mdl,
|
||||
expr_ref_vector& result);
|
||||
|
||||
expr_ref eval_heavy(const model_ref& mdl, expr* fml);
|
||||
|
||||
expr_ref eval(const model_ref& mdl, expr* e);
|
||||
expr_ref eval(const model_ref& mdl, func_decl* d);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* OLD_MEV_H */
|
||||
387
src/muz/spacer/spacer_manager.cpp
Normal file
387
src/muz/spacer/spacer_manager.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for SPACER, taking care of creating of AST
|
||||
objects and conversions between them.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/has_free_vars.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "model/model2expr.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "tactic/model_converter.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class collect_decls_proc {
|
||||
func_decl_set& m_bound_decls;
|
||||
func_decl_set& m_aux_decls;
|
||||
public:
|
||||
collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls):
|
||||
m_bound_decls(bound_decls),
|
||||
m_aux_decls(aux_decls)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(app* a)
|
||||
{
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
func_decl* f = a->get_decl();
|
||||
if (!m_bound_decls.contains(f)) {
|
||||
m_aux_decls.insert(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator()(var* v) {}
|
||||
void operator()(quantifier* q) {}
|
||||
};
|
||||
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
|
||||
expr_ref inductive_property::fixup_clause(expr* fml) const
|
||||
{
|
||||
expr_ref_vector disjs(m);
|
||||
flatten_or(fml, disjs);
|
||||
expr_ref result(m);
|
||||
bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref inductive_property::fixup_clauses(expr* fml) const
|
||||
{
|
||||
expr_ref_vector conjs(m);
|
||||
expr_ref result(m);
|
||||
flatten_and(fml, conjs);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
conjs[i] = fixup_clause(conjs[i].get());
|
||||
}
|
||||
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string inductive_property::to_string() const
|
||||
{
|
||||
std::stringstream stm;
|
||||
model_ref md;
|
||||
expr_ref result(m);
|
||||
to_model(md);
|
||||
model_smt2_pp(stm, m, *md.get(), 0);
|
||||
return stm.str();
|
||||
}
|
||||
|
||||
void inductive_property::to_model(model_ref& md) const
|
||||
{
|
||||
md = alloc(model, m);
|
||||
vector<relation_info> const& rs = m_relation_info;
|
||||
expr_ref_vector conjs(m);
|
||||
for (unsigned i = 0; i < rs.size(); ++i) {
|
||||
relation_info ri(rs[i]);
|
||||
func_decl * pred = ri.m_pred;
|
||||
expr_ref prop = fixup_clauses(ri.m_body);
|
||||
func_decl_ref_vector const& sig = ri.m_vars;
|
||||
expr_ref q(m);
|
||||
expr_ref_vector sig_vars(m);
|
||||
for (unsigned j = 0; j < sig.size(); ++j) {
|
||||
sig_vars.push_back(m.mk_const(sig[sig.size() - j - 1]));
|
||||
}
|
||||
expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q);
|
||||
if (sig.empty()) {
|
||||
md->register_decl(pred, q);
|
||||
} else {
|
||||
func_interp* fi = alloc(func_interp, m, sig.size());
|
||||
fi->set_else(q);
|
||||
md->register_decl(pred, fi);
|
||||
}
|
||||
}
|
||||
TRACE("spacer", model_smt2_pp(tout, m, *md, 0););
|
||||
apply(const_cast<model_converter_ref&>(m_mc), md, 0);
|
||||
}
|
||||
|
||||
expr_ref inductive_property::to_expr() const
|
||||
{
|
||||
model_ref md;
|
||||
expr_ref result(m);
|
||||
to_model(md);
|
||||
model2expr(md, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void inductive_property::display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const
|
||||
{
|
||||
func_decl_set bound_decls, aux_decls;
|
||||
collect_decls_proc collect_decls(bound_decls, aux_decls);
|
||||
|
||||
for (unsigned i = 0; i < m_relation_info.size(); ++i) {
|
||||
bound_decls.insert(m_relation_info[i].m_pred);
|
||||
func_decl_ref_vector const& sig = m_relation_info[i].m_vars;
|
||||
for (unsigned j = 0; j < sig.size(); ++j) {
|
||||
bound_decls.insert(sig[j]);
|
||||
}
|
||||
for_each_expr(collect_decls, m_relation_info[i].m_body);
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
bound_decls.insert(rules[i]->get_decl());
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
unsigned u_sz = rules[i]->get_uninterpreted_tail_size();
|
||||
unsigned t_sz = rules[i]->get_tail_size();
|
||||
for (unsigned j = u_sz; j < t_sz; ++j) {
|
||||
for_each_expr(collect_decls, rules[i]->get_tail(j));
|
||||
}
|
||||
}
|
||||
smt2_pp_environment_dbg env(m);
|
||||
func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end();
|
||||
for (; it != end; ++it) {
|
||||
func_decl* f = *it;
|
||||
ast_smt2_pp(out, f, env);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
out << to_string() << "\n";
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
out << "(push)\n";
|
||||
out << "(assert (not\n";
|
||||
rm.display_smt2(*rules[i], out);
|
||||
out << "))\n";
|
||||
out << "(check-sat)\n";
|
||||
out << "(pop)\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> manager::get_state_suffixes()
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
res.push_back("_n");
|
||||
return res;
|
||||
}
|
||||
|
||||
manager::manager(unsigned max_num_contexts, ast_manager& manager) :
|
||||
m(manager),
|
||||
m_brwr(m),
|
||||
m_mux(m, get_state_suffixes()),
|
||||
m_background(m.mk_true(), m),
|
||||
m_contexts(m, max_num_contexts),
|
||||
m_contexts2(m, max_num_contexts),
|
||||
m_contexts3(m, max_num_contexts),
|
||||
m_next_unique_num(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void manager::add_new_state(func_decl * s)
|
||||
{
|
||||
SASSERT(s->get_arity() == 0); //we currently don't support non-constant states
|
||||
decl_vector vect;
|
||||
|
||||
SASSERT(o_index(0) == 1); //we assume this in the number of retrieved symbols
|
||||
m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect);
|
||||
m_o0_preds.push_back(vect[o_index(0)]);
|
||||
}
|
||||
|
||||
func_decl * manager::get_o_pred(func_decl* s, unsigned idx)
|
||||
{
|
||||
func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx));
|
||||
if (res) { return res; }
|
||||
add_new_state(s);
|
||||
res = m_mux.try_get_by_prefix(s, o_index(idx));
|
||||
SASSERT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
func_decl * manager::get_n_pred(func_decl* s)
|
||||
{
|
||||
func_decl * res = m_mux.try_get_by_prefix(s, n_index());
|
||||
if (res) { return res; }
|
||||
add_new_state(s);
|
||||
res = m_mux.try_get_by_prefix(s, n_index());
|
||||
SASSERT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res)
|
||||
{
|
||||
m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res);
|
||||
}
|
||||
|
||||
void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res)
|
||||
{
|
||||
m_brwr.mk_and(core.size(), core.c_ptr(), res);
|
||||
}
|
||||
|
||||
void manager::mk_cube_into_lemma(expr * cube, expr_ref & res)
|
||||
{
|
||||
m_brwr.mk_not(cube, res);
|
||||
}
|
||||
|
||||
void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res)
|
||||
{
|
||||
m_brwr.mk_not(lemma, res);
|
||||
}
|
||||
|
||||
expr_ref manager::mk_and(unsigned sz, expr* const* exprs)
|
||||
{
|
||||
expr_ref result(m);
|
||||
m_brwr.mk_and(sz, exprs, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref manager::mk_or(unsigned sz, expr* const* exprs)
|
||||
{
|
||||
expr_ref result(m);
|
||||
m_brwr.mk_or(sz, exprs, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref manager::mk_not_and(expr_ref_vector const& conjs)
|
||||
{
|
||||
expr_ref result(m), e(m);
|
||||
expr_ref_vector es(conjs);
|
||||
flatten_and(es);
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
m_brwr.mk_not(es[i].get(), e);
|
||||
es[i] = e;
|
||||
}
|
||||
m_brwr.mk_or(es.size(), es.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void manager::get_or(expr* e, expr_ref_vector& result)
|
||||
{
|
||||
result.push_back(e);
|
||||
for (unsigned i = 0; i < result.size();) {
|
||||
e = result[i].get();
|
||||
if (m.is_or(e)) {
|
||||
result.append(to_app(e)->get_num_args(), to_app(e)->get_args());
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value)
|
||||
{
|
||||
if (!is_app(atom0)) {
|
||||
return false;
|
||||
}
|
||||
app * atom = to_app(atom0);
|
||||
expr * arg1;
|
||||
expr * arg2;
|
||||
app * candidate_state;
|
||||
app_ref candidate_value(m);
|
||||
if (m.is_not(atom, arg1)) {
|
||||
if (!is_app(arg1)) {
|
||||
return false;
|
||||
}
|
||||
candidate_state = to_app(arg1);
|
||||
candidate_value = m.mk_false();
|
||||
} else if (m.is_eq(atom, arg1, arg2)) {
|
||||
if (!is_app(arg1) || !is_app(arg2)) {
|
||||
return false;
|
||||
}
|
||||
if (!m_mux.is_muxed(to_app(arg1)->get_decl())) {
|
||||
std::swap(arg1, arg2);
|
||||
}
|
||||
candidate_state = to_app(arg1);
|
||||
candidate_value = to_app(arg2);
|
||||
} else {
|
||||
candidate_state = atom;
|
||||
candidate_value = m.mk_true();
|
||||
}
|
||||
if (!m_mux.is_muxed(candidate_state->get_decl())) {
|
||||
return false;
|
||||
}
|
||||
state = candidate_state;
|
||||
value = candidate_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state)
|
||||
{
|
||||
app_ref dummy_value_holder(m);
|
||||
app * s;
|
||||
if (try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) {
|
||||
state = s->get_decl();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new skolem constant
|
||||
*/
|
||||
app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) {
|
||||
std::stringstream name;
|
||||
name << "sk!" << idx;
|
||||
return m.mk_const(symbol(name.str().c_str()), s);
|
||||
}
|
||||
|
||||
namespace find_zk_const_ns {
|
||||
struct proc {
|
||||
app_ref_vector &m_out;
|
||||
proc (app_ref_vector &out) : m_out(out) {}
|
||||
void operator() (var const * n) const {}
|
||||
void operator() (app *n) const {
|
||||
if (is_uninterp_const(n) &&
|
||||
n->get_decl()->get_name().str().compare (0, 3, "sk!") == 0) {
|
||||
m_out.push_back (n);
|
||||
}
|
||||
}
|
||||
void operator() (quantifier const *n) const {}
|
||||
};
|
||||
}
|
||||
|
||||
void find_zk_const(expr *e, app_ref_vector &res) {
|
||||
find_zk_const_ns::proc p(res);
|
||||
for_each_expr (p, e);
|
||||
}
|
||||
|
||||
namespace has_zk_const_ns {
|
||||
struct found {};
|
||||
struct proc {
|
||||
void operator() (var const *n) const {}
|
||||
void operator() (app const *n) const {
|
||||
if (is_uninterp_const(n) &&
|
||||
n->get_decl()->get_name().str().compare(0, 3, "sk!") == 0) {
|
||||
throw found();
|
||||
}
|
||||
}
|
||||
void operator() (quantifier const *n) const {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
bool has_zk_const(expr *e){
|
||||
has_zk_const_ns::proc p;
|
||||
try {
|
||||
for_each_expr(p, e);
|
||||
}
|
||||
catch (has_zk_const_ns::found) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
346
src/muz/spacer/spacer_manager.h
Normal file
346
src/muz/spacer/spacer_manager.h
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for SPACER, taking care of creating of AST
|
||||
objects and conversions between them.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_MANAGER_H_
|
||||
#define _SPACER_MANAGER_H_
|
||||
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "util/map.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_sym_mux.h"
|
||||
#include "muz/spacer/spacer_farkas_learner.h"
|
||||
#include "muz/spacer/spacer_smt_context_manager.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
|
||||
namespace smt {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace spacer {
|
||||
|
||||
struct relation_info {
|
||||
func_decl_ref m_pred;
|
||||
func_decl_ref_vector m_vars;
|
||||
expr_ref m_body;
|
||||
relation_info(ast_manager& m, func_decl* pred, ptr_vector<func_decl> const& vars, expr* b):
|
||||
m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {}
|
||||
relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {}
|
||||
};
|
||||
|
||||
class unknown_exception {};
|
||||
|
||||
class inductive_property {
|
||||
ast_manager& m;
|
||||
model_converter_ref m_mc;
|
||||
vector<relation_info> m_relation_info;
|
||||
expr_ref fixup_clauses(expr* property) const;
|
||||
expr_ref fixup_clause(expr* clause) const;
|
||||
public:
|
||||
inductive_property(ast_manager& m, model_converter_ref& mc, vector<relation_info> const& relations):
|
||||
m(m),
|
||||
m_mc(mc),
|
||||
m_relation_info(relations) {}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
expr_ref to_expr() const;
|
||||
|
||||
void to_model(model_ref& md) const;
|
||||
|
||||
void display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const;
|
||||
};
|
||||
|
||||
class manager {
|
||||
ast_manager& m;
|
||||
|
||||
mutable bool_rewriter m_brwr;
|
||||
|
||||
sym_mux m_mux;
|
||||
expr_ref m_background;
|
||||
decl_vector m_o0_preds;
|
||||
spacer::smt_context_manager m_contexts;
|
||||
spacer::smt_context_manager m_contexts2;
|
||||
spacer::smt_context_manager m_contexts3;
|
||||
|
||||
/** whenever we need an unique number, we get this one and increase */
|
||||
unsigned m_next_unique_num;
|
||||
|
||||
|
||||
static std::vector<std::string> get_state_suffixes();
|
||||
|
||||
unsigned n_index() const { return 0; }
|
||||
unsigned o_index(unsigned i) const { return i + 1; }
|
||||
|
||||
void add_new_state(func_decl * s);
|
||||
|
||||
public:
|
||||
manager(unsigned max_num_contexts, ast_manager & manager);
|
||||
|
||||
ast_manager& get_manager() const { return m; }
|
||||
bool_rewriter& get_brwr() const { return m_brwr; }
|
||||
|
||||
expr_ref mk_and(unsigned sz, expr* const* exprs);
|
||||
expr_ref mk_and(expr_ref_vector const& exprs)
|
||||
{
|
||||
return mk_and(exprs.size(), exprs.c_ptr());
|
||||
}
|
||||
expr_ref mk_and(expr* a, expr* b)
|
||||
{
|
||||
expr* args[2] = { a, b };
|
||||
return mk_and(2, args);
|
||||
}
|
||||
expr_ref mk_or(unsigned sz, expr* const* exprs);
|
||||
expr_ref mk_or(expr_ref_vector const& exprs)
|
||||
{
|
||||
return mk_or(exprs.size(), exprs.c_ptr());
|
||||
}
|
||||
|
||||
expr_ref mk_not_and(expr_ref_vector const& exprs);
|
||||
|
||||
void get_or(expr* e, expr_ref_vector& result);
|
||||
|
||||
//"o" predicates stand for the old states and "n" for the new states
|
||||
func_decl * get_o_pred(func_decl * s, unsigned idx);
|
||||
func_decl * get_n_pred(func_decl * s);
|
||||
|
||||
/**
|
||||
Marks symbol as non-model which means it will not appear in models collected by
|
||||
get_state_cube_from_model function.
|
||||
This is to take care of auxiliary symbols introduced by the disjunction relations
|
||||
to relativize lemmas coming from disjuncts.
|
||||
*/
|
||||
void mark_as_non_model(func_decl * p)
|
||||
{
|
||||
m_mux.mark_as_non_model(p);
|
||||
}
|
||||
|
||||
|
||||
func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); }
|
||||
func_decl * const * end_o0_preds() const { return m_o0_preds.end(); }
|
||||
|
||||
bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); }
|
||||
func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); }
|
||||
|
||||
bool is_o(func_decl * p, unsigned idx) const
|
||||
{
|
||||
return m_mux.has_index(p, o_index(idx));
|
||||
}
|
||||
void get_o_index(func_decl* p, unsigned& idx) const
|
||||
{
|
||||
m_mux.try_get_index(p, idx);
|
||||
SASSERT(idx != n_index());
|
||||
idx--; // m_mux has indices starting at 1
|
||||
}
|
||||
bool is_o(expr* e, unsigned idx) const
|
||||
{
|
||||
return is_app(e) && is_o(to_app(e)->get_decl(), idx);
|
||||
}
|
||||
bool is_o(func_decl * p) const
|
||||
{
|
||||
unsigned idx;
|
||||
return m_mux.try_get_index(p, idx) && idx != n_index();
|
||||
}
|
||||
bool is_o(expr* e) const
|
||||
{
|
||||
return is_app(e) && is_o(to_app(e)->get_decl());
|
||||
}
|
||||
bool is_n(func_decl * p) const
|
||||
{
|
||||
return m_mux.has_index(p, n_index());
|
||||
}
|
||||
bool is_n(expr* e) const
|
||||
{
|
||||
return is_app(e) && is_n(to_app(e)->get_decl());
|
||||
}
|
||||
|
||||
/** true if p should not appead in models propagates into child relations */
|
||||
bool is_non_model_sym(func_decl * p) const
|
||||
{ return m_mux.is_non_model_sym(p); }
|
||||
|
||||
|
||||
/** true if f doesn't contain any n predicates */
|
||||
bool is_o_formula(expr * f) const
|
||||
{
|
||||
return !m_mux.contains(f, n_index());
|
||||
}
|
||||
|
||||
/** true if f contains only o state preds of index o_idx */
|
||||
bool is_o_formula(expr * f, unsigned o_idx) const
|
||||
{
|
||||
return m_mux.is_homogenous_formula(f, o_index(o_idx));
|
||||
}
|
||||
/** true if f doesn't contain any o predicates */
|
||||
bool is_n_formula(expr * f) const
|
||||
{
|
||||
return m_mux.is_homogenous_formula(f, n_index());
|
||||
}
|
||||
|
||||
func_decl * o2n(func_decl * p, unsigned o_idx) const
|
||||
{
|
||||
return m_mux.conv(p, o_index(o_idx), n_index());
|
||||
}
|
||||
func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const
|
||||
{
|
||||
return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));
|
||||
}
|
||||
func_decl * n2o(func_decl * p, unsigned o_idx) const
|
||||
{
|
||||
return m_mux.conv(p, n_index(), o_index(o_idx));
|
||||
}
|
||||
|
||||
void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const
|
||||
{ m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); }
|
||||
|
||||
void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const
|
||||
{ m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); }
|
||||
|
||||
void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const
|
||||
{ m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); }
|
||||
|
||||
void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const
|
||||
{ m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); }
|
||||
|
||||
/**
|
||||
Return true if all state symbols which e contains are of one kind (either "n" or one of "o").
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e) const
|
||||
{
|
||||
return m_mux.is_homogenous_formula(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Collect indices used in expression.
|
||||
*/
|
||||
void collect_indices(expr* e, unsigned_vector& indices) const
|
||||
{
|
||||
m_mux.collect_indices(e, indices);
|
||||
}
|
||||
|
||||
/**
|
||||
Collect used variables of each index.
|
||||
*/
|
||||
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const
|
||||
{
|
||||
m_mux.collect_variables(e, vars);
|
||||
}
|
||||
|
||||
/**
|
||||
Return true iff both s1 and s2 are either "n" or "o" of the same index.
|
||||
If one (or both) of them are not state symbol, return false.
|
||||
*/
|
||||
bool have_different_state_kinds(func_decl * s1, func_decl * s2) const
|
||||
{
|
||||
unsigned i1, i2;
|
||||
return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1 != i2;
|
||||
}
|
||||
|
||||
/**
|
||||
Increase indexes of state symbols in formula by dist.
|
||||
The 'N' index becomes 'O' index with number dist-1.
|
||||
*/
|
||||
void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const
|
||||
{
|
||||
SASSERT(n_index() == 0);
|
||||
SASSERT(o_index(0) == 1);
|
||||
m_mux.shift_formula(src, dist, tgt);
|
||||
}
|
||||
|
||||
void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res);
|
||||
void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res);
|
||||
void mk_cube_into_lemma(expr * cube, expr_ref & res);
|
||||
void mk_lemma_into_cube(expr * lemma, expr_ref & res);
|
||||
|
||||
/**
|
||||
Remove from vec all atoms that do not have an "o" state.
|
||||
The order of elements in vec may change.
|
||||
An assumption is that atoms having "o" state of given index
|
||||
do not have "o" states of other indexes or "n" states.
|
||||
*/
|
||||
void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const
|
||||
{ m_mux.filter_idx(vec, o_index(o_idx)); }
|
||||
void filter_n_atoms(expr_ref_vector& vec) const
|
||||
{ m_mux.filter_idx(vec, n_index()); }
|
||||
|
||||
/**
|
||||
Partition literals into o_lits and others.
|
||||
*/
|
||||
void partition_o_atoms(expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other,
|
||||
unsigned o_idx) const
|
||||
{
|
||||
m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx));
|
||||
}
|
||||
|
||||
void filter_out_non_model_atoms(expr_ref_vector& vec) const
|
||||
{ m_mux.filter_non_model_lits(vec); }
|
||||
|
||||
bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value);
|
||||
bool try_get_state_decl_from_atom(expr * atom, func_decl *& state);
|
||||
|
||||
|
||||
std::string pp_model(const model_core & mdl) const
|
||||
{ return m_mux.pp_model(mdl); }
|
||||
|
||||
|
||||
void set_background(expr* b) { m_background = b; }
|
||||
|
||||
expr* get_background() const { return m_background; }
|
||||
|
||||
unsigned get_unique_num() { return m_next_unique_num++; }
|
||||
|
||||
solver* mk_fresh() {return m_contexts.mk_fresh();}
|
||||
smt_params& fparams() { return m_contexts.fparams(); }
|
||||
solver* mk_fresh2() {return m_contexts2.mk_fresh();}
|
||||
smt_params &fparams2() { return m_contexts2.fparams(); }
|
||||
solver* mk_fresh3() {return m_contexts3.mk_fresh();}
|
||||
smt_params &fparams3() {return m_contexts3.fparams();}
|
||||
|
||||
|
||||
|
||||
void collect_statistics(statistics& st) const
|
||||
{
|
||||
m_contexts.collect_statistics(st);
|
||||
m_contexts2.collect_statistics(st);
|
||||
m_contexts3.collect_statistics(st);
|
||||
}
|
||||
|
||||
void reset_statistics()
|
||||
{
|
||||
m_contexts.reset_statistics();
|
||||
m_contexts2.reset_statistics();
|
||||
m_contexts3.reset_statistics();
|
||||
}
|
||||
};
|
||||
|
||||
app* mk_zk_const (ast_manager &m, unsigned idx, sort *s);
|
||||
void find_zk_const(expr* e, app_ref_vector &out);
|
||||
bool has_zk_const(expr* e);
|
||||
}
|
||||
|
||||
#endif
|
||||
56
src/muz/spacer/spacer_marshal.cpp
Normal file
56
src/muz/spacer/spacer_marshal.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
Module Name:
|
||||
|
||||
spacer_marshal.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
marshaling and unmarshaling of expressions
|
||||
|
||||
--*/
|
||||
#include "muz/spacer/spacer_marshal.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "cmd_context/cmd_context.h"
|
||||
#include "parsers/smt2/smt2parser.h"
|
||||
#include "util/vector.h"
|
||||
#include "ast/ast_smt_pp.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
||||
namespace spacer {
|
||||
std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m)
|
||||
{
|
||||
ast_smt_pp pp(m);
|
||||
pp.display_smt2(os, e);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string marshal(expr_ref e, ast_manager &m)
|
||||
{
|
||||
std::stringstream ss;
|
||||
marshal(ss, e, m);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
expr_ref unmarshal(std::istream &is, ast_manager &m)
|
||||
{
|
||||
cmd_context ctx(false, &m);
|
||||
ctx.set_ignore_check(true);
|
||||
if (!parse_smt2_commands(ctx, is)) { return expr_ref(0, m); }
|
||||
|
||||
ptr_vector<expr>::const_iterator it = ctx.begin_assertions();
|
||||
ptr_vector<expr>::const_iterator end = ctx.end_assertions();
|
||||
if (it == end) { return expr_ref(m.mk_true(), m); }
|
||||
unsigned size = static_cast<unsigned>(end - it);
|
||||
return expr_ref(m.mk_and(size, it), m);
|
||||
}
|
||||
|
||||
expr_ref unmarshal(std::string s, ast_manager &m)
|
||||
{
|
||||
std::istringstream is(s);
|
||||
return unmarshal(is, m);
|
||||
}
|
||||
}
|
||||
30
src/muz/spacer/spacer_marshal.h
Normal file
30
src/muz/spacer/spacer_marshal.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
Module Name:
|
||||
|
||||
spacer_marshal.h
|
||||
|
||||
Abstract:
|
||||
|
||||
marshaling and unmarshaling of expressions
|
||||
|
||||
--*/
|
||||
#ifndef _SPACER_MARSHAL_H_
|
||||
#define _SPACER_MARSHAL_H_
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "ast/ast.h"
|
||||
|
||||
namespace spacer {
|
||||
std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m);
|
||||
std::string marshal(expr_ref e, ast_manager &m);
|
||||
expr_ref unmarshal(std::string s, ast_manager &m);
|
||||
expr_ref unmarshal(std::istream &is, ast_manager &m);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
159
src/muz/spacer/spacer_matrix.cpp
Normal file
159
src/muz/spacer/spacer_matrix.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_matrix.cpp
|
||||
|
||||
Abstract:
|
||||
a matrix
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#include "muz/spacer/spacer_matrix.h"
|
||||
|
||||
namespace spacer
|
||||
{
|
||||
spacer_matrix::spacer_matrix(unsigned m, unsigned n) : m_num_rows(m), m_num_cols(n)
|
||||
{
|
||||
for (unsigned i=0; i < m; ++i)
|
||||
{
|
||||
vector<rational> v;
|
||||
for (unsigned j=0; j < n; ++j)
|
||||
{
|
||||
v.push_back(rational(0));
|
||||
}
|
||||
m_matrix.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned spacer_matrix::num_rows()
|
||||
{
|
||||
return m_num_rows;
|
||||
}
|
||||
|
||||
unsigned spacer_matrix::num_cols()
|
||||
{
|
||||
return m_num_cols;
|
||||
}
|
||||
|
||||
rational spacer_matrix::get(unsigned int i, unsigned int j)
|
||||
{
|
||||
SASSERT(i < m_num_rows);
|
||||
SASSERT(j < m_num_cols);
|
||||
|
||||
return m_matrix[i][j];
|
||||
}
|
||||
|
||||
void spacer_matrix::set(unsigned int i, unsigned int j, rational v)
|
||||
{
|
||||
SASSERT(i < m_num_rows);
|
||||
SASSERT(j < m_num_cols);
|
||||
|
||||
m_matrix[i][j] = v;
|
||||
}
|
||||
|
||||
unsigned spacer_matrix::perform_gaussian_elimination()
|
||||
{
|
||||
unsigned i=0;
|
||||
unsigned j=0;
|
||||
while(i < m_matrix.size() && j < m_matrix[0].size())
|
||||
{
|
||||
// find maximal element in column with row index bigger or equal i
|
||||
rational max = m_matrix[i][j];
|
||||
unsigned max_index = i;
|
||||
|
||||
for (unsigned k=i+1; k < m_matrix.size(); ++k)
|
||||
{
|
||||
if (max < m_matrix[k][j])
|
||||
{
|
||||
max = m_matrix[k][j];
|
||||
max_index = k;
|
||||
}
|
||||
}
|
||||
|
||||
if (max.is_zero()) // skip this column
|
||||
{
|
||||
++j;
|
||||
}
|
||||
else
|
||||
{
|
||||
// reorder rows if necessary
|
||||
vector<rational> tmp = m_matrix[i];
|
||||
m_matrix[i] = m_matrix[max_index];
|
||||
m_matrix[max_index] = m_matrix[i];
|
||||
|
||||
// normalize row
|
||||
rational pivot = m_matrix[i][j];
|
||||
if (!pivot.is_one())
|
||||
{
|
||||
for (unsigned k=0; k < m_matrix[i].size(); ++k)
|
||||
{
|
||||
m_matrix[i][k] = m_matrix[i][k] / pivot;
|
||||
}
|
||||
}
|
||||
|
||||
// subtract row from all other rows
|
||||
for (unsigned k=1; k < m_matrix.size(); ++k)
|
||||
{
|
||||
if (k != i)
|
||||
{
|
||||
rational factor = m_matrix[k][j];
|
||||
for (unsigned l=0; l < m_matrix[k].size(); ++l)
|
||||
{
|
||||
m_matrix[k][l] = m_matrix[k][l] - (factor * m_matrix[i][l]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (get_verbosity_level() >= 1)
|
||||
{
|
||||
SASSERT(m_matrix.size() > 0);
|
||||
}
|
||||
|
||||
return i; //i points to the row after the last row which is non-zero
|
||||
}
|
||||
|
||||
void spacer_matrix::print_matrix()
|
||||
{
|
||||
verbose_stream() << "\nMatrix\n";
|
||||
for (const auto& row : m_matrix)
|
||||
{
|
||||
for (const auto& element : row)
|
||||
{
|
||||
verbose_stream() << element << ", ";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
}
|
||||
verbose_stream() << "\n";
|
||||
}
|
||||
void spacer_matrix::normalize()
|
||||
{
|
||||
rational den = rational::one();
|
||||
for (unsigned i=0; i < m_num_rows; ++i)
|
||||
{
|
||||
for (unsigned j=0; j < m_num_cols; ++j)
|
||||
{
|
||||
den = lcm(den, denominator(m_matrix[i][j]));
|
||||
}
|
||||
}
|
||||
for (unsigned i=0; i < m_num_rows; ++i)
|
||||
{
|
||||
for (unsigned j=0; j < m_num_cols; ++j)
|
||||
{
|
||||
m_matrix[i][j] = den * m_matrix[i][j];
|
||||
SASSERT(m_matrix[i][j].is_int());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/muz/spacer/spacer_matrix.h
Normal file
47
src/muz/spacer/spacer_matrix.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_matrix.h
|
||||
|
||||
Abstract:
|
||||
a matrix
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#ifndef _SPACER_MATRIX_H_
|
||||
#define _SPACER_MATRIX_H_
|
||||
|
||||
#include "util/rational.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class spacer_matrix {
|
||||
public:
|
||||
spacer_matrix(unsigned m, unsigned n); // m rows, n colums
|
||||
|
||||
unsigned num_rows();
|
||||
unsigned num_cols();
|
||||
|
||||
rational get(unsigned i, unsigned j);
|
||||
void set(unsigned i, unsigned j, rational v);
|
||||
|
||||
unsigned perform_gaussian_elimination();
|
||||
|
||||
void print_matrix();
|
||||
void normalize();
|
||||
private:
|
||||
unsigned m_num_rows;
|
||||
unsigned m_num_cols;
|
||||
vector<vector<rational>> m_matrix;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
217
src/muz/spacer/spacer_mev_array.cpp
Normal file
217
src/muz/spacer/spacer_mev_array.cpp
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model_mev_array.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Evaluate array expressions in a given model.
|
||||
|
||||
Author:
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include"model/model.h"
|
||||
#include "model/model_evaluator_params.hpp"
|
||||
#include"ast/rewriter/rewriter_types.h"
|
||||
#include"model/model_evaluator.h"
|
||||
#include"muz/spacer/spacer_mev_array.h"
|
||||
#include"ast/rewriter/bool_rewriter.h"
|
||||
#include"ast/rewriter/arith_rewriter.h"
|
||||
#include"ast/rewriter/bv_rewriter.h"
|
||||
#include"ast/rewriter/datatype_rewriter.h"
|
||||
#include"ast/rewriter/array_rewriter.h"
|
||||
#include"ast/rewriter/rewriter_def.h"
|
||||
#include"util/cooperate.h"
|
||||
#include"ast/ast_pp.h"
|
||||
#include"model/func_interp.h"
|
||||
|
||||
|
||||
|
||||
// model_evaluator_array_util
|
||||
|
||||
|
||||
void model_evaluator_array_util::eval_exprs(model& mdl, expr_ref_vector& es) {
|
||||
for (unsigned j = 0; j < es.size(); ++j) {
|
||||
if (m_array.is_as_array(es.get (j))) {
|
||||
expr_ref r (m);
|
||||
eval(mdl, es.get (j), r);
|
||||
es.set (j, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool model_evaluator_array_util::extract_array_func_interp(model& mdl, expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) {
|
||||
SASSERT(m_array.is_array(a));
|
||||
|
||||
TRACE("model_evaluator", tout << mk_pp(a, m) << "\n";);
|
||||
while (m_array.is_store(a)) {
|
||||
expr_ref_vector store(m);
|
||||
store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
|
||||
eval_exprs(mdl, store);
|
||||
stores.push_back(store);
|
||||
a = to_app(a)->get_arg(0);
|
||||
}
|
||||
|
||||
if (m_array.is_const(a)) {
|
||||
else_case = to_app(a)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (m_array.is_as_array(a)) {
|
||||
func_decl* f = m_array.get_as_array_func_decl(to_app(a));
|
||||
func_interp* g = mdl.get_func_interp(f);
|
||||
unsigned sz = g->num_entries();
|
||||
unsigned arity = f->get_arity();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr_ref_vector store(m);
|
||||
func_entry const* fe = g->get_entry(i);
|
||||
store.append(arity, fe->get_args());
|
||||
store.push_back(fe->get_result());
|
||||
for (unsigned j = 0; j < store.size(); ++j) {
|
||||
if (!is_ground(store[j].get())) {
|
||||
TRACE("model_evaluator", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
eval_exprs(mdl, store);
|
||||
stores.push_back(store);
|
||||
}
|
||||
else_case = g->get_else();
|
||||
if (!else_case) {
|
||||
TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (!is_ground(else_case)) {
|
||||
TRACE("model_evaluator", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (m_array.is_as_array(else_case)) {
|
||||
expr_ref r (m);
|
||||
eval(mdl, else_case, r);
|
||||
else_case = r;
|
||||
}
|
||||
TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
TRACE("model_evaluator", tout << "no translation: " << mk_pp(a, m) << "\n";);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void model_evaluator_array_util::eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res) {
|
||||
TRACE("model_evaluator", tout << "array equality: " << mk_pp(e, m) << "\n";);
|
||||
expr_ref v1(m), v2(m);
|
||||
eval (mdl, arg1, v1);
|
||||
eval (mdl, arg2, v2);
|
||||
if (v1 == v2) {
|
||||
res = m.mk_true ();
|
||||
return;
|
||||
}
|
||||
sort* s = m.get_sort(arg1);
|
||||
sort* r = get_array_range(s);
|
||||
// give up evaluating finite domain/range arrays
|
||||
if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) {
|
||||
TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
res.reset ();
|
||||
return;
|
||||
}
|
||||
vector<expr_ref_vector> store;
|
||||
expr_ref else1(m), else2(m);
|
||||
if (!extract_array_func_interp(mdl, v1, store, else1) ||
|
||||
!extract_array_func_interp(mdl, v2, store, else2)) {
|
||||
TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
res.reset ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (else1 != else2) {
|
||||
if (m.is_value(else1) && m.is_value(else2)) {
|
||||
TRACE("model_evaluator", tout
|
||||
<< "defaults are different: " << mk_pp(e, m) << " "
|
||||
<< mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";);
|
||||
res = m.mk_false ();
|
||||
}
|
||||
else if (m_array.is_array(else1)) {
|
||||
eval_array_eq(mdl, e, else1, else2, res);
|
||||
}
|
||||
else {
|
||||
TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
res.reset ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref s1(m), s2(m), w1(m), w2(m);
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
args1.push_back(v1);
|
||||
args2.push_back(v2);
|
||||
for (unsigned i = 0; i < store.size(); ++i) {
|
||||
args1.resize(1);
|
||||
args2.resize(1);
|
||||
args1.append(store[i].size()-1, store[i].c_ptr());
|
||||
args2.append(store[i].size()-1, store[i].c_ptr());
|
||||
s1 = m_array.mk_select(args1.size(), args1.c_ptr());
|
||||
s2 = m_array.mk_select(args2.size(), args2.c_ptr());
|
||||
eval (mdl, s1, w1);
|
||||
eval (mdl, s2, w2);
|
||||
if (w1 == w2) {
|
||||
continue;
|
||||
}
|
||||
if (m.is_value(w1) && m.is_value(w2)) {
|
||||
TRACE("model_evaluator", tout << "Equality evaluation: " << mk_pp(e, m) << "\n";
|
||||
tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n";
|
||||
tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";);
|
||||
res = m.mk_false ();
|
||||
}
|
||||
else if (m_array.is_array(w1)) {
|
||||
eval_array_eq(mdl, e, w1, w2, res);
|
||||
if (m.is_true (res)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("model_evaluator", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
res.reset ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
res = m.mk_true ();
|
||||
}
|
||||
|
||||
void model_evaluator_array_util::eval(model& mdl, expr* e, expr_ref& r, bool model_completion) {
|
||||
model_evaluator mev (mdl);
|
||||
mev.set_model_completion (model_completion);
|
||||
bool eval_result = true;
|
||||
try {
|
||||
mev (e, r);
|
||||
}
|
||||
catch (model_evaluator_exception &) {
|
||||
eval_result = false;
|
||||
}
|
||||
VERIFY(eval_result);
|
||||
|
||||
if (m_array.is_array(e)) {
|
||||
vector<expr_ref_vector> stores;
|
||||
expr_ref_vector args(m);
|
||||
expr_ref else_case(m);
|
||||
if (extract_array_func_interp(mdl, r, stores, else_case)) {
|
||||
r = m_array.mk_const_array(m.get_sort(e), else_case);
|
||||
while (!stores.empty() && stores.back().back() == else_case) {
|
||||
stores.pop_back();
|
||||
}
|
||||
for (unsigned i = stores.size(); i > 0; ) {
|
||||
--i;
|
||||
args.resize(1);
|
||||
args[0] = r;
|
||||
args.append(stores[i]);
|
||||
r = m_array.mk_store(args.size(), args.c_ptr());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
52
src/muz/spacer/spacer_mev_array.h
Normal file
52
src/muz/spacer/spacer_mev_array.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_mev_array.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utilities to evaluate arrays in the model.
|
||||
|
||||
Author:
|
||||
based on model_evaluator in muz/pdr/pdr_util.h
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#ifndef _SPACER_MEV_ARRAY_H_
|
||||
#define _SPACER_MEV_ARRAY_H_
|
||||
|
||||
#include"ast/ast.h"
|
||||
#include"ast/rewriter/rewriter_types.h"
|
||||
#include"util/params.h"
|
||||
#include"ast/array_decl_plugin.h"
|
||||
|
||||
/**
|
||||
* based on model_evaluator in muz/pdr/pdr_util.h
|
||||
*/
|
||||
class model_evaluator_array_util {
|
||||
ast_manager& m;
|
||||
array_util m_array;
|
||||
|
||||
void eval_exprs(model& mdl, expr_ref_vector& es);
|
||||
|
||||
bool extract_array_func_interp(model& mdl, expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case);
|
||||
|
||||
public:
|
||||
|
||||
model_evaluator_array_util (ast_manager& m):
|
||||
m (m),
|
||||
m_array (m)
|
||||
{}
|
||||
|
||||
/**
|
||||
* best effort evaluator of extensional array equality.
|
||||
*/
|
||||
void eval_array_eq(model& mdl, app* e, expr* arg1, expr* arg2, expr_ref& res);
|
||||
|
||||
void eval(model& mdl, expr* e, expr_ref& r, bool model_completion = true);
|
||||
};
|
||||
|
||||
#endif
|
||||
289
src/muz/spacer/spacer_min_cut.cpp
Normal file
289
src/muz/spacer/spacer_min_cut.cpp
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_min_cut.cpp
|
||||
|
||||
Abstract:
|
||||
min cut solver
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#include "muz/spacer/spacer_min_cut.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
spacer_min_cut::spacer_min_cut()
|
||||
{
|
||||
m_n = 2;
|
||||
|
||||
// push back two empty vectors for source and sink
|
||||
m_edges.push_back(vector<std::pair<unsigned, unsigned>>());
|
||||
m_edges.push_back(vector<std::pair<unsigned, unsigned>>());
|
||||
}
|
||||
|
||||
unsigned spacer_min_cut::new_node()
|
||||
{
|
||||
return m_n++;
|
||||
}
|
||||
|
||||
void spacer_min_cut::add_edge(unsigned int i, unsigned int j, unsigned int capacity)
|
||||
{
|
||||
if (i >= m_edges.size())
|
||||
{
|
||||
m_edges.resize(i + 1);
|
||||
}
|
||||
m_edges[i].insert(std::make_pair(j, 1));
|
||||
STRACE("spacer.mincut",
|
||||
verbose_stream() << "adding edge (" << i << "," << j << ")\n";
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void spacer_min_cut::compute_min_cut(vector<unsigned>& cut_nodes)
|
||||
{
|
||||
if (m_n == 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_d.resize(m_n);
|
||||
m_pred.resize(m_n);
|
||||
|
||||
// compute initial distances and number of nodes
|
||||
compute_initial_distances();
|
||||
|
||||
unsigned i = 0;
|
||||
|
||||
while (m_d[0] < m_n)
|
||||
{
|
||||
unsigned j = get_admissible_edge(i);
|
||||
|
||||
if (j < m_n)
|
||||
{
|
||||
// advance(i)
|
||||
m_pred[j] = i;
|
||||
i = j;
|
||||
|
||||
// if i is the sink, augment path
|
||||
if (i == 1)
|
||||
{
|
||||
augment_path();
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// retreat
|
||||
compute_distance(i);
|
||||
if (i != 0)
|
||||
{
|
||||
i = m_pred[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// split nodes into reachable and unreachable ones
|
||||
vector<bool> reachable(m_n);
|
||||
compute_reachable_nodes(reachable);
|
||||
|
||||
// find all edges between reachable and unreachable nodes and for each such edge, add corresponding lemma to unsat-core
|
||||
compute_cut_and_add_lemmas(reachable, cut_nodes);
|
||||
}
|
||||
|
||||
void spacer_min_cut::compute_initial_distances()
|
||||
{
|
||||
vector<unsigned> todo;
|
||||
vector<bool> visited(m_n);
|
||||
|
||||
todo.push_back(0); // start at the source, since we do postorder traversel
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned current = todo.back();
|
||||
|
||||
// if we haven't already visited current
|
||||
if (!visited[current]) {
|
||||
bool existsUnvisitedParent = false;
|
||||
|
||||
// add unprocessed parents to stack for DFS. If there is at least one unprocessed parent, don't compute the result
|
||||
// for current now, but wait until those unprocessed parents are processed.
|
||||
for (unsigned i = 0, sz = m_edges[current].size(); i < sz; ++i)
|
||||
{
|
||||
unsigned parent = m_edges[current][i].first;
|
||||
|
||||
// if we haven't visited the current parent yet
|
||||
if(!visited[parent])
|
||||
{
|
||||
// add it to the stack
|
||||
todo.push_back(parent);
|
||||
existsUnvisitedParent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we already visited all parents, we can visit current too
|
||||
if (!existsUnvisitedParent) {
|
||||
visited[current] = true;
|
||||
todo.pop_back();
|
||||
|
||||
compute_distance(current); // I.H. all parent distances are already computed
|
||||
}
|
||||
}
|
||||
else {
|
||||
todo.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned spacer_min_cut::get_admissible_edge(unsigned i)
|
||||
{
|
||||
for (const auto& pair : m_edges[i])
|
||||
{
|
||||
if (pair.second > 0 && m_d[i] == m_d[pair.first] + 1)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
}
|
||||
return m_n; // no element found
|
||||
}
|
||||
|
||||
void spacer_min_cut::augment_path()
|
||||
{
|
||||
// find bottleneck capacity
|
||||
unsigned max = std::numeric_limits<unsigned int>::max();
|
||||
unsigned k = 1;
|
||||
while (k != 0)
|
||||
{
|
||||
unsigned l = m_pred[k];
|
||||
for (const auto& pair : m_edges[l])
|
||||
{
|
||||
if (pair.first == k)
|
||||
{
|
||||
if (max > pair.second)
|
||||
{
|
||||
max = pair.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
k = l;
|
||||
}
|
||||
|
||||
k = 1;
|
||||
while (k != 0)
|
||||
{
|
||||
unsigned l = m_pred[k];
|
||||
|
||||
// decrease capacity
|
||||
for (auto& pair : m_edges[l])
|
||||
{
|
||||
if (pair.first == k)
|
||||
{
|
||||
pair.second -= max;
|
||||
}
|
||||
}
|
||||
// increase reverse flow
|
||||
bool already_exists = false;
|
||||
for (auto& pair : m_edges[k])
|
||||
{
|
||||
if (pair.first == l)
|
||||
{
|
||||
already_exists = true;
|
||||
pair.second += max;
|
||||
}
|
||||
}
|
||||
if (!already_exists)
|
||||
{
|
||||
m_edges[k].insert(std::make_pair(l, max));
|
||||
}
|
||||
k = l;
|
||||
}
|
||||
}
|
||||
|
||||
void spacer_min_cut::compute_distance(unsigned i)
|
||||
{
|
||||
if (i == 1) // sink node
|
||||
{
|
||||
m_d[1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned min = std::numeric_limits<unsigned int>::max();
|
||||
|
||||
// find edge (i,j) with positive residual capacity and smallest distance
|
||||
for (const auto& pair : m_edges[i])
|
||||
{
|
||||
if (pair.second > 0)
|
||||
{
|
||||
unsigned tmp = m_d[pair.first] + 1;
|
||||
if (tmp < min)
|
||||
{
|
||||
min = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_d[i] = min;
|
||||
}
|
||||
}
|
||||
|
||||
void spacer_min_cut::compute_reachable_nodes(vector<bool>& reachable)
|
||||
{
|
||||
vector<unsigned> todo;
|
||||
|
||||
todo.push_back(0);
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned current = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
if (!reachable[current])
|
||||
{
|
||||
reachable[current] = true;
|
||||
|
||||
for (const auto& pair : m_edges[current])
|
||||
{
|
||||
if (pair.second > 0)
|
||||
{
|
||||
todo.push_back(pair.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spacer_min_cut::compute_cut_and_add_lemmas(vector<bool>& reachable, vector<unsigned>& cut_nodes)
|
||||
{
|
||||
vector<unsigned> todo;
|
||||
vector<bool> visited(m_n);
|
||||
|
||||
todo.push_back(0);
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned current = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
if (!visited[current])
|
||||
{
|
||||
visited[current] = true;
|
||||
|
||||
for (const auto& pair : m_edges[current])
|
||||
{
|
||||
unsigned successor = pair.first;
|
||||
if (reachable[successor])
|
||||
{
|
||||
todo.push_back(successor);
|
||||
}
|
||||
else
|
||||
{
|
||||
cut_nodes.push_back(successor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/muz/spacer/spacer_min_cut.h
Normal file
53
src/muz/spacer/spacer_min_cut.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_min_cut.h
|
||||
|
||||
Abstract:
|
||||
min cut solver
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_MIN_CUT_H_
|
||||
#define _SPACER_MIN_CUT_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class spacer_min_cut {
|
||||
public:
|
||||
spacer_min_cut();
|
||||
|
||||
unsigned new_node();
|
||||
void add_edge(unsigned i, unsigned j, unsigned capacity);
|
||||
void compute_min_cut(vector<unsigned>& cut_nodes);
|
||||
|
||||
private:
|
||||
|
||||
unsigned m_n; // number of vertices in the graph
|
||||
|
||||
vector<vector<std::pair<unsigned, unsigned> > > m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges")
|
||||
vector<unsigned> m_d; // approximation of distance from node to sink in residual graph
|
||||
vector<unsigned> m_pred; // predecessor-information for reconstruction of augmenting path
|
||||
vector<expr*> m_node_to_formula; // maps each node to the corresponding formula in the original proof
|
||||
|
||||
void compute_initial_distances();
|
||||
unsigned get_admissible_edge(unsigned i);
|
||||
void augment_path();
|
||||
void compute_distance(unsigned i);
|
||||
void compute_reachable_nodes(vector<bool>& reachable);
|
||||
void compute_cut_and_add_lemmas(vector<bool>& reachable, vector<unsigned>& cut_nodes);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
231
src/muz/spacer/spacer_notes.txt
Normal file
231
src/muz/spacer/spacer_notes.txt
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
a queue contains a model_node
|
||||
|
||||
let n = leaves.pop_top ()
|
||||
|
||||
if (!n.has_derivation ())
|
||||
|
||||
if n.pt ().must_reach (n.post ())
|
||||
add parent of n to the leaves
|
||||
return
|
||||
|
||||
check abstract reachability of n
|
||||
|
||||
if must reachable then
|
||||
create new reachability fact for n.pt ()
|
||||
add parent of n to the leaves
|
||||
else if may reachable then
|
||||
create derivation d for n
|
||||
create model_node kid for the top of d
|
||||
add kid to the leaves
|
||||
|
||||
else /* unreachable */
|
||||
create a lemma for n.pt ()
|
||||
p = parent of n
|
||||
p.reset_derivation()
|
||||
add p to the leaves
|
||||
|
||||
else if (n.has_derivation ())
|
||||
|
||||
create next model_node kid for n.get_derivation ()
|
||||
|
||||
if (kid != NULL)
|
||||
add kid to leaves
|
||||
else /* done with the derivation, no more kids */
|
||||
// the derivation is reachable, otherwise it was reset in another branch
|
||||
p = parent of n
|
||||
p.reset_derivation ()
|
||||
add p to the leaves
|
||||
|
||||
|
||||
=================================================================================
|
||||
create derivation for the top of d
|
||||
input:
|
||||
model M,
|
||||
transition relation formula trans with auxiliary variables quantified out
|
||||
sequence of pedicates P_i,
|
||||
may and must summaries of P_i
|
||||
=================================================================================
|
||||
|
||||
create first derivation child:
|
||||
input: model
|
||||
|
||||
|
||||
create next derivation child:
|
||||
create new model
|
||||
update trans by computing pre-image over new reachability facts
|
||||
call create next derivation child
|
||||
|
||||
private:
|
||||
create next derivation child using a given model, and starting index
|
||||
|
||||
=========================================================
|
||||
|
||||
create a next model for a derivation
|
||||
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
|
||||
// an obligation
|
||||
model_node
|
||||
// NULL means root
|
||||
model_node_ref parent
|
||||
model_node_ref_vector kids
|
||||
|
||||
pred_transformer &predicate
|
||||
expr* condition
|
||||
unsigned level
|
||||
unsigned depth
|
||||
// monotonically increasing
|
||||
unsigned id
|
||||
|
||||
bool open;
|
||||
|
||||
|
||||
model_node::close ()
|
||||
open = false
|
||||
for k : kids do k.close ()
|
||||
|
||||
model_search
|
||||
|
||||
model_node_ref root;
|
||||
|
||||
// proof obligations
|
||||
priority_queue m_obligations;
|
||||
model_node_ref m_last_reachable;
|
||||
|
||||
|
||||
bool model_node::operator< (model_node& other)
|
||||
lexicographic order based on
|
||||
level<, depth<, id>
|
||||
|
||||
|
||||
|
||||
assert (!m_last_reachable);
|
||||
while (!m_obligations.empty ())
|
||||
{
|
||||
// propagate reachability as much as possible
|
||||
while (m_last_reachable)
|
||||
{
|
||||
obl = m_last_reachable
|
||||
m_last_reachable.reset ();
|
||||
if (is_root (obl)) return true;
|
||||
if (discharge_obligation (obl.get_parent ()) == l_true)
|
||||
m_last_reachable = obl.get_parent ();
|
||||
}
|
||||
|
||||
// at least one obligation is not closed, ow root was reachable
|
||||
while (m_obligations.top ().is_closed ()) m_obligations.pop ();
|
||||
assert (!m_obligations.empty ());
|
||||
|
||||
// process an obligation
|
||||
assert (!m_last_reachable)
|
||||
obl = m_obligations.top ();
|
||||
switch (discharge_obligation (obl))
|
||||
{
|
||||
case l_true:
|
||||
// if reachable, schedule a reachability round
|
||||
m_last_reachable = m_obligations.top ();
|
||||
m_obligations.pop ();
|
||||
break;
|
||||
case l_false:
|
||||
// if unreachable removed from the queue
|
||||
m_obligations.pop ();
|
||||
/// bump level
|
||||
obl.inc_level ();
|
||||
/// optionally insert back into the queue
|
||||
if (is_active (obl)) m_obligations.push (obl);
|
||||
break;
|
||||
default:
|
||||
assert (m_obligations.top () != obl);
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
/// with priority queue
|
||||
bool is_active (model_node obl) { return level <= m_search.max_level (); }
|
||||
/// with out priority queue. Discharged obligations are dropped
|
||||
bool is_active (model_node obl) { return false; }
|
||||
|
||||
discharge_obligation (model_node obl)
|
||||
{
|
||||
assert (!obl.is_closed ());
|
||||
switch (check_reachability (obl))
|
||||
{
|
||||
case l_true:
|
||||
obl.close ()
|
||||
update reachability facts
|
||||
return l_true;
|
||||
case l_false:
|
||||
update lemmas
|
||||
return l_false
|
||||
case l_unknown:
|
||||
create children
|
||||
populate m_obligations queue
|
||||
return l_unknown
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
=============================================================
|
||||
|
||||
a node keeps a derivation object
|
||||
|
||||
if a node is sat, a new node is constructed and inherits the derivation object
|
||||
if a node is sat and the derivation is done, this is reported to the parent
|
||||
|
||||
expand_node(n):
|
||||
process node ignoring derivation
|
||||
if sat:
|
||||
if concrete:
|
||||
if has derivation and has next child
|
||||
close current node and push new node
|
||||
return l_undef
|
||||
else
|
||||
return l_true
|
||||
else
|
||||
create_child (creates a new node and optionally sets derivation)
|
||||
else if unsat
|
||||
generate lemmas
|
||||
derivation remains unchanged to be used at a higher level
|
||||
return
|
||||
======================================================================
|
||||
1. open disjunction for transition relation
|
||||
- a fresh literal to open the disjunction of the transition relation
|
||||
- expr* expand_init (expr *e) -- add e to initial state and return
|
||||
new disj var
|
||||
- close the disjunction by passing the negation of the literal
|
||||
during various calls
|
||||
- store the literal negated to have access to both positive and
|
||||
negative versions
|
||||
- with this, can do an optional check whether the lemmas alone are
|
||||
strong enough to discharge the counterexample. Easiest is to
|
||||
implement it as a separate pre-check.
|
||||
|
||||
2. auxiliary variables in lemmas and reach-facts.
|
||||
- store and expect auxiliary variables
|
||||
- quantify them out when necessary
|
||||
|
||||
3. initial rules as reach-facts
|
||||
- add initial rules of a predicate to its reach-facts. Propagate them to uses.
|
||||
- this way, the checks at level 0 will include initial rules of
|
||||
immediate predecessors
|
||||
======================================================================
|
||||
|
||||
reach_fact_ref_vector m_reach_facts
|
||||
app_ref_vector m_reach_case_vars
|
||||
|
||||
bool is_must_reachable (expr *state, model_ref *model)
|
||||
reach_fact* get_used_reach_fact (model_evaluator &mev)
|
||||
app* mk_fresh_reach_case_var ()
|
||||
expr* get_reach ()
|
||||
expr* get_last_reach_case_var ()
|
||||
app* get_reach_case_var (unsigned idx)
|
||||
|
||||
get_used_origin_reach_fact():
|
||||
|
||||
|
||||
======================================================================
|
||||
4. track relationship between an obligation and lemmas. Attempt to
|
||||
generalize an obligation into the exact lemma that worked
|
||||
before. Perhaps pick one lemma with highest level? Implement as
|
||||
core-generalizer. Will require reworking how legacy_frames is implemented.
|
||||
332
src/muz/spacer/spacer_proof_utils.cpp
Normal file
332
src/muz/spacer/spacer_proof_utils.cpp
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_proof_utils.cpp
|
||||
|
||||
Abstract:
|
||||
Utilities to traverse and manipulate proofs
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/spacer/spacer_proof_utils.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
||||
#include "ast/proof_checker/proof_checker.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
ProofIteratorPostOrder::ProofIteratorPostOrder(proof* root, ast_manager& manager) : m(manager)
|
||||
{m_todo.push_back(root);}
|
||||
|
||||
bool ProofIteratorPostOrder::hasNext()
|
||||
{return !m_todo.empty();}
|
||||
|
||||
/*
|
||||
* iterative post-order depth-first search (DFS) through the proof DAG
|
||||
*/
|
||||
proof* ProofIteratorPostOrder::next()
|
||||
{
|
||||
while (!m_todo.empty()) {
|
||||
proof* currentNode = m_todo.back();
|
||||
|
||||
// if we haven't already visited the current unit
|
||||
if (!m_visited.is_marked(currentNode)) {
|
||||
bool existsUnvisitedParent = false;
|
||||
|
||||
// add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result
|
||||
// for currentProof now, but wait until those unprocessed premises are processed.
|
||||
for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) {
|
||||
SASSERT(m.is_proof(currentNode->get_arg(i)));
|
||||
proof* premise = to_app(currentNode->get_arg(i));
|
||||
|
||||
// if we haven't visited the current premise yet
|
||||
if (!m_visited.is_marked(premise)) {
|
||||
// add it to the stack
|
||||
m_todo.push_back(premise);
|
||||
existsUnvisitedParent = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we already visited all parent-inferences, we can visit the inference too
|
||||
if (!existsUnvisitedParent) {
|
||||
m_visited.mark(currentNode, true);
|
||||
m_todo.pop_back();
|
||||
return currentNode;
|
||||
}
|
||||
} else {
|
||||
m_todo.pop_back();
|
||||
}
|
||||
}
|
||||
// we have already iterated through all inferences
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
class reduce_hypotheses {
|
||||
ast_manager &m;
|
||||
// tracking all created expressions
|
||||
expr_ref_vector m_pinned;
|
||||
|
||||
// cache for the transformation
|
||||
obj_map<proof, proof*> m_cache;
|
||||
|
||||
// map from unit literals to their hypotheses-free derivations
|
||||
obj_map<expr, proof*> m_units;
|
||||
|
||||
// -- all hypotheses in the the proof
|
||||
obj_hashtable<expr> m_hyps;
|
||||
|
||||
// marks hypothetical proofs
|
||||
ast_mark m_hypmark;
|
||||
|
||||
|
||||
// stack
|
||||
ptr_vector<proof> m_todo;
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_cache.reset();
|
||||
m_units.reset();
|
||||
m_hyps.reset();
|
||||
m_hypmark.reset();
|
||||
m_pinned.reset();
|
||||
}
|
||||
|
||||
bool compute_mark1(proof *pr)
|
||||
{
|
||||
bool hyp_mark = false;
|
||||
// lemmas clear all hypotheses
|
||||
if (!m.is_lemma(pr)) {
|
||||
for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) {
|
||||
if (m_hypmark.is_marked(m.get_parent(pr, i))) {
|
||||
hyp_mark = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_hypmark.mark(pr, hyp_mark);
|
||||
return hyp_mark;
|
||||
}
|
||||
|
||||
void compute_marks(proof* pr)
|
||||
{
|
||||
proof *p;
|
||||
ProofIteratorPostOrder pit(pr, m);
|
||||
while (pit.hasNext()) {
|
||||
p = pit.next();
|
||||
if (m.is_hypothesis(p)) {
|
||||
m_hypmark.mark(p, true);
|
||||
m_hyps.insert(m.get_fact(p));
|
||||
} else {
|
||||
bool hyp_mark = compute_mark1(p);
|
||||
// collect units that are hyp-free and are used as hypotheses somewhere
|
||||
if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p)))
|
||||
{ m_units.insert(m.get_fact(p), p); }
|
||||
}
|
||||
}
|
||||
}
|
||||
void find_units(proof *pr)
|
||||
{
|
||||
// optional. not implemented yet.
|
||||
}
|
||||
|
||||
void reduce(proof* pf, proof_ref &out)
|
||||
{
|
||||
proof *res = NULL;
|
||||
|
||||
m_todo.reset();
|
||||
m_todo.push_back(pf);
|
||||
ptr_buffer<proof> args;
|
||||
bool dirty = false;
|
||||
|
||||
while (!m_todo.empty()) {
|
||||
proof *p, *tmp, *pp;
|
||||
unsigned todo_sz;
|
||||
|
||||
p = m_todo.back();
|
||||
if (m_cache.find(p, tmp)) {
|
||||
res = tmp;
|
||||
m_todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
args.reset();
|
||||
todo_sz = m_todo.size();
|
||||
for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) {
|
||||
pp = m.get_parent(p, i);
|
||||
if (m_cache.find(pp, tmp)) {
|
||||
args.push_back(tmp);
|
||||
dirty = dirty || pp != tmp;
|
||||
} else {
|
||||
m_todo.push_back(pp);
|
||||
}
|
||||
}
|
||||
|
||||
if (todo_sz < m_todo.size()) { continue; }
|
||||
else { m_todo.pop_back(); }
|
||||
|
||||
if (m.is_hypothesis(p)) {
|
||||
// hyp: replace by a corresponding unit
|
||||
if (m_units.find(m.get_fact(p), tmp)) {
|
||||
res = tmp;
|
||||
} else { res = p; }
|
||||
}
|
||||
|
||||
else if (!dirty) { res = p; }
|
||||
|
||||
else if (m.is_lemma(p)) {
|
||||
//lemma: reduce the premise; remove reduced consequences from conclusion
|
||||
SASSERT(args.size() == 1);
|
||||
res = mk_lemma_core(args.get(0), m.get_fact(p));
|
||||
compute_mark1(res);
|
||||
} else if (m.is_unit_resolution(p)) {
|
||||
// unit: reduce untis; reduce the first premise; rebuild unit resolution
|
||||
res = mk_unit_resolution_core(args.size(), args.c_ptr());
|
||||
compute_mark1(res);
|
||||
} else {
|
||||
// other: reduce all premises; reapply
|
||||
if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); }
|
||||
SASSERT(p->get_decl()->get_arity() == args.size());
|
||||
res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr());
|
||||
m_pinned.push_back(res);
|
||||
compute_mark1(res);
|
||||
}
|
||||
|
||||
SASSERT(res);
|
||||
m_cache.insert(p, res);
|
||||
|
||||
if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; }
|
||||
}
|
||||
|
||||
out = res;
|
||||
}
|
||||
|
||||
// returns true if (hypothesis (not a)) would be reduced
|
||||
bool is_reduced(expr *a)
|
||||
{
|
||||
expr_ref e(m);
|
||||
if (m.is_not(a)) { e = to_app(a)->get_arg(0); }
|
||||
else { e = m.mk_not(a); }
|
||||
|
||||
return m_units.contains(e);
|
||||
}
|
||||
proof *mk_lemma_core(proof *pf, expr *fact)
|
||||
{
|
||||
ptr_buffer<expr> args;
|
||||
expr_ref lemma(m);
|
||||
|
||||
if (m.is_or(fact)) {
|
||||
for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) {
|
||||
expr *a = to_app(fact)->get_arg(i);
|
||||
if (!is_reduced(a))
|
||||
{ args.push_back(a); }
|
||||
}
|
||||
} else if (!is_reduced(fact))
|
||||
{ args.push_back(fact); }
|
||||
|
||||
|
||||
if (args.size() == 0) { return pf; }
|
||||
else if (args.size() == 1) {
|
||||
lemma = args.get(0);
|
||||
} else {
|
||||
lemma = m.mk_or(args.size(), args.c_ptr());
|
||||
}
|
||||
proof* res = m.mk_lemma(pf, lemma);
|
||||
m_pinned.push_back(res);
|
||||
|
||||
if (m_hyps.contains(lemma))
|
||||
{ m_units.insert(lemma, res); }
|
||||
return res;
|
||||
}
|
||||
|
||||
proof *mk_unit_resolution_core(unsigned num_args, proof* const *args)
|
||||
{
|
||||
|
||||
ptr_buffer<proof> pf_args;
|
||||
pf_args.push_back(args [0]);
|
||||
|
||||
app *cls_fact = to_app(m.get_fact(args[0]));
|
||||
ptr_buffer<expr> cls;
|
||||
if (m.is_or(cls_fact)) {
|
||||
for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i)
|
||||
{ cls.push_back(cls_fact->get_arg(i)); }
|
||||
} else { cls.push_back(cls_fact); }
|
||||
|
||||
// construct new resovent
|
||||
ptr_buffer<expr> new_fact_cls;
|
||||
bool found;
|
||||
// XXX quadratic
|
||||
for (unsigned i = 0, sz = cls.size(); i < sz; ++i) {
|
||||
found = false;
|
||||
for (unsigned j = 1; j < num_args; ++j) {
|
||||
if (m.is_complement(cls.get(i), m.get_fact(args [j]))) {
|
||||
found = true;
|
||||
pf_args.push_back(args [j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
new_fact_cls.push_back(cls.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size());
|
||||
expr_ref new_fact(m);
|
||||
new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr());
|
||||
|
||||
// create new proof step
|
||||
proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact);
|
||||
m_pinned.push_back(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// reduce all units, if any unit reduces to false return true and put its proof into out
|
||||
bool reduce_units(proof_ref &out)
|
||||
{
|
||||
proof_ref res(m);
|
||||
for (auto entry : m_units) {
|
||||
reduce(entry.get_value(), res);
|
||||
if (m.is_false(m.get_fact(res))) {
|
||||
out = res;
|
||||
return true;
|
||||
}
|
||||
res.reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {}
|
||||
|
||||
|
||||
void operator()(proof_ref &pr)
|
||||
{
|
||||
compute_marks(pr);
|
||||
if (!reduce_units(pr)) {
|
||||
reduce(pr.get(), pr);
|
||||
}
|
||||
reset();
|
||||
}
|
||||
};
|
||||
void reduce_hypotheses(proof_ref &pr)
|
||||
{
|
||||
ast_manager &m = pr.get_manager();
|
||||
class reduce_hypotheses hypred(m);
|
||||
hypred(pr);
|
||||
DEBUG_CODE(proof_checker pc(m);
|
||||
expr_ref_vector side(m);
|
||||
SASSERT(pc.check(pr, side));
|
||||
);
|
||||
}
|
||||
}
|
||||
43
src/muz/spacer/spacer_proof_utils.h
Normal file
43
src/muz/spacer/spacer_proof_utils.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_proof_utils.cpp
|
||||
|
||||
Abstract:
|
||||
Utilities to traverse and manipulate proofs
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_PROOF_UTILS_H_
|
||||
#define _SPACER_PROOF_UTILS_H_
|
||||
#include "ast/ast.h"
|
||||
|
||||
namespace spacer {
|
||||
/*
|
||||
* iterator, which traverses the proof in depth-first post-order.
|
||||
*/
|
||||
class ProofIteratorPostOrder {
|
||||
public:
|
||||
ProofIteratorPostOrder(proof* refutation, ast_manager& manager);
|
||||
bool hasNext();
|
||||
proof* next();
|
||||
|
||||
private:
|
||||
ptr_vector<proof> m_todo;
|
||||
ast_mark m_visited; // the proof nodes we have already visited
|
||||
|
||||
ast_manager& m;
|
||||
};
|
||||
|
||||
|
||||
void reduce_hypotheses(proof_ref &pr);
|
||||
}
|
||||
#endif
|
||||
302
src/muz/spacer/spacer_prop_solver.cpp
Normal file
302
src/muz/spacer/spacer_prop_solver.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_prop_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SAT solver abstraction for SPACER.
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
Anvesh Komuravelli
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
|
||||
#include "smt/params/smt_params.h"
|
||||
|
||||
#include "model/model.h"
|
||||
#include "model/model_pp.h"
|
||||
|
||||
#include "muz/base/dl_util.h"
|
||||
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_farkas_learner.h"
|
||||
#include "muz/spacer/spacer_prop_solver.h"
|
||||
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) :
|
||||
m(pm.get_manager()),
|
||||
m_pm(pm),
|
||||
m_name(name),
|
||||
m_ctx(NULL),
|
||||
m_pos_level_atoms(m),
|
||||
m_neg_level_atoms(m),
|
||||
m_core(0),
|
||||
m_subset_based_core(false),
|
||||
m_uses_level(infty_level()),
|
||||
m_delta_level(false),
|
||||
m_in_level(false),
|
||||
m_use_push_bg(p.spacer_keep_proxy())
|
||||
{
|
||||
|
||||
m_solvers[0] = pm.mk_fresh();
|
||||
m_fparams[0] = &pm.fparams();
|
||||
|
||||
m_solvers[1] = pm.mk_fresh2();
|
||||
m_fparams[1] = &pm.fparams2();
|
||||
|
||||
m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals());
|
||||
m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals());
|
||||
|
||||
for (unsigned i = 0; i < 2; ++i)
|
||||
{ m_contexts[i]->assert_expr(m_pm.get_background()); }
|
||||
}
|
||||
|
||||
void prop_solver::add_level()
|
||||
{
|
||||
unsigned idx = level_cnt();
|
||||
std::stringstream name;
|
||||
name << m_name << "#level_" << idx;
|
||||
func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0, m.mk_bool_sort());
|
||||
m_level_preds.push_back(lev_pred);
|
||||
|
||||
app_ref pos_la(m.mk_const(lev_pred), m);
|
||||
app_ref neg_la(m.mk_not(pos_la.get()), m);
|
||||
|
||||
m_pos_level_atoms.push_back(pos_la);
|
||||
m_neg_level_atoms.push_back(neg_la);
|
||||
|
||||
m_level_atoms_set.insert(pos_la.get());
|
||||
m_level_atoms_set.insert(neg_la.get());
|
||||
}
|
||||
|
||||
void prop_solver::ensure_level(unsigned lvl)
|
||||
{
|
||||
while (lvl >= level_cnt()) {
|
||||
add_level();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prop_solver::level_cnt() const
|
||||
{
|
||||
return m_level_preds.size();
|
||||
}
|
||||
|
||||
void prop_solver::assert_level_atoms(unsigned level)
|
||||
{
|
||||
unsigned lev_cnt = level_cnt();
|
||||
for (unsigned i = 0; i < lev_cnt; i++) {
|
||||
bool active = m_delta_level ? i == level : i >= level;
|
||||
app * lev_atom =
|
||||
active ? m_neg_level_atoms.get(i) : m_pos_level_atoms.get(i);
|
||||
m_ctx->push_bg(lev_atom);
|
||||
}
|
||||
}
|
||||
|
||||
void prop_solver::assert_expr(expr * form)
|
||||
{
|
||||
SASSERT(!m_in_level);
|
||||
m_contexts[0]->assert_expr(form);
|
||||
m_contexts[1]->assert_expr(form);
|
||||
IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";);
|
||||
TRACE("spacer", tout << "add_formula: " << mk_pp(form, m) << "\n";);
|
||||
}
|
||||
|
||||
void prop_solver::assert_expr(expr * form, unsigned level)
|
||||
{
|
||||
ensure_level(level);
|
||||
app * lev_atom = m_pos_level_atoms[level].get();
|
||||
app_ref lform(m.mk_or(form, lev_atom), m);
|
||||
assert_expr(lform);
|
||||
}
|
||||
|
||||
|
||||
/// Poor man's maxsat. No guarantees of maximum solution
|
||||
/// Runs maxsat loop on m_ctx Returns l_false if hard is unsat,
|
||||
/// otherwise reduces soft such that hard & soft is sat.
|
||||
lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft)
|
||||
{
|
||||
// replace expressions by assumption literals
|
||||
itp_solver::scoped_mk_proxy _p_(*m_ctx, hard);
|
||||
unsigned hard_sz = hard.size();
|
||||
// assume soft constraints are propositional literals (no need to proxy)
|
||||
hard.append(soft);
|
||||
|
||||
lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr());
|
||||
// if hard constraints alone are unsat or there are no soft
|
||||
// constraints, we are done
|
||||
if (res != l_false || soft.empty()) { return res; }
|
||||
|
||||
// clear soft constraints, we will recompute them later
|
||||
soft.reset();
|
||||
|
||||
expr_ref saved(m);
|
||||
ptr_vector<expr> core;
|
||||
m_ctx->get_unsat_core(core);
|
||||
|
||||
// while there are soft constraints
|
||||
while (hard.size() > hard_sz) {
|
||||
bool found = false;
|
||||
// look for a soft constraint that is in the unsat core
|
||||
for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i)
|
||||
if (core.contains(hard.get(i))) {
|
||||
found = true;
|
||||
// AG: not sure why we are saving it
|
||||
saved = hard.get(i);
|
||||
hard[i] = hard.back();
|
||||
hard.pop_back();
|
||||
break;
|
||||
}
|
||||
// if no soft constraints in the core, return this should
|
||||
// not happen because it implies that hard alone is unsat
|
||||
// and that is taken care of earlier
|
||||
if (!found) {
|
||||
hard.resize(hard_sz);
|
||||
return l_false;
|
||||
}
|
||||
|
||||
// check that the NEW constraints became sat
|
||||
res = m_ctx->check_sat(hard.size(), hard.c_ptr());
|
||||
if (res != l_false) { break; }
|
||||
// still unsat, update the core and repeat
|
||||
core.reset();
|
||||
m_ctx->get_unsat_core(core);
|
||||
}
|
||||
|
||||
// update soft with found soft constraints
|
||||
if (res == l_true) {
|
||||
for (unsigned i = hard_sz, sz = hard.size(); i < sz; ++i)
|
||||
{ soft.push_back(hard.get(i)); }
|
||||
}
|
||||
// revert hard back to the right size
|
||||
// proxies are undone on exit via scoped_mk_proxy
|
||||
hard.resize(hard_sz);
|
||||
return res;
|
||||
}
|
||||
|
||||
lbool prop_solver::internal_check_assumptions(
|
||||
expr_ref_vector& hard_atoms,
|
||||
expr_ref_vector& soft_atoms)
|
||||
{
|
||||
// XXX Turn model generation if m_model != 0
|
||||
SASSERT(m_ctx);
|
||||
SASSERT(m_ctx_fparams);
|
||||
flet<bool> _model(m_ctx_fparams->m_model, m_model != 0);
|
||||
|
||||
if (m_in_level) { assert_level_atoms(m_current_level); }
|
||||
lbool result = maxsmt(hard_atoms, soft_atoms);
|
||||
if (result != l_false && m_model) { m_ctx->get_model(*m_model); }
|
||||
|
||||
SASSERT(result != l_false || soft_atoms.empty());
|
||||
|
||||
/// compute level used in the core
|
||||
// XXX this is a poor approximation because the core will get minimized further
|
||||
if (result == l_false) {
|
||||
ptr_vector<expr> core;
|
||||
m_ctx->get_full_unsat_core(core);
|
||||
unsigned core_size = core.size();
|
||||
m_uses_level = infty_level();
|
||||
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
if (m_level_atoms_set.contains(core[i])) {
|
||||
unsigned sz = std::min(m_uses_level, m_neg_level_atoms.size());
|
||||
for (unsigned j = 0; j < sz; ++j)
|
||||
if (m_neg_level_atoms [j].get() == core[i]) {
|
||||
m_uses_level = j;
|
||||
break;
|
||||
}
|
||||
SASSERT(!is_infty_level(m_uses_level));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) {
|
||||
TRACE("spacer", tout << "theory core\n";);
|
||||
m_core->reset();
|
||||
m_ctx->get_itp_core(*m_core);
|
||||
} else if (result == l_false && m_core) {
|
||||
m_core->reset();
|
||||
m_ctx->get_unsat_core(*m_core);
|
||||
// manually undo proxies because maxsmt() call above manually adds proxies
|
||||
m_ctx->undo_proxies(*m_core);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
lbool prop_solver::check_assumptions(const expr_ref_vector & _hard,
|
||||
expr_ref_vector& soft,
|
||||
unsigned num_bg, expr * const * bg,
|
||||
unsigned solver_id)
|
||||
{
|
||||
// current clients expect that flattening of HARD is
|
||||
// done implicitly during check_assumptions
|
||||
expr_ref_vector hard(m);
|
||||
hard.append(_hard.size(), _hard.c_ptr());
|
||||
flatten_and(hard);
|
||||
|
||||
m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get();
|
||||
m_ctx_fparams = m_fparams [solver_id == 0 ? 0 : 0 /* 1 */];
|
||||
|
||||
// can be disabled if use_push_bg == true
|
||||
// solver::scoped_push _s_(*m_ctx);
|
||||
if (!m_use_push_bg) { m_ctx->push(); }
|
||||
itp_solver::scoped_bg _b_(*m_ctx);
|
||||
|
||||
for (unsigned i = 0; i < num_bg; ++i)
|
||||
if (m_use_push_bg) { m_ctx->push_bg(bg [i]); }
|
||||
else { m_ctx->assert_expr(bg[i]); }
|
||||
|
||||
unsigned soft_sz = soft.size();
|
||||
(void) soft_sz;
|
||||
lbool res = internal_check_assumptions(hard, soft);
|
||||
if (!m_use_push_bg) { m_ctx->pop(1); }
|
||||
|
||||
TRACE("psolve_verbose",
|
||||
tout << "sat: " << mk_pp(mk_and(hard), m) << "\n"
|
||||
<< mk_pp(mk_and(soft), m) << "\n";
|
||||
for (unsigned i = 0; i < num_bg; ++i)
|
||||
tout << "bg" << i << ": " << mk_pp(bg[i], m) << "\n";
|
||||
tout << "res: " << res << "\n";);
|
||||
CTRACE("psolve", m_core,
|
||||
tout << "core is: " << mk_pp(mk_and(*m_core), m) << "\n";);
|
||||
|
||||
SASSERT(soft_sz >= soft.size());
|
||||
|
||||
// -- reset all parameters
|
||||
m_core = 0;
|
||||
m_model = 0;
|
||||
m_subset_based_core = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
void prop_solver::collect_statistics(statistics& st) const
|
||||
{
|
||||
m_contexts[0]->collect_statistics(st);
|
||||
m_contexts[1]->collect_statistics(st);
|
||||
}
|
||||
|
||||
void prop_solver::reset_statistics()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
144
src/muz/spacer/spacer_prop_solver.h
Normal file
144
src/muz/spacer/spacer_prop_solver.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_prop_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SAT solver abstraction for SPACER.
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _PROP_SOLVER_H_
|
||||
#define _PROP_SOLVER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "util/util.h"
|
||||
#include "util/vector.h"
|
||||
#include "muz/spacer/spacer_manager.h"
|
||||
#include "muz/spacer/spacer_smt_context_manager.h"
|
||||
#include "muz/spacer/spacer_itp_solver.h"
|
||||
|
||||
struct fixedpoint_params;
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class prop_solver {
|
||||
|
||||
private:
|
||||
ast_manager& m;
|
||||
manager& m_pm;
|
||||
symbol m_name;
|
||||
smt_params* m_fparams[2];
|
||||
solver* m_solvers[2];
|
||||
scoped_ptr<itp_solver> m_contexts[2];
|
||||
itp_solver * m_ctx;
|
||||
smt_params * m_ctx_fparams;
|
||||
decl_vector m_level_preds;
|
||||
app_ref_vector m_pos_level_atoms; // atoms used to identify level
|
||||
app_ref_vector m_neg_level_atoms; //
|
||||
obj_hashtable<expr> m_level_atoms_set;
|
||||
expr_ref_vector* m_core;
|
||||
model_ref* m_model;
|
||||
bool m_subset_based_core;
|
||||
unsigned m_uses_level;
|
||||
/// if true sets the solver into a delta level, enabling only
|
||||
/// atoms explicitly asserted in m_current_level
|
||||
bool m_delta_level;
|
||||
bool m_in_level;
|
||||
bool m_use_push_bg;
|
||||
unsigned m_current_level; // set when m_in_level
|
||||
|
||||
void assert_level_atoms(unsigned level);
|
||||
|
||||
void ensure_level(unsigned lvl);
|
||||
|
||||
lbool internal_check_assumptions(expr_ref_vector &hard,
|
||||
expr_ref_vector &soft);
|
||||
|
||||
lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft);
|
||||
|
||||
|
||||
public:
|
||||
prop_solver(spacer::manager& pm, fixedpoint_params const& p, symbol const& name);
|
||||
|
||||
|
||||
void set_core(expr_ref_vector* core) { m_core = core; }
|
||||
void set_model(model_ref* mdl) { m_model = mdl; }
|
||||
void set_subset_based_core(bool f) { m_subset_based_core = f; }
|
||||
bool assumes_level() const { return !is_infty_level(m_uses_level); }
|
||||
unsigned uses_level() const {return m_uses_level;}
|
||||
|
||||
|
||||
void add_level();
|
||||
unsigned level_cnt() const;
|
||||
|
||||
|
||||
void assert_expr(expr * form);
|
||||
void assert_expr(expr * form, unsigned level);
|
||||
|
||||
/**
|
||||
* check assumptions with a background formula
|
||||
*/
|
||||
lbool check_assumptions(const expr_ref_vector & hard,
|
||||
expr_ref_vector & soft,
|
||||
unsigned num_bg = 0,
|
||||
expr * const *bg = NULL,
|
||||
unsigned solver_id = 0);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
class scoped_level {
|
||||
bool& m_lev;
|
||||
public:
|
||||
scoped_level(prop_solver& ps, unsigned lvl): m_lev(ps.m_in_level)
|
||||
{
|
||||
SASSERT(!m_lev);
|
||||
m_lev = true;
|
||||
ps.m_current_level = lvl;
|
||||
}
|
||||
~scoped_level() { m_lev = false; }
|
||||
};
|
||||
|
||||
class scoped_subset_core {
|
||||
prop_solver &m_ps;
|
||||
bool m_subset_based_core;
|
||||
|
||||
public:
|
||||
scoped_subset_core(prop_solver &ps, bool subset_core) :
|
||||
m_ps(ps), m_subset_based_core(ps.m_subset_based_core)
|
||||
{m_ps.set_subset_based_core(subset_core);}
|
||||
|
||||
~scoped_subset_core()
|
||||
{m_ps.set_subset_based_core(m_subset_based_core);}
|
||||
};
|
||||
|
||||
class scoped_delta_level : public scoped_level {
|
||||
bool &m_delta;
|
||||
public:
|
||||
scoped_delta_level(prop_solver &ps, unsigned lvl) :
|
||||
scoped_level(ps, lvl), m_delta(ps.m_delta_level) {m_delta = true;}
|
||||
~scoped_delta_level() {m_delta = false;}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
2339
src/muz/spacer/spacer_qe_project.cpp
Normal file
2339
src/muz/spacer/spacer_qe_project.cpp
Normal file
File diff suppressed because it is too large
Load diff
49
src/muz/spacer/spacer_qe_project.h
Normal file
49
src/muz/spacer/spacer_qe_project.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_qe_project.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Model-based projection
|
||||
|
||||
Author:
|
||||
|
||||
Anvesh Komuravelli
|
||||
Arie Gurfinkel (arie)
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef SPACER_QE_PROJECT_H_
|
||||
#define SPACER_QE_PROJECT_H_
|
||||
|
||||
#include "model/model.h"
|
||||
#include "ast/expr_map.h"
|
||||
|
||||
namespace qe {
|
||||
/**
|
||||
Loos-Weispfenning model-based projection for a basic conjunction.
|
||||
Lits is a vector of literals.
|
||||
return vector of variables that could not be projected.
|
||||
*/
|
||||
expr_ref arith_project(model& model, app_ref_vector& vars, expr_ref_vector const& lits);
|
||||
|
||||
void arith_project(model& model, app_ref_vector& vars, expr_ref& fml);
|
||||
|
||||
void arith_project(model& model, app_ref_vector& vars, expr_ref& fml, expr_map& map);
|
||||
|
||||
void array_project_eqs (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars);
|
||||
|
||||
void reduce_array_selects (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false);
|
||||
|
||||
void reduce_array_selects (model& mdl, expr_ref& fml);
|
||||
|
||||
void array_project_selects (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars);
|
||||
|
||||
void array_project (model& model, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects = false);
|
||||
};
|
||||
|
||||
#endif
|
||||
79
src/muz/spacer/spacer_smt_context_manager.cpp
Normal file
79
src/muz/spacer/spacer_smt_context_manager.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_smt_context_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
Arie Gurfinkel
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_pp_util.h"
|
||||
#include "ast/ast_smt_pp.h"
|
||||
|
||||
#include "smt/smt_context.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_smt_context_manager.h"
|
||||
namespace spacer {
|
||||
|
||||
|
||||
|
||||
|
||||
smt_context_manager::smt_context_manager(ast_manager &m,
|
||||
unsigned max_num_contexts,
|
||||
const params_ref &p) :
|
||||
m_fparams(p),
|
||||
m(m),
|
||||
m_max_num_contexts(max_num_contexts),
|
||||
m_num_contexts(0) { m_stats.reset();}
|
||||
|
||||
|
||||
smt_context_manager::~smt_context_manager()
|
||||
{
|
||||
std::for_each(m_solvers.begin(), m_solvers.end(),
|
||||
delete_proc<spacer::virtual_solver_factory>());
|
||||
}
|
||||
|
||||
virtual_solver* smt_context_manager::mk_fresh()
|
||||
{
|
||||
++m_num_contexts;
|
||||
virtual_solver_factory *solver_factory = 0;
|
||||
|
||||
if (m_max_num_contexts == 0 || m_solvers.size() < m_max_num_contexts) {
|
||||
m_solvers.push_back(alloc(spacer::virtual_solver_factory, m, m_fparams));
|
||||
solver_factory = m_solvers.back();
|
||||
} else
|
||||
{ solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; }
|
||||
|
||||
return solver_factory->mk_solver();
|
||||
}
|
||||
|
||||
void smt_context_manager::collect_statistics(statistics& st) const
|
||||
{
|
||||
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
||||
m_solvers[i]->collect_statistics(st);
|
||||
}
|
||||
}
|
||||
|
||||
void smt_context_manager::reset_statistics()
|
||||
{
|
||||
for (unsigned i = 0; i < m_solvers.size(); ++i) {
|
||||
m_solvers[i]->reset_statistics();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
68
src/muz/spacer/spacer_smt_context_manager.h
Normal file
68
src/muz/spacer/spacer_smt_context_manager.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_smt_context_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
Arie Gurfinkel
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_SMT_CONTEXT_MANAGER_H_
|
||||
#define _SPACER_SMT_CONTEXT_MANAGER_H_
|
||||
|
||||
#include "util/stopwatch.h"
|
||||
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "muz/spacer/spacer_virtual_solver.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class smt_context_manager {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_smt_checks;
|
||||
unsigned m_num_sat_smt_checks;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
smt_params m_fparams;
|
||||
ast_manager& m;
|
||||
unsigned m_max_num_contexts;
|
||||
ptr_vector<virtual_solver_factory> m_solvers;
|
||||
unsigned m_num_contexts;
|
||||
|
||||
|
||||
stats m_stats;
|
||||
stopwatch m_check_watch;
|
||||
stopwatch m_check_sat_watch;
|
||||
|
||||
public:
|
||||
smt_context_manager(ast_manager& m, unsigned max_num_contexts = 1,
|
||||
const params_ref &p = params_ref::get_empty());
|
||||
|
||||
~smt_context_manager();
|
||||
virtual_solver* mk_fresh();
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
void updt_params(params_ref const &p) { m_fparams.updt_params(p); }
|
||||
smt_params& fparams() {return m_fparams;}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
608
src/muz/spacer/spacer_sym_mux.cpp
Normal file
608
src/muz/spacer/spacer_sym_mux.cpp
Normal file
|
|
@ -0,0 +1,608 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sym_mux.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-8.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
|
||||
#include "model/model.h"
|
||||
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_sym_mux.h"
|
||||
|
||||
using namespace spacer;
|
||||
|
||||
sym_mux::sym_mux(ast_manager & m, const std::vector<std::string> & suffixes)
|
||||
: m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes)
|
||||
{
|
||||
for (std::string const& s : m_suffixes) {
|
||||
m_used_suffixes.insert(symbol(s.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string sym_mux::get_suffix(unsigned i) const
|
||||
{
|
||||
while (m_suffixes.size() <= i) {
|
||||
std::string new_suffix;
|
||||
symbol new_syffix_sym;
|
||||
do {
|
||||
std::stringstream stm;
|
||||
stm << '_' << m_next_sym_suffix_idx;
|
||||
m_next_sym_suffix_idx++;
|
||||
new_suffix = stm.str();
|
||||
new_syffix_sym = symbol(new_suffix.c_str());
|
||||
} while (m_used_suffixes.contains(new_syffix_sym));
|
||||
m_used_suffixes.insert(new_syffix_sym);
|
||||
m_suffixes.push_back(new_suffix);
|
||||
}
|
||||
return m_suffixes[i];
|
||||
}
|
||||
|
||||
void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
|
||||
unsigned tuple_length, decl_vector & tuple)
|
||||
{
|
||||
SASSERT(tuple_length > 0);
|
||||
while (tuple.size() < tuple_length) {
|
||||
tuple.push_back(0);
|
||||
}
|
||||
SASSERT(tuple.size() == tuple_length);
|
||||
std::string pre = prefix->get_name().str();
|
||||
for (unsigned i = 0; i < tuple_length; i++) {
|
||||
|
||||
if (tuple[i] != 0) {
|
||||
SASSERT(tuple[i]->get_arity() == arity);
|
||||
SASSERT(tuple[i]->get_range() == range);
|
||||
//domain should match as well, but we won't bother checking an array equality
|
||||
} else {
|
||||
std::string name = pre + get_suffix(i);
|
||||
tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range);
|
||||
}
|
||||
m_ref_holder.push_back(tuple[i]);
|
||||
m_sym2idx.insert(tuple[i], i);
|
||||
m_sym2prim.insert(tuple[i], tuple[0]);
|
||||
}
|
||||
|
||||
m_prim2all.insert(tuple[0], tuple);
|
||||
m_prefix2prim.insert(prefix, tuple[0]);
|
||||
m_prim2prefix.insert(tuple[0], prefix);
|
||||
m_prim_preds.push_back(tuple[0]);
|
||||
m_ref_holder.push_back(prefix);
|
||||
}
|
||||
|
||||
void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const
|
||||
{
|
||||
SASSERT(m_prim2all.contains(prim));
|
||||
decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value;
|
||||
SASSERT(tuple[0] == prim);
|
||||
|
||||
if (sz <= tuple.size()) { return; }
|
||||
|
||||
func_decl * prefix;
|
||||
TRUSTME(m_prim2prefix.find(prim, prefix));
|
||||
std::string prefix_name = prefix->get_name().bare_str();
|
||||
for (unsigned i = tuple.size(); i < sz; ++i) {
|
||||
std::string name = prefix_name + get_suffix(i);
|
||||
func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(),
|
||||
prefix->get_domain(), prefix->get_range());
|
||||
|
||||
tuple.push_back(new_sym);
|
||||
m_ref_holder.push_back(new_sym);
|
||||
m_sym2idx.insert(new_sym, i);
|
||||
m_sym2prim.insert(new_sym, prim);
|
||||
}
|
||||
}
|
||||
|
||||
func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const
|
||||
{
|
||||
if (src_idx == tgt_idx) { return sym; }
|
||||
func_decl * prim = (src_idx == 0) ? sym : get_primary(sym);
|
||||
if (tgt_idx > src_idx) {
|
||||
ensure_tuple_size(prim, tgt_idx + 1);
|
||||
}
|
||||
decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value;
|
||||
SASSERT(sym_vect[src_idx] == sym);
|
||||
return sym_vect[tgt_idx];
|
||||
}
|
||||
|
||||
|
||||
func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
func_decl * prim = try_get_primary_by_prefix(prefix);
|
||||
if (prim) {
|
||||
SASSERT(prim->get_arity() == arity);
|
||||
SASSERT(prim->get_range() == range);
|
||||
//domain should match as well, but we won't bother checking an array equality
|
||||
|
||||
return conv(prim, 0, idx);
|
||||
}
|
||||
|
||||
decl_vector syms;
|
||||
create_tuple(prefix, arity, domain, range, idx + 1, syms);
|
||||
return syms[idx];
|
||||
}
|
||||
|
||||
bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const
|
||||
{
|
||||
if (!is_app(e)) { return false; }
|
||||
app * a = to_app(e);
|
||||
if (m.is_not(a) && is_app(a->get_arg(0))) {
|
||||
a = to_app(a->get_arg(0));
|
||||
}
|
||||
return is_muxed(a->get_decl());
|
||||
}
|
||||
|
||||
|
||||
struct sym_mux::formula_checker {
|
||||
formula_checker(const sym_mux & parent, bool all, unsigned idx) :
|
||||
m_parent(parent), m_all(all), m_idx(idx),
|
||||
m_found_what_needed(false)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if (m_found_what_needed || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned sym_idx;
|
||||
if (!m_parent.try_get_index(sym, sym_idx)) { return; }
|
||||
|
||||
bool have_idx = sym_idx == m_idx;
|
||||
|
||||
if (m_all ? (!have_idx) : have_idx) {
|
||||
m_found_what_needed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool all_have_idx() const
|
||||
{
|
||||
SASSERT(m_all); //we were looking for the queried property
|
||||
return !m_found_what_needed;
|
||||
}
|
||||
|
||||
bool some_with_idx() const
|
||||
{
|
||||
SASSERT(!m_all); //we were looking for the queried property
|
||||
return m_found_what_needed;
|
||||
}
|
||||
|
||||
private:
|
||||
const sym_mux & m_parent;
|
||||
bool m_all;
|
||||
unsigned m_idx;
|
||||
|
||||
/**
|
||||
If we check whether all muxed symbols are of given index, we look for
|
||||
counter-examples, checking whether form contains a muxed symbol of an index,
|
||||
we look for symbol of index m_idx.
|
||||
*/
|
||||
bool m_found_what_needed;
|
||||
};
|
||||
|
||||
bool sym_mux::contains(expr * e, unsigned idx) const
|
||||
{
|
||||
formula_checker chck(*this, false, idx);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return chck.some_with_idx();
|
||||
}
|
||||
|
||||
bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const
|
||||
{
|
||||
formula_checker chck(*this, true, idx);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return chck.all_have_idx();
|
||||
}
|
||||
|
||||
bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const
|
||||
{
|
||||
expr * const * begin = vect.c_ptr();
|
||||
expr * const * end = begin + vect.size();
|
||||
for (expr * const * it = begin; it != end; it++) {
|
||||
if (!is_homogenous_formula(*it, idx)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class sym_mux::index_collector {
|
||||
sym_mux const& m_parent;
|
||||
svector<bool> m_indices;
|
||||
public:
|
||||
index_collector(sym_mux const& s):
|
||||
m_parent(s) {}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if (is_app(e)) {
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned idx;
|
||||
if (m_parent.try_get_index(sym, idx)) {
|
||||
SASSERT(idx > 0);
|
||||
--idx;
|
||||
if (m_indices.size() <= idx) {
|
||||
m_indices.resize(idx + 1, false);
|
||||
}
|
||||
m_indices[idx] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void extract(unsigned_vector& indices)
|
||||
{
|
||||
for (unsigned i = 0; i < m_indices.size(); ++i) {
|
||||
if (m_indices[i]) {
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const
|
||||
{
|
||||
indices.reset();
|
||||
index_collector collector(*this);
|
||||
for_each_expr(collector, m_visited, e);
|
||||
m_visited.reset();
|
||||
collector.extract(indices);
|
||||
}
|
||||
|
||||
class sym_mux::variable_collector {
|
||||
sym_mux const& m_parent;
|
||||
vector<ptr_vector<app> >& m_vars;
|
||||
public:
|
||||
variable_collector(sym_mux const& s, vector<ptr_vector<app> >& vars):
|
||||
m_parent(s), m_vars(vars) {}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if (is_app(e)) {
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned idx;
|
||||
if (m_parent.try_get_index(sym, idx)) {
|
||||
SASSERT(idx > 0);
|
||||
--idx;
|
||||
if (m_vars.size() <= idx) {
|
||||
m_vars.resize(idx + 1, ptr_vector<app>());
|
||||
}
|
||||
m_vars[idx].push_back(to_app(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::collect_variables(expr* e, vector<ptr_vector<app> >& vars) const
|
||||
{
|
||||
vars.reset();
|
||||
variable_collector collector(*this, vars);
|
||||
for_each_expr(collector, m_visited, e);
|
||||
m_visited.reset();
|
||||
}
|
||||
|
||||
class sym_mux::hmg_checker {
|
||||
const sym_mux & m_parent;
|
||||
|
||||
bool m_found_idx;
|
||||
unsigned m_idx;
|
||||
bool m_multiple_indexes;
|
||||
|
||||
public:
|
||||
hmg_checker(const sym_mux & parent) :
|
||||
m_parent(parent), m_found_idx(false), m_multiple_indexes(false)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if (m_multiple_indexes || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned sym_idx;
|
||||
if (!m_parent.try_get_index(sym, sym_idx)) { return; }
|
||||
|
||||
if (!m_found_idx) {
|
||||
m_found_idx = true;
|
||||
m_idx = sym_idx;
|
||||
return;
|
||||
}
|
||||
if (m_idx == sym_idx) { return; }
|
||||
m_multiple_indexes = true;
|
||||
}
|
||||
|
||||
bool has_multiple_indexes() const
|
||||
{
|
||||
return m_multiple_indexes;
|
||||
}
|
||||
};
|
||||
|
||||
bool sym_mux::is_homogenous_formula(expr * e) const
|
||||
{
|
||||
hmg_checker chck(*this);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return !chck.has_multiple_indexes();
|
||||
}
|
||||
|
||||
|
||||
struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg {
|
||||
private:
|
||||
ast_manager & m;
|
||||
const sym_mux & m_parent;
|
||||
unsigned m_from_idx;
|
||||
unsigned m_to_idx;
|
||||
bool m_homogenous;
|
||||
public:
|
||||
conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous)
|
||||
: m(parent.get_manager()),
|
||||
m_parent(parent),
|
||||
m_from_idx(from_idx),
|
||||
m_to_idx(to_idx),
|
||||
m_homogenous(homogenous) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr)
|
||||
{
|
||||
if (!is_app(s)) { return false; }
|
||||
app * a = to_app(s);
|
||||
func_decl * sym = a->get_decl();
|
||||
if (!m_parent.has_index(sym, m_from_idx)) {
|
||||
(void) m_homogenous;
|
||||
SASSERT(!m_homogenous || !m_parent.is_muxed(sym));
|
||||
return false;
|
||||
}
|
||||
func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx);
|
||||
|
||||
t = m.mk_app(tgt, a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const
|
||||
{
|
||||
if (src_idx == tgt_idx) {
|
||||
res = f;
|
||||
return;
|
||||
}
|
||||
conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous);
|
||||
rewriter_tpl<conv_rewriter_cfg> rwr(m, false, r_cfg);
|
||||
rwr(f, res);
|
||||
}
|
||||
|
||||
struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg {
|
||||
private:
|
||||
ast_manager & m;
|
||||
const sym_mux & m_parent;
|
||||
int m_shift;
|
||||
public:
|
||||
shifting_rewriter_cfg(const sym_mux & parent, int shift)
|
||||
: m(parent.get_manager()),
|
||||
m_parent(parent),
|
||||
m_shift(shift) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr)
|
||||
{
|
||||
if (!is_app(s)) { return false; }
|
||||
app * a = to_app(s);
|
||||
func_decl * sym = a->get_decl();
|
||||
|
||||
unsigned idx;
|
||||
if (!m_parent.try_get_index(sym, idx)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(static_cast<int>(idx) + m_shift >= 0);
|
||||
func_decl * tgt = m_parent.conv(sym, idx, idx + m_shift);
|
||||
t = m.mk_app(tgt, a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const
|
||||
{
|
||||
if (dist == 0) {
|
||||
res = f;
|
||||
return;
|
||||
}
|
||||
shifting_rewriter_cfg r_cfg(*this, dist);
|
||||
rewriter_tpl<shifting_rewriter_cfg> rwr(m, false, r_cfg);
|
||||
rwr(f, res);
|
||||
}
|
||||
|
||||
void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
|
||||
expr_ref_vector & res) const
|
||||
{
|
||||
res.reset();
|
||||
expr * const * begin = vect.c_ptr();
|
||||
expr * const * end = begin + vect.size();
|
||||
for (expr * const * it = begin; it != end; it++) {
|
||||
expr_ref converted(m);
|
||||
conv_formula(*it, src_idx, tgt_idx, converted);
|
||||
res.push_back(converted);
|
||||
}
|
||||
}
|
||||
|
||||
void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
while (i < vect.size()) {
|
||||
expr* e = vect[i].get();
|
||||
if (contains(e, idx) && is_homogenous_formula(e, idx)) {
|
||||
i++;
|
||||
} else {
|
||||
//we don't allow mixing states inside vector elements
|
||||
SASSERT(!contains(e, idx));
|
||||
vect[i] = vect.back();
|
||||
vect.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sym_mux::partition_o_idx(
|
||||
expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other, unsigned idx) const
|
||||
{
|
||||
|
||||
for (unsigned i = 0; i < lits.size(); ++i) {
|
||||
if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) {
|
||||
o_lits.push_back(lits[i]);
|
||||
} else {
|
||||
other.push_back(lits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class sym_mux::nonmodel_sym_checker {
|
||||
const sym_mux & m_parent;
|
||||
|
||||
bool m_found;
|
||||
public:
|
||||
nonmodel_sym_checker(const sym_mux & parent) :
|
||||
m_parent(parent), m_found(false)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if (m_found || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
|
||||
if (m_parent.is_non_model_sym(sym)) {
|
||||
m_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool found() const
|
||||
{
|
||||
return m_found;
|
||||
}
|
||||
};
|
||||
|
||||
bool sym_mux::has_nonmodel_symbol(expr * e) const
|
||||
{
|
||||
nonmodel_sym_checker chck(*this);
|
||||
for_each_expr(chck, e);
|
||||
return chck.found();
|
||||
}
|
||||
|
||||
void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const
|
||||
{
|
||||
unsigned i = 0;
|
||||
while (i < vect.size()) {
|
||||
if (!has_nonmodel_symbol(vect[i].get())) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
vect[i] = vect.back();
|
||||
vect.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
class sym_mux::decl_idx_comparator {
|
||||
const sym_mux & m_parent;
|
||||
public:
|
||||
decl_idx_comparator(const sym_mux & parent)
|
||||
: m_parent(parent)
|
||||
{ }
|
||||
|
||||
bool operator()(func_decl * sym1, func_decl * sym2)
|
||||
{
|
||||
unsigned idx1, idx2;
|
||||
if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; }
|
||||
if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; }
|
||||
|
||||
if (idx1 != idx2) { return idx1 < idx2; }
|
||||
return lt(sym1->get_name(), sym2->get_name());
|
||||
}
|
||||
};
|
||||
|
||||
std::string sym_mux::pp_model(const model_core & mdl) const
|
||||
{
|
||||
decl_vector consts;
|
||||
unsigned sz = mdl.get_num_constants();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
func_decl * d = mdl.get_constant(i);
|
||||
consts.push_back(d);
|
||||
}
|
||||
|
||||
std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this));
|
||||
|
||||
std::stringstream res;
|
||||
|
||||
decl_vector::iterator end = consts.end();
|
||||
for (decl_vector::iterator it = consts.begin(); it != end; it++) {
|
||||
func_decl * d = *it;
|
||||
std::string name = d->get_name().str();
|
||||
const char * arrow = " -> ";
|
||||
res << name << arrow;
|
||||
unsigned indent = static_cast<unsigned>(name.length() + strlen(arrow));
|
||||
res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n";
|
||||
|
||||
if (it + 1 != end) {
|
||||
unsigned idx1, idx2;
|
||||
if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; }
|
||||
if (!try_get_index(*(it + 1), idx2)) { idx2 = UINT_MAX; }
|
||||
if (idx1 != idx2) { res << "\n"; }
|
||||
}
|
||||
}
|
||||
return res.str();
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
class sym_mux::index_renamer_cfg : public default_rewriter_cfg {
|
||||
const sym_mux & m_parent;
|
||||
unsigned m_idx;
|
||||
|
||||
public:
|
||||
index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr)
|
||||
{
|
||||
if (!is_app(s)) { return false; }
|
||||
app * a = to_app(s);
|
||||
if (a->get_family_id() != null_family_id) {
|
||||
return false;
|
||||
}
|
||||
func_decl * sym = a->get_decl();
|
||||
unsigned idx;
|
||||
if (!m_parent.try_get_index(sym, idx)) {
|
||||
return false;
|
||||
}
|
||||
if (m_idx == idx) {
|
||||
return false;
|
||||
}
|
||||
ast_manager& m = m_parent.get_manager();
|
||||
symbol name = symbol((sym->get_name().str() + "!").c_str());
|
||||
func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range());
|
||||
t = m.mk_app(tgt, a->get_num_args(), a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
256
src/muz/spacer/spacer_sym_mux.h
Normal file
256
src/muz/spacer/spacer_sym_mux.h
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sym_mux.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-8.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SYM_MUX_H_
|
||||
#define _SYM_MUX_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "util/map.h"
|
||||
#include "util/vector.h"
|
||||
#include <vector>
|
||||
|
||||
class model_core;
|
||||
|
||||
namespace spacer {
|
||||
class sym_mux {
|
||||
public:
|
||||
typedef ptr_vector<app> app_vector;
|
||||
typedef ptr_vector<func_decl> decl_vector;
|
||||
private:
|
||||
typedef obj_map<func_decl, unsigned> sym2u;
|
||||
typedef obj_map<func_decl, decl_vector> sym2dv;
|
||||
typedef obj_map<func_decl, func_decl *> sym2sym;
|
||||
typedef obj_map<func_decl, func_decl *> sym2pred;
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbols;
|
||||
|
||||
ast_manager & m;
|
||||
mutable ast_ref_vector m_ref_holder;
|
||||
mutable expr_mark m_visited;
|
||||
|
||||
mutable unsigned m_next_sym_suffix_idx;
|
||||
mutable symbols m_used_suffixes;
|
||||
/** Here we have default suffixes for each of the variants */
|
||||
mutable std::vector<std::string> m_suffixes;
|
||||
|
||||
|
||||
/**
|
||||
Primary symbol is the 0-th variant. This member maps from primary symbol
|
||||
to vector of all its variants (including the primary variant).
|
||||
*/
|
||||
sym2dv m_prim2all;
|
||||
|
||||
/**
|
||||
For each symbol contains its variant index
|
||||
*/
|
||||
mutable sym2u m_sym2idx;
|
||||
/**
|
||||
For each symbol contains its primary variant
|
||||
*/
|
||||
mutable sym2sym m_sym2prim;
|
||||
|
||||
/**
|
||||
Maps prefixes passed to the create_tuple to
|
||||
the primary symbol created from it.
|
||||
*/
|
||||
sym2pred m_prefix2prim;
|
||||
|
||||
/**
|
||||
Maps pripary symbols to prefixes that were used to create them.
|
||||
*/
|
||||
sym2sym m_prim2prefix;
|
||||
|
||||
decl_vector m_prim_preds;
|
||||
|
||||
obj_hashtable<func_decl> m_non_model_syms;
|
||||
|
||||
struct formula_checker;
|
||||
struct conv_rewriter_cfg;
|
||||
struct shifting_rewriter_cfg;
|
||||
class decl_idx_comparator;
|
||||
class hmg_checker;
|
||||
class nonmodel_sym_checker;
|
||||
class index_renamer_cfg;
|
||||
class index_collector;
|
||||
class variable_collector;
|
||||
|
||||
std::string get_suffix(unsigned i) const;
|
||||
void ensure_tuple_size(func_decl * prim, unsigned sz) const;
|
||||
|
||||
expr_ref isolate_o_idx(expr* e, unsigned idx) const;
|
||||
public:
|
||||
sym_mux(ast_manager & m, const std::vector<std::string> & suffixes);
|
||||
|
||||
ast_manager & get_manager() const { return m; }
|
||||
|
||||
bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); }
|
||||
|
||||
bool try_get_index(func_decl * sym, unsigned & idx) const
|
||||
{
|
||||
return m_sym2idx.find(sym, idx);
|
||||
}
|
||||
|
||||
bool has_index(func_decl * sym, unsigned idx) const
|
||||
{
|
||||
unsigned actual_idx;
|
||||
return try_get_index(sym, actual_idx) && idx == actual_idx;
|
||||
}
|
||||
|
||||
/** Return primary symbol. sym must be muxed. */
|
||||
func_decl * get_primary(func_decl * sym) const
|
||||
{
|
||||
func_decl * prim;
|
||||
TRUSTME(m_sym2prim.find(sym, prim));
|
||||
return prim;
|
||||
}
|
||||
|
||||
/**
|
||||
Return primary symbol created from prefix, or 0 if the prefix was never used.
|
||||
*/
|
||||
func_decl * try_get_primary_by_prefix(func_decl* prefix) const
|
||||
{
|
||||
func_decl * res;
|
||||
if(!m_prefix2prim.find(prefix, res)) {
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
Return symbol created from prefix, or 0 if the prefix was never used.
|
||||
*/
|
||||
func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const
|
||||
{
|
||||
func_decl * prim = try_get_primary_by_prefix(prefix);
|
||||
if(!prim) {
|
||||
return 0;
|
||||
}
|
||||
return conv(prim, 0, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
Marks symbol as non-model which means it will not appear in models collected by
|
||||
get_muxed_cube_from_model function.
|
||||
This is to take care of auxiliary symbols introduced by the disjunction relations
|
||||
to relativize lemmas coming from disjuncts.
|
||||
*/
|
||||
void mark_as_non_model(func_decl * sym)
|
||||
{
|
||||
SASSERT(is_muxed(sym));
|
||||
m_non_model_syms.insert(get_primary(sym));
|
||||
}
|
||||
|
||||
func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
|
||||
|
||||
|
||||
bool is_muxed_lit(expr * e, unsigned idx) const;
|
||||
|
||||
bool is_non_model_sym(func_decl * s) const
|
||||
{
|
||||
return is_muxed(s) && m_non_model_syms.contains(get_primary(s));
|
||||
}
|
||||
|
||||
/**
|
||||
Create a multiplexed tuple of propositional constants.
|
||||
Symbols may be suplied in the tuple vector,
|
||||
those beyond the size of the array and those with corresponding positions
|
||||
assigned to zero will be created using prefix.
|
||||
Tuple length must be at least one.
|
||||
*/
|
||||
void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
|
||||
unsigned tuple_length, decl_vector & tuple);
|
||||
|
||||
/**
|
||||
Return true if the only multiplexed symbols which e contains are of index idx.
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e, unsigned idx) const;
|
||||
bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Return true if all multiplexed symbols which e contains are of one index.
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e) const;
|
||||
|
||||
/**
|
||||
Return true if expression e contains a muxed symbol of index idx.
|
||||
*/
|
||||
bool contains(expr * e, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Collect indices used in expression.
|
||||
*/
|
||||
void collect_indices(expr* e, unsigned_vector& indices) const;
|
||||
|
||||
/**
|
||||
Collect used variables of each index.
|
||||
*/
|
||||
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const;
|
||||
|
||||
/**
|
||||
Convert symbol sym which has to be of src_idx variant into variant tgt_idx.
|
||||
*/
|
||||
func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const;
|
||||
|
||||
|
||||
/**
|
||||
Convert src_idx symbols in formula f variant into tgt_idx.
|
||||
If homogenous is true, formula cannot contain symbols of other variants.
|
||||
*/
|
||||
void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous = true) const;
|
||||
void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
|
||||
expr_ref_vector & res) const;
|
||||
|
||||
/**
|
||||
Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift
|
||||
symbol index to a negative value.
|
||||
*/
|
||||
void shift_formula(expr * f, int dist, expr_ref & res) const;
|
||||
|
||||
/**
|
||||
Remove from vect literals (atoms or negations of atoms) of symbols
|
||||
that contain multiplexed symbols with indexes other than idx.
|
||||
|
||||
Each of the literals can contain only symbols multiplexed with one index
|
||||
(this trivially holds if the literals are propositional).
|
||||
|
||||
Order of elements in vect may be modified by this function
|
||||
*/
|
||||
void filter_idx(expr_ref_vector & vect, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Partition literals into o_literals and others.
|
||||
*/
|
||||
void partition_o_idx(expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other, unsigned idx) const;
|
||||
|
||||
bool has_nonmodel_symbol(expr * e) const;
|
||||
void filter_non_model_lits(expr_ref_vector & vect) const;
|
||||
|
||||
func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); }
|
||||
func_decl * const * end_prim_preds() const { return m_prim_preds.end(); }
|
||||
|
||||
void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const;
|
||||
|
||||
std::string pp_model(const model_core & mdl) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
355
src/muz/spacer/spacer_unsat_core_learner.cpp
Normal file
355
src/muz/spacer/spacer_unsat_core_learner.cpp
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_unsat_core_learner.cpp
|
||||
|
||||
Abstract:
|
||||
itp cores
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#include <unordered_map>
|
||||
|
||||
#include "muz/spacer/spacer_unsat_core_learner.h"
|
||||
#include "muz/spacer/spacer_unsat_core_plugin.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
|
||||
namespace spacer
|
||||
{
|
||||
|
||||
|
||||
unsat_core_learner::~unsat_core_learner()
|
||||
{
|
||||
std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc<unsat_core_plugin>());
|
||||
|
||||
}
|
||||
|
||||
void unsat_core_learner::register_plugin(unsat_core_plugin* plugin)
|
||||
{
|
||||
m_plugins.push_back(plugin);
|
||||
}
|
||||
|
||||
void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, expr_ref_vector& unsat_core)
|
||||
{
|
||||
// transform proof in order to get a proof which is better suited for unsat-core-extraction
|
||||
proof_ref pr(root, m);
|
||||
|
||||
spacer::reduce_hypotheses(pr);
|
||||
STRACE("spacer.unsat_core_learner",
|
||||
verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";
|
||||
);
|
||||
|
||||
// compute symbols occuring in B
|
||||
collect_symbols_b(asserted_b);
|
||||
|
||||
// traverse proof
|
||||
ProofIteratorPostOrder it(root, m);
|
||||
while (it.hasNext())
|
||||
{
|
||||
proof* currentNode = it.next();
|
||||
|
||||
if (m.get_num_parents(currentNode) == 0)
|
||||
{
|
||||
switch(currentNode->get_decl_kind())
|
||||
{
|
||||
|
||||
case PR_ASSERTED: // currentNode is an axiom
|
||||
{
|
||||
if (asserted_b.contains(m.get_fact(currentNode)))
|
||||
{
|
||||
m_b_mark.mark(currentNode, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_a_mark.mark(currentNode, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// currentNode is a hypothesis:
|
||||
case PR_HYPOTHESIS:
|
||||
{
|
||||
m_h_mark.mark(currentNode, true);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis
|
||||
bool need_to_mark_a = false;
|
||||
bool need_to_mark_b = false;
|
||||
bool need_to_mark_h = false;
|
||||
bool need_to_mark_closed = true;
|
||||
|
||||
for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i)
|
||||
{
|
||||
SASSERT(m.is_proof(currentNode->get_arg(i)));
|
||||
proof* premise = to_app(currentNode->get_arg(i));
|
||||
|
||||
need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise);
|
||||
need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise);
|
||||
need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise);
|
||||
need_to_mark_closed = need_to_mark_closed && (!m_b_mark.is_marked(premise) || m_closed.is_marked(premise));
|
||||
}
|
||||
|
||||
// if current node is application of lemma, we know that all hypothesis are removed
|
||||
if(currentNode->get_decl_kind() == PR_LEMMA)
|
||||
{
|
||||
need_to_mark_h = false;
|
||||
}
|
||||
|
||||
// save results
|
||||
m_a_mark.mark(currentNode, need_to_mark_a);
|
||||
m_b_mark.mark(currentNode, need_to_mark_b);
|
||||
m_h_mark.mark(currentNode, need_to_mark_h);
|
||||
m_closed.mark(currentNode, need_to_mark_closed);
|
||||
}
|
||||
|
||||
// we have now collected all necessary information, so we can visit the node
|
||||
// if the node mixes A-reasoning and B-reasoning and contains non-closed premises
|
||||
if (m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode) && !m_closed.is_marked(currentNode))
|
||||
{
|
||||
compute_partial_core(currentNode); // then we need to compute a partial core
|
||||
// SASSERT(!(m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode)) || m_closed.is_marked(currentNode)); TODO: doesn't hold anymore if we do the mincut-thing!
|
||||
}
|
||||
}
|
||||
|
||||
// give plugins chance to finalize their unsat-core-computation
|
||||
finalize();
|
||||
|
||||
// TODO: remove duplicates from unsat core?
|
||||
|
||||
bool debug_proof = false;
|
||||
if(debug_proof)
|
||||
{
|
||||
// print proof for debugging
|
||||
verbose_stream() << "\n\nProof:\n";
|
||||
std::unordered_map<unsigned, unsigned> id_to_small_id;
|
||||
unsigned counter = 0;
|
||||
|
||||
ProofIteratorPostOrder it2(root, m);
|
||||
while (it2.hasNext())
|
||||
{
|
||||
proof* currentNode = it2.next();
|
||||
|
||||
SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end());
|
||||
id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter));
|
||||
|
||||
verbose_stream() << counter << " ";
|
||||
verbose_stream() << "[";
|
||||
if (is_a_marked(currentNode))
|
||||
{
|
||||
verbose_stream() << "a";
|
||||
}
|
||||
if (is_b_marked(currentNode))
|
||||
{
|
||||
verbose_stream() << "b";
|
||||
}
|
||||
if (is_h_marked(currentNode))
|
||||
{
|
||||
verbose_stream() << "h";
|
||||
}
|
||||
if (is_closed(currentNode))
|
||||
{
|
||||
verbose_stream() << "c";
|
||||
}
|
||||
verbose_stream() << "] ";
|
||||
|
||||
if (m.get_num_parents(currentNode) == 0)
|
||||
{
|
||||
switch (currentNode->get_decl_kind())
|
||||
{
|
||||
case PR_ASSERTED:
|
||||
verbose_stream() << "asserted";
|
||||
break;
|
||||
case PR_HYPOTHESIS:
|
||||
verbose_stream() << "hypothesis";
|
||||
break;
|
||||
default:
|
||||
verbose_stream() << "unknown axiom-type";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentNode->get_decl_kind() == PR_LEMMA)
|
||||
{
|
||||
verbose_stream() << "lemma";
|
||||
}
|
||||
else if (currentNode->get_decl_kind() == PR_TH_LEMMA)
|
||||
{
|
||||
verbose_stream() << "th_lemma";
|
||||
func_decl* d = currentNode->get_decl();
|
||||
symbol sym;
|
||||
if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
|
||||
d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
|
||||
d->get_parameter(1).is_symbol(sym) && sym == "farkas")
|
||||
{
|
||||
verbose_stream() << "(farkas)";
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose_stream() << "(other)";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose_stream() << "step";
|
||||
}
|
||||
verbose_stream() << " from ";
|
||||
for (int i = m.get_num_parents(currentNode) - 1; i >= 0 ; --i)
|
||||
{
|
||||
proof* premise = to_app(currentNode->get_arg(i));
|
||||
unsigned premise_small_id = id_to_small_id[premise->get_id()];
|
||||
if (i > 0)
|
||||
{
|
||||
verbose_stream() << premise_small_id << ", ";
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose_stream() << premise_small_id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode)))
|
||||
{
|
||||
verbose_stream() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << std::endl;
|
||||
}
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
// move all lemmas into vector
|
||||
for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it)
|
||||
{
|
||||
unsat_core.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void unsat_core_learner::compute_partial_core(proof* step)
|
||||
{
|
||||
for (unsat_core_plugin** it=m_plugins.begin(), **end = m_plugins.end (); it != end && !m_closed.is_marked(step); ++it)
|
||||
{
|
||||
unsat_core_plugin* plugin = *it;
|
||||
plugin->compute_partial_core(step);
|
||||
}
|
||||
}
|
||||
|
||||
void unsat_core_learner::finalize()
|
||||
{
|
||||
for (unsat_core_plugin** it=m_plugins.begin(); it != m_plugins.end(); ++it)
|
||||
{
|
||||
unsat_core_plugin* plugin = *it;
|
||||
plugin->finalize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool unsat_core_learner::is_a_marked(proof* p)
|
||||
{
|
||||
return m_a_mark.is_marked(p);
|
||||
}
|
||||
bool unsat_core_learner::is_b_marked(proof* p)
|
||||
{
|
||||
return m_b_mark.is_marked(p);
|
||||
}
|
||||
bool unsat_core_learner::is_h_marked(proof* p)
|
||||
{
|
||||
return m_h_mark.is_marked(p);
|
||||
}
|
||||
bool unsat_core_learner::is_closed(proof*p)
|
||||
{
|
||||
return m_closed.is_marked(p);
|
||||
}
|
||||
void unsat_core_learner::set_closed(proof* p, bool value)
|
||||
{
|
||||
m_closed.mark(p, value);
|
||||
}
|
||||
|
||||
void unsat_core_learner::add_lemma_to_core(expr* lemma)
|
||||
{
|
||||
m_unsat_core.push_back(lemma);
|
||||
}
|
||||
|
||||
|
||||
class collect_pure_proc {
|
||||
func_decl_set& m_symbs;
|
||||
public:
|
||||
collect_pure_proc(func_decl_set& s):m_symbs(s) {}
|
||||
|
||||
void operator()(app* a) {
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
m_symbs.insert(a->get_decl());
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
void unsat_core_learner::collect_symbols_b(expr_set axioms_b)
|
||||
{
|
||||
expr_mark visited;
|
||||
collect_pure_proc proc(m_symbols_b);
|
||||
for (expr_set::iterator it = axioms_b.begin(); it != axioms_b.end(); ++it)
|
||||
{
|
||||
for_each_expr(proc, visited, *it);
|
||||
}
|
||||
}
|
||||
|
||||
class is_pure_expr_proc {
|
||||
func_decl_set const& m_symbs;
|
||||
array_util m_au;
|
||||
public:
|
||||
struct non_pure {};
|
||||
|
||||
is_pure_expr_proc(func_decl_set const& s, ast_manager& m):
|
||||
m_symbs(s),
|
||||
m_au (m)
|
||||
{}
|
||||
|
||||
void operator()(app* a) {
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
if (!m_symbs.contains(a->get_decl())) {
|
||||
throw non_pure();
|
||||
}
|
||||
}
|
||||
else if (a->get_family_id () == m_au.get_family_id () &&
|
||||
a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) {
|
||||
throw non_pure();
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool unsat_core_learner::only_contains_symbols_b(expr* expr) const
|
||||
{
|
||||
is_pure_expr_proc proc(m_symbols_b, m);
|
||||
try {
|
||||
for_each_expr(proc, expr);
|
||||
}
|
||||
catch (is_pure_expr_proc::non_pure)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
107
src/muz/spacer/spacer_unsat_core_learner.h
Normal file
107
src/muz/spacer/spacer_unsat_core_learner.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_unsat_core_learner.h
|
||||
|
||||
Abstract:
|
||||
itp cores
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#ifndef _SPACER_UNSAT_CORE_LEARNER_H_
|
||||
#define _SPACER_UNSAT_CORE_LEARNER_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "muz/spacer/spacer_proof_utils.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
|
||||
class unsat_core_plugin;
|
||||
class unsat_core_learner
|
||||
{
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
|
||||
public:
|
||||
unsat_core_learner(ast_manager& m) : m(m), m_unsat_core(m) {};
|
||||
virtual ~unsat_core_learner();
|
||||
|
||||
ast_manager& m;
|
||||
|
||||
/*
|
||||
* register a plugin for computation of partial unsat cores
|
||||
* currently plugins are called in the order they have been registered
|
||||
*/
|
||||
void register_plugin(unsat_core_plugin* plugin);
|
||||
|
||||
/*
|
||||
* compute unsat core using the registered unsat-core-plugins
|
||||
*/
|
||||
void compute_unsat_core(proof* root, expr_set& asserted_b, expr_ref_vector& unsat_core);
|
||||
|
||||
/*
|
||||
* getter/setter methods for data structures exposed to plugins
|
||||
* the following invariants can be assumed and need to be maintained by the plugins:
|
||||
* - a node is a-marked iff it is derived using at least one asserted proof step from A.
|
||||
* - a node is b-marked iff its derivation contains no asserted proof steps from A and
|
||||
* no hypothesis (with the additional complication that lemmas conceptually remove hypothesis)
|
||||
* - a node is h-marked, iff it is derived using at least one hypothesis
|
||||
* - a node is closed, iff it has already been interpolated, i.e. its contribution is
|
||||
* already covered by the unsat-core.
|
||||
*/
|
||||
bool is_a_marked(proof* p);
|
||||
bool is_b_marked(proof* p);
|
||||
bool is_h_marked(proof* p);
|
||||
bool is_closed(proof* p);
|
||||
void set_closed(proof* p, bool value);
|
||||
|
||||
/*
|
||||
* adds a lemma to the unsat core
|
||||
*/
|
||||
void add_lemma_to_core(expr* lemma);
|
||||
|
||||
/*
|
||||
* helper method, which can be used by plugins
|
||||
* returns true iff all symbols of expr occur in some b-asserted formula.
|
||||
* must only be called after a call to collect_symbols_b.
|
||||
*/
|
||||
bool only_contains_symbols_b(expr* expr) const;
|
||||
bool is_b_pure (proof *p)
|
||||
{return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));}
|
||||
bool is_b_open (proof *p)
|
||||
{ return is_b_marked (p) && !is_closed (p); }
|
||||
|
||||
private:
|
||||
ptr_vector<unsat_core_plugin> m_plugins;
|
||||
func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula
|
||||
void collect_symbols_b(expr_set axioms_b);
|
||||
|
||||
ast_mark m_a_mark;
|
||||
ast_mark m_b_mark;
|
||||
ast_mark m_h_mark;
|
||||
ast_mark m_closed;
|
||||
|
||||
expr_ref_vector m_unsat_core; // collects the lemmas of the unsat-core, will at the end be inserted into unsat_core.
|
||||
|
||||
/*
|
||||
* computes partial core for step by delegating computation to plugins
|
||||
*/
|
||||
void compute_partial_core(proof* step);
|
||||
|
||||
/*
|
||||
* finalize computation of unsat-core
|
||||
*/
|
||||
void finalize();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
790
src/muz/spacer/spacer_unsat_core_plugin.cpp
Normal file
790
src/muz/spacer/spacer_unsat_core_plugin.cpp
Normal file
|
|
@ -0,0 +1,790 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_unsat_core_plugin.cpp
|
||||
|
||||
Abstract:
|
||||
plugin for itp cores
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#include <set>
|
||||
#include <limits>
|
||||
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
#include "solver/solver.h"
|
||||
|
||||
#include "smt/smt_farkas_util.h"
|
||||
#include "smt/smt_solver.h"
|
||||
|
||||
#include "muz/spacer/spacer_proof_utils.h"
|
||||
#include "muz/spacer/spacer_matrix.h"
|
||||
#include "muz/spacer/spacer_unsat_core_plugin.h"
|
||||
#include "muz/spacer/spacer_unsat_core_learner.h"
|
||||
|
||||
namespace spacer
|
||||
{
|
||||
|
||||
|
||||
void unsat_core_plugin_lemma::compute_partial_core(proof* step)
|
||||
{
|
||||
SASSERT(m_learner.is_a_marked(step));
|
||||
SASSERT(m_learner.is_b_marked(step));
|
||||
|
||||
for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(step->get_arg(i)));
|
||||
proof* premise = to_app(step->get_arg(i));
|
||||
|
||||
if (m_learner.is_b_open (premise))
|
||||
{
|
||||
// by IH, premises that are AB marked are already closed
|
||||
SASSERT(!m_learner.is_a_marked(premise));
|
||||
add_lowest_split_to_core(premise);
|
||||
}
|
||||
}
|
||||
m_learner.set_closed(step, true);
|
||||
}
|
||||
|
||||
void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const
|
||||
{
|
||||
SASSERT(m_learner.is_b_open(step));
|
||||
ast_manager &m = m_learner.m;
|
||||
|
||||
ptr_vector<proof> todo;
|
||||
todo.push_back(step);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
proof* pf = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
// if current step hasn't been processed,
|
||||
if (!m_learner.is_closed(pf))
|
||||
{
|
||||
m_learner.set_closed(pf, true);
|
||||
// the step is b-marked and not closed.
|
||||
// by I.H. the step must be already visited
|
||||
// so if it is also a-marked, it must be closed
|
||||
SASSERT(m_learner.is_b_marked(pf));
|
||||
SASSERT(!m_learner.is_a_marked(pf));
|
||||
|
||||
// the current step needs to be interpolated:
|
||||
expr* fact = m_learner.m.get_fact(pf);
|
||||
// if we trust the current step and we are able to use it
|
||||
if (m_learner.is_b_pure (pf) &&
|
||||
(m.is_asserted(pf) || is_literal(m, fact)))
|
||||
{
|
||||
// just add it to the core
|
||||
m_learner.add_lemma_to_core(fact);
|
||||
}
|
||||
// otherwise recurse on premises
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0, sz = m_learner.m.get_num_parents(pf);
|
||||
i < sz; ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(pf->get_arg(i)));
|
||||
proof* premise = m.get_parent (pf, i);
|
||||
if (m_learner.is_b_open(premise)) {
|
||||
todo.push_back(premise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step)
|
||||
{
|
||||
ast_manager &m = m_learner.m;
|
||||
SASSERT(m_learner.is_a_marked(step));
|
||||
SASSERT(m_learner.is_b_marked(step));
|
||||
// XXX this assertion should be true so there is no need to check for it
|
||||
SASSERT (!m_learner.is_closed (step));
|
||||
func_decl* d = step->get_decl();
|
||||
symbol sym;
|
||||
if(!m_learner.is_closed(step) && // if step is not already interpolated
|
||||
step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma
|
||||
d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
|
||||
d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
|
||||
d->get_parameter(1).is_symbol(sym) && sym == "farkas" &&
|
||||
d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients
|
||||
{
|
||||
SASSERT(m_learner.m.has_fact(step));
|
||||
|
||||
ptr_vector<app> literals;
|
||||
vector<rational> coefficients;
|
||||
|
||||
/* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and
|
||||
* ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which
|
||||
* is entailed by BP and together with A and BNP entails D.
|
||||
*
|
||||
* Let Fark(F) be the farkas coefficient for F. We can use the fact that
|
||||
* (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1)
|
||||
* We further have that A+B => C implies (A \land B) => C. (E2)
|
||||
*
|
||||
* Alternative 1:
|
||||
* From (E1) immediately get that BP*Fark(BP) is a solution.
|
||||
*
|
||||
* Alternative 2:
|
||||
* We can rewrite (E2) to rewrite (E1) to
|
||||
* (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3)
|
||||
* and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from
|
||||
* A, BNP and D, we also know that it is inconsisent. Therefore
|
||||
* neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution.
|
||||
*
|
||||
* Finally we also need the following workaround:
|
||||
* 1) Although we know from theory, that the Farkas coefficients are always nonnegative,
|
||||
* the Farkas coefficients provided by arith_core are sometimes negative (must be a bug)
|
||||
* as workaround we take the absolute value of the provided coefficients.
|
||||
*/
|
||||
parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient
|
||||
|
||||
STRACE("spacer.farkas",
|
||||
verbose_stream() << "Farkas input: "<< "\n";
|
||||
for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(step->get_arg(i)));
|
||||
proof *prem = m.get_parent (step, i);
|
||||
|
||||
rational coef;
|
||||
VERIFY(params[i].is_rational(coef));
|
||||
|
||||
bool b_pure = m_learner.is_b_pure (prem);
|
||||
verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n";
|
||||
}
|
||||
);
|
||||
|
||||
bool can_be_closed = true;
|
||||
|
||||
for(unsigned i = 0; i < m.get_num_parents(step); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(step->get_arg(i)));
|
||||
proof * premise = m.get_parent (step, i);
|
||||
|
||||
if (m_learner.is_b_open (premise))
|
||||
{
|
||||
SASSERT(!m_learner.is_a_marked(premise));
|
||||
|
||||
if (m_learner.is_b_pure (step))
|
||||
{
|
||||
if (!m_use_constant_from_a)
|
||||
{
|
||||
rational coefficient;
|
||||
VERIFY(params[i].is_rational(coefficient));
|
||||
literals.push_back(to_app(m_learner.m.get_fact(premise)));
|
||||
coefficients.push_back(abs(coefficient));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
can_be_closed = false;
|
||||
|
||||
if (m_use_constant_from_a)
|
||||
{
|
||||
rational coefficient;
|
||||
VERIFY(params[i].is_rational(coefficient));
|
||||
literals.push_back(to_app(m_learner.m.get_fact(premise)));
|
||||
coefficients.push_back(abs(coefficient));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_use_constant_from_a)
|
||||
{
|
||||
rational coefficient;
|
||||
VERIFY(params[i].is_rational(coefficient));
|
||||
literals.push_back(to_app(m_learner.m.get_fact(premise)));
|
||||
coefficients.push_back(abs(coefficient));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_use_constant_from_a)
|
||||
{
|
||||
params += m_learner.m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion
|
||||
|
||||
// the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations
|
||||
if (m_learner.m.get_num_parents(step) + 2 < d->get_num_parameters())
|
||||
{
|
||||
unsigned num_args = 1;
|
||||
expr* conclusion = m_learner.m.get_fact(step);
|
||||
expr* const* args = &conclusion;
|
||||
if (m_learner.m.is_or(conclusion))
|
||||
{
|
||||
app* _or = to_app(conclusion);
|
||||
num_args = _or->get_num_args();
|
||||
args = _or->get_args();
|
||||
}
|
||||
SASSERT(m_learner.m.get_num_parents(step) + 2 + num_args == d->get_num_parameters());
|
||||
|
||||
bool_rewriter brw(m_learner.m);
|
||||
for (unsigned i = 0; i < num_args; ++i)
|
||||
{
|
||||
expr* premise = args[i];
|
||||
|
||||
expr_ref negatedPremise(m_learner.m);
|
||||
brw.mk_not(premise, negatedPremise);
|
||||
literals.push_back(to_app(negatedPremise));
|
||||
|
||||
rational coefficient;
|
||||
VERIFY(params[i].is_rational(coefficient));
|
||||
coefficients.push_back(abs(coefficient));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only if all b-premises can be used directly, add the farkas core and close the step
|
||||
if (can_be_closed)
|
||||
{
|
||||
m_learner.set_closed(step, true);
|
||||
|
||||
expr_ref res(m_learner.m);
|
||||
compute_linear_combination(coefficients, literals, res);
|
||||
|
||||
m_learner.add_lemma_to_core(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res)
|
||||
{
|
||||
SASSERT(literals.size() == coefficients.size());
|
||||
|
||||
ast_manager& m = res.get_manager();
|
||||
smt::farkas_util util(m);
|
||||
if (m_use_constant_from_a)
|
||||
{
|
||||
util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints
|
||||
}
|
||||
for(unsigned i = 0; i < literals.size(); ++i)
|
||||
{
|
||||
util.add(coefficients[i], literals[i]);
|
||||
}
|
||||
if (m_use_constant_from_a)
|
||||
{
|
||||
res = util.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
expr_ref negated_linear_combination = util.get();
|
||||
res = mk_not(m, negated_linear_combination);
|
||||
}
|
||||
}
|
||||
|
||||
void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step)
|
||||
{
|
||||
SASSERT(m_learner.is_a_marked(step));
|
||||
SASSERT(m_learner.is_b_marked(step));
|
||||
|
||||
func_decl* d = step->get_decl();
|
||||
symbol sym;
|
||||
if(!m_learner.is_closed(step) && // if step is not already interpolated
|
||||
step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma
|
||||
d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step
|
||||
d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas",
|
||||
d->get_parameter(1).is_symbol(sym) && sym == "farkas" &&
|
||||
d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients
|
||||
{
|
||||
SASSERT(m_learner.m.has_fact(step));
|
||||
|
||||
vector<std::pair<app*,rational> > linear_combination; // collects all summands of the linear combination
|
||||
|
||||
parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient
|
||||
|
||||
STRACE("spacer.farkas",
|
||||
verbose_stream() << "Farkas input: "<< "\n";
|
||||
for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(step->get_arg(i)));
|
||||
proof *prem = m.get_parent (step, i);
|
||||
|
||||
rational coef;
|
||||
VERIFY(params[i].is_rational(coef));
|
||||
|
||||
bool b_pure = m_learner.is_b_pure (prem);
|
||||
verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n";
|
||||
}
|
||||
);
|
||||
|
||||
bool can_be_closed = true;
|
||||
for(unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(step->get_arg(i)));
|
||||
proof * premise = m.get_parent (step, i);
|
||||
|
||||
if (m_learner.is_b_marked(premise) && !m_learner.is_closed(premise))
|
||||
{
|
||||
SASSERT(!m_learner.is_a_marked(premise));
|
||||
|
||||
if (m_learner.only_contains_symbols_b(m_learner.m.get_fact(premise)) && !m_learner.is_h_marked(premise))
|
||||
{
|
||||
rational coefficient;
|
||||
VERIFY(params[i].is_rational(coefficient));
|
||||
linear_combination.push_back(std::make_pair(to_app(m_learner.m.get_fact(premise)), abs(coefficient)));
|
||||
}
|
||||
else
|
||||
{
|
||||
can_be_closed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only if all b-premises can be used directly, close the step and add linear combinations for later processing
|
||||
if (can_be_closed)
|
||||
{
|
||||
m_learner.set_closed(step, true);
|
||||
if (!linear_combination.empty())
|
||||
{
|
||||
m_linear_combinations.push_back(linear_combination);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct farkas_optimized_less_than_pairs
|
||||
{
|
||||
inline bool operator() (const std::pair<app*,rational>& pair1, const std::pair<app*,rational>& pair2) const
|
||||
{
|
||||
return (pair1.first->get_id() < pair2.first->get_id());
|
||||
}
|
||||
};
|
||||
|
||||
void unsat_core_plugin_farkas_lemma_optimized::finalize()
|
||||
{
|
||||
if(m_linear_combinations.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
DEBUG_CODE(
|
||||
for (auto& linear_combination : m_linear_combinations) {
|
||||
SASSERT(linear_combination.size() > 0);
|
||||
});
|
||||
|
||||
// 1. construct ordered basis
|
||||
ptr_vector<app> ordered_basis;
|
||||
obj_map<app, unsigned> map;
|
||||
unsigned counter = 0;
|
||||
for (const auto& linear_combination : m_linear_combinations)
|
||||
{
|
||||
for (const auto& pair : linear_combination)
|
||||
{
|
||||
if (!map.contains(pair.first))
|
||||
{
|
||||
ordered_basis.push_back(pair.first);
|
||||
map.insert(pair.first, counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. populate matrix
|
||||
spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size());
|
||||
|
||||
for (unsigned i=0; i < m_linear_combinations.size(); ++i)
|
||||
{
|
||||
auto linear_combination = m_linear_combinations[i];
|
||||
for (const auto& pair : linear_combination)
|
||||
{
|
||||
matrix.set(i, map[pair.first], pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. perform gaussian elimination
|
||||
unsigned i = matrix.perform_gaussian_elimination();
|
||||
|
||||
// 4. extract linear combinations from matrix and add result to core
|
||||
for (unsigned k=0; k < i; k++)// i points to the row after the last row which is non-zero
|
||||
{
|
||||
ptr_vector<app> literals;
|
||||
vector<rational> coefficients;
|
||||
for (unsigned l=0; l < matrix.num_cols(); ++l)
|
||||
{
|
||||
if (!matrix.get(k,l).is_zero())
|
||||
{
|
||||
literals.push_back(ordered_basis[l]);
|
||||
coefficients.push_back(matrix.get(k,l));
|
||||
}
|
||||
}
|
||||
SASSERT(literals.size() > 0);
|
||||
expr_ref linear_combination(m);
|
||||
compute_linear_combination(coefficients, literals, linear_combination);
|
||||
|
||||
m_learner.add_lemma_to_core(linear_combination);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res)
|
||||
{
|
||||
SASSERT(literals.size() == coefficients.size());
|
||||
|
||||
ast_manager& m = res.get_manager();
|
||||
smt::farkas_util util(m);
|
||||
for(unsigned i = 0; i < literals.size(); ++i)
|
||||
{
|
||||
util.add(coefficients[i], literals[i]);
|
||||
}
|
||||
expr_ref negated_linear_combination = util.get();
|
||||
SASSERT(m.is_not(negated_linear_combination));
|
||||
res = mk_not(m, negated_linear_combination); //TODO: rewrite the get-method to return nonnegated stuff?
|
||||
}
|
||||
|
||||
|
||||
void unsat_core_plugin_farkas_lemma_bounded::finalize() {
|
||||
if (m_linear_combinations.empty()) {
|
||||
return;
|
||||
}
|
||||
DEBUG_CODE(
|
||||
for (auto& linear_combination : m_linear_combinations) {
|
||||
SASSERT(linear_combination.size() > 0);
|
||||
});
|
||||
|
||||
// 1. construct ordered basis
|
||||
ptr_vector<app> ordered_basis;
|
||||
obj_map<app, unsigned> map;
|
||||
unsigned counter = 0;
|
||||
for (const auto& linear_combination : m_linear_combinations) {
|
||||
for (const auto& pair : linear_combination) {
|
||||
if (!map.contains(pair.first)) {
|
||||
ordered_basis.push_back(pair.first);
|
||||
map.insert(pair.first, counter++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. populate matrix
|
||||
spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size());
|
||||
|
||||
for (unsigned i=0; i < m_linear_combinations.size(); ++i) {
|
||||
auto linear_combination = m_linear_combinations[i];
|
||||
for (const auto& pair : linear_combination) {
|
||||
matrix.set(i, map[pair.first], pair.second);
|
||||
}
|
||||
}
|
||||
matrix.print_matrix();
|
||||
|
||||
// 3. normalize matrix to integer values
|
||||
matrix.normalize();
|
||||
|
||||
|
||||
arith_util util(m);
|
||||
|
||||
vector<expr_ref_vector> coeffs;
|
||||
for (unsigned i=0; i < matrix.num_rows(); ++i) {
|
||||
coeffs.push_back(expr_ref_vector(m));
|
||||
}
|
||||
|
||||
vector<expr_ref_vector> bounded_vectors;
|
||||
for (unsigned j=0; j < matrix.num_cols(); ++j)
|
||||
{
|
||||
bounded_vectors.push_back(expr_ref_vector(m));
|
||||
}
|
||||
|
||||
// 4. find smallest n using guess and check algorithm
|
||||
for(unsigned n = 1; true; ++n)
|
||||
{
|
||||
params_ref p;
|
||||
p.set_bool("model", true);
|
||||
scoped_ptr<solver> s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version?
|
||||
|
||||
// add new variables w_in,
|
||||
for (unsigned i=0; i < matrix.num_rows(); ++i)
|
||||
{
|
||||
std::string name = "w_" + std::to_string(i) + std::to_string(n);
|
||||
|
||||
func_decl_ref decl(m);
|
||||
decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int());
|
||||
coeffs[i].push_back(m.mk_const(decl));
|
||||
}
|
||||
|
||||
// we need s_jn
|
||||
for (unsigned j=0; j < matrix.num_cols(); ++j)
|
||||
{
|
||||
std::string name = "s_" + std::to_string(j) + std::to_string(n);
|
||||
|
||||
func_decl_ref decl(m);
|
||||
decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int());
|
||||
|
||||
expr_ref s_jn(m);
|
||||
s_jn = m.mk_const(decl);
|
||||
|
||||
bounded_vectors[j].push_back(s_jn);
|
||||
}
|
||||
|
||||
// assert bounds for all s_jn
|
||||
for (unsigned l=0; l < n; ++l) {
|
||||
for (unsigned j=0; j < matrix.num_cols(); ++j) {
|
||||
expr* s_jn = bounded_vectors[j][l].get();
|
||||
|
||||
expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m);
|
||||
expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m);
|
||||
s->assert_expr(lb);
|
||||
s->assert_expr(ub);
|
||||
}
|
||||
}
|
||||
|
||||
// assert: forall i,j: a_ij = sum_k w_ik * s_jk
|
||||
for (unsigned i=0; i < matrix.num_rows(); ++i)
|
||||
{
|
||||
for (unsigned j=0; j < matrix.num_cols(); ++j)
|
||||
{
|
||||
SASSERT(matrix.get(i, j).is_int());
|
||||
app_ref a_ij(util.mk_numeral(matrix.get(i,j), true),m);
|
||||
|
||||
app_ref sum(m);
|
||||
sum = util.mk_int(0);
|
||||
for (unsigned k=0; k < n; ++k)
|
||||
{
|
||||
sum = util.mk_add(sum, util.mk_mul(coeffs[i][k].get(), bounded_vectors[j][k].get()));
|
||||
}
|
||||
expr_ref eq(m.mk_eq(a_ij, sum),m);
|
||||
s->assert_expr(eq);
|
||||
}
|
||||
}
|
||||
|
||||
// check result
|
||||
lbool res = s->check_sat(0,0);
|
||||
|
||||
// if sat extract model and add corresponding linear combinations to core
|
||||
if (res == lbool::l_true) {
|
||||
model_ref model;
|
||||
s->get_model(model);
|
||||
|
||||
for (unsigned k=0; k < n; ++k) {
|
||||
ptr_vector<app> literals;
|
||||
vector<rational> coefficients;
|
||||
for (unsigned j=0; j < matrix.num_cols(); ++j) {
|
||||
expr_ref evaluation(m);
|
||||
|
||||
model.get()->eval(bounded_vectors[j][k].get(), evaluation, false);
|
||||
if (!util.is_zero(evaluation)) {
|
||||
literals.push_back(ordered_basis[j]);
|
||||
coefficients.push_back(rational(1));
|
||||
}
|
||||
}
|
||||
SASSERT(!literals.empty()); // since then previous outer loop would have found solution already
|
||||
expr_ref linear_combination(m);
|
||||
compute_linear_combination(coefficients, literals, linear_combination);
|
||||
|
||||
m_learner.add_lemma_to_core(linear_combination);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsat_core_plugin_min_cut::unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m){}
|
||||
|
||||
/*
|
||||
* traverses proof rooted in step and constructs graph, which corresponds to the proof-DAG, with the following differences:
|
||||
*
|
||||
* 1) we want to skip vertices, which have a conclusion, which we don't like (call those steps bad and the other ones good). In other words, we start at a good step r and compute the smallest subproof with root r, which has only good leaves. Then we add an edge from the root to each of the leaves and remember that we already dealt with s. Then we recurse on all leaves.
|
||||
* 2) we want to introduce two vertices for all (unskipped) edges in order to run the min-cut algorithm
|
||||
* 3) we want to introduce a single super-source vertex, which is connected to all source-vertices of the proof-DAG and a
|
||||
* single super-sink vertex, to which all sink-vertices of the proof-DAG are connected
|
||||
*
|
||||
* 1) is dealt with using advance_to_lowest_partial_cut
|
||||
* 2) and 3) are dealt with using add_edge
|
||||
*/
|
||||
void unsat_core_plugin_min_cut::compute_partial_core(proof* step)
|
||||
{
|
||||
ptr_vector<proof> todo;
|
||||
|
||||
SASSERT(m_learner.is_a_marked(step));
|
||||
SASSERT(m_learner.is_b_marked(step));
|
||||
SASSERT(m.get_num_parents(step) > 0);
|
||||
SASSERT(!m_learner.is_closed(step));
|
||||
todo.push_back(step);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
proof* current = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
// if we need to deal with the node and if we haven't added the corresponding edges so far
|
||||
if (!m_learner.is_closed(current) && !m_visited.is_marked(current))
|
||||
{
|
||||
// compute smallest subproof rooted in current, which has only good edges
|
||||
// add an edge from current to each leaf of that subproof
|
||||
// add the leaves to todo
|
||||
advance_to_lowest_partial_cut(current, todo);
|
||||
|
||||
m_visited.mark(current, true);
|
||||
|
||||
}
|
||||
}
|
||||
m_learner.set_closed(step, true);
|
||||
}
|
||||
|
||||
|
||||
void unsat_core_plugin_min_cut::advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo)
|
||||
{
|
||||
bool is_sink = true;
|
||||
|
||||
ast_manager &m = m_learner.m;
|
||||
ptr_vector<proof> todo_subproof;
|
||||
|
||||
for (unsigned i = 0, sz = m.get_num_parents(step); i < sz; ++i)
|
||||
{
|
||||
proof* premise = m.get_parent (step, i);
|
||||
{
|
||||
if (m_learner.is_b_marked(premise))
|
||||
{
|
||||
todo_subproof.push_back(premise);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!todo_subproof.empty())
|
||||
{
|
||||
proof* current = todo_subproof.back();
|
||||
todo_subproof.pop_back();
|
||||
|
||||
// if we need to deal with the node
|
||||
if (!m_learner.is_closed(current))
|
||||
{
|
||||
SASSERT(!m_learner.is_a_marked(current)); // by I.H. the step must be already visited
|
||||
|
||||
// and the current step needs to be interpolated:
|
||||
if (m_learner.is_b_marked(current))
|
||||
{
|
||||
// if we trust the current step and we are able to use it
|
||||
if (m_learner.is_b_pure (current) &&
|
||||
(m.is_asserted(current) ||
|
||||
is_literal(m, m.get_fact(current))))
|
||||
{
|
||||
// we found a leaf of the subproof, so
|
||||
// 1) we add corresponding edges
|
||||
if (m_learner.is_a_marked(step))
|
||||
{
|
||||
add_edge(nullptr, current); // current is sink
|
||||
}
|
||||
else
|
||||
{
|
||||
add_edge(step, current); // standard edge
|
||||
}
|
||||
// 2) add the leaf to todo
|
||||
todo.push_back(current);
|
||||
is_sink = false;
|
||||
}
|
||||
// otherwise continue search for leaves of subproof
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < m_learner.m.get_num_parents(current); ++i)
|
||||
{
|
||||
SASSERT(m_learner.m.is_proof(current->get_arg(i)));
|
||||
proof* premise = m.get_parent (current, i);
|
||||
todo_subproof.push_back(premise);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_sink)
|
||||
{
|
||||
add_edge(step, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adds an edge from the out-vertex of i to the in-vertex of j to the graph
|
||||
* if i or j isn't already present, it adds the corresponding in- and out-vertices to the graph
|
||||
* if i is a nullptr, it is treated as source vertex
|
||||
* if j is a nullptr, it is treated as sink vertex
|
||||
*/
|
||||
void unsat_core_plugin_min_cut::add_edge(proof* i, proof* j)
|
||||
{
|
||||
unsigned node_i;
|
||||
unsigned node_j;
|
||||
if (i == nullptr)
|
||||
{
|
||||
node_i = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned tmp;
|
||||
if (m_proof_to_node_plus.find(i, tmp))
|
||||
{
|
||||
node_i = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned node_other = m_min_cut.new_node();
|
||||
node_i = m_min_cut.new_node();
|
||||
|
||||
m_proof_to_node_minus.insert(i, node_other);
|
||||
m_proof_to_node_plus.insert(i, node_i);
|
||||
|
||||
if (node_i >= m_node_to_formula.size())
|
||||
{
|
||||
m_node_to_formula.resize(node_i + 1);
|
||||
}
|
||||
m_node_to_formula[node_other] = m.get_fact(i);
|
||||
m_node_to_formula[node_i] = m.get_fact(i);
|
||||
|
||||
m_min_cut.add_edge(node_other, node_i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (j == nullptr)
|
||||
{
|
||||
node_j = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned tmp;
|
||||
if (m_proof_to_node_minus.find(j, tmp))
|
||||
{
|
||||
node_j = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
node_j = m_min_cut.new_node();
|
||||
unsigned node_other = m_min_cut.new_node();
|
||||
|
||||
m_proof_to_node_minus.insert(j, node_j);
|
||||
m_proof_to_node_plus.insert(j, node_other);
|
||||
|
||||
if (node_other >= m_node_to_formula.size())
|
||||
{
|
||||
m_node_to_formula.resize(node_other + 1);
|
||||
}
|
||||
m_node_to_formula[node_j] = m.get_fact(j);
|
||||
m_node_to_formula[node_other] = m.get_fact(j);
|
||||
|
||||
m_min_cut.add_edge(node_j, node_other, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// finally connect nodes
|
||||
m_min_cut.add_edge(node_i, node_j, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* computes min-cut on the graph constructed by compute_partial_core-method
|
||||
* and adds the corresponding lemmas to the core
|
||||
*/
|
||||
void unsat_core_plugin_min_cut::finalize()
|
||||
{
|
||||
vector<unsigned int> cut_nodes;
|
||||
m_min_cut.compute_min_cut(cut_nodes);
|
||||
|
||||
for (unsigned cut_node : cut_nodes)
|
||||
{
|
||||
m_learner.add_lemma_to_core(m_node_to_formula[cut_node]);
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/muz/spacer/spacer_unsat_core_plugin.h
Normal file
115
src/muz/spacer/spacer_unsat_core_plugin.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_unsat_core_plugin.h
|
||||
|
||||
Abstract:
|
||||
plugin for itp cores
|
||||
|
||||
Author:
|
||||
Bernhard Gleiss
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#ifndef _SPACER_UNSAT_CORE_PLUGIN_H_
|
||||
#define _SPACER_UNSAT_CORE_PLUGIN_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "muz/spacer/spacer_min_cut.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class unsat_core_learner;
|
||||
|
||||
|
||||
class unsat_core_plugin {
|
||||
|
||||
public:
|
||||
unsat_core_plugin(unsat_core_learner& learner) : m_learner(learner){};
|
||||
virtual ~unsat_core_plugin(){};
|
||||
virtual void compute_partial_core(proof* step) = 0;
|
||||
virtual void finalize(){};
|
||||
|
||||
unsat_core_learner& m_learner;
|
||||
};
|
||||
|
||||
|
||||
class unsat_core_plugin_lemma : public unsat_core_plugin {
|
||||
|
||||
public:
|
||||
unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){};
|
||||
|
||||
virtual void compute_partial_core(proof* step) override;
|
||||
|
||||
private:
|
||||
void add_lowest_split_to_core(proof* step) const;
|
||||
};
|
||||
|
||||
|
||||
class unsat_core_plugin_farkas_lemma : public unsat_core_plugin {
|
||||
|
||||
public:
|
||||
unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, bool split_literals, bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), m_use_constant_from_a(use_constant_from_a) {};
|
||||
|
||||
virtual void compute_partial_core(proof* step) override;
|
||||
|
||||
private:
|
||||
bool m_split_literals;
|
||||
bool m_use_constant_from_a;
|
||||
/*
|
||||
* compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res
|
||||
*/
|
||||
void compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res);
|
||||
};
|
||||
|
||||
class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin {
|
||||
|
||||
public:
|
||||
unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m) {};
|
||||
|
||||
virtual void compute_partial_core(proof* step) override;
|
||||
virtual void finalize() override;
|
||||
|
||||
protected:
|
||||
vector<vector<std::pair<app*, rational> > > m_linear_combinations;
|
||||
ast_manager& m;
|
||||
/*
|
||||
* compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res
|
||||
*/
|
||||
void compute_linear_combination(const vector<rational>& coefficients, const ptr_vector<app>& literals, expr_ref& res);
|
||||
};
|
||||
|
||||
class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized {
|
||||
|
||||
public:
|
||||
unsat_core_plugin_farkas_lemma_bounded(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin_farkas_lemma_optimized(learner, m) {};
|
||||
|
||||
virtual void finalize() override;
|
||||
};
|
||||
|
||||
class unsat_core_plugin_min_cut : public unsat_core_plugin {
|
||||
|
||||
public:
|
||||
unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m);
|
||||
|
||||
virtual void compute_partial_core(proof* step) override;
|
||||
virtual void finalize() override;
|
||||
private:
|
||||
ast_manager& m;
|
||||
|
||||
ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem
|
||||
obj_map<proof, unsigned> m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source)
|
||||
obj_map<proof, unsigned> m_proof_to_node_plus; // maps proof-steps to the corresponding plus-nodes (the ones which are closer to sink)
|
||||
void advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo);
|
||||
void add_edge(proof* i, proof* j);
|
||||
|
||||
vector<expr*> m_node_to_formula; // maps each node to the corresponding formula in the original proof
|
||||
|
||||
spacer_min_cut m_min_cut;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
1393
src/muz/spacer/spacer_util.cpp
Normal file
1393
src/muz/spacer/spacer_util.cpp
Normal file
File diff suppressed because it is too large
Load diff
181
src/muz/spacer/spacer_util.h
Normal file
181
src/muz/spacer/spacer_util.h
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_util.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for SPACER.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
Arie Gurfinkel
|
||||
Anvesh Komuravelli
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_UTIL_H_
|
||||
#define _SPACER_UTIL_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "util/trace.h"
|
||||
#include "util/vector.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/expr_map.h"
|
||||
#include "model/model.h"
|
||||
|
||||
#include "util/stopwatch.h"
|
||||
#include "muz/spacer/spacer_antiunify.h"
|
||||
|
||||
class model;
|
||||
class model_core;
|
||||
class model_evaluator;
|
||||
|
||||
namespace spacer {
|
||||
|
||||
inline unsigned infty_level () {return UINT_MAX;}
|
||||
|
||||
inline bool is_infty_level(unsigned lvl)
|
||||
{ return lvl == infty_level (); }
|
||||
|
||||
inline unsigned next_level(unsigned lvl)
|
||||
{ return is_infty_level(lvl)?lvl:(lvl+1); }
|
||||
|
||||
inline unsigned prev_level (unsigned lvl)
|
||||
{
|
||||
if(is_infty_level(lvl)) { return infty_level(); }
|
||||
if(lvl == 0) { return 0; }
|
||||
return lvl -1;
|
||||
}
|
||||
|
||||
struct pp_level {
|
||||
unsigned m_level;
|
||||
pp_level(unsigned l): m_level(l) {}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, pp_level const& p)
|
||||
{
|
||||
if (is_infty_level(p.m_level)) {
|
||||
return out << "oo";
|
||||
} else {
|
||||
return out << p.m_level;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct scoped_watch {
|
||||
stopwatch &m_sw;
|
||||
scoped_watch (stopwatch &sw, bool reset=false): m_sw(sw)
|
||||
{
|
||||
if(reset) { m_sw.reset(); }
|
||||
m_sw.start ();
|
||||
}
|
||||
~scoped_watch () {m_sw.stop ();}
|
||||
};
|
||||
|
||||
|
||||
typedef ptr_vector<app> app_vector;
|
||||
typedef ptr_vector<func_decl> decl_vector;
|
||||
typedef obj_hashtable<func_decl> func_decl_set;
|
||||
|
||||
|
||||
class model_evaluator_util {
|
||||
ast_manager& m;
|
||||
model_ref m_model;
|
||||
model_evaluator* m_mev;
|
||||
|
||||
/// initialize with a given model. All previous state is lost. model can be NULL
|
||||
void reset (model *model);
|
||||
public:
|
||||
model_evaluator_util(ast_manager& m);
|
||||
~model_evaluator_util();
|
||||
|
||||
void set_model(model &model) {reset (&model);}
|
||||
model_ref &get_model() {return m_model;}
|
||||
ast_manager& get_ast_manager() const {return m;}
|
||||
|
||||
public:
|
||||
bool is_true (const expr_ref_vector &v);
|
||||
bool is_false(expr* x);
|
||||
bool is_true(expr* x);
|
||||
|
||||
bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion);
|
||||
/// evaluates an expression
|
||||
bool eval (expr *e, expr_ref &result, bool model_completion);
|
||||
// expr_ref eval(expr* e, bool complete=true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
\brief replace variables that are used in many disequalities by
|
||||
an equality using the model.
|
||||
|
||||
Assumption: the model satisfies the conjunctions.
|
||||
*/
|
||||
void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml);
|
||||
|
||||
/**
|
||||
\brief hoist non-boolean if expressions.
|
||||
*/
|
||||
void hoist_non_bool_if(expr_ref& fml);
|
||||
|
||||
bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
|
||||
|
||||
bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
|
||||
|
||||
/**
|
||||
* do the following in sequence
|
||||
* 1. use qe_lite to cheaply eliminate vars
|
||||
* 2. for remaining boolean vars, substitute using M
|
||||
* 3. use MBP for remaining array and arith variables
|
||||
* 4. for any remaining arith variables, substitute using M
|
||||
*/
|
||||
void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml,
|
||||
const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false,
|
||||
bool dont_sub=false);
|
||||
|
||||
void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map);
|
||||
|
||||
void expand_literals(ast_manager &m, expr_ref_vector& conjs);
|
||||
void compute_implicant_literals (model_evaluator_util &mev,
|
||||
expr_ref_vector &formula, expr_ref_vector &res);
|
||||
void simplify_bounds (expr_ref_vector &lemmas);
|
||||
void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false);
|
||||
|
||||
/** ground expression by replacing all free variables by skolem constants */
|
||||
void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars);
|
||||
|
||||
|
||||
void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml);
|
||||
|
||||
bool contains_selects (expr* fml, ast_manager& m);
|
||||
void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m);
|
||||
|
||||
void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix);
|
||||
|
||||
/** extended pretty-printer
|
||||
* used for debugging
|
||||
* disables aliasing of common sub-expressions
|
||||
*/
|
||||
struct mk_epp : public mk_pp {
|
||||
params_ref m_epp_params;
|
||||
expr_ref m_epp_expr;
|
||||
mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0);
|
||||
void rw(expr *e, expr_ref &out);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
519
src/muz/spacer/spacer_virtual_solver.cpp
Normal file
519
src/muz/spacer/spacer_virtual_solver.cpp
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_virtual_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
multi-solver view of a single smt::kernel
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/spacer/spacer_virtual_solver.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp_util.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
|
||||
#include "ast/proof_checker/proof_checker.h"
|
||||
|
||||
#include "ast/scoped_proof.h"
|
||||
|
||||
namespace spacer {
|
||||
virtual_solver::virtual_solver(virtual_solver_factory &factory,
|
||||
smt::kernel &context, app* pred) :
|
||||
solver_na2as(context.m()),
|
||||
m_factory(factory),
|
||||
m(context.m()),
|
||||
m_context(context),
|
||||
m_pred(pred, m),
|
||||
m_virtual(!m.is_true(pred)),
|
||||
m_assertions(m),
|
||||
m_head(0),
|
||||
m_flat(m),
|
||||
m_pushed(false),
|
||||
m_in_delay_scope(false),
|
||||
m_dump_benchmarks(factory.fparams().m_dump_benchmarks),
|
||||
m_dump_counter(0),
|
||||
m_proof(m)
|
||||
{
|
||||
// -- insert m_pred->true background assumption this will not
|
||||
// -- change m_context, but will add m_pred to
|
||||
// -- the private field solver_na2as::m_assumptions
|
||||
if (m_virtual)
|
||||
{ solver_na2as::assert_expr(m.mk_true(), m_pred); }
|
||||
}
|
||||
|
||||
virtual_solver::~virtual_solver()
|
||||
{
|
||||
SASSERT(!m_pushed || get_scope_level() > 0);
|
||||
if (m_pushed) { pop(get_scope_level()); }
|
||||
|
||||
if (m_virtual) {
|
||||
m_pred = m.mk_not(m_pred);
|
||||
m_context.assert_expr(m_pred);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
static bool matches_fact(expr_ref_vector &args, expr* &match)
|
||||
{
|
||||
ast_manager &m = args.get_manager();
|
||||
expr *fact = args.back();
|
||||
for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) {
|
||||
expr *arg = args.get(i);
|
||||
if (m.is_proof(arg) &&
|
||||
m.has_fact(to_app(arg)) &&
|
||||
m.get_fact(to_app(arg)) == fact) {
|
||||
match = arg;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class elim_aux_assertions {
|
||||
app_ref m_aux;
|
||||
public:
|
||||
elim_aux_assertions(app_ref aux) : m_aux(aux) {}
|
||||
|
||||
void mk_or_core(expr_ref_vector &args, expr_ref &res)
|
||||
{
|
||||
ast_manager &m = args.get_manager();
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0, sz = args.size(); i < sz; ++i) {
|
||||
if (m.is_false(args.get(i))) { continue; }
|
||||
if (i != j) { args [j] = args.get(i); }
|
||||
++j;
|
||||
}
|
||||
SASSERT(j >= 1);
|
||||
res = j > 1 ? m.mk_or(j, args.c_ptr()) : args.get(0);
|
||||
}
|
||||
|
||||
void mk_app(func_decl *decl, expr_ref_vector &args, expr_ref &res)
|
||||
{
|
||||
ast_manager &m = args.get_manager();
|
||||
bool_rewriter brwr(m);
|
||||
|
||||
if (m.is_or(decl))
|
||||
{ mk_or_core(args, res); }
|
||||
else if (m.is_iff(decl) && args.size() == 2)
|
||||
// avoiding simplifying equalities. In particular,
|
||||
// we don't want (= (not a) (not b)) to be reduced to (= a b)
|
||||
{ res = m.mk_iff(args.get(0), args.get(1)); }
|
||||
else
|
||||
{ brwr.mk_app(decl, args.size(), args.c_ptr(), res); }
|
||||
}
|
||||
|
||||
void operator()(ast_manager &m, proof *pr, proof_ref &res)
|
||||
{
|
||||
DEBUG_CODE(proof_checker pc(m);
|
||||
expr_ref_vector side(m);
|
||||
SASSERT(pc.check(pr, side));
|
||||
);
|
||||
obj_map<app, app*> cache;
|
||||
bool_rewriter brwr(m);
|
||||
|
||||
// for reference counting of new proofs
|
||||
app_ref_vector pinned(m);
|
||||
|
||||
ptr_vector<app> todo;
|
||||
todo.push_back(pr);
|
||||
|
||||
expr_ref not_aux(m);
|
||||
not_aux = m.mk_not(m_aux);
|
||||
|
||||
expr_ref_vector args(m);
|
||||
|
||||
while (!todo.empty()) {
|
||||
app *p, *r;
|
||||
expr *a;
|
||||
|
||||
p = todo.back();
|
||||
if (cache.find(pr, r)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
SASSERT(!todo.empty() || pr == p);
|
||||
bool dirty = false;
|
||||
unsigned todo_sz = todo.size();
|
||||
args.reset();
|
||||
for (unsigned i = 0, sz = p->get_num_args(); i < sz; ++i) {
|
||||
expr* arg = p->get_arg(i);
|
||||
if (arg == m_aux.get()) {
|
||||
dirty = true;
|
||||
args.push_back(m.mk_true());
|
||||
} else if (arg == not_aux.get()) {
|
||||
dirty = true;
|
||||
args.push_back(m.mk_false());
|
||||
}
|
||||
// skip (asserted m_aux)
|
||||
else if (m.is_asserted(arg, a) && a == m_aux.get()) {
|
||||
dirty = true;
|
||||
}
|
||||
// skip (hypothesis m_aux)
|
||||
else if (m.is_hypothesis(arg, a) && a == m_aux.get()) {
|
||||
dirty = true;
|
||||
} else if (is_app(arg) && cache.find(to_app(arg), r)) {
|
||||
dirty |= (arg != r);
|
||||
args.push_back(r);
|
||||
} else if (is_app(arg))
|
||||
{ todo.push_back(to_app(arg)); }
|
||||
else
|
||||
// -- not an app
|
||||
{ args.push_back(arg); }
|
||||
|
||||
}
|
||||
if (todo_sz < todo.size()) {
|
||||
// -- process parents
|
||||
args.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
// ready to re-create
|
||||
app_ref newp(m);
|
||||
if (!dirty) { newp = p; }
|
||||
else if (m.is_unit_resolution(p)) {
|
||||
if (args.size() == 2)
|
||||
// unit resolution with m_aux that got collapsed to nothing
|
||||
{ newp = to_app(args.get(0)); }
|
||||
else {
|
||||
ptr_vector<proof> parents;
|
||||
for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i)
|
||||
{ parents.push_back(to_app(args.get(i))); }
|
||||
SASSERT(parents.size() == args.size() - 1);
|
||||
newp = m.mk_unit_resolution(parents.size(), parents.c_ptr());
|
||||
// XXX the old and new facts should be
|
||||
// equivalent. The test here is much
|
||||
// stronger. It might need to be relaxed.
|
||||
SASSERT(m.get_fact(newp) == args.back());
|
||||
pinned.push_back(newp);
|
||||
}
|
||||
} else if (matches_fact(args, a)) {
|
||||
newp = to_app(a);
|
||||
} else {
|
||||
expr_ref papp(m);
|
||||
mk_app(p->get_decl(), args, papp);
|
||||
newp = to_app(papp.get());
|
||||
pinned.push_back(newp);
|
||||
}
|
||||
cache.insert(p, newp);
|
||||
todo.pop_back();
|
||||
CTRACE("virtual",
|
||||
p->get_decl_kind() == PR_TH_LEMMA &&
|
||||
p->get_decl()->get_parameter(0).get_symbol() == "arith" &&
|
||||
p->get_decl()->get_num_parameters() > 1 &&
|
||||
p->get_decl()->get_parameter(1).get_symbol() == "farkas",
|
||||
tout << "Old pf: " << mk_pp(p, m) << "\n"
|
||||
<< "New pf: " << mk_pp(newp, m) << "\n";);
|
||||
}
|
||||
|
||||
proof *r;
|
||||
VERIFY(cache.find(pr, r));
|
||||
|
||||
DEBUG_CODE(
|
||||
proof_checker pc(m);
|
||||
expr_ref_vector side(m);
|
||||
SASSERT(pc.check(r, side));
|
||||
);
|
||||
|
||||
res = r ;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
proof *virtual_solver::get_proof()
|
||||
{
|
||||
scoped_watch _t_(m_factory.m_proof_watch);
|
||||
|
||||
if (!m_proof.get()) {
|
||||
elim_aux_assertions pc(m_pred);
|
||||
m_proof = m_context.get_proof();
|
||||
pc(m, m_proof.get(), m_proof);
|
||||
}
|
||||
return m_proof.get();
|
||||
}
|
||||
|
||||
bool virtual_solver::is_aux_predicate(expr *p)
|
||||
{return is_app(p) && to_app(p) == m_pred.get();}
|
||||
|
||||
lbool virtual_solver::check_sat_core(unsigned num_assumptions,
|
||||
expr *const * assumptions)
|
||||
{
|
||||
SASSERT(!m_pushed || get_scope_level() > 0);
|
||||
m_proof.reset();
|
||||
scoped_watch _t_(m_factory.m_check_watch);
|
||||
m_factory.m_stats.m_num_smt_checks++;
|
||||
|
||||
stopwatch sw;
|
||||
sw.start();
|
||||
internalize_assertions();
|
||||
if (false) {
|
||||
std::stringstream file_name;
|
||||
file_name << "virt_solver";
|
||||
if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); }
|
||||
file_name << "_" << (m_dump_counter++) << ".smt2";
|
||||
|
||||
verbose_stream() << "Dumping SMT2 benchmark: " << file_name.str() << "\n";
|
||||
|
||||
std::ofstream out(file_name.str().c_str());
|
||||
|
||||
to_smt2_benchmark(out, m_context, num_assumptions, assumptions,
|
||||
"virt_solver");
|
||||
|
||||
out << "(exit)\n";
|
||||
out.close();
|
||||
}
|
||||
lbool res = m_context.check(num_assumptions, assumptions);
|
||||
sw.stop();
|
||||
if (res == l_true) {
|
||||
m_factory.m_check_sat_watch.add(sw);
|
||||
m_factory.m_stats.m_num_sat_smt_checks++;
|
||||
} else if (res == l_undef) {
|
||||
m_factory.m_check_undef_watch.add(sw);
|
||||
m_factory.m_stats.m_num_undef_smt_checks++;
|
||||
}
|
||||
set_status(res);
|
||||
|
||||
if (m_dump_benchmarks &&
|
||||
sw.get_seconds() >= m_factory.fparams().m_dump_min_time) {
|
||||
std::stringstream file_name;
|
||||
file_name << "virt_solver";
|
||||
if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); }
|
||||
file_name << "_" << (m_dump_counter++) << ".smt2";
|
||||
|
||||
std::ofstream out(file_name.str().c_str());
|
||||
|
||||
|
||||
out << "(set-info :status ";
|
||||
if (res == l_true) { out << "sat"; }
|
||||
else if (res == l_false) { out << "unsat"; }
|
||||
else { out << "unknown"; }
|
||||
out << ")\n";
|
||||
|
||||
to_smt2_benchmark(out, m_context, num_assumptions, assumptions,
|
||||
"virt_solver");
|
||||
|
||||
out << "(exit)\n";
|
||||
::statistics st;
|
||||
m_context.collect_statistics(st);
|
||||
st.update("time", sw.get_seconds());
|
||||
st.display_smt2(out);
|
||||
|
||||
out.close();
|
||||
|
||||
if (m_factory.fparams().m_dump_recheck) {
|
||||
scoped_no_proof _no_proof_(m);
|
||||
smt_params p;
|
||||
stopwatch sw2;
|
||||
smt::kernel kernel(m, p);
|
||||
for (unsigned i = 0, sz = m_context.size(); i < sz; ++i)
|
||||
{ kernel.assert_expr(m_context.get_formula(i)); }
|
||||
sw2.start();
|
||||
kernel.check(num_assumptions, assumptions);
|
||||
sw2.stop();
|
||||
verbose_stream() << file_name.str() << " :orig "
|
||||
<< sw.get_seconds() << " :new " << sw2.get_seconds();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void virtual_solver::push_core()
|
||||
{
|
||||
SASSERT(!m_pushed || get_scope_level() > 0);
|
||||
if (m_in_delay_scope) {
|
||||
// second push
|
||||
internalize_assertions();
|
||||
m_context.push();
|
||||
m_pushed = true;
|
||||
m_in_delay_scope = false;
|
||||
}
|
||||
|
||||
if (!m_pushed) { m_in_delay_scope = true; }
|
||||
else {
|
||||
SASSERT(m_pushed);
|
||||
SASSERT(!m_in_delay_scope);
|
||||
m_context.push();
|
||||
}
|
||||
}
|
||||
void virtual_solver::pop_core(unsigned n)
|
||||
{
|
||||
SASSERT(!m_pushed || get_scope_level() > 0);
|
||||
if (m_pushed) {
|
||||
SASSERT(!m_in_delay_scope);
|
||||
m_context.pop(n);
|
||||
m_pushed = get_scope_level() - n > 0;
|
||||
} else
|
||||
{ m_in_delay_scope = get_scope_level() - n > 0; }
|
||||
}
|
||||
|
||||
void virtual_solver::get_unsat_core(ptr_vector<expr> &r)
|
||||
{
|
||||
for (unsigned i = 0, sz = m_context.get_unsat_core_size(); i < sz; ++i) {
|
||||
expr *core = m_context.get_unsat_core_expr(i);
|
||||
if (is_aux_predicate(core)) { continue; }
|
||||
r.push_back(core);
|
||||
}
|
||||
}
|
||||
|
||||
void virtual_solver::assert_expr(expr *e)
|
||||
{
|
||||
SASSERT(!m_pushed || get_scope_level() > 0);
|
||||
if (m.is_true(e)) { return; }
|
||||
if (m_in_delay_scope) {
|
||||
internalize_assertions();
|
||||
m_context.push();
|
||||
m_pushed = true;
|
||||
m_in_delay_scope = false;
|
||||
}
|
||||
|
||||
if (m_pushed)
|
||||
{ m_context.assert_expr(e); }
|
||||
else {
|
||||
m_flat.push_back(e);
|
||||
flatten_and(m_flat);
|
||||
m_assertions.append(m_flat);
|
||||
m_flat.reset();
|
||||
}
|
||||
}
|
||||
void virtual_solver::internalize_assertions()
|
||||
{
|
||||
SASSERT(!m_pushed || m_head == m_assertions.size());
|
||||
for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) {
|
||||
expr_ref f(m);
|
||||
f = m.mk_implies(m_pred, (m_assertions.get(m_head)));
|
||||
m_context.assert_expr(f);
|
||||
}
|
||||
}
|
||||
void virtual_solver::refresh()
|
||||
{
|
||||
SASSERT(!m_pushed);
|
||||
m_head = 0;
|
||||
}
|
||||
|
||||
void virtual_solver::reset()
|
||||
{
|
||||
SASSERT(!m_pushed);
|
||||
m_head = 0;
|
||||
m_assertions.reset();
|
||||
m_factory.refresh();
|
||||
}
|
||||
|
||||
void virtual_solver::get_labels(svector<symbol> &r)
|
||||
{
|
||||
r.reset();
|
||||
buffer<symbol> tmp;
|
||||
m_context.get_relevant_labels(0, tmp);
|
||||
r.append(tmp.size(), tmp.c_ptr());
|
||||
}
|
||||
|
||||
solver* virtual_solver::translate(ast_manager& m, params_ref const& p)
|
||||
{
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
void virtual_solver::updt_params(params_ref const &p)
|
||||
{ m_factory.updt_params(p); }
|
||||
void virtual_solver::collect_param_descrs(param_descrs &r)
|
||||
{ m_factory.collect_param_descrs(r); }
|
||||
void virtual_solver::set_produce_models(bool f)
|
||||
{ m_factory.set_produce_models(f); }
|
||||
bool virtual_solver::get_produce_models()
|
||||
{return m_factory.get_produce_models(); }
|
||||
smt_params &virtual_solver::fparams()
|
||||
{return m_factory.fparams();}
|
||||
|
||||
void virtual_solver::to_smt2_benchmark(std::ostream &out,
|
||||
smt::kernel &context,
|
||||
unsigned num_assumptions,
|
||||
expr * const * assumptions,
|
||||
char const * name,
|
||||
symbol const &logic,
|
||||
char const * status,
|
||||
char const * attributes)
|
||||
{
|
||||
ast_pp_util pp(m);
|
||||
expr_ref_vector asserts(m);
|
||||
|
||||
|
||||
for (unsigned i = 0, sz = context.size(); i < sz; ++i) {
|
||||
asserts.push_back(context.get_formula(i));
|
||||
pp.collect(asserts.back());
|
||||
}
|
||||
pp.collect(num_assumptions, assumptions);
|
||||
pp.display_decls(out);
|
||||
pp.display_asserts(out, asserts);
|
||||
out << "(check-sat ";
|
||||
for (unsigned i = 0; i < num_assumptions; ++i)
|
||||
{ out << mk_pp(assumptions[i], m) << " "; }
|
||||
out << ")\n";
|
||||
}
|
||||
|
||||
|
||||
virtual_solver_factory::virtual_solver_factory(ast_manager &mgr, smt_params &fparams) :
|
||||
m_fparams(fparams), m(mgr), m_context(m, m_fparams)
|
||||
{
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
virtual_solver* virtual_solver_factory::mk_solver()
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "vsolver#" << m_solvers.size();
|
||||
app_ref pred(m);
|
||||
pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
|
||||
SASSERT(m_context.get_scope_level() == 0);
|
||||
m_solvers.push_back(alloc(virtual_solver, *this, m_context, pred));
|
||||
return m_solvers.back();
|
||||
}
|
||||
|
||||
void virtual_solver_factory::collect_statistics(statistics &st) const
|
||||
{
|
||||
m_context.collect_statistics(st);
|
||||
st.update("time.virtual_solver.smt.total", m_check_watch.get_seconds());
|
||||
st.update("time.virtual_solver.smt.total.sat", m_check_sat_watch.get_seconds());
|
||||
st.update("time.virtual_solver.smt.total.undef", m_check_undef_watch.get_seconds());
|
||||
st.update("time.virtual_solver.proof", m_proof_watch.get_seconds());
|
||||
st.update("virtual_solver.checks", m_stats.m_num_smt_checks);
|
||||
st.update("virtual_solver.checks.sat", m_stats.m_num_sat_smt_checks);
|
||||
st.update("virtual_solver.checks.undef", m_stats.m_num_undef_smt_checks);
|
||||
}
|
||||
void virtual_solver_factory::reset_statistics()
|
||||
{
|
||||
m_context.reset_statistics();
|
||||
m_stats.reset();
|
||||
m_check_sat_watch.reset();
|
||||
m_check_undef_watch.reset();
|
||||
m_check_watch.reset();
|
||||
m_proof_watch.reset();
|
||||
}
|
||||
|
||||
void virtual_solver_factory::refresh()
|
||||
{
|
||||
m_context.reset();
|
||||
for (unsigned i = 0, e = m_solvers.size(); i < e; ++i)
|
||||
{ m_solvers [i]->refresh(); }
|
||||
}
|
||||
|
||||
virtual_solver_factory::~virtual_solver_factory()
|
||||
{
|
||||
for (unsigned i = 0, e = m_solvers.size(); i < e; ++i)
|
||||
{ dealloc(m_solvers [i]); }
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
155
src/muz/spacer/spacer_virtual_solver.h
Normal file
155
src/muz/spacer/spacer_virtual_solver.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_virtual_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
multi-solver view of a single smt::kernel
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef SPACER_VIRTUAL_SOLVER_H_
|
||||
#define SPACER_VIRTUAL_SOLVER_H_
|
||||
#include"ast/ast.h"
|
||||
#include"util/params.h"
|
||||
#include"solver/solver_na2as.h"
|
||||
#include"smt/smt_kernel.h"
|
||||
#include"smt/params/smt_params.h"
|
||||
#include"util/stopwatch.h"
|
||||
namespace spacer {
|
||||
class virtual_solver_factory;
|
||||
|
||||
class virtual_solver : public solver_na2as {
|
||||
friend class virtual_solver_factory;
|
||||
|
||||
private:
|
||||
virtual_solver_factory &m_factory;
|
||||
ast_manager &m;
|
||||
smt::kernel &m_context;
|
||||
app_ref m_pred;
|
||||
|
||||
bool m_virtual;
|
||||
expr_ref_vector m_assertions;
|
||||
unsigned m_head;
|
||||
// temporary to flatten conjunction
|
||||
expr_ref_vector m_flat;
|
||||
|
||||
bool m_pushed;
|
||||
bool m_in_delay_scope;
|
||||
bool m_dump_benchmarks;
|
||||
unsigned m_dump_counter;
|
||||
|
||||
proof_ref m_proof;
|
||||
|
||||
virtual_solver(virtual_solver_factory &factory, smt::kernel &context, app* pred);
|
||||
|
||||
bool is_aux_predicate(expr *p);
|
||||
void internalize_assertions();
|
||||
void to_smt2_benchmark(std::ostream &out,
|
||||
smt::kernel &context,
|
||||
unsigned num_assumptions,
|
||||
expr * const * assumptions,
|
||||
char const * name = "benchmarks",
|
||||
symbol const &logic = symbol::null,
|
||||
char const * status = "unknown",
|
||||
char const * attributes = "");
|
||||
|
||||
void refresh();
|
||||
|
||||
public:
|
||||
virtual ~virtual_solver();
|
||||
virtual unsigned get_num_assumptions() const
|
||||
{
|
||||
unsigned sz = solver_na2as::get_num_assumptions();
|
||||
return m_virtual ? sz - 1 : sz;
|
||||
}
|
||||
virtual expr* get_assumption(unsigned idx) const
|
||||
{
|
||||
if(m_virtual) { idx++; }
|
||||
return solver_na2as::get_assumption(idx);
|
||||
}
|
||||
|
||||
virtual void get_unsat_core(ptr_vector<expr> &r);
|
||||
virtual void assert_expr(expr *e);
|
||||
virtual void collect_statistics(statistics &st) const {}
|
||||
virtual void get_model(model_ref &m) {m_context.get_model(m);}
|
||||
virtual proof* get_proof();
|
||||
virtual std::string reason_unknown() const
|
||||
{return m_context.last_failure_as_string();}
|
||||
virtual void set_reason_unknown(char const *msg)
|
||||
{m_context.set_reason_unknown(msg);}
|
||||
virtual ast_manager& get_manager() const {return m;}
|
||||
virtual void get_labels(svector<symbol> &r);
|
||||
virtual void set_produce_models(bool f);
|
||||
virtual bool get_produce_models();
|
||||
virtual smt_params &fparams();
|
||||
virtual void reset();
|
||||
|
||||
virtual void set_progress_callback(progress_callback *callback)
|
||||
{UNREACHABLE();}
|
||||
virtual void assert_lemma(expr* e) { NOT_IMPLEMENTED_YET(); }
|
||||
virtual expr_ref lookahead(const expr_ref_vector &,const expr_ref_vector &) { return expr_ref(m.mk_true(), m); }
|
||||
|
||||
virtual solver *translate(ast_manager &m, params_ref const &p);
|
||||
|
||||
virtual void updt_params(params_ref const &p);
|
||||
virtual void collect_param_descrs(param_descrs &r);
|
||||
|
||||
|
||||
protected:
|
||||
virtual lbool check_sat_core(unsigned num_assumptions, expr *const * assumptions);
|
||||
virtual void push_core();
|
||||
virtual void pop_core(unsigned n);
|
||||
};
|
||||
|
||||
/// multi-solver abstraction on top of a single smt::kernel
|
||||
class virtual_solver_factory {
|
||||
friend class virtual_solver;
|
||||
private:
|
||||
smt_params &m_fparams;
|
||||
ast_manager &m;
|
||||
smt::kernel m_context;
|
||||
/// solvers managed by this factory
|
||||
ptr_vector<virtual_solver> m_solvers;
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_smt_checks;
|
||||
unsigned m_num_sat_smt_checks;
|
||||
unsigned m_num_undef_smt_checks;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
stats m_stats;
|
||||
stopwatch m_check_watch;
|
||||
stopwatch m_check_sat_watch;
|
||||
stopwatch m_check_undef_watch;
|
||||
stopwatch m_proof_watch;
|
||||
|
||||
|
||||
void refresh();
|
||||
public:
|
||||
virtual_solver_factory(ast_manager &mgr, smt_params &fparams);
|
||||
virtual ~virtual_solver_factory();
|
||||
virtual_solver* mk_solver();
|
||||
void collect_statistics(statistics &st) const;
|
||||
void reset_statistics();
|
||||
void updt_params(params_ref const &p) { m_fparams.updt_params(p); }
|
||||
void collect_param_descrs(param_descrs &r) { /* empty */ }
|
||||
void set_produce_models(bool f) { m_fparams.m_model = f; }
|
||||
bool get_produce_models() { return m_fparams.m_model; }
|
||||
smt_params &fparams() { return m_fparams; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue