#include "api/z3.h"
#include "math/lp/nla_core.h"
#include "math/lp/lar_solver.h"
#include "util/rational.h"
#include "util/rlimit.h"
#include <string>
namespace nla {
void test_sin_cos_identity_detects_conflict() {
std::cout << "test_sin_cos_identity_detects_conflict\n";
lp::lar_solver s;
reslimit rl;
params_ref p;
p.set_bool("arith.nl.nra", true);
lpvar t = s.add_var(0, false);
lpvar sin_val = s.add_var(1, false);
lpvar cos_val = s.add_var(2, false);
lpvar sin2 = s.add_var(3, false);
lpvar cos2 = s.add_var(4, false);
nla::core nla_solver(s, p, rl);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::SIN, t, sin_val);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::COS, t, cos_val);
vector<lpvar> sin_vars, cos_vars;
sin_vars.push_back(sin_val); sin_vars.push_back(sin_val);
cos_vars.push_back(cos_val); cos_vars.push_back(cos_val);
nla_solver.add_monic(sin2, sin_vars.size(), sin_vars.begin());
nla_solver.add_monic(cos2, cos_vars.size(), cos_vars.begin());
s.add_var_bound(sin2, lp::lconstraint_kind::GE, rational(3, 5));
s.add_var_bound(cos2, lp::lconstraint_kind::GE, rational(3, 5));
s.set_column_value_test(sin_val, lp::impq(rational(4, 5)));
s.set_column_value_test(cos_val, lp::impq(rational(4, 5)));
s.set_column_value_test(sin2, lp::impq(rational(16, 25)));
s.set_column_value_test(cos2, lp::impq(rational(16, 25)));
for (lpvar v : {sin_val, cos_val, sin2, cos2}) {
VERIFY(s.column_is_feasible(v));
VERIFY(s.get_column_value(v).y.is_zero());
}
lbool result = nla_solver.test_check();
VERIFY(result == l_false);
}
void test_cosh_sinh_identity_detects_conflict() {
std::cout << "test_cosh_sinh_identity_detects_conflict\n";
lp::lar_solver s;
reslimit rl;
params_ref p;
p.set_bool("arith.nl.nra", true);
lpvar t = s.add_var(0, true);
lpvar cosh_val = s.add_var(1, true);
lpvar sinh_val = s.add_var(2, true);
nla::core nla_solver(s, p, rl);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::COSH, t, cosh_val);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::SINH, t, sinh_val);
s.set_column_value_test(cosh_val, lp::impq(rational(2)));
s.set_column_value_test(sinh_val, lp::impq(rational(2)));
s.add_var_bound(cosh_val, lp::lconstraint_kind::LE, rational(2));
s.add_var_bound(cosh_val, lp::lconstraint_kind::GE, rational(2));
s.add_var_bound(sinh_val, lp::lconstraint_kind::LE, rational(2));
s.add_var_bound(sinh_val, lp::lconstraint_kind::GE, rational(2));
lbool result = nla_solver.test_check();
VERIFY(result == l_false);
}
void test_is_nla_context_satisfied() {
std::cout << "test_is_nla_context_satisfied\n";
lp::lar_solver s;
reslimit rl;
params_ref p;
p.set_bool("arith.nl.nra", true);
lpvar x = s.add_var(0, true);
lpvar xx = s.add_var(1, true);
nla::core nla_solver(s, p, rl);
vector<lpvar> vars;
vars.push_back(x); vars.push_back(x);
nla_solver.add_monic(xx, vars.size(), vars.begin());
s.set_column_value_test(x, lp::impq(rational(3)));
s.set_column_value_test(xx, lp::impq(rational(9)));
VERIFY(nla_solver.is_nla_context_satisfied());
lpvar t = s.add_var(2, true);
lpvar sin_val = s.add_var(3, true);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::SIN, t, sin_val);
s.set_column_value_test(t, lp::impq(rational(0)));
s.add_var_bound(t, lp::lconstraint_kind::LE, rational(0));
s.add_var_bound(t, lp::lconstraint_kind::GE, rational(0));
s.set_column_value_test(sin_val, lp::impq(rational(1)));
VERIFY(!nla_solver.is_nla_context_satisfied());
}
void test_unrefined_failure_is_not_sat() {
std::cout << "test_unrefined_failure_is_not_sat\n";
lp::lar_solver s;
reslimit rl;
params_ref p;
p.set_bool("arith.nl.nra", false);
lpvar x = s.add_var(0, false);
lpvar exp_val = s.add_var(1, false);
nla::core nla_solver(s, p, rl);
nla_solver.add_transcendental(nlsat::transcendental_op_kind::EXP, x, exp_val);
s.set_column_value_test(x, lp::impq(rational(-3)));
s.set_column_value_test(exp_val, lp::impq(rational(1, 10)));
VERIFY(nla_solver.test_check() != l_true);
}
void test_incremental_exp_unsat() {
std::cout << "test_incremental_exp_unsat\n";
Z3_config cfg = Z3_mk_config();
Z3_context ctx = Z3_mk_context(cfg);
Z3_del_config(cfg);
char const* spec =
"(set-logic ALL)\n"
"(declare-const x Real)\n"
"(push 1)\n"
"(assert (< x 0))\n"
"(assert (> (exp x) 0))\n"
"(check-sat)\n"
"(pop 1)\n"
"(assert (>= x (- 3)))\n"
"(assert (<= x (- (/ 299 100))))\n"
"(assert (> (* (exp x) (exp x)) (/ 1 300)))\n"
"(check-sat)\n";
std::string response = Z3_eval_smtlib2_string(ctx, spec);
if (response != "sat\nunsat\n" && response != "unknown\nunsat\n")
std::cout << response << "\n";
VERIFY(response == "sat\nunsat\n" || response == "unknown\nunsat\n");
Z3_del_context(ctx);
}
void test_exp_negative_twenty() {
Z3_config cfg = Z3_mk_config();
Z3_context ctx = Z3_mk_context(cfg);
Z3_del_config(cfg);
char const* spec =
"(set-logic ALL)\n"
"(assert (= (exp (- 20.0)) 1.0))\n"
"(check-sat)\n";
std::string response = Z3_eval_smtlib2_string(ctx, spec);
if (response != "unsat\n")
std::cout << response << "\n";
VERIFY(response == "unsat\n");
Z3_del_context(ctx);
}
void test_transcendental_enclosures_do_not_certify_sat() {
char const* assertions[] = {
"(= (exp (- 20.0)) 0.1)",
"(= (exp 0.0) 1.00000001)",
"(= (atan2 1.0 1.0) 0.7853982)"
};
for (char const* assertion : assertions) {
Z3_config cfg = Z3_mk_config();
Z3_context ctx = Z3_mk_context(cfg);
Z3_del_config(cfg);
std::string spec = "(set-logic ALL)\n(assert ";
spec += assertion;
spec += ")\n(check-sat)\n";
std::string response = Z3_eval_smtlib2_string(ctx, spec.c_str());
if (response == "sat\n")
std::cout << response << "\n";
VERIFY(response != "sat\n");
Z3_del_context(ctx);
}
}
void test_pi_positive_sat() {
Z3_config cfg = Z3_mk_config();
Z3_context ctx = Z3_mk_context(cfg);
Z3_del_config(cfg);
char const* spec =
"(assert (> pi 0))\n"
"(check-sat)\n";
std::string response = Z3_eval_smtlib2_string(ctx, spec);
if (response != "sat\n")
std::cout << response << "\n";
VERIFY(response == "sat\n");
Z3_del_context(ctx);
}
void test_atan2_unsat_regression() {
Z3_config cfg = Z3_mk_config();
Z3_context ctx = Z3_mk_context(cfg);
Z3_del_config(cfg);
char const* spec =
"(set-logic QF_NTA)\n"
"(declare-const y Real)\n"
"(assert (< y 1))\n"
"(assert (> (atan2 y 1) 0.5))\n"
"(check-sat)\n";
std::string response = Z3_eval_smtlib2_string(ctx, spec);
if (response != "sat\n")
std::cout << response << "\n";
VERIFY(response == "sat\n");
Z3_del_context(ctx);
}
void test_nla_transcendentals() {
test_sin_cos_identity_detects_conflict();
test_cosh_sinh_identity_detects_conflict();
test_is_nla_context_satisfied();
test_unrefined_failure_is_not_sat();
test_incremental_exp_unsat();
test_exp_negative_twenty();
test_transcendental_enclosures_do_not_certify_sat();
test_pi_positive_sat();
test_atan2_unsat_regression();
}
}
void tst_nla_transcendentals() {
nla::test_nla_transcendentals();
}