3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-04-15 16:54:11 +00:00

Does model construction work properly now?

This commit is contained in:
CEisenhofer 2026-04-10 17:59:16 +02:00
parent e6ef0d29c4
commit 2b7204b07c
4 changed files with 204 additions and 22 deletions

View file

@ -4155,15 +4155,11 @@ nielsen_graph::generate_length_constraints(vector<length_constraint>& constraint
// and do not require incremental skipping.
IF_VERBOSE(1, verbose_stream() << "solve_sat_path_ints: sat_path length=" << m_sat_path.size() << "\n";);
m_solver.push();
for (nielsen_edge* e : m_sat_path) {
for (auto const& ic : e->side_constraints()) {
if (m_sat_node) {
for (auto const& ic : m_sat_node->constraints()) {
m_solver.assert_expr(ic.fml);
}
}
if (m_sat_node) {
for (auto const& ic : m_sat_node->constraints())
m_solver.assert_expr(ic.fml);
}
lbool result = m_solver.check();
IF_VERBOSE(1, verbose_stream() << "solve_sat_path_ints result: " << result << "\n";);
if (result == l_true) {

View file

@ -24,9 +24,107 @@ Author:
namespace smt {
seq_model::seq_model(ast_manager& m, seq_util& seq,
static void collect_expr_dependencies(context& ctx, expr* e, obj_hashtable<enode>& seen, ptr_vector<enode>& deps) {
if (!e)
return;
ptr_vector<expr> todo;
obj_hashtable<expr> seen_expr;
todo.push_back(e);
while (!todo.empty()) {
expr* cur = todo.back();
todo.pop_back();
if (seen_expr.contains(cur))
continue;
seen_expr.insert(cur);
if (ctx.e_internalized(cur)) {
enode* dep = ctx.get_enode(cur)->get_root();
if (!seen.contains(dep)) {
seen.insert(dep);
deps.push_back(dep);
}
}
if (!is_app(cur))
continue;
for (expr* arg : *to_app(cur))
todo.push_back(arg);
}
}
static expr_ref substitute_dependency_values(ast_manager& m, context& ctx, expr* e, obj_map<enode, expr*> const& dep_values) {
if (!e)
return expr_ref(m);
expr* cur = e;
if (ctx.e_internalized(e)) {
expr* dval = nullptr;
enode* dep = ctx.get_enode(e)->get_root();
if (dep_values.find(dep, dval) && dval) {
if (m.is_value(dval))
return expr_ref(dval, m);
cur = dval;
}
}
if (!is_app(cur))
return expr_ref(cur, m);
app* a = to_app(cur);
expr_ref_vector args(m);
bool changed = false;
for (expr* arg : *a) {
expr_ref new_arg = substitute_dependency_values(m, ctx, arg, dep_values);
changed = changed || (new_arg != arg);
args.push_back(new_arg);
}
if (!changed)
return expr_ref(cur, m);
return expr_ref(m.mk_app(a->get_decl(), args.size(), args.data()), m);
}
class seq_snode_value_proc : public model_value_proc {
seq_model& m_owner;
enode* m_node;
euf::snode* m_snode;
ptr_vector<enode> m_dependencies;
public:
seq_snode_value_proc(seq_model& owner, enode* node, euf::snode* snode)
: m_owner(owner), m_node(node), m_snode(snode) {
obj_hashtable<enode> seen;
if (m_node)
seen.insert(m_node->get_root());
m_owner.collect_dependencies(m_snode, seen, m_dependencies);
}
void get_dependencies(buffer<model_value_dependency>& result) override {
for (enode* d : m_dependencies)
result.push_back(model_value_dependency(d));
}
app* mk_value(model_generator& mg, expr_ref_vector const& values) override {
SASSERT(values.size() == m_dependencies.size());
obj_map<enode, expr*> dep_values;
for (unsigned i = 0; i < m_dependencies.size(); ++i)
dep_values.insert(m_dependencies[i]->get_root(), values[i]);
expr_ref val = m_owner.snode_to_value(m_snode, mg, &dep_values);
if (!val)
val = m_owner.snode_to_value(m_snode, mg);
if (!val)
val = m_owner.m_seq.str.mk_empty(m_node->get_expr()->get_sort());
m_owner.m_trail.push_back(val);
m_owner.m_factory->add_trail(val);
TRACE(seq, tout << "nseq seq_snode_value_proc: " << mk_pp(m_node->get_expr(), m_owner.m) << " -> " << mk_pp(val, m_owner.m) << "\n";);
return to_app(val);
}
};
seq_model::seq_model(ast_manager& m, context& ctx, seq_util& seq,
seq_rewriter& rw, euf::sgraph& sg)
: m(m), m_seq(seq), m_rewriter(rw), m_sg(sg), m_trail(m)
: m(m), m_ctx(ctx), m_seq(seq), m_rewriter(rw), m_sg(sg), m_trail(m)
{}
void seq_model::init(model_generator& mg, seq::nielsen_graph& nielsen) {
@ -48,7 +146,7 @@ namespace smt {
// solve integer constraints from the sat_path FIRST so that
// m_int_model is available when snode_to_value evaluates power exponents
VERIFY(nielsen.solve_sat_path_raw(m_int_model));
// VERIFY(nielsen.solve_sat_path_raw(m_int_model));
// extract variable assignments from the satisfying leaf's substitution path
extract_assignments(nielsen.sat_path());
@ -88,7 +186,7 @@ namespace smt {
});
expr_ref val(m);
if (sn)
val = snode_to_value(sn);
return alloc(seq_snode_value_proc, *this, n, sn);
if (!val) {
// no assignment found — default to empty string
@ -143,7 +241,7 @@ namespace smt {
unsigned id = b.first->first()->id();
if (m_var_values.contains(id))
continue;
expr_ref val = snode_to_value(b.second);
expr_ref val = snode_to_value(b.second, *m_mg);
IF_VERBOSE(1, {
verbose_stream() << " var snode[" << id << "]";
if (b.first->get_expr()) verbose_stream() << "=" << mk_bounded_pp(b.first->get_expr(), m, 2);
@ -158,7 +256,11 @@ namespace smt {
}
}
expr_ref seq_model::snode_to_value(euf::snode* n) {
expr_ref seq_model::snode_to_value(euf::snode* n, model_generator& mg) {
return snode_to_value(n, mg, nullptr);
}
expr_ref seq_model::snode_to_value(euf::snode* n, model_generator& mg, obj_map<enode, expr*> const* dep_values) {
if (!n)
return expr_ref(m);
@ -173,14 +275,38 @@ namespace smt {
expr* e = n->get_expr();
SASSERT(m_seq.str.is_unit(e));
e = to_app(e)->get_arg(0);
unsigned c;
if (dep_values && e && m_ctx.e_internalized(e)) {
expr* dval = nullptr;
enode* dep = m_ctx.get_enode(e)->get_root();
if (dep_values->find(dep, dval) && dval && m_seq.is_const_char(dval, c))
return expr_ref(m_seq.str.mk_string(zstring(c)), m);
}
if (dep_values && e && m_mg) {
expr_ref e_sub = substitute_dependency_values(m, m_ctx, e, *dep_values);
expr_ref val_sub(m);
if (m_mg->get_model().eval(e_sub, val_sub, true) && val_sub && m_seq.is_const_char(val_sub, c))
return expr_ref(m_seq.str.mk_string(zstring(c)), m);
}
expr_ref val(m);
if (e && m_int_model) {
unsigned c;
m_int_model->eval_expr(e, val, true);
if (e && m_mg && m_mg->get_model().eval(e, val, true)) {
if (val && m_seq.is_const_char(val, c))
return expr_ref(m_seq.str.mk_string(zstring(c)), m);
}
return e ? expr_ref(e, m) : expr_ref(m);
if (e && m_seq.is_char(e)) {
return expr_ref(m_seq.str.mk_string("0"), m);
}
if (m_mg && e) {
expr* some = m_mg->get_model().get_some_value(e->get_sort());
if (some)
return expr_ref(m_seq.str.mk_unit(some), m);
}
return expr_ref(m_seq.str.mk_unit(e), m);
}
if (n->is_var())
@ -188,8 +314,8 @@ namespace smt {
if (n->is_concat()) {
SASSERT(n->get_sort() && m_seq.is_seq(n->get_sort()));
expr_ref lhs = snode_to_value(n->arg(0));
expr_ref rhs = snode_to_value(n->arg(1));
expr_ref lhs = snode_to_value(n->arg(0), mg, dep_values);
expr_ref rhs = snode_to_value(n->arg(1), mg, dep_values);
if (lhs && rhs)
return expr_ref(m_seq.str.mk_concat(lhs, rhs), m);
if (lhs)
@ -204,7 +330,7 @@ namespace smt {
// Evaluate the base and exponent to produce a concrete string.
// The base is a string snode; the exponent is an integer expression
// whose value comes from the sat_path integer model.
expr_ref base_val = snode_to_value(n->arg(0));
expr_ref base_val = snode_to_value(n->arg(0), mg, dep_values);
if (!base_val)
return expr_ref(m);
@ -219,6 +345,21 @@ namespace smt {
if (exp_expr && arith.is_numeral(exp_expr, exp_val)) {
// already concrete
}
else if (dep_values && exp_expr && m_ctx.e_internalized(exp_expr)) {
expr* dval = nullptr;
enode* dep = m_ctx.get_enode(exp_expr)->get_root();
if (dep_values->find(dep, dval) && dval && arith.is_numeral(dval, exp_val)) {
// evaluated from dependency values
}
else if (m_mg) {
expr_ref sub_exp = substitute_dependency_values(m, m_ctx, exp_expr, *dep_values);
expr_ref result(m);
if (!(m_mg->get_model().eval(sub_exp, result, true) && arith.is_numeral(result, exp_val)))
exp_val = rational(0);
}
else
exp_val = rational(0);
}
else if (exp_expr && m_int_model.get()) {
expr_ref result(m);
if (m_int_model->eval_expr(exp_expr, result, true) && arith.is_numeral(result, exp_val)) {
@ -277,6 +418,38 @@ namespace smt {
return e ? expr_ref(e, m) : expr_ref(m);
}
void seq_model::collect_dependencies(euf::snode* n, obj_hashtable<enode>& seen, ptr_vector<enode>& deps) const {
if (!n)
return;
if (n->is_char() || n->is_unit()) {
expr* e = n->get_expr();
if (e && m_seq.str.is_unit(e)) {
expr* ch = to_app(e)->get_arg(0);
collect_expr_dependencies(m_ctx, ch, seen, deps);
}
return;
}
if (n->is_concat()) {
collect_dependencies(n->arg(0), seen, deps);
collect_dependencies(n->arg(1), seen, deps);
return;
}
if (n->is_power()) {
collect_dependencies(n->arg(0), seen, deps);
if (n->num_args() == 2) {
euf::snode* exp_snode = n->arg(1);
expr* exp_expr = exp_snode ? exp_snode->get_expr() : nullptr;
rational exp_val;
arith_util arith(m);
if (exp_expr && !arith.is_numeral(exp_expr, exp_val))
collect_expr_dependencies(m_ctx, exp_expr, seen, deps);
}
}
}
void seq_model::register_existing_values(seq::nielsen_graph& nielsen) {
seq::nielsen_node const* root = nielsen.root();
if (!root)

View file

@ -28,6 +28,7 @@ Author:
--*/
#pragma once
#include "smt_context.h"
#include "ast/seq_decl_plugin.h"
#include "ast/rewriter/seq_rewriter.h"
#include "ast/euf/euf_sgraph.h"
@ -43,9 +44,13 @@ namespace smt {
class model_generator;
struct tracked_str_mem;
class model_value_proc;
class seq_snode_value_proc;
class seq_model {
friend class seq_snode_value_proc;
ast_manager& m;
context& m_ctx;
seq_util& m_seq;
seq_rewriter& m_rewriter;
euf::sgraph& m_sg;
@ -69,7 +74,7 @@ namespace smt {
u_map<euf::snode*> m_var_regex;
public:
seq_model(ast_manager& m, seq_util& seq,
seq_model(ast_manager& m, context& ctx, seq_util& seq,
seq_rewriter& rw, euf::sgraph& sg);
// Phase 1: initialize model construction.
@ -97,7 +102,14 @@ namespace smt {
// recursively substitute known variable assignments into an snode tree.
// Returns a concrete Z3 expression.
expr_ref snode_to_value(euf::snode* n);
expr_ref snode_to_value(euf::snode* n, model_generator& mg);
// Same as above, but optionally uses pre-evaluated model values for
// enode dependencies (provided by model_generator).
expr_ref snode_to_value(euf::snode* n, model_generator& mg, obj_map<enode, expr*> const* dep_values);
// Collect enode dependencies required to evaluate an snode value.
void collect_dependencies(euf::snode* n, obj_hashtable<enode>& seen, ptr_vector<enode>& deps) const;
// register all string literals appearing in the constraint store
// with the factory to avoid collisions with fresh values.

View file

@ -38,7 +38,7 @@ namespace smt {
m_nielsen(m_sgraph, m_context_solver, m_core_solver),
m_axioms(m_th_rewriter),
m_regex(m_sgraph),
m_model(m, m_seq, m_rewriter, m_sgraph),
m_model(m, ctx, m_seq, m_rewriter, m_sgraph),
m_relevant_lengths(m)
{
std::function<void(expr_ref_vector const&)> add_clause =
@ -733,6 +733,7 @@ namespace smt {
bool was_internalized = ctx.e_internalized(c.fml);
auto lit = mk_literal(c.fml);
m_nielsen_literals.push_back(lit);
std::cout << "Assumption: " << mk_pp(c.fml, m) << std::endl;
switch (ctx.get_assignment(lit)) {
case l_true:
break;