3
0
Fork 0
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:
Jakob Rath 2023-02-01 16:28:57 +01:00
commit 20b5455d08
669 changed files with 26145 additions and 20652 deletions

View file

@ -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
)

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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"

View file

@ -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";
}
}

View file

@ -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;
};

View file

@ -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);
}

View file

@ -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; }
};

View 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)")
*/

View file

@ -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));
}

View file

@ -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)")
*/

View file

@ -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"

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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");
}

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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););

View file

@ -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

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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");
}
/**

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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)")
*/

View file

@ -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);
}

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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);

View file

@ -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,

View file

@ -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"

View file

@ -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

View file

@ -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; }

View file

@ -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

View file

@ -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);
}

View file

@ -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)")
*/

View file

@ -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 {

View file

@ -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

View 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)")
*/

View file

@ -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;

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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));
}

View file

@ -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)")
*/

View file

@ -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);
}
};

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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";);
}

View file

@ -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

View file

@ -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());

View file

@ -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,

View file

@ -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

View 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)")
*/

View file

@ -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

View file

@ -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);
}

View file

@ -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)")
*/

View file

@ -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));
}

View file

@ -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)")
*/

View file

@ -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 {

View file

@ -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

View 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)")
*/

View file

@ -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 {

View 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)")
*/

View 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); });
}

View 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)")
*/

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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);
}

View file

@ -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

View 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)")
*/

View file

@ -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 {

View file

@ -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