z3-z3-4.13.0.src.solver.check_logic.cpp Maven / Gradle / Ivy
The newest version!
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
check_logic.cpp
Abstract:
Check whether a given assertion is in the correct logic or not
Author:
Leonardo de Moura (leonardo) 2011-08-11.
Revision History:
--*/
#include "solver/check_logic.h"
#include "solver/smt_logics.h"
#include "ast/arith_decl_plugin.h"
#include "ast/array_decl_plugin.h"
#include "ast/bv_decl_plugin.h"
#include "ast/seq_decl_plugin.h"
#include "ast/pb_decl_plugin.h"
#include "ast/datatype_decl_plugin.h"
#include "ast/ast_pp.h"
#include "ast/for_each_expr.h"
#include
struct check_logic::imp {
ast_manager & m;
symbol m_logic;
arith_util m_a_util;
bv_util m_bv_util;
array_util m_ar_util;
seq_util m_seq_util;
datatype_util m_dt_util;
pb_util m_pb_util;
bool m_uf; // true if the logic supports uninterpreted functions
bool m_dt; // true if the logic supports dattypes
bool m_arrays; // true if the logic supports arbitrary arrays
bool m_bv_arrays; // true if the logic supports only bv arrays
bool m_reals; // true if the logic supports reals
bool m_ints; // true if the logic supports integers
bool m_diff; // true if the logic supports difference logic only
bool m_nonlinear; // true if the logic supports nonlinear arithmetic
bool m_bvs; // true if the logic supports bit-vectors
bool m_quantifiers; // true if the logic supports quantifiers
bool m_unknown_logic;
imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m), m_seq_util(m), m_dt_util(m), m_pb_util(m) {
reset();
}
void reset() {
m_uf = false;
m_dt = false;
m_arrays = false;
m_bv_arrays = false;
m_reals = false;
m_ints = false;
m_diff = false;
m_nonlinear = false;
m_bvs = false;
m_quantifiers = false;
m_unknown_logic = true;
}
void set_logic(symbol const & logic) {
reset();
m_unknown_logic = false;
if (logic == "AUFLIA") {
m_uf = true;
m_arrays = true;
m_ints = true;
m_quantifiers = true;
}
else if (logic == "AUFLIRA") {
m_uf = true;
m_arrays = true;
m_reals = true;
m_ints = true;
m_quantifiers = true;
}
else if (logic == "AUFNIRA") {
m_uf = true;
m_arrays = true;
m_reals = true;
m_ints = true;
m_nonlinear = true;
m_quantifiers = true;
}
else if (logic == "LRA") {
m_reals = true;
m_quantifiers = true;
}
else if (logic == "QF_ABV") {
m_bv_arrays = true;
m_bvs = true;
}
else if (logic == "QF_AUFBV") {
m_uf = true;
m_bv_arrays = true;
m_bvs = true;
}
else if (logic == "QF_UFBV") {
m_uf = true;
m_bvs = true;
}
else if (logic == "QF_UFDT") {
m_uf = true;
m_dt = true;
}
else if (logic == "QF_DT") {
m_dt = true;
}
else if (logic == "QF_AUFLIA") {
m_uf = true;
m_arrays = true;
m_ints = true;
}
else if (logic == "QF_AX") {
m_arrays = true;
}
else if (logic == "QF_BV") {
m_bvs = true;
}
else if (logic == "QF_IDL") {
m_ints = true;
m_diff = true;
}
else if (logic == "QF_RDL") {
m_reals = true;
m_diff = true;
}
else if (logic == "QF_LIA") {
m_ints = true;
}
else if (logic == "QF_LRA") {
m_reals = true;
}
else if (logic == "QF_NIA") {
m_ints = true;
m_nonlinear = true;
}
else if (logic == "QF_NRA") {
m_reals = true;
m_nonlinear = true;
}
else if (logic == "QF_UF") {
m_uf = true;
}
else if (logic == "QF_UFIDL") {
m_uf = true;
m_ints = true;
m_diff = true;
}
else if (logic == "QF_UFLIA") {
m_uf = true;
m_ints = true;
}
else if (logic == "QF_UFLRA") {
m_uf = true;
m_reals = true;
}
else if (logic == "QF_UFNRA") {
m_uf = true;
m_reals = true;
m_nonlinear = true;
}
else if (logic == "UFLRA") {
m_uf = true;
m_reals = true;
m_quantifiers = true;
}
else if (logic == "UFNIA") {
m_uf = true;
m_ints = true;
m_quantifiers = true;
m_nonlinear = true;
}
else if (logic == "UFBV") {
m_uf = true;
m_bvs = true;
m_quantifiers = true;
}
else if (logic == "QF_S" || logic == "QF_SLIA") {
m_uf = true;
m_bvs = true;
m_ints = true;
m_arrays = true;
m_reals = true;
m_quantifiers = true; // some QF_SLIA benchmarks are miss-classified
}
else if (logic == "QF_FD") {
m_bvs = true;
m_uf = true;
m_ints = true;
m_dt = true;
m_nonlinear = true; // non-linear 0-1 variables may get eliminated
}
else if (logic == "SMTFD") {
m_bvs = true;
m_uf = true;
m_arrays = true;
m_ints = false;
m_dt = false;
m_nonlinear = false;
}
else {
m_unknown_logic = true;
}
m_logic = logic;
}
struct failed {};
std::string m_last_error;
void fail(std::string &&msg) {
m_last_error = std::move(msg);
throw failed();
}
void fail(char const * msg) {
m_last_error = msg;
throw failed();
}
void check_sort(sort * s) {
if (s->get_family_id() == null_family_id) {
if (!m_uf)
fail("logic does not support uninterpreted sorts");
}
else if (m.is_bool(s)) {
return;
}
else if (m_a_util.is_int(s)) {
if (!m_ints)
fail("logic does not support integers");
}
else if (m_a_util.is_real(s)) {
if (!m_reals)
fail("logic does not support reals");
}
else if (m_bv_util.is_bv_sort(s)) {
if (!m_bvs)
fail("logic does not support bitvectors");
}
else if (m_dt_util.is_datatype(s)) {
if (!m_dt)
fail("logic does not support algebraic datatypes");
}
else if (m_ar_util.is_array(s)) {
if (m_arrays) {
return;
}
else if (m_bv_arrays) {
unsigned sz = get_array_arity(s);
for (unsigned i = 0; i < sz; ++i) {
if (!m_bv_util.is_bv_sort(get_array_domain(s, i)))
fail("logic supports only arrays from bitvectors to bitvectors");
}
check_sort(get_array_range(s));
}
else {
fail("logic does not support arrays");
}
}
}
void operator()(var * n) {
if (!m_quantifiers)
fail("logic does not support quantifiers");
check_sort(n->get_sort());
}
bool is_int(expr * t) {
if (m_a_util.is_uminus(t))
t = to_app(t)->get_arg(0);
// Take care of coercions automatically added by Z3
if (m_a_util.is_to_real(t))
t = to_app(t)->get_arg(0);
return m_a_util.is_numeral(t);
}
bool is_numeral(expr * t) {
if (m_a_util.is_uminus(t))
t = to_app(t)->get_arg(0);
// c
if (is_int(t))
return true;
// c1/c2
if (m_a_util.is_div(t) && is_int(to_app(t)->get_arg(0)) && is_int(to_app(t)->get_arg(1)))
return true;
return false;
}
// check if n has at most one argument that is not numeral.
void check_mul(app * n) {
if (m_nonlinear)
return; // nothing to check
unsigned num_args = n->get_num_args();
bool found_non_numeral = false;
for (unsigned i = 0; i < num_args; i++) {
if (!is_numeral(n->get_arg(i))) {
if (found_non_numeral)
fail("logic does not support nonlinear arithmetic");
else
found_non_numeral = true;
}
}
}
// check if the divisor is a numeral
void check_div(app * n) {
if (n->get_num_args() != 2 || (!m_nonlinear && !is_numeral(n->get_arg(1))))
fail("logic does not support nonlinear arithmetic");
}
bool is_diff_var(expr * t) const {
if (is_app(t) && to_app(t)->get_decl()->get_family_id() == null_family_id)
return true;
if (m.is_ite(t))
return true;
return false;
}
void fail_non_diff(expr * t) {
TRACE("check_logic", tout << mk_pp(t, m) << "\n";);
fail("logic only supports difference arithmetic");
}
bool same_args(app * t) {
unsigned num_args = t->get_num_args();
if (num_args == 0)
return false;
expr * arg = t->get_arg(0);
for (unsigned i = 1; i < num_args; i++) {
if (t->get_arg(i) != arg)
return false;
}
return true;
}
bool is_arith(expr * t) const {
return t->get_sort()->get_family_id() == m_a_util.get_family_id();
}
bool is_offset(app * t) {
while (true) {
expr * non_numeral = nullptr;
unsigned num_args = t->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
expr * arg = t->get_arg(i);
if (is_numeral(arg))
continue;
if (non_numeral != nullptr)
return false;
non_numeral = arg;
}
if (non_numeral == nullptr)
return true;
if (is_diff_var(non_numeral))
return true;
if (!m_a_util.is_add(non_numeral) && !m_a_util.is_sub(non_numeral))
return false;
t = to_app(non_numeral);
}
return true;
}
bool is_diff_arg(expr * t) {
if (is_diff_var(t))
return true;
if (is_numeral(t))
return true;
if (m_a_util.is_add(t) || m_a_util.is_sub(t))
return is_offset(to_app(t));
return false;
}
// Check if n is a diff logic predicate
void check_diff_predicate(app * n) {
expr * lhs = n->get_arg(0);
expr * rhs = n->get_arg(1);
if (!is_arith(lhs))
return; // formula is not in arithmetic
if (is_diff_arg(lhs) && is_diff_arg(rhs))
return;
if (is_numeral(lhs))
std::swap(lhs, rhs);
if (!is_numeral(rhs))
fail_non_diff(n);
if (!m_a_util.is_sub(lhs) || to_app(lhs)->get_num_args() != 2)
fail_non_diff(n);
expr * t1 = to_app(lhs)->get_arg(0);
expr * t2 = to_app(lhs)->get_arg(1);
if (is_diff_var(t1) && is_diff_var(t2))
return;
if (m_a_util.is_add(t1) && m_a_util.is_add(t2)) {
// QF_RDL supports (<= (- (+ x ... x) (+ y ... y)) c)
if (to_app(t1)->get_num_args() != to_app(t2)->get_num_args())
fail_non_diff(n);
if (!same_args(to_app(t1)) || !same_args(to_app(t2)))
fail_non_diff(n);
return;
}
fail_non_diff(n);
}
void check_diff_arg(expr * t) {
if (!is_diff_arg(t))
fail_non_diff(t);
}
// Check if the arith args of n are of the form (t + k) where k is a numeral.
void check_diff_args(app * n) {
unsigned num_args = n->get_num_args();
for (unsigned i = 0; i < num_args; i++) {
if (is_arith(n))
check_diff_arg(n);
}
}
void operator()(app * n) {
sort * s = n->get_sort();
check_sort(s);
func_decl * f = n->get_decl();
family_id fid = f->get_family_id();
if (fid == null_family_id) {
if (!m_uf && f->get_arity() > 0)
fail("logic does not support uninterpreted functions");
if (m_diff)
check_diff_args(n);
}
else if (fid == m_a_util.get_family_id()) {
if (m_a_util.is_mul(n))
check_mul(n);
else if (m_a_util.is_div(n) || m_a_util.is_idiv(n) || m_a_util.is_rem(n) || m_a_util.is_mod(n))
check_div(n);
if (m_diff) {
if (m_a_util.is_le(n) || m_a_util.is_lt(n) || m_a_util.is_ge(n) || m_a_util.is_gt(n))
check_diff_predicate(n);
}
if (!m_ints || !m_reals) {
if (m_a_util.is_to_real(n) || m_a_util.is_to_int(n))
fail("logic does not support casting operators");
}
}
else if (fid == m_bv_util.get_family_id()) {
// nothing to check...
}
else if (fid == m_ar_util.get_family_id()) {
// nothing to check...
if (m_diff)
check_diff_args(n);
}
else if (fid == m.get_basic_family_id()) {
// nothing to check...
if (m_diff) {
if (m.is_eq(n))
check_diff_predicate(n);
else if (m.is_distinct(n) || m.is_ite(n))
check_diff_args(n);
}
}
else if (m.is_builtin_family_id(fid)) {
// nothing to check
}
else if (fid == m_seq_util.get_family_id()) {
// nothing to check
}
else if (fid == m_dt_util.get_family_id() && m_dt) {
// nothing to check
}
else if (fid == m_pb_util.get_family_id() && smt_logics::logic_has_pb(m_logic)) {
// nothing to check
}
else {
std::stringstream strm;
strm << "logic does not support theory " << m.get_family_name(fid);
fail(strm.str());
}
}
void operator()(quantifier * n) {
if (!m_quantifiers)
fail("logic does not support quantifiers");
}
bool operator()(expr * n) {
if (m_unknown_logic)
return true;
try {
quick_for_each_expr(*this, n);
return true;
}
catch (const failed &) {
return false;
}
}
bool operator()(func_decl * f) {
if (m_unknown_logic)
return true;
try {
unsigned arity = f->get_arity();
if (arity > 0) {
if (!m_uf && f->get_family_id() == null_family_id)
fail("logic does not support uninterpreted functions");
for (unsigned i = 0; i < arity; i++)
check_sort(f->get_domain(i));
}
check_sort(f->get_range());
return true;
}
catch (const failed &) {
return false;
}
}
};
check_logic::check_logic() {
m_imp = nullptr;
}
check_logic::~check_logic() {
if (m_imp)
dealloc(m_imp);
}
void check_logic::reset() {
if (m_imp)
dealloc(m_imp);
m_imp = nullptr;
}
void check_logic::set_logic(ast_manager & m, symbol const & logic) {
reset();
m_imp = alloc(imp, m);
m_imp->set_logic(logic);
}
bool check_logic::operator()(expr * n) {
if (m_imp)
return m_imp->operator()(n);
return true;
}
bool check_logic::operator()(func_decl * f) {
if (m_imp)
return m_imp->operator()(f);
return true;
}
char const * check_logic::get_last_error() const {
if (m_imp)
return m_imp->m_last_error.c_str();
return "";
}