mirror of
https://github.com/Z3Prover/z3
synced 2025-04-14 21:08:46 +00:00
remove pdr
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
cefdb8c01d
commit
6adaed718f
|
@ -81,7 +81,6 @@ add_subdirectory(muz/base)
|
|||
add_subdirectory(muz/dataflow)
|
||||
add_subdirectory(muz/transforms)
|
||||
add_subdirectory(muz/rel)
|
||||
add_subdirectory(muz/pdr)
|
||||
add_subdirectory(muz/clp)
|
||||
add_subdirectory(muz/tab)
|
||||
add_subdirectory(muz/bmc)
|
||||
|
|
|
@ -188,9 +188,7 @@ namespace datalog {
|
|||
if (m_trail.get_num_scopes() == 0) {
|
||||
throw default_exception("there are no backtracking points to pop to");
|
||||
}
|
||||
if (m_engine.get()) {
|
||||
throw default_exception("pop operation is only supported by duality engine");
|
||||
}
|
||||
throw default_exception("pop operation is not supported");
|
||||
m_trail.pop_scope(1);
|
||||
}
|
||||
|
||||
|
@ -576,17 +574,11 @@ namespace datalog {
|
|||
m_rule_properties.check_infinite_sorts();
|
||||
break;
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
break;
|
||||
case QPDR_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
break;
|
||||
case BMC_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
|
@ -776,19 +768,14 @@ namespace datalog {
|
|||
DL_ENGINE get_engine() const { return m_engine_type; }
|
||||
|
||||
void operator()(expr* e) {
|
||||
if (is_quantifier(e)) {
|
||||
m_engine_type = QPDR_ENGINE;
|
||||
}
|
||||
else if (m_engine_type != QPDR_ENGINE) {
|
||||
if (a.is_int_real(e)) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (is_var(e) && m.is_bool(e)) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (dt.is_datatype(m.get_sort(e))) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
}
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -805,12 +792,6 @@ namespace datalog {
|
|||
else if (e == symbol("spacer")) {
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (e == symbol("pdr")) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
}
|
||||
else if (e == symbol("qpdr")) {
|
||||
m_engine_type = QPDR_ENGINE;
|
||||
}
|
||||
else if (e == symbol("bmc")) {
|
||||
m_engine_type = BMC_ENGINE;
|
||||
}
|
||||
|
@ -858,8 +839,6 @@ namespace datalog {
|
|||
switch (get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
case BMC_ENGINE:
|
||||
case QBMC_ENGINE:
|
||||
case TAB_ENGINE:
|
||||
|
@ -882,8 +861,6 @@ namespace datalog {
|
|||
switch (get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
case BMC_ENGINE:
|
||||
case QBMC_ENGINE:
|
||||
case TAB_ENGINE:
|
||||
|
|
|
@ -25,9 +25,7 @@ Revision History:
|
|||
namespace datalog {
|
||||
enum DL_ENGINE {
|
||||
DATALOG_ENGINE,
|
||||
PDR_ENGINE,
|
||||
SPACER_ENGINE,
|
||||
QPDR_ENGINE,
|
||||
BMC_ENGINE,
|
||||
QBMC_ENGINE,
|
||||
TAB_ENGINE,
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
def_module_params('fixedpoint',
|
||||
def_module_params('fixedpoint',
|
||||
description='fixedpoint parameters',
|
||||
export=True,
|
||||
params=(('timeout', UINT, UINT_MAX, 'set timeout'),
|
||||
('engine', SYMBOL, 'auto-config',
|
||||
'Select: auto-config, datalog, spacer, pdr, bmc'),
|
||||
('datalog.default_table', SYMBOL, 'sparse',
|
||||
('engine', SYMBOL, 'auto-config',
|
||||
'Select: auto-config, datalog, bmc, spacer'),
|
||||
('datalog.default_table', SYMBOL, 'sparse',
|
||||
'default table implementation: sparse, hashtable, bitvector, interval'),
|
||||
('datalog.default_relation', SYMBOL, 'pentagon',
|
||||
('datalog.default_relation', SYMBOL, 'pentagon',
|
||||
'default relation implementation: external_relation, pentagon'),
|
||||
('datalog.generate_explanations', BOOL, False,
|
||||
('datalog.generate_explanations', BOOL, False,
|
||||
'produce explanations for produced facts when using the datalog engine'),
|
||||
('datalog.use_map_names', BOOL, True,
|
||||
('datalog.use_map_names', BOOL, True,
|
||||
"use names from map files when displaying tuples"),
|
||||
('datalog.magic_sets_for_queries', BOOL, False,
|
||||
('datalog.magic_sets_for_queries', BOOL, False,
|
||||
"magic set transformation will be used for queries"),
|
||||
('datalog.explanations_on_relation_level', BOOL, False,
|
||||
'if true, explanations are generated as history of each relation, ' +
|
||||
'rather than per fact (generate_explanations must be set to true for ' +
|
||||
('datalog.explanations_on_relation_level', BOOL, False,
|
||||
'if true, explanations are generated as history of each relation, ' +
|
||||
'rather than per fact (generate_explanations must be set to true for ' +
|
||||
'this option to have any effect)'),
|
||||
('datalog.unbound_compressor', BOOL, True,
|
||||
"auxiliary relations will be introduced to avoid unbound variables " +
|
||||
('datalog.unbound_compressor', BOOL, True,
|
||||
"auxiliary relations will be introduced to avoid unbound variables " +
|
||||
"in rule heads"),
|
||||
('datalog.similarity_compressor', BOOL, True,
|
||||
"rules that differ only in values of constants will be merged into " +
|
||||
('datalog.similarity_compressor', BOOL, True,
|
||||
"rules that differ only in values of constants will be merged into " +
|
||||
"a single rule"),
|
||||
('datalog.similarity_compressor_threshold', UINT, 11,
|
||||
"if similarity_compressor is on, this value determines how many " +
|
||||
('datalog.similarity_compressor_threshold', UINT, 11,
|
||||
"if similarity_compressor is on, this value determines how many " +
|
||||
"similar rules there must be in order for them to be merged"),
|
||||
('datalog.all_or_nothing_deltas', BOOL, False,
|
||||
('datalog.all_or_nothing_deltas', BOOL, False,
|
||||
"compile rules so that it is enough for the delta relation in " +
|
||||
"union and widening operations to determine only whether the " +
|
||||
"union and widening operations to determine only whether the " +
|
||||
"updated relation was modified or not"),
|
||||
('datalog.compile_with_widening', BOOL, False,
|
||||
('datalog.compile_with_widening', BOOL, False,
|
||||
"widening will be used to compile recursive rules"),
|
||||
('datalog.default_table_checked', BOOL, False, "if true, the default " +
|
||||
'table will be default_table inside a wrapper that checks that its results ' +
|
||||
|
@ -39,15 +39,15 @@ def_module_params('fixedpoint',
|
|||
('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"),
|
||||
('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " +
|
||||
"operations on the default relation will be verified using SMT solving"),
|
||||
('datalog.initial_restart_timeout', UINT, 0,
|
||||
"length of saturation run before the first restart (in ms), " +
|
||||
('datalog.initial_restart_timeout', UINT, 0,
|
||||
"length of saturation run before the first restart (in ms), " +
|
||||
"zero means no restarts"),
|
||||
('datalog.output_profile', BOOL, False,
|
||||
"determines whether profile information should be " +
|
||||
('datalog.output_profile', BOOL, False,
|
||||
"determines whether profile information should be " +
|
||||
"output when outputting Datalog rules or instructions"),
|
||||
('datalog.print.tuples', BOOL, True,
|
||||
('datalog.print.tuples', BOOL, True,
|
||||
"determines whether tuples for output predicates should be output"),
|
||||
('datalog.profile_timeout_milliseconds', UINT, 0,
|
||||
('datalog.profile_timeout_milliseconds', UINT, 0,
|
||||
"instructions and rules that took less than the threshold " +
|
||||
"will not be printed when printed the instruction/rule list"),
|
||||
('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False,
|
||||
|
@ -56,94 +56,94 @@ def_module_params('fixedpoint',
|
|||
"table columns, if it would have been empty otherwise"),
|
||||
('datalog.subsumption', BOOL, True,
|
||||
"if true, removes/filters predicates with total transitions"),
|
||||
('pdr.bfs_model_search', BOOL, True,
|
||||
"use BFS strategy for expanding model search"),
|
||||
('pdr.farkas', BOOL, True,
|
||||
('pdr.bfs_model_search', BOOL, True,
|
||||
"use BFS strategy for expanding model search"),
|
||||
('pdr.farkas', BOOL, True,
|
||||
"use lemma generator based on Farkas (for linear real arithmetic)"),
|
||||
('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"),
|
||||
('pdr.flexible_trace', BOOL, False,
|
||||
('pdr.flexible_trace', BOOL, False,
|
||||
"allow PDR generate long counter-examples " +
|
||||
"by extending candidate trace within search area"),
|
||||
('pdr.flexible_trace_depth', UINT, UINT_MAX,
|
||||
('pdr.flexible_trace_depth', UINT, UINT_MAX,
|
||||
'Controls the depth (below the current level) at which flexible trace can be applied'),
|
||||
('pdr.use_model_generalizer', BOOL, False,
|
||||
('pdr.use_model_generalizer', BOOL, False,
|
||||
"use model for backwards propagation (instead of symbolic simulation)"),
|
||||
('pdr.validate_result', BOOL, False,
|
||||
('pdr.validate_result', BOOL, False,
|
||||
"validate result (by proof checking or model checking)"),
|
||||
('pdr.simplify_formulas_pre', BOOL, False,
|
||||
('pdr.simplify_formulas_pre', BOOL, False,
|
||||
"simplify derived formulas before inductive propagation"),
|
||||
('pdr.simplify_formulas_post', BOOL, False,
|
||||
('pdr.simplify_formulas_post', BOOL, False,
|
||||
"simplify derived formulas after inductive propagation"),
|
||||
('pdr.use_multicore_generalizer', BOOL, False,
|
||||
('pdr.use_multicore_generalizer', BOOL, False,
|
||||
"extract multiple cores for blocking states"),
|
||||
('pdr.use_inductive_generalizer', BOOL, True,
|
||||
('pdr.use_inductive_generalizer', BOOL, True,
|
||||
"generalize lemmas using induction strengthening"),
|
||||
('pdr.use_arith_inductive_generalizer', BOOL, False,
|
||||
('pdr.use_arith_inductive_generalizer', BOOL, False,
|
||||
"generalize lemmas using arithmetic heuristics for induction strengthening"),
|
||||
('pdr.use_convex_closure_generalizer', BOOL, False,
|
||||
('pdr.use_convex_closure_generalizer', BOOL, False,
|
||||
"generalize using convex closures of lemmas"),
|
||||
('pdr.use_convex_interior_generalizer', BOOL, False,
|
||||
('pdr.use_convex_interior_generalizer', BOOL, False,
|
||||
"generalize using convex interiors of lemmas"),
|
||||
('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " +
|
||||
('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " +
|
||||
"cache (2) for model search"),
|
||||
('pdr.inductive_reachability_check', BOOL, False,
|
||||
('pdr.inductive_reachability_check', BOOL, False,
|
||||
"assume negation of the cube on the previous level when " +
|
||||
"checking for reachability (not only during cube weakening)"),
|
||||
('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"),
|
||||
('pdr.try_minimize_core', BOOL, False,
|
||||
('pdr.try_minimize_core', BOOL, False,
|
||||
"try to reduce core size (before inductive minimization)"),
|
||||
('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'),
|
||||
('print_fixedpoint_extensions', BOOL, True,
|
||||
"use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " +
|
||||
('print_fixedpoint_extensions', BOOL, True,
|
||||
"use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " +
|
||||
"when printing rules"),
|
||||
('print_low_level_smt2', BOOL, False,
|
||||
"use (faster) low-level SMT2 printer (the printer is scalable " +
|
||||
('print_low_level_smt2', BOOL, False,
|
||||
"use (faster) low-level SMT2 printer (the printer is scalable " +
|
||||
"but the result may not be as readable)"),
|
||||
('print_with_variable_declarations', BOOL, True,
|
||||
('print_with_variable_declarations', BOOL, True,
|
||||
"use variable declarations when displaying rules " +
|
||||
"(instead of attempting to use original names)"),
|
||||
('print_answer', BOOL, False, 'print answer instance(s) to query'),
|
||||
('print_certificate', BOOL, False,
|
||||
('print_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability'),
|
||||
('print_boogie_certificate', BOOL, False,
|
||||
('print_boogie_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability using a ' +
|
||||
'format understood by Boogie'),
|
||||
('print_statistics', BOOL, False, 'print statistics'),
|
||||
('print_aig', SYMBOL, '',
|
||||
('print_aig', SYMBOL, '',
|
||||
'Dump clauses in AIG text format (AAG) to the given file name'),
|
||||
('tab.selection', SYMBOL, 'weight',
|
||||
'selection method for tabular strategy: weight (default), first, var-use'),
|
||||
('xform.bit_blast', BOOL, False,
|
||||
('xform.bit_blast', BOOL, False,
|
||||
'bit-blast bit-vectors'),
|
||||
('xform.magic', BOOL, False,
|
||||
('xform.magic', BOOL, False,
|
||||
"perform symbolic magic set transformation"),
|
||||
('xform.scale', BOOL, False,
|
||||
('xform.scale', BOOL, False,
|
||||
"add scaling variable to linear real arithmetic clauses"),
|
||||
('xform.inline_linear', BOOL, True, "try linear inlining method"),
|
||||
('xform.inline_eager', BOOL, True, "try eager inlining of rules"),
|
||||
('xform.inline_linear_branch', BOOL, False,
|
||||
('xform.inline_linear_branch', BOOL, False,
|
||||
"try linear inlining method with potential expansion"),
|
||||
('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"),
|
||||
('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
|
||||
('xform.unfold_rules', UINT, 0,
|
||||
('xform.unfold_rules', UINT, 0,
|
||||
"unfold rules statically using iterative squarring"),
|
||||
('xform.slice', BOOL, True, "simplify clause set using slicing"),
|
||||
('xform.karr', BOOL, False,
|
||||
('xform.karr', BOOL, False,
|
||||
"Add linear invariants to clauses using Karr's method"),
|
||||
('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"),
|
||||
('xform.transform_arrays', BOOL, False,
|
||||
('xform.transform_arrays', BOOL, False,
|
||||
"Rewrites arrays equalities and applies select over store"),
|
||||
('xform.instantiate_arrays', BOOL, False,
|
||||
('xform.instantiate_arrays', BOOL, False,
|
||||
"Transforms P(a) into P(i, a[i] a)"),
|
||||
('xform.instantiate_arrays.enforce', BOOL, False,
|
||||
('xform.instantiate_arrays.enforce', BOOL, False,
|
||||
"Transforms P(a) into P(i, a[i]), discards a from predicate"),
|
||||
('xform.instantiate_arrays.nb_quantifier', UINT, 1,
|
||||
('xform.instantiate_arrays.nb_quantifier', UINT, 1,
|
||||
"Gives the number of quantifiers per array"),
|
||||
('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing",
|
||||
('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing",
|
||||
"<no-slicing>=> GetId(i) = i, <smash> => GetId(i) = true"),
|
||||
('xform.quantify_arrays', BOOL, False,
|
||||
('xform.quantify_arrays', BOOL, False,
|
||||
"create quantified Horn clauses from clauses with arrays"),
|
||||
('xform.instantiate_quantifiers', BOOL, False,
|
||||
('xform.instantiate_quantifiers', BOOL, False,
|
||||
"instantiate quantified Horn clauses using E-matching heuristic"),
|
||||
('xform.coalesce_rules', BOOL, False, "coalesce rules"),
|
||||
('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"),
|
||||
|
@ -154,8 +154,8 @@ def_module_params('fixedpoint',
|
|||
('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'),
|
||||
('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'),
|
||||
('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'),
|
||||
('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'),
|
||||
('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"),
|
||||
('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'),
|
||||
('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"),
|
||||
('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"),
|
||||
('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"),
|
||||
('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"),
|
||||
|
|
|
@ -9,7 +9,6 @@ z3_add_component(fp
|
|||
clp
|
||||
ddnf
|
||||
muz
|
||||
pdr
|
||||
rel
|
||||
spacer
|
||||
tab
|
||||
|
|
|
@ -21,7 +21,6 @@ Revision History:
|
|||
#include "muz/clp/clp_context.h"
|
||||
#include "muz/tab/tab_context.h"
|
||||
#include "muz/rel/rel_context.h"
|
||||
#include "muz/pdr/pdr_dl_interface.h"
|
||||
#include "muz/ddnf/ddnf.h"
|
||||
#include "muz/spacer/spacer_dl_interface.h"
|
||||
|
||||
|
@ -30,9 +29,6 @@ namespace datalog {
|
|||
|
||||
engine_base* register_engine::mk_engine(DL_ENGINE engine_type) {
|
||||
switch(engine_type) {
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
return alloc(pdr::dl_interface, *m_ctx);
|
||||
case SPACER_ENGINE:
|
||||
return alloc(spacer::dl_interface, *m_ctx);
|
||||
case DATALOG_ENGINE:
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
z3_add_component(pdr
|
||||
SOURCES
|
||||
pdr_closure.cpp
|
||||
pdr_context.cpp
|
||||
pdr_dl_interface.cpp
|
||||
pdr_farkas_learner.cpp
|
||||
pdr_generalizers.cpp
|
||||
pdr_manager.cpp
|
||||
pdr_prop_solver.cpp
|
||||
pdr_reachable_cache.cpp
|
||||
pdr_smt_context_manager.cpp
|
||||
pdr_sym_mux.cpp
|
||||
pdr_util.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
arith_tactics
|
||||
core_tactics
|
||||
muz
|
||||
smt_tactic
|
||||
transforms
|
||||
)
|
|
@ -1,177 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_closure.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for computing closures.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-9-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_closure.h"
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/ast_util.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
expr_ref scaler::operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate) {
|
||||
m_cache[0].reset();
|
||||
m_cache[1].reset();
|
||||
m_translate = translate;
|
||||
m_k = k;
|
||||
return scale(e, false);
|
||||
}
|
||||
|
||||
expr_ref scaler::scale(expr* e, bool is_mul) {
|
||||
expr* r;
|
||||
if (m_cache[is_mul].find(e, r)) {
|
||||
return expr_ref(r, m);
|
||||
}
|
||||
if (!is_app(e)) {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
app* ap = to_app(e);
|
||||
if (m_translate && m_translate->find(ap->get_decl(), r)) {
|
||||
return expr_ref(r, m);
|
||||
}
|
||||
if (!is_mul && a.is_numeral(e)) {
|
||||
return expr_ref(a.mk_mul(m_k, e), m);
|
||||
}
|
||||
expr_ref_vector args(m);
|
||||
bool is_mul_rec = is_mul || a.is_mul(e);
|
||||
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
|
||||
args.push_back(scale(ap->get_arg(i), is_mul_rec));
|
||||
}
|
||||
expr_ref result(m);
|
||||
result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr());
|
||||
m_cache[is_mul].insert(e, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref scaler::undo_k(expr* e, expr* k) {
|
||||
expr_safe_replace sub(m);
|
||||
th_rewriter rw(m);
|
||||
expr_ref result(e, m);
|
||||
sub.insert(k, a.mk_numeral(rational(1), false));
|
||||
sub(result);
|
||||
rw(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
closure::closure(pred_transformer& p, bool is_closure):
|
||||
m(p.get_manager()), m_pt(p), a(m),
|
||||
m_is_closure(is_closure), m_sigma(m), m_trail(m) {}
|
||||
|
||||
|
||||
void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) {
|
||||
manager& pm = m_pt.get_pdr_manager();
|
||||
SASSERT(num_vars > 0);
|
||||
while (m_vars.size() < num_vars) {
|
||||
m_vars.resize(m_vars.size()+1);
|
||||
m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real()));
|
||||
}
|
||||
|
||||
unsigned sz = m_pt.sig_size();
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* var;
|
||||
ptr_vector<expr> vars;
|
||||
func_decl* fn0 = m_pt.sig(i);
|
||||
func_decl* fn1 = pm.o2n(fn0, 0);
|
||||
sort* srt = fn0->get_range();
|
||||
if (a.is_int_real(srt)) {
|
||||
for (unsigned j = 0; j < num_vars; ++j) {
|
||||
if (!m_vars[j].find(fn1, var)) {
|
||||
var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt);
|
||||
m_trail.push_back(var);
|
||||
m_vars[j].insert(fn1, var);
|
||||
}
|
||||
vars.push_back(var);
|
||||
}
|
||||
fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr())));
|
||||
}
|
||||
}
|
||||
if (m_is_closure) {
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// is interior:
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
|
||||
}
|
||||
}
|
||||
fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr())));
|
||||
}
|
||||
|
||||
expr_ref closure::close_fml(expr* e) {
|
||||
expr* e0, *e1, *e2;
|
||||
expr_ref result(m);
|
||||
if (a.is_lt(e, e1, e2)) {
|
||||
result = a.mk_le(e1, e2);
|
||||
}
|
||||
else if (a.is_gt(e, e1, e2)) {
|
||||
result = a.mk_ge(e1, e2);
|
||||
}
|
||||
else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) {
|
||||
result = a.mk_le(e1, e2);
|
||||
}
|
||||
else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) {
|
||||
result = a.mk_ge(e1, e2);
|
||||
}
|
||||
else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) ||
|
||||
(m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) {
|
||||
result = e;
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";);
|
||||
result = m.mk_true();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref closure::close_conjunction(expr* fml) {
|
||||
expr_ref_vector fmls(m);
|
||||
flatten_and(fml, fmls);
|
||||
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||
fmls[i] = close_fml(fmls[i].get());
|
||||
}
|
||||
return expr_ref(mk_and(fmls), m);
|
||||
}
|
||||
|
||||
expr_ref closure::relax(unsigned i, expr* fml) {
|
||||
scaler sc(m);
|
||||
expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]);
|
||||
return close_conjunction(result);
|
||||
}
|
||||
|
||||
expr_ref closure::operator()(expr_ref_vector const& As) {
|
||||
if (As.empty()) {
|
||||
return expr_ref(m.mk_false(), m);
|
||||
}
|
||||
if (As.size() == 1) {
|
||||
return expr_ref(As[0], m);
|
||||
}
|
||||
expr_ref_vector fmls(m);
|
||||
expr_ref B(m);
|
||||
add_variables(As.size(), fmls);
|
||||
for (unsigned i = 0; i < As.size(); ++i) {
|
||||
fmls.push_back(relax(i, As[i]));
|
||||
}
|
||||
B = mk_and(fmls);
|
||||
return B;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_closure.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for computing closures.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-9-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_CLOSURE_H_
|
||||
#define PDR_CLOSURE_H_
|
||||
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
// Arithmetic scaling functor.
|
||||
// Variables are replaced using
|
||||
// m_translate. Constants are replaced by
|
||||
// multiplication with a variable 'k' (scale factor).
|
||||
class scaler {
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
obj_map<expr, expr*> m_cache[2];
|
||||
expr* m_k;
|
||||
obj_map<func_decl, expr*>* m_translate;
|
||||
public:
|
||||
scaler(ast_manager& m): m(m), a(m), m_translate(nullptr) {}
|
||||
expr_ref operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate = nullptr);
|
||||
expr_ref undo_k(expr* e, expr* k);
|
||||
private:
|
||||
expr_ref scale(expr* e, bool is_mul);
|
||||
};
|
||||
|
||||
class pred_transformer;
|
||||
|
||||
class closure {
|
||||
ast_manager& m;
|
||||
pred_transformer& m_pt;
|
||||
arith_util a;
|
||||
bool m_is_closure;
|
||||
expr_ref_vector m_sigma;
|
||||
expr_ref_vector m_trail;
|
||||
vector<obj_map<func_decl, expr*> > m_vars;
|
||||
|
||||
expr_ref relax(unsigned i, expr* fml);
|
||||
expr_ref close_conjunction(expr* fml);
|
||||
expr_ref close_fml(expr* fml);
|
||||
void add_variables(unsigned num_vars, expr_ref_vector& fmls);
|
||||
public:
|
||||
closure(pred_transformer& pt, bool is_closure);
|
||||
expr_ref operator()(expr_ref_vector const& As);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,448 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_context.h
|
||||
|
||||
Abstract:
|
||||
|
||||
PDR for datalog
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_CONTEXT_H_
|
||||
#define PDR_CONTEXT_H_
|
||||
|
||||
#ifdef _CYGWIN
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#include <deque>
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_prop_solver.h"
|
||||
#include "muz/pdr/pdr_reachable_cache.h"
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
class rule_set;
|
||||
class context;
|
||||
};
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class pred_transformer;
|
||||
class model_node;
|
||||
class context;
|
||||
|
||||
typedef obj_map<datalog::rule const, app_ref_vector*> rule2inst;
|
||||
typedef obj_map<func_decl, pred_transformer*> decl2rel;
|
||||
|
||||
|
||||
//
|
||||
// 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;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
typedef obj_map<datalog::rule const, expr*> rule2expr;
|
||||
typedef obj_map<datalog::rule const, ptr_vector<app> > rule2apps;
|
||||
|
||||
manager& pm; // pdr-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
|
||||
vector<expr_ref_vector> m_levels; // level formulas
|
||||
expr_ref_vector m_invariants; // properties that are invariant.
|
||||
obj_map<expr, unsigned> m_prop2level; // map property to level where it occurs.
|
||||
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.
|
||||
reachable_cache m_reachable;
|
||||
ptr_vector<func_decl> m_predicates;
|
||||
stats m_stats;
|
||||
|
||||
void init_sig();
|
||||
void ensure_level(unsigned level);
|
||||
bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl.
|
||||
void add_child_property(pred_transformer& child, expr* 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, expr_ref& 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);
|
||||
|
||||
public:
|
||||
pred_transformer(context& ctx, manager& pm, func_decl* head);
|
||||
~pred_transformer();
|
||||
|
||||
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) { init_sig(); return m_sig[i].get(); } // signature
|
||||
func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); }
|
||||
unsigned sig_size() { init_sig(); 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_levels.size(); }
|
||||
expr_ref get_cover_delta(func_decl* p_orig, int level);
|
||||
void add_cover(unsigned level, expr* property);
|
||||
context& get_context() { return ctx; }
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
bool is_reachable(expr* state);
|
||||
void remove_predecessors(expr_ref_vector& literals);
|
||||
void find_predecessors(datalog::rule const& r, ptr_vector<func_decl>& predicates) const;
|
||||
datalog::rule const& find_rule(model_core const& model) const;
|
||||
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);
|
||||
void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level.
|
||||
|
||||
lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level);
|
||||
bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = nullptr);
|
||||
bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level);
|
||||
|
||||
expr_ref get_formulas(unsigned level, bool add_axioms);
|
||||
|
||||
void simplify_formulas();
|
||||
|
||||
expr_ref get_propagation_formula(decl2rel const& pts, unsigned level);
|
||||
|
||||
manager& get_pdr_manager() const { return pm; }
|
||||
ast_manager& get_manager() const { return m; }
|
||||
|
||||
void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r);
|
||||
|
||||
void close(expr* e);
|
||||
|
||||
app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);}
|
||||
|
||||
void inherit_properties(pred_transformer& other);
|
||||
|
||||
void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector<app>& aux_vars);
|
||||
|
||||
prop_solver& get_solver() { return m_solver; }
|
||||
prop_solver const& get_solver() const { return m_solver; }
|
||||
|
||||
void set_use_farkas(bool f) { get_solver().set_use_farkas(f); }
|
||||
bool get_use_farkas() const { return get_solver().get_use_farkas(); }
|
||||
class scoped_farkas {
|
||||
bool m_old;
|
||||
pred_transformer& m_p;
|
||||
public:
|
||||
scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) {
|
||||
p.set_use_farkas(v);
|
||||
}
|
||||
~scoped_farkas() { m_p.set_use_farkas(m_old); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
// structure for counter-example search.
|
||||
class model_node {
|
||||
model_node* m_parent;
|
||||
model_node* m_next;
|
||||
model_node* m_prev;
|
||||
pred_transformer& m_pt;
|
||||
expr_ref m_state;
|
||||
model_ref m_model;
|
||||
ptr_vector<model_node> m_children;
|
||||
unsigned m_level;
|
||||
unsigned m_orig_level;
|
||||
unsigned m_depth;
|
||||
bool m_closed;
|
||||
datalog::rule const* m_rule;
|
||||
public:
|
||||
model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level):
|
||||
m_parent(parent), m_next(nullptr), m_prev(nullptr), m_pt(pt), m_state(state), m_model(nullptr),
|
||||
m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(nullptr) {
|
||||
model_node* p = m_parent;
|
||||
if (p) {
|
||||
p->m_children.push_back(this);
|
||||
SASSERT(p->m_level == level+1);
|
||||
SASSERT(p->m_level > 0);
|
||||
m_depth = p->m_depth+1;
|
||||
if (p && p->is_closed()) {
|
||||
p->set_open();
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_model(model_ref& m) { m_model = m; }
|
||||
unsigned level() const { return m_level; }
|
||||
unsigned orig_level() const { return m_orig_level; }
|
||||
unsigned depth() const { return m_depth; }
|
||||
void increase_level() { ++m_level; }
|
||||
expr_ref const& state() const { return m_state; }
|
||||
ptr_vector<model_node> const& children() { return m_children; }
|
||||
pred_transformer& pt() const { return m_pt; }
|
||||
model_node* parent() const { return m_parent; }
|
||||
model* get_model_ptr() const { return m_model.get(); }
|
||||
model const& get_model() const { return *m_model; }
|
||||
unsigned index() const;
|
||||
|
||||
bool is_closed() const { return m_closed; }
|
||||
bool is_open() const { return !is_closed(); }
|
||||
|
||||
bool is_1closed() {
|
||||
if (is_closed()) return true;
|
||||
if (m_children.empty()) return false;
|
||||
for (unsigned i = 0; i < m_children.size(); ++i) {
|
||||
if (m_children[i]->is_open()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_pre_closed();
|
||||
void set_closed();
|
||||
void set_open();
|
||||
void set_pre_closed() { TRACE("pdr", tout << state() << "\n";); m_closed = true; }
|
||||
void reset() { m_children.reset(); }
|
||||
|
||||
void set_rule(datalog::rule const* r) { m_rule = r; }
|
||||
datalog::rule* get_rule();
|
||||
|
||||
void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding);
|
||||
|
||||
std::ostream& display(std::ostream& out, unsigned indent);
|
||||
|
||||
void dequeue(model_node*& root);
|
||||
void enqueue(model_node* n);
|
||||
model_node* next() const { return m_next; }
|
||||
bool is_goal() const { return nullptr != next(); }
|
||||
};
|
||||
|
||||
class model_search {
|
||||
typedef ptr_vector<model_node> model_nodes;
|
||||
bool m_bfs;
|
||||
model_node* m_root;
|
||||
model_node* m_goal;
|
||||
vector<obj_map<expr, model_nodes > > m_cache;
|
||||
obj_map<expr, model_nodes>& cache(model_node const& n);
|
||||
void erase_children(model_node& n, bool backtrack);
|
||||
void remove_node(model_node& n, bool backtrack);
|
||||
void enqueue_leaf(model_node* n); // add leaf to priority queue.
|
||||
void update_models();
|
||||
void set_leaf(model_node& n); // Set node as leaf, remove children.
|
||||
unsigned num_goals() const;
|
||||
|
||||
public:
|
||||
model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_goal(nullptr) {}
|
||||
~model_search();
|
||||
|
||||
void reset();
|
||||
model_node* next();
|
||||
void add_leaf(model_node& n); // add fresh node.
|
||||
|
||||
void set_root(model_node* n);
|
||||
model_node& get_root() const { return *m_root; }
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
expr_ref get_trace(context const& ctx);
|
||||
proof_ref get_proof_trace(context const& ctx);
|
||||
void backtrack_level(bool uses_level, model_node& n);
|
||||
void remove_goal(model_node& n);
|
||||
|
||||
void well_formed();
|
||||
};
|
||||
|
||||
struct model_exception { };
|
||||
struct inductive_exception {};
|
||||
|
||||
|
||||
// 'state' is unsatisfiable at 'level' with 'core'.
|
||||
// Minimize or weaken core.
|
||||
class core_generalizer {
|
||||
protected:
|
||||
context& m_ctx;
|
||||
public:
|
||||
typedef vector<std::pair<expr_ref_vector,bool> > cores;
|
||||
core_generalizer(context& ctx): m_ctx(ctx) {}
|
||||
virtual ~core_generalizer() {}
|
||||
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0;
|
||||
virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
if (!core.empty()) {
|
||||
(*this)(n, new_cores.back().first, new_cores.back().second);
|
||||
}
|
||||
}
|
||||
virtual void collect_statistics(statistics& st) const {}
|
||||
virtual void reset_statistics() {}
|
||||
};
|
||||
|
||||
class context {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_nodes;
|
||||
unsigned m_max_depth;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
smt_params& m_fparams;
|
||||
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.
|
||||
decl2rel m_rels_tmp;
|
||||
func_decl_ref m_query_pred;
|
||||
pred_transformer* m_query;
|
||||
mutable model_search m_search;
|
||||
lbool m_last_result;
|
||||
unsigned m_inductive_lvl;
|
||||
unsigned m_expanded_lvl;
|
||||
ptr_vector<core_generalizer> m_core_generalizers;
|
||||
stats m_stats;
|
||||
model_converter_ref m_mc;
|
||||
proof_converter_ref m_pc;
|
||||
|
||||
// Functions used by search.
|
||||
void solve_impl();
|
||||
bool check_reachability(unsigned level);
|
||||
void propagate(unsigned max_prop_lvl);
|
||||
void close_node(model_node& n);
|
||||
void check_pre_closed(model_node& n);
|
||||
void expand_node(model_node& n);
|
||||
lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level);
|
||||
void create_children(model_node& n);
|
||||
expr_ref mk_sat_answer() const;
|
||||
expr_ref mk_unsat_answer();
|
||||
|
||||
// Generate inductive property
|
||||
void get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info> & rs);
|
||||
|
||||
|
||||
// Initialization
|
||||
class classifier_proc;
|
||||
void init_core_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_core_generalizers();
|
||||
|
||||
void reset(decl2rel& rels);
|
||||
|
||||
void validate();
|
||||
void validate_proof();
|
||||
void validate_search();
|
||||
void validate_model();
|
||||
|
||||
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(
|
||||
smt_params& fparams,
|
||||
fixedpoint_params const& params,
|
||||
ast_manager& m);
|
||||
|
||||
~context();
|
||||
|
||||
smt_params& get_fparams() const { return m_fparams; }
|
||||
fixedpoint_params const& get_params() const { return m_params; }
|
||||
ast_manager& get_manager() const { return m; }
|
||||
manager& get_pdr_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_context() const { SASSERT(m_context); return *m_context; }
|
||||
expr_ref get_answer();
|
||||
|
||||
bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; }
|
||||
bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; }
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void display_certificate(std::ostream& strm);
|
||||
|
||||
lbool solve();
|
||||
|
||||
void reset(bool full = true);
|
||||
|
||||
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; }
|
||||
|
||||
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);
|
||||
|
||||
model_ref get_model();
|
||||
|
||||
proof_ref get_proof() const;
|
||||
|
||||
model_node& get_root() const { return m_search.get_root(); }
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,225 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_dl.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/base/dl_context.h"
|
||||
#include "muz/transforms/dl_mk_coi_filter.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_transformer.h"
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_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 "muz/transforms/dl_transforms.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
using namespace pdr;
|
||||
|
||||
dl_interface::dl_interface(datalog::context& ctx) :
|
||||
engine_base(ctx.get_manager(), "pdr"),
|
||||
m_ctx(ctx),
|
||||
m_pdr_rules(ctx),
|
||||
m_old_rules(ctx),
|
||||
m_context(nullptr),
|
||||
m_refs(ctx.get_manager()) {
|
||||
m_context = alloc(pdr::context, ctx.get_fparams(), 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("pdr", 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, rules0);
|
||||
expr_ref bg_assertion = m_ctx.get_background_assertion();
|
||||
|
||||
check_reset();
|
||||
|
||||
TRACE("pdr",
|
||||
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();
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
m_ctx.display_smt2(0, 0, tout);
|
||||
);
|
||||
|
||||
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
|
||||
m_pdr_rules.replace_rules(rules);
|
||||
m_pdr_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_pdr_rules);
|
||||
|
||||
if (m_pdr_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();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dl_interface::updt_params() {
|
||||
dealloc(m_context);
|
||||
m_context = alloc(pdr::context, m_ctx.get_fparams(), 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();
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_dl_interface.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_DL_INTERFACE_H_
|
||||
#define PDR_DL_INTERFACE_H_
|
||||
|
||||
#include "util/lbool.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "muz/base/dl_engine_base.h"
|
||||
#include "util/statistics.h"
|
||||
|
||||
namespace datalog {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class context;
|
||||
|
||||
class dl_interface : public datalog::engine_base {
|
||||
datalog::context& m_ctx;
|
||||
datalog::rule_set m_pdr_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() override;
|
||||
|
||||
lbool query(expr* query) override;
|
||||
|
||||
void display_certificate(std::ostream& out) const override;
|
||||
|
||||
void collect_statistics(statistics& st) const override;
|
||||
|
||||
void reset_statistics() override;
|
||||
|
||||
expr_ref get_answer() override;
|
||||
|
||||
unsigned get_num_levels(func_decl* pred) override;
|
||||
|
||||
expr_ref get_cover_delta(int level, func_decl* pred) override;
|
||||
|
||||
void add_cover(int level, func_decl* pred, expr* property) override;
|
||||
|
||||
void updt_params() override;
|
||||
|
||||
model_ref get_model() override;
|
||||
|
||||
proof_ref get_proof() override;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,128 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_farkas_learner.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-11-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_FARKAS_LEARNER_H_
|
||||
#define PDR_FARKAS_LEARNER_H_
|
||||
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/ast_translation.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "tactic/tactic.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class farkas_learner {
|
||||
class farkas_collector;
|
||||
class constant_replacer_cfg;
|
||||
class equality_expander_cfg;
|
||||
class constr;
|
||||
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
|
||||
smt_params m_proof_params;
|
||||
ast_manager m_pr;
|
||||
scoped_ptr<smt::kernel> m_ctx;
|
||||
constr* m_constr;
|
||||
|
||||
//
|
||||
// true: produce a combined constraint by applying Farkas coefficients.
|
||||
// false: produce a conjunction of the negated literals from the theory lemmas.
|
||||
//
|
||||
bool m_combine_farkas_coefficients;
|
||||
|
||||
|
||||
static smt_params get_proof_params(smt_params& orig_params);
|
||||
|
||||
//
|
||||
// all ast objects passed to private functions have m_proof_mgs as their ast_manager
|
||||
//
|
||||
|
||||
ast_translation p2o; /** Translate expression from inner ast_manager to outer one */
|
||||
ast_translation o2p; /** Translate expression from outer ast_manager to inner one */
|
||||
|
||||
|
||||
/** All ast opbjects here are in the m_proof_mgs */
|
||||
void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas);
|
||||
|
||||
bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res);
|
||||
|
||||
void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res);
|
||||
|
||||
bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang);
|
||||
|
||||
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) const;
|
||||
|
||||
static void test();
|
||||
|
||||
public:
|
||||
farkas_learner(smt_params& params, ast_manager& m);
|
||||
|
||||
~farkas_learner();
|
||||
|
||||
/**
|
||||
All ast objects have the ast_manager which was passed as
|
||||
an argument to the constructor (i.e. m_outer_mgr)
|
||||
|
||||
B is a conjunction of literals.
|
||||
A && B is unsat, equivalently A => ~B is valid
|
||||
Find a weakened B' such that
|
||||
A && B' is unsat and B' uses vocabulary (and constants) in common with A.
|
||||
return lemmas to weaken B.
|
||||
*/
|
||||
|
||||
bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas);
|
||||
|
||||
/**
|
||||
Traverse a proof and retrieve lemmas using the vocabulary from bs.
|
||||
*/
|
||||
void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas);
|
||||
|
||||
/**
|
||||
Traverse a proof and retrieve consequences of A that are used to establish ~B.
|
||||
The assumption is that:
|
||||
|
||||
A => \/ ~consequences[i] and \/ ~consequences[i] => ~B
|
||||
|
||||
e.g., the second implication can be rewritten as:
|
||||
|
||||
B => /\ consequences[i]
|
||||
*/
|
||||
void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences);
|
||||
|
||||
/**
|
||||
\brief Simplify lemmas using subsumption.
|
||||
*/
|
||||
void simplify_lemmas(expr_ref_vector& lemmas);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,777 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_generalizers.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Generalizers of satisfiable states and unsat cores.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "muz/pdr/pdr_generalizers.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
|
||||
namespace pdr {
|
||||
|
||||
|
||||
// ------------------------
|
||||
// core_bool_inductive_generalizer
|
||||
|
||||
// main propositional induction generalizer.
|
||||
// drop literals one by one from the core and check if the core is still inductive.
|
||||
//
|
||||
void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
if (core.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
ast_manager& m = core.get_manager();
|
||||
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
|
||||
unsigned num_failures = 0, i = 0, old_core_size = core.size();
|
||||
ptr_vector<expr> processed;
|
||||
|
||||
while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) {
|
||||
expr_ref lit(m);
|
||||
lit = core[i].get();
|
||||
core[i] = m.mk_true();
|
||||
if (n.pt().check_inductive(n.level(), core, uses_level)) {
|
||||
num_failures = 0;
|
||||
for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i);
|
||||
}
|
||||
else {
|
||||
core[i] = lit;
|
||||
processed.push_back(lit);
|
||||
++num_failures;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
|
||||
TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
|
||||
}
|
||||
|
||||
|
||||
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Find minimal cores.
|
||||
Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one
|
||||
literal from each of the literals in the minimal cores.
|
||||
*/
|
||||
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
ast_manager& m = core.get_manager();
|
||||
expr_ref_vector old_core(m), core0(core);
|
||||
bool uses_level1 = uses_level;
|
||||
m_gen(n, core0, uses_level1);
|
||||
new_cores.push_back(std::make_pair(core0, uses_level1));
|
||||
obj_hashtable<expr> core_exprs, core1_exprs;
|
||||
set_union(core_exprs, core0);
|
||||
for (unsigned i = 0; i < old_core.size(); ++i) {
|
||||
expr* lit = old_core[i].get();
|
||||
if (core_exprs.contains(lit)) {
|
||||
expr_ref_vector core1(old_core);
|
||||
core1[i] = core1.back();
|
||||
core1.pop_back();
|
||||
uses_level1 = uses_level;
|
||||
m_gen(n, core1, uses_level1);
|
||||
SASSERT(core1.size() <= old_core.size());
|
||||
if (core1.size() < old_core.size()) {
|
||||
new_cores.push_back(std::make_pair(core1, uses_level1));
|
||||
core1_exprs.reset();
|
||||
set_union(core1_exprs, core1);
|
||||
set_intersection(core_exprs, core1_exprs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// core_farkas_generalizer
|
||||
|
||||
//
|
||||
// for each disjunct of core:
|
||||
// weaken predecessor.
|
||||
//
|
||||
|
||||
core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p):
|
||||
core_generalizer(ctx),
|
||||
m_farkas_learner(p, m)
|
||||
{}
|
||||
|
||||
void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
ast_manager& m = n.pt().get_manager();
|
||||
if (core.empty()) return;
|
||||
expr_ref A(m), B(mk_and(core)), C(m);
|
||||
expr_ref_vector Bs(m);
|
||||
flatten_or(B, Bs);
|
||||
A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level());
|
||||
|
||||
bool change = false;
|
||||
for (unsigned i = 0; i < Bs.size(); ++i) {
|
||||
expr_ref_vector lemmas(m);
|
||||
C = Bs[i].get();
|
||||
if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) {
|
||||
TRACE("pdr",
|
||||
tout << "Old core:\n" << mk_pp(B, m) << "\n";
|
||||
tout << "New core:\n" << mk_and(lemmas) << "\n";);
|
||||
Bs[i] = mk_and(lemmas);
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
C = mk_or(Bs);
|
||||
TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";);
|
||||
core.reset();
|
||||
flatten_and(C, core);
|
||||
uses_level = true;
|
||||
}
|
||||
}
|
||||
|
||||
void core_farkas_generalizer::collect_statistics(statistics& st) const {
|
||||
m_farkas_learner.collect_statistics(st);
|
||||
}
|
||||
|
||||
|
||||
core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure):
|
||||
core_generalizer(ctx),
|
||||
m(ctx.get_manager()),
|
||||
m_is_closure(is_closure) {
|
||||
}
|
||||
|
||||
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
// method3(n, core, uses_level, new_cores);
|
||||
method1(n, core, uses_level, new_cores);
|
||||
}
|
||||
|
||||
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// use the entire region as starting point for generalization.
|
||||
//
|
||||
// Constraints:
|
||||
// add_variables: y = y1 + y2
|
||||
// core: Ay <= b -> conv1: A*y1 <= b*sigma1
|
||||
// sigma1 > 0
|
||||
// sigma2 > 0
|
||||
// 1 = sigma1 + sigma2
|
||||
// A'y <= b' -> conv2: A'*y2 <= b'*sigma2
|
||||
//
|
||||
// If Constraints & Transition(y0, y) is unsat, then
|
||||
// update with new core.
|
||||
//
|
||||
void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
expr_ref_vector conv2(m), fmls(m), fml1_2(m);
|
||||
bool change = false;
|
||||
|
||||
if (core.empty()) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
return;
|
||||
}
|
||||
closure cl(n.pt(), m_is_closure);
|
||||
|
||||
expr_ref fml1 = mk_and(core);
|
||||
expr_ref fml2 = n.pt().get_formulas(n.level(), false);
|
||||
fml1_2.push_back(fml1);
|
||||
fml1_2.push_back(nullptr);
|
||||
flatten_and(fml2, fmls);
|
||||
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||
fml2 = m.mk_not(fmls[i].get());
|
||||
fml1_2[1] = fml2;
|
||||
expr_ref state = cl(fml1_2);
|
||||
TRACE("pdr",
|
||||
tout << "Check states:\n" << mk_pp(state, m) << "\n";
|
||||
tout << "Old states:\n" << mk_pp(fml2, m) << "\n";
|
||||
);
|
||||
model_node nd(nullptr, state, n.pt(), n.level());
|
||||
pred_transformer::scoped_farkas sf(n.pt(), true);
|
||||
bool uses_level1 = uses_level;
|
||||
if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) {
|
||||
new_cores.push_back(std::make_pair(conv2, uses_level1));
|
||||
change = true;
|
||||
expr_ref state1 = mk_and(conv2);
|
||||
TRACE("pdr",
|
||||
tout << mk_pp(state, m) << "\n";
|
||||
tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << mk_pp(state, m) << "\n";
|
||||
verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
|
||||
}
|
||||
}
|
||||
if (!m_is_closure || !change) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Extract the lemmas from the transition relation that were used to establish unsatisfiability.
|
||||
Take convex closures of conbinations of these lemmas.
|
||||
*/
|
||||
void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
TRACE("dl", tout << "method: generalize consequences of F(R)\n";
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
tout << "B:" << mk_pp(core[i], m) << "\n";
|
||||
});
|
||||
bool uses_level1;
|
||||
expr_ref_vector core1(m);
|
||||
core1.append(core);
|
||||
expr_ref_vector consequences(m);
|
||||
{
|
||||
n.pt().get_solver().set_consequences(&consequences);
|
||||
pred_transformer::scoped_farkas sf (n.pt(), true);
|
||||
VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1));
|
||||
n.pt().get_solver().set_consequences(nullptr);
|
||||
}
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << "Consequences: " << consequences.size() << "\n";
|
||||
for (unsigned i = 0; i < consequences.size(); ++i) {
|
||||
verbose_stream() << mk_pp(consequences[i].get(), m) << "\n";
|
||||
}
|
||||
verbose_stream() << "core: " << core1.size() << "\n";
|
||||
for (unsigned i = 0; i < core1.size(); ++i) {
|
||||
verbose_stream() << mk_pp(core1[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
expr_ref tmp(m);
|
||||
|
||||
// Check that F(R) => \/ consequences
|
||||
{
|
||||
expr_ref_vector cstate(m);
|
||||
for (unsigned i = 0; i < consequences.size(); ++i) {
|
||||
cstate.push_back(m.mk_not(consequences[i].get()));
|
||||
}
|
||||
tmp = m.mk_and(cstate.size(), cstate.c_ptr());
|
||||
model_node nd(nullptr, tmp, n.pt(), n.level());
|
||||
pred_transformer::scoped_farkas sf (n.pt(), false);
|
||||
VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1));
|
||||
}
|
||||
|
||||
// Create disjunction.
|
||||
tmp = m.mk_and(core.size(), core.c_ptr());
|
||||
|
||||
// Check that \/ consequences => not (core)
|
||||
if (!is_unsat(consequences, tmp)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";);
|
||||
return;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";);
|
||||
|
||||
if (!strengthen_consequences(n, consequences, tmp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";);
|
||||
// Use the resulting formula to find Farkas lemmas from core.
|
||||
}
|
||||
|
||||
bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) {
|
||||
expr_ref A(m), tmp(m), convA(m);
|
||||
unsigned sz = As.size();
|
||||
closure cl(n.pt(), m_is_closure);
|
||||
for (unsigned i = 0; i < As.size(); ++i) {
|
||||
expr_ref_vector Hs(m);
|
||||
Hs.push_back(As[i].get());
|
||||
for (unsigned j = i + 1; j < As.size(); ++j) {
|
||||
Hs.push_back(As[j].get());
|
||||
bool unsat = false;
|
||||
A = cl(Hs);
|
||||
tmp = As[i].get();
|
||||
As[i] = A;
|
||||
unsat = is_unsat(As, B);
|
||||
As[i] = tmp;
|
||||
if (unsat) {
|
||||
IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";);
|
||||
convA = A;
|
||||
As[j] = As.back();
|
||||
As.pop_back();
|
||||
--j;
|
||||
}
|
||||
else {
|
||||
Hs.pop_back();
|
||||
}
|
||||
}
|
||||
if (Hs.size() > 1) {
|
||||
As[i] = convA;
|
||||
}
|
||||
}
|
||||
return sz > As.size();
|
||||
}
|
||||
|
||||
|
||||
bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) {
|
||||
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
|
||||
expr_ref disj(m);
|
||||
disj = m.mk_or(As.size(), As.c_ptr());
|
||||
ctx.assert_expr(disj);
|
||||
ctx.assert_expr(B);
|
||||
std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n";
|
||||
return l_false == ctx.check();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------
|
||||
// core_arith_inductive_generalizer
|
||||
// NB. this is trying out some ideas for generalization in
|
||||
// an ad hoc specialized way. arith_inductive_generalizer should
|
||||
// not be used by default. It is a place-holder for a general purpose
|
||||
// extrapolator of a lattice basis.
|
||||
|
||||
core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx):
|
||||
core_generalizer(ctx),
|
||||
m(ctx.get_manager()),
|
||||
a(m),
|
||||
m_refs(m) {}
|
||||
|
||||
void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
if (core.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
expr_ref e(m), t1(m), t2(m), t3(m);
|
||||
rational r;
|
||||
|
||||
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
|
||||
|
||||
svector<eq> eqs;
|
||||
get_eqs(core, eqs);
|
||||
|
||||
if (eqs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref_vector new_core(m);
|
||||
new_core.append(core);
|
||||
|
||||
for (unsigned eq = 0; eq < eqs.size(); ++eq) {
|
||||
rational r = eqs[eq].m_value;
|
||||
expr* x = eqs[eq].m_term;
|
||||
unsigned k = eqs[eq].m_i;
|
||||
unsigned l = eqs[eq].m_j;
|
||||
|
||||
new_core[l] = m.mk_true();
|
||||
new_core[k] = m.mk_true();
|
||||
|
||||
for (unsigned i = 0; i < new_core.size(); ++i) {
|
||||
if (substitute_alias(r, x, new_core[i].get(), e)) {
|
||||
new_core[i] = e;
|
||||
}
|
||||
}
|
||||
if (abs(r) >= rational(2) && a.is_int(x)) {
|
||||
new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true));
|
||||
new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true));
|
||||
}
|
||||
}
|
||||
|
||||
bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level);
|
||||
|
||||
IF_VERBOSE(1,
|
||||
verbose_stream() << (inductive?"":"non") << "inductive\n";
|
||||
verbose_stream() << "old\n";
|
||||
for (unsigned j = 0; j < core.size(); ++j) {
|
||||
verbose_stream() << mk_pp(core[j].get(), m) << "\n";
|
||||
}
|
||||
verbose_stream() << "new\n";
|
||||
for (unsigned j = 0; j < new_core.size(); ++j) {
|
||||
verbose_stream() << mk_pp(new_core[j].get(), m) << "\n";
|
||||
});
|
||||
|
||||
if (inductive) {
|
||||
core.reset();
|
||||
core.append(new_core);
|
||||
}
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) {
|
||||
if (r.is_neg()) {
|
||||
expr_ref e(m);
|
||||
e = a.mk_uminus(x);
|
||||
m_refs.push_back(e);
|
||||
x = e;
|
||||
is_lower = !is_lower;
|
||||
}
|
||||
|
||||
vector<term_loc_t> bound;
|
||||
bound.push_back(std::make_pair(x, i));
|
||||
if (is_lower) {
|
||||
m_lb.insert(abs(r), bound);
|
||||
}
|
||||
else {
|
||||
m_ub.insert(abs(r), bound);
|
||||
}
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::reset() {
|
||||
m_refs.reset();
|
||||
m_lb.reset();
|
||||
m_ub.reset();
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector<eq>& eqs) {
|
||||
expr* e1, *x, *y;
|
||||
expr_ref e(m);
|
||||
rational r;
|
||||
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
e = core[i];
|
||||
if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||
// not (<= x r) <=> x >= r + 1
|
||||
insert_bound(true, x, r + rational(1), i);
|
||||
}
|
||||
else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||
// not (>= x r) <=> x <= r - 1
|
||||
insert_bound(false, x, r - rational(1), i);
|
||||
}
|
||||
else if (a.is_le(e, x, y) && a.is_numeral(y, r)) {
|
||||
insert_bound(false, x, r, i);
|
||||
}
|
||||
else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) {
|
||||
insert_bound(true, x, r, i);
|
||||
}
|
||||
}
|
||||
bounds_t::iterator it = m_lb.begin(), end = m_lb.end();
|
||||
for (; it != end; ++it) {
|
||||
rational r = it->m_key;
|
||||
vector<term_loc_t> & terms1 = it->m_value;
|
||||
vector<term_loc_t> terms2;
|
||||
if (r >= rational(2) && m_ub.find(r, terms2)) {
|
||||
for (unsigned i = 0; i < terms1.size(); ++i) {
|
||||
bool done = false;
|
||||
for (unsigned j = 0; !done && j < terms2.size(); ++j) {
|
||||
expr* t1 = terms1[i].first;
|
||||
expr* t2 = terms2[j].first;
|
||||
if (t1 == t2) {
|
||||
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||
done = true;
|
||||
}
|
||||
else {
|
||||
e = m.mk_eq(t1, t2);
|
||||
th_rewriter rw(m);
|
||||
rw(e);
|
||||
if (m.is_true(e)) {
|
||||
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) {
|
||||
rational r2;
|
||||
expr* y, *z, *e1;
|
||||
if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) {
|
||||
result = m.mk_not(result);
|
||||
return true;
|
||||
}
|
||||
if (a.is_le(e, y, z) && a.is_numeral(z, r2)) {
|
||||
if (r == r2) {
|
||||
result = a.mk_le(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r == r2 + rational(1)) {
|
||||
result = a.mk_lt(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r == r2 - rational(1)) {
|
||||
result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) {
|
||||
if (r == r2) {
|
||||
result = a.mk_ge(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r2 == r + rational(1)) {
|
||||
result = a.mk_gt(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r2 == r - rational(1)) {
|
||||
result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// < F, phi, i + 1>
|
||||
// |
|
||||
// < G, psi, i >
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// p(x) <- F(x,y,p,q)
|
||||
// q(x) <- G(x,y)
|
||||
//
|
||||
// Hyp:
|
||||
// Q_k(x) => phi(x) j <= k <= i
|
||||
// Q_k(x) => R_k(x) j <= k <= i + 1
|
||||
// Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1
|
||||
// Conclusion:
|
||||
// Q_{i+1}(x) => phi(x)
|
||||
//
|
||||
class core_induction_generalizer::imp {
|
||||
context& m_ctx;
|
||||
manager& pm;
|
||||
ast_manager& m;
|
||||
|
||||
//
|
||||
// Create predicate Q_level
|
||||
//
|
||||
func_decl_ref mk_pred(unsigned level, func_decl* f) {
|
||||
func_decl_ref result(m);
|
||||
std::ostringstream name;
|
||||
name << f->get_name() << "_" << level;
|
||||
symbol sname(name.str().c_str());
|
||||
result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range());
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Create formula exists y . z . F[Q_{level-1}, x, y, z]
|
||||
//
|
||||
expr_ref mk_transition_rule(
|
||||
expr_ref_vector const& reps,
|
||||
unsigned level,
|
||||
datalog::rule const& rule)
|
||||
{
|
||||
expr_ref_vector conj(m), sub(m);
|
||||
expr_ref result(m);
|
||||
svector<symbol> names;
|
||||
unsigned ut_size = rule.get_uninterpreted_tail_size();
|
||||
unsigned t_size = rule.get_tail_size();
|
||||
if (0 == level && 0 < ut_size) {
|
||||
result = m.mk_false();
|
||||
return result;
|
||||
}
|
||||
app* atom = rule.get_head();
|
||||
SASSERT(atom->get_num_args() == reps.size());
|
||||
|
||||
for (unsigned i = 0; i < reps.size(); ++i) {
|
||||
expr* arg = atom->get_arg(i);
|
||||
if (is_var(arg)) {
|
||||
unsigned idx = to_var(arg)->get_idx();
|
||||
if (idx >= sub.size()) sub.resize(idx+1);
|
||||
if (sub[idx].get()) {
|
||||
conj.push_back(m.mk_eq(sub[idx].get(), reps[i]));
|
||||
}
|
||||
else {
|
||||
sub[idx] = reps[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
conj.push_back(m.mk_eq(arg, reps[i]));
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; 0 < level && i < ut_size; i++) {
|
||||
app* atom = rule.get_tail(i);
|
||||
func_decl* head = atom->get_decl();
|
||||
func_decl_ref fn = mk_pred(level-1, head);
|
||||
conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args()));
|
||||
}
|
||||
for (unsigned i = ut_size; i < t_size; i++) {
|
||||
conj.push_back(rule.get_tail(i));
|
||||
}
|
||||
result = mk_and(conj);
|
||||
if (!sub.empty()) {
|
||||
expr_ref tmp = result;
|
||||
var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result);
|
||||
}
|
||||
expr_free_vars fv;
|
||||
fv(result);
|
||||
fv.set_default_sort(m.mk_bool_sort());
|
||||
for (unsigned i = 0; i < fv.size(); ++i) {
|
||||
names.push_back(symbol(fv.size() - i - 1));
|
||||
}
|
||||
if (!fv.empty()) {
|
||||
fv.reverse();
|
||||
result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref bind_head(expr_ref_vector const& reps, expr* fml) {
|
||||
expr_ref result(m);
|
||||
expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result);
|
||||
ptr_vector<sort> sorts;
|
||||
svector<symbol> names;
|
||||
unsigned sz = reps.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
sorts.push_back(m.get_sort(reps[sz-i-1]));
|
||||
names.push_back(symbol(sz-i-1));
|
||||
}
|
||||
if (sz > 0) {
|
||||
result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref_vector mk_reps(pred_transformer& pt) {
|
||||
expr_ref_vector reps(m);
|
||||
expr_ref rep(m);
|
||||
for (unsigned i = 0; i < pt.head()->get_arity(); ++i) {
|
||||
rep = m.mk_const(pm.o2n(pt.sig(i), 0));
|
||||
reps.push_back(rep);
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
|
||||
//
|
||||
// extract transition axiom:
|
||||
//
|
||||
// forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x]
|
||||
//
|
||||
expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) {
|
||||
expr_ref fml(m.mk_false(), m), tr(m);
|
||||
expr_ref_vector reps = mk_reps(pt);
|
||||
ptr_vector<datalog::rule> const& rules = pt.rules();
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
tr = mk_transition_rule(reps, level, *rules[i]);
|
||||
fml = (i == 0)?tr.get():m.mk_or(fml, tr);
|
||||
}
|
||||
func_decl_ref fn = mk_pred(level, pt.head());
|
||||
fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml);
|
||||
fml = bind_head(reps, fml);
|
||||
return fml;
|
||||
}
|
||||
|
||||
//
|
||||
// Create implication:
|
||||
// Q_level(x) => phi(x)
|
||||
//
|
||||
expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) {
|
||||
expr_ref_vector reps = mk_reps(pt);
|
||||
func_decl_ref fn = mk_pred(level, pt.head());
|
||||
expr_ref fml(m);
|
||||
fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi);
|
||||
fml = bind_head(reps, fml);
|
||||
return fml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {}
|
||||
|
||||
//
|
||||
// not exists y . F(x,y)
|
||||
//
|
||||
expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) {
|
||||
SASSERT(level > 0);
|
||||
expr_ref fml(m.mk_true(), m);
|
||||
expr_ref_vector reps = mk_reps(pt), fmls(m);
|
||||
ptr_vector<datalog::rule> const& rules = pt.rules();
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i])));
|
||||
}
|
||||
fml = mk_and(fmls);
|
||||
TRACE("pdr", tout << mk_pp(fml, m) << "\n";);
|
||||
return fml;
|
||||
}
|
||||
|
||||
expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) {
|
||||
SASSERT(level >= depth);
|
||||
expr_ref_vector conjs(m);
|
||||
ptr_vector<pred_transformer> pts;
|
||||
unsigned_vector levels;
|
||||
// negated goal
|
||||
expr_ref phi = mk_blocked_transition(pt, level);
|
||||
conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi)));
|
||||
pts.push_back(&pt);
|
||||
levels.push_back(level);
|
||||
// Add I.H.
|
||||
for (unsigned lvl = level-depth; lvl < level; ++lvl) {
|
||||
if (lvl > 0) {
|
||||
expr_ref psi = mk_blocked_transition(pt, lvl);
|
||||
conjs.push_back(mk_predicate_property(lvl, pt, psi));
|
||||
pts.push_back(&pt);
|
||||
levels.push_back(lvl);
|
||||
}
|
||||
}
|
||||
// Transitions:
|
||||
for (unsigned qhead = 0; qhead < pts.size(); ++qhead) {
|
||||
pred_transformer& qt = *pts[qhead];
|
||||
unsigned lvl = levels[qhead];
|
||||
|
||||
// Add transition definition and properties at level.
|
||||
conjs.push_back(mk_transition_axiom(qt, lvl));
|
||||
conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true)));
|
||||
|
||||
// Enqueue additional hypotheses
|
||||
ptr_vector<datalog::rule> const& rules = qt.rules();
|
||||
if (lvl + depth < level || lvl == 0) {
|
||||
continue;
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
datalog::rule& r = *rules[i];
|
||||
unsigned ut_size = r.get_uninterpreted_tail_size();
|
||||
for (unsigned j = 0; j < ut_size; ++j) {
|
||||
func_decl* f = r.get_tail(j)->get_decl();
|
||||
pred_transformer* rt = m_ctx.get_pred_transformers().find(f);
|
||||
bool found = false;
|
||||
for (unsigned k = 0; !found && k < levels.size(); ++k) {
|
||||
found = (rt == pts[k] && levels[k] + 1 == lvl);
|
||||
}
|
||||
if (!found) {
|
||||
levels.push_back(lvl-1);
|
||||
pts.push_back(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref result = mk_and(conjs);
|
||||
TRACE("pdr", tout << mk_pp(result, m) << "\n";);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Instantiate Peano induction schema.
|
||||
//
|
||||
void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
model_node* p = n.parent();
|
||||
if (p == nullptr) {
|
||||
return;
|
||||
}
|
||||
unsigned depth = 2;
|
||||
imp imp(m_ctx);
|
||||
ast_manager& m = core.get_manager();
|
||||
expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth);
|
||||
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
|
||||
ctx.assert_expr(goal);
|
||||
lbool r = ctx.check();
|
||||
TRACE("pdr", tout << r << "\n";
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
tout << mk_pp(core[i].get(), m) << "\n";
|
||||
});
|
||||
if (r == l_false) {
|
||||
core.reset();
|
||||
expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level());
|
||||
core.push_back(m.mk_not(phi));
|
||||
uses_level = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_generalizers.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Generalizer plugins.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_GENERALIZERS_H_
|
||||
#define PDR_GENERALIZERS_H_
|
||||
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_closure.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class core_bool_inductive_generalizer : public core_generalizer {
|
||||
unsigned m_failure_limit;
|
||||
public:
|
||||
core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {}
|
||||
~core_bool_inductive_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class r_map : public map<rational, T, rational::hash_proc, rational::eq_proc> {
|
||||
};
|
||||
|
||||
class core_arith_inductive_generalizer : public core_generalizer {
|
||||
typedef std::pair<expr*, unsigned> term_loc_t;
|
||||
typedef r_map<vector<term_loc_t> > bounds_t;
|
||||
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
expr_ref_vector m_refs;
|
||||
bounds_t m_lb;
|
||||
bounds_t m_ub;
|
||||
|
||||
struct eq {
|
||||
expr* m_term;
|
||||
rational m_value;
|
||||
unsigned m_i;
|
||||
unsigned m_j;
|
||||
eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {}
|
||||
};
|
||||
void reset();
|
||||
void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i);
|
||||
void get_eqs(expr_ref_vector const& core, svector<eq>& eqs);
|
||||
bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result);
|
||||
public:
|
||||
core_arith_inductive_generalizer(context& ctx);
|
||||
~core_arith_inductive_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
class core_farkas_generalizer : public core_generalizer {
|
||||
farkas_learner m_farkas_learner;
|
||||
public:
|
||||
core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p);
|
||||
~core_farkas_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
void collect_statistics(statistics& st) const override;
|
||||
};
|
||||
|
||||
|
||||
class core_convex_hull_generalizer : public core_generalizer {
|
||||
ast_manager& m;
|
||||
obj_map<expr, expr*> m_models;
|
||||
bool m_is_closure;
|
||||
void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
|
||||
void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
|
||||
bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B);
|
||||
bool is_unsat(expr_ref_vector const& As, expr* B);
|
||||
public:
|
||||
core_convex_hull_generalizer(context& ctx, bool is_closure);
|
||||
~core_convex_hull_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override;
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
class core_multi_generalizer : public core_generalizer {
|
||||
core_bool_inductive_generalizer m_gen;
|
||||
public:
|
||||
core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {}
|
||||
~core_multi_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override;
|
||||
};
|
||||
|
||||
class core_induction_generalizer : public core_generalizer {
|
||||
class imp;
|
||||
public:
|
||||
core_induction_generalizer(context& ctx): core_generalizer(ctx) {}
|
||||
~core_induction_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
};
|
||||
#endif
|
|
@ -1,321 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for PDR, 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/pdr/pdr_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 pdr {
|
||||
|
||||
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("pdr", model_smt2_pp(tout, m, *md, 0););
|
||||
apply(const_cast<model_converter_ref&>(m_mc), md);
|
||||
}
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) :
|
||||
m(manager),
|
||||
m_fparams(fparams),
|
||||
m_brwr(m),
|
||||
m_mux(m),
|
||||
m_background(m.mk_true(), m),
|
||||
m_contexts(fparams, max_num_contexts, m),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) {
|
||||
smt::kernel sctx(m, get_fparams());
|
||||
if(bg) {
|
||||
sctx.assert_expr(bg);
|
||||
}
|
||||
sctx.assert_expr(lhs);
|
||||
expr_ref neg_rhs(m.mk_not(rhs),m);
|
||||
sctx.assert_expr(neg_rhs);
|
||||
lbool smt_res = sctx.check();
|
||||
return smt_res==l_false;
|
||||
}
|
||||
|
||||
};
|
|
@ -1,304 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for PDR, taking care of creating of AST
|
||||
objects and conversions between them.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_MANAGER_H_
|
||||
#define PDR_MANAGER_H_
|
||||
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#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/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_sym_mux.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
|
||||
|
||||
namespace smt {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace pdr {
|
||||
|
||||
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;
|
||||
smt_params& m_fparams;
|
||||
|
||||
mutable bool_rewriter m_brwr;
|
||||
|
||||
sym_mux m_mux;
|
||||
expr_ref m_background;
|
||||
decl_vector m_o0_preds;
|
||||
pdr::smt_context_manager m_contexts;
|
||||
|
||||
/** whenever we need an unique number, we get this one and increase */
|
||||
unsigned m_next_unique_num;
|
||||
|
||||
|
||||
unsigned n_index() const { return 0; }
|
||||
unsigned o_index(unsigned i) const { return i+1; }
|
||||
|
||||
void add_new_state(func_decl * s);
|
||||
|
||||
public:
|
||||
manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager);
|
||||
|
||||
ast_manager& get_manager() const { return m; }
|
||||
smt_params& get_fparams() const { return m_fparams; }
|
||||
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));
|
||||
}
|
||||
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) {
|
||||
return m_mux.conv(p, o_index(o_idx), n_index());
|
||||
}
|
||||
func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) {
|
||||
return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));
|
||||
}
|
||||
func_decl * n2o(func_decl * p, unsigned o_idx) {
|
||||
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)
|
||||
{ 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)
|
||||
{ m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); }
|
||||
|
||||
void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result)
|
||||
{ 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)
|
||||
{ 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) {
|
||||
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; }
|
||||
|
||||
|
||||
/**
|
||||
Return true if we can show that lhs => rhs. The function can have false negatives
|
||||
(i.e. when smt::context returns unknown), but no false positives.
|
||||
|
||||
bg is background knowledge and can be null
|
||||
*/
|
||||
bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=nullptr);
|
||||
|
||||
unsigned get_unique_num() { return m_next_unique_num++; }
|
||||
|
||||
pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); }
|
||||
|
||||
void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); }
|
||||
|
||||
void reset_statistics() { m_contexts.reset_statistics(); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,459 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
prop_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver abstraction for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "model/model.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_prop_solver.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "model/model_pp.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
|
||||
//
|
||||
// Auxiliary structure to introduce propositional names for assumptions that are not
|
||||
// propositional. It is to work with the smt::context's restriction
|
||||
// that assumptions be propositional literals.
|
||||
//
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class prop_solver::safe_assumptions {
|
||||
prop_solver& s;
|
||||
ast_manager& m;
|
||||
expr_ref_vector m_atoms;
|
||||
expr_ref_vector m_assumptions;
|
||||
obj_map<app,expr *> m_proxies2expr;
|
||||
obj_map<expr, app*> m_expr2proxies;
|
||||
unsigned m_num_proxies;
|
||||
|
||||
app * mk_proxy(expr* literal) {
|
||||
app* res;
|
||||
SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables
|
||||
if (m_expr2proxies.find(literal, res)) {
|
||||
return res;
|
||||
}
|
||||
SASSERT(s.m_proxies.size() >= m_num_proxies);
|
||||
if (m_num_proxies == s.m_proxies.size()) {
|
||||
std::stringstream name;
|
||||
name << "pdr_proxy_" << s.m_proxies.size();
|
||||
res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
|
||||
s.m_proxies.push_back(res);
|
||||
s.m_aux_symbols.insert(res->get_decl());
|
||||
}
|
||||
else {
|
||||
res = s.m_proxies[m_num_proxies].get();
|
||||
}
|
||||
++m_num_proxies;
|
||||
m_expr2proxies.insert(literal, res);
|
||||
m_proxies2expr.insert(res, literal);
|
||||
expr_ref implies(m.mk_or(m.mk_not(res), literal), m);
|
||||
s.m_ctx->assert_expr(implies);
|
||||
m_assumptions.push_back(implies);
|
||||
TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";);
|
||||
return res;
|
||||
}
|
||||
|
||||
void mk_safe(expr_ref_vector& conjs) {
|
||||
flatten_and(conjs);
|
||||
expand_literals(conjs);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr * lit = conjs[i].get();
|
||||
expr * lit_core = lit;
|
||||
m.is_not(lit, lit_core);
|
||||
SASSERT(!m.is_true(lit));
|
||||
if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) {
|
||||
conjs[i] = mk_proxy(lit);
|
||||
}
|
||||
}
|
||||
m_assumptions.append(conjs);
|
||||
}
|
||||
|
||||
expr* apply_accessor(
|
||||
ptr_vector<func_decl> const& acc,
|
||||
unsigned j,
|
||||
func_decl* f,
|
||||
expr* c) {
|
||||
if (is_app(c) && to_app(c)->get_decl() == f) {
|
||||
return to_app(c)->get_arg(j);
|
||||
}
|
||||
else {
|
||||
return m.mk_app(acc[j], c);
|
||||
}
|
||||
}
|
||||
|
||||
void expand_literals(expr_ref_vector& conjs) {
|
||||
arith_util arith(m);
|
||||
datatype_util dt(m);
|
||||
bv_util bv(m);
|
||||
expr* e1, *e2, *c, *val;
|
||||
rational r;
|
||||
unsigned bv_size;
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "begin expand\n";
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
tout << mk_pp(conjs[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr* e = conjs[i].get();
|
||||
if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) {
|
||||
conjs[i] = arith.mk_le(e1,e2);
|
||||
if (i+1 == conjs.size()) {
|
||||
conjs.push_back(arith.mk_ge(e1, e2));
|
||||
}
|
||||
else {
|
||||
conjs.push_back(conjs[i+1].get());
|
||||
conjs[i+1] = arith.mk_ge(e1, e2);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) ||
|
||||
(m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){
|
||||
func_decl* f = to_app(val)->get_decl();
|
||||
func_decl* r = dt.get_constructor_is(f);
|
||||
conjs[i] = m.mk_app(r, c);
|
||||
ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
|
||||
for (unsigned j = 0; j < acc.size(); ++j) {
|
||||
conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j)));
|
||||
}
|
||||
}
|
||||
else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
|
||||
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
|
||||
rational two(2);
|
||||
for (unsigned j = 0; j < bv_size; ++j) {
|
||||
parameter p(j);
|
||||
//expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c);
|
||||
expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c));
|
||||
if ((r % two).is_zero()) {
|
||||
e = m.mk_not(e);
|
||||
}
|
||||
r = div(r, two);
|
||||
if (j == 0) {
|
||||
conjs[i] = e;
|
||||
}
|
||||
else {
|
||||
conjs.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("pdr",
|
||||
tout << "end expand\n";
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
tout << mk_pp(conjs[i].get(), m) << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions):
|
||||
s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) {
|
||||
mk_safe(m_atoms);
|
||||
}
|
||||
|
||||
~safe_assumptions() {
|
||||
}
|
||||
|
||||
expr_ref_vector const& atoms() const { return m_atoms; }
|
||||
|
||||
unsigned assumptions_size() const { return m_assumptions.size(); }
|
||||
|
||||
expr* assumptions(unsigned i) const { return m_assumptions[i]; }
|
||||
|
||||
void undo_proxies(expr_ref_vector& es) {
|
||||
expr_ref e(m);
|
||||
expr* r;
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
e = es[i].get();
|
||||
if (is_app(e) && m_proxies2expr.find(to_app(e), r)) {
|
||||
es[i] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void elim_proxies(expr_ref_vector& es) {
|
||||
expr_substitution sub(m, false, m.proofs_enabled());
|
||||
proof_ref pr(m);
|
||||
if (m.proofs_enabled()) {
|
||||
pr = m.mk_asserted(m.mk_true());
|
||||
}
|
||||
obj_map<app,expr*>::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end();
|
||||
for (; it != end; ++it) {
|
||||
sub.insert(it->m_key, m.mk_true(), pr);
|
||||
}
|
||||
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
|
||||
rep->set_substitution(&sub);
|
||||
replace_proxies(*rep, es);
|
||||
}
|
||||
private:
|
||||
|
||||
void replace_proxies(expr_replacer& rep, expr_ref_vector& es) {
|
||||
expr_ref e(m);
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
e = es[i].get();
|
||||
rep(e);
|
||||
es[i] = e;
|
||||
if (m.is_true(e)) {
|
||||
es[i] = es.back();
|
||||
es.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
prop_solver::prop_solver(manager& pm, symbol const& name) :
|
||||
m_fparams(pm.get_fparams()),
|
||||
m(pm.get_manager()),
|
||||
m_pm(pm),
|
||||
m_name(name),
|
||||
m_ctx(pm.mk_fresh()),
|
||||
m_pos_level_atoms(m),
|
||||
m_neg_level_atoms(m),
|
||||
m_proxies(m),
|
||||
m_core(nullptr),
|
||||
m_model(nullptr),
|
||||
m_consequences(nullptr),
|
||||
m_subset_based_core(false),
|
||||
m_use_farkas(false),
|
||||
m_in_level(false),
|
||||
m_current_level(0)
|
||||
{
|
||||
m_ctx->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, nullptr,m.mk_bool_sort());
|
||||
m_aux_symbols.insert(lev_pred);
|
||||
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::push_level_atoms(unsigned level, expr_ref_vector& tgt) const {
|
||||
unsigned lev_cnt = level_cnt();
|
||||
for (unsigned i=0; i<lev_cnt; i++) {
|
||||
bool active = i>=level;
|
||||
app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i];
|
||||
tgt.push_back(lev_atom);
|
||||
}
|
||||
}
|
||||
|
||||
void prop_solver::add_formula(expr * form) {
|
||||
SASSERT(!m_in_level);
|
||||
m_ctx->assert_expr(form);
|
||||
IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";);
|
||||
TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";);
|
||||
}
|
||||
|
||||
void prop_solver::add_level_formula(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);
|
||||
add_formula(lform.get());
|
||||
}
|
||||
|
||||
|
||||
lbool prop_solver::check_safe_assumptions(
|
||||
safe_assumptions& safe,
|
||||
const expr_ref_vector& atoms)
|
||||
{
|
||||
flet<bool> _model(m_fparams.m_model, m_model != nullptr);
|
||||
expr_ref_vector expr_atoms(m);
|
||||
expr_atoms.append(atoms.size(), atoms.c_ptr());
|
||||
|
||||
if (m_in_level) {
|
||||
push_level_atoms(m_current_level, expr_atoms);
|
||||
}
|
||||
|
||||
lbool result = m_ctx->check(expr_atoms);
|
||||
|
||||
TRACE("pdr",
|
||||
tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n";
|
||||
tout << result << "\n";);
|
||||
|
||||
if (result == l_true && m_model) {
|
||||
m_ctx->get_model(*m_model);
|
||||
TRACE("pdr_verbose", model_pp(tout, **m_model); );
|
||||
}
|
||||
|
||||
if (result == l_false) {
|
||||
unsigned core_size = m_ctx->get_unsat_core_size();
|
||||
m_assumes_level = false;
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) {
|
||||
m_assumes_level = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == l_false &&
|
||||
m_core &&
|
||||
m.proofs_enabled() &&
|
||||
m_use_farkas &&
|
||||
!m_subset_based_core) {
|
||||
extract_theory_core(safe);
|
||||
}
|
||||
else if (result == l_false && m_core) {
|
||||
extract_subset_core(safe);
|
||||
SASSERT(expr_atoms.size() >= m_core->size());
|
||||
}
|
||||
m_core = nullptr;
|
||||
m_model = nullptr;
|
||||
m_subset_based_core = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void prop_solver::extract_subset_core(safe_assumptions& safe) {
|
||||
unsigned core_size = m_ctx->get_unsat_core_size();
|
||||
m_core->reset();
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
expr * core_expr = m_ctx->get_unsat_core_expr(i);
|
||||
SASSERT(is_app(core_expr));
|
||||
|
||||
if (m_level_atoms_set.contains(core_expr)) {
|
||||
continue;
|
||||
}
|
||||
if (m_ctx->is_aux_predicate(core_expr)) {
|
||||
continue;
|
||||
}
|
||||
m_core->push_back(to_app(core_expr));
|
||||
}
|
||||
|
||||
safe.undo_proxies(*m_core);
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "core_exprs: ";
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " ";
|
||||
}
|
||||
tout << "\n";
|
||||
tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void prop_solver::extract_theory_core(safe_assumptions& safe) {
|
||||
proof_ref pr(m);
|
||||
pr = m_ctx->get_proof();
|
||||
IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";);
|
||||
farkas_learner fl(m_fparams, m);
|
||||
expr_ref_vector lemmas(m);
|
||||
obj_hashtable<expr> bs;
|
||||
for (unsigned i = 0; i < safe.assumptions_size(); ++i) {
|
||||
bs.insert(safe.assumptions(i));
|
||||
}
|
||||
fl.get_lemmas(pr, bs, lemmas);
|
||||
safe.elim_proxies(lemmas);
|
||||
fl.simplify_lemmas(lemmas); // redundant?
|
||||
|
||||
bool outside_of_logic =
|
||||
(m_fparams.m_arith_mode == AS_DIFF_LOGIC &&
|
||||
!is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) ||
|
||||
(m_fparams.m_arith_mode == AS_UTVPI &&
|
||||
!is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr()));
|
||||
|
||||
if (outside_of_logic) {
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << "not diff\n";
|
||||
for (unsigned i = 0; i < lemmas.size(); ++i) {
|
||||
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
|
||||
});
|
||||
extract_subset_core(safe);
|
||||
}
|
||||
else {
|
||||
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << "Lemmas\n";
|
||||
for (unsigned i = 0; i < lemmas.size(); ++i) {
|
||||
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
m_core->reset();
|
||||
m_core->append(lemmas);
|
||||
|
||||
if (m_consequences) {
|
||||
fl.get_consequences(pr, bs, *m_consequences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) {
|
||||
return check_assumptions_and_formula(atoms, m.mk_true());
|
||||
}
|
||||
|
||||
lbool prop_solver::check_conjunction_as_assumptions(expr * conj) {
|
||||
expr_ref_vector asmp(m);
|
||||
asmp.push_back(conj);
|
||||
return check_assumptions(asmp);
|
||||
}
|
||||
|
||||
lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form)
|
||||
{
|
||||
pdr::smt_context::scoped _scoped(*m_ctx);
|
||||
safe_assumptions safe(*this, atoms);
|
||||
m_ctx->assert_expr(form);
|
||||
CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";);
|
||||
lbool res = check_safe_assumptions(safe, safe.atoms());
|
||||
|
||||
//
|
||||
// we don't have to undo model naming, as from the model
|
||||
// we extract the values for state variables directly
|
||||
//
|
||||
return res;
|
||||
}
|
||||
|
||||
void prop_solver::collect_statistics(statistics& st) const {
|
||||
}
|
||||
|
||||
void prop_solver::reset_statistics() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
prop_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SAT solver abstraction for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-17.
|
||||
|
||||
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/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
|
||||
|
||||
namespace pdr {
|
||||
class prop_solver {
|
||||
|
||||
private:
|
||||
smt_params& m_fparams;
|
||||
ast_manager& m;
|
||||
manager& m_pm;
|
||||
symbol m_name;
|
||||
scoped_ptr<pdr::smt_context> m_ctx;
|
||||
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;
|
||||
app_ref_vector m_proxies; // predicates for assumptions
|
||||
expr_ref_vector* m_core;
|
||||
model_ref* m_model;
|
||||
expr_ref_vector* m_consequences;
|
||||
bool m_subset_based_core;
|
||||
bool m_assumes_level;
|
||||
bool m_use_farkas;
|
||||
func_decl_set m_aux_symbols;
|
||||
bool m_in_level;
|
||||
unsigned m_current_level; // set when m_in_level
|
||||
|
||||
/** Add level atoms activating certain level into a vector */
|
||||
void push_level_atoms(unsigned level, expr_ref_vector & tgt) const;
|
||||
|
||||
void ensure_level(unsigned lvl);
|
||||
|
||||
class safe_assumptions;
|
||||
|
||||
void extract_theory_core(safe_assumptions& assumptions);
|
||||
|
||||
void extract_subset_core(safe_assumptions& assumptions);
|
||||
|
||||
lbool check_safe_assumptions(
|
||||
safe_assumptions& assumptions,
|
||||
expr_ref_vector const& atoms);
|
||||
|
||||
|
||||
public:
|
||||
prop_solver(pdr::manager& pm, symbol const& name);
|
||||
|
||||
/** return true is s is a symbol introduced by prop_solver */
|
||||
bool is_aux_symbol(func_decl * s) const {
|
||||
return
|
||||
m_aux_symbols.contains(s) ||
|
||||
m_ctx->is_aux_predicate(s);
|
||||
}
|
||||
|
||||
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; }
|
||||
void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; }
|
||||
|
||||
bool assumes_level() const { return m_assumes_level; }
|
||||
|
||||
void add_level();
|
||||
unsigned level_cnt() const;
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
void set_use_farkas(bool f) { m_use_farkas = f; }
|
||||
bool get_use_farkas() const { return m_use_farkas; }
|
||||
|
||||
void add_formula(expr * form);
|
||||
void add_level_formula(expr * form, unsigned level);
|
||||
|
||||
/**
|
||||
* Return true iff conjunction of atoms is consistent with the current state of
|
||||
* the solver.
|
||||
*
|
||||
* If the conjunction of atoms is inconsistent with the solver state and core is non-zero,
|
||||
* core will contain an unsatisfiable core of atoms.
|
||||
*
|
||||
* If the conjunction of atoms is consistent with the solver state and o_model is non-zero,
|
||||
* o_model will contain the "o" literals true in the assignment.
|
||||
*/
|
||||
lbool check_assumptions(const expr_ref_vector & atoms);
|
||||
|
||||
lbool check_conjunction_as_assumptions(expr * conj);
|
||||
|
||||
/**
|
||||
* Like check_assumptions, except it also asserts an extra formula
|
||||
*/
|
||||
lbool check_assumptions_and_formula(
|
||||
const expr_ref_vector & atoms,
|
||||
expr * form);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
void reset_statistics();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -1,132 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
reachable_cache.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Object for caching of reachable states.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_reachable_cache.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm)
|
||||
: m(pm.get_manager()),
|
||||
m_pm(pm),
|
||||
m_ctx(nullptr),
|
||||
m_ref_holder(m),
|
||||
m_disj_connector(m),
|
||||
m_cache_mode(cm) {
|
||||
if (m_cache_mode == datalog::CONSTRAINT_CACHE) {
|
||||
m_ctx = pm.mk_fresh();
|
||||
m_ctx->assert_expr(m_pm.get_background());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reachable_cache::add_disjuncted_formula(expr * f) {
|
||||
app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m);
|
||||
app_ref neg_new_connector(m.mk_not(new_connector), m);
|
||||
app_ref extended_form(m);
|
||||
|
||||
if(m_disj_connector) {
|
||||
extended_form = m.mk_or(m_disj_connector, neg_new_connector, f);
|
||||
}
|
||||
else {
|
||||
extended_form = m.mk_or(neg_new_connector, f);
|
||||
}
|
||||
if (m_ctx) {
|
||||
m_ctx->assert_expr(extended_form);
|
||||
}
|
||||
|
||||
m_disj_connector = new_connector;
|
||||
}
|
||||
|
||||
void reachable_cache::add_reachable(expr * cube) {
|
||||
|
||||
switch (m_cache_mode) {
|
||||
case datalog::NO_CACHE:
|
||||
break;
|
||||
|
||||
case datalog::HASH_CACHE:
|
||||
m_stats.m_inserts++;
|
||||
m_cache.insert(cube);
|
||||
m_ref_holder.push_back(cube);
|
||||
break;
|
||||
|
||||
case datalog::CONSTRAINT_CACHE:
|
||||
m_stats.m_inserts++;
|
||||
TRACE("pdr", tout << mk_pp(cube, m) << "\n";);
|
||||
add_disjuncted_formula(cube);
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool reachable_cache::is_reachable(expr * cube) {
|
||||
bool found = false;
|
||||
switch (m_cache_mode) {
|
||||
case datalog::NO_CACHE:
|
||||
return false;
|
||||
|
||||
case datalog::HASH_CACHE:
|
||||
found = m_cache.contains(cube);
|
||||
break;
|
||||
|
||||
case datalog::CONSTRAINT_CACHE: {
|
||||
if(!m_disj_connector) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
expr * connector = m_disj_connector.get();
|
||||
expr_ref_vector assms(m);
|
||||
assms.push_back(connector);
|
||||
m_ctx->push();
|
||||
m_ctx->assert_expr(cube);
|
||||
lbool res = m_ctx->check(assms);
|
||||
m_ctx->pop();
|
||||
|
||||
TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";);
|
||||
|
||||
found = res == l_true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
m_stats.m_hits++;
|
||||
}
|
||||
else {
|
||||
m_stats.m_miss++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void reachable_cache::collect_statistics(statistics& st) const {
|
||||
st.update("cache inserts", m_stats.m_inserts);
|
||||
st.update("cache miss", m_stats.m_miss);
|
||||
st.update("cache hits", m_stats.m_hits);
|
||||
}
|
||||
|
||||
void reachable_cache::reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
reachable_cache.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Object for caching of reachable states.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#ifndef REACHABLE_CACHE_H_
|
||||
#define REACHABLE_CACHE_H_
|
||||
#include "ast/ast.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
|
||||
namespace pdr {
|
||||
class reachable_cache {
|
||||
struct stats {
|
||||
unsigned m_hits;
|
||||
unsigned m_miss;
|
||||
unsigned m_inserts;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
ast_manager & m;
|
||||
manager & m_pm;
|
||||
scoped_ptr<smt_context> m_ctx;
|
||||
ast_ref_vector m_ref_holder;
|
||||
app_ref m_disj_connector;
|
||||
obj_hashtable<expr> m_cache;
|
||||
stats m_stats;
|
||||
datalog::PDR_CACHE_MODE m_cache_mode;
|
||||
|
||||
void add_disjuncted_formula(expr * f);
|
||||
|
||||
public:
|
||||
reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm);
|
||||
|
||||
void add_init(app * f) { add_disjuncted_formula(f); }
|
||||
|
||||
/** add cube whose all models are reachable */
|
||||
void add_reachable(expr * cube);
|
||||
|
||||
/** return true if there is a model of cube which is reachable */
|
||||
bool is_reachable(expr * cube);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
void reset_statistics();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,167 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_smt_context_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
#include "ast/has_free_vars.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_smt_pp.h"
|
||||
#include <sstream>
|
||||
#include "smt/params/smt_params.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred):
|
||||
m_pred(pred, m),
|
||||
m_parent(p),
|
||||
m_in_delay_scope(false),
|
||||
m_pushed(false)
|
||||
{}
|
||||
|
||||
bool smt_context::is_aux_predicate(func_decl* p) {
|
||||
return m_parent.is_aux_predicate(p);
|
||||
}
|
||||
|
||||
smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) {
|
||||
SASSERT(!m_ctx.m_in_delay_scope);
|
||||
SASSERT(!m_ctx.m_pushed);
|
||||
m_ctx.m_in_delay_scope = true;
|
||||
}
|
||||
|
||||
smt_context::scoped::~scoped() {
|
||||
SASSERT(m_ctx.m_in_delay_scope);
|
||||
if (m_ctx.m_pushed) {
|
||||
m_ctx.pop();
|
||||
m_ctx.m_pushed = false;
|
||||
}
|
||||
m_ctx.m_in_delay_scope = false;
|
||||
}
|
||||
|
||||
|
||||
_smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred):
|
||||
smt_context(p, ctx.m(), pred),
|
||||
m_context(ctx)
|
||||
{}
|
||||
|
||||
void _smt_context::assert_expr(expr* e) {
|
||||
ast_manager& m = m_context.m();
|
||||
if (m.is_true(e)) {
|
||||
return;
|
||||
}
|
||||
CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";);
|
||||
SASSERT(!has_free_vars(e));
|
||||
if (m_in_delay_scope && !m_pushed) {
|
||||
m_context.push();
|
||||
m_pushed = true;
|
||||
}
|
||||
expr_ref fml(m);
|
||||
fml = m_pushed?e:m.mk_implies(m_pred, e);
|
||||
m_context.assert_expr(fml);
|
||||
}
|
||||
|
||||
lbool _smt_context::check(expr_ref_vector& assumptions) {
|
||||
ast_manager& m = m_pred.get_manager();
|
||||
if (!m.is_true(m_pred)) {
|
||||
assumptions.push_back(m_pred);
|
||||
}
|
||||
TRACE("pdr_check",
|
||||
{
|
||||
ast_smt_pp pp(m);
|
||||
for (unsigned i = 0; i < m_context.size(); ++i) {
|
||||
pp.add_assumption(m_context.get_formula(i));
|
||||
}
|
||||
for (unsigned i = 0; i < assumptions.size(); ++i) {
|
||||
pp.add_assumption(assumptions[i].get());
|
||||
}
|
||||
|
||||
static unsigned lemma_id = 0;
|
||||
std::ostringstream strm;
|
||||
strm << "pdr-lemma-" << lemma_id << ".smt2";
|
||||
std::ofstream out(strm.str().c_str());
|
||||
pp.display_smt2(out, m.mk_true());
|
||||
out.close();
|
||||
lemma_id++;
|
||||
tout << "pdr_check: " << strm.str() << "\n";
|
||||
});
|
||||
lbool result = m_context.check(assumptions.size(), assumptions.c_ptr());
|
||||
if (!m.is_true(m_pred)) {
|
||||
assumptions.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _smt_context::get_model(model_ref& model) {
|
||||
m_context.get_model(model);
|
||||
}
|
||||
|
||||
proof* _smt_context::get_proof() {
|
||||
return m_context.get_proof();
|
||||
}
|
||||
|
||||
smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m):
|
||||
m_fparams(fp),
|
||||
m(m),
|
||||
m_max_num_contexts(max_num_contexts),
|
||||
m_num_contexts(0),
|
||||
m_predicate_list(m) {
|
||||
}
|
||||
|
||||
|
||||
smt_context_manager::~smt_context_manager() {
|
||||
TRACE("pdr",tout << "\n";);
|
||||
std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc<smt::kernel>());
|
||||
}
|
||||
|
||||
smt_context* smt_context_manager::mk_fresh() {
|
||||
++m_num_contexts;
|
||||
app_ref pred(m);
|
||||
smt::kernel * ctx = nullptr;
|
||||
if (m_max_num_contexts == 0) {
|
||||
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
|
||||
pred = m.mk_true();
|
||||
ctx = m_contexts[m_num_contexts-1];
|
||||
}
|
||||
else {
|
||||
if (m_contexts.size() < m_max_num_contexts) {
|
||||
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
|
||||
}
|
||||
std::stringstream name;
|
||||
name << "#context" << m_num_contexts;
|
||||
pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
|
||||
m_predicate_list.push_back(pred);
|
||||
m_predicate_set.insert(pred->get_decl());
|
||||
ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts];
|
||||
}
|
||||
return alloc(_smt_context, *ctx, *this, pred);
|
||||
}
|
||||
|
||||
void smt_context_manager::collect_statistics(statistics& st) const {
|
||||
for (unsigned i = 0; i < m_contexts.size(); ++i) {
|
||||
m_contexts[i]->collect_statistics(st);
|
||||
}
|
||||
}
|
||||
|
||||
void smt_context_manager::reset_statistics() {
|
||||
for (unsigned i = 0; i < m_contexts.size(); ++i) {
|
||||
m_contexts[i]->reset_statistics();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_smt_context_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_SMT_CONTEXT_MANAGER_H_
|
||||
#define PDR_SMT_CONTEXT_MANAGER_H_
|
||||
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "ast/func_decl_dependencies.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class smt_context_manager;
|
||||
|
||||
class smt_context {
|
||||
protected:
|
||||
app_ref m_pred;
|
||||
smt_context_manager& m_parent;
|
||||
bool m_in_delay_scope;
|
||||
bool m_pushed;
|
||||
public:
|
||||
smt_context(smt_context_manager& p, ast_manager& m, app* pred);
|
||||
virtual ~smt_context() {}
|
||||
virtual void assert_expr(expr* e) = 0;
|
||||
virtual lbool check(expr_ref_vector& assumptions) = 0;
|
||||
virtual void get_model(model_ref& model) = 0;
|
||||
virtual proof* get_proof() = 0;
|
||||
virtual unsigned get_unsat_core_size() = 0;
|
||||
virtual expr* get_unsat_core_expr(unsigned i) = 0;
|
||||
virtual void push() = 0;
|
||||
virtual void pop() = 0;
|
||||
bool is_aux_predicate(func_decl* p);
|
||||
bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); }
|
||||
class scoped {
|
||||
smt_context& m_ctx;
|
||||
public:
|
||||
scoped(smt_context& ctx);
|
||||
~scoped();
|
||||
};
|
||||
};
|
||||
|
||||
class _smt_context : public smt_context {
|
||||
smt::kernel & m_context;
|
||||
public:
|
||||
_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred);
|
||||
~_smt_context() override {}
|
||||
void assert_expr(expr* e) override;
|
||||
lbool check(expr_ref_vector& assumptions) override;
|
||||
void get_model(model_ref& model) override;
|
||||
proof* get_proof() override;
|
||||
void push() override { m_context.push(); }
|
||||
void pop() override { m_context.pop(1); }
|
||||
unsigned get_unsat_core_size() override { return m_context.get_unsat_core_size(); }
|
||||
expr* get_unsat_core_expr(unsigned i) override { return m_context.get_unsat_core_expr(i); }
|
||||
};
|
||||
|
||||
class smt_context_manager {
|
||||
smt_params& m_fparams;
|
||||
ast_manager& m;
|
||||
unsigned m_max_num_contexts;
|
||||
ptr_vector<smt::kernel> m_contexts;
|
||||
unsigned m_num_contexts;
|
||||
app_ref_vector m_predicate_list;
|
||||
func_decl_set m_predicate_set;
|
||||
public:
|
||||
smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m);
|
||||
~smt_context_manager();
|
||||
smt_context* mk_fresh();
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,601 +0,0 @@
|
|||
/*++
|
||||
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 <sstream>
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "model/model.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_sym_mux.h"
|
||||
|
||||
using namespace pdr;
|
||||
|
||||
sym_mux::sym_mux(ast_manager & m)
|
||||
: m(m), m_ref_holder(m),
|
||||
m_next_sym_suffix_idx(0) {
|
||||
m_suffixes.push_back("_n");
|
||||
size_t suf_sz = m_suffixes.size();
|
||||
for(unsigned i = 0; i < suf_sz; ++i) {
|
||||
symbol suff_sym = symbol(m_suffixes[i].c_str());
|
||||
m_used_suffixes.insert(suff_sym);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sym_mux::get_suffix(unsigned i) {
|
||||
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);
|
||||
}
|
||||
std::string result = m_suffixes[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
sym_mux & m_parent;
|
||||
unsigned m_from_idx;
|
||||
unsigned m_to_idx;
|
||||
bool m_homogenous;
|
||||
public:
|
||||
conv_rewriter_cfg(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)
|
||||
{
|
||||
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;
|
||||
sym_mux & m_parent;
|
||||
int m_shift;
|
||||
public:
|
||||
shifting_rewriter_cfg(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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
else {
|
||||
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
|
|
@ -1,247 +0,0 @@
|
|||
/*++
|
||||
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 pdr {
|
||||
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 */
|
||||
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);
|
||||
void ensure_tuple_size(func_decl * prim, unsigned sz);
|
||||
|
||||
public:
|
||||
sym_mux(ast_manager & m);
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
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) {
|
||||
func_decl * prim = try_get_primary_by_prefix(prefix);
|
||||
if(!prim) {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
|
||||
|
||||
/**
|
||||
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);
|
||||
void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
|
||||
expr_ref_vector & res);
|
||||
|
||||
/**
|
||||
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);
|
||||
|
||||
/**
|
||||
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(); }
|
||||
|
||||
std::string pp_model(const model_core & mdl) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,508 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_util.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "util/util.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/poly_rewriter.h"
|
||||
#include "ast/rewriter/poly_rewriter_def.h"
|
||||
#include "ast/rewriter/arith_rewriter.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "model/model.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
|
||||
|
||||
namespace pdr {
|
||||
|
||||
unsigned ceil_log2(unsigned u) {
|
||||
if (u == 0) { return 0; }
|
||||
unsigned pow2 = next_power_of_two(u);
|
||||
return get_num_1bits(pow2-1);
|
||||
}
|
||||
|
||||
std::string pp_cube(const ptr_vector<expr>& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const expr_ref_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const app_ref_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const app_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(unsigned sz, app * const * lits, ast_manager& m) {
|
||||
return pp_cube(sz, (expr * const *)(lits), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) {
|
||||
std::stringstream res;
|
||||
res << "(";
|
||||
expr * const * end = lits+sz;
|
||||
for (expr * const * it = lits; it!=end; it++) {
|
||||
res << mk_pp(*it, m);
|
||||
if (it+1!=end) {
|
||||
res << ", ";
|
||||
}
|
||||
}
|
||||
res << ")";
|
||||
return res.str();
|
||||
}
|
||||
|
||||
void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) {
|
||||
ast_manager& m = fml.get_manager();
|
||||
expr_ref_vector conjs(m);
|
||||
flatten_and(fml, conjs);
|
||||
obj_map<expr, unsigned> diseqs;
|
||||
expr* n, *lhs, *rhs;
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
if (m.is_not(conjs[i].get(), n) &&
|
||||
m.is_eq(n, lhs, rhs)) {
|
||||
if (!m.is_value(rhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
if (!m.is_value(rhs)) {
|
||||
continue;
|
||||
}
|
||||
diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++;
|
||||
}
|
||||
}
|
||||
expr_substitution sub(m);
|
||||
|
||||
unsigned orig_size = conjs.size();
|
||||
unsigned num_deleted = 0;
|
||||
expr_ref val(m), tmp(m);
|
||||
proof_ref pr(m);
|
||||
pr = m.mk_asserted(m.mk_true());
|
||||
obj_map<expr, unsigned>::iterator it = diseqs.begin();
|
||||
obj_map<expr, unsigned>::iterator end = diseqs.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->m_value >= threshold) {
|
||||
model.eval(it->m_key, val);
|
||||
sub.insert(it->m_key, val, pr);
|
||||
conjs.push_back(m.mk_eq(it->m_key, val));
|
||||
num_deleted += it->m_value;
|
||||
}
|
||||
}
|
||||
if (orig_size < conjs.size()) {
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer(m);
|
||||
rep->set_substitution(&sub);
|
||||
for (unsigned i = 0; i < orig_size; ++i) {
|
||||
tmp = conjs[i].get();
|
||||
(*rep)(tmp);
|
||||
if (m.is_true(tmp)) {
|
||||
conjs[i] = conjs.back();
|
||||
SASSERT(orig_size <= conjs.size());
|
||||
conjs.pop_back();
|
||||
SASSERT(orig_size <= 1 + conjs.size());
|
||||
if (i + 1 == orig_size) {
|
||||
// no-op.
|
||||
}
|
||||
else if (orig_size <= conjs.size()) {
|
||||
// no-op
|
||||
}
|
||||
else {
|
||||
SASSERT(orig_size == 1 + conjs.size());
|
||||
--orig_size;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
conjs[i] = tmp;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";);
|
||||
}
|
||||
fml = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
}
|
||||
|
||||
class test_diff_logic {
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
bv_util bv;
|
||||
bool m_is_dl;
|
||||
bool m_test_for_utvpi;
|
||||
|
||||
bool is_numeric(expr* e) const {
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* cond, *th, *el;
|
||||
if (m.is_ite(e, cond, th, el)) {
|
||||
return is_numeric(th) && is_numeric(el);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_arith_expr(expr *e) const {
|
||||
return is_app(e) && a.get_family_id() == to_app(e)->get_family_id();
|
||||
}
|
||||
|
||||
bool is_offset(expr* e) const {
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* cond, *th, *el, *e1, *e2;
|
||||
if (m.is_ite(e, cond, th, el)) {
|
||||
return is_offset(th) && is_offset(el);
|
||||
}
|
||||
// recognize offsets.
|
||||
if (a.is_add(e, e1, e2)) {
|
||||
if (is_numeric(e1)) {
|
||||
return is_offset(e2);
|
||||
}
|
||||
if (is_numeric(e2)) {
|
||||
return is_offset(e1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_test_for_utvpi) {
|
||||
if (a.is_mul(e, e1, e2)) {
|
||||
if (is_minus_one(e1)) {
|
||||
return is_offset(e2);
|
||||
}
|
||||
if (is_minus_one(e2)) {
|
||||
return is_offset(e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return !is_arith_expr(e);
|
||||
}
|
||||
|
||||
bool is_minus_one(expr const * e) const {
|
||||
rational r; return a.is_numeral(e, r) && r.is_minus_one();
|
||||
}
|
||||
|
||||
bool test_ineq(expr* e) const {
|
||||
SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e));
|
||||
SASSERT(to_app(e)->get_num_args() == 2);
|
||||
expr * lhs = to_app(e)->get_arg(0);
|
||||
expr * rhs = to_app(e)->get_arg(1);
|
||||
if (is_offset(lhs) && is_offset(rhs))
|
||||
return true;
|
||||
if (!is_numeric(rhs))
|
||||
std::swap(lhs, rhs);
|
||||
if (!is_numeric(rhs))
|
||||
return false;
|
||||
// lhs can be 'x' or '(+ x (* -1 y))'
|
||||
if (is_offset(lhs))
|
||||
return true;
|
||||
expr* arg1, *arg2;
|
||||
if (!a.is_add(lhs, arg1, arg2))
|
||||
return false;
|
||||
// x
|
||||
if (m_test_for_utvpi) {
|
||||
return is_offset(arg1) && is_offset(arg2);
|
||||
}
|
||||
if (is_arith_expr(arg1))
|
||||
std::swap(arg1, arg2);
|
||||
if (is_arith_expr(arg1))
|
||||
return false;
|
||||
// arg2: (* -1 y)
|
||||
expr* m1, *m2;
|
||||
if (!a.is_mul(arg2, m1, m2))
|
||||
return false;
|
||||
return is_minus_one(m1) && is_offset(m2);
|
||||
}
|
||||
|
||||
bool test_eq(expr* e) const {
|
||||
expr* lhs = nullptr, *rhs = nullptr;
|
||||
VERIFY(m.is_eq(e, lhs, rhs));
|
||||
if (!a.is_int_real(lhs)) {
|
||||
return true;
|
||||
}
|
||||
if (a.is_numeral(lhs) || a.is_numeral(rhs)) {
|
||||
return test_ineq(e);
|
||||
}
|
||||
return
|
||||
test_term(lhs) &&
|
||||
test_term(rhs) &&
|
||||
!a.is_mul(lhs) &&
|
||||
!a.is_mul(rhs);
|
||||
}
|
||||
|
||||
bool test_term(expr* e) const {
|
||||
if (m.is_bool(e)) {
|
||||
return true;
|
||||
}
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
if (is_offset(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* lhs, *rhs;
|
||||
if (a.is_add(e, lhs, rhs)) {
|
||||
if (!a.is_numeral(lhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
return a.is_numeral(lhs) && is_offset(rhs);
|
||||
}
|
||||
if (a.is_mul(e, lhs, rhs)) {
|
||||
return is_minus_one(lhs) || is_minus_one(rhs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_non_arith_or_basic(expr* e) {
|
||||
if (!is_app(e)) {
|
||||
return false;
|
||||
}
|
||||
family_id fid = to_app(e)->get_family_id();
|
||||
|
||||
if (fid == null_family_id &&
|
||||
!m.is_bool(e) &&
|
||||
to_app(e)->get_num_args() > 0) {
|
||||
return true;
|
||||
}
|
||||
return
|
||||
fid != m.get_basic_family_id() &&
|
||||
fid != null_family_id &&
|
||||
fid != a.get_family_id() &&
|
||||
fid != bv.get_family_id();
|
||||
}
|
||||
|
||||
public:
|
||||
test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {}
|
||||
|
||||
void test_for_utvpi() { m_test_for_utvpi = true; }
|
||||
|
||||
void operator()(expr* e) {
|
||||
if (!m_is_dl) {
|
||||
return;
|
||||
}
|
||||
if (a.is_le(e) || a.is_ge(e)) {
|
||||
m_is_dl = test_ineq(e);
|
||||
}
|
||||
else if (m.is_eq(e)) {
|
||||
m_is_dl = test_eq(e);
|
||||
}
|
||||
else if (is_non_arith_or_basic(e)) {
|
||||
m_is_dl = false;
|
||||
}
|
||||
else if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) {
|
||||
m_is_dl = test_term(a->get_arg(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_is_dl) {
|
||||
char const* msg = "non-diff: ";
|
||||
if (m_test_for_utvpi) {
|
||||
msg = "non-utvpi: ";
|
||||
}
|
||||
IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_dl() const { return m_is_dl; }
|
||||
};
|
||||
|
||||
bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) {
|
||||
test_diff_logic test(m);
|
||||
expr_fast_mark1 mark;
|
||||
for (unsigned i = 0; i < num_fmls; ++i) {
|
||||
quick_for_each_expr(test, mark, fmls[i]);
|
||||
}
|
||||
return test.is_dl();
|
||||
}
|
||||
|
||||
bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) {
|
||||
test_diff_logic test(m);
|
||||
test.test_for_utvpi();
|
||||
expr_fast_mark1 mark;
|
||||
for (unsigned i = 0; i < num_fmls; ++i) {
|
||||
quick_for_each_expr(test, mark, fmls[i]);
|
||||
}
|
||||
return test.is_dl();
|
||||
}
|
||||
|
||||
class arith_normalizer : public poly_rewriter<arith_rewriter_core> {
|
||||
ast_manager& m;
|
||||
arith_util m_util;
|
||||
enum op_kind { LE, GE, EQ };
|
||||
public:
|
||||
arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter<arith_rewriter_core>(m, p), m(m), m_util(m) {}
|
||||
|
||||
br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) {
|
||||
br_status st = BR_FAILED;
|
||||
if (m.is_eq(f)) {
|
||||
SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result);
|
||||
}
|
||||
|
||||
if (f->get_family_id() != get_fid()) {
|
||||
return st;
|
||||
}
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: st = BR_FAILED; break;
|
||||
case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break;
|
||||
case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break;
|
||||
case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break;
|
||||
case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break;
|
||||
case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break;
|
||||
default: st = BR_FAILED; break;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, EQ, result);
|
||||
}
|
||||
br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, LE, result);
|
||||
}
|
||||
br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, GE, result);
|
||||
}
|
||||
br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
result = m.mk_not(m_util.mk_ge(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
result = m.mk_not(m_util.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) {
|
||||
if (m_util.is_real(arg1)) {
|
||||
numeral g(0);
|
||||
get_coeffs(arg1, g);
|
||||
get_coeffs(arg2, g);
|
||||
if (!g.is_one() && !g.is_zero()) {
|
||||
SASSERT(g.is_pos());
|
||||
expr_ref new_arg1 = rdiv_polynomial(arg1, g);
|
||||
expr_ref new_arg2 = rdiv_polynomial(arg2, g);
|
||||
switch(kind) {
|
||||
case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE;
|
||||
case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE;
|
||||
case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
void update_coeff(numeral const& r, numeral& g) {
|
||||
if (g.is_zero() || abs(r) < g) {
|
||||
g = abs(r);
|
||||
}
|
||||
}
|
||||
|
||||
void get_coeffs(expr* e, numeral& g) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
expr* const* args = get_monomials(e, sz);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (!m_util.is_numeral(arg, r)) {
|
||||
get_power_product(arg, r);
|
||||
}
|
||||
update_coeff(r, g);
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref rdiv_polynomial(expr* e, numeral const& g) {
|
||||
rational r;
|
||||
SASSERT(g.is_pos());
|
||||
SASSERT(!g.is_one());
|
||||
expr_ref_vector monomes(m);
|
||||
unsigned sz;
|
||||
expr* const* args = get_monomials(e, sz);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (m_util.is_numeral(arg, r)) {
|
||||
monomes.push_back(m_util.mk_numeral(r/g, false));
|
||||
}
|
||||
else {
|
||||
expr* p = get_power_product(arg, r);
|
||||
r /= g;
|
||||
if (r.is_one()) {
|
||||
monomes.push_back(p);
|
||||
}
|
||||
else {
|
||||
monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p));
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_ref result(m);
|
||||
mk_add(monomes.size(), monomes.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct arith_normalizer_cfg: public default_rewriter_cfg {
|
||||
arith_normalizer m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {}
|
||||
};
|
||||
|
||||
class arith_normalizer_star : public rewriter_tpl<arith_normalizer_cfg> {
|
||||
arith_normalizer_cfg m_cfg;
|
||||
public:
|
||||
arith_normalizer_star(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<arith_normalizer_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, p) {}
|
||||
};
|
||||
|
||||
|
||||
void normalize_arithmetic(expr_ref& t) {
|
||||
ast_manager& m = t.get_manager();
|
||||
scoped_no_proof _sp(m);
|
||||
params_ref p;
|
||||
arith_normalizer_star rw(m, p);
|
||||
expr_ref tmp(m);
|
||||
rw(t, tmp);
|
||||
t = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template class rewriter_tpl<pdr::arith_normalizer_cfg>;
|
||||
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_util.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_UTIL_H_
|
||||
#define PDR_UTIL_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_util.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"
|
||||
|
||||
|
||||
class model;
|
||||
class model_core;
|
||||
|
||||
namespace pdr {
|
||||
|
||||
/**
|
||||
* Return the ceiling of base 2 logarithm of a number,
|
||||
* or zero if the nmber is zero.
|
||||
*/
|
||||
unsigned ceil_log2(unsigned u);
|
||||
|
||||
typedef ptr_vector<app> app_vector;
|
||||
typedef ptr_vector<func_decl> decl_vector;
|
||||
typedef obj_hashtable<func_decl> func_decl_set;
|
||||
|
||||
std::string pp_cube(const ptr_vector<expr>& model, ast_manager& manager);
|
||||
std::string pp_cube(const expr_ref_vector& model, ast_manager& manager);
|
||||
std::string pp_cube(const ptr_vector<app>& model, ast_manager& manager);
|
||||
std::string pp_cube(const app_ref_vector& model, ast_manager& manager);
|
||||
std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager);
|
||||
std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager);
|
||||
|
||||
|
||||
/**
|
||||
\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 normalize coefficients in polynomials so that least coefficient is 1.
|
||||
*/
|
||||
void normalize_arithmetic(expr_ref& t);
|
||||
|
||||
|
||||
/**
|
||||
\brief determine if formulas belong to difference logic or UTVPI fragment.
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue