mirror of
https://github.com/Z3Prover/z3
synced 2025-04-28 03:15:50 +00:00
Merge branch 'master' into polysat
This commit is contained in:
commit
20b5455d08
669 changed files with 26145 additions and 20652 deletions
|
@ -1,25 +1,19 @@
|
|||
z3_add_component(tactic
|
||||
SOURCES
|
||||
dependency_converter.cpp
|
||||
equiv_proof_converter.cpp
|
||||
generic_model_converter.cpp
|
||||
goal.cpp
|
||||
goal_num_occurs.cpp
|
||||
goal_shared_occs.cpp
|
||||
goal_util.cpp
|
||||
horn_subsume_model_converter.cpp
|
||||
model_converter.cpp
|
||||
probe.cpp
|
||||
proof_converter.cpp
|
||||
replace_proof_converter.cpp
|
||||
tactical.cpp
|
||||
tactic.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
ast
|
||||
model
|
||||
simplifiers
|
||||
converters
|
||||
TACTIC_HEADERS
|
||||
probe.h
|
||||
tactic.h
|
||||
PYG_FILES
|
||||
tactic_params.pyg
|
||||
)
|
||||
|
|
|
@ -13,7 +13,31 @@ Author:
|
|||
|
||||
Leonardo (leonardo) 2011-10-24
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic aig
|
||||
|
||||
### Short Description
|
||||
|
||||
Simplify Boolean structure using AIGs (And-inverter graphs).
|
||||
|
||||
### Long Description
|
||||
|
||||
And-inverter graphs (AIGs) uses just the Boolean connectives `and` and `not` to encode Boolean
|
||||
formulas. The circuit representation using AIGs first converts formulas using other connectives to this normal form,
|
||||
then performs local simplification steps to minimize the circuit representation.
|
||||
Note that the simplification steps used by this tactic are heuristic, trading speed for power,
|
||||
and do not represent a high-quality circuit minimization approach.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const a Bool)
|
||||
(declare-const b Bool)
|
||||
(declare-const c Bool)
|
||||
(assert (or (and a b) (and b a c)))
|
||||
(apply aig)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -2,11 +2,8 @@ z3_add_component(arith_tactics
|
|||
SOURCES
|
||||
add_bounds_tactic.cpp
|
||||
arith_bounds_tactic.cpp
|
||||
bound_manager.cpp
|
||||
bound_propagator.cpp
|
||||
bv2int_rewriter.cpp
|
||||
bv2real_rewriter.cpp
|
||||
card2bv_tactic.cpp
|
||||
degree_shift_tactic.cpp
|
||||
diff_neq_tactic.cpp
|
||||
eq2bv_tactic.cpp
|
||||
|
@ -15,13 +12,11 @@ z3_add_component(arith_tactics
|
|||
fm_tactic.cpp
|
||||
lia2card_tactic.cpp
|
||||
lia2pb_tactic.cpp
|
||||
linear_equation.cpp
|
||||
nla2bv_tactic.cpp
|
||||
normalize_bounds_tactic.cpp
|
||||
pb2bv_model_converter.cpp
|
||||
pb2bv_tactic.cpp
|
||||
probe_arith.cpp
|
||||
propagate_ineqs_tactic.cpp
|
||||
purify_arith_tactic.cpp
|
||||
recover_01_tactic.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
|
|
|
@ -19,7 +19,7 @@ Revision History:
|
|||
#include "tactic/tactical.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
|
||||
struct is_unbounded_proc {
|
||||
struct found {};
|
||||
|
@ -41,7 +41,8 @@ struct is_unbounded_proc {
|
|||
bool is_unbounded(goal const & g) {
|
||||
ast_manager & m = g.m();
|
||||
bound_manager bm(m);
|
||||
bm(g);
|
||||
for (unsigned i = 0; i < g.size(); ++i)
|
||||
bm(g.form(i), g.dep(i), g.pr(i));
|
||||
is_unbounded_proc proc(bm);
|
||||
return test(g, proc);
|
||||
}
|
||||
|
|
|
@ -7,13 +7,33 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-30.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic add-bounds
|
||||
|
||||
### Short Description
|
||||
|
||||
Tactic for bounding unbounded variables.
|
||||
|
||||
### Long Description
|
||||
|
||||
The tactic creates a stronger sub-goal by adding bounds to variables.
|
||||
The new goal may not be satisfiable even if the original goal is.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (> (+ x y) 10))
|
||||
(apply add-bounds)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -61,7 +61,7 @@ struct arith_bounds_tactic : public tactic {
|
|||
return true;
|
||||
}
|
||||
if ((!is_negated && (a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1))) ||
|
||||
(is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) {
|
||||
(is_negated && (a.is_le(e, e2, e1) || a.is_ge(e, e1, e2)))) {
|
||||
is_strict = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ Notes:
|
|||
for assembling bounds, but it does not have a way to check for
|
||||
subsumption of atoms.
|
||||
|
||||
## Tactic arith-bounds
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "tactic/tactic.h"
|
||||
|
|
|
@ -1,296 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Collect bounds.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-05-16
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "tactic/goal.h"
|
||||
|
||||
bound_manager::bound_manager(ast_manager & m):
|
||||
m_util(m),
|
||||
m_bounded_vars(m) {
|
||||
}
|
||||
|
||||
bound_manager::~bound_manager() {
|
||||
reset();
|
||||
}
|
||||
|
||||
bound_manager* bound_manager::translate(ast_manager& dst_m) {
|
||||
bound_manager* result = alloc(bound_manager, dst_m);
|
||||
ast_translation tr(m(), dst_m);
|
||||
expr_dependency_translation edtr(tr);
|
||||
for (auto& kv : m_lowers) result->m_lowers.insert(tr(kv.m_key), kv.m_value);
|
||||
for (auto& kv : m_uppers) result->m_uppers.insert(tr(kv.m_key), kv.m_value);
|
||||
for (auto& kv : m_lower_deps) result->m_lower_deps.insert(tr(kv.m_key), edtr(kv.m_value));
|
||||
for (auto& kv : m_upper_deps) result->m_upper_deps.insert(tr(kv.m_key), edtr(kv.m_value));
|
||||
for (expr* e : m_bounded_vars) result->m_bounded_vars.push_back(tr(e));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static decl_kind swap_decl(decl_kind k) {
|
||||
switch (k) {
|
||||
case OP_LE: return OP_GE;
|
||||
case OP_LT: return OP_GT;
|
||||
case OP_GE: return OP_LE;
|
||||
case OP_GT: return OP_LT;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
decl_kind bound_manager::neg(decl_kind k) {
|
||||
switch (k) {
|
||||
case OP_LE: return OP_GT;
|
||||
case OP_LT: return OP_GE;
|
||||
case OP_GE: return OP_LT;
|
||||
case OP_GT: return OP_LE;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return k;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::norm(numeral & n, decl_kind & k) {
|
||||
switch (k) {
|
||||
case OP_LE: return;
|
||||
case OP_GE: return;
|
||||
case OP_LT:
|
||||
// x < n --> x <= n-1
|
||||
n--;
|
||||
k = OP_LE;
|
||||
return;
|
||||
case OP_GT:
|
||||
// x > n --> x >= n+1
|
||||
n++;
|
||||
k = OP_GE;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_lower(decl_kind k) {
|
||||
return k == OP_GT || k == OP_GE;
|
||||
}
|
||||
|
||||
static bool is_strict(decl_kind k) {
|
||||
return k == OP_LT || k == OP_GT;
|
||||
}
|
||||
|
||||
bool bound_manager::is_numeral(expr* v, numeral& n, bool& is_int) {
|
||||
expr* w;
|
||||
if (m_util.is_uminus(v, w) && is_numeral(w, n, is_int)) {
|
||||
n.neg();
|
||||
return true;
|
||||
}
|
||||
return m_util.is_numeral(v, n, is_int);
|
||||
}
|
||||
|
||||
void bound_manager::operator()(expr * f, expr_dependency * d) {
|
||||
TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";);
|
||||
expr * v;
|
||||
numeral n;
|
||||
if (is_disjunctive_bound(f, d))
|
||||
return;
|
||||
if (is_equality_bound(f, d))
|
||||
return;
|
||||
bool pos = true;
|
||||
while (m().is_not(f, f))
|
||||
pos = !pos;
|
||||
if (!is_app(f))
|
||||
return;
|
||||
app * t = to_app(f);
|
||||
if (t->get_family_id() != m_util.get_family_id())
|
||||
return;
|
||||
decl_kind k = t->get_decl_kind();
|
||||
if (k != OP_LE && k != OP_GE && k != OP_LT && k != OP_GT)
|
||||
return;
|
||||
expr * lhs = t->get_arg(0);
|
||||
expr * rhs = t->get_arg(1);
|
||||
bool is_int;
|
||||
if (is_uninterp_const(lhs) && is_numeral(rhs, n, is_int)) {
|
||||
v = lhs;
|
||||
}
|
||||
else if (is_uninterp_const(rhs) && is_numeral(lhs, n, is_int)) {
|
||||
v = rhs;
|
||||
k = swap_decl(k);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (!pos)
|
||||
k = neg(k);
|
||||
if (is_int)
|
||||
norm(n, k);
|
||||
TRACE("bound_manager", tout << "found bound for:\n" << mk_ismt2_pp(v, m()) << "\n";);
|
||||
bool strict = is_strict(k);
|
||||
if (is_lower(k)) {
|
||||
insert_lower(v, strict, n, d);
|
||||
}
|
||||
else {
|
||||
insert_upper(v, strict, n, d);
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d) {
|
||||
limit old;
|
||||
if (m_uppers.find(v, old)) {
|
||||
if (n < old.first || (n == old.first && strict && !old.second)) {
|
||||
// improved bound
|
||||
m_uppers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_upper_deps.insert(v, d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_uppers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_upper_deps.insert(v, d);
|
||||
if (!m_lowers.contains(v)) {
|
||||
m_bounded_vars.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d) {
|
||||
limit old;
|
||||
if (m_lowers.find(v, old)) {
|
||||
if (n > old.first || (n == old.first && strict && !old.second)) {
|
||||
// improved bound
|
||||
m_lowers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_lower_deps.insert(v, d);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_lowers.insert(v, limit(n, strict));
|
||||
if (d)
|
||||
m_lower_deps.insert(v, d);
|
||||
if (!m_uppers.contains(v)) {
|
||||
m_bounded_vars.push_back(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) {
|
||||
expr* x, *y;
|
||||
if (!m().is_eq(f, x, y)) {
|
||||
return false;
|
||||
}
|
||||
if (!is_uninterp_const(x)) {
|
||||
std::swap(x, y);
|
||||
}
|
||||
numeral n;
|
||||
bool is_int;
|
||||
if (is_uninterp_const(x) && is_numeral(y, n, is_int)) {
|
||||
insert_lower(x, false, n, d);
|
||||
insert_upper(x, false, n, d);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) {
|
||||
numeral lo, hi, n;
|
||||
if (!m().is_or(f)) return false;
|
||||
unsigned sz = to_app(f)->get_num_args();
|
||||
if (sz == 0) return false;
|
||||
expr * x, * y, * v = nullptr;
|
||||
bool is_int;
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr * e = to_app(f)->get_arg(i);
|
||||
if (!m().is_eq(e, x, y)) return false;
|
||||
if (is_uninterp_const(x) &&
|
||||
is_numeral(y, n, is_int) && is_int &&
|
||||
(x == v || v == nullptr)) {
|
||||
if (v == nullptr) { v = x; lo = hi = n; }
|
||||
if (n < lo) lo = n;
|
||||
if (n > hi) hi = n;
|
||||
}
|
||||
else if (is_uninterp_const(y) &&
|
||||
is_numeral(x, n, is_int) && is_int &&
|
||||
(y == v || v == nullptr)) {
|
||||
if (v == nullptr) { v = y; lo = hi = n; }
|
||||
if (n < lo) lo = n;
|
||||
if (n > hi) hi = n;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TRACE("bound_manager", tout << "bounds: " << lo << " " << hi << "\n";);
|
||||
insert_lower(v, false, lo, d);
|
||||
insert_upper(v, false, hi, d);
|
||||
return true;
|
||||
}
|
||||
|
||||
void bound_manager::operator()(goal const & g) {
|
||||
if (g.proofs_enabled())
|
||||
return;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
operator()(g.form(i), g.dep(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void bound_manager::reset() {
|
||||
m_bounded_vars.finalize();
|
||||
m_lowers.finalize();
|
||||
m_uppers.finalize();
|
||||
m_lower_deps.finalize();
|
||||
m_upper_deps.finalize();
|
||||
}
|
||||
|
||||
bool bound_manager::inconsistent() const {
|
||||
for (auto const& kv : m_lowers) {
|
||||
limit const& lim1 = kv.m_value;
|
||||
limit lim2;
|
||||
if (m_uppers.find(kv.m_key, lim2)) {
|
||||
if (lim1.first > lim2.first) {
|
||||
return true;
|
||||
}
|
||||
if (lim1.first == lim2.first &&
|
||||
!lim1.second && lim2.second) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void bound_manager::display(std::ostream & out) const {
|
||||
numeral n; bool strict;
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
expr * v = *it;
|
||||
if (has_lower(v, n, strict))
|
||||
out << n << " " << (strict ? "<" : "<=");
|
||||
else
|
||||
out << "-oo <";
|
||||
out << " " << mk_ismt2_pp(v, m()) << " ";
|
||||
if (has_upper(v, n, strict))
|
||||
out << (strict ? "<" : "<=") << " " << n;
|
||||
else
|
||||
out << "< oo";
|
||||
out << "\n";
|
||||
}
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Collect bounds.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-05-16
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
class goal;
|
||||
|
||||
class bound_manager {
|
||||
public:
|
||||
typedef rational numeral;
|
||||
private:
|
||||
typedef std::pair<numeral, bool> limit;
|
||||
arith_util m_util;
|
||||
obj_map<expr, limit> m_lowers;
|
||||
obj_map<expr, limit> m_uppers;
|
||||
obj_map<expr, expr_dependency*> m_lower_deps;
|
||||
obj_map<expr, expr_dependency*> m_upper_deps;
|
||||
expr_ref_vector m_bounded_vars;
|
||||
bool is_disjunctive_bound(expr * f, expr_dependency * d);
|
||||
bool is_equality_bound(expr * f, expr_dependency * d);
|
||||
bool is_numeral(expr* v, rational& n, bool& is_int);
|
||||
void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d);
|
||||
void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d);
|
||||
public:
|
||||
static decl_kind neg(decl_kind k);
|
||||
static void norm(numeral & n, decl_kind & k);
|
||||
|
||||
bound_manager(ast_manager & m);
|
||||
~bound_manager();
|
||||
|
||||
bound_manager* translate(ast_manager& dst_m);
|
||||
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
|
||||
void operator()(goal const & g);
|
||||
void operator()(expr * n, expr_dependency * d = nullptr);
|
||||
|
||||
bool has_lower(expr * c, numeral & v, bool & strict) const {
|
||||
limit l;
|
||||
if (m_lowers.find(c, l)) {
|
||||
v = l.first;
|
||||
strict = l.second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool has_upper(expr * c, numeral & v, bool & strict) const {
|
||||
limit l;
|
||||
if (m_uppers.find(c, l)) {
|
||||
v = l.first;
|
||||
strict = l.second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expr_dependency * lower_dep(expr * c) const {
|
||||
expr_dependency * d;
|
||||
if (m_lower_deps.find(c, d))
|
||||
return d;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
expr_dependency * upper_dep(expr * c) const {
|
||||
expr_dependency * d;
|
||||
if (m_upper_deps.find(c, d))
|
||||
return d;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool inconsistent() const;
|
||||
|
||||
bool has_lower(expr * c) const {
|
||||
return m_lowers.contains(c);
|
||||
}
|
||||
|
||||
bool has_upper(expr * c) const {
|
||||
return m_uppers.contains(c);
|
||||
}
|
||||
|
||||
typedef ptr_vector<expr>::const_iterator iterator;
|
||||
|
||||
/**
|
||||
\brief Iterator for all bounded constants.
|
||||
*/
|
||||
iterator begin() const { return m_bounded_vars.begin(); }
|
||||
iterator end() const { return m_bounded_vars.end(); }
|
||||
|
||||
void reset();
|
||||
|
||||
// for debugging purposes
|
||||
void display(std::ostream & out) const;
|
||||
};
|
||||
|
|
@ -1,943 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "tactic/arith/bound_propagator.h"
|
||||
#include<cmath>
|
||||
|
||||
// -------------------------------
|
||||
// Bound Relaxation configuration
|
||||
//
|
||||
// The idea is to minimize errors in floating point computations
|
||||
//
|
||||
// If RELAX_BOUNDS is undefined, then bound relaxation is disabled.
|
||||
// Otherwise, lower bounds l are relaxed using the formula
|
||||
// PRECISION * floor(l * INV_PRECISION + TOLERANCE)
|
||||
// and upper bounds u as:
|
||||
// PRECISION * ceil(u * INV_PRECISION - TOLERANCE)
|
||||
// In the LP literature, the suggested values are
|
||||
// l := 10^-5 * floor(l*10^5 + 10^-6)
|
||||
// u := 10^-5 * ceil(u*10^5 - 10^-6)
|
||||
// I'm using the following values because of strict bounds
|
||||
// l := 10^-6 * floor(l*10^6 + 10^-7)
|
||||
// u := 10^-6 * ceil(u*10^6 - 10^-7)
|
||||
#define RELAX_BOUNDS
|
||||
#define TOLERANCE 0.0000001
|
||||
#define PRECISION 0.000001
|
||||
#define INV_PRECISION 1000000.0
|
||||
// -------------------------------
|
||||
|
||||
bound_propagator::bound::bound(numeral_manager & m,
|
||||
mpq const & k,
|
||||
double approx_k,
|
||||
bool lower,
|
||||
bool strict,
|
||||
unsigned lvl,
|
||||
unsigned ts,
|
||||
bkind bk,
|
||||
unsigned c_idx,
|
||||
assumption a,
|
||||
bound * prev):
|
||||
m_approx_k(approx_k),
|
||||
m_lower(lower),
|
||||
m_strict(strict),
|
||||
m_kind(bk),
|
||||
m_level(lvl),
|
||||
m_timestamp(ts),
|
||||
m_prev(prev) {
|
||||
m.set(m_k, k);
|
||||
if (bk == DERIVED)
|
||||
m_constraint_idx = c_idx;
|
||||
else
|
||||
m_assumption = a;
|
||||
}
|
||||
|
||||
bound_propagator::bound_propagator(numeral_manager & _m, allocator & a, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator(a),
|
||||
m_eq_manager(m, a) {
|
||||
m_timestamp = 0;
|
||||
m_qhead = 0;
|
||||
m_conflict = null_var;
|
||||
updt_params(p);
|
||||
reset_statistics();
|
||||
}
|
||||
|
||||
bound_propagator::~bound_propagator() {
|
||||
m.del(m_tmp);
|
||||
reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints_core() {
|
||||
for (auto & c : m_constraints) {
|
||||
del_constraint(c);
|
||||
}
|
||||
m_constraints.reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraints() {
|
||||
SASSERT(scope_lvl() == 0);
|
||||
if (m_constraints.empty())
|
||||
return;
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
for (auto& wl : m_watches) {
|
||||
wl.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::del_constraint(constraint & c) {
|
||||
switch (c.m_kind) {
|
||||
case LINEAR:
|
||||
m_eq_manager.del(c.m_eq);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::updt_params(params_ref const & p) {
|
||||
m_max_refinements = p.get_uint("bound_max_refinements", 16);
|
||||
m_threshold = p.get_double("bound_threshold", 0.05);
|
||||
m_small_interval = p.get_double("bound_small_interval", 128);
|
||||
m_strict2double = p.get_double("strict2double", 0.00001);
|
||||
}
|
||||
|
||||
void bound_propagator::get_param_descrs(param_descrs & r) {
|
||||
r.insert("bound_max_refinements", CPK_UINT, "(default: 16) maximum number of bound refinements (per round) for unbounded variables.");
|
||||
r.insert("bound_threshold", CPK_DOUBLE, "(default: 0.05) bound propagation improvement threshold ratio.");
|
||||
}
|
||||
|
||||
void bound_propagator::collect_statistics(statistics & st) const {
|
||||
st.update("bound conflicts", m_conflicts);
|
||||
st.update("bound propagations", m_propagations);
|
||||
st.update("bound false alarms", m_false_alarms);
|
||||
}
|
||||
|
||||
void bound_propagator::reset_statistics() {
|
||||
m_conflicts = 0;
|
||||
m_propagations = 0;
|
||||
m_false_alarms = 0;
|
||||
}
|
||||
|
||||
void bound_propagator::mk_var(var x, bool is_int) {
|
||||
m_is_int.reserve(x+1, false);
|
||||
m_dead.reserve(x+1, true);
|
||||
m_lowers.reserve(x+1, 0);
|
||||
m_uppers.reserve(x+1, 0);
|
||||
m_lower_refinements.reserve(x+1, 0);
|
||||
m_upper_refinements.reserve(x+1, 0);
|
||||
m_watches.reserve(x+1);
|
||||
|
||||
SASSERT(m_dead[x]);
|
||||
|
||||
m_is_int[x] = is_int;
|
||||
m_dead[x] = false;
|
||||
m_lowers[x] = 0;
|
||||
m_uppers[x] = 0;
|
||||
m_lower_refinements[x] = 0;
|
||||
m_upper_refinements[x] = 0;
|
||||
m_watches[x].reset();
|
||||
}
|
||||
|
||||
void bound_propagator::del_var(var x) {
|
||||
SASSERT(!m_dead[x]);
|
||||
m_dead[x] = true;
|
||||
// mark constraints containing x as dead.
|
||||
wlist & wl = m_watches[x];
|
||||
for (auto c : wl) {
|
||||
m_constraints[c].m_dead = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpq * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) {
|
||||
linear_equation * eq = m_eq_manager.mk(sz, as, xs);
|
||||
init_eq(eq);
|
||||
}
|
||||
|
||||
void bound_propagator::init_eq(linear_equation * eq) {
|
||||
if (eq == nullptr)
|
||||
return;
|
||||
unsigned c_idx = m_constraints.size();
|
||||
m_constraints.push_back(constraint());
|
||||
constraint & new_c = m_constraints.back();
|
||||
new_c.m_kind = LINEAR;
|
||||
new_c.m_dead = false;
|
||||
new_c.m_timestamp = 0;
|
||||
new_c.m_act = 0;
|
||||
new_c.m_counter = 0;
|
||||
new_c.m_eq = eq;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m_watches[eq->x(i)].push_back(c_idx);
|
||||
}
|
||||
if (propagate(c_idx) && scope_lvl() > 0)
|
||||
m_reinit_stack.push_back(c_idx);
|
||||
}
|
||||
|
||||
void bound_propagator::push() {
|
||||
m_scopes.push_back(scope());
|
||||
scope & s = m_scopes.back();
|
||||
s.m_trail_limit = m_trail.size();
|
||||
s.m_qhead_old = m_qhead;
|
||||
s.m_reinit_stack_limit = m_reinit_stack.size();
|
||||
s.m_timestamp_old = m_timestamp;
|
||||
s.m_in_conflict = inconsistent();
|
||||
}
|
||||
|
||||
void bound_propagator::undo_trail(unsigned old_sz) {
|
||||
SASSERT(old_sz <= m_trail.size());
|
||||
unsigned i = m_trail.size();
|
||||
while (i > old_sz) {
|
||||
--i;
|
||||
trail_info & info = m_trail.back();
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
m_trail.pop_back();
|
||||
bound * b;
|
||||
if (is_lower) {
|
||||
b = m_lowers[x];
|
||||
m_lowers[x] = b->m_prev;
|
||||
}
|
||||
else {
|
||||
b = m_uppers[x];
|
||||
m_uppers[x] = b->m_prev;
|
||||
}
|
||||
m.del(b->m_k);
|
||||
b->~bound();
|
||||
m_allocator.deallocate(sizeof(bound), b);
|
||||
}
|
||||
SASSERT(m_trail.size() == old_sz);
|
||||
}
|
||||
|
||||
void bound_propagator::pop(unsigned num_scopes) {
|
||||
unsigned lvl = scope_lvl();
|
||||
SASSERT(num_scopes <= lvl);
|
||||
unsigned new_lvl = lvl - num_scopes;
|
||||
scope & s = m_scopes[new_lvl];
|
||||
undo_trail(s.m_trail_limit);
|
||||
m_timestamp = s.m_timestamp_old;
|
||||
m_qhead = s.m_qhead_old;
|
||||
if (!s.m_in_conflict)
|
||||
m_conflict = null_var;
|
||||
unsigned reinit_stack_sz = s.m_reinit_stack_limit;
|
||||
m_scopes.shrink(new_lvl);
|
||||
|
||||
// reinitialize
|
||||
unsigned i = reinit_stack_sz;
|
||||
unsigned j = reinit_stack_sz;
|
||||
unsigned sz = m_reinit_stack.size();
|
||||
for (; i < sz; i++) {
|
||||
unsigned c_idx = m_reinit_stack[i];
|
||||
bool p = propagate(c_idx);
|
||||
if (new_lvl > 0 && p) {
|
||||
m_reinit_stack[j] = c_idx;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
m_reinit_stack.shrink(j);
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.inc(k);
|
||||
}
|
||||
else {
|
||||
m.ceil(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
TRACE("bound_propagator_detail", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_lower = m_lowers[x];
|
||||
if (old_lower) {
|
||||
bool improves = m.gt(k, old_lower->m_k) || (!old_lower->m_strict && strict && m.eq(k, old_lower->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_derived", tout << "new lower x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
m_propagations++;
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*floor(approx_k*INV_PRECISION + TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " lower: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_lower = new (mem) bound(m, k, approx_k, true, strict, scope_lvl(), m_timestamp, bk, c_idx, a, old_lower);
|
||||
m_timestamp++;
|
||||
m_lowers[x] = new_lower;
|
||||
m_trail.push_back(trail_info(x, true));
|
||||
m_lower_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a) {
|
||||
if (is_int(x)) {
|
||||
if (m.is_int(k)) {
|
||||
if (strict)
|
||||
m.dec(k);
|
||||
}
|
||||
else {
|
||||
m.floor(k, k);
|
||||
}
|
||||
SASSERT(m.is_int(k));
|
||||
strict = false;
|
||||
}
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
|
||||
bound * old_upper = m_uppers[x];
|
||||
if (old_upper) {
|
||||
bool improves = m.lt(k, old_upper->m_k) || (!old_upper->m_strict && strict && m.eq(k, old_upper->m_k));
|
||||
if (!improves) {
|
||||
if (bk == DERIVED) {
|
||||
TRACE("bound_propagator_detail", tout << "false alarm\n";);
|
||||
m_false_alarms++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bk == DERIVED) {
|
||||
m_propagations++;
|
||||
TRACE("bound_propagator_derived", tout << "new upper x" << x << " " << m.to_string(k) << " strict: " << strict << "\n";);
|
||||
}
|
||||
|
||||
if (scope_lvl() == 0 && bk == DERIVED)
|
||||
bk = AXIOM; // don't need justification at level 0
|
||||
|
||||
double approx_k = m.get_double(k);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " approx: " << approx_k << "\n";);
|
||||
#ifdef RELAX_BOUNDS
|
||||
approx_k = PRECISION*ceil(approx_k*INV_PRECISION - TOLERANCE);
|
||||
TRACE("new_bound", tout << "x" << x << " upper: " << m.to_string(k) << " relaxed approx: " << approx_k << "\n";);
|
||||
#endif
|
||||
|
||||
void * mem = m_allocator.allocate(sizeof(bound));
|
||||
bound * new_upper = new (mem) bound(m, k, approx_k, false, strict, scope_lvl(), m_timestamp, bk, c_idx, a, m_uppers[x]);
|
||||
m_timestamp++;
|
||||
m_uppers[x] = new_upper;
|
||||
m_trail.push_back(trail_info(x, false));
|
||||
m_upper_refinements[x]++;
|
||||
check_feasibility(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::get_interval_size(var x, double & r) const {
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
r = u->m_approx_k - l->m_approx_k;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<bool LOWER>
|
||||
bool bound_propagator::relevant_bound(var x, double new_k) const {
|
||||
TRACE("bound_propagator_detail", tout << "relevant_bound x" << x << " " << new_k << " LOWER: " << LOWER << "\n";
|
||||
if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n";
|
||||
if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";);
|
||||
bound * b = LOWER ? m_lowers[x] : m_uppers[x];
|
||||
if (b == nullptr)
|
||||
return true; // variable did not have a bound
|
||||
|
||||
double interval_size;
|
||||
bool bounded = get_interval_size(x, interval_size);
|
||||
|
||||
if (!is_int(x)) {
|
||||
// check if the improvement is significant
|
||||
double improvement;
|
||||
double abs_k = b->m_approx_k;
|
||||
if (abs_k < 0.0)
|
||||
abs_k -= abs_k;
|
||||
if (bounded)
|
||||
improvement = m_threshold * std::max(std::min(interval_size, abs_k), 1.0);
|
||||
else
|
||||
improvement = m_threshold * std::max(abs_k, 1.0);
|
||||
|
||||
if (LOWER) {
|
||||
if (new_k <= b->m_approx_k + improvement) {
|
||||
TRACE("bound_propagator", tout << "LOWER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (new_k >= b->m_approx_k - improvement) {
|
||||
TRACE("bound_propagator", tout << "UPPER new: " << new_k << " old: " << b->m_approx_k << " improvement is too small\n";);
|
||||
return false; // improvement is too small
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (LOWER) {
|
||||
if (new_k < b->m_approx_k + 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
else {
|
||||
if (new_k > b->m_approx_k - 1.0)
|
||||
return false; // no improvement
|
||||
}
|
||||
}
|
||||
|
||||
if (bounded && interval_size <= m_small_interval)
|
||||
return true;
|
||||
|
||||
if (LOWER)
|
||||
return m_lower_refinements[x] < m_max_refinements;
|
||||
else
|
||||
return m_upper_refinements[x] < m_max_refinements;
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_lower(var x, double approx_k) const {
|
||||
return relevant_bound<true>(x, approx_k);
|
||||
}
|
||||
|
||||
bool bound_propagator::relevant_upper(var x, double approx_k) const {
|
||||
return relevant_bound<false>(x, approx_k);
|
||||
}
|
||||
|
||||
void bound_propagator::check_feasibility(var x) {
|
||||
if (inconsistent())
|
||||
return;
|
||||
bound * l = m_lowers[x];
|
||||
bound * u = m_uppers[x];
|
||||
if (l && u) {
|
||||
if (m.lt(l->m_k, u->m_k))
|
||||
return;
|
||||
if (!l->m_strict && !u->m_strict && m.eq(l->m_k, u->m_k))
|
||||
return;
|
||||
m_conflict = x;
|
||||
m_conflicts++;
|
||||
SASSERT(inconsistent());
|
||||
TRACE("bound_propagator", tout << "inconsistency detected: x" << x << "\n"; display(tout););
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::propagate() {
|
||||
m_to_reset_ts.reset();
|
||||
|
||||
while (m_qhead < m_trail.size()) {
|
||||
if (inconsistent())
|
||||
break;
|
||||
trail_info & info = m_trail[m_qhead];
|
||||
var x = info.x();
|
||||
bool is_lower = info.is_lower();
|
||||
bound * b = is_lower ? m_lowers[x] : m_uppers[x];
|
||||
SASSERT(b);
|
||||
unsigned ts = b->m_timestamp;
|
||||
TRACE("bound_propagator_detail", tout << "propagating x" << x << "\n";);
|
||||
m_qhead++;
|
||||
wlist const & wl = m_watches[x];
|
||||
for (unsigned c_idx : wl) {
|
||||
constraint & c = m_constraints[c_idx];
|
||||
// We don't need to visit c if it was already propagated using b.
|
||||
// Whenever we visit c we store in c.m_timestamp the current timestamp
|
||||
// So, we know that c was already propagated any bound using bounds with timestamp lower than c.m_timestamp.
|
||||
if (ts >= c.m_timestamp) {
|
||||
if (c.m_timestamp == 0)
|
||||
m_to_reset_ts.push_back(c_idx);
|
||||
c.m_timestamp = m_timestamp;
|
||||
propagate(c_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned c : m_to_reset_ts)
|
||||
m_constraints[c].m_timestamp = 0;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
if (c.m_dead)
|
||||
return false;
|
||||
if (c.m_kind == LINEAR)
|
||||
return propagate_eq(c_idx);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bound_propagator::propagate_eq(unsigned c_idx) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
|
||||
#if 0
|
||||
{
|
||||
static unsigned counter = 0;
|
||||
static unsigned visited = 0;
|
||||
counter++;
|
||||
visited += eq->size();
|
||||
if (counter % 1000 == 0)
|
||||
verbose_stream() << "[bound-propagator] :propagate-eq " << counter << " :visited-vars " << visited << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE("bound_propagator_detail", tout << "propagating using eq: "; m_eq_manager.display(tout, *eq); tout << "\n";);
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
unsigned ll_i = UINT_MAX; // position of the variable that couldn't contribute to ll
|
||||
unsigned uu_i = UINT_MAX; // position of the variable that coundn't contribute to uu
|
||||
bool ll_failed = false;
|
||||
bool uu_failed = false;
|
||||
double ll = 0.0;
|
||||
double uu = 0.0;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
if (a_i < 0.0) {
|
||||
if (!ll_failed) {
|
||||
if (l_i == nullptr) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (u_i == nullptr) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!ll_failed) {
|
||||
if (u_i == nullptr) {
|
||||
if (ll_i == UINT_MAX)
|
||||
ll_i = i;
|
||||
else
|
||||
ll_failed = true;
|
||||
}
|
||||
else {
|
||||
ll -= a_i * u_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed) {
|
||||
if (l_i == nullptr) {
|
||||
if (uu_i == UINT_MAX)
|
||||
uu_i = i;
|
||||
else
|
||||
uu_failed = true;
|
||||
}
|
||||
else {
|
||||
uu -= a_i * l_i->m_approx_k;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ll_failed && uu_failed)
|
||||
return false; // nothing to propagate
|
||||
}
|
||||
|
||||
bool propagated = false;
|
||||
|
||||
SASSERT(!ll_failed || !uu_failed);
|
||||
if (ll_i == UINT_MAX || uu_i == UINT_MAX) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
double a_i = eq->approx_a(i);
|
||||
bound * l_i = m_lowers[x_i];
|
||||
bound * u_i = m_uppers[x_i];
|
||||
// ll = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_i > 0} -a_i * upper(x_i))
|
||||
// uu = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_i < 0} -a_i * upper(x_i))
|
||||
if (ll_i == UINT_MAX) {
|
||||
// can propagate a lower bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (ll + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (ll + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
if (uu_i == UINT_MAX) {
|
||||
// can propagate an upper bound for a_i*x_i
|
||||
if (a_i > 0.0) {
|
||||
// can propagate a upper bound for x_i
|
||||
double new_k = (uu + a_i * l_i->m_approx_k)/a_i;
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
// a_i < 0.0
|
||||
// can propagate a lower bound for x_i
|
||||
double new_k = (uu + a_i * u_i->m_approx_k)/a_i;
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ll_failed && ll_i != UINT_MAX) {
|
||||
// can propagate a lower bound for the monomial at position ll_i
|
||||
var x_i = eq->x(ll_i);
|
||||
double a_i = eq->approx_a(ll_i);
|
||||
double new_k = ll/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, ll_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!uu_failed && uu_i != UINT_MAX) {
|
||||
// can propagate a upper bound for the monomial at position uu_i
|
||||
var x_i = eq->x(uu_i);
|
||||
double a_i = eq->approx_a(uu_i);
|
||||
double new_k = uu/a_i;
|
||||
if (a_i > 0.0) {
|
||||
if (relevant_upper(x_i, new_k) && propagate_upper(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
else {
|
||||
if (relevant_lower(x_i, new_k) && propagate_lower(c_idx, uu_i))
|
||||
propagated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return propagated;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a lower bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_lower(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_uppers[x_j] : m_lowers[x_j];
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << " b_j->m_k: " << m.to_string(b_j->m_k) <<
|
||||
" a_j: " << m.to_string(a_j) << "\n";);
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
TRACE("bound_propagator_step_detail", tout << "k: " << m.to_string(k) << "\n";);
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating lower x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_lower_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Try to propagate a upper bound for the variable stored at position i, using mpq's (rationals).
|
||||
When this method is invoked, we know that all other variables have the "right" bounds, and
|
||||
using doubles we improve the current known bound.
|
||||
*/
|
||||
bool bound_propagator::propagate_upper(unsigned c_idx, unsigned i) {
|
||||
constraint const & c = m_constraints[c_idx];
|
||||
linear_equation * eq = c.m_eq;
|
||||
var x_i = eq->x(i);
|
||||
mpz const & a_i = eq->a(i);
|
||||
unsigned sz = eq->size();
|
||||
mpq k;
|
||||
bool strict = false;
|
||||
bool neg_a_i = m.is_neg(a_i);
|
||||
for (unsigned j = 0; j < sz; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
var x_j = eq->x(j);
|
||||
mpz const & a_j = eq->a(j);
|
||||
bound * b_j = (m.is_neg(a_j) == neg_a_i) ? m_lowers[x_j] : m_uppers[x_j];
|
||||
SASSERT(b_j);
|
||||
if (b_j->m_strict)
|
||||
strict = true;
|
||||
m.addmul(k, a_j, b_j->m_k, k);
|
||||
}
|
||||
m.neg(k);
|
||||
m.div(k, a_i, k);
|
||||
TRACE("bound_propagator_step", tout << "propagating upper x" << x_i << " " << m.to_string(k) << " strict: " << strict << " using\n";
|
||||
m_eq_manager.display(tout, *eq); tout << "\n"; display_bounds_of(tout, *eq););
|
||||
bool r = assert_upper_core(x_i, k, strict, DERIVED, c_idx, null_assumption);
|
||||
m.del(k);
|
||||
return r;
|
||||
}
|
||||
|
||||
void bound_propagator::reset() {
|
||||
undo_trail(0);
|
||||
del_constraints_core();
|
||||
m_constraints.finalize();
|
||||
m_is_int.finalize();
|
||||
m_dead.finalize();
|
||||
m_lowers.finalize();
|
||||
m_uppers.finalize();
|
||||
m_watches.finalize();
|
||||
m_trail.finalize();
|
||||
m_qhead = 0;
|
||||
m_reinit_stack.finalize();
|
||||
m_lower_refinements.finalize();
|
||||
m_upper_refinements.finalize();
|
||||
m_timestamp = 0;
|
||||
m_conflict = null_var;
|
||||
m_scopes.finalize();
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_lowers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const {
|
||||
bound * b = m_uppers[x];
|
||||
if (!b)
|
||||
return false;
|
||||
m.set(k, b->m_k);
|
||||
strict = b->m_strict;
|
||||
ts = b->m_timestamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) {
|
||||
bound * r = this;
|
||||
while (r != nullptr && r->m_timestamp >= timestamp)
|
||||
r = r->m_prev;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Return true if the coefficient of x in eq is positive
|
||||
*/
|
||||
bool bound_propagator::is_a_i_pos(linear_equation const & eq, var x) const {
|
||||
unsigned i = eq.pos(x);
|
||||
if (i == UINT_MAX)
|
||||
return false;
|
||||
return m.is_pos(eq.a(i));
|
||||
}
|
||||
|
||||
void bound_propagator::explain(var x, bound * b, unsigned ts, assumption_vector & ex) const {
|
||||
if (!b)
|
||||
return;
|
||||
b = b->at(ts);
|
||||
if (!b)
|
||||
return;
|
||||
if (b->m_kind == AXIOM || b->m_kind == DECISION)
|
||||
return;
|
||||
if (b->m_kind == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
return;
|
||||
}
|
||||
svector<var_bound> & todo = const_cast<bound_propagator*>(this)->m_todo;
|
||||
todo.reset();
|
||||
unsigned qhead = 0;
|
||||
todo.push_back(var_bound(x, b));
|
||||
b->m_mark = true;
|
||||
while (qhead < todo.size()) {
|
||||
var_bound & vb = todo[qhead];
|
||||
qhead ++;
|
||||
var x = vb.first;
|
||||
bound * b = vb.second;
|
||||
SASSERT(b->kind() == ASSUMPTION || b->kind() == DERIVED);
|
||||
if (b->kind() == ASSUMPTION) {
|
||||
ex.push_back(b->m_assumption);
|
||||
continue;
|
||||
}
|
||||
SASSERT(b->kind() == DERIVED);
|
||||
constraint const & c = m_constraints[b->m_constraint_idx];
|
||||
switch (c.m_kind) {
|
||||
case LINEAR: {
|
||||
linear_equation * eq = c.m_eq;
|
||||
bool is_lower = b->is_lower();
|
||||
if (!is_a_i_pos(*eq, x))
|
||||
is_lower = !is_lower;
|
||||
unsigned sz = eq->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = eq->x(i);
|
||||
if (x_i == x)
|
||||
continue;
|
||||
bound * b = (m.is_neg(eq->a(i)) == is_lower) ? m_lowers[x_i] : m_uppers[x_i];
|
||||
SASSERT(b);
|
||||
if (b->kind() == DERIVED || b->kind() == ASSUMPTION) {
|
||||
if (!b->m_mark) {
|
||||
b->m_mark = true;
|
||||
todo.push_back(var_bound(x_i, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned sz = todo.size();
|
||||
for (unsigned i = 0; i < sz; i++)
|
||||
todo[i].second->m_mark = false;
|
||||
todo.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Compute lower (upper) bound for the linear polynomial as[0]*xs[0] + ... + as[sz-1]*xs[sz-1]
|
||||
|
||||
Return false if the lower (upper) bound is -oo (oo)
|
||||
*/
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool bound_propagator::get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const {
|
||||
st = false;
|
||||
m.reset(r);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x_i = xs[i];
|
||||
Numeral const & a_i = as[i];
|
||||
if (m.is_zero(a_i))
|
||||
continue;
|
||||
bound * b = (m.is_neg(a_i) == LOWER) ? m_uppers[x_i] : m_lowers[x_i];
|
||||
if (!b) {
|
||||
m.reset(r);
|
||||
return false;
|
||||
}
|
||||
if (b->m_strict)
|
||||
st = true;
|
||||
m.addmul(r, a_i, b->m_k, r);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bound_propagator::lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<true, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
bool bound_propagator::upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const {
|
||||
return get_bound<false, mpq>(sz, as, xs, r, st);
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds_of(std::ostream & out, linear_equation const & eq) const {
|
||||
for (unsigned i = 0; i < eq.size(); i++) {
|
||||
display_var_bounds(out, eq.x(i));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_var_bounds(std::ostream & out, var x, bool approx, bool precise) const {
|
||||
if (m_lowers[x]) {
|
||||
if (precise)
|
||||
out << m.to_string(m_lowers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_lowers[x]->m_approx_k;
|
||||
out << " " << (m_lowers[x]->m_strict ? "<" : "<=");
|
||||
}
|
||||
else {
|
||||
out << "-oo <";
|
||||
}
|
||||
out << " x" << x << " ";
|
||||
if (m_uppers[x]) {
|
||||
out << (m_uppers[x]->m_strict ? "<" : "<=") << " ";
|
||||
if (precise)
|
||||
out << m.to_string(m_uppers[x]->m_k);
|
||||
if (precise && approx)
|
||||
out << " | ";
|
||||
if (approx)
|
||||
out << m_uppers[x]->m_approx_k;
|
||||
}
|
||||
else {
|
||||
out << "< oo";
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_bounds(std::ostream & out, bool approx, bool precise) const {
|
||||
unsigned num_vars = m_dead.size();
|
||||
for (unsigned x = 0; x < num_vars; x++) {
|
||||
if (!is_dead(x)) {
|
||||
display_var_bounds(out, x, approx, precise);
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display_constraints(std::ostream & out) const {
|
||||
for (constraint const& c : m_constraints) {
|
||||
if (c.m_kind == LINEAR) {
|
||||
m_eq_manager.display(out, *(c.m_eq));
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bound_propagator::display(std::ostream & out) const {
|
||||
display_bounds(out);
|
||||
display_constraints(out);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_propagator.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Bound propagators for arithmetic.
|
||||
Support class for implementing strategies and search procedures
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-18.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/mpq.h"
|
||||
#include "util/vector.h"
|
||||
#include "util/params.h"
|
||||
#include "util/statistics.h"
|
||||
#include "util/numeral_buffer.h"
|
||||
#include "tactic/arith/linear_equation.h"
|
||||
|
||||
class bound_propagator {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
typedef unsigned assumption;
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef unsigned_vector assumption_vector;
|
||||
typedef unsigned constraint_id;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
typedef svector<double> double_vector;
|
||||
static const assumption null_assumption = UINT_MAX;
|
||||
static const var null_var = UINT_MAX;
|
||||
static const unsigned null_constraint_idx = UINT_MAX;
|
||||
class trail_info {
|
||||
unsigned m_x_lower;
|
||||
public:
|
||||
trail_info(var x, bool is_lower):m_x_lower((x << 1) + static_cast<unsigned>(is_lower)) {}
|
||||
trail_info():m_x_lower(UINT_MAX) {}
|
||||
var x() const { return m_x_lower >> 1; }
|
||||
bool is_lower() const { return (m_x_lower & 1) != 0; }
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
enum ckind { LINEAR // only linear equalities so far.
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Constraints don't need justification.
|
||||
*/
|
||||
class constraint {
|
||||
friend class bound_propagator;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_dead:1;
|
||||
unsigned m_timestamp; // Constraint tried to propagate new bounds using bounds with timestamp < m_timestamp.
|
||||
unsigned m_act; // activity
|
||||
unsigned m_counter; // number of times the constraint propagated
|
||||
union {
|
||||
linear_equation * m_eq;
|
||||
};
|
||||
};
|
||||
|
||||
enum bkind { AXIOM, // doesn't need justification
|
||||
ASSUMPTION, // aka external case-split, it is used to connect with external search engine.
|
||||
DERIVED, // implied
|
||||
DECISION // internal case-split
|
||||
};
|
||||
|
||||
struct bound {
|
||||
mpq m_k;
|
||||
double m_approx_k;
|
||||
unsigned m_lower:1;
|
||||
unsigned m_strict:1;
|
||||
unsigned m_mark:1;
|
||||
unsigned m_kind:2;
|
||||
unsigned m_level:27;
|
||||
unsigned m_timestamp;
|
||||
union {
|
||||
assumption m_assumption;
|
||||
unsigned m_constraint_idx;
|
||||
};
|
||||
bound * m_prev;
|
||||
bound(numeral_manager & m, mpq const & k, double approx_k, bool lower, bool strict, unsigned lvl, unsigned ts, bkind bk,
|
||||
unsigned c_idx, assumption a, bound * prev);
|
||||
|
||||
bound * at(unsigned timestamp);
|
||||
bkind kind() const { return static_cast<bkind>(m_kind); }
|
||||
bool is_lower() const { return m_lower != 0; }
|
||||
};
|
||||
|
||||
typedef ptr_vector<bound> var2bound;
|
||||
typedef svector<var> var_vector;
|
||||
typedef svector<constraint> constraint_vector;
|
||||
typedef unsigned_vector c_idx_vector;
|
||||
typedef c_idx_vector wlist;
|
||||
typedef small_object_allocator allocator;
|
||||
typedef linear_equation_manager lin_eq_manager;
|
||||
|
||||
numeral_manager & m;
|
||||
allocator & m_allocator;
|
||||
lin_eq_manager m_eq_manager;
|
||||
constraint_vector m_constraints;
|
||||
char_vector m_is_int;
|
||||
char_vector m_dead;
|
||||
var2bound m_lowers;
|
||||
var2bound m_uppers;
|
||||
vector<wlist> m_watches;
|
||||
svector<trail_info> m_trail;
|
||||
unsigned m_qhead;
|
||||
c_idx_vector m_reinit_stack;
|
||||
unsigned_vector m_lower_refinements; // number of times a lower bound was propagated for each variable (loop prevention)
|
||||
unsigned_vector m_upper_refinements; // number of times a upper bound was propagated for each variable (loop prevention)
|
||||
unsigned m_timestamp;
|
||||
var m_conflict;
|
||||
mpq m_tmp;
|
||||
|
||||
struct scope {
|
||||
unsigned m_trail_limit;
|
||||
unsigned m_qhead_old;
|
||||
unsigned m_reinit_stack_limit;
|
||||
unsigned m_timestamp_old:31;
|
||||
unsigned m_in_conflict:1;
|
||||
};
|
||||
|
||||
svector<scope> m_scopes;
|
||||
|
||||
unsigned_vector m_to_reset_ts; // temp field: ids of the constraints we must reset the field m_timestamp
|
||||
|
||||
// config
|
||||
unsigned m_max_refinements; // maximum number of refinements per round
|
||||
double m_small_interval;
|
||||
double m_threshold; // improvement threshold
|
||||
double m_strict2double;
|
||||
|
||||
// statistics
|
||||
unsigned m_conflicts;
|
||||
unsigned m_propagations;
|
||||
unsigned m_false_alarms;
|
||||
|
||||
void del_constraint(constraint & cnstr);
|
||||
void del_constraints_core();
|
||||
|
||||
template<bool LOWER>
|
||||
bool relevant_bound(var x, double approx_k) const;
|
||||
bool relevant_lower(var x, double approx_k) const;
|
||||
bool relevant_upper(var x, double approx_k) const;
|
||||
bool get_interval_size(var x, double & r) const;
|
||||
void check_feasibility(var x);
|
||||
|
||||
bool assert_lower_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
bool assert_upper_core(var x, mpq & k, bool strict, bkind bk, unsigned c_idx, assumption a);
|
||||
|
||||
bool propagate(unsigned c_idx);
|
||||
bool propagate_eq(unsigned c_idx);
|
||||
bool propagate_lower(unsigned c_idx, unsigned i);
|
||||
bool propagate_upper(unsigned c_idx, unsigned i);
|
||||
void undo_trail(unsigned old_sz);
|
||||
|
||||
typedef std::pair<var, bound *> var_bound;
|
||||
svector<var_bound> m_todo;
|
||||
void explain(var x, bound * b, unsigned ts, assumption_vector & ex) const;
|
||||
bool is_a_i_pos(linear_equation const & eq, var x) const;
|
||||
|
||||
template<bool LOWER, typename Numeral>
|
||||
bool get_bound(unsigned sz, Numeral const * as, var const * xs, mpq & r, bool & st) const;
|
||||
|
||||
void init_eq(linear_equation * eq);
|
||||
|
||||
public:
|
||||
bound_propagator(numeral_manager & m, allocator & a, params_ref const & p);
|
||||
~bound_propagator();
|
||||
|
||||
void updt_params(params_ref const & p);
|
||||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void collect_statistics(statistics & st) const;
|
||||
void reset_statistics();
|
||||
|
||||
double strict2double() const { return m_strict2double; }
|
||||
|
||||
bool is_int(var x) const { return m_is_int[x] != 0; }
|
||||
|
||||
unsigned scope_lvl() const { return m_scopes.size(); }
|
||||
void mk_var(var x, bool is_int);
|
||||
void del_var(var x);
|
||||
bool is_dead(var x) const { return m_dead[x] != 0; }
|
||||
void mk_eq(unsigned sz, mpq * as, var * xs);
|
||||
void mk_eq(unsigned sz, mpz * as, var * xs);
|
||||
void del_constraints();
|
||||
void assert_lower(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_upper(var x, mpq const & k, bool strict, assumption a = null_assumption) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, strict, a == null_assumption ? AXIOM : ASSUMPTION, 0, a);
|
||||
}
|
||||
void assert_decided_lower(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_lower_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void assert_decided_upper(var x, mpq const & k) {
|
||||
m.set(m_tmp, k);
|
||||
assert_upper_core(x, m_tmp, false, DECISION, 0, null_assumption);
|
||||
}
|
||||
void propagate();
|
||||
void push();
|
||||
void pop(unsigned num_scopes);
|
||||
void reset();
|
||||
bool has_lower(var x) const { return m_lowers[x] != 0; }
|
||||
bool has_upper(var x) const { return m_uppers[x] != 0; }
|
||||
bool lower(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool upper(var x, mpq & k, bool & strict, unsigned & ts) const;
|
||||
bool is_fixed(var x) const { return has_lower(x) && has_upper(x) && m.eq(m_lowers[x]->m_k, m_uppers[x]->m_k) && !inconsistent(); }
|
||||
mpq const & lower(var x, bool & strict) const { SASSERT(has_lower(x)); bound * b = m_lowers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & upper(var x, bool & strict) const { SASSERT(has_upper(x)); bound * b = m_uppers[x]; strict = b->m_strict; return b->m_k; }
|
||||
mpq const & lower(var x) const { SASSERT(has_lower(x)); return m_lowers[x]->m_k; }
|
||||
mpq const & upper(var x) const { SASSERT(has_upper(x)); return m_uppers[x]->m_k; }
|
||||
double approx_lower(var x) const {
|
||||
SASSERT(has_lower(x));
|
||||
return m_lowers[x]->m_strict ? m_lowers[x]->m_approx_k + m_strict2double : m_lowers[x]->m_approx_k;
|
||||
}
|
||||
double approx_upper(var x) const {
|
||||
SASSERT(has_upper(x));
|
||||
return m_uppers[x]->m_strict ? m_uppers[x]->m_approx_k - m_strict2double : m_uppers[x]->m_approx_k;
|
||||
}
|
||||
bool is_zero(var x) const { return has_lower(x) && has_upper(x) && m.is_zero(lower(x)) && m.is_zero(upper(x)); }
|
||||
void explain_lower(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_lowers[x], ts, ex); }
|
||||
void explain_upper(var x, unsigned ts, assumption_vector & ex) const { explain(x, m_uppers[x], ts, ex); }
|
||||
void explain_lower(var x, assumption_vector & ex) const { explain_lower(x, m_timestamp, ex); }
|
||||
void explain_upper(var x, assumption_vector & ex) const { explain_upper(x, m_timestamp, ex); }
|
||||
var conflict_var() const { return m_conflict; }
|
||||
bool inconsistent() const { return m_conflict != null_var; }
|
||||
|
||||
unsigned trail_size() const { return m_trail.size(); }
|
||||
unsigned qhead() const { return m_qhead; }
|
||||
|
||||
typedef svector<trail_info>::const_iterator trail_iterator;
|
||||
|
||||
trail_iterator begin_trail() const { return m_trail.begin(); }
|
||||
trail_iterator end_trail() const { return m_trail.end(); }
|
||||
|
||||
bool lower(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
bool upper(unsigned sz, mpq const * as, var const * xs, mpq & r, bool & st) const;
|
||||
void display(std::ostream & out) const;
|
||||
void display_var_bounds(std::ostream & out, var x, bool approx = true, bool precise = true) const;
|
||||
void display_bounds(std::ostream & out, bool approx = true, bool precise = true) const;
|
||||
void display_precise_bounds(std::ostream & out) const { display_bounds(out, false, true); }
|
||||
void display_approx_bounds(std::ostream & out) const { display_bounds(out, true, false); }
|
||||
void display_constraints(std::ostream & out) const;
|
||||
void display_bounds_of(std::ostream & out, linear_equation const & eq) const;
|
||||
|
||||
unsigned get_num_false_alarms() const { return m_false_alarms; }
|
||||
unsigned get_num_propagations() const { return m_propagations; }
|
||||
};
|
||||
|
42
src/tactic/arith/bound_simplifier_tactic.h
Normal file
42
src/tactic/arith/bound_simplifier_tactic.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*++
|
||||
Copyright (c) 2023 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bound_simplifier_tactic.h
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2023-01-22
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic bound-simplifier
|
||||
|
||||
### Short Description
|
||||
|
||||
Tactic for simplifying arithmetical expressions modulo bounds
|
||||
|
||||
### Long Description
|
||||
|
||||
The tactic is used to eliminate occurrences of modulus expressions when it is known that terms are within the bounds
|
||||
of the modulus.
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/bound_simplifier.h"
|
||||
|
||||
inline tactic* mk_bound_simplifier_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "mk_bound_simplifier_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("bound-simplifier", "Simplify arithmetical expressions modulo bounds.", "alloc(bound_simplifier, m, p, s)")
|
||||
*/
|
|
@ -1,105 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2014 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
card2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for converting Pseudo-Boolean constraints to BV
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-03-20
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "tactic/arith/card2bv_tactic.h"
|
||||
#include "ast/rewriter/pb2bv_rewriter.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
|
||||
class card2bv_tactic : public tactic {
|
||||
ast_manager & m;
|
||||
params_ref m_params;
|
||||
|
||||
public:
|
||||
|
||||
card2bv_tactic(ast_manager & m, params_ref const & p):
|
||||
m(m),
|
||||
m_params(p) {
|
||||
}
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
return alloc(card2bv_tactic, m, m_params);
|
||||
}
|
||||
|
||||
char const* name() const override { return "card2bv"; }
|
||||
|
||||
void updt_params(params_ref const & p) override {
|
||||
m_params.append(p);
|
||||
}
|
||||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver");
|
||||
pb2bv_rewriter rw(m, m_params);
|
||||
rw.collect_param_descrs(r);
|
||||
}
|
||||
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result) override {
|
||||
TRACE("card2bv-before", g->display(tout););
|
||||
result.reset();
|
||||
tactic_report report("card2bv", *g);
|
||||
th_rewriter rw1(m, m_params);
|
||||
pb2bv_rewriter rw2(m, m_params);
|
||||
|
||||
if (g->inconsistent()) {
|
||||
result.push_back(g.get());
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref new_f1(m), new_f2(m);
|
||||
for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) {
|
||||
proof_ref new_pr1(m), new_pr2(m);
|
||||
rw1(g->form(idx), new_f1, new_pr1);
|
||||
TRACE("card2bv", tout << "Rewriting " << new_f1 << "\n" << new_pr1 << std::endl;);
|
||||
rw2(false, new_f1, new_f2, new_pr2);
|
||||
TRACE("card2bv", tout << "Rewriting " << new_f2 << "\n" << new_pr2 << std::endl;);
|
||||
if (m.proofs_enabled()) {
|
||||
new_pr1 = m.mk_transitivity(new_pr1, new_pr2);
|
||||
new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1);
|
||||
}
|
||||
g->update(idx, new_f2, new_pr1, g->dep(idx));
|
||||
}
|
||||
expr_ref_vector fmls(m);
|
||||
rw2.flush_side_constraints(fmls);
|
||||
for (expr* e : fmls) {
|
||||
g->assert_expr(e);
|
||||
}
|
||||
|
||||
func_decl_ref_vector const& fns = rw2.fresh_constants();
|
||||
if (!fns.empty()) {
|
||||
generic_model_converter* filter = alloc(generic_model_converter, m, "card2bv");
|
||||
for (func_decl* f : fns) filter->hide(f);
|
||||
g->add(filter);
|
||||
}
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
}
|
||||
|
||||
void cleanup() override {
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(card2bv_tactic, m, p));
|
||||
}
|
||||
|
|
@ -3,107 +3,71 @@ Copyright (c) 2014 Microsoft Corporation
|
|||
|
||||
Module Name:
|
||||
|
||||
card2bv_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for converting Pseudo-Boolean constraints to BV
|
||||
card2bv_tactic.h
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-03-20
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic card2bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Tactic for converting Pseudo-Boolean constraints to bit-vectors.
|
||||
|
||||
### Long Description
|
||||
|
||||
The tactic implements a set of standard methods for converting cardinality and Pseudo-Boolean constraints into bit-vector or propositional formulas
|
||||
(using basic logical connectives, conjunction, disjunction, negation). The conversions from cardinality constraints are controlled
|
||||
separately from the conversions from Pseudo-Boolean constraints using different parameters.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const a1 Bool)
|
||||
(declare-const a2 Bool)
|
||||
(declare-const a3 Bool)
|
||||
(declare-const a4 Bool)
|
||||
(declare-const a5 Bool)
|
||||
(declare-const a6 Bool)
|
||||
(push)
|
||||
(assert ((_ at-most 1) a1 a2 a3 a4 a5 a6))
|
||||
(assert ((_ at-most 2) a1 a2 a3 a4 a5 a6))
|
||||
(apply (with card2bv :cardinality.encoding unate))
|
||||
(apply (with card2bv :cardinality.encoding circuit))
|
||||
(apply (with card2bv :cardinality.encoding ordered))
|
||||
(apply (with card2bv :cardinality.encoding grouped))
|
||||
(apply (with card2bv :cardinality.encoding bimander))
|
||||
(pop)
|
||||
(assert ((_ pbge 5 2 3 4 4 3 5) a1 a2 a3 a4 a5 a6))
|
||||
(apply (with card2bv :pb.solver totalizer))
|
||||
(apply (with card2bv :pb.solver sorting))
|
||||
(apply (with card2bv :pb.solver binary_merge))
|
||||
(apply (with card2bv :pb.solver bv))
|
||||
(apply (with card2bv :pb.solver solver))
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports cores
|
||||
* does not support proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "ast/pb_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include<typeinfo>
|
||||
#include "util/sorting_network.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/card2bv.h"
|
||||
|
||||
inline tactic* mk_card2bv_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(card2bv, m, p, s); });
|
||||
}
|
||||
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
namespace pb {
|
||||
|
||||
class card2bv_rewriter {
|
||||
public:
|
||||
typedef expr* pliteral;
|
||||
typedef ptr_vector<expr> pliteral_vector;
|
||||
private:
|
||||
ast_manager& m;
|
||||
arith_util au;
|
||||
pb_util pb;
|
||||
bv_util bv;
|
||||
psort_nw<card2bv_rewriter> m_sort;
|
||||
expr_ref_vector m_lemmas;
|
||||
expr_ref_vector m_trail;
|
||||
|
||||
unsigned get_num_bits(func_decl* f);
|
||||
void mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result);
|
||||
br_status mk_shannon(func_decl * f, unsigned sz, expr * const* args, expr_ref & result);
|
||||
expr* negate(expr* e);
|
||||
expr* mk_ite(expr* c, expr* hi, expr* lo);
|
||||
bool is_or(func_decl* f);
|
||||
bool is_and(func_decl* f);
|
||||
bool is_atmost1(func_decl* f, unsigned sz, expr * const* args, expr_ref& result);
|
||||
expr_ref mk_atmost1(unsigned sz, expr * const* args);
|
||||
void mk_at_most_1_small(bool last, unsigned n, pliteral const* xs, expr_ref_vector& result, expr_ref_vector& ors);
|
||||
|
||||
public:
|
||||
card2bv_rewriter(ast_manager& m);
|
||||
br_status mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result);
|
||||
void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas);
|
||||
|
||||
// definitions used for sorting network
|
||||
pliteral mk_false() { return m.mk_false(); }
|
||||
pliteral mk_true() { return m.mk_true(); }
|
||||
pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); }
|
||||
pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); }
|
||||
pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); }
|
||||
std::ostream& pp(std::ostream& out, pliteral lit);
|
||||
pliteral fresh();
|
||||
pliteral trail(pliteral l);
|
||||
void mk_clause(unsigned n, pliteral const* lits);
|
||||
|
||||
};
|
||||
|
||||
struct card2bv_rewriter_cfg : public default_rewriter_cfg {
|
||||
card2bv_rewriter m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
bool flat_assoc(func_decl * f) const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
result_pr = nullptr;
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
card2bv_rewriter_cfg(ast_manager & m):m_r(m) {}
|
||||
};
|
||||
|
||||
class card_pb_rewriter : public rewriter_tpl<card2bv_rewriter_cfg> {
|
||||
card2bv_rewriter_cfg m_cfg;
|
||||
pb_util pb;
|
||||
expr_ref_vector m_lemmas;
|
||||
public:
|
||||
card_pb_rewriter(ast_manager & m):
|
||||
rewriter_tpl<card2bv_rewriter_cfg>(m, false, m_cfg),
|
||||
m_cfg(m),
|
||||
pb(m),
|
||||
m_lemmas(m) {}
|
||||
|
||||
void rewrite(expr* e, expr_ref& result);
|
||||
|
||||
expr_ref_vector& lemmas() { return m_lemmas; }
|
||||
};
|
||||
};
|
||||
|
||||
tactic * mk_card2bv_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
/*
|
||||
ADD_TACTIC("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "mk_card2bv_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("card2bv", "convert pseudo-boolean constraints to bit-vectors.", "alloc(card2bv, m, p, s)")
|
||||
*/
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "tactic/core/simplify_tactic.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
|
|
@ -5,18 +5,37 @@ Module Name:
|
|||
|
||||
degree_shift_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Simple degree shift procedure.
|
||||
Basic idea: if goal G contains a real variable x, x occurs with degrees
|
||||
d_1, ..., d_k in G, and n = gcd(d_1, ..., d_k) > 1.
|
||||
Then, replace x^n with a new fresh variable y.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic degree-shift
|
||||
|
||||
### Short Description
|
||||
|
||||
The procedure reduces the degrees of variables.
|
||||
|
||||
### Long Description
|
||||
|
||||
Basic idea: if goal $G$ contains a real variable $x$, $x$ occurs with degrees
|
||||
$d_1, ..., d_k$ in $G$, and $n = \gcd(d_1, ..., d_k) > 1$.
|
||||
Then, replace $x^n$ with a new fresh variable $y$.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Real)
|
||||
(declare-const y Real)
|
||||
(assert (> (+ (* x x x 4) (* x x 3)) 0))
|
||||
(assert (= (* x x) (* y y)))
|
||||
(apply degree-shift)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports proofs and cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -365,7 +365,7 @@ public:
|
|||
}
|
||||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
r.insert("diff_neq_max_k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver.");
|
||||
r.insert("diff_neq_max_k", CPK_UINT, "maximum variable upper bound for diff neq solver.", "1024");
|
||||
}
|
||||
|
||||
void collect_statistics(statistics & st) const override {
|
||||
|
|
|
@ -5,19 +5,45 @@ Module Name:
|
|||
|
||||
diff_neq_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
And all variables are bounded.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic diff-neq
|
||||
|
||||
### Short Description
|
||||
|
||||
A specialized solver for integer problems using only constant bounds and differences to constants.
|
||||
|
||||
### Long Description
|
||||
|
||||
Solver for integer problems that contains literals of the form
|
||||
```
|
||||
k <= x
|
||||
x <= k
|
||||
x - y != k
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (<= 0 x))
|
||||
(assert (<= x 1))
|
||||
(assert (<= 0 y))
|
||||
(assert (<= y 1))
|
||||
(assert (not (= (+ x (* -1 y)) -1)))
|
||||
(assert (not (= (+ x (* -1 y)) 1)))
|
||||
(assert (not (= (+ x (* -1 y)) 0)))
|
||||
(apply diff-neq)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* The tactic works only when the lower bounds are 0 and disequalities use multiplication with -1. Use normalize-bounds to ensure all lower bounds are 0.
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -18,7 +18,7 @@ Notes:
|
|||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
@ -179,7 +179,8 @@ public:
|
|||
|
||||
tactic_report report("eq2bv", *g);
|
||||
|
||||
m_bounds(*g);
|
||||
for (unsigned i = 0; i < g->size(); ++i)
|
||||
m_bounds(g->form(i), g->dep(i), g->pr(i));
|
||||
|
||||
if (m_bounds.inconsistent() || g->proofs_enabled()) {
|
||||
g->inc_depth();
|
||||
|
|
|
@ -5,16 +5,32 @@ Module Name:
|
|||
|
||||
eq2bv_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Extract integer variables that are used as finite domain indicators.
|
||||
The integer variables can only occur in equalities.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2015-8-19
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic eq2bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Extract integer variables that are used as finite domain indicators.
|
||||
The integer variables can only occur in equalities.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (or (= x 5) (> y 3)))
|
||||
(assert (or (= x 4) (= y 2)))
|
||||
(apply eq2bv)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -303,7 +303,7 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
r.insert("split_factors", CPK_BOOL,
|
||||
"(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).");
|
||||
"apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0)).", "true");
|
||||
polynomial::factor_params::get_param_descrs(r);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,21 @@ Author:
|
|||
|
||||
Leonardo de Moura (leonardo) 2012-02-03
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic factor
|
||||
|
||||
### Short Description
|
||||
|
||||
Factor polynomials in equalities and inequalities.
|
||||
|
||||
### Example
|
||||
```z3
|
||||
(declare-const x Real)
|
||||
(declare-const y Real)
|
||||
(assert (> (* x x) (* x y)))
|
||||
(apply factor)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -23,7 +23,7 @@ Revision History:
|
|||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
|
|
@ -7,18 +7,35 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
|
||||
Fix a difference logic variable to 0.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form (x + k), and the arithmetic atoms are of the
|
||||
form x - y <= k or x - y = k. Then, we can set one variable to 0.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-29
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic fix-dl-var
|
||||
|
||||
### Short Description
|
||||
|
||||
Fix a difference logic variable to `0`.
|
||||
If the problem is in the difference logic fragment, that is, all arithmetic terms
|
||||
are of the form `(x + k)`, and the arithmetic atoms are of the
|
||||
form `x - y <= k` or `x - y = k`. Then, we can set one variable to `0`.
|
||||
|
||||
This is useful because, many bounds can be exposed after this operation is performed.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Real)
|
||||
(declare-const y Real)
|
||||
(declare-const z Real)
|
||||
(assert (<= (+ x (* -1.0 y)) 3.0))
|
||||
(assert (<= (+ x (* -1.0 z)) 5.0))
|
||||
(apply fix-dl-var)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -467,10 +467,8 @@ class fm_tactic : public tactic {
|
|||
x = t;
|
||||
return true;
|
||||
}
|
||||
else if (m_util.is_to_real(t) && is_uninterp_const(to_app(t)->get_arg(0))) {
|
||||
x = to_app(t)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
else if (m_util.is_to_real(t, x) && is_uninterp_const(x))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1675,12 +1673,12 @@ public:
|
|||
void collect_param_descrs(param_descrs & r) override {
|
||||
insert_produce_models(r);
|
||||
insert_max_memory(r);
|
||||
r.insert("fm_real_only", CPK_BOOL, "(default: true) consider only real variables for fourier-motzkin elimination.");
|
||||
r.insert("fm_occ", CPK_BOOL, "(default: false) consider inequalities occurring in clauses for FM.");
|
||||
r.insert("fm_limit", CPK_UINT, "(default: 5000000) maximum number of constraints, monomials, clauses visited during FM.");
|
||||
r.insert("fm_cutoff1", CPK_UINT, "(default: 8) first cutoff for FM based on maximum number of lower/upper occurrences.");
|
||||
r.insert("fm_cutoff2", CPK_UINT, "(default: 256) second cutoff for FM based on num_lower * num_upper occurrences.");
|
||||
r.insert("fm_extra", CPK_UINT, "(default: 0) max. increase on the number of inequalities for each FM variable elimination step.");
|
||||
r.insert("fm_real_only", CPK_BOOL, "consider only real variables for fourier-motzkin elimination.", "true");
|
||||
r.insert("fm_occ", CPK_BOOL, "consider inequalities occurring in clauses for FM.", "false");
|
||||
r.insert("fm_limit", CPK_UINT, "maximum number of constraints, monomials, clauses visited during FM.", "5000000");
|
||||
r.insert("fm_cutoff1", CPK_UINT, "first cutoff for FM based on maximum number of lower/upper occurrences.", "8");
|
||||
r.insert("fm_cutoff2", CPK_UINT, "second cutoff for FM based on num_lower * num_upper occurrences.", "256");
|
||||
r.insert("fm_extra", CPK_UINT, "max. increase on the number of inequalities for each FM variable elimination step.", "0");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,20 +5,43 @@ Module Name:
|
|||
|
||||
fm_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Use Fourier-Motzkin to eliminate variables.
|
||||
This strategy can handle conditional bounds
|
||||
(i.e., clauses with at most one constraint).
|
||||
|
||||
The strategy mk_occf can be used to put the
|
||||
formula in OCC form.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-04.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic fm
|
||||
|
||||
### Short Description
|
||||
|
||||
Use Fourier-Motzkin to eliminate variables.
|
||||
This strategy can handle conditional bounds
|
||||
(i.e., clauses with at most one constraint).
|
||||
|
||||
The tactic occf can be used to put the
|
||||
formula in OCC form.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Real)
|
||||
(declare-const y Real)
|
||||
(declare-const z Real)
|
||||
(declare-const u Real)
|
||||
(declare-const v Real)
|
||||
(declare-const w Real)
|
||||
(declare-fun P (Real) Bool)
|
||||
(assert (<= x (+ y (* 2.0 z))))
|
||||
(assert (>= x (- y z)))
|
||||
(assert (>= x (- y 3 (* 3 z))))
|
||||
(assert (>= x 5))
|
||||
(assert (<= x u))
|
||||
(assert (>= x v))
|
||||
(assert (P u))
|
||||
(assert (P v))
|
||||
(apply fm)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -24,8 +24,8 @@ Notes:
|
|||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp_util.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class lia2card_tactic : public tactic {
|
||||
|
||||
|
@ -180,7 +180,8 @@ public:
|
|||
tactic_report report("lia2card", *g);
|
||||
|
||||
bound_manager bounds(m);
|
||||
bounds(*g);
|
||||
for (unsigned i = 0; i < g->size(); ++i)
|
||||
bounds(g->form(i), g->dep(i), g->pr(i));
|
||||
|
||||
for (expr* x : bounds) {
|
||||
checkpoint();
|
||||
|
|
|
@ -5,16 +5,38 @@ Module Name:
|
|||
|
||||
lia2card_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Extract 0-1 integer variables used in
|
||||
cardinality constraints and replace them by Booleans.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-11-5
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic lia2card
|
||||
|
||||
### Short Description
|
||||
|
||||
Extract 0-1 integer variables used in
|
||||
cardinality and pseudo-Boolean constraints and replace them by Booleans.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(assert (<= 0 x))
|
||||
(assert (<= 0 y))
|
||||
(assert (<= 0 z))
|
||||
(assert (>= 1 x))
|
||||
(assert (>= 1 y))
|
||||
(assert (>= 1 z))
|
||||
(assert (>= (+ (* 5 x) (* -2 z) (* 3 y) 1) 4))
|
||||
(apply lia2card)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* The tactic does not (properly) support proofs or cores.
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -17,10 +17,10 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
@ -197,7 +197,8 @@ class lia2pb_tactic : public tactic {
|
|||
return;
|
||||
}
|
||||
|
||||
m_bm(*g);
|
||||
for (unsigned i = 0; i < g->size(); ++i)
|
||||
m_bm(g->form(i), g->dep(i), g->pr(i));
|
||||
|
||||
TRACE("lia2pb", m_bm.display(tout););
|
||||
|
||||
|
|
|
@ -5,15 +5,31 @@ Module Name:
|
|||
|
||||
lia2pb_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-07.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic lia2pb
|
||||
|
||||
### Short Description
|
||||
|
||||
Reduce bounded LIA benchmark into 0-1 LIA benchmark.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (<= 0 x))
|
||||
(assert (<= x 5))
|
||||
(assert (<= 0 y))
|
||||
(assert (<= y 5))
|
||||
(assert (>= (+ (* 2 x) y) 5))
|
||||
(apply lia2pb)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -1,279 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "tactic/arith/linear_equation.h"
|
||||
|
||||
/**
|
||||
\brief Return the position of variable x_i in the linear equation.
|
||||
Return UINT_MAX, if the variable is not in the linear_equation.
|
||||
*/
|
||||
unsigned linear_equation::pos(unsigned x_i) const {
|
||||
int low = 0;
|
||||
int high = m_size - 1;
|
||||
while (true) {
|
||||
int mid = low + ((high - low) / 2);
|
||||
var x_mid = m_xs[mid];
|
||||
if (x_i > x_mid) {
|
||||
low = mid + 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else if (x_i < x_mid) {
|
||||
high = mid - 1;
|
||||
if (low > high)
|
||||
return UINT_MAX;
|
||||
}
|
||||
else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void linear_equation_manager::display(std::ostream & out, linear_equation const & eq) const {
|
||||
unsigned sz = eq.m_size;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (i > 0)
|
||||
out << " + ";
|
||||
out << m.to_string(eq.m_as[i]) << "*x" << eq.m_xs[i];
|
||||
}
|
||||
out << " = 0";
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpq * as, var * xs, bool normalized) {
|
||||
SASSERT(sz > 1);
|
||||
|
||||
// compute lcm of the denominators
|
||||
mpz l;
|
||||
mpz r;
|
||||
m.set(l, as[0].denominator());
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
m.set(r, as[i].denominator());
|
||||
m.lcm(r, l, l);
|
||||
}
|
||||
|
||||
TRACE("linear_equation_mk", tout << "lcm: " << m.to_string(l) << "\n";);
|
||||
|
||||
// copy l * as to m_int_buffer.
|
||||
m_int_buffer.reset();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
TRACE("linear_equation_mk", tout << "before as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
m.mul(l, as[i], as[i]);
|
||||
TRACE("linear_equation_mk", tout << "after as[" << i << "]: " << m.to_string(as[i]) << "\n";);
|
||||
SASSERT(m.is_int(as[i]));
|
||||
m_int_buffer.push_back(as[i].numerator());
|
||||
}
|
||||
|
||||
linear_equation * new_eq = mk(sz, m_int_buffer.data(), xs, normalized);
|
||||
|
||||
m.del(r);
|
||||
m.del(l);
|
||||
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk_core(unsigned sz, mpz * as, var * xs) {
|
||||
SASSERT(sz > 0);
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
SASSERT(xs[i-1] < xs[i]);
|
||||
}
|
||||
});
|
||||
|
||||
TRACE("linear_equation_bug", for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
mpz g;
|
||||
m.set(g, as[0]);
|
||||
for (unsigned i = 1; i < sz; i++) {
|
||||
if (m.is_one(g))
|
||||
break;
|
||||
if (m.is_neg(as[i])) {
|
||||
m.neg(as[i]);
|
||||
m.gcd(g, as[i], g);
|
||||
m.neg(as[i]);
|
||||
}
|
||||
else {
|
||||
m.gcd(g, as[i], g);
|
||||
}
|
||||
}
|
||||
if (!m.is_one(g)) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
m.div(as[i], g, as[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("linear_equation_bug",
|
||||
tout << "g: " << m.to_string(g) << "\n";
|
||||
for (unsigned i = 0; i < sz; i++) tout << m.to_string(as[i]) << "*x" << xs[i] << " "; tout << "\n";);
|
||||
|
||||
m.del(g);
|
||||
|
||||
unsigned obj_sz = linear_equation::get_obj_size(sz);
|
||||
void * mem = m_allocator.allocate(obj_sz);
|
||||
linear_equation * new_eq = new (mem) linear_equation();
|
||||
mpz * new_as = reinterpret_cast<mpz*>(reinterpret_cast<char*>(new_eq) + sizeof(linear_equation));
|
||||
double * new_app_as = reinterpret_cast<double*>(reinterpret_cast<char*>(new_as) + sz * sizeof(mpz));
|
||||
var * new_xs = reinterpret_cast<var *>(reinterpret_cast<char*>(new_app_as) + sz * sizeof(double));
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
new (new_as + i) mpz();
|
||||
m.set(new_as[i], as[i]);
|
||||
new_app_as[i] = m.get_double(as[i]);
|
||||
var x_i = xs[i];
|
||||
new_xs[i] = x_i;
|
||||
}
|
||||
new_eq->m_size = sz;
|
||||
new_eq->m_as = new_as;
|
||||
new_eq->m_approx_as = new_app_as;
|
||||
new_eq->m_xs = new_xs;
|
||||
return new_eq;
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, bool normalized) {
|
||||
if (!normalized) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
m_val_buffer.reserve(x+1);
|
||||
|
||||
if (m_mark[x]) {
|
||||
m.add(m_val_buffer[x], as[i], m_val_buffer[x]);
|
||||
}
|
||||
else {
|
||||
m.set(m_val_buffer[x], as[i]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
if (m_mark[x]) {
|
||||
if (!m.is_zero(m_val_buffer[x])) {
|
||||
xs[j] = xs[i];
|
||||
m.set(as[j], m_val_buffer[x]);
|
||||
j++;
|
||||
}
|
||||
m_mark[x] = false;
|
||||
}
|
||||
}
|
||||
sz = j;
|
||||
if (sz <= 1)
|
||||
return nullptr;
|
||||
}
|
||||
else {
|
||||
DEBUG_CODE({
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark.reserve(x+1, false);
|
||||
SASSERT(!m_mark[x]);
|
||||
m_mark[x] = true;
|
||||
}
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_mark[x] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m_val_buffer.reserve(x+1);
|
||||
m.swap(m_val_buffer[x], as[i]);
|
||||
}
|
||||
std::sort(xs, xs+sz);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
var x = xs[i];
|
||||
m.swap(as[i], m_val_buffer[x]);
|
||||
}
|
||||
|
||||
return mk_core(sz, as, xs);
|
||||
}
|
||||
|
||||
linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2) {
|
||||
SASSERT(!m.is_zero(b1));
|
||||
SASSERT(!m.is_zero(b2));
|
||||
mpz tmp, new_a;
|
||||
m_int_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
unsigned sz1 = eq1.size();
|
||||
unsigned sz2 = eq2.size();
|
||||
unsigned i1 = 0;
|
||||
unsigned i2 = 0;
|
||||
while (true) {
|
||||
if (i1 == sz1) {
|
||||
// copy remaining entries from eq2
|
||||
while (i2 < sz2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i2 == sz2) {
|
||||
// copy remaining entries from eq1
|
||||
while (i1 < sz1) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
var x1 = eq1.x(i1);
|
||||
var x2 = eq2.x(i2);
|
||||
if (x1 < x2) {
|
||||
m_int_buffer.push_back(eq1.a(i1));
|
||||
m.mul(m_int_buffer.back(), b1, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
i1++;
|
||||
}
|
||||
else if (x1 > x2) {
|
||||
m_int_buffer.push_back(eq2.a(i2));
|
||||
m.mul(m_int_buffer.back(), b2, m_int_buffer.back());
|
||||
m_var_buffer.push_back(eq2.x(i2));
|
||||
i2++;
|
||||
}
|
||||
else {
|
||||
m.mul(eq1.a(i1), b1, tmp);
|
||||
m.addmul(tmp, b2, eq2.a(i2), new_a);
|
||||
if (!m.is_zero(new_a)) {
|
||||
m_int_buffer.push_back(new_a);
|
||||
m_var_buffer.push_back(eq1.x(i1));
|
||||
}
|
||||
i1++;
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
m.del(tmp);
|
||||
m.del(new_a);
|
||||
SASSERT(m_int_buffer.size() == m_var_buffer.size());
|
||||
if (m_int_buffer.empty())
|
||||
return nullptr;
|
||||
return mk_core(m_int_buffer.size(), m_int_buffer.data(), m_var_buffer.data());
|
||||
}
|
||||
|
||||
void linear_equation_manager::del(linear_equation * eq) {
|
||||
for (unsigned i = 0; i < eq->m_size; i++) {
|
||||
m.del(eq->m_as[i]);
|
||||
}
|
||||
unsigned obj_sz = linear_equation::get_obj_size(eq->m_size);
|
||||
m_allocator.deallocate(obj_sz, eq);
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
linear_equation.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic infrastructure for managing linear equations of the form:
|
||||
|
||||
a_1 * x_1 + ... + a_n * x_n = 0
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-06-28
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/mpq.h"
|
||||
#include "util/small_object_allocator.h"
|
||||
#include "util/numeral_buffer.h"
|
||||
#include "util/double_manager.h"
|
||||
|
||||
class linear_equation {
|
||||
public:
|
||||
typedef unsigned var;
|
||||
private:
|
||||
static unsigned get_obj_size(unsigned sz) { return sizeof(linear_equation) + sz * (sizeof(mpz) + sizeof(double) + sizeof(var)); }
|
||||
friend class linear_equation_manager;
|
||||
unsigned m_size;
|
||||
mpz * m_as; // precise coefficients
|
||||
double * m_approx_as; // approximated coefficients
|
||||
var * m_xs; // var ids
|
||||
linear_equation() {}
|
||||
public:
|
||||
unsigned size() const { return m_size; }
|
||||
mpz const & a(unsigned idx) const { SASSERT(idx < m_size); return m_as[idx]; }
|
||||
double approx_a(unsigned idx) const { SASSERT(idx < m_size); return m_approx_as[idx]; }
|
||||
var x(unsigned idx) const { SASSERT(idx < m_size); return m_xs[idx]; }
|
||||
unsigned pos(unsigned x_i) const;
|
||||
void get_a(double_manager & m, unsigned idx, double & r) const { r = m_approx_as[idx]; }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpq & r) const { m.set(r, m_as[idx]); }
|
||||
template<typename NumManager>
|
||||
void get_a(NumManager & m, unsigned idx, mpz & r) const { m.set(r, m_as[idx]); }
|
||||
};
|
||||
|
||||
class linear_equation_manager {
|
||||
public:
|
||||
typedef unsynch_mpq_manager numeral_manager;
|
||||
typedef linear_equation::var var;
|
||||
typedef numeral_buffer<mpz, numeral_manager> mpz_buffer;
|
||||
private:
|
||||
typedef svector<var> var_buffer;
|
||||
|
||||
small_object_allocator & m_allocator;
|
||||
numeral_manager & m;
|
||||
mpz_buffer m_int_buffer;
|
||||
mpz_buffer m_val_buffer;
|
||||
char_vector m_mark;
|
||||
var_buffer m_var_buffer;
|
||||
|
||||
linear_equation * mk_core(unsigned sz, mpz * as, var * xs);
|
||||
|
||||
public:
|
||||
linear_equation_manager(numeral_manager & _m, small_object_allocator & a):m_allocator(a), m(_m), m_int_buffer(m), m_val_buffer(m) {}
|
||||
|
||||
linear_equation * mk(unsigned sz, mpq * as, var * xs, bool normalized = false);
|
||||
linear_equation * mk(unsigned sz, mpz * as, var * xs, bool normalized = false);
|
||||
void del(linear_equation * eq);
|
||||
|
||||
// Return b1 * eq1 + b2 * eq2
|
||||
// return 0 if the b1 * eq1 + b2 * eq2 == 0
|
||||
linear_equation * mk(mpz const & b1, linear_equation const & eq1, mpz const & b2, linear_equation const & eq2);
|
||||
|
||||
void display(std::ostream & out, linear_equation const & eq) const;
|
||||
};
|
||||
|
|
@ -27,8 +27,8 @@ Notes:
|
|||
#include "util/optional.h"
|
||||
#include "tactic/arith/bv2int_rewriter.h"
|
||||
#include "tactic/arith/bv2real_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "util/obj_pair_hashtable.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
|
@ -89,7 +89,8 @@ class nla2bv_tactic : public tactic {
|
|||
);
|
||||
tactic_report report("nla->bv", g);
|
||||
m_fmc = alloc(generic_model_converter, m_manager, "nla2bv");
|
||||
m_bounds(g);
|
||||
for (unsigned i = 0; i < g.size(); ++i)
|
||||
m_bounds(g.form(i), g.dep(i), g.pr(i));
|
||||
collect_power2(g);
|
||||
switch (collect_vars(g)) {
|
||||
case has_num:
|
||||
|
@ -442,9 +443,9 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
r.insert("nla2bv_max_bv_size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic");
|
||||
r.insert("nla2bv_bv_size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic.");
|
||||
r.insert("nla2bv_root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding.");
|
||||
r.insert("nla2bv_divisor", CPK_UINT, "(default: 2) nla2bv tactic parameter.");
|
||||
r.insert("nla2bv_bv_size", CPK_UINT, "default bit-vector size used by nla2bv tactic.", "4");
|
||||
r.insert("nla2bv_root", CPK_UINT, "nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding.", "2");
|
||||
r.insert("nla2bv_divisor", CPK_UINT, "nla2bv tactic parameter.", "2");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,6 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
Author:
|
||||
|
||||
|
@ -16,6 +15,29 @@ Author:
|
|||
Notes:
|
||||
Ported to tactic framework on 2012-02-28
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic nla2bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Convert quantified NIA problems to bounded bit-vector arithmetic problems.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(assert (= (* x x y) (* 2 z y y)))
|
||||
(apply nla2bv)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* The tactic creates an under-approximation (a stronger set of formulas)
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -19,9 +19,9 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
@ -67,13 +67,11 @@ class normalize_bounds_tactic : public tactic {
|
|||
}
|
||||
|
||||
bool has_lowers() {
|
||||
bound_manager::iterator it = m_bm.begin();
|
||||
bound_manager::iterator end = m_bm.end();
|
||||
for (; it != end; ++it) {
|
||||
for (auto* e : m_bm) {
|
||||
TRACE("normalize_bounds_tactic",
|
||||
rational val; bool strict;
|
||||
tout << mk_ismt2_pp(*it, m) << " has_lower: " << m_bm.has_lower(*it, val, strict) << " val: " << val << "\n";);
|
||||
if (is_target(*it))
|
||||
tout << mk_ismt2_pp(e, m) << " has_lower: " << m_bm.has_lower(e, val, strict) << " val: " << val << "\n";);
|
||||
if (is_target(e))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -83,8 +81,9 @@ class normalize_bounds_tactic : public tactic {
|
|||
bool produce_models = in->models_enabled();
|
||||
bool produce_proofs = in->proofs_enabled();
|
||||
tactic_report report("normalize-bounds", *in);
|
||||
|
||||
m_bm(*in);
|
||||
|
||||
for (unsigned i = 0; i < in->size(); ++i)
|
||||
m_bm(in->form(i), in->dep(i), in->pr(i));
|
||||
|
||||
if (!has_lowers()) {
|
||||
result.push_back(in.get());
|
||||
|
@ -161,7 +160,7 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
insert_produce_models(r);
|
||||
r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants.");
|
||||
r.insert("norm_int_only", CPK_BOOL, "normalize only the bounds of integer constants.", "true");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & in,
|
||||
|
|
|
@ -5,17 +5,34 @@ Module Name:
|
|||
|
||||
normalize_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Replace x with x' + l, when l <= x
|
||||
where x' is a fresh variable.
|
||||
Note that, after the transformation 0 <= x'.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-10-21.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic normalize-bounds
|
||||
|
||||
### Short Description
|
||||
|
||||
Replace $x$ with $x' + l$, when $l \leq x$
|
||||
where $x'$ is a fresh variable.
|
||||
Note that, after the transformation $0 \leq x'$.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(assert (<= 3 x))
|
||||
(assert (<= (+ x y) z))
|
||||
(apply normalize-bounds)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports proofs and cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -18,8 +18,8 @@ Notes:
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
|
||||
class pb2bv_model_converter : public model_converter {
|
||||
typedef std::pair<func_decl *, func_decl *> func_decl_pair;
|
||||
|
|
|
@ -28,8 +28,8 @@ Notes:
|
|||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "ast/rewriter/pb2bv_rewriter.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_manager.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/simplifiers/bound_manager.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "tactic/arith/pb2bv_model_converter.h"
|
||||
#include "tactic/arith/pb2bv_tactic.h"
|
||||
|
||||
|
@ -866,7 +866,7 @@ private:
|
|||
m_used_dependencies(m),
|
||||
m_rw(*this) {
|
||||
updt_params(p);
|
||||
m_b_rw.set_flat(false); // no flattening otherwise will blowup the memory
|
||||
m_b_rw.set_flat_and_or(false); // no flattening otherwise will blowup the memory
|
||||
m_b_rw.set_elim_and(true);
|
||||
}
|
||||
|
||||
|
@ -913,7 +913,9 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
m_bm(*g);
|
||||
unsigned size = g->size();
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
m_bm(g->form(i), g->dep(i), g->pr(i));
|
||||
|
||||
TRACE("pb2bv", m_bm.display(tout););
|
||||
|
||||
|
@ -924,7 +926,6 @@ private:
|
|||
throw_tactic(p.e);
|
||||
}
|
||||
|
||||
unsigned size = g->size();
|
||||
expr_ref_vector new_exprs(m);
|
||||
expr_dependency_ref_vector new_deps(m);
|
||||
|
||||
|
@ -1042,7 +1043,8 @@ struct is_pb_probe : public probe {
|
|||
try {
|
||||
ast_manager & m = g.m();
|
||||
bound_manager bm(m);
|
||||
bm(g);
|
||||
for (unsigned i = 0; i < g.size(); i++)
|
||||
bm(g.form(i), g.dep(i), g.pr(i));
|
||||
arith_util a_util(m);
|
||||
pb_util pb(m);
|
||||
expr_fast_mark1 visited;
|
||||
|
|
|
@ -13,7 +13,32 @@ Author:
|
|||
|
||||
Christoph (cwinter) 2012-02-15
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic pb2bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Convert pseudo-boolean constraints to bit-vectors
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(declare-const u Int)
|
||||
(assert (<= 0 x))
|
||||
(assert (<= 0 y))
|
||||
(assert (<= 0 z))
|
||||
(assert (<= 0 u))
|
||||
(assert (<= x 1))
|
||||
(assert (<= y 1))
|
||||
(assert (<= z 1))
|
||||
(assert (<= u 1))
|
||||
(assert (>= (+ (* 3 x) (* 2 y) (* 2 z) (* 2 u)) 4))
|
||||
(apply pb2bv)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -1,566 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/arith/bound_propagator.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "tactic/core/simplify_tactic.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
class propagate_ineqs_tactic : public tactic {
|
||||
struct imp;
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
propagate_ineqs_tactic(ast_manager & m, params_ref const & p);
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
return alloc(propagate_ineqs_tactic, m, m_params);
|
||||
}
|
||||
|
||||
~propagate_ineqs_tactic() override;
|
||||
|
||||
char const* name() const override { return "propagate_ineqs"; }
|
||||
|
||||
void updt_params(params_ref const & p) override;
|
||||
void collect_param_descrs(param_descrs & r) override {}
|
||||
|
||||
void operator()(goal_ref const & g, goal_ref_buffer & result) override;
|
||||
|
||||
void cleanup() override;
|
||||
};
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(propagate_ineqs_tactic, m, p));
|
||||
}
|
||||
|
||||
struct propagate_ineqs_tactic::imp {
|
||||
ast_manager & m;
|
||||
unsynch_mpq_manager nm;
|
||||
small_object_allocator m_allocator;
|
||||
bound_propagator bp;
|
||||
arith_util m_util;
|
||||
typedef bound_propagator::var a_var;
|
||||
obj_map<expr, a_var> m_expr2var;
|
||||
expr_ref_vector m_var2expr;
|
||||
|
||||
typedef numeral_buffer<mpq, unsynch_mpq_manager> mpq_buffer;
|
||||
typedef svector<a_var> var_buffer;
|
||||
|
||||
mpq_buffer m_num_buffer;
|
||||
var_buffer m_var_buffer;
|
||||
goal_ref m_new_goal;
|
||||
|
||||
imp(ast_manager & _m, params_ref const & p):
|
||||
m(_m),
|
||||
m_allocator("ineq-simplifier"),
|
||||
bp(nm, m_allocator, p),
|
||||
m_util(m),
|
||||
m_var2expr(m),
|
||||
m_num_buffer(nm) {
|
||||
updt_params_core(p);
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
updt_params_core(p);
|
||||
bp.updt_params(p);
|
||||
}
|
||||
|
||||
void display_bounds(std::ostream & out) {
|
||||
unsigned sz = m_var2expr.size();
|
||||
mpq k;
|
||||
bool strict;
|
||||
unsigned ts;
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
if (bp.lower(x, k, strict, ts))
|
||||
out << nm.to_string(k) << " " << (strict ? "<" : "<=");
|
||||
else
|
||||
out << "-oo <";
|
||||
out << " " << mk_ismt2_pp(m_var2expr.get(x), m) << " ";
|
||||
if (bp.upper(x, k, strict, ts))
|
||||
out << (strict ? "<" : "<=") << " " << nm.to_string(k);
|
||||
else
|
||||
out << "< oo";
|
||||
out << "\n";
|
||||
}
|
||||
nm.del(k);
|
||||
}
|
||||
|
||||
a_var mk_var(expr * t) {
|
||||
if (m_util.is_to_real(t))
|
||||
t = to_app(t)->get_arg(0);
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = m_var2expr.size();
|
||||
bp.mk_var(x, m_util.is_int(t));
|
||||
m_var2expr.push_back(t);
|
||||
m_expr2var.insert(t, x);
|
||||
return x;
|
||||
}
|
||||
|
||||
void expr2linear_pol(expr * t, mpq_buffer & as, var_buffer & xs) {
|
||||
mpq c_mpq_val;
|
||||
if (m_util.is_add(t)) {
|
||||
rational c_val;
|
||||
unsigned num = to_app(t)->get_num_args();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
expr * mon = to_app(t)->get_arg(i);
|
||||
expr * c, * x;
|
||||
if (m_util.is_mul(mon, c, x) && m_util.is_numeral(c, c_val)) {
|
||||
nm.set(c_mpq_val, c_val.to_mpq());
|
||||
as.push_back(c_mpq_val);
|
||||
xs.push_back(mk_var(x));
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(mon));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
as.push_back(mpq(1));
|
||||
xs.push_back(mk_var(t));
|
||||
}
|
||||
nm.del(c_mpq_val);
|
||||
}
|
||||
|
||||
a_var mk_linear_pol(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return x;
|
||||
x = mk_var(t);
|
||||
if (m_util.is_add(t)) {
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(t, m_num_buffer, m_var_buffer);
|
||||
m_num_buffer.push_back(mpq(-1));
|
||||
m_var_buffer.push_back(x);
|
||||
bp.mk_eq(m_num_buffer.size(), m_num_buffer.data(), m_var_buffer.data());
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
enum kind { EQ, LE, GE };
|
||||
|
||||
bool process(expr * t) {
|
||||
bool sign = false;
|
||||
while (m.is_not(t, t))
|
||||
sign = !sign;
|
||||
bool strict = false;
|
||||
kind k;
|
||||
if (m.is_eq(t)) {
|
||||
if (sign)
|
||||
return false;
|
||||
k = EQ;
|
||||
}
|
||||
else if (m_util.is_le(t)) {
|
||||
if (sign) {
|
||||
k = GE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = LE;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_ge(t)) {
|
||||
if (sign) {
|
||||
k = LE;
|
||||
strict = true;
|
||||
}
|
||||
else {
|
||||
k = GE;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_lt(t)) {
|
||||
if (sign) {
|
||||
k = GE;
|
||||
strict = false;
|
||||
} else {
|
||||
k = LE;
|
||||
strict = true;
|
||||
}
|
||||
}
|
||||
else if (m_util.is_gt(t)) {
|
||||
//x > y == x <=y, strict = false
|
||||
if (sign) {
|
||||
k = LE;
|
||||
strict = false;
|
||||
} else {
|
||||
k = GE;
|
||||
strict = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
expr * lhs = to_app(t)->get_arg(0);
|
||||
expr * rhs = to_app(t)->get_arg(1);
|
||||
if (m_util.is_numeral(lhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
if (k == LE)
|
||||
k = GE;
|
||||
else if (k == GE)
|
||||
k = LE;
|
||||
}
|
||||
|
||||
rational c;
|
||||
if (!m_util.is_numeral(rhs, c))
|
||||
return false;
|
||||
a_var x = mk_linear_pol(lhs);
|
||||
mpq c_prime;
|
||||
nm.set(c_prime, c.to_mpq());
|
||||
if (k == EQ) {
|
||||
SASSERT(!strict);
|
||||
bp.assert_lower(x, c_prime, false);
|
||||
bp.assert_upper(x, c_prime, false);
|
||||
}
|
||||
else if (k == LE) {
|
||||
bp.assert_upper(x, c_prime, strict);
|
||||
}
|
||||
else {
|
||||
SASSERT(k == GE);
|
||||
bp.assert_lower(x, c_prime, strict);
|
||||
}
|
||||
nm.del(c_prime);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool collect_bounds(goal const & g) {
|
||||
bool found = false;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * t = g.form(i);
|
||||
if (process(t))
|
||||
found = true;
|
||||
else
|
||||
m_new_goal->assert_expr(t); // save non-bounds here
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
bool lower_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.lower(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) &&
|
||||
(nm.gt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool upper_subsumed(expr * p, mpq const & k, bool strict) {
|
||||
if (!m_util.is_add(p))
|
||||
return false;
|
||||
m_num_buffer.reset();
|
||||
m_var_buffer.reset();
|
||||
expr2linear_pol(p, m_num_buffer, m_var_buffer);
|
||||
mpq implied_k;
|
||||
bool implied_strict;
|
||||
bool result =
|
||||
bp.upper(m_var_buffer.size(), m_num_buffer.data(), m_var_buffer.data(), implied_k, implied_strict) &&
|
||||
(nm.lt(implied_k, k) || (nm.eq(implied_k, k) && (!strict || implied_strict)));
|
||||
nm.del(implied_k);
|
||||
return result;
|
||||
}
|
||||
|
||||
void restore_bounds() {
|
||||
mpq l, u;
|
||||
bool strict_l, strict_u, has_l, has_u;
|
||||
unsigned ts;
|
||||
unsigned sz = m_var2expr.size();
|
||||
for (unsigned x = 0; x < sz; x++) {
|
||||
expr * p = m_var2expr.get(x);
|
||||
has_l = bp.lower(x, l, strict_l, ts);
|
||||
has_u = bp.upper(x, u, strict_u, ts);
|
||||
if (!has_l && !has_u)
|
||||
continue;
|
||||
if (has_l && has_u && nm.eq(l, u) && !strict_l && !strict_u) {
|
||||
// l <= p <= l --> p = l
|
||||
m_new_goal->assert_expr(m.mk_eq(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
continue;
|
||||
}
|
||||
if (has_l && !lower_subsumed(p, l, strict_l)) {
|
||||
if (strict_l)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_le(p, m_util.mk_numeral(rational(l), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_ge(p, m_util.mk_numeral(rational(l), m_util.is_int(p))));
|
||||
}
|
||||
if (has_u && !upper_subsumed(p, u, strict_u)) {
|
||||
if (strict_u)
|
||||
m_new_goal->assert_expr(m.mk_not(m_util.mk_ge(p, m_util.mk_numeral(rational(u), m_util.is_int(p)))));
|
||||
else
|
||||
m_new_goal->assert_expr(m_util.mk_le(p, m_util.mk_numeral(rational(u), m_util.is_int(p))));
|
||||
}
|
||||
}
|
||||
nm.del(l);
|
||||
nm.del(u);
|
||||
}
|
||||
|
||||
bool is_x_minus_y_eq_0(expr * t, expr * & x, expr * & y) {
|
||||
expr * lhs, * rhs, * m1, * m2;
|
||||
if (m.is_eq(t, lhs, rhs) && m_util.is_zero(rhs) && m_util.is_add(lhs, m1, m2)) {
|
||||
if (m_util.is_times_minus_one(m2, y) && is_uninterp_const(m1)) {
|
||||
x = m1;
|
||||
return true;
|
||||
}
|
||||
if (m_util.is_times_minus_one(m1, y) && is_uninterp_const(m2)) {
|
||||
x = m2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_unbounded(expr * t) {
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return !bp.has_lower(x) && !bp.has_upper(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lower(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.lower(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upper(expr * t, mpq & k, bool & strict) {
|
||||
unsigned ts;
|
||||
a_var x;
|
||||
if (m_expr2var.find(t, x))
|
||||
return bp.upper(x, k, strict, ts);
|
||||
return false;
|
||||
}
|
||||
|
||||
void find_ite_bounds(expr * root) {
|
||||
TRACE("find_ite_bounds_bug", display_bounds(tout););
|
||||
expr * n = root;
|
||||
expr * target = nullptr;
|
||||
expr * c, * t, * e;
|
||||
expr * x, * y;
|
||||
bool has_l, has_u;
|
||||
mpq l_min, u_max;
|
||||
bool l_strict, u_strict;
|
||||
mpq curr;
|
||||
bool curr_strict;
|
||||
while (true) {
|
||||
TRACE("find_ite_bounds_bug", tout << mk_ismt2_pp(n, m) << "\n";);
|
||||
|
||||
if (m.is_ite(n, c, t, e)) {
|
||||
if (is_x_minus_y_eq_0(t, x, y))
|
||||
n = e;
|
||||
else if (is_x_minus_y_eq_0(e, x, y))
|
||||
n = t;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (is_x_minus_y_eq_0(n, x, y)) {
|
||||
n = nullptr;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE("find_ite_bounds_bug", tout << "x: " << mk_ismt2_pp(x, m) << ", y: " << mk_ismt2_pp(y, m) << "\n";
|
||||
if (target) {
|
||||
tout << "target: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " has_u: " << has_u << " " << nm.to_string(u_max) << "\n";
|
||||
});
|
||||
|
||||
if (is_unbounded(y))
|
||||
std::swap(x, y);
|
||||
|
||||
if (!is_unbounded(x)) {
|
||||
TRACE("find_ite_bounds_bug", tout << "x is already bounded\n";);
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr) {
|
||||
target = x;
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
has_l = true;
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
has_u = true;
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
else if (target == x) {
|
||||
if (has_l) {
|
||||
if (lower(y, curr, curr_strict)) {
|
||||
if (nm.lt(curr, l_min) || (!curr_strict && l_strict && nm.eq(curr, l_min))) {
|
||||
nm.set(l_min, curr);
|
||||
l_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_l = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have lower\n";);
|
||||
}
|
||||
}
|
||||
if (has_u) {
|
||||
if (upper(y, curr, curr_strict)) {
|
||||
if (nm.gt(curr, u_max) || (curr_strict && !u_strict && nm.eq(curr, u_max))) {
|
||||
nm.set(u_max, curr);
|
||||
u_strict = curr_strict;
|
||||
}
|
||||
}
|
||||
else {
|
||||
has_u = false;
|
||||
TRACE("find_ite_bounds_bug", tout << "y does not have upper\n";);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!has_l && !has_u)
|
||||
break;
|
||||
|
||||
if (n == nullptr) {
|
||||
TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n";
|
||||
tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n";
|
||||
tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n";
|
||||
tout << "root:\n" << mk_ismt2_pp(root, m) << "\n";);
|
||||
a_var x = mk_var(target);
|
||||
if (has_l)
|
||||
bp.assert_lower(x, l_min, l_strict);
|
||||
if (has_u)
|
||||
bp.assert_upper(x, u_max, u_strict);
|
||||
break;
|
||||
}
|
||||
}
|
||||
nm.del(l_min);
|
||||
nm.del(u_max);
|
||||
nm.del(curr);
|
||||
}
|
||||
|
||||
void find_ite_bounds() {
|
||||
unsigned sz = m_new_goal->size();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
expr * f = m_new_goal->form(i);
|
||||
if (m.is_ite(f))
|
||||
find_ite_bounds(to_app(f));
|
||||
}
|
||||
bp.propagate();
|
||||
TRACE("find_ite_bounds", display_bounds(tout););
|
||||
}
|
||||
|
||||
void operator()(goal * g, goal_ref & r) {
|
||||
tactic_report report("propagate-ineqs", *g);
|
||||
|
||||
m_new_goal = alloc(goal, *g, true);
|
||||
m_new_goal->inc_depth();
|
||||
r = m_new_goal.get();
|
||||
if (!collect_bounds(*g)) {
|
||||
m_new_goal = nullptr;
|
||||
r = g;
|
||||
return; // nothing to be done
|
||||
}
|
||||
|
||||
TRACE("propagate_ineqs_tactic", g->display(tout); display_bounds(tout); tout << "bound propagator:\n"; bp.display(tout););
|
||||
|
||||
bp.propagate();
|
||||
|
||||
report_tactic_progress(":bound-propagations", bp.get_num_propagations());
|
||||
report_tactic_progress(":bound-false-alarms", bp.get_num_false_alarms());
|
||||
|
||||
if (bp.inconsistent()) {
|
||||
r->reset();
|
||||
r->assert_expr(m.mk_false());
|
||||
return;
|
||||
}
|
||||
|
||||
// find_ite_bounds(); // did not help
|
||||
|
||||
restore_bounds();
|
||||
|
||||
TRACE("propagate_ineqs_tactic", tout << "after propagation:\n"; display_bounds(tout); bp.display(tout););
|
||||
TRACE("propagate_ineqs_tactic", r->display(tout););
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
propagate_ineqs_tactic::propagate_ineqs_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
propagate_ineqs_tactic::~propagate_ineqs_tactic() {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::updt_params(params_ref const & p) {
|
||||
m_params.append(p);
|
||||
m_imp->updt_params(m_params);
|
||||
}
|
||||
|
||||
void propagate_ineqs_tactic::operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result) {
|
||||
fail_if_proof_generation("propagate-ineqs", g);
|
||||
fail_if_unsat_core_generation("propagate-ineqs", g);
|
||||
result.reset();
|
||||
goal_ref r;
|
||||
(*m_imp)(g.get(), r);
|
||||
result.push_back(r.get());
|
||||
SASSERT(r->is_well_formed());
|
||||
}
|
||||
|
||||
|
||||
void propagate_ineqs_tactic::cleanup() {
|
||||
imp * d = alloc(imp, m_imp->m, m_params);
|
||||
std::swap(d, m_imp);
|
||||
dealloc(d);
|
||||
}
|
|
@ -4,40 +4,66 @@ Copyright (c) 2012 Microsoft Corporation
|
|||
Module Name:
|
||||
|
||||
propagate_ineqs_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
For example:
|
||||
x - y >= 3
|
||||
can be replaced with true if we know that
|
||||
x >= 3 and y <= 0
|
||||
|
||||
- Convert inequalities of the form p <= k and p >= k into p = k,
|
||||
where p is a polynomial and k is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the
|
||||
simplifier.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2012-02-19
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic propagate-ineqs
|
||||
|
||||
### Short Description
|
||||
|
||||
Propagate ineqs/bounds, remove subsumed inequalities
|
||||
|
||||
### Long Description
|
||||
|
||||
This tactic performs the following tasks:
|
||||
|
||||
- Propagate bounds using the bound_propagator.
|
||||
- Eliminate subsumed inequalities.
|
||||
- For example:
|
||||
`x - y >= 3` can be replaced with true if we know that `x >= 3` and `y <= 0`
|
||||
|
||||
- Convert inequalities of the form `p <= k` and `p >= k` into `p = k`,
|
||||
where `p` is a polynomial and `k` is a constant.
|
||||
|
||||
This strategy assumes the input is in arith LHS mode.
|
||||
This can be achieved by using option :arith-lhs true in the simplifier.
|
||||
|
||||
### Example
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(declare-const u Int)
|
||||
(declare-const v Int)
|
||||
(declare-const w Int)
|
||||
(assert (>= x 3))
|
||||
(assert (<= y 0))
|
||||
(assert (>= (- x y) 3))
|
||||
(assert (>= (* u v w) 2))
|
||||
(assert (<= (* v u w) 2))
|
||||
(apply (and-then simplify propagate-ineqs))
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/bound_simplifier.h"
|
||||
|
||||
inline tactic* mk_propagate_ineqs_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bound_simplifier, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "mk_propagate_ineqs_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("propagate-ineqs", "propagate ineqs/bounds, remove subsumed inequalities.", "alloc(bound_simplifier, m, p, s)")
|
||||
*/
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ Revision History:
|
|||
#include "tactic/core/nnf_tactic.h"
|
||||
#include "tactic/core/simplify_tactic.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
|
@ -911,11 +911,11 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
r.insert("complete", CPK_BOOL,
|
||||
"(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root");
|
||||
"add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root", "true");
|
||||
r.insert("elim_root_objects", CPK_BOOL,
|
||||
"(default: true) eliminate root objects.");
|
||||
"eliminate root objects.", "true");
|
||||
r.insert("elim_inverses", CPK_BOOL,
|
||||
"(default: true) eliminate inverse trigonometric functions (asin, acos, atan).");
|
||||
"eliminate inverse trigonometric functions (asin, acos, atan).", "true");
|
||||
th_rewriter::get_param_descrs(r);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,28 @@ Author:
|
|||
|
||||
Leonardo de Moura (leonardo) 2011-12-30.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic purify-arith
|
||||
|
||||
### Short Description
|
||||
|
||||
Eliminate unnecessary operators: -, /, div, mod, rem, is-int, to-int, ^, root-objects.
|
||||
These operators can be replaced by introcing fresh variables and using multiplication and addition.
|
||||
|
||||
### Example
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-const z Int)
|
||||
(declare-const u Int)
|
||||
(declare-const v Int)
|
||||
(declare-const w Int)
|
||||
(assert (= (div x 3) y))
|
||||
(assert (= (mod z 4) u))
|
||||
(assert (> (mod v w) u))
|
||||
(apply purify-arith)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -57,7 +57,7 @@ Revision History:
|
|||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "util/dec_ref_util.h"
|
||||
|
@ -407,7 +407,7 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause.");
|
||||
r.insert("recover_01_max_bits", CPK_UINT, "maximum number of bits to consider in a clause.", "10");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
|
|
|
@ -5,29 +5,56 @@ Module Name:
|
|||
|
||||
recover_01_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Recover 01 variables
|
||||
|
||||
Search for clauses of the form
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
|
||||
Then, replaces
|
||||
x with k1*y1 + k2*y2
|
||||
p with y1=1
|
||||
q with y2=1
|
||||
where y1 and y2 are fresh 01 variables
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-17.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic recover-01
|
||||
|
||||
### Short Description
|
||||
|
||||
Recover 01 variables from propositional constants.
|
||||
|
||||
### Long Description
|
||||
|
||||
Search for clauses of the form
|
||||
|
||||
```
|
||||
p or q or x = 0
|
||||
~p or q or x = k1
|
||||
p or ~q or x = k2
|
||||
~p or ~q or x = k1+k2
|
||||
```
|
||||
|
||||
Then, replaces
|
||||
|
||||
|
||||
* `x` with `k1*y1 + k2*y2`
|
||||
* `p` with `y1 = 1`
|
||||
* `q` with `y2 = 1`
|
||||
|
||||
where `y1` and `y2` are fresh 01 variables.
|
||||
|
||||
The clauses are also removed.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const p Bool)
|
||||
(declare-const q Bool)
|
||||
(declare-const x Int)
|
||||
(assert (or p q (= x 0)))
|
||||
(assert (or (not p) q (= x 3)))
|
||||
(assert (or p (not q) (= x 6)))
|
||||
(assert (or (not p) (not q) (= x 9)))
|
||||
(apply recover-01)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs, does not support cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -10,7 +10,6 @@ z3_add_component(bv_tactics
|
|||
bv_size_reduction_tactic.cpp
|
||||
dt2bv_tactic.cpp
|
||||
elim_small_bv_tactic.cpp
|
||||
max_bv_sharing_tactic.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
bit_blaster
|
||||
core_tactics
|
||||
|
@ -21,6 +20,7 @@ z3_add_component(bv_tactics
|
|||
bv_bound_chk_tactic.h
|
||||
bv_bounds_tactic.h
|
||||
bv_size_reduction_tactic.h
|
||||
bv_slice_tactic.h
|
||||
bvarray2uf_tactic.h
|
||||
dt2bv_tactic.h
|
||||
elim_small_bv_tactic.h
|
||||
|
|
|
@ -18,7 +18,7 @@ Notes:
|
|||
--*/
|
||||
#include "model/model.h"
|
||||
#include "model/model_pp.h"
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
@ -148,7 +148,8 @@ struct bit_blaster_model_converter : public model_converter {
|
|||
for (expr* bit : *to_app(bs)) {
|
||||
func_decl * bit_decl = to_app(bit)->get_decl();
|
||||
expr * bit_val = old_model->get_const_interp(bit_decl);
|
||||
SASSERT(bit_val);
|
||||
if (!bit_val)
|
||||
bit_val = m().mk_false();
|
||||
vals.push_back(bit_val);
|
||||
}
|
||||
if (TO_BOOL)
|
||||
|
|
|
@ -18,7 +18,7 @@ Notes:
|
|||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "tactic/model_converter.h"
|
||||
#include "ast/converters/model_converter.h"
|
||||
|
||||
model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits, ptr_vector<func_decl> const& newbits);
|
||||
model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map<func_decl, expr*> const & const2bits, ptr_vector<func_decl> const& newbits);
|
||||
|
|
|
@ -129,10 +129,10 @@ public:
|
|||
void collect_param_descrs(param_descrs & r) override {
|
||||
insert_max_memory(r);
|
||||
insert_max_steps(r);
|
||||
r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders).");
|
||||
r.insert("blast_add", CPK_BOOL, "(default: true) bit-blast adders.");
|
||||
r.insert("blast_quant", CPK_BOOL, "(default: false) bit-blast quantified variables.");
|
||||
r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.");
|
||||
r.insert("blast_mul", CPK_BOOL, "bit-blast multipliers (and dividers, remainders).", "true");
|
||||
r.insert("blast_add", CPK_BOOL, "bit-blast adders.", "true");
|
||||
r.insert("blast_quant", CPK_BOOL, "bit-blast quantified variables.", "false");
|
||||
r.insert("blast_full", CPK_BOOL, "bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms.", "false");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
Module Name:
|
||||
|
||||
bit_blaster_tactic.h
|
||||
|
||||
Abstract:
|
||||
Author:
|
||||
|
||||
Apply bit-blasting to a given goal.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-10-25
|
||||
|
||||
Notes:
|
||||
Leonardo (leonardo) 2011-10-25
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic bit-blast
|
||||
|
||||
### Short Description
|
||||
|
||||
Apply bit-blasting to a given goal.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x (_ BitVec 8))
|
||||
(declare-const y (_ BitVec 8))
|
||||
(assert (bvule x y))
|
||||
(apply bit-blast)
|
||||
```
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
|
|
|
@ -5,21 +5,37 @@ Module Name:
|
|||
|
||||
bv1_blaster_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
|
||||
This rewriter only supports concat and extract operators.
|
||||
This transformation is useful for handling benchmarks that contain
|
||||
many BV equalities.
|
||||
|
||||
Remark: other operators can be mapped into concat/extract by using
|
||||
the simplifiers.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-10-25
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic bv1-blast
|
||||
|
||||
### Short Description
|
||||
|
||||
Reduce bit-vector expressions into bit-vectors of size 1 (notes: only equality, extract and concat are supported).
|
||||
|
||||
### Long Description
|
||||
|
||||
Rewriter for "blasting" bit-vectors of size n into bit-vectors of size 1.
|
||||
This rewriter only supports concat and extract operators.
|
||||
This transformation is useful for handling benchmarks that contain
|
||||
many BV equalities.
|
||||
|
||||
_Remark_: other operators can be mapped into concat/extract by using
|
||||
the simplifiers.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x (_ BitVec 8))
|
||||
(declare-const y (_ BitVec 4))
|
||||
(declare-const z (_ BitVec 4))
|
||||
(assert (= (concat y z) x))
|
||||
(apply bv1-blast)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -48,7 +48,6 @@ struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg {
|
|||
m_bv_ineq_consistency_test_max = p.bv_ineq_consistency_test_max();
|
||||
m_max_memory = p.max_memory();
|
||||
m_max_steps = p.max_steps();
|
||||
|
||||
}
|
||||
|
||||
ast_manager & m() const { return m_m; }
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
Module Name:
|
||||
|
||||
bv_bound_chk_tactic.h
|
||||
bv_bound_chk_tactic.h
|
||||
|
||||
Abstract:
|
||||
Author:
|
||||
|
||||
Mikolas Janota
|
||||
|
||||
Author:
|
||||
Tactic Documentation
|
||||
|
||||
Mikolas Janota
|
||||
## Tactic bv_bound_chk
|
||||
|
||||
### Short Description
|
||||
|
||||
Attempts to detect inconsistencies of bounds on bv expressions.
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs, does not support cores
|
||||
|
||||
Revision History:
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -18,232 +18,25 @@ Author:
|
|||
|
||||
--*/
|
||||
|
||||
#include "tactic/bv/bv_bounds_tactic.h"
|
||||
#include "tactic/core/ctx_simplify_tactic.h"
|
||||
#include "tactic/core/dom_simplify_tactic.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/bv_bounds_base.h"
|
||||
#include "ast/simplifiers/dominator_simplifier.h"
|
||||
#include "ast/simplifiers/bv_bounds_simplifier.h"
|
||||
#include "tactic/bv/bv_bounds_tactic.h"
|
||||
#include "tactic/core/ctx_simplify_tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include <climits>
|
||||
|
||||
static uint64_t uMaxInt(unsigned sz) {
|
||||
SASSERT(sz <= 64);
|
||||
return ULLONG_MAX >> (64u - sz);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct interval {
|
||||
// l < h: [l, h]
|
||||
// l > h: [0, h] U [l, UMAX_INT]
|
||||
uint64_t l, h;
|
||||
unsigned sz;
|
||||
bool tight;
|
||||
|
||||
interval() {}
|
||||
interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) {
|
||||
// canonicalize full set
|
||||
if (is_wrapped() && l == h + 1) {
|
||||
this->l = 0;
|
||||
this->h = uMaxInt(sz);
|
||||
}
|
||||
SASSERT(invariant());
|
||||
}
|
||||
|
||||
bool invariant() const {
|
||||
return l <= uMaxInt(sz) && h <= uMaxInt(sz) &&
|
||||
(!is_wrapped() || l != h+1);
|
||||
}
|
||||
|
||||
bool is_full() const { return l == 0 && h == uMaxInt(sz); }
|
||||
bool is_wrapped() const { return l > h; }
|
||||
bool is_singleton() const { return l == h; }
|
||||
|
||||
bool operator==(const interval& b) const {
|
||||
SASSERT(sz == b.sz);
|
||||
return l == b.l && h == b.h && tight == b.tight;
|
||||
}
|
||||
bool operator!=(const interval& b) const { return !(*this == b); }
|
||||
|
||||
bool implies(const interval& b) const {
|
||||
if (b.is_full())
|
||||
return true;
|
||||
if (is_full())
|
||||
return false;
|
||||
|
||||
if (is_wrapped()) {
|
||||
// l >= b.l >= b.h >= h
|
||||
return b.is_wrapped() && h <= b.h && l >= b.l;
|
||||
}
|
||||
else if (b.is_wrapped()) {
|
||||
// b.l > b.h >= h >= l
|
||||
// h >= l >= b.l > b.h
|
||||
return h <= b.h || l >= b.l;
|
||||
}
|
||||
else {
|
||||
//
|
||||
return l >= b.l && h <= b.h;
|
||||
}
|
||||
}
|
||||
|
||||
/// return false if intersection is unsat
|
||||
bool intersect(const interval& b, interval& result) const {
|
||||
if (is_full() || *this == b) {
|
||||
result = b;
|
||||
return true;
|
||||
}
|
||||
if (b.is_full()) {
|
||||
result = *this;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_wrapped()) {
|
||||
if (b.is_wrapped()) {
|
||||
if (h >= b.l) {
|
||||
result = b;
|
||||
} else if (b.h >= l) {
|
||||
result = *this;
|
||||
} else {
|
||||
result = interval(std::max(l, b.l), std::min(h, b.h), sz);
|
||||
}
|
||||
} else {
|
||||
return b.intersect(*this, result);
|
||||
}
|
||||
}
|
||||
else if (b.is_wrapped()) {
|
||||
// ... b.h ... l ... h ... b.l ..
|
||||
if (h < b.l && l > b.h) {
|
||||
return false;
|
||||
}
|
||||
// ... l ... b.l ... h ...
|
||||
if (h >= b.l && l <= b.h) {
|
||||
result = b;
|
||||
} else if (h >= b.l) {
|
||||
result = interval(b.l, h, sz);
|
||||
} else {
|
||||
// ... l .. b.h .. h .. b.l ...
|
||||
SASSERT(l <= b.h);
|
||||
result = interval(l, std::min(h, b.h), sz);
|
||||
}
|
||||
} else {
|
||||
if (l > b.h || h < b.l)
|
||||
return false;
|
||||
|
||||
// 0 .. l.. l' ... h ... h'
|
||||
result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// return false if negation is empty
|
||||
bool negate(interval& result) const {
|
||||
if (!tight) {
|
||||
result = interval(0, uMaxInt(sz), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_full())
|
||||
return false;
|
||||
if (l == 0) {
|
||||
result = interval(h + 1, uMaxInt(sz), sz);
|
||||
} else if (uMaxInt(sz) == h) {
|
||||
result = interval(0, l - 1, sz);
|
||||
} else {
|
||||
result = interval(h + 1, l - 1, sz);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _TRACE
|
||||
std::ostream& operator<<(std::ostream& o, const interval& I) {
|
||||
o << "[" << I.l << ", " << I.h << "]";
|
||||
return o;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct undo_bound {
|
||||
expr* e { nullptr };
|
||||
interval b;
|
||||
bool fresh { false };
|
||||
undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {}
|
||||
};
|
||||
|
||||
class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier {
|
||||
typedef obj_map<expr, interval> map;
|
||||
typedef obj_map<expr, bool> expr_set;
|
||||
typedef obj_map<expr, unsigned> expr_cnt;
|
||||
|
||||
ast_manager& m;
|
||||
class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier, public bv::bv_bounds_base {
|
||||
params_ref m_params;
|
||||
bool m_propagate_eq;
|
||||
bv_util m_bv;
|
||||
vector<undo_bound> m_scopes;
|
||||
map m_bound;
|
||||
svector<expr_set*> m_expr_vars;
|
||||
svector<expr_cnt*> m_bound_exprs;
|
||||
|
||||
bool is_number(expr *e, uint64_t& n, unsigned& sz) const {
|
||||
rational r;
|
||||
if (m_bv.is_numeral(e, r, sz) && sz <= 64) {
|
||||
n = r.get_uint64();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_bound(expr *e, expr*& v, interval& b) const {
|
||||
uint64_t n;
|
||||
expr *lhs = nullptr, *rhs = nullptr;
|
||||
unsigned sz;
|
||||
|
||||
if (m_bv.is_bv_ule(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) { // C ule x <=> x uge C
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, uMaxInt(sz), sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) { // x ule C
|
||||
b = interval(0, n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_bv.is_bv_sle(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) { // C sle x <=> x sge C
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, (1ull << (sz-1)) - 1, sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) { // x sle C
|
||||
b = interval(1ull << (sz-1), n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
} else if (m.is_eq(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) {
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, n, sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) {
|
||||
b = interval(n, n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) {
|
||||
bv_bounds_simplifier(ast_manager& m, params_ref const& p) : bv::bv_bounds_base(m), m_params(p) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
|
@ -252,160 +45,17 @@ namespace {
|
|||
}
|
||||
|
||||
static void get_param_descrs(param_descrs& r) {
|
||||
r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities");
|
||||
r.insert("propagate-eq", CPK_BOOL, "propagate equalities from inequalities", "false");
|
||||
}
|
||||
|
||||
~bv_bounds_simplifier() override {
|
||||
for (auto* v : m_expr_vars) dealloc(v);
|
||||
for (auto* b : m_bound_exprs) dealloc(b);
|
||||
}
|
||||
~bv_bounds_simplifier() override {}
|
||||
|
||||
bool assert_expr(expr * t, bool sign) override {
|
||||
TRACE("bv", tout << expr_ref(t, m) << "\n";);
|
||||
while (m.is_not(t, t)) {
|
||||
sign = !sign;
|
||||
}
|
||||
|
||||
interval b;
|
||||
expr* t1;
|
||||
if (is_bound(t, t1, b)) {
|
||||
SASSERT(!m_bv.is_numeral(t1));
|
||||
if (sign) {
|
||||
if (!b.negate(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";);
|
||||
map::obj_map_entry* e = m_bound.find_core(t1);
|
||||
if (e) {
|
||||
interval& old = e->get_data().m_value;
|
||||
interval intr;
|
||||
if (!old.intersect(b, intr))
|
||||
return false;
|
||||
if (old == intr)
|
||||
return true;
|
||||
m_scopes.insert(undo_bound(t1, old, false));
|
||||
old = intr;
|
||||
} else {
|
||||
m_bound.insert(t1, b);
|
||||
m_scopes.insert(undo_bound(t1, interval(), true));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return assert_expr_core(t, sign);
|
||||
}
|
||||
|
||||
bool simplify(expr* t, expr_ref& result) override {
|
||||
expr* t1;
|
||||
interval b;
|
||||
|
||||
if (m_bound.find(t, b) && b.is_singleton()) {
|
||||
result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m.is_bool(t))
|
||||
return false;
|
||||
|
||||
bool sign = false;
|
||||
while (m.is_not(t, t)) {
|
||||
sign = !sign;
|
||||
}
|
||||
|
||||
if (!is_bound(t, t1, b))
|
||||
return false;
|
||||
|
||||
if (sign && b.tight) {
|
||||
sign = false;
|
||||
if (!b.negate(b)) {
|
||||
result = m.mk_false();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
interval ctx, intr;
|
||||
result = nullptr;
|
||||
|
||||
if (b.is_full() && b.tight) {
|
||||
result = m.mk_true();
|
||||
} else if (m_bound.find(t1, ctx)) {
|
||||
if (ctx.implies(b)) {
|
||||
result = m.mk_true();
|
||||
}
|
||||
else if (!b.intersect(ctx, intr)) {
|
||||
result = m.mk_false();
|
||||
}
|
||||
else if (m_propagate_eq && intr.is_singleton()) {
|
||||
result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), t1->get_sort()));
|
||||
}
|
||||
}
|
||||
|
||||
CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";);
|
||||
if (sign && result != 0)
|
||||
result = m.mk_not(result);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
// check if t contains v
|
||||
ptr_vector<expr> todo;
|
||||
bool contains(expr* t, expr* v) {
|
||||
ast_fast_mark1 mark;
|
||||
todo.push_back(t);
|
||||
while (!todo.empty()) {
|
||||
t = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark.is_marked(t)) {
|
||||
continue;
|
||||
}
|
||||
if (t == v) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
mark.mark(t);
|
||||
|
||||
if (!is_app(t)) {
|
||||
continue;
|
||||
}
|
||||
app* a = to_app(t);
|
||||
todo.append(a->get_num_args(), a->get_args());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool contains_bound(expr* t) {
|
||||
ast_fast_mark1 mark1;
|
||||
ast_fast_mark2 mark2;
|
||||
|
||||
todo.push_back(t);
|
||||
while (!todo.empty()) {
|
||||
t = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark1.is_marked(t)) {
|
||||
continue;
|
||||
}
|
||||
mark1.mark(t);
|
||||
|
||||
if (!is_app(t)) {
|
||||
continue;
|
||||
}
|
||||
interval b;
|
||||
expr* e;
|
||||
if (is_bound(t, e, b)) {
|
||||
if (mark2.is_marked(e)) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
mark2.mark(e);
|
||||
if (m_bound.contains(e)) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
app* a = to_app(t);
|
||||
todo.append(a->get_num_args(), a->get_args());
|
||||
}
|
||||
return false;
|
||||
return simplify_core(t, result);
|
||||
}
|
||||
|
||||
bool may_simplify(expr* t) override {
|
||||
|
@ -414,43 +64,21 @@ namespace {
|
|||
|
||||
while (m.is_not(t, t));
|
||||
|
||||
for (auto & v : m_bound) {
|
||||
if (contains(t, v.m_key)) return true;
|
||||
}
|
||||
for (auto & v : m_bound)
|
||||
if (contains(t, v.m_key))
|
||||
return true;
|
||||
|
||||
expr* t1;
|
||||
interval b;
|
||||
bv::interval b;
|
||||
// skip common case: single bound constraint without any context for simplification
|
||||
if (is_bound(t, t1, b)) {
|
||||
if (is_bound(t, t1, b))
|
||||
return b.is_full() || m_bound.contains(t1);
|
||||
}
|
||||
|
||||
if (contains_bound(t)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return contains_bound(t);
|
||||
}
|
||||
|
||||
void pop(unsigned num_scopes) override {
|
||||
TRACE("bv", tout << "pop: " << num_scopes << "\n";);
|
||||
if (m_scopes.empty())
|
||||
return;
|
||||
unsigned target = m_scopes.size() - num_scopes;
|
||||
if (target == 0) {
|
||||
m_bound.reset();
|
||||
m_scopes.reset();
|
||||
return;
|
||||
}
|
||||
for (unsigned i = m_scopes.size()-1; i >= target; --i) {
|
||||
undo_bound& undo = m_scopes[i];
|
||||
SASSERT(m_bound.contains(undo.e));
|
||||
if (undo.fresh) {
|
||||
m_bound.erase(undo.e);
|
||||
} else {
|
||||
m_bound.insert(undo.e, undo.b);
|
||||
}
|
||||
}
|
||||
m_scopes.shrink(target);
|
||||
pop_core(num_scopes);
|
||||
}
|
||||
|
||||
simplifier * translate(ast_manager & m) override {
|
||||
|
@ -462,290 +90,12 @@ namespace {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
class dom_bv_bounds_simplifier : public dom_simplifier {
|
||||
typedef obj_map<expr, interval> map;
|
||||
typedef obj_map<expr, bool> expr_set;
|
||||
typedef obj_map<expr, unsigned> expr_cnt;
|
||||
|
||||
ast_manager& m;
|
||||
params_ref m_params;
|
||||
bool m_propagate_eq;
|
||||
bv_util m_bv;
|
||||
vector<undo_bound> m_scopes;
|
||||
map m_bound;
|
||||
svector<expr_set*> m_expr_vars;
|
||||
svector<expr_cnt*> m_bound_exprs;
|
||||
|
||||
bool is_number(expr *e, uint64_t& n, unsigned& sz) const {
|
||||
rational r;
|
||||
if (m_bv.is_numeral(e, r, sz) && sz <= 64) {
|
||||
n = r.get_uint64();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_bound(expr *e, expr*& v, interval& b) const {
|
||||
uint64_t n;
|
||||
expr *lhs = nullptr, *rhs = nullptr;
|
||||
unsigned sz = 0;
|
||||
|
||||
if (m_bv.is_bv_ule(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) { // C ule x <=> x uge C
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, uMaxInt(sz), sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) { // x ule C
|
||||
b = interval(0, n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (m_bv.is_bv_sle(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) { // C sle x <=> x sge C
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, (1ull << (sz-1)) - 1, sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) { // x sle C
|
||||
b = interval(1ull << (sz-1), n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
} else if (m.is_eq(e, lhs, rhs)) {
|
||||
if (is_number(lhs, n, sz)) {
|
||||
if (m_bv.is_numeral(rhs))
|
||||
return false;
|
||||
b = interval(n, n, sz, true);
|
||||
v = rhs;
|
||||
return true;
|
||||
}
|
||||
if (is_number(rhs, n, sz)) {
|
||||
b = interval(n, n, sz, true);
|
||||
v = lhs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
dom_bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
virtual void updt_params(params_ref const & p) {
|
||||
m_propagate_eq = p.get_bool("propagate_eq", false);
|
||||
}
|
||||
|
||||
static void get_param_descrs(param_descrs& r) {
|
||||
r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities");
|
||||
}
|
||||
|
||||
~dom_bv_bounds_simplifier() override {
|
||||
for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) {
|
||||
dealloc(m_expr_vars[i]);
|
||||
}
|
||||
for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) {
|
||||
dealloc(m_bound_exprs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool assert_expr(expr * t, bool sign) override {
|
||||
while (m.is_not(t, t)) {
|
||||
sign = !sign;
|
||||
}
|
||||
|
||||
interval b;
|
||||
expr* t1;
|
||||
if (is_bound(t, t1, b)) {
|
||||
SASSERT(!m_bv.is_numeral(t1));
|
||||
if (sign)
|
||||
VERIFY(b.negate(b));
|
||||
|
||||
TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";);
|
||||
map::obj_map_entry* e = m_bound.find_core(t1);
|
||||
if (e) {
|
||||
interval& old = e->get_data().m_value;
|
||||
interval intr;
|
||||
if (!old.intersect(b, intr))
|
||||
return false;
|
||||
if (old == intr)
|
||||
return true;
|
||||
m_scopes.push_back(undo_bound(t1, old, false));
|
||||
old = intr;
|
||||
} else {
|
||||
m_bound.insert(t1, b);
|
||||
m_scopes.push_back(undo_bound(t1, interval(), true));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void operator()(expr_ref& r) override {
|
||||
expr* t1, * t = r;
|
||||
interval b;
|
||||
|
||||
if (m_bound.find(t, b) && b.is_singleton()) {
|
||||
r = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m.is_bool(t))
|
||||
return;
|
||||
|
||||
bool sign = false;
|
||||
while (m.is_not(t, t)) {
|
||||
sign = !sign;
|
||||
}
|
||||
|
||||
if (!is_bound(t, t1, b))
|
||||
return;
|
||||
|
||||
if (sign && b.tight) {
|
||||
sign = false;
|
||||
if (!b.negate(b)) {
|
||||
r = m.mk_false();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
interval ctx, intr;
|
||||
bool was_updated = true;
|
||||
if (b.is_full() && b.tight) {
|
||||
r = m.mk_true();
|
||||
}
|
||||
else if (m_bound.find(t1, ctx)) {
|
||||
if (ctx.implies(b)) {
|
||||
r = m.mk_true();
|
||||
}
|
||||
else if (!b.intersect(ctx, intr)) {
|
||||
r = m.mk_false();
|
||||
}
|
||||
else if (m_propagate_eq && intr.is_singleton()) {
|
||||
r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()),
|
||||
t1->get_sort()));
|
||||
}
|
||||
else {
|
||||
was_updated = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
was_updated = false;
|
||||
}
|
||||
|
||||
TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";);
|
||||
if (sign && was_updated)
|
||||
r = m.mk_not(r);
|
||||
}
|
||||
|
||||
// check if t contains v
|
||||
ptr_vector<expr> todo;
|
||||
bool contains(expr* t, expr* v) {
|
||||
ast_fast_mark1 mark;
|
||||
todo.push_back(t);
|
||||
while (!todo.empty()) {
|
||||
t = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark.is_marked(t)) {
|
||||
continue;
|
||||
}
|
||||
if (t == v) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
mark.mark(t);
|
||||
|
||||
if (!is_app(t)) {
|
||||
continue;
|
||||
}
|
||||
app* a = to_app(t);
|
||||
todo.append(a->get_num_args(), a->get_args());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool contains_bound(expr* t) {
|
||||
ast_fast_mark1 mark1;
|
||||
ast_fast_mark2 mark2;
|
||||
|
||||
todo.push_back(t);
|
||||
while (!todo.empty()) {
|
||||
t = todo.back();
|
||||
todo.pop_back();
|
||||
if (mark1.is_marked(t)) {
|
||||
continue;
|
||||
}
|
||||
mark1.mark(t);
|
||||
|
||||
if (!is_app(t)) {
|
||||
continue;
|
||||
}
|
||||
interval b;
|
||||
expr* e;
|
||||
if (is_bound(t, e, b)) {
|
||||
if (mark2.is_marked(e)) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
mark2.mark(e);
|
||||
if (m_bound.contains(e)) {
|
||||
todo.reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
app* a = to_app(t);
|
||||
todo.append(a->get_num_args(), a->get_args());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void pop(unsigned num_scopes) override {
|
||||
TRACE("bv", tout << "pop: " << num_scopes << "\n";);
|
||||
if (m_scopes.empty())
|
||||
return;
|
||||
unsigned target = m_scopes.size() - num_scopes;
|
||||
if (target == 0) {
|
||||
m_bound.reset();
|
||||
m_scopes.reset();
|
||||
return;
|
||||
}
|
||||
for (unsigned i = m_scopes.size()-1; i >= target; --i) {
|
||||
undo_bound& undo = m_scopes[i];
|
||||
SASSERT(m_bound.contains(undo.e));
|
||||
if (undo.fresh) {
|
||||
m_bound.erase(undo.e);
|
||||
} else {
|
||||
m_bound.insert(undo.e, undo.b);
|
||||
}
|
||||
}
|
||||
m_scopes.shrink(target);
|
||||
}
|
||||
|
||||
dom_simplifier * translate(ast_manager & m) override {
|
||||
return alloc(dom_bv_bounds_simplifier, m, m_params);
|
||||
}
|
||||
|
||||
unsigned scope_level() const override {
|
||||
return m_scopes.size();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m, p), p));
|
||||
}
|
||||
|
||||
tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(dom_simplify_tactic, m, alloc(dom_bv_bounds_simplifier, m, p), p));
|
||||
tactic* mk_dom_bv_bounds_tactic(ast_manager& m, params_ref const& p) {
|
||||
return alloc(dependent_expr_state_tactic, m, p, mk_bv_bounds_simplifier);
|
||||
}
|
||||
|
|
|
@ -5,19 +5,39 @@ Module Name:
|
|||
|
||||
bv_bounds_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Contextual bounds simplification tactic.
|
||||
|
||||
Author:
|
||||
|
||||
Nuno Lopes (nlopes) 2016-2-12
|
||||
Nikolaj Bjorner (nbjorner)
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic propagate-bv-bounds
|
||||
|
||||
### Short Description
|
||||
|
||||
Contextual bounds simplification tactic.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x (_ BitVec 32))
|
||||
(declare-const y (_ BitVec 32))
|
||||
(declare-const z (_ BitVec 32))
|
||||
(assert (bvule (_ bv4 32) x))
|
||||
(assert (bvule x (_ bv24 32)))
|
||||
(assert (or (bvule x (_ bv100 32)) (bvule (_ bv32 32) x)))
|
||||
(apply propagate-bv-bounds)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* assumes that bit-vector inequalities have been simplified to use bvule/bvsle
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
#include "tactic/tactic.h"
|
||||
#include "ast/simplifiers/bv_bounds_simplifier.h"
|
||||
|
||||
tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
|
@ -26,8 +46,9 @@ tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_
|
|||
/*
|
||||
ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)")
|
||||
|
||||
ADD_SIMPLIFIER("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_simplifier(m, p, s)")
|
||||
|
||||
ADD_TACTIC("propagate-bv-bounds-new", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)")
|
||||
ADD_TACTIC("propagate-bv-bounds2", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)")
|
||||
|
||||
|
||||
*/
|
||||
|
|
|
@ -24,7 +24,7 @@ Notes:
|
|||
#include "tactic/tactical.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -7,12 +7,6 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
|
||||
Reduce the number of bits used to encode constants, by using signed bounds.
|
||||
Example: suppose x is a bit-vector of size 8, and we have
|
||||
signed bounds for x such that:
|
||||
-2 <= x <= 2
|
||||
Then, x can be replaced by ((sign-extend 5) k)
|
||||
where k is a fresh bit-vector constant of size 3.
|
||||
|
||||
Author:
|
||||
|
||||
|
@ -20,6 +14,41 @@ Author:
|
|||
|
||||
Notes:
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic reduce-bv-size
|
||||
|
||||
### Short Description
|
||||
|
||||
Rry to reduce bit-vector sizes using inequalities.
|
||||
|
||||
### Long Description
|
||||
|
||||
Reduce the number of bits used to encode constants, by using signed bounds.
|
||||
Example: suppose $x$ is a bit-vector of size 8, and we have
|
||||
signed bounds for $x$ such that:
|
||||
|
||||
```
|
||||
-2 <= x <= 2
|
||||
```
|
||||
|
||||
Then, $x$ can be replaced by `((sign-extend 5) k)`
|
||||
where `k` is a fresh bit-vector constant of size 3.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x (_ BitVec 32))
|
||||
(assert (bvsle (bvneg (_ bv2 32)) x))
|
||||
(assert (bvsle x (_ bv2 32)))
|
||||
(assert (= (bvmul x x) (_ bv9 32)))
|
||||
(apply (and-then simplify reduce-bv-size))
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs, nor unsat cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
|
|
66
src/tactic/bv/bv_slice_tactic.h
Normal file
66
src/tactic/bv/bv_slice_tactic.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bv_slice_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for simplifying with bit-vector slices
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
Tactic Documentation
|
||||
|
||||
## Tactic bv-slice
|
||||
|
||||
### Short Description
|
||||
|
||||
Slices bit-vectors into sub-ranges to allow simplifying sub-ranges.
|
||||
|
||||
### Long Description
|
||||
|
||||
It rewrites a state using bit-vector slices.
|
||||
Slices are extracted from bit-vector equality assertions.
|
||||
An equality assertion may equate a sub-range of a bit-vector
|
||||
with a constant. The tactic ensures that all occurrences of the
|
||||
subrange are replaced by the constants to allow additional
|
||||
simplification
|
||||
|
||||
### Example
|
||||
|
||||
```z3 ignore-errors
|
||||
(declare-const x (_ BitVec 32))
|
||||
(declare-const y (_ BitVec 32))
|
||||
(assert (= ((_ extract 31 16) x) (_ bv123 16)))
|
||||
(assert (= ((_ extract 15 0) x) ((_ extract 16 1) y)))
|
||||
(assert (= (bvadd x x) y))
|
||||
(apply bv-slice)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/bv_slice.h"
|
||||
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
inline tactic* mk_bv_slice_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(bv::slice, m, s); });
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
ADD_TACTIC("bv-slice", "simplify using bit-vector slices.", "mk_bv_slice_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("bv-slice", "simplify using bit-vector slices.", "alloc(bv::slice, m, s)")
|
||||
*/
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ Notes:
|
|||
#pragma once
|
||||
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class bvarray2uf_rewriter_cfg : public default_rewriter_cfg {
|
||||
ast_manager & m_manager;
|
||||
|
|
|
@ -20,7 +20,7 @@ Notes:
|
|||
#include "tactic/tactical.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
|
||||
#include "tactic/bv/bvarray2uf_tactic.h"
|
||||
|
|
|
@ -3,18 +3,30 @@ Copyright (c) 2015 Microsoft Corporation
|
|||
|
||||
Module Name:
|
||||
|
||||
bvarray2ufbvarray2uf_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic that rewrites bit-vector arrays into bit-vector
|
||||
(uninterpreted) functions.
|
||||
bvarray2uf_tactic.h
|
||||
|
||||
Author:
|
||||
|
||||
Christoph (cwinter) 2015-11-04
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic bvarray2uf
|
||||
|
||||
### Short Description
|
||||
|
||||
Tactic that rewrites bit-vector arrays into bit-vector
|
||||
(uninterpreted) functions.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const a (Array (_ BitVec 32) (_ BitVec 32)))
|
||||
(declare-const b (_ BitVec 32))
|
||||
(declare-const c (_ BitVec 32))
|
||||
(assert (= (select a b) c))
|
||||
(apply bvarray2uf)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -21,7 +21,7 @@ Revision History:
|
|||
|
||||
#include "tactic/bv/dt2bv_tactic.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
|
|
|
@ -5,15 +5,28 @@ Module Name:
|
|||
|
||||
dt2bv_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic that eliminates finite domain data-types.
|
||||
|
||||
Author:
|
||||
|
||||
nbjorner 2016-07-22
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation
|
||||
|
||||
## Tactic dt2bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Tactic that eliminates finite domain data-types.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-datatypes ((Color 0)) (((Red) (Blue) (Green) (DarkBlue) (MetallicBlack) (MetallicSilver) (Silver) (Black))))
|
||||
(declare-const x Color)
|
||||
(declare-const y Color)
|
||||
(assert (not (= x y)))
|
||||
(assert (not (= x Red)))
|
||||
(apply dt2bv)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -18,7 +18,7 @@ Revision History:
|
|||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/used_vars.h"
|
||||
#include "ast/well_sorted.h"
|
||||
|
|
|
@ -15,6 +15,22 @@ Author:
|
|||
|
||||
Revision History:
|
||||
|
||||
Tactic Documentation
|
||||
|
||||
## Tactic elim-small-bv
|
||||
|
||||
### Short Description
|
||||
|
||||
Eliminate small, quantified bit-vectors by expansion
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-fun p ((_ BitVec 2)) Bool)
|
||||
(assert (forall ((x (_ BitVec 2))) (p x)))
|
||||
(apply elim-small-bv)
|
||||
```
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -1,310 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
max_bv_sharing_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Rewriter for "maximing" the number of shared terms.
|
||||
The idea is to rewrite AC terms to maximize sharing.
|
||||
This rewriter is particularly useful for reducing
|
||||
the number of Adders and Multipliers before "bit-blasting".
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-29.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "util/obj_pair_hashtable.h"
|
||||
#include "ast/ast_lt.h"
|
||||
|
||||
class max_bv_sharing_tactic : public tactic {
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
typedef std::pair<expr *, expr *> expr_pair;
|
||||
typedef obj_pair_hashtable<expr, expr> set;
|
||||
bv_util m_util;
|
||||
set m_add_apps;
|
||||
set m_mul_apps;
|
||||
set m_xor_apps;
|
||||
set m_or_apps;
|
||||
unsigned long long m_max_memory;
|
||||
unsigned m_max_steps;
|
||||
unsigned m_max_args;
|
||||
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
|
||||
rw_cfg(ast_manager & m, params_ref const & p):
|
||||
m_util(m) {
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
m_add_apps.finalize();
|
||||
m_mul_apps.finalize();
|
||||
m_or_apps.finalize();
|
||||
m_xor_apps.finalize();
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX));
|
||||
m_max_steps = p.get_uint("max_steps", UINT_MAX);
|
||||
m_max_args = p.get_uint("max_args", 128);
|
||||
}
|
||||
|
||||
bool max_steps_exceeded(unsigned num_steps) const {
|
||||
if (memory::get_allocation_size() > m_max_memory)
|
||||
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
|
||||
return num_steps > m_max_steps;
|
||||
}
|
||||
|
||||
set & f2set(func_decl * f) {
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_BADD: return m_add_apps;
|
||||
case OP_BMUL: return m_mul_apps;
|
||||
case OP_BXOR: return m_xor_apps;
|
||||
case OP_BOR: return m_or_apps;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return m_or_apps; // avoid compilation error
|
||||
}
|
||||
}
|
||||
|
||||
expr * reuse(set & s, func_decl * f, expr * arg1, expr * arg2) {
|
||||
if (s.contains(expr_pair(arg1, arg2)))
|
||||
return m().mk_app(f, arg1, arg2);
|
||||
if (s.contains(expr_pair(arg2, arg1)))
|
||||
return m().mk_app(f, arg2, arg1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct ref_count_lt {
|
||||
bool operator()(expr * t1, expr * t2) const {
|
||||
if (t1->get_ref_count() < t2->get_ref_count())
|
||||
return true;
|
||||
return (t1->get_ref_count() == t2->get_ref_count()) && lt(t1, t2);
|
||||
}
|
||||
};
|
||||
|
||||
br_status reduce_ac_app(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
set & s = f2set(f);
|
||||
|
||||
if (num_args == 2) {
|
||||
if (!m_util.is_numeral(args[0]) && !m_util.is_numeral(args[1]))
|
||||
s.insert(expr_pair(args[0], args[1]));
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
ptr_buffer<expr, 128> _args;
|
||||
bool first = false;
|
||||
expr * num = nullptr;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = args[i];
|
||||
if (num == nullptr && m_util.is_numeral(arg)) {
|
||||
if (i == 0) first = true;
|
||||
num = arg;
|
||||
}
|
||||
else {
|
||||
_args.push_back(arg);
|
||||
}
|
||||
}
|
||||
num_args = _args.size();
|
||||
|
||||
|
||||
// std::sort(_args.begin(), _args.end(), ref_count_lt());
|
||||
// std::sort(_args.begin(), _args.end(), ast_to_lt());
|
||||
|
||||
try_to_reuse:
|
||||
if (num_args > 1 && num_args < m_max_args) {
|
||||
for (unsigned i = 0; i < num_args - 1; i++) {
|
||||
for (unsigned j = i + 1; j < num_args; j++) {
|
||||
expr * r = reuse(s, f, _args[i], _args[j]);
|
||||
if (r != nullptr) {
|
||||
TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";);
|
||||
_args[i] = r;
|
||||
SASSERT(num_args > 1);
|
||||
for (unsigned w = j; w < num_args - 1; w++) {
|
||||
_args[w] = _args[w+1];
|
||||
}
|
||||
num_args--;
|
||||
goto try_to_reuse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// some benchmarks are more efficiently solved using a tree-like structure (better sharing)
|
||||
// other benchmarks are more efficiently solved using a chain-like structure (better propagation for arguments "closer to the output").
|
||||
//
|
||||
// One possible solution is to do a global analysis that finds a good order that increases sharing without affecting
|
||||
// propagation.
|
||||
//
|
||||
// Another cheap trick is to create an option, and try both for a small amount of time.
|
||||
#if 0
|
||||
SASSERT(num_args > 0);
|
||||
if (num_args == 1) {
|
||||
result = _args[0];
|
||||
}
|
||||
else {
|
||||
// ref_count_lt is not a total order on expr's
|
||||
std::stable_sort(_args.c_ptr(), _args.c_ptr() + num_args, ref_count_lt());
|
||||
result = m().mk_app(f, _args[0], _args[1]);
|
||||
for (unsigned i = 2; i < num_args; i++) {
|
||||
result = m().mk_app(f, result.get(), _args[i]);
|
||||
}
|
||||
}
|
||||
if (num != 0) {
|
||||
if (first)
|
||||
result = m().mk_app(f, num, result);
|
||||
else
|
||||
result = m().mk_app(f, result, num);
|
||||
}
|
||||
return BR_DONE;
|
||||
#else
|
||||
// Create "tree-like circuit"
|
||||
while (true) {
|
||||
TRACE("bv_sharing_detail", tout << "tree-loop: num_args: " << num_args << "\n";);
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0; i < num_args; i += 2, j++) {
|
||||
if (i == num_args - 1) {
|
||||
_args[j] = _args[i];
|
||||
}
|
||||
else {
|
||||
s.insert(expr_pair(_args[i], _args[i+1]));
|
||||
_args[j] = m().mk_app(f, _args[i], _args[i+1]);
|
||||
}
|
||||
}
|
||||
num_args = j;
|
||||
if (num_args == 1) {
|
||||
if (num == nullptr) {
|
||||
result = _args[0];
|
||||
}
|
||||
else {
|
||||
if (first)
|
||||
result = m().mk_app(f, num, _args[0]);
|
||||
else
|
||||
result = m().mk_app(f, _args[0], num);
|
||||
}
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if (f->get_family_id() != m_util.get_family_id())
|
||||
return BR_FAILED;
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_BADD:
|
||||
case OP_BMUL:
|
||||
case OP_BOR:
|
||||
case OP_BXOR:
|
||||
result_pr = nullptr;
|
||||
return reduce_ac_app(f, num, args, result);
|
||||
default:
|
||||
return BR_FAILED;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<rw_cfg>(m, m.proofs_enabled(), m_cfg),
|
||||
m_cfg(m, p) {
|
||||
}
|
||||
};
|
||||
|
||||
struct imp {
|
||||
rw m_rw;
|
||||
unsigned m_num_steps;
|
||||
|
||||
imp(ast_manager & m, params_ref const & p):
|
||||
m_rw(m, p) {
|
||||
}
|
||||
|
||||
ast_manager & m() const { return m_rw.m(); }
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result) {
|
||||
tactic_report report("max-bv-sharing", *g);
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
|
||||
expr_ref new_curr(m());
|
||||
proof_ref new_pr(m());
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
if (g->inconsistent())
|
||||
break;
|
||||
expr * curr = g->form(idx);
|
||||
m_rw(curr, new_curr, new_pr);
|
||||
m_num_steps += m_rw.get_num_steps();
|
||||
|
||||
if (produce_proofs) {
|
||||
proof * pr = g->pr(idx);
|
||||
new_pr = m().mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g->update(idx, new_curr, new_pr, g->dep(idx));
|
||||
}
|
||||
m_rw.cfg().cleanup();
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
}
|
||||
};
|
||||
|
||||
imp * m_imp;
|
||||
params_ref m_params;
|
||||
public:
|
||||
max_bv_sharing_tactic(ast_manager & m, params_ref const & p):
|
||||
m_params(p) {
|
||||
m_imp = alloc(imp, m, p);
|
||||
}
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
return alloc(max_bv_sharing_tactic, m, m_params);
|
||||
}
|
||||
|
||||
~max_bv_sharing_tactic() override {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
||||
char const* name() const override { return "max_bv_sharing"; }
|
||||
|
||||
void updt_params(params_ref const & p) override {
|
||||
m_params.append(p);
|
||||
m_imp->m_rw.cfg().updt_params(m_params);
|
||||
}
|
||||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
insert_max_memory(r);
|
||||
insert_max_steps(r);
|
||||
r.insert("max_args", CPK_UINT,
|
||||
"(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic.");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & in,
|
||||
goal_ref_buffer & result) override {
|
||||
(*m_imp)(in, result);
|
||||
}
|
||||
|
||||
void cleanup() override {
|
||||
ast_manager & m = m_imp->m();
|
||||
params_ref p = std::move(m_params);
|
||||
m_imp->~imp();
|
||||
new (m_imp) imp(m, p);
|
||||
}
|
||||
};
|
||||
|
||||
tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(max_bv_sharing_tactic, m, p));
|
||||
}
|
||||
|
|
@ -7,25 +7,37 @@ Module Name:
|
|||
|
||||
Abstract:
|
||||
|
||||
Rewriter for "maximing" the number of shared terms.
|
||||
The idea is to rewrite AC terms to maximize sharing.
|
||||
This rewriter is particularly useful for reducing
|
||||
the number of Adders and Multipliers before "bit-blasting".
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-29.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation
|
||||
|
||||
## Tactic max-bv-sharing
|
||||
|
||||
### Short Description
|
||||
|
||||
Use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers
|
||||
|
||||
### Long Description
|
||||
|
||||
Rewriter for "maximing" the number of shared terms.
|
||||
The idea is to rewrite AC terms to maximize sharing.
|
||||
This rewriter is particularly useful for reducing
|
||||
the number of Adders and Multipliers before "bit-blasting".
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
#include "ast/simplifiers/max_bv_sharing.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
|
||||
inline tactic* mk_max_bv_sharing_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p, mk_max_bv_sharing);
|
||||
}
|
||||
|
||||
tactic * mk_max_bv_sharing_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
/*
|
||||
ADD_TACTIC("max-bv-sharing", "use heuristics to maximize the sharing of bit-vector expressions such as adders and multipliers.", "mk_max_bv_sharing_tactic(m, p)")
|
||||
*/
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
converter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Abstract class and templates for proof and model converters.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-11-14
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/vector.h"
|
||||
#include "util/ref.h"
|
||||
#include "ast/ast_translation.h"
|
||||
|
||||
class converter {
|
||||
unsigned m_ref_count;
|
||||
public:
|
||||
converter():m_ref_count(0) {}
|
||||
virtual ~converter() = default;
|
||||
|
||||
void inc_ref() { ++m_ref_count; }
|
||||
|
||||
void dec_ref() {
|
||||
--m_ref_count;
|
||||
if (m_ref_count == 0) {
|
||||
dealloc(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void cancel() {}
|
||||
|
||||
virtual void display(std::ostream & out) = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class concat_converter : public T {
|
||||
protected:
|
||||
ref<T> m_c1;
|
||||
ref<T> m_c2;
|
||||
|
||||
template<typename T2>
|
||||
T * translate_core(ast_translation & translator) {
|
||||
T * t1 = m_c1->translate(translator);
|
||||
T * t2 = m_c2->translate(translator);
|
||||
return alloc(T2, t1, t2);
|
||||
}
|
||||
|
||||
public:
|
||||
concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {}
|
||||
|
||||
void cancel() override {
|
||||
m_c2->cancel();
|
||||
m_c1->cancel();
|
||||
}
|
||||
|
||||
virtual char const * get_name() const = 0;
|
||||
|
||||
void display(std::ostream & out) override {
|
||||
m_c1->display(out);
|
||||
m_c2->display(out);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class concat_star_converter : public T {
|
||||
protected:
|
||||
ref<T> m_c1;
|
||||
ptr_vector<T> m_c2s;
|
||||
unsigned_vector m_szs;
|
||||
|
||||
template<typename T2>
|
||||
T * translate_core(ast_translation & translator) {
|
||||
T * t1 = m_c1 ? m_c1->translate(translator) : nullptr;
|
||||
ptr_buffer<T> t2s;
|
||||
for (T* c : m_c2s)
|
||||
t2s.push_back(c ? c->translate(translator) : nullptr);
|
||||
return alloc(T2, t1, m_c2s.size(), t2s.data(), m_szs.data());
|
||||
}
|
||||
|
||||
public:
|
||||
concat_star_converter(T * c1, unsigned num, T * const * c2s, unsigned * szs):
|
||||
m_c1(c1) {
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
T * c2 = c2s[i];
|
||||
if (c2)
|
||||
c2->inc_ref();
|
||||
m_c2s.push_back(c2);
|
||||
m_szs.push_back(szs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
~concat_star_converter() override {
|
||||
for (T* c : m_c2s)
|
||||
if (c) c->dec_ref();
|
||||
}
|
||||
|
||||
void cancel() override {
|
||||
if (m_c1)
|
||||
m_c1->cancel();
|
||||
for (T* c : m_c2s)
|
||||
if (c) c->cancel();
|
||||
}
|
||||
|
||||
virtual char const * get_name() const = 0;
|
||||
|
||||
void display(std::ostream & out) override {
|
||||
if (m_c1)
|
||||
m_c1->display(out);
|
||||
for (T* c : m_c2s)
|
||||
if (c) c->display(out);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -6,19 +6,16 @@ z3_add_component(core_tactics
|
|||
collect_statistics_tactic.cpp
|
||||
ctx_simplify_tactic.cpp
|
||||
der_tactic.cpp
|
||||
distribute_forall_tactic.cpp
|
||||
dom_simplify_tactic.cpp
|
||||
elim_term_ite_tactic.cpp
|
||||
elim_uncnstr_tactic.cpp
|
||||
euf_completion_tactic.cpp
|
||||
injectivity_tactic.cpp
|
||||
nnf_tactic.cpp
|
||||
occf_tactic.cpp
|
||||
pb_preprocess_tactic.cpp
|
||||
propagate_values_tactic.cpp
|
||||
reduce_args_tactic.cpp
|
||||
reduce_invertible_tactic.cpp
|
||||
simplify_tactic.cpp
|
||||
solve_eqs_tactic.cpp
|
||||
special_relations_tactic.cpp
|
||||
split_clause_tactic.cpp
|
||||
symmetry_reduce_tactic.cpp
|
||||
|
@ -33,18 +30,22 @@ z3_add_component(core_tactics
|
|||
cofactor_term_ite_tactic.h
|
||||
collect_statistics_tactic.h
|
||||
ctx_simplify_tactic.h
|
||||
demodulator_tactic.h
|
||||
der_tactic.h
|
||||
distribute_forall_tactic.h
|
||||
dom_simplify_tactic.h
|
||||
elim_term_ite_tactic.h
|
||||
elim_uncnstr_tactic.h
|
||||
elim_uncnstr2_tactic.h
|
||||
eliminate_predicates_tactic.h
|
||||
euf_completion_tactic.h
|
||||
injectivity_tactic.h
|
||||
nnf_tactic.h
|
||||
occf_tactic.h
|
||||
pb_preprocess_tactic.h
|
||||
propagate_values_tactic.h
|
||||
propagate_values2_tactic.h
|
||||
reduce_args_tactic.h
|
||||
reduce_invertible_tactic.h
|
||||
simplify_tactic.h
|
||||
solve_eqs_tactic.h
|
||||
special_relations_tactic.h
|
||||
|
|
|
@ -13,14 +13,12 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2013-11-4
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include "ast/normal_forms/defined_names.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/tactic_params.hpp"
|
||||
#include "params/tactic_params.hpp"
|
||||
|
||||
|
||||
|
||||
|
@ -181,7 +179,7 @@ public:
|
|||
void collect_param_descrs(param_descrs & r) override {
|
||||
insert_max_memory(r);
|
||||
insert_max_steps(r);
|
||||
r.insert("max_inflation", CPK_UINT, "(default: infinity) multiplicative factor of initial term size.");
|
||||
r.insert("max_inflation", CPK_UINT, "(default: infinity) multiplicative factor of initial term size.", "4294967295");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & in, goal_ref_buffer & result) override {
|
||||
|
|
|
@ -4,20 +4,42 @@ Copyright (c) 2013 Microsoft Corporation
|
|||
Module Name:
|
||||
|
||||
blast_term_ite_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Blast term if-then-else by hoisting them up.
|
||||
This is expensive but useful in some cases, such as
|
||||
for enforcing constraints being in difference logic.
|
||||
Use elim-term-ite elsewhere when possible.
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-11-4
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic blast-term-ite
|
||||
|
||||
### Short Description:
|
||||
|
||||
Blast term if-then-else by hoisting them up.
|
||||
This is expensive but useful in some cases, such as
|
||||
for enforcing constraints being in difference logic.
|
||||
Use `elim-term-ite` elsewhere when possible.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-fun f (Int) Int)
|
||||
(declare-fun p (Int) Bool)
|
||||
(declare-const c1 Bool)
|
||||
(declare-const c2 Bool)
|
||||
(declare-const c3 Bool)
|
||||
(declare-const e1 Int)
|
||||
(declare-const e2 Int)
|
||||
(declare-const e3 Int)
|
||||
(declare-const e4 Int)
|
||||
(assert (p (f (if c1 (if c2 e1 (if c3 e2 e3)) e4))))
|
||||
(apply blast-term-ite)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -128,9 +128,8 @@ struct cofactor_elim_term_ite::imp {
|
|||
fr.m_first = false;
|
||||
bool visited = true;
|
||||
if (is_app(t)) {
|
||||
unsigned num_args = to_app(t)->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
visit(to_app(t)->get_arg(i), form_ctx, visited);
|
||||
for (expr* arg : *to_app(t))
|
||||
visit(arg, form_ctx, visited);
|
||||
}
|
||||
// ignoring quantifiers
|
||||
if (!visited)
|
||||
|
@ -138,16 +137,13 @@ struct cofactor_elim_term_ite::imp {
|
|||
}
|
||||
|
||||
if (is_app(t)) {
|
||||
unsigned num_args = to_app(t)->get_num_args();
|
||||
unsigned i;
|
||||
for (i = 0; i < num_args; i++) {
|
||||
if (m_has_term_ite.is_marked(to_app(t)->get_arg(i)))
|
||||
for (expr* arg : *to_app(t)) {
|
||||
if (m_has_term_ite.is_marked(arg)) {
|
||||
m_has_term_ite.mark(t);
|
||||
TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);
|
||||
save_candidate(t, form_ctx);
|
||||
break;
|
||||
}
|
||||
if (i < num_args) {
|
||||
m_has_term_ite.mark(t);
|
||||
TRACE("cofactor", tout << "saving candidate: " << form_ctx << "\n" << mk_bounded_pp(t, m) << "\n";);
|
||||
save_candidate(t, form_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -284,16 +280,14 @@ struct cofactor_elim_term_ite::imp {
|
|||
}
|
||||
expr * best = nullptr;
|
||||
unsigned best_occs = 0;
|
||||
obj_map<expr, unsigned>::iterator it = occs.begin();
|
||||
obj_map<expr, unsigned>::iterator end = occs.end();
|
||||
for (; it != end; ++it) {
|
||||
for (auto const& [k, v] : occs) {
|
||||
if ((!best) ||
|
||||
(get_depth(it->m_key) < get_depth(best)) ||
|
||||
(get_depth(it->m_key) == get_depth(best) && it->m_value > best_occs) ||
|
||||
(get_depth(k) < get_depth(best)) ||
|
||||
(get_depth(k) == get_depth(best) && v > best_occs) ||
|
||||
// break ties by giving preference to equalities
|
||||
(get_depth(it->m_key) == get_depth(best) && it->m_value == best_occs && m.is_eq(it->m_key) && !m.is_eq(best))) {
|
||||
best = it->m_key;
|
||||
best_occs = it->m_value;
|
||||
(get_depth(k) == get_depth(best) && v == best_occs && m.is_eq(k) && !m.is_eq(best))) {
|
||||
best = k;
|
||||
best_occs = v;
|
||||
}
|
||||
}
|
||||
visited.reset();
|
||||
|
@ -444,7 +438,6 @@ struct cofactor_elim_term_ite::imp {
|
|||
|
||||
if (m_cache.find(s, t))
|
||||
return true;
|
||||
unsigned step = 0;
|
||||
TRACE("cofactor_ite", tout << "cofactor target:\n" << mk_ismt2_pp(s, m) << "\n";);
|
||||
expr_ref curr(m);
|
||||
curr = s;
|
||||
|
@ -457,7 +450,6 @@ struct cofactor_elim_term_ite::imp {
|
|||
t = curr.get();
|
||||
return true;
|
||||
}
|
||||
step++;
|
||||
expr_ref pos_cofactor(m);
|
||||
expr_ref neg_cofactor(m);
|
||||
m_cofactor.set_cofactor_atom(c);
|
||||
|
@ -467,7 +459,7 @@ struct cofactor_elim_term_ite::imp {
|
|||
m_cofactor.set_cofactor_atom(neg_c);
|
||||
m_cofactor(curr, neg_cofactor);
|
||||
curr = m.mk_ite(c, pos_cofactor, neg_cofactor);
|
||||
TRACE("cofactor", tout << "cofactor_ite step: " << step << "\n" << mk_ismt2_pp(curr, m) << "\n";);
|
||||
TRACE("cofactor", tout << "cofactor_ite step\n" << mk_ismt2_pp(curr, m) << "\n";);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -521,7 +513,6 @@ struct cofactor_elim_term_ite::imp {
|
|||
}
|
||||
|
||||
void cofactor(expr * t, expr_ref & r) {
|
||||
unsigned step = 0;
|
||||
TRACE("cofactor", tout << "cofactor target:\n" << mk_ismt2_pp(t, m) << "\n";);
|
||||
expr_ref curr(m);
|
||||
curr = t;
|
||||
|
@ -532,7 +523,6 @@ struct cofactor_elim_term_ite::imp {
|
|||
r = curr.get();
|
||||
return;
|
||||
}
|
||||
step++;
|
||||
expr_ref pos_cofactor(m);
|
||||
expr_ref neg_cofactor(m);
|
||||
m_cofactor.set_cofactor_atom(c);
|
||||
|
@ -554,7 +544,7 @@ struct cofactor_elim_term_ite::imp {
|
|||
curr = m.mk_ite(c, pos_cofactor, neg_cofactor);
|
||||
}
|
||||
TRACE("cofactor",
|
||||
tout << "cofactor_ite step: " << step << "\n";
|
||||
tout << "cofactor_ite step\n";
|
||||
tout << "cofactor: " << mk_ismt2_pp(c, m) << "\n";
|
||||
tout << mk_ismt2_pp(curr, m) << "\n";);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,22 @@ Module Name:
|
|||
Abstract:
|
||||
|
||||
Wrap cofactor_elim_term_ite as a tactic.
|
||||
Eliminate (ground) term if-then-else's using cofactors.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-20.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic cofactor-term-ite
|
||||
|
||||
### Short Description
|
||||
Eliminate (ground) term if-then-else's using cofactors.
|
||||
It hoists nested if-then-else expressions inside terms into the top level of the formula.
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support proofs, does not support cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -73,10 +73,10 @@ public:
|
|||
for (unsigned i = 0; i < sz; i++)
|
||||
for_each_expr(cp, visited, g->form(i));
|
||||
|
||||
std::cout << "(" << std::endl;
|
||||
std::cout << "(\n";
|
||||
for (auto const& kv : m_stats)
|
||||
std::cout << " :" << kv.first << " " << kv.second << std::endl;
|
||||
std::cout << ")" << std::endl;
|
||||
std::cout << " :" << kv.first << " " << kv.second << '\n';
|
||||
std::cout << ")\n";
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
|
|
|
@ -611,8 +611,8 @@ void ctx_simplify_tactic::updt_params(params_ref const & p) {
|
|||
void ctx_simplify_tactic::get_param_descrs(param_descrs & r) {
|
||||
insert_max_memory(r);
|
||||
insert_max_steps(r);
|
||||
r.insert("max_depth", CPK_UINT, "(default: 1024) maximum term depth.");
|
||||
r.insert("propagate_eq", CPK_BOOL, "(default: false) enable equality propagation from bounds.");
|
||||
r.insert("max_depth", CPK_UINT, "maximum term depth.", "1024");
|
||||
r.insert("propagate_eq", CPK_BOOL, "enable equality propagation from bounds.", "false");
|
||||
}
|
||||
|
||||
void ctx_simplify_tactic::operator()(goal_ref const & in,
|
||||
|
|
|
@ -13,7 +13,30 @@ Author:
|
|||
|
||||
Leonardo (leonardo) 2011-10-26
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic ctx-simplify
|
||||
|
||||
### Short Description:
|
||||
|
||||
The tactic performs simplifies sub-formulas using context built up by walking assertions and sub-formulas.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const p Bool)
|
||||
(declare-const q Bool)
|
||||
(declare-const r Bool)
|
||||
(declare-fun f (Bool) Bool)
|
||||
(assert p)
|
||||
(assert (or (f p) (and r (or (not r) q))))
|
||||
(apply ctx-simplify)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports proof terms with limited features
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
104
src/tactic/core/demodulator_tactic.h
Normal file
104
src/tactic/core/demodulator_tactic.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
demodulator_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for rewriting goals using quantified equalities
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic demodulator
|
||||
|
||||
### Short Description:
|
||||
|
||||
Extracts equalities from quantifiers and applies them for simplification
|
||||
|
||||
### Long Description
|
||||
|
||||
In first-order theorem proving (FOTP), a demodulator is a universally quantified formula of the form:
|
||||
|
||||
`Forall X1, ..., Xn. L[X1, ..., Xn] = R[X1, ..., Xn]`
|
||||
Where `L[X1, ..., Xn]` contains all variables in `R[X1, ..., Xn]`, and
|
||||
`L[X1, ..., Xn]` is "bigger" than `R[X1, ...,Xn]`.
|
||||
|
||||
The idea is to replace something big `L[X1, ..., Xn]` with something smaller `R[X1, ..., Xn]`.
|
||||
|
||||
After selecting the demodulators, we traverse the rest of the formula looking for instances of `L[X1, ..., Xn]`.
|
||||
Whenever we find an instance, we replace it with the associated instance of `R[X1, ..., Xn]`.
|
||||
|
||||
For example, suppose we have
|
||||
|
||||
```
|
||||
Forall x, y. f(x+y, y) = y
|
||||
and
|
||||
f(g(b) + h(c), h(c)) <= 0
|
||||
```
|
||||
|
||||
The term `f(g(b) + h(c), h(c))` is an instance of `f(x+y, y)` if we replace `x <- g(b)` and `y <- h(c)`.
|
||||
So, we can replace it with `y` which is bound to `h(c)` in this example. So, the result of the transformation is:
|
||||
|
||||
```
|
||||
Forall x, y. f(x+y, y) = y
|
||||
and
|
||||
h(c) <= 0
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
(declare-sort S 0)
|
||||
(declare-sort S1 0)
|
||||
(declare-sort S2 0)
|
||||
(declare-fun f () S)
|
||||
(declare-fun f1 () S)
|
||||
(declare-fun f2 (S1 S) S)
|
||||
(declare-fun f3 (S2 S) S1)
|
||||
(declare-fun f4 () S)
|
||||
(declare-fun f5 () S2)
|
||||
(assert (not (= f1 (f2 (f3 f5 f4) f))))
|
||||
(assert (forall ((q S) (v S)) (or (= q v) (= f1 (f2 (f3 f5 q) v)) (= (f2 (f3 f5 v) q) f1))))
|
||||
(assert (forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1))))
|
||||
(apply demodulator)
|
||||
```
|
||||
|
||||
It generates
|
||||
|
||||
```
|
||||
(goals
|
||||
(goal
|
||||
(forall ((q S) (v S)) (= q v))
|
||||
(forall ((q S) (x S)) (not (= (f2 (f3 f5 q) x) f1)))
|
||||
:precision precise :depth 1)
|
||||
)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores
|
||||
* does not support fine-grained proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/demodulator_simplifier.h"
|
||||
|
||||
inline tactic * mk_demodulator_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(demodulator_simplifier, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "mk_demodulator_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("demodulator", "extracts equalities from quantifiers and applies them to simplify.", "alloc(demodulator_simplifier, m, p, s)")
|
||||
*/
|
|
@ -13,6 +13,39 @@ Author:
|
|||
|
||||
Leonardo de Moura (leonardo) 2012-10-20
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic der
|
||||
|
||||
### Short Description:
|
||||
|
||||
The tactic performs _destructive equality resolution_.
|
||||
|
||||
### Long Description
|
||||
|
||||
Destructive equality resolution replaces bound variables that are
|
||||
_solved_ by their solutions in formulas. In short, the destructive
|
||||
equality resolution rule takes the form:
|
||||
|
||||
```
|
||||
(forall (X Y) (or X /= s C[X])) --> (forall (Y) C[Y])
|
||||
```
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-fun f (Int) Int)
|
||||
(declare-fun p (Int Int) Bool)
|
||||
(assert (forall ((x Int) (y Int)) (or (not (= x (f y))) (p x y))))
|
||||
(apply der)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores, proof terms
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
distribute_forall_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2012-02-18.
|
||||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
|
||||
class distribute_forall_tactic : public tactic {
|
||||
|
||||
struct rw_cfg : public default_rewriter_cfg {
|
||||
ast_manager & m;
|
||||
|
||||
rw_cfg(ast_manager & _m):m(_m) {}
|
||||
bool reduce_quantifier(quantifier * old_q,
|
||||
expr * new_body,
|
||||
expr * const * new_patterns,
|
||||
expr * const * new_no_patterns,
|
||||
expr_ref & result,
|
||||
proof_ref & result_pr) {
|
||||
|
||||
if (!is_forall(old_q)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.is_not(new_body) && m.is_or(to_app(new_body)->get_arg(0))) {
|
||||
// (forall X (not (or F1 ... Fn)))
|
||||
// -->
|
||||
// (and (forall X (not F1))
|
||||
// ...
|
||||
// (forall X (not Fn)))
|
||||
app * or_e = to_app(to_app(new_body)->get_arg(0));
|
||||
unsigned num_args = or_e->get_num_args();
|
||||
expr_ref_buffer new_args(m);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = or_e->get_arg(i);
|
||||
expr * not_arg = mk_not(m, arg);
|
||||
quantifier_ref tmp_q(m);
|
||||
tmp_q = m.update_quantifier(old_q, not_arg);
|
||||
new_args.push_back(elim_unused_vars(m, tmp_q, params_ref()));
|
||||
}
|
||||
result = m.mk_and(new_args.size(), new_args.data());
|
||||
if (m.proofs_enabled()) {
|
||||
result_pr = m.mk_push_quant(old_q, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m.is_and(new_body)) {
|
||||
// (forall X (and F1 ... Fn))
|
||||
// -->
|
||||
// (and (forall X F1)
|
||||
// ...
|
||||
// (forall X Fn)
|
||||
unsigned num_args = to_app(new_body)->get_num_args();
|
||||
expr_ref_buffer new_args(m);
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
expr * arg = to_app(new_body)->get_arg(i);
|
||||
quantifier_ref tmp_q(m);
|
||||
tmp_q = m.update_quantifier(old_q, arg);
|
||||
new_args.push_back(elim_unused_vars(m, tmp_q, params_ref()));
|
||||
}
|
||||
result = m.mk_and(new_args.size(), new_args.data());
|
||||
if (m.proofs_enabled()) {
|
||||
result_pr = m.mk_push_quant(old_q, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct rw : public rewriter_tpl<rw_cfg> {
|
||||
rw_cfg m_cfg;
|
||||
|
||||
rw(ast_manager & m, bool proofs_enabled):
|
||||
rewriter_tpl<rw_cfg>(m, proofs_enabled, m_cfg),
|
||||
m_cfg(m) {
|
||||
}
|
||||
};
|
||||
|
||||
rw * m_rw;
|
||||
|
||||
public:
|
||||
distribute_forall_tactic():m_rw(nullptr) {}
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
return alloc(distribute_forall_tactic);
|
||||
}
|
||||
|
||||
char const* name() const override { return "distribute_forall"; }
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result) override {
|
||||
ast_manager & m = g->m();
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
rw r(m, produce_proofs);
|
||||
m_rw = &r;
|
||||
result.reset();
|
||||
tactic_report report("distribute-forall", *g);
|
||||
|
||||
expr_ref new_curr(m);
|
||||
proof_ref new_pr(m);
|
||||
unsigned size = g->size();
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
if (g->inconsistent())
|
||||
break;
|
||||
expr * curr = g->form(idx);
|
||||
r(curr, new_curr, new_pr);
|
||||
if (g->proofs_enabled()) {
|
||||
proof * pr = g->pr(idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
g->update(idx, new_curr, new_pr, g->dep(idx));
|
||||
}
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
m_rw = nullptr;
|
||||
}
|
||||
|
||||
void cleanup() override {}
|
||||
};
|
||||
|
||||
tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) {
|
||||
return alloc(distribute_forall_tactic);
|
||||
}
|
|
@ -13,16 +13,43 @@ Author:
|
|||
|
||||
Leonardo de Moura (leonardo) 2012-02-18.
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic distribute-forall
|
||||
|
||||
### Short Description:
|
||||
|
||||
Distribute $\forall$ over conjunctions (and distribute $\exists$ over disjunctions)
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-fun p (Int) Bool)
|
||||
(declare-fun q (Int) Bool)
|
||||
(assert (forall ((x Int)) (and (p x) (q x))))
|
||||
(apply distribute-forall)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores, proof terms
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/distribute_forall.h"
|
||||
|
||||
tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p);
|
||||
inline tactic * mk_distribute_forall_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(distribute_forall_simplifier, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("distribute-forall", "distribute forall over conjunctions.", "mk_distribute_forall_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("distribute-forall", "distribute forall over conjunctions.", "alloc(distribute_forall_simplifier, m, p, s)")
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,609 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
dom_simplify_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Dominator-based context simplifer.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj and Nuno
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_ll_pp.h"
|
||||
#include "tactic/core/dom_simplify_tactic.h"
|
||||
|
||||
/**
|
||||
\brief compute a post-order traversal for e.
|
||||
Also populate the set of parents
|
||||
*/
|
||||
void expr_dominators::compute_post_order() {
|
||||
unsigned post_num = 0;
|
||||
SASSERT(m_post2expr.empty());
|
||||
SASSERT(m_expr2post.empty());
|
||||
ast_mark mark;
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(m_root);
|
||||
while (!todo.empty()) {
|
||||
expr* e = todo.back();
|
||||
if (mark.is_marked(e)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
bool done = true;
|
||||
for (expr* arg : *a) {
|
||||
if (!mark.is_marked(arg)) {
|
||||
todo.push_back(arg);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
mark.mark(e, true);
|
||||
m_expr2post.insert(e, post_num++);
|
||||
m_post2expr.push_back(e);
|
||||
todo.pop_back();
|
||||
for (expr* arg : *a) {
|
||||
add_edge(m_parents, arg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mark.mark(e, true);
|
||||
todo.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr* expr_dominators::intersect(expr* x, expr * y) {
|
||||
unsigned n1 = m_expr2post[x];
|
||||
unsigned n2 = m_expr2post[y];
|
||||
while (n1 != n2) {
|
||||
if (n1 < n2) {
|
||||
x = m_doms[x];
|
||||
n1 = m_expr2post[x];
|
||||
}
|
||||
else if (n1 > n2) {
|
||||
y = m_doms[y];
|
||||
n2 = m_expr2post[y];
|
||||
}
|
||||
}
|
||||
SASSERT(x == y);
|
||||
return x;
|
||||
}
|
||||
|
||||
bool expr_dominators::compute_dominators() {
|
||||
expr * e = m_root;
|
||||
SASSERT(m_doms.empty());
|
||||
m_doms.insert(e, e);
|
||||
bool change = true;
|
||||
unsigned iterations = 1;
|
||||
while (change) {
|
||||
change = false;
|
||||
TRACE("simplify",
|
||||
for (auto & kv : m_doms) {
|
||||
tout << mk_bounded_pp(kv.m_key, m) << " |-> " << mk_bounded_pp(kv.m_value, m) << "\n";
|
||||
});
|
||||
|
||||
SASSERT(m_post2expr.empty() || m_post2expr.back() == e);
|
||||
for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) {
|
||||
expr * child = m_post2expr[i];
|
||||
ptr_vector<expr> const& p = m_parents[child];
|
||||
expr * new_idom = nullptr, *idom2 = nullptr;
|
||||
|
||||
for (expr * pred : p) {
|
||||
if (m_doms.contains(pred)) {
|
||||
new_idom = !new_idom ? pred : intersect(new_idom, pred);
|
||||
}
|
||||
}
|
||||
if (!new_idom) {
|
||||
m_doms.insert(child, p[0]);
|
||||
change = true;
|
||||
}
|
||||
else if (!m_doms.find(child, idom2) || idom2 != new_idom) {
|
||||
m_doms.insert(child, new_idom);
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
iterations *= 2;
|
||||
if (change && iterations > m_post2expr.size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void expr_dominators::extract_tree() {
|
||||
for (auto const& kv : m_doms) {
|
||||
add_edge(m_tree, kv.m_value, kv.m_key);
|
||||
}
|
||||
}
|
||||
|
||||
bool expr_dominators::compile(expr * e) {
|
||||
reset();
|
||||
m_root = e;
|
||||
compute_post_order();
|
||||
if (!compute_dominators()) return false;
|
||||
extract_tree();
|
||||
TRACE("simplify", display(tout););
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_dominators::compile(unsigned sz, expr * const* es) {
|
||||
expr_ref e(m.mk_and(sz, es), m);
|
||||
return compile(e);
|
||||
}
|
||||
|
||||
void expr_dominators::reset() {
|
||||
m_expr2post.reset();
|
||||
m_post2expr.reset();
|
||||
m_parents.reset();
|
||||
m_doms.reset();
|
||||
m_tree.reset();
|
||||
m_root.reset();
|
||||
}
|
||||
|
||||
std::ostream& expr_dominators::display(std::ostream& out) {
|
||||
return display(out, 0, m_root);
|
||||
}
|
||||
|
||||
std::ostream& expr_dominators::display(std::ostream& out, unsigned indent, expr* r) {
|
||||
for (unsigned i = 0; i < indent; ++i) out << " ";
|
||||
out << r->get_id() << ": " << mk_bounded_pp(r, m, 1) << "\n";
|
||||
if (m_tree.contains(r)) {
|
||||
for (expr* child : m_tree[r]) {
|
||||
if (child != r)
|
||||
display(out, indent + 1, child);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------
|
||||
// dom_simplify_tactic
|
||||
|
||||
dom_simplify_tactic::~dom_simplify_tactic() {
|
||||
dealloc(m_simplifier);
|
||||
}
|
||||
|
||||
tactic * dom_simplify_tactic::translate(ast_manager & m) {
|
||||
return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params);
|
||||
}
|
||||
|
||||
|
||||
void dom_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) {
|
||||
tactic_report report("dom-simplify", *in.get());
|
||||
simplify_goal(*(in.get()));
|
||||
in->inc_depth();
|
||||
result.push_back(in.get());
|
||||
}
|
||||
|
||||
void dom_simplify_tactic::cleanup() {
|
||||
m_trail.reset();
|
||||
m_args.reset();
|
||||
m_result.reset();
|
||||
m_dominators.reset();
|
||||
}
|
||||
|
||||
expr_ref dom_simplify_tactic::simplify_ite(app * ite) {
|
||||
expr_ref r(m);
|
||||
expr * c = nullptr, *t = nullptr, *e = nullptr;
|
||||
VERIFY(m.is_ite(ite, c, t, e));
|
||||
unsigned old_lvl = scope_level();
|
||||
expr_ref new_c = simplify_arg(c);
|
||||
if (m.is_true(new_c)) {
|
||||
r = simplify_arg(t);
|
||||
}
|
||||
else if (!assert_expr(new_c, false)) {
|
||||
r = simplify_arg(e);
|
||||
}
|
||||
else {
|
||||
for (expr * child : tree(ite))
|
||||
if (is_subexpr(child, t) && !is_subexpr(child, e))
|
||||
simplify_rec(child);
|
||||
|
||||
pop(scope_level() - old_lvl);
|
||||
expr_ref new_t = simplify_arg(t);
|
||||
reset_cache();
|
||||
if (!assert_expr(new_c, true)) {
|
||||
return new_t;
|
||||
}
|
||||
for (expr * child : tree(ite))
|
||||
if (is_subexpr(child, e) && !is_subexpr(child, t))
|
||||
simplify_rec(child);
|
||||
pop(scope_level() - old_lvl);
|
||||
expr_ref new_e = simplify_arg(e);
|
||||
|
||||
if (c == new_c && t == new_t && e == new_e) {
|
||||
r = ite;
|
||||
}
|
||||
else if (new_t == new_e) {
|
||||
r = new_t;
|
||||
}
|
||||
else {
|
||||
TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";);
|
||||
r = m.mk_ite(new_c, new_t, new_e);
|
||||
}
|
||||
}
|
||||
reset_cache();
|
||||
return r;
|
||||
}
|
||||
|
||||
expr_ref dom_simplify_tactic::simplify_arg(expr * e) {
|
||||
expr_ref r(m);
|
||||
r = get_cached(e);
|
||||
(*m_simplifier)(r);
|
||||
CTRACE("simplify", e != r, tout << "depth: " << m_depth << " " << mk_pp(e, m) << " -> " << r << "\n";);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief simplify e recursively.
|
||||
*/
|
||||
expr_ref dom_simplify_tactic::simplify_rec(expr * e0) {
|
||||
expr_ref r(m);
|
||||
expr* e = nullptr;
|
||||
|
||||
if (!m_result.find(e0, e)) {
|
||||
e = e0;
|
||||
}
|
||||
|
||||
++m_depth;
|
||||
if (m_depth > m_max_depth) {
|
||||
r = e;
|
||||
}
|
||||
else if (m.is_ite(e)) {
|
||||
r = simplify_ite(to_app(e));
|
||||
}
|
||||
else if (m.is_and(e)) {
|
||||
r = simplify_and(to_app(e));
|
||||
}
|
||||
else if (m.is_or(e)) {
|
||||
r = simplify_or(to_app(e));
|
||||
}
|
||||
else if (m.is_not(e)) {
|
||||
r = simplify_not(to_app(e));
|
||||
}
|
||||
else {
|
||||
for (expr * child : tree(e)) {
|
||||
if (child != e)
|
||||
simplify_rec(child);
|
||||
}
|
||||
if (is_app(e)) {
|
||||
m_args.reset();
|
||||
for (expr* arg : *to_app(e)) {
|
||||
// we don't have a way to distinguish between e.g.
|
||||
// ite(c, f(c), foo) (which should go to ite(c, f(true), foo))
|
||||
// from and(or(x, y), f(x)), where we do a "trial" with x=false
|
||||
// Trials are good for boolean formula simplification but not sound
|
||||
// for fn applications.
|
||||
m_args.push_back(m.is_bool(arg) ? arg : simplify_arg(arg));
|
||||
}
|
||||
r = m.mk_app(to_app(e)->get_decl(), m_args.size(), m_args.data());
|
||||
}
|
||||
else {
|
||||
r = e;
|
||||
}
|
||||
}
|
||||
CTRACE("simplify", e0 != r, tout << "depth before: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";);
|
||||
(*m_simplifier)(r);
|
||||
cache(e0, r);
|
||||
CTRACE("simplify", e0 != r, tout << "depth: " << m_depth << " " << mk_pp(e0, m) << " -> " << r << "\n";);
|
||||
--m_depth;
|
||||
m_subexpr_cache.reset();
|
||||
return r;
|
||||
}
|
||||
|
||||
expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) {
|
||||
expr_ref r(m);
|
||||
unsigned old_lvl = scope_level();
|
||||
|
||||
auto is_subexpr_arg = [&](expr * child, expr * except) {
|
||||
if (!is_subexpr(child, except))
|
||||
return false;
|
||||
for (expr * arg : *e) {
|
||||
if (arg != except && is_subexpr(child, arg))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
expr_ref_vector args(m);
|
||||
|
||||
auto simp_arg = [&](expr* arg) {
|
||||
for (expr * child : tree(arg)) {
|
||||
if (is_subexpr_arg(child, arg)) {
|
||||
simplify_rec(child);
|
||||
}
|
||||
}
|
||||
r = simplify_arg(arg);
|
||||
args.push_back(r);
|
||||
if (!assert_expr(r, !is_and)) {
|
||||
pop(scope_level() - old_lvl);
|
||||
r = is_and ? m.mk_false() : m.mk_true();
|
||||
reset_cache();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (m_forward) {
|
||||
for (expr * arg : *e) {
|
||||
if (simp_arg(arg))
|
||||
return r;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned i = e->get_num_args(); i-- > 0; ) {
|
||||
if (simp_arg(e->get_arg(i)))
|
||||
return r;
|
||||
}
|
||||
args.reverse();
|
||||
}
|
||||
|
||||
pop(scope_level() - old_lvl);
|
||||
reset_cache();
|
||||
return { is_and ? mk_and(args) : mk_or(args), m };
|
||||
}
|
||||
|
||||
expr_ref dom_simplify_tactic::simplify_not(app * e) {
|
||||
expr *ee;
|
||||
ENSURE(m.is_not(e, ee));
|
||||
unsigned old_lvl = scope_level();
|
||||
expr_ref t = simplify_rec(ee);
|
||||
pop(scope_level() - old_lvl);
|
||||
reset_cache();
|
||||
return mk_not(t);
|
||||
}
|
||||
|
||||
|
||||
bool dom_simplify_tactic::init(goal& g) {
|
||||
expr_ref_vector args(m);
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; i < sz; ++i) args.push_back(g.form(i));
|
||||
expr_ref fml = mk_and(args);
|
||||
m_result.reset();
|
||||
m_trail.reset();
|
||||
return m_dominators.compile(fml);
|
||||
}
|
||||
|
||||
void dom_simplify_tactic::simplify_goal(goal& g) {
|
||||
|
||||
SASSERT(scope_level() == 0);
|
||||
bool change = true;
|
||||
unsigned n = 0;
|
||||
m_depth = 0;
|
||||
while (change && n < 10) {
|
||||
change = false;
|
||||
++n;
|
||||
|
||||
// go forwards
|
||||
m_forward = true;
|
||||
if (!init(g)) return;
|
||||
unsigned sz = g.size();
|
||||
for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) {
|
||||
expr_ref r = simplify_rec(g.form(i));
|
||||
if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) {
|
||||
r = m.mk_false();
|
||||
}
|
||||
CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";);
|
||||
change |= r != g.form(i);
|
||||
proof_ref new_pr(m);
|
||||
if (g.proofs_enabled() && g.pr(i)) {
|
||||
new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r));
|
||||
}
|
||||
g.update(i, r, new_pr, g.dep(i));
|
||||
}
|
||||
pop(scope_level());
|
||||
|
||||
// go backwards
|
||||
m_forward = false;
|
||||
if (!init(g)) return;
|
||||
sz = g.size();
|
||||
for (unsigned i = sz; !g.inconsistent() && i > 0; ) {
|
||||
--i;
|
||||
expr_ref r = simplify_rec(g.form(i));
|
||||
if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !g.proofs_enabled() && !assert_expr(r, false)) {
|
||||
r = m.mk_false();
|
||||
}
|
||||
change |= r != g.form(i);
|
||||
CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";);
|
||||
proof_ref new_pr(m);
|
||||
if (g.proofs_enabled() && g.pr(i)) {
|
||||
new_pr = m.mk_rewrite(g.form(i), r);
|
||||
new_pr = m.mk_modus_ponens(g.pr(i), new_pr);
|
||||
}
|
||||
g.update(i, r, new_pr, g.dep(i));
|
||||
}
|
||||
pop(scope_level());
|
||||
}
|
||||
SASSERT(scope_level() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief determine if a is dominated by b.
|
||||
Walk the immediate dominators of a upwards until hitting b or a term that is deeper than b.
|
||||
Save intermediary results in a cache to avoid recomputations.
|
||||
*/
|
||||
|
||||
bool dom_simplify_tactic::is_subexpr(expr * a, expr * b) {
|
||||
if (a == b)
|
||||
return true;
|
||||
|
||||
bool r;
|
||||
if (m_subexpr_cache.find(a, b, r))
|
||||
return r;
|
||||
|
||||
if (get_depth(a) >= get_depth(b)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(a != idom(a) && get_depth(idom(a)) > get_depth(a));
|
||||
r = is_subexpr(idom(a), b);
|
||||
m_subexpr_cache.insert(a, b, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
ptr_vector<expr> const & dom_simplify_tactic::tree(expr * e) {
|
||||
if (auto p = m_dominators.get_tree().find_core(e))
|
||||
return p->get_data().get_value();
|
||||
return m_empty;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// expr_substitution_simplifier
|
||||
namespace {
|
||||
|
||||
class expr_substitution_simplifier : public dom_simplifier {
|
||||
ast_manager& m;
|
||||
expr_substitution m_subst;
|
||||
scoped_expr_substitution m_scoped_substitution;
|
||||
obj_map<expr, unsigned> m_expr2depth;
|
||||
expr_ref_vector m_trail;
|
||||
|
||||
// move from asserted_formulas to here..
|
||||
void compute_depth(expr* e) {
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(e);
|
||||
while (!todo.empty()) {
|
||||
e = todo.back();
|
||||
unsigned d = 0;
|
||||
if (m_expr2depth.contains(e)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
bool visited = true;
|
||||
for (expr* arg : *a) {
|
||||
unsigned d1 = 0;
|
||||
if (m_expr2depth.find(arg, d1)) {
|
||||
d = std::max(d, d1);
|
||||
}
|
||||
else {
|
||||
visited = false;
|
||||
todo.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (!visited) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
todo.pop_back();
|
||||
m_expr2depth.insert(e, d + 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_gt(expr* lhs, expr* rhs) {
|
||||
if (lhs == rhs) {
|
||||
return false;
|
||||
}
|
||||
if (m.is_value(rhs)) {
|
||||
return true;
|
||||
}
|
||||
SASSERT(is_ground(lhs) && is_ground(rhs));
|
||||
if (depth(lhs) > depth(rhs)) {
|
||||
return true;
|
||||
}
|
||||
if (depth(lhs) == depth(rhs) && is_app(lhs) && is_app(rhs)) {
|
||||
app* l = to_app(lhs);
|
||||
app* r = to_app(rhs);
|
||||
if (l->get_decl()->get_id() != r->get_decl()->get_id()) {
|
||||
return l->get_decl()->get_id() > r->get_decl()->get_id();
|
||||
}
|
||||
if (l->get_num_args() != r->get_num_args()) {
|
||||
return l->get_num_args() > r->get_num_args();
|
||||
}
|
||||
for (unsigned i = 0; i < l->get_num_args(); ++i) {
|
||||
if (l->get_arg(i) != r->get_arg(i)) {
|
||||
return is_gt(l->get_arg(i), r->get_arg(i));
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned depth(expr* e) { return m_expr2depth[e]; }
|
||||
|
||||
public:
|
||||
expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {}
|
||||
|
||||
bool assert_expr(expr * t, bool sign) override {
|
||||
expr* tt;
|
||||
if (m.is_not(t, tt))
|
||||
return assert_expr(tt, !sign);
|
||||
if (m.is_false(t))
|
||||
return sign;
|
||||
if (m.is_true(t))
|
||||
return !sign;
|
||||
|
||||
TRACE("simplify", tout << t->get_id() << ": " << mk_bounded_pp(t, m) << " " << (sign?" - neg":" - pos") << "\n";);
|
||||
|
||||
m_scoped_substitution.push();
|
||||
if (!sign) {
|
||||
update_substitution(t, nullptr);
|
||||
}
|
||||
else {
|
||||
expr_ref nt(m.mk_not(t), m);
|
||||
update_substitution(nt, nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_substitution(expr* n, proof* pr) {
|
||||
expr* lhs, *rhs, *n1;
|
||||
if (is_ground(n) && m.is_eq(n, lhs, rhs)) {
|
||||
compute_depth(lhs);
|
||||
compute_depth(rhs);
|
||||
m_trail.push_back(lhs);
|
||||
m_trail.push_back(rhs);
|
||||
if (is_gt(lhs, rhs)) {
|
||||
TRACE("propagate_values", tout << "insert " << mk_pp(lhs, m) << " -> " << mk_pp(rhs, m) << "\n";);
|
||||
m_scoped_substitution.insert(lhs, rhs, pr);
|
||||
return;
|
||||
}
|
||||
if (is_gt(rhs, lhs)) {
|
||||
TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";);
|
||||
m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr));
|
||||
return;
|
||||
}
|
||||
TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";);
|
||||
}
|
||||
if (m.is_not(n, n1)) {
|
||||
m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr));
|
||||
}
|
||||
else {
|
||||
m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr));
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); }
|
||||
|
||||
void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); }
|
||||
|
||||
unsigned scope_level() const override { return m_scoped_substitution.scope_level(); }
|
||||
|
||||
dom_simplifier * translate(ast_manager & m) override {
|
||||
SASSERT(m_subst.empty());
|
||||
return alloc(expr_substitution_simplifier, m);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(dom_simplify_tactic, m, alloc(expr_substitution_simplifier, m), p));
|
||||
}
|
|
@ -14,7 +14,29 @@ Author:
|
|||
|
||||
Nikolaj and Nuno
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic dom-simplify
|
||||
|
||||
### Short Description
|
||||
|
||||
Apply dominator simplification rules
|
||||
|
||||
### Long Description
|
||||
|
||||
Dominator-based simplification is a context dependent simplification function that uses a dominator tree to control the number of paths it
|
||||
visits during simplification. The expression DAG may have an exponential number of paths, but only paths corresponding to a dominator
|
||||
tree are visited. Since the paths selected by the dominator trees are limited, the simplifier may easily fail to simplify within a context.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const a Bool)
|
||||
(declare-const b Bool)
|
||||
(assert (and a (or a b)))
|
||||
(apply dom-simplify)
|
||||
```
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
|
@ -22,128 +44,18 @@ Notes:
|
|||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "ast/rewriter/dom_simplifier.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "util/obj_pair_hashtable.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/dominator_simplifier.h"
|
||||
|
||||
|
||||
class expr_dominators {
|
||||
public:
|
||||
typedef obj_map<expr, ptr_vector<expr>> tree_t;
|
||||
private:
|
||||
ast_manager& m;
|
||||
expr_ref m_root;
|
||||
obj_map<expr, unsigned> m_expr2post; // reverse post-order number
|
||||
ptr_vector<expr> m_post2expr;
|
||||
tree_t m_parents;
|
||||
obj_map<expr, expr*> m_doms;
|
||||
tree_t m_tree;
|
||||
|
||||
void add_edge(tree_t& tree, expr * src, expr* dst) {
|
||||
tree.insert_if_not_there(src, ptr_vector<expr>()).push_back(dst);
|
||||
}
|
||||
|
||||
void compute_post_order();
|
||||
expr* intersect(expr* x, expr * y);
|
||||
bool compute_dominators();
|
||||
void extract_tree();
|
||||
|
||||
std::ostream& display(std::ostream& out, unsigned indent, expr* r);
|
||||
|
||||
public:
|
||||
expr_dominators(ast_manager& m): m(m), m_root(m) {}
|
||||
|
||||
bool compile(expr * e);
|
||||
bool compile(unsigned sz, expr * const* es);
|
||||
tree_t const& get_tree() { return m_tree; }
|
||||
void reset();
|
||||
expr* idom(expr *e) const { return m_doms[e]; }
|
||||
|
||||
std::ostream& display(std::ostream& out);
|
||||
};
|
||||
|
||||
class dom_simplifier {
|
||||
public:
|
||||
virtual ~dom_simplifier() = default;
|
||||
/**
|
||||
\brief assert_expr performs an implicit push
|
||||
*/
|
||||
virtual bool assert_expr(expr * t, bool sign) = 0;
|
||||
|
||||
/**
|
||||
\brief apply simplification.
|
||||
*/
|
||||
virtual void operator()(expr_ref& r) = 0;
|
||||
|
||||
/**
|
||||
\brief pop scopes accumulated from assertions.
|
||||
*/
|
||||
virtual void pop(unsigned num_scopes) = 0;
|
||||
|
||||
virtual dom_simplifier * translate(ast_manager & m) = 0;
|
||||
|
||||
virtual unsigned scope_level() const = 0;
|
||||
|
||||
};
|
||||
|
||||
class dom_simplify_tactic : public tactic {
|
||||
ast_manager& m;
|
||||
dom_simplifier* m_simplifier;
|
||||
params_ref m_params;
|
||||
expr_ref_vector m_trail, m_args;
|
||||
obj_map<expr, expr*> m_result;
|
||||
expr_dominators m_dominators;
|
||||
unsigned m_depth;
|
||||
unsigned m_max_depth;
|
||||
ptr_vector<expr> m_empty;
|
||||
obj_pair_map<expr, expr, bool> m_subexpr_cache;
|
||||
bool m_forward;
|
||||
|
||||
expr_ref simplify_rec(expr* t);
|
||||
expr_ref simplify_arg(expr* t);
|
||||
expr_ref simplify_ite(app * ite);
|
||||
expr_ref simplify_and(app * e) { return simplify_and_or(true, e); }
|
||||
expr_ref simplify_or(app * e) { return simplify_and_or(false, e); }
|
||||
expr_ref simplify_and_or(bool is_and, app * e);
|
||||
expr_ref simplify_not(app * e);
|
||||
void simplify_goal(goal& g);
|
||||
|
||||
bool is_subexpr(expr * a, expr * b);
|
||||
|
||||
expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); }
|
||||
void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); }
|
||||
void reset_cache() { m_result.reset(); }
|
||||
|
||||
ptr_vector<expr> const & tree(expr * e);
|
||||
expr* idom(expr *e) const { return m_dominators.idom(e); }
|
||||
|
||||
unsigned scope_level() { return m_simplifier->scope_level(); }
|
||||
void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); }
|
||||
bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); }
|
||||
|
||||
bool init(goal& g);
|
||||
|
||||
public:
|
||||
dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()):
|
||||
m(m), m_simplifier(s), m_params(p),
|
||||
m_trail(m), m_args(m),
|
||||
m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {}
|
||||
|
||||
~dom_simplify_tactic() override;
|
||||
|
||||
char const* name() const override { return "dom_simplify"; }
|
||||
|
||||
tactic * translate(ast_manager & m) override;
|
||||
void updt_params(params_ref const & p) override {}
|
||||
static void get_param_descrs(param_descrs & r) {}
|
||||
void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); }
|
||||
void operator()(goal_ref const & in, goal_ref_buffer & result) override;
|
||||
void cleanup() override;
|
||||
};
|
||||
|
||||
tactic * mk_dom_simplify_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
inline tactic* mk_dom_simplify_tactic(ast_manager& m, params_ref const& p) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto& s) -> dependent_expr_simplifier* { return alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("dom-simplify", "apply dominator simplification rules.", "mk_dom_simplify_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("dom-simplify", "apply dominator simplification rules.", "alloc(dominator_simplifier, m, s, mk_expr_substitution_simplifier(m), p)")
|
||||
*/
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ Notes:
|
|||
#include "tactic/tactical.h"
|
||||
#include "ast/normal_forms/defined_names.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class elim_term_ite_tactic : public tactic {
|
||||
|
||||
|
|
|
@ -5,16 +5,39 @@ Module Name:
|
|||
|
||||
elim_term_ite_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Eliminate term if-then-else by adding
|
||||
new fresh auxiliary variables.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo (leonardo) 2011-12-29
|
||||
|
||||
Notes:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic elim-term-ite
|
||||
|
||||
### Short Description:
|
||||
|
||||
Eliminate term if-then-else by adding
|
||||
new fresh auxiliary variables.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-fun f (Int) Int)
|
||||
(declare-fun p (Int) Bool)
|
||||
(declare-const c1 Bool)
|
||||
(declare-const c2 Bool)
|
||||
(declare-const c3 Bool)
|
||||
(declare-const e1 Int)
|
||||
(declare-const e2 Int)
|
||||
(declare-const e3 Int)
|
||||
(declare-const e4 Int)
|
||||
(assert (p (f (if c1 (if c2 e1 (if c3 e2 e3)) e4))))
|
||||
(apply elim-term-ite)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports proof terms and unsat cores
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
122
src/tactic/core/elim_uncnstr2_tactic.h
Normal file
122
src/tactic/core/elim_uncnstr2_tactic.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
elim_unconstr2_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating unconstrained terms.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic elim-uncnstr
|
||||
|
||||
### Short Description
|
||||
|
||||
Eliminate Unconstrained uninterpreted constants
|
||||
|
||||
### Long Description
|
||||
|
||||
The tactic eliminates uninterpreted constants that occur only once in a goal and such that the immediate context
|
||||
where they occur can be replaced by a fresh constant. We call these occurrences invertible.
|
||||
It relies on a series of theory specific invertibility transformations.
|
||||
In the following assume `x` and `x'` occur in a unique subterm and `y` is a fresh uninterpreted constant.
|
||||
|
||||
#### Boolean Connectives
|
||||
|
||||
| Original Context | New Term | Updated solution |
|
||||
|------------------|----------|------------------------ |
|
||||
`(if c x x')` | `y` | `x = x' = y` |
|
||||
`(if x x' e)` | `y` | `x = true, x' = y` |
|
||||
`(if x t x')` | `y` | `x = false, x' = y` |
|
||||
`(not x)` | `y` | `x = (not y)` |
|
||||
`(and x x')` | `y` | `x = y, x' = true` |
|
||||
`(or x x')` | `y` | `x = y, x' = false` |
|
||||
`(= x t)` | `y` | `x = (if y t (diff t))` |
|
||||
|
||||
where diff is a diagnonalization function available in domains of size `>` 1.
|
||||
|
||||
#### Arithmetic
|
||||
|
||||
| Original Context | New Term | Updated solution |
|
||||
|------------------|----------|------------------------ |
|
||||
`(+ x t)` | `y` | `x = y - t` |
|
||||
`(* x x')` | `y` | `x = y, x' = 1` |
|
||||
`(* -1 x)` | `y` | `x = -y` |
|
||||
`(<= x t)` | `y` | `x = (if y t (+ t 1))` |
|
||||
`(<= t x)` | `y` | `x = (if y t (- t 1))` |
|
||||
|
||||
#### Bit-vectors
|
||||
|
||||
| Original Context | New Term | Updated solution |
|
||||
|------------------|----------|--------------------------|
|
||||
`(bvadd x t)` | `y` | `x = y - t` |
|
||||
`(bvmul x x')` | `y` | `x = y, x' = 1` |
|
||||
`(bvmul odd x)` | `y` | `x = inv(odd)*y` |
|
||||
`((extract sz-1 0) x)` | `y` | `x = y` |
|
||||
`((extract hi lo) x)` | `y` | `x = (concat y1 y y2)` |
|
||||
`(udiv x x')` | `y` | `x = y, x' = 1` |
|
||||
`(concat x x')` | `y` | `x = (extract hi1 lo1 y)` |
|
||||
`(bvule x t)` | `(or y (= t MAX))` | `x = (if y t (bvadd t 1))` |
|
||||
`(bvule t x)` | `(or y (= t MIN))` | `x = (if y t (bvsub t 1))` |
|
||||
`(bvnot x)` | `y` | `x = (bvnot y)` |
|
||||
`(bvand x x')` | `y` | `x = y, x' = MAX` |
|
||||
|
||||
In addition there are conversions for shift and bit-wise or and signed comparison.
|
||||
|
||||
#### Arrays
|
||||
|
||||
| Original Context | New Term | Updated solution |
|
||||
|------------------|----------|--------------------------|
|
||||
`(select x t)` | `y` | `x = (const y)` |
|
||||
`(store x x1 x2)` | `y` | `x2 = (select x x1), x = y, x1 = arb` |
|
||||
|
||||
#### Algebraic Datatypes
|
||||
|
||||
| Original Context | New Term | Updated solution |
|
||||
|------------------|----------|--------------------------|
|
||||
`(head x)` | `y` | `x = (cons y arb)` |
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-fun p (Int) Bool)
|
||||
(assert (>= (+ y (+ x y)) y))
|
||||
(assert (p y))
|
||||
(apply elim-uncnstr)
|
||||
(assert (p (+ x y)))
|
||||
(apply elim-uncnstr)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores
|
||||
* does not support fine-grained proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/elim_unconstrained.h"
|
||||
|
||||
inline tactic * mk_elim_uncnstr2_tactic(ast_manager & m, params_ref const & p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(elim_unconstrained, m, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("elim-uncnstr2", "eliminate unconstrained variables.", "mk_elim_uncnstr2_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("elim-unconstrained", "eliminate unconstrained variables.", "alloc(elim_unconstrained, m, s)")
|
||||
*/
|
|
@ -17,7 +17,7 @@ Notes:
|
|||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
@ -121,7 +121,7 @@ class elim_uncnstr_tactic : public tactic {
|
|||
SASSERT(uncnstr(v));
|
||||
SASSERT(to_app(v)->get_num_args() == 0);
|
||||
if (m_mc)
|
||||
m_mc->add(to_app(v)->get_decl(), def);
|
||||
m_mc->add(v, def);
|
||||
}
|
||||
|
||||
void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) {
|
||||
|
@ -270,7 +270,36 @@ class elim_uncnstr_tactic : public tactic {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* if (c, x, x') -> fresh
|
||||
* x := fresh
|
||||
* x' := fresh
|
||||
*
|
||||
* if (x, x', e) -> fresh
|
||||
* x := true
|
||||
* x' := fresh
|
||||
*
|
||||
* if (x, t, x') -> fresh
|
||||
* x := false
|
||||
* x' := fresh
|
||||
*
|
||||
* not x -> fresh
|
||||
* x := not fresh
|
||||
*
|
||||
* x & x' -> fresh
|
||||
* x := fresh
|
||||
* x' := true
|
||||
*
|
||||
* x or x' -> fresh
|
||||
* x := fresh
|
||||
* x' := false
|
||||
*
|
||||
* x = t -> fresh
|
||||
* x := if(fresh, t, diff(t))
|
||||
* where diff is a diagnonalization function available in domains of size > 1.
|
||||
*
|
||||
*/
|
||||
app * process_basic_app(func_decl * f, unsigned num, expr * const * args) {
|
||||
SASSERT(f->get_family_id() == m().get_basic_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
|
@ -434,6 +463,10 @@ class elim_uncnstr_tactic : public tactic {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* similar as for bit-vectors
|
||||
*/
|
||||
|
||||
app * process_arith_app(func_decl * f, unsigned num, expr * const * args) {
|
||||
|
||||
|
@ -466,7 +499,7 @@ class elim_uncnstr_tactic : public tactic {
|
|||
add_defs(num, args, r, m_bv_util.mk_numeral(rational(1), s));
|
||||
return r;
|
||||
}
|
||||
// c * v (c is even) case
|
||||
// c * v (c is odd) case
|
||||
unsigned bv_size;
|
||||
rational val;
|
||||
rational inv;
|
||||
|
@ -595,7 +628,46 @@ class elim_uncnstr_tactic : public tactic {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* x + t -> fresh
|
||||
* x := fresh - t
|
||||
*
|
||||
* x * x' * x'' -> fresh
|
||||
* x := fresh
|
||||
* x', x'' := 1
|
||||
*
|
||||
* c * x -> fresh, c is odd
|
||||
* x := fresh*c^-1
|
||||
*
|
||||
* x[sz-1:0] -> fresh
|
||||
* x := fresh
|
||||
*
|
||||
* x[hi:lo] -> fresh
|
||||
* x := fresh1 ++ fresh ++ fresh2
|
||||
*
|
||||
* x udiv x', x sdiv x' -> fresh
|
||||
* x' := 1
|
||||
* x := fresh
|
||||
*
|
||||
* x ++ x' ++ x'' -> fresh
|
||||
* x := fresh[hi1:lo1]
|
||||
* x' := fresh[hi2:lo2]
|
||||
* x'' := fresh[hi3:lo3]
|
||||
*
|
||||
* x <= t -> fresh or t == MAX
|
||||
* x := if(fresh, t, t + 1)
|
||||
* t <= x -> fresh or t == MIN
|
||||
* x := if(fresh, t, t - 1)
|
||||
*
|
||||
* ~x -> fresh
|
||||
* x := ~fresh
|
||||
*
|
||||
* x | y -> fresh
|
||||
* x := fresh
|
||||
* y := 0
|
||||
*
|
||||
*/
|
||||
app * process_bv_app(func_decl * f, unsigned num, expr * const * args) {
|
||||
SASSERT(f->get_family_id() == m_bv_util.get_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
|
@ -646,6 +718,15 @@ class elim_uncnstr_tactic : public tactic {
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* F[select(x, i)] -> F[fresh]
|
||||
* x := const(fresh)
|
||||
|
||||
* F[store(x, ..., x')] -> F[fresh]
|
||||
* x' := select(x, ...)
|
||||
* x := fresh
|
||||
*/
|
||||
|
||||
app * process_array_app(func_decl * f, unsigned num, expr * const * args) {
|
||||
SASSERT(f->get_family_id() == m_ar_util.get_family_id());
|
||||
|
@ -676,7 +757,11 @@ class elim_uncnstr_tactic : public tactic {
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* head(x) -> fresh
|
||||
* x := cons(fresh, arb)
|
||||
*/
|
||||
app * process_datatype_app(func_decl * f, unsigned num, expr * const * args) {
|
||||
if (m_dt_util.is_accessor(f)) {
|
||||
SASSERT(num == 1);
|
||||
|
@ -771,9 +856,8 @@ class elim_uncnstr_tactic : public tactic {
|
|||
|
||||
void init_mc(bool produce_models) {
|
||||
m_mc = nullptr;
|
||||
if (produce_models) {
|
||||
m_mc = alloc(mc, m(), "elim_uncstr");
|
||||
}
|
||||
if (produce_models)
|
||||
m_mc = alloc(mc, m(), "elim_uncstr");
|
||||
}
|
||||
|
||||
void init_rw(bool produce_proofs) {
|
||||
|
@ -783,11 +867,12 @@ class elim_uncnstr_tactic : public tactic {
|
|||
void run(goal_ref const & g, goal_ref_buffer & result) {
|
||||
bool produce_proofs = g->proofs_enabled();
|
||||
TRACE("goal", g->display(tout););
|
||||
statistics_report sreport([&](statistics& st) { collect_statistics(st); });
|
||||
tactic_report report("elim-uncnstr", *g);
|
||||
m_vars.reset();
|
||||
collect_occs p;
|
||||
p(*g, m_vars);
|
||||
if (m_vars.empty() || recfun::util(m()).has_defs()) {
|
||||
if (m_vars.empty() || recfun::util(m()).has_rec_defs()) {
|
||||
result.push_back(g.get());
|
||||
// did not increase depth since it didn't do anything.
|
||||
return;
|
||||
|
@ -875,7 +960,6 @@ public:
|
|||
void operator()(goal_ref const & g,
|
||||
goal_ref_buffer & result) override {
|
||||
run(g, result);
|
||||
report_tactic_progress(":num-elim-apps", m_num_elim_apps);
|
||||
}
|
||||
|
||||
void cleanup() override {
|
||||
|
@ -885,7 +969,7 @@ public:
|
|||
}
|
||||
|
||||
void collect_statistics(statistics & st) const override {
|
||||
st.update("eliminated applications", m_num_elim_apps);
|
||||
st.update("elim-unconstrained", m_num_elim_apps);
|
||||
}
|
||||
|
||||
void reset_statistics() override {
|
||||
|
|
68
src/tactic/core/eliminate_predicates_tactic.h
Normal file
68
src/tactic/core/eliminate_predicates_tactic.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
eliminate_predicates_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for eliminating macros and predicates
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic elim-predicates
|
||||
|
||||
### Short Description
|
||||
Eliminates predicates and macros from a formula.
|
||||
|
||||
### Long Description
|
||||
The tactic subsumes the functionality of `macro-finder` and `quasi-macros`.
|
||||
Besides finding macros, it eliminates predicates using Davis-Putnam
|
||||
resolution.
|
||||
|
||||
### Example
|
||||
|
||||
the predicate `p` occurs once positively. All negative occurrences of `p` are resolved against this positive occurrence.
|
||||
The result of resolution is a set of equalities between arguments to `p`. The function `f` is replaced by a partial solution.
|
||||
|
||||
```
|
||||
(declare-fun f (Int Int Int) Int)
|
||||
(declare-fun p (Int) Bool)
|
||||
(declare-const a Int)
|
||||
(declare-const b Int)
|
||||
|
||||
(assert (forall ((x Int) (y Int)) (= (f x y (+ x y)) (* 2 x y))))
|
||||
(assert (p (f 8 a (+ a 8))))
|
||||
(assert (not (p (f 0 a (+ a 8)))))
|
||||
(assert (not (p (f 2 a (+ a 8)))))
|
||||
(assert (not (p (f 1 a (+ a b)))))
|
||||
(apply elim-predicates)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* support unsat cores
|
||||
* does not support proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "ast/simplifiers/eliminate_predicates.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
|
||||
inline tactic * mk_eliminate_predicates_tactic(ast_manager& m, params_ref const& p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(eliminate_predicates, m, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("elim-predicates", "eliminate predicates, macros and implicit definitions.", "mk_eliminate_predicates_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("elim-predicates", "eliminate predicates, macros and implicit definitions.", "alloc(eliminate_predicates, m, s)")
|
||||
*/
|
24
src/tactic/core/euf_completion_tactic.cpp
Normal file
24
src/tactic/core/euf_completion_tactic.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
euf_completion_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for simplifying with equations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
--*/
|
||||
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/core/euf_completion_tactic.h"
|
||||
|
||||
tactic * mk_euf_completion_tactic(ast_manager& m, params_ref const& p) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(euf::completion, m, s); });
|
||||
}
|
49
src/tactic/core/euf_completion_tactic.h
Normal file
49
src/tactic/core/euf_completion_tactic.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
euf_completion_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Tactic for simplifying with equations.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-10-30
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic euf-completion
|
||||
|
||||
### Short Description
|
||||
|
||||
Uses the ground equalities as a rewrite system. The formulas are simplified
|
||||
using the rewrite system.
|
||||
|
||||
### Long Description
|
||||
|
||||
The tactic uses congruence closure to represent and orient the rewrite system. Equalities from the formula
|
||||
are inserted in the an E-graph (congruence closure structure) and then a representative that is most shallow
|
||||
is extracted.
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/euf_completion.h"
|
||||
|
||||
class ast_manager;
|
||||
class tactic;
|
||||
|
||||
tactic * mk_euf_completion_tactic(ast_manager & m, params_ref const & p = params_ref());
|
||||
|
||||
/*
|
||||
ADD_TACTIC("euf-completion", "simplify using equalities.", "mk_euf_completion_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("euf-completion", "simplify modulo congruence closure.", "alloc(euf::completion, m, s)")
|
||||
*/
|
||||
|
||||
|
|
@ -5,19 +5,11 @@ Module Name:
|
|||
|
||||
injectivity_tactic.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Injectivity tactics
|
||||
- Discover axioms of the form `forall x. (= (g (f x)) x`
|
||||
Mark `f` as injective
|
||||
- Rewrite (sub)terms of the form `(= (f x) (f y))` to `(= x y)` whenever `f` is injective.
|
||||
|
||||
Author:
|
||||
|
||||
Nicolas Braud-Santoni (t-nibrau) 2017-08-10
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
@ -164,8 +156,6 @@ class injectivity_tactic : public tactic {
|
|||
struct rewriter_eq_cfg : public default_rewriter_cfg {
|
||||
ast_manager & m_manager;
|
||||
InjHelper & inj_map;
|
||||
// expr_ref_vector m_out;
|
||||
// sort_ref_vector m_bindings;
|
||||
|
||||
ast_manager & m() const { return m_manager; }
|
||||
|
||||
|
@ -176,14 +166,13 @@ class injectivity_tactic : public tactic {
|
|||
}
|
||||
|
||||
void cleanup_buffers() {
|
||||
// m_out.finalize();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
}
|
||||
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
if(num != 2)
|
||||
if (num != 2)
|
||||
return BR_FAILED;
|
||||
|
||||
if (!m().is_eq(f))
|
||||
|
@ -230,8 +219,6 @@ class injectivity_tactic : public tactic {
|
|||
finder * m_finder;
|
||||
rewriter_eq * m_eq;
|
||||
InjHelper * m_map;
|
||||
// rewriter_inverse * m_inverse;
|
||||
|
||||
params_ref m_params;
|
||||
ast_manager & m_manager;
|
||||
|
||||
|
|
|
@ -13,7 +13,33 @@ Author:
|
|||
|
||||
Nicolas Braud-Santoni (t-nibrau) 2017-08-10
|
||||
|
||||
Notes:
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic injectivity
|
||||
|
||||
### Short Description:
|
||||
|
||||
- Discover axioms of the form `forall x. (= (g (f x)) x`
|
||||
Mark `f` as injective
|
||||
|
||||
- Rewrite (sub)terms of the form `(= (f x) (f y))` to `(= x y)` whenever `f` is injective.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-fun f (Int) Int)
|
||||
(declare-fun g (Int) Int)
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(assert (forall ((x Int)) (= (g (f x)) x)))
|
||||
(assert (not (= (f x) (f (f y)))))
|
||||
(apply injectivity)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* does not support cores nor proofs
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -18,7 +18,7 @@ Revision History:
|
|||
--*/
|
||||
#include "ast/normal_forms/nnf.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class nnf_tactic : public tactic {
|
||||
params_ref m_params;
|
||||
|
|
|
@ -13,7 +13,46 @@ Author:
|
|||
|
||||
Leonardo de Moura (leonardo) 2011-12-28.
|
||||
|
||||
Revision History:
|
||||
Note:
|
||||
|
||||
tactic documentation below co-created using gptchat (with some corrections) :-)
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic nnf
|
||||
|
||||
### Short Description:
|
||||
|
||||
The tactic converts formulas to negation normal form (NNF)
|
||||
|
||||
### Long Description
|
||||
|
||||
In NNF, negations only appear in front of atomic formulas.
|
||||
|
||||
Standard rules for conversion into negation normal form are:
|
||||
- `(not (and p q))` is converted to `(or (not p) (not q))`
|
||||
- `(not (or p q))` is converted to `(and (not p) (not q))`
|
||||
- `(not (not p))` is converted to `p`
|
||||
- `(not (exists x. p))` is converted to `(forall x. (not p))`
|
||||
- `(not (forall x. p))` is converted to `(exists x. (not p))`
|
||||
|
||||
|
||||
Once all negations are pushed inside, the resulting formula is in NNF.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(assert (not (or (> x 0) (< x 0))))
|
||||
(apply nnf)
|
||||
```
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores, proof terms
|
||||
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -23,7 +23,7 @@ Revision History:
|
|||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/core/occf_tactic.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
class occf_tactic : public tactic {
|
||||
struct imp {
|
||||
|
|
|
@ -5,20 +5,42 @@ Module Name:
|
|||
|
||||
occf_tactic.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Put clauses in the assertion set in
|
||||
OOC (one constraint per clause) form.
|
||||
Constraints occurring in formulas that
|
||||
are not clauses are ignored.
|
||||
The formula can be put into CNF by
|
||||
using mk_sat_preprocessor strategy.
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2011-12-28.
|
||||
|
||||
Revision History:
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic occf
|
||||
|
||||
### Short Description
|
||||
|
||||
Put goal in one constraint per clause normal form
|
||||
|
||||
### Long Description
|
||||
|
||||
Put clauses in the assertion set in
|
||||
OOC (one constraint per clause) form.
|
||||
Constraints occurring in formulas that
|
||||
are not clauses are ignored.
|
||||
The formula can be put into CNF by
|
||||
using `mk_sat_preprocessor` strategy.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
|
||||
(assert (or (= x y) (> x (- y))))
|
||||
(assert (or (= x y) (< x (- y))))
|
||||
(apply occf)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* Does not support proofs
|
||||
* only clauses are considered
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
|
@ -14,26 +14,10 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2013-12-23
|
||||
|
||||
Notes:
|
||||
|
||||
Resolution for PB constraints require the implicit
|
||||
inequalities that each variable ranges over [0,1]
|
||||
so not all resolvents produce smaller sets of clauses.
|
||||
|
||||
We here implement subsumption resolution.
|
||||
|
||||
x + y >= 1
|
||||
A~x + B~y + Cz >= k
|
||||
---------------------
|
||||
Cz >= k - B
|
||||
|
||||
where A <= B, x, y do not occur elsewhere.
|
||||
|
||||
|
||||
--*/
|
||||
#include "tactic/core/pb_preprocess_tactic.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/pb_decl_plugin.h"
|
||||
#include "ast/rewriter/th_rewriter.h"
|
||||
|
@ -106,22 +90,20 @@ public:
|
|||
return alloc(pb_preprocess_tactic, m);
|
||||
}
|
||||
|
||||
char const* name() const override { return "pb_preprocess"; }
|
||||
char const* name() const override { return "pb-preprocess"; }
|
||||
|
||||
void operator()(
|
||||
goal_ref const & g,
|
||||
goal_ref_buffer & result) override {
|
||||
tactic_report report("pb-preprocess", *g);
|
||||
if (g->proofs_enabled()) {
|
||||
throw tactic_exception("pb-preprocess does not support proofs");
|
||||
}
|
||||
|
||||
generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess");
|
||||
|
||||
g->inc_depth();
|
||||
result.push_back(g.get());
|
||||
while (simplify(g, *pp));
|
||||
g->add(pp);
|
||||
|
||||
if (!g->proofs_enabled()) {
|
||||
generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess");
|
||||
while (simplify(g, *pp));
|
||||
g->add(pp);
|
||||
}
|
||||
|
||||
// decompose(g);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,51 @@ Author:
|
|||
|
||||
Nikolaj Bjorner (nbjorner) 2013-12-23
|
||||
|
||||
Notes:
|
||||
Documentation:
|
||||
|
||||
## Tactic pb-preprocess
|
||||
|
||||
### Short Description:
|
||||
|
||||
The tactic eliminates variables from pseudo-Boolean inequalities and performs algebraic simplifcations on formulas
|
||||
|
||||
### Long Description
|
||||
|
||||
Resolution for PB constraints require the implicit
|
||||
inequalities that each variable ranges over [0,1]
|
||||
so not all resolvents produce smaller sets of clauses.
|
||||
|
||||
We here implement subsumption resolution.
|
||||
|
||||
```
|
||||
x + y >= 1
|
||||
A~x + B~y + Cz >= k
|
||||
---------------------
|
||||
Cz >= k - B
|
||||
```
|
||||
|
||||
where `A <= B` and `x, y` do not occur elsewhere.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Bool)
|
||||
(declare-const y Bool)
|
||||
(declare-const z Bool)
|
||||
(declare-const u Bool)
|
||||
(declare-const v Bool)
|
||||
(assert ((_ pbge 1 1 1 2) (not x) (not y) (not z)))
|
||||
(assert ((_ pbge 1 1 1 2) x u v))
|
||||
(assert (not (and y v)))
|
||||
(assert (not (and z u)))
|
||||
(apply pb-preprocess)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores
|
||||
* does not support proof terms
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
|
59
src/tactic/core/propagate_values2_tactic.h
Normal file
59
src/tactic/core/propagate_values2_tactic.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*++
|
||||
Copyright (c) 2022 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
propagate_values2_tactic.h
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2022-11-24
|
||||
|
||||
Tactic Documentation:
|
||||
|
||||
## Tactic propagate-values
|
||||
|
||||
### Short Description:
|
||||
|
||||
Tactic for propagating equalities `(= t v)` where `v` is a value
|
||||
|
||||
### Long Description
|
||||
|
||||
In a context where terms are equated to constants it is invariably beneficial to
|
||||
replace terms, that can be compound, with the constants and then simplify the resulting formulas.
|
||||
The propagate-values tactic accomplishes the task of replacing such terms.
|
||||
|
||||
### Example
|
||||
|
||||
```z3
|
||||
(declare-const x Int)
|
||||
(declare-const y Int)
|
||||
(declare-fun f (Int) Int)
|
||||
(assert (= 1 (f (+ x y))))
|
||||
(assert (= 2 x))
|
||||
(assert (> (f (+ 2 y)) y))
|
||||
(apply propagate-values)
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
* supports unsat cores
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "util/params.h"
|
||||
#include "tactic/tactic.h"
|
||||
#include "tactic/dependent_expr_state_tactic.h"
|
||||
#include "ast/simplifiers/propagate_values.h"
|
||||
|
||||
inline tactic * mk_propagate_values2_tactic(ast_manager & m, params_ref const & p = params_ref()) {
|
||||
return alloc(dependent_expr_state_tactic, m, p,
|
||||
[](auto& m, auto& p, auto &s) -> dependent_expr_simplifier* { return alloc(propagate_values, m, p, s); });
|
||||
}
|
||||
|
||||
/*
|
||||
ADD_TACTIC("propagate-values2", "propagate constants.", "mk_propagate_values2_tactic(m, p)")
|
||||
ADD_SIMPLIFIER("propagate-values", "propagate constants.", "alloc(propagate_values, m, p, s)")
|
||||
*/
|
|
@ -24,7 +24,7 @@ Revision History:
|
|||
#include "ast/ast_pp.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "tactic/goal_shared_occs.h"
|
||||
#include "tactic/tactic_params.hpp"
|
||||
#include "params/tactic_params.hpp"
|
||||
|
||||
namespace {
|
||||
class propagate_values_tactic : public tactic {
|
||||
|
@ -74,10 +74,13 @@ class propagate_values_tactic : public tactic {
|
|||
|
||||
void push_result(expr * new_curr, proof * new_pr) {
|
||||
if (m_goal->proofs_enabled()) {
|
||||
proof * pr = m_goal->pr(m_idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
proof* pr = m_goal->pr(m_idx);
|
||||
new_pr = m.mk_modus_ponens(pr, new_pr);
|
||||
}
|
||||
|
||||
else
|
||||
new_pr = nullptr;
|
||||
|
||||
|
||||
expr_dependency_ref new_d(m);
|
||||
if (m_goal->unsat_core_enabled()) {
|
||||
new_d = m_goal->dep(m_idx);
|
||||
|
@ -210,6 +213,7 @@ public:
|
|||
m_occs(m, true /* track atoms */),
|
||||
m_params(p) {
|
||||
updt_params_core(p);
|
||||
m_r.set_flat_and_or(false);
|
||||
}
|
||||
|
||||
tactic * translate(ast_manager & m) override {
|
||||
|
@ -226,7 +230,7 @@ public:
|
|||
|
||||
void collect_param_descrs(param_descrs & r) override {
|
||||
th_rewriter::get_param_descrs(r);
|
||||
r.insert("max_rounds", CPK_UINT, "(default: 4) maximum number of rounds.");
|
||||
r.insert("max_rounds", CPK_UINT, "maximum number of rounds.", "4");
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & in, goal_ref_buffer & result) override {
|
||||
|
|
|
@ -18,11 +18,12 @@ Notes:
|
|||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/has_free_vars.h"
|
||||
#include "util/map.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/converters/generic_model_converter.h"
|
||||
|
||||
/**
|
||||
\brief Reduce the number of arguments in function applications.
|
||||
|
@ -397,10 +398,12 @@ struct reduce_args_tactic::imp {
|
|||
ptr_buffer<expr> new_args;
|
||||
var_ref_vector new_vars(m);
|
||||
ptr_buffer<expr> new_eqs;
|
||||
generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args");
|
||||
for (auto const& kv : decl2arg2funcs) {
|
||||
func_decl * f = kv.m_key;
|
||||
arg2func * map = kv.m_value;
|
||||
generic_model_converter * f_mc = alloc(generic_model_converter, m, "reduce_args");
|
||||
for (auto const& [f, map] : decl2arg2funcs)
|
||||
for (auto const& [t, new_def] : *map)
|
||||
f_mc->hide(new_def);
|
||||
|
||||
for (auto const& [f, map] : decl2arg2funcs) {
|
||||
expr * def = nullptr;
|
||||
SASSERT(decl2args.contains(f));
|
||||
bit_vector & bv = decl2args.find(f);
|
||||
|
@ -412,9 +415,8 @@ struct reduce_args_tactic::imp {
|
|||
new_args.push_back(new_vars.back());
|
||||
}
|
||||
for (auto const& [t, new_def] : *map) {
|
||||
f_mc->hide(new_def);
|
||||
SASSERT(new_def->get_arity() == new_args.size());
|
||||
app * new_t = m.mk_app(new_def, new_args.size(), new_args.data());
|
||||
app * new_t = m.mk_app(new_def, new_args);
|
||||
if (def == nullptr) {
|
||||
def = new_t;
|
||||
}
|
||||
|
@ -425,11 +427,7 @@ struct reduce_args_tactic::imp {
|
|||
new_eqs.push_back(m.mk_eq(new_vars.get(i), t->get_arg(i)));
|
||||
}
|
||||
SASSERT(new_eqs.size() > 0);
|
||||
expr * cond;
|
||||
if (new_eqs.size() == 1)
|
||||
cond = new_eqs[0];
|
||||
else
|
||||
cond = m.mk_and(new_eqs.size(), new_eqs.data());
|
||||
expr * cond = mk_and(m, new_eqs);
|
||||
def = m.mk_ite(cond, new_t, def);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue