3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 17:15:31 +00:00

adding pre-processing of BP constraints

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2013-12-23 01:33:24 -08:00
parent 670f56e5e4
commit 24f2fd380c
9 changed files with 679 additions and 281 deletions

View file

@ -83,18 +83,18 @@ public:
app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
bool is_at_most_k(func_decl *a) const;
bool is_at_most_k(app *a) const { return is_at_most_k(a->get_decl()); }
bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); }
bool is_at_most_k(app *a, rational& k) const;
bool is_at_least_k(func_decl *a) const;
bool is_at_least_k(app *a) const { return is_at_most_k(a->get_decl()); }
bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); }
bool is_at_least_k(app *a, rational& k) const;
rational get_k(func_decl *a) const;
rational get_k(app *a) const { return get_k(a->get_decl()); }
bool is_le(func_decl *a) const;
bool is_le(app *a) const { return is_le(a->get_decl()); }
bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); }
bool is_le(app* a, rational& k) const;
bool is_ge(func_decl* a) const;
bool is_ge(app* a) const { return is_ge(a->get_decl()); }
bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); }
bool is_ge(app* a, rational& k) const;
rational get_coeff(app* a, unsigned index) const { return get_coeff(a->get_decl(), index); }
rational get_coeff(func_decl* a, unsigned index) const;

View file

@ -18,6 +18,60 @@ Notes:
--*/
#include "pb_rewriter.h"
#include "pb_rewriter_def.h"
#include "ast_pp.h"
class pb_ast_rewriter_util {
ast_manager& m;
expr_ref_vector m_refs;
public:
typedef std::pair<expr*, rational> arg_t;
typedef vector<arg_t> args_t;
typedef rational numeral;
pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {}
expr* negate(expr* e) {
if (m.is_true(e)) {
return m.mk_false();
}
if (m.is_false(e)) {
return m.mk_true();
}
if (m.is_not(e, e)) {
return e;
}
m_refs.push_back(m.mk_not(e));
return m_refs.back();
}
void display(std::ostream& out, expr* e) {
out << mk_pp(e, m);
}
bool is_negated(expr* e) const {
return m.is_not(e);
}
bool is_true(expr* e) const {
return m.is_true(e);
}
bool is_false(expr* e) const {
return m.is_false(e);
}
struct compare {
bool operator()(std::pair<expr*,rational> const& a,
std::pair<expr*,rational> const& b) {
return a.first->get_id() < b.first->get_id();
}
};
};
br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
ast_manager& m = result.get_manager();
@ -32,34 +86,59 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
}
}
rational k = m_util.get_k(f);
vector<std::pair<expr*,rational> > vec;
for (unsigned i = 0; i < num_args; ++i) {
vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i)));
}
switch(f->get_decl_kind()) {
case OP_AT_MOST_K:
case OP_PB_LE:
if (sum > k) {
result = m.mk_false();
return BR_DONE;
for (unsigned i = 0; i < num_args; ++i) {
vec[i].second.neg();
}
if (maxsum <= k) {
result = m.mk_true();
return BR_DONE;
}
return BR_FAILED;
k.neg();
break;
case OP_AT_LEAST_K:
case OP_PB_GE:
if (sum >= k) {
result = m.mk_true();
return BR_DONE;
}
if (maxsum < k) {
result = m.mk_false();
return BR_DONE;
}
return BR_FAILED;
break;
default:
UNREACHABLE();
return BR_FAILED;
}
pb_ast_rewriter_util pbu(m);
pb_rewriter_util<pb_ast_rewriter_util> util(pbu);
util.unique(vec, k);
lbool is_sat = util.normalize(vec, k);
util.prune(vec, k);
switch (is_sat) {
case l_true:
result = m.mk_true();
break;
case l_false:
result = m.mk_false();
break;
default:
m_args.reset();
m_coeffs.reset();
for (unsigned i = 0; i < vec.size(); ++i) {
m_args.push_back(vec[i].first);
m_coeffs.push_back(vec[i].second);
}
result = m_util.mk_ge(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k);
break;
}
TRACE("pb",
expr_ref tmp(m);
tmp = m.mk_app(f, num_args, args);
tout << tmp << "\n";
tout << result << "\n";);
return BR_DONE;
}

View file

@ -22,12 +22,27 @@ Notes:
#include"pb_decl_plugin.h"
#include"rewriter_types.h"
#include"params.h"
#include"lbool.h"
template<typename PBU>
class pb_rewriter_util {
PBU& m_util;
void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k);
public:
pb_rewriter_util(PBU& u) : m_util(u) {}
void unique(typename PBU::args_t& args, typename PBU::numeral& k);
lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k);
void prune(typename PBU::args_t& args, typename PBU::numeral& k);
};
/**
\brief Cheap rewrite rules for PB constraints
*/
class pb_rewriter {
pb_util m_util;
vector<rational> m_coeffs;
ptr_vector<expr> m_args;
public:
pb_rewriter(ast_manager & m, params_ref const & p = params_ref()):
m_util(m) {

View file

@ -0,0 +1,263 @@
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
pb_rewriter_def.h
Abstract:
Basic rewriting rules for PB constraints.
Author:
Nikolaj Bjorner (nbjorner) 2013-14-12
Notes:
--*/
#ifndef _PB_REWRITER_DEF_H_
#define _PB_REWRITER_DEF_H_
#include"pb_rewriter.h"
template<typename PBU>
void pb_rewriter_util<PBU>::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k) {
for (unsigned i = 0; i < args.size(); ++i) {
out << args[i].second << " * ";
m_util.display(out, args[i].first);
out << " ";
if (i+1 < args.size()) out << "+ ";
}
out << " >= " << k << "\n";
}
template<typename PBU>
void pb_rewriter_util<PBU>::unique(typename PBU::args_t& args, typename PBU::numeral& k) {
TRACE("pb", display(tout << "pre-unique:", args, k););
for (unsigned i = 0; i < args.size(); ++i) {
if (m_util.is_negated(args[i].first)) {
args[i].first = m_util.negate(args[i].first);
k -= args[i].second;
args[i].second = -args[i].second;
}
}
// remove constants
for (unsigned i = 0; i < args.size(); ++i) {
if (m_util.is_true(args[i].first)) {
k -= args[i].second;
std::swap(args[i], args[args.size()-1]);
args.pop_back();
--i;
}
else if (m_util.is_false(args[i].first)) {
std::swap(args[i], args[args.size()-1]);
args.pop_back();
--i;
}
}
// sort and coalesce arguments:
PBU::compare cmp;
std::sort(args.begin(), args.end(), cmp);
unsigned i = 0, j = 1;
for (; j < args.size(); ++i) {
SASSERT(j > i);
for (; j < args.size() && args[j].first == args[i].first; ++j) {
args[i].second += args[j].second;
}
if (j < args.size()) {
args[i+1].first = args[j].first;
args[i+1].second = args[j].second;
++j;
}
}
if (i + 1 < args.size()) {
args.resize(i+1);
}
TRACE("pb", display(tout << "post-unique:", args, k););
}
template<typename PBU>
lbool pb_rewriter_util<PBU>::normalize(typename PBU::args_t& args, typename PBU::numeral& k) {
TRACE("pb", display(tout << "pre-normalize:", args, k););
//
// Ensure all coefficients are positive:
// c*l + y >= k
// <=>
// c*(1-~l) + y >= k
// <=>
// c - c*~l + y >= k
// <=>
// -c*~l + y >= k - c
//
PBU::numeral sum(0);
for (unsigned i = 0; i < args.size(); ++i) {
PBU::numeral c = args[i].second;
if (c.is_neg()) {
args[i].second = -c;
args[i].first = m_util.negate(args[i].first);
k -= c;
}
sum += args[i].second;
}
// detect tautologies:
if (k <= PBU::numeral::zero()) {
args.reset();
k = PBU::numeral::zero();
return l_true;
}
// detect infeasible constraints:
if (sum < k) {
args.reset();
k = PBU::numeral::one();
return l_false;
}
bool all_int = true;
for (unsigned i = 0; all_int && i < args.size(); ++i) {
all_int = args[i].second.is_int();
}
if (!all_int) {
// normalize to integers.
PBU::numeral d(denominator(k));
for (unsigned i = 0; i < args.size(); ++i) {
d = lcm(d, denominator(args[i].second));
}
SASSERT(!d.is_one());
k *= d;
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second *= d;
}
}
// Ensure the largest coefficient is not larger than k:
sum = PBU::numeral::zero();
for (unsigned i = 0; i < args.size(); ++i) {
PBU::numeral c = args[i].second;
if (c > k) {
args[i].second = k;
}
sum += args[i].second;
}
SASSERT(!args.empty());
// normalize tight inequalities to unit coefficients.
if (sum == k) {
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = PBU::numeral::one();
}
k = PBU::numeral(args.size());
}
// apply cutting plane reduction:
PBU::numeral g(0);
for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) {
PBU::numeral c = args[i].second;
if (c != k) {
if (g.is_zero()) {
g = c;
}
else {
g = gcd(g, c);
}
}
}
if (g.is_zero()) {
// all coefficients are equal to k.
for (unsigned i = 0; i < args.size(); ++i) {
SASSERT(args[i].second == k);
args[i].second = PBU::numeral::one();
}
k = PBU::numeral::one();
}
else if (g > PBU::numeral::one()) {
IF_VERBOSE(3, verbose_stream() << "cut " << g << "\n";
display(verbose_stream(), args, k);
);
//
// Example 5x + 5y + 2z + 2u >= 5
// becomes 3x + 3y + z + u >= 3
//
PBU::numeral k_new = div(k, g);
if (!(k % g).is_zero()) { // k_new is the ceiling of k / g.
k_new++;
}
for (unsigned i = 0; i < args.size(); ++i) {
SASSERT(args[i].second.is_pos());
PBU::numeral c = args[i].second;
if (c == k) {
c = k_new;
}
else {
c = div(c, g);
}
args[i].second = c;
SASSERT(args[i].second.is_pos());
}
k = k_new;
}
//
// normalize coefficients that fall within a range
// k/n <= ... < k/(n-1) for some n = 1,2,...
//
// e.g, k/n <= min <= max < k/(n-1)
// k/min <= n, n-1 < k/max
// . floor(k/max) = ceil(k/min) - 1
// . floor(k/max) < k/max
//
// example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2
// replace all coefficients by 1, and k by 2.
//
if (!k.is_one()) {
PBU::numeral min = args[0].second, max = args[0].second;
for (unsigned i = 1; i < args.size(); ++i) {
if (args[i].second < min) min = args[i].second;
if (args[i].second > max) max = args[i].second;
}
PBU::numeral n0 = k/max;
PBU::numeral n1 = floor(n0);
PBU::numeral n2 = ceil(k/min) - PBU::numeral::one();
if (n1 == n2 && !n0.is_int()) {
IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k););
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = PBU::numeral::one();
}
k = n1 + PBU::numeral::one();
}
}
return l_undef;
}
template<typename PBU>
void pb_rewriter_util<PBU>::prune(typename PBU::args_t& args, typename PBU::numeral& k) {
PBU::numeral nlt(0);
unsigned occ = 0;
for (unsigned i = 0; nlt < k && i < args.size(); ++i) {
if (args[i].second < k) {
nlt += args[i].second;
++occ;
}
}
if (0 < occ && nlt < k) {
for (unsigned i = 0; i < args.size(); ++i) {
if (args[i].second < k) {
args[i] = args.back();
args.pop_back();
--i;
}
}
normalize(args, k);
}
}
#endif