3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 09:55:19 +00:00
z3/src/muz/dl_mk_karr_invariants.cpp
Nikolaj Bjorner 137339a2e1 split muz_qe into two directories
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2013-08-28 12:08:47 -07:00

1115 lines
38 KiB
C++

/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
dl_mk_karr_invariants.cpp
Abstract:
Extract integer linear invariants.
The linear invariants are extracted according to Karr's method.
A short description is in
Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation
of Invariants and Intermediate Assertions, in CP 95.
The algorithm is here adapted to Horn clauses.
The idea is to maintain two data-structures for each recursive relation.
We call them R and RD
- R - set of linear congruences that are true of R.
- RD - the dual basis of of solutions for R.
RD is updated by accumulating basis vectors for solutions
to R (the homogeneous dual of R)
R is updated from the inhomogeneous dual of RD.
Author:
Nikolaj Bjorner (nbjorner) 2013-03-09
Revision History:
--*/
#include"dl_mk_karr_invariants.h"
#include"expr_safe_replace.h"
#include"bool_rewriter.h"
#include"dl_mk_backwards.h"
#include"dl_mk_loop_counter.h"
#include "for_each_expr.h"
namespace datalog {
mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority):
rule_transformer::plugin(priority, false),
m_ctx(ctx),
m(ctx.get_manager()),
rm(ctx.get_rule_manager()),
m_inner_ctx(m, ctx.get_fparams()),
a(m),
m_pinned(m),
m_cancel(false) {
params_ref params;
params.set_sym("default_relation", symbol("karr_relation"));
params.set_sym("engine", symbol("datalog"));
params.set_bool("karr", false);
m_inner_ctx.updt_params(params);
}
mk_karr_invariants::~mk_karr_invariants() { }
matrix& matrix::operator=(matrix const& other) {
reset();
append(other);
return *this;
}
void matrix::display_row(
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq) {
for (unsigned j = 0; j < row.size(); ++j) {
out << row[j] << " ";
}
out << (is_eq?" = ":" >= ") << -b << "\n";
}
void matrix::display_ineq(
std::ostream& out, vector<rational> const& row, rational const& b, bool is_eq) {
bool first = true;
for (unsigned j = 0; j < row.size(); ++j) {
if (!row[j].is_zero()) {
if (!first && row[j].is_pos()) {
out << "+ ";
}
if (row[j].is_minus_one()) {
out << "- ";
}
if (row[j] > rational(1) || row[j] < rational(-1)) {
out << row[j] << "*";
}
out << "x" << j << " ";
first = false;
}
}
out << (is_eq?"= ":">= ") << -b << "\n";
}
void matrix::display(std::ostream& out) const {
for (unsigned i = 0; i < A.size(); ++i) {
display_row(out, A[i], b[i], eq[i]);
}
}
class mk_karr_invariants::add_invariant_model_converter : public model_converter {
ast_manager& m;
arith_util a;
func_decl_ref_vector m_funcs;
expr_ref_vector m_invs;
public:
add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {}
virtual ~add_invariant_model_converter() { }
void add(func_decl* p, expr* inv) {
if (!m.is_true(inv)) {
m_funcs.push_back(p);
m_invs.push_back(inv);
}
}
virtual void operator()(model_ref & mr) {
for (unsigned i = 0; i < m_funcs.size(); ++i) {
func_decl* p = m_funcs[i].get();
func_interp* f = mr->get_func_interp(p);
expr_ref body(m);
unsigned arity = p->get_arity();
SASSERT(0 < arity);
if (f) {
SASSERT(f->num_entries() == 0);
if (!f->is_partial()) {
bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body);
}
}
else {
f = alloc(func_interp, m, arity);
mr->register_decl(p, f);
body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible.
}
f->set_else(body);
}
}
virtual model_converter * translate(ast_translation & translator) {
add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m);
for (unsigned i = 0; i < m_funcs.size(); ++i) {
mc->add(translator(m_funcs[i].get()), m_invs[i].get());
}
return mc;
}
private:
void mk_body(matrix const& M, expr_ref& body) {
expr_ref_vector conj(m);
for (unsigned i = 0; i < M.size(); ++i) {
mk_body(M.A[i], M.b[i], M.eq[i], conj);
}
bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body);
}
void mk_body(vector<rational> const& row, rational const& b, bool is_eq, expr_ref_vector& conj) {
expr_ref_vector sum(m);
expr_ref zero(m), lhs(m);
zero = a.mk_numeral(rational(0), true);
for (unsigned i = 0; i < row.size(); ++i) {
if (row[i].is_zero()) {
continue;
}
var* var = m.mk_var(i, a.mk_int());
if (row[i].is_one()) {
sum.push_back(var);
}
else {
sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var));
}
}
if (!b.is_zero()) {
sum.push_back(a.mk_numeral(b, true));
}
lhs = a.mk_add(sum.size(), sum.c_ptr());
if (is_eq) {
conj.push_back(m.mk_eq(lhs, zero));
}
else {
conj.push_back(a.mk_ge(lhs, zero));
}
}
};
void mk_karr_invariants::cancel() {
m_cancel = true;
m_inner_ctx.cancel();
}
rule_set * mk_karr_invariants::operator()(rule_set const & source) {
if (!m_ctx.get_params().karr()) {
return 0;
}
rule_set::iterator it = source.begin(), end = source.end();
for (; it != end; ++it) {
rule const& r = **it;
if (r.has_negation()) {
return 0;
}
}
mk_loop_counter lc(m_ctx);
mk_backwards bwd(m_ctx);
scoped_ptr<rule_set> src_loop = lc(source);
TRACE("dl", src_loop->display(tout << "source loop\n"););
get_invariants(*src_loop);
if (m_cancel) {
return 0;
}
// figure out whether to update same rules as used for saturation.
scoped_ptr<rule_set> rev_source = bwd(*src_loop);
get_invariants(*rev_source);
scoped_ptr<rule_set> src_annot = update_rules(*src_loop);
rule_set* rules = lc.revert(*src_annot);
rules->inherit_predicates(source);
TRACE("dl", rules->display(tout););
m_pinned.reset();
m_fun2inv.reset();
return rules;
}
void mk_karr_invariants::get_invariants(rule_set const& src) {
m_inner_ctx.reset();
rel_context& rctx = *m_inner_ctx.get_rel_context();
ptr_vector<func_decl> heads;
func_decl_set const& predicates = m_ctx.get_predicates();
for (func_decl_set::iterator fit = predicates.begin(); fit != predicates.end(); ++fit) {
m_inner_ctx.register_predicate(*fit, false);
}
m_inner_ctx.ensure_opened();
m_inner_ctx.replace_rules(src);
m_inner_ctx.close();
rule_set::decl2rules::iterator dit = src.begin_grouped_rules();
rule_set::decl2rules::iterator dend = src.end_grouped_rules();
for (; dit != dend; ++dit) {
heads.push_back(dit->m_key);
}
m_inner_ctx.rel_query(heads.size(), heads.c_ptr());
// retrieve invariants.
dit = src.begin_grouped_rules();
for (; dit != dend; ++dit) {
func_decl* p = dit->m_key;
relation_base* rb = rctx.try_get_relation(p);
if (rb) {
expr_ref fml(m);
rb->to_formula(fml);
if (m.is_true(fml)) {
continue;
}
expr* inv = 0;
if (m_fun2inv.find(p, inv)) {
fml = m.mk_and(inv, fml);
}
m_pinned.push_back(fml);
m_fun2inv.insert(p, fml);
}
}
}
rule_set* mk_karr_invariants::update_rules(rule_set const& src) {
scoped_ptr<rule_set> dst = alloc(rule_set, m_ctx);
rule_set::iterator it = src.begin(), end = src.end();
for (; it != end; ++it) {
update_body(*dst, **it);
}
if (m_ctx.get_model_converter()) {
add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m);
rule_set::decl2rules::iterator git = src.begin_grouped_rules();
rule_set::decl2rules::iterator gend = src.end_grouped_rules();
for (; git != gend; ++git) {
func_decl* p = git->m_key;
expr* fml = 0;
if (m_fun2inv.find(p, fml)) {
kmc->add(p, fml);
}
}
m_ctx.add_model_converter(kmc);
}
dst->inherit_predicates(src);
return dst.detach();
}
void mk_karr_invariants::update_body(rule_set& rules, rule& r) {
unsigned utsz = r.get_uninterpreted_tail_size();
unsigned tsz = r.get_tail_size();
app_ref_vector tail(m);
expr_ref fml(m);
for (unsigned i = 0; i < tsz; ++i) {
tail.push_back(r.get_tail(i));
}
for (unsigned i = 0; i < utsz; ++i) {
func_decl* q = r.get_decl(i);
expr* fml = 0;
if (m_fun2inv.find(q, fml)) {
expr_safe_replace rep(m);
for (unsigned j = 0; j < q->get_arity(); ++j) {
rep.insert(m.mk_var(j, q->get_domain(j)),
r.get_tail(i)->get_arg(j));
}
expr_ref tmp(fml, m);
rep(tmp);
tail.push_back(to_app(tmp));
}
}
rule* new_rule = &r;
if (tail.size() != tsz) {
new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name());
}
rules.add_rule(new_rule);
rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule.
}
class karr_relation : public relation_base {
friend class karr_relation_plugin;
friend class karr_relation_plugin::filter_equal_fn;
karr_relation_plugin& m_plugin;
ast_manager& m;
mutable arith_util a;
func_decl_ref m_fn;
mutable bool m_empty;
mutable matrix m_ineqs;
mutable bool m_ineqs_valid;
mutable matrix m_basis;
mutable bool m_basis_valid;
public:
karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty):
relation_base(p, s),
m_plugin(p),
m(p.get_ast_manager()),
a(m),
m_fn(f, m),
m_empty(is_empty),
m_ineqs_valid(!is_empty),
m_basis_valid(false)
{
}
virtual bool empty() const {
return m_empty;
}
virtual bool is_precise() const { return false; }
virtual void add_fact(const relation_fact & f) {
SASSERT(m_empty);
SASSERT(!m_basis_valid);
m_empty = false;
m_ineqs_valid = true;
for (unsigned i = 0; i < f.size(); ++i) {
rational n;
if (a.is_numeral(f[i], n) && n.is_int()) {
vector<rational> row;
row.resize(f.size());
row[i] = rational(1);
m_ineqs.A.push_back(row);
m_ineqs.b.push_back(-n);
m_ineqs.eq.push_back(true);
}
}
}
virtual bool contains_fact(const relation_fact & f) const {
UNREACHABLE();
return false;
}
virtual void display(std::ostream & out) const {
if (m_fn) {
out << m_fn->get_name() << "\n";
}
if (empty()) {
out << "empty\n";
}
else {
if (m_ineqs_valid) {
m_ineqs.display(out << "ineqs:\n");
}
if (m_basis_valid) {
m_basis.display(out << "basis:\n");
}
}
}
virtual karr_relation * clone() const {
karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty);
result->copy(*this);
return result;
}
virtual karr_relation * complement(func_decl*) const {
UNREACHABLE();
return 0;
}
virtual void to_formula(expr_ref& fml) const {
if (empty()) {
fml = m.mk_false();
}
else {
matrix const& M = get_ineqs();
expr_ref_vector conj(m);
for (unsigned i = 0; i < M.size(); ++i) {
to_formula(M.A[i], M.b[i], M.eq[i], conj);
}
bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml);
}
}
karr_relation_plugin& get_plugin() const { return m_plugin; }
void filter_interpreted(app* cond) {
rational one(1), mone(-1);
expr* e1, *e2, *en;
var* v, *w;
rational n1, n2;
expr_ref_vector conjs(m);
qe::flatten_and(cond, conjs);
matrix& M = get_ineqs();
unsigned num_columns = get_signature().size();
for (unsigned i = 0; i < conjs.size(); ++i) {
expr* e = conjs[i].get();
rational b(0);
vector<rational> row;
row.resize(num_columns, rational(0));
bool processed = true;
if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) {
M.A.push_back(row);
M.b.push_back(b);
M.eq.push_back(true);
}
else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) &&
is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
M.A.push_back(row);
M.b.push_back(b);
M.eq.push_back(false);
}
else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) &&
is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
M.A.push_back(row);
M.b.push_back(b - rational(1));
M.eq.push_back(false);
}
else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) &&
is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
M.A.push_back(row);
M.b.push_back(b);
M.eq.push_back(false);
}
else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) &&
is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) {
M.A.push_back(row);
M.b.push_back(b - rational(1));
M.eq.push_back(false);
}
else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) {
if (n1 > n2) {
std::swap(n1, n2);
}
SASSERT(n1 <= n2);
row[v->get_idx()] = rational(1);
// v - n1 >= 0
M.A.push_back(row);
M.b.push_back(-n1);
M.eq.push_back(false);
// -v + n2 >= 0
row[v->get_idx()] = rational(-1);
M.A.push_back(row);
M.b.push_back(n2);
M.eq.push_back(false);
}
else {
processed = false;
}
TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n";
if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back());
);
}
TRACE("dl", display(tout););
}
void mk_join(karr_relation const& r1, karr_relation const& r2,
unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) {
if (r1.empty() || r2.empty()) {
m_empty = true;
return;
}
matrix const& M1 = r1.get_ineqs();
matrix const& M2 = r2.get_ineqs();
unsigned sig1_size = r1.get_signature().size();
unsigned sig_size = get_signature().size();
m_ineqs.reset();
for (unsigned i = 0; i < M1.size(); ++i) {
vector<rational> row;
row.append(M1.A[i]);
row.resize(sig_size);
m_ineqs.A.push_back(row);
m_ineqs.b.push_back(M1.b[i]);
m_ineqs.eq.push_back(M1.eq[i]);
}
for (unsigned i = 0; i < M2.size(); ++i) {
vector<rational> row;
row.resize(sig_size);
for (unsigned j = 0; j < M2.A[i].size(); ++j) {
row[sig1_size + j] = M2.A[i][j];
}
m_ineqs.A.push_back(row);
m_ineqs.b.push_back(M2.b[i]);
m_ineqs.eq.push_back(M2.eq[i]);
}
for (unsigned i = 0; i < col_cnt; ++i) {
vector<rational> row;
row.resize(sig_size);
row[cols1[i]] = rational(1);
row[sig1_size + cols2[i]] = rational(-1);
m_ineqs.A.push_back(row);
m_ineqs.b.push_back(rational(0));
m_ineqs.eq.push_back(true);
}
m_ineqs_valid = true;
m_basis_valid = false;
m_empty = false;
if (r1.m_fn) {
m_fn = r1.m_fn;
}
if (r2.m_fn) {
m_fn = r2.m_fn;
}
}
void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) {
if (r.m_empty) {
m_empty = true;
return;
}
matrix const& M = r.get_basis();
m_basis.reset();
for (unsigned i = 0; i < M.size(); ++i) {
vector<rational> row;
unsigned k = 0;
for (unsigned j = 0; j < M.A[i].size(); ++j) {
if (k < cnt && j == cols[k]) {
++k;
}
else {
row.push_back(M.A[i][j]);
}
}
SASSERT(row.size() + cnt == M.A[i].size());
SASSERT(M.eq[i]);
m_basis.A.push_back(row);
m_basis.b.push_back(M.b[i]);
m_basis.eq.push_back(true);
}
m_basis_valid = true;
m_ineqs_valid = false;
m_empty = false;
m_fn = r.m_fn;
TRACE("dl",
for (unsigned i = 0; i < cnt; ++i) {
tout << cols[i] << " ";
}
tout << "\n";
r.display(tout);
display(tout););
}
void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) {
if (r.empty()) {
m_empty = true;
return;
}
m_ineqs.reset();
m_basis.reset();
m_ineqs_valid = r.m_ineqs_valid;
m_basis_valid = r.m_basis_valid;
if (m_ineqs_valid) {
m_ineqs.append(r.m_ineqs);
mk_rename(m_ineqs, col_cnt, cols);
}
if (m_basis_valid) {
m_basis.append(r.m_basis);
mk_rename(m_basis, col_cnt, cols);
}
m_fn = r.m_fn;
TRACE("dl", r.display(tout); display(tout););
}
void mk_union(karr_relation const& src, karr_relation* delta) {
if (src.empty()) {
if (delta) {
delta->m_empty = true;
}
return;
}
matrix const& M = src.get_basis();
if (empty()) {
m_basis = M;
m_basis_valid = true;
m_empty = false;
m_ineqs_valid = false;
if (delta) {
delta->copy(*this);
}
return;
}
matrix& N = get_basis();
unsigned N_size = N.size();
for (unsigned i = 0; i < M.size(); ++i) {
bool found = false;
for (unsigned j = 0; !found && j < N_size; ++j) {
found =
same_row(M.A[i], N.A[j]) &&
M.b[i] == N.b[j] &&
M.eq[i] == N.eq[j];
}
if (!found) {
N.A.push_back(M.A[i]);
N.b.push_back(M.b[i]);
N.eq.push_back(M.eq[i]);
}
}
m_ineqs_valid = false;
if (N_size != N.size()) {
if (delta) {
delta->copy(*this);
}
}
}
matrix const& get_basis() const {
init_basis();
return m_basis;
}
matrix& get_basis() {
init_basis();
return m_basis;
}
matrix const& get_ineqs() const {
init_ineqs();
return m_ineqs;
}
matrix & get_ineqs() {
init_ineqs();
return m_ineqs;
}
private:
void copy(karr_relation const& other) {
m_ineqs = other.m_ineqs;
m_basis = other.m_basis;
m_basis_valid = other.m_basis_valid;
m_ineqs_valid = other.m_ineqs_valid;
m_empty = other.m_empty;
}
bool same_row(vector<rational> const& r1, vector<rational> const& r2) const {
SASSERT(r1.size() == r2.size());
for (unsigned i = 0; i < r1.size(); ++i) {
if (r1[i] != r2[i]) {
return false;
}
}
return true;
}
void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) {
for (unsigned j = 0; j < M.size(); ++j) {
vector<rational> & row = M.A[j];
rational tmp = row[cols[0]];
for (unsigned i = 0; i + 1 < col_cnt; ++i) {
row[cols[i]] = row[cols[i+1]];
}
row[cols[col_cnt-1]] = tmp;
}
}
bool is_eq(expr* e, var*& v, rational& n) {
expr* e1, *e2;
if (!m.is_eq(e, e1, e2)) {
return false;
}
if (!is_var(e1)) {
std::swap(e1, e2);
}
if (!is_var(e1)) {
return false;
}
v = to_var(e1);
if (!a.is_numeral(e2, n)) {
return false;
}
return true;
}
bool is_linear(expr* e, vector<rational>& row, rational& b, rational const& mul) {
if (!a.is_int(e)) {
return false;
}
if (is_var(e)) {
row[to_var(e)->get_idx()] += mul;
return true;
}
if (!is_app(e)) {
return false;
}
rational n;
if (a.is_numeral(e, n)) {
b += mul*n;
return true;
}
if (a.is_add(e)) {
for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) {
if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) {
return false;
}
}
return true;
}
expr* e1, *e2;
if (a.is_sub(e, e1, e2)) {
return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul);
}
if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) {
return is_linear(e2, row, b, mul*n);
}
if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) {
return is_linear(e1, row, b, mul*n);
}
if (a.is_uminus(e, e1)) {
return is_linear(e1, row, b, -mul);
}
return false;
}
void init_ineqs() const {
if (!m_ineqs_valid) {
SASSERT(m_basis_valid);
m_plugin.dualizeH(m_ineqs, m_basis);
m_ineqs_valid = true;
}
}
void init_basis() const {
if (!m_basis_valid) {
SASSERT(m_ineqs_valid);
if (m_plugin.dualizeI(m_basis, m_ineqs)) {
m_basis_valid = true;
}
else {
m_empty = true;
}
}
}
void to_formula(vector<rational> const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const {
expr_ref_vector sum(m);
expr_ref zero(m), lhs(m);
zero = a.mk_numeral(rational(0), true);
for (unsigned i = 0; i < row.size(); ++i) {
if (row[i].is_zero()) {
continue;
}
var* var = m.mk_var(i, a.mk_int());
if (row[i].is_one()) {
sum.push_back(var);
}
else {
sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var));
}
}
if (!b.is_zero()) {
sum.push_back(a.mk_numeral(b, true));
}
lhs = a.mk_add(sum.size(), sum.c_ptr());
if (is_eq) {
conj.push_back(m.mk_eq(lhs, zero));
}
else {
conj.push_back(a.mk_ge(lhs, zero));
}
}
};
karr_relation& karr_relation_plugin::get(relation_base& r) {
return dynamic_cast<karr_relation&>(r);
}
karr_relation const & karr_relation_plugin::get(relation_base const& r) {
return dynamic_cast<karr_relation const&>(r);
}
void karr_relation_plugin::set_cancel(bool f) {
m_hb.set_cancel(f);
}
relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) {
return alloc(karr_relation, *this, 0, s, true);
}
relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) {
return alloc(karr_relation, *this, p, s, false);
}
class karr_relation_plugin::join_fn : public convenient_relation_join_fn {
public:
join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt,
const unsigned * cols1, const unsigned * cols2)
: convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){
}
virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) {
karr_relation const& r1 = get(_r1);
karr_relation const& r2 = get(_r2);
karr_relation_plugin& p = r1.get_plugin();
karr_relation* result = dynamic_cast<karr_relation*>(p.mk_full(0, get_result_signature()));
result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr());
return result;
}
};
relation_join_fn * karr_relation_plugin::mk_join_fn(
const relation_base & t1, const relation_base & t2,
unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) {
if (!check_kind(t1) || !check_kind(t2)) {
return 0;
}
return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2);
}
class karr_relation_plugin::project_fn : public convenient_relation_project_fn {
public:
project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols)
: convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) {
}
virtual relation_base * operator()(const relation_base & _r) {
karr_relation const& r = get(_r);
karr_relation_plugin& p = r.get_plugin();
karr_relation* result = dynamic_cast<karr_relation*>(p.mk_full(0, get_result_signature()));
result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr());
return result;
}
};
relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r,
unsigned col_cnt, const unsigned * removed_cols) {
return alloc(project_fn, r.get_signature(), col_cnt, removed_cols);
}
class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn {
public:
rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle)
: convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {}
virtual relation_base * operator()(const relation_base & _r) {
karr_relation const& r = get(_r);
karr_relation_plugin& p = r.get_plugin();
karr_relation* result = dynamic_cast<karr_relation*>(p.mk_full(0, get_result_signature()));
result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr());
return result;
}
};
relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r,
unsigned cycle_len, const unsigned * permutation_cycle) {
if (!check_kind(r)) {
return 0;
}
return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle);
}
bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) {
dst.reset();
m_hb.reset();
for (unsigned i = 0; i < src.size(); ++i) {
if (src.eq[i]) {
m_hb.add_eq(src.A[i], -src.b[i]);
}
else {
m_hb.add_ge(src.A[i], -src.b[i]);
}
}
for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) {
m_hb.set_is_int(i);
}
lbool is_sat = l_undef;
try {
is_sat = m_hb.saturate();
}
catch (...) {
is_sat = l_undef;
}
TRACE("dl_verbose", m_hb.display(tout););
if (is_sat == l_false) {
return false;
}
if (is_sat == l_undef) {
return true;
}
unsigned basis_size = m_hb.get_basis_size();
bool first_initial = true;
for (unsigned i = 0; i < basis_size; ++i) {
bool is_initial;
vector<rational> soln;
m_hb.get_basis_solution(i, soln, is_initial);
if (is_initial && first_initial) {
dst.A.push_back(soln);
dst.b.push_back(rational(1));
dst.eq.push_back(true);
first_initial = false;
}
else if (!is_initial) {
dst.A.push_back(soln);
dst.b.push_back(rational(0));
dst.eq.push_back(true);
}
}
return true;
}
void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) {
dst.reset();
if (src.size() == 0) {
return;
}
m_hb.reset();
for (unsigned i = 0; i < src.size(); ++i) {
vector<rational> v(src.A[i]);
v.push_back(src.b[i]);
if (src.eq[i]) {
m_hb.add_eq(v, rational(0));
}
else {
m_hb.add_ge(v, rational(0));
}
}
for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) {
m_hb.set_is_int(i);
}
lbool is_sat = l_undef;
try {
is_sat = m_hb.saturate();
}
catch (...) {
is_sat = l_undef;
}
if (is_sat != l_true) {
return;
}
TRACE("dl_verbose", m_hb.display(tout););
SASSERT(is_sat == l_true);
unsigned basis_size = m_hb.get_basis_size();
for (unsigned i = 0; i < basis_size; ++i) {
bool is_initial;
vector<rational> soln;
m_hb.get_basis_solution(i, soln, is_initial);
if (!is_initial) {
dst.b.push_back(soln.back());
dst.eq.push_back(true);
soln.pop_back();
dst.A.push_back(soln);
}
}
}
class karr_relation_plugin::union_fn : public relation_union_fn {
public:
union_fn() {}
virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) {
karr_relation& r = get(_r);
karr_relation const& src = get(_src);
TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n"););
if (_delta) {
karr_relation& d = get(*_delta);
r.mk_union(src, &d);
}
else {
r.mk_union(src, 0);
}
TRACE("dl", r.display(tout << "result:\n"););
}
};
relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src,
const relation_base * delta) {
if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) {
return 0;
}
return alloc(union_fn);
}
class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn {
unsigned_vector m_identical_cols;
public:
filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols)
: m_identical_cols(col_cnt, identical_cols) {}
virtual void operator()(relation_base & _r) {
karr_relation & r = get(_r);
TRACE("dl", r.display(tout << "src:\n"););
r.get_ineqs();
for (unsigned i = 1; i < m_identical_cols.size(); ++i) {
unsigned c1 = m_identical_cols[0];
unsigned c2 = m_identical_cols[i];
vector<rational> row;
row.resize(r.get_signature().size());
row[c1] = rational(1);
row[c2] = rational(-1);
r.m_ineqs.A.push_back(row);
r.m_ineqs.b.push_back(rational(0));
r.m_ineqs.eq.push_back(true);
r.m_basis_valid = false;
}
TRACE("dl", r.display(tout << "result:\n"););
}
};
relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn(
const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) {
if(!check_kind(t)) {
return 0;
}
return alloc(filter_identical_fn, col_cnt, identical_cols);
}
class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn {
unsigned m_col;
rational m_value;
bool m_valid;
public:
filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col)
: m_col(col) {
arith_util arith(m.get_context().get_manager());
m_valid = arith.is_numeral(value, m_value) && m_value.is_int();
}
virtual void operator()(relation_base & _r) {
karr_relation & r = get(_r);
if (m_valid) {
r.get_ineqs();
vector<rational> row;
row.resize(r.get_signature().size());
row[m_col] = rational(1);
r.m_ineqs.A.push_back(row);
r.m_ineqs.b.push_back(rational(-1));
r.m_ineqs.eq.push_back(true);
r.m_basis_valid = false;
}
TRACE("dl", tout << m_value << "\n"; r.display(tout););
}
};
relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r,
const relation_element & value, unsigned col) {
if (check_kind(r)) {
return alloc(filter_equal_fn, get_manager(), value, col);
}
return 0;
}
class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn {
app_ref m_cond;
public:
filter_interpreted_fn(karr_relation const& t, app* cond):
m_cond(cond, t.get_plugin().get_ast_manager()) {
}
void operator()(relation_base& t) {
get(t).filter_interpreted(m_cond);
TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout););
}
};
relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) {
if (check_kind(t)) {
return alloc(filter_interpreted_fn, get(t), condition);
}
return 0;
}
};