3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-20 00:49:33 +00:00

Merge remote-tracking branch 'origin/master' into c3

# Conflicts:
#	.github/workflows/qf-s-benchmark.lock.yml
#	.github/workflows/qf-s-benchmark.md
#	.github/workflows/zipt-code-reviewer.lock.yml
#	.github/workflows/zipt-code-reviewer.md
#	.gitignore
#	src/ast/rewriter/seq_rewriter.cpp
#	src/test/main.cpp
This commit is contained in:
Nikolaj Bjorner 2026-03-24 17:44:48 -07:00
commit 6a6f9b1892
185 changed files with 16422 additions and 5692 deletions

View file

@ -1360,6 +1360,7 @@ namespace {
// to check it again.
get_check_mark(reg) == NOT_CHECKED &&
is_ground(m_registers[reg]) &&
instr->m_enode != nullptr &&
get_pat_lbl_hash(reg) == instr->m_enode->get_lbl_hash();
}

View file

@ -125,7 +125,7 @@ namespace smt {
if (!m_non_diff_logic_exprs) {
TRACE(non_diff_logic, tout << "found non diff logic expression:\n" << mk_pp(n, m) << "\n";);
ctx.push_trail(value_trail<bool>(m_non_diff_logic_exprs));
IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, m) << ")\n";);
IF_VERBOSE(2, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, m) << ")\n";);
m_non_diff_logic_exprs = true;
}
}

View file

@ -170,7 +170,7 @@ template<typename Ext>
void theory_diff_logic<Ext>::found_non_diff_logic_expr(expr * n) {
if (!m_non_diff_logic_exprs) {
TRACE(non_diff_logic, tout << "found non diff logic expression:\n" << mk_pp(n, m) << "\n";);
IF_VERBOSE(0, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, m) << ")\n";);
IF_VERBOSE(2, verbose_stream() << "(smt.diff_logic: non-diff logic expression " << mk_pp(n, m) << ")\n";);
ctx.push_trail(value_trail<bool>(m_non_diff_logic_exprs));
m_non_diff_logic_exprs = true;
}

View file

@ -155,6 +155,7 @@ class theory_lra::imp {
ptr_vector<expr> m_not_handled;
ptr_vector<app> m_underspecified;
ptr_vector<app> m_bv_terms;
ptr_vector<expr> m_mul_defs; // fresh multiplication definition vars
vector<ptr_vector<api_bound> > m_use_list; // bounds where variables are used.
// attributes for incremental version:
@ -267,9 +268,25 @@ class theory_lra::imp {
};
m_nla->set_relevant(is_relevant);
m_nla->updt_params(ctx().get_params());
m_nla->get_core().set_add_mul_def_hook([&](unsigned sz, lpvar const* vs) { return add_mul_def(sz, vs); });
}
}
lpvar add_mul_def(unsigned sz, lpvar const* vs) {
bool is_int = true;
for (unsigned i = 0; i < sz; ++i) {
theory_var tv = lp().local_to_external(vs[i]);
is_int &= this->is_int(tv);
}
sort* srt = is_int ? a.mk_int() : a.mk_real();
app_ref c(m.mk_fresh_const("mul!", srt), m);
mk_enode(c);
theory_var v = mk_var(c);
ctx().push_trail(push_back_vector<ptr_vector<expr>>(m_mul_defs));
m_mul_defs.push_back(c);
return register_theory_var_in_lar_solver(v);
}
void found_unsupported(expr* n) {
ctx().push_trail(push_back_vector<ptr_vector<expr>>(m_not_handled));
TRACE(arith, tout << "unsupported " << mk_pp(n, m) << "\n");
@ -3983,6 +4000,81 @@ public:
return inf_eps(rational(0), inf_rational(ival.x, ival.y));
}
lp::lp_status max_with_lp(theory_var v, lpvar& vi, lp::impq& term_max) {
if (!lp().is_feasible() || lp().has_changed_columns())
make_feasible();
vi = get_lpvar(v);
auto st = lp().maximize_term(vi, term_max);
if (has_int() && lp().has_inf_int()) {
st = lp::lp_status::FEASIBLE;
lp().restore_x();
}
return st;
}
// Returns true if NLA handled the result (blocker and result are set).
// Returns false if maximize should fall through to the normal status switch.
bool max_with_nl(theory_var v, lp::lp_status& st, unsigned level, expr_ref& blocker, inf_eps& result) {
if (!m_nla || (st != lp::lp_status::OPTIMAL && st != lp::lp_status::UNBOUNDED))
return false;
// Save the LP optimum before NLA check may restore x.
auto lp_val = value(v);
auto lp_ival = get_ivalue(v);
auto nla_st = check_nla(level);
TRACE(opt, tout << "check_nla returned " << nla_st
<< " lp_ival=" << lp_ival << "\n";
if (nla_st == FC_CONTINUE) {
tout << "LP assignment at maximize optimum:\n";
for (unsigned j = 0; j < lp().column_count(); j++) {
if (!lp().get_column_value(j).is_zero())
tout << " x[" << j << "] = " << lp().get_column_value(j) << "\n";
}
});
switch (nla_st) {
case FC_DONE:
// NLA satisfied: keep the optimal assignment, return LP value
blocker = mk_gt(v);
result = lp_val;
st = lp::lp_status::FEASIBLE;
return true;
case FC_CONTINUE:
// NLA found the LP optimum violates nonlinear constraints.
// Restore x but return the LP optimum value and blocker
// as a bound for the optimizer to validate via check_bound().
lp().restore_x();
blocker = mk_gt(v, lp_ival);
result = lp_val;
st = lp::lp_status::FEASIBLE;
return true;
case FC_GIVEUP:
lp().restore_x();
st = lp::lp_status::UNBOUNDED;
return false;
}
UNREACHABLE();
return false;
}
theory_lra::inf_eps max_result(theory_var v, lpvar vi, lp::lp_status st, expr_ref& blocker, bool& has_shared) {
switch (st) {
case lp::lp_status::OPTIMAL:
init_variable_values();
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
blocker = mk_gt(v);
return value(v);
case lp::lp_status::FEASIBLE:
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
blocker = mk_gt(v);
return value(v);
default:
SASSERT(st == lp::lp_status::UNBOUNDED);
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
has_shared = false;
blocker = m.mk_false();
return inf_eps(rational::one(), inf_rational());
}
}
theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) {
unsigned level = 2;
lp::impq term_max;
@ -3999,55 +4091,21 @@ public:
st = lp::lp_status::UNBOUNDED;
}
else {
if (!lp().is_feasible() || lp().has_changed_columns())
make_feasible();
vi = get_lpvar(v);
st = lp().maximize_term(vi, term_max);
if (has_int() && lp().has_inf_int()) {
st = lp::lp_status::FEASIBLE;
lp().restore_x();
}
if (m_nla && (st == lp::lp_status::OPTIMAL || st == lp::lp_status::UNBOUNDED)) {
switch (check_nla(level)) {
case FC_DONE:
case FC_CONTINUE:
st = lp::lp_status::FEASIBLE;
break;
case FC_GIVEUP:
st = lp::lp_status::UNBOUNDED;
break;
}
lp().restore_x();
}
}
switch (st) {
case lp::lp_status::OPTIMAL: {
init_variable_values();
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
auto val = value(v);
blocker = mk_gt(v);
return val;
}
case lp::lp_status::FEASIBLE: {
auto val = value(v);
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
blocker = mk_gt(v);
return val;
}
default:
SASSERT(st == lp::lp_status::UNBOUNDED);
TRACE(arith, display(tout << st << " v" << v << " vi: " << vi << "\n"););
has_shared = false;
blocker = m.mk_false();
return inf_eps(rational::one(), inf_rational());
st = max_with_lp(v, vi, term_max);
inf_eps nl_result;
if (max_with_nl(v, st, level, blocker, nl_result))
return nl_result;
}
return max_result(v, vi, st, blocker, has_shared);
}
expr_ref mk_gt(theory_var v) {
lp::impq val = get_ivalue(v);
return mk_gt(v, val);
}
// Overload: create blocker from a saved impq value (used when x has been restored)
expr_ref mk_gt(theory_var v, lp::impq const& val) {
expr* obj = get_enode(v)->get_expr();
rational r = val.x;
expr_ref e(m);

View file

@ -345,11 +345,6 @@ final_check_status theory_seq::final_check_eh(unsigned level) {
TRACEFIN("regex propagate");
return FC_CONTINUE;
}
if (check_contains()) {
++m_stats.m_propagate_contains;
TRACEFIN("propagate_contains");
return FC_CONTINUE;
}
if (check_fixed_length(true, false)) {
++m_stats.m_fixed_length;
TRACEFIN("zero_length");
@ -365,6 +360,16 @@ final_check_status theory_seq::final_check_eh(unsigned level) {
TRACEFIN("fixed_length");
return FC_CONTINUE;
}
if (check_fixed_length(false, true)) {
++m_stats.m_fixed_length;
TRACEFIN("fixed_length");
return FC_CONTINUE;
}
if (check_contains()) {
++m_stats.m_propagate_contains;
TRACEFIN("propagate_contains");
return FC_CONTINUE;
}
if (check_int_string()) {
++m_stats.m_int_string;
TRACEFIN("int_string");
@ -499,12 +504,6 @@ bool theory_seq::fixed_length(expr* len_e, bool is_zero, bool check_long_strings
m_fixed.contains(e)) {
return false;
}
m_trail_stack.push(insert_obj_trail<expr>(m_fixed, e));
m_fixed.insert(e);
expr_ref seq(e, m), head(m), tail(m);
TRACE(seq, tout << "Fixed: " << mk_bounded_pp(e, m, 2) << " " << lo << "\n";);
literal a = mk_eq(len_e, m_autil.mk_numeral(lo, true), false);
@ -514,6 +513,11 @@ bool theory_seq::fixed_length(expr* len_e, bool is_zero, bool check_long_strings
if (!check_long_strings && lo > 20 && !is_zero)
return false;
m_trail_stack.push(insert_obj_trail<expr>(m_fixed, e));
m_fixed.insert(e);
expr_ref seq(e, m), head(m), tail(m);
if (lo.is_zero()) {
seq = m_util.str.mk_empty(e->get_sort());
}
@ -2976,7 +2980,7 @@ void theory_seq::add_axiom(literal_vector & lits) {
TRACE(seq, ctx.display_literals_verbose(tout << "assert " << lits << " :", lits) << "\n";);
for (literal lit : lits)
if (ctx.get_assignment(lit) == l_true)
if (ctx.get_assignment(lit) == l_true && ctx.get_assign_level(lit) == 0)
return;
for (literal lit : lits)