Skip to content

Commit

Permalink
bug fixes and cleanup in projection functions
Browse files Browse the repository at this point in the history
spacer would drop variables of sorts not handled by main loop.
- projection with witness needs to disable qel style preprocessing to ensure witnesses are returned.
- add euf plugin to handle uninterpreted sorts (and then uninterpreted functions)
  • Loading branch information
NikolajBjorner committed Feb 15, 2025
1 parent 0cf2b5f commit eee96ec
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 107 deletions.
4 changes: 4 additions & 0 deletions genaisrc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# auto-generated
genaiscript.d.ts
tsconfig.json
jsconfig.json
77 changes: 42 additions & 35 deletions src/muz/spacer/spacer_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ void qe_project_z3(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
params_ref p;
p.set_bool("reduce_all_selects", reduce_all_selects);
p.set_bool("dont_sub", dont_sub);
TRACE("qe", tout << "qe-project-z3\n");

qe::mbproj mbp(m, p);
mbp.spacer(vars, mdl, fml);
Expand All @@ -167,8 +168,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
bool dont_sub) {
th_rewriter rw(m);
TRACE("spacer_mbp", tout << "Before projection:\n"; tout << fml << "\n";
tout << "Vars:\n"
<< vars;);
tout << "Vars:" << vars << "\n";);

{
// Ensure that top-level AND of fml is flat
Expand All @@ -182,6 +182,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,

app_ref_vector arith_vars(m);
app_ref_vector array_vars(m);
app_ref_vector other_vars(m);
array_util arr_u(m);
arith_util ari_u(m);
expr_safe_replace bool_sub(m);
Expand All @@ -194,8 +195,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
rw(fml);

TRACE("spacer_mbp", tout << "After qe_lite:\n";
tout << mk_pp(fml, m) << "\n"; tout << "Vars:\n"
<< vars;);
tout << mk_pp(fml, m) << "\nVars:" << vars << "\n";);

SASSERT(!m.is_false(fml));

Expand All @@ -206,12 +206,13 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
// using model completion
model::scoped_model_completion _sc_(mdl, true);
bool_sub.insert(v, mdl(v));
} else if (arr_u.is_array(v)) {
}
else if (arr_u.is_array(v))
array_vars.push_back(v);
} else {
SASSERT(ari_u.is_int(v) || ari_u.is_real(v));
else if (ari_u.is_int(v) || ari_u.is_real(v))
arith_vars.push_back(v);
}
else
other_vars.push_back(v);
}

// substitute Booleans
Expand All @@ -220,8 +221,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
// -- bool_sub is not simplifying
rw(fml);
SASSERT(!m.is_false(fml));
TRACE("spacer_mbp", tout << "Projected Booleans:\n"
<< fml << "\n";);
TRACE("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n";);
bool_sub.reset();
}

Expand All @@ -230,7 +230,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
vars.reset();

// project arrays
{
if (!array_vars.empty()) {
scoped_no_proof _sp(m);
// -- local rewriter that is aware of current proof mode
th_rewriter srw(m);
Expand All @@ -243,14 +243,15 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,

TRACE("spacer_mbp", tout << "extended model:\n"; model_pp(tout, mdl);
tout << "Auxiliary variables of index and value sorts:\n";
tout << vars;);
tout << vars << "\n";);

if (vars.empty()) { break; }
if (vars.empty())
break;
}

// project reals and ints
if (!arith_vars.empty()) {
TRACE("spacer_mbp", tout << "Arith vars:\n" << arith_vars;);
TRACE("spacer_mbp", tout << "Arith vars:" << arith_vars << "\n";);

if (use_native_mbp) {
qe::mbproj mbp(m);
Expand All @@ -260,19 +261,19 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
mbp(true, arith_vars, mdl, fmls);
fml = mk_and(fmls);
SASSERT(arith_vars.empty());
} else {
}
else {
scoped_no_proof _sp(m);
spacer_qe::arith_project(mdl, arith_vars, fml);
}

TRACE("spacer_mbp", tout << "Projected arith vars:\n"
<< fml << "\n";
tout << "Remaining arith vars:\n"
<< arith_vars << "\n";);
TRACE("spacer_mbp", tout << "Projected arith vars: "<< fml << "\n";
tout << "Remaining arith vars:" << arith_vars << "\n";);
SASSERT(!m.is_false(fml));
}

if (!arith_vars.empty()) { mbqi_project(mdl, arith_vars, fml); }
if (!arith_vars.empty())
mbqi_project(mdl, arith_vars, fml);

// substitute any remaining arith vars
if (!dont_sub && !arith_vars.empty()) {
Expand All @@ -289,26 +290,30 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
SASSERT(mev.is_true(fml)););

vars.reset();
if (dont_sub && !arith_vars.empty()) { vars.append(arith_vars); }
vars.append(other_vars);
if (dont_sub && !arith_vars.empty())
vars.append(arith_vars);
TRACE("qe", tout << "after projection: " << fml << ": " << vars << "\n");
}

static expr *apply_accessor(ast_manager &m, ptr_vector<func_decl> const &acc,
unsigned j, func_decl *f, expr *c) {
if (is_app(c) && to_app(c)->get_decl() == f) {
if (is_app(c) && to_app(c)->get_decl() == f)
return to_app(c)->get_arg(j);
} else {
else
return m.mk_app(acc[j], c);
}
}

void qe_project(ast_manager &m, app_ref_vector &vars, expr_ref &fml, model &mdl,
bool reduce_all_selects, bool use_native_mbp, bool dont_sub) {
if (use_native_mbp)
qe_project_z3(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
dont_sub);
else
if (!use_native_mbp)
qe_project_spacer(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
dont_sub);

if (!vars.empty())
qe_project_z3(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
dont_sub);

}

void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
Expand All @@ -329,12 +334,14 @@ void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
conjs[i] = arith.mk_le(e1, e2);
if (i + 1 == conjs.size()) {
conjs.push_back(arith.mk_ge(e1, e2));
} else {
}
else {
conjs.push_back(conjs[i + 1].get());
conjs[i + 1] = arith.mk_ge(e1, e2);
}
++i;
} else if ((m.is_eq(e, c, val) && is_app(val) &&
}
else if ((m.is_eq(e, c, val) && is_app(val) &&
dt.is_constructor(to_app(val))) ||
(m.is_eq(e, val, c) && is_app(val) &&
dt.is_constructor(to_app(val)))) {
Expand All @@ -346,20 +353,20 @@ void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c),
to_app(val)->get_arg(j)));
}
} else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
}
else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
rational two(2);
for (unsigned j = 0; j < bv_size; ++j) {
parameter p(j);
expr *e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1),
bv.mk_extract(j, j, c));
if ((r % two).is_zero()) { e = m.mk_not(e); }
r = div(r, two);
if (j == 0) {
if (j == 0)
conjs[i] = e;
} else {
else
conjs.push_back(e);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/qe/mbp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ z3_add_component(mbp
mbp_basic_tg.cpp
mbp_datatypes.cpp
mbp_dt_tg.cpp
mbp_euf.cpp
mbp_qel.cpp
mbp_qel_util.cpp
mbp_plugin.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/qe/mbp/mbp_basic_tg.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class mbp_basic_tg : public mbp_tg_plugin {
struct impl;
impl *m_impl;

public:
public:
mbp_basic_tg(ast_manager &man, mbp::term_graph &tg, model &mdl,
obj_hashtable<app> &vars_set, expr_sparse_mark &seen);
// iterate through all terms in m_tg and apply all basic MBP rules once
Expand Down
85 changes: 85 additions & 0 deletions src/qe/mbp/mbp_euf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*++
Copyright (c) 2025 Microsoft Corporation
--*/


#include "ast/ast_util.h"
#include "ast/for_each_expr.h"
#include "qe/mbp/mbp_euf.h"
#include "qe/mbp/mbp_term_graph.h"

namespace mbp {
euf_project_plugin::euf_project_plugin(ast_manager& m): project_plugin(m) {

}

euf_project_plugin::~euf_project_plugin() {

}

bool euf_project_plugin::project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) {
return false;
}

family_id euf_project_plugin::get_family_id() {
return basic_family_id;
}

bool euf_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
return false;
}

bool euf_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
if (vars.empty())
return false;
flatten_and(lits);
expr_mark var_set;
auto is_pure = [&](expr_mark& var_set, expr* v) {
return all_of(subterms::all(expr_ref(v, m)), [&](expr* w) { return !var_set.is_marked(w); });
};
for (auto v : vars)
var_set.mark(v, true);
unsigned has_def = false;
#if 1
// solve trivial equations
for (auto e : lits) {
expr* x = nullptr, *y = nullptr;
if (m.is_eq(e, x, y) && var_set.is_marked(x) && is_pure(var_set, y)) {
vars.erase(to_app(x));
defs.push_back({ expr_ref(x, m), expr_ref(y, m) });
has_def = true;
}
else if (m.is_eq(e, y, x) && var_set.is_marked(x) && is_pure(var_set, y)) {
vars.erase(to_app(x));
defs.push_back({ expr_ref(x, m), expr_ref(y, m) });
has_def = true;
}
}
if (has_def)
return true;
#endif

// check if there is a variable of uninterp sort
if (all_of(vars, [&](expr* v) { return !m.is_uninterp(v->get_sort()); }))
return has_def;

term_graph tg(m);
tg.add_lits(lits);
for (auto v : vars)
if (m.is_uninterp(v->get_sort()))
tg.add_var(v);

//
// now what:
/// walk all subterms of lits.
// push in partitions by value.
// add equations from model
// compute repr from tg.
//


return has_def;
}

}
30 changes: 30 additions & 0 deletions src/qe/mbp/mbp_euf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

/*++
Copyright (c) 2025 Microsoft Corporation
--*/


#pragma once

#include "model/model.h"
#include "qe/mbp/mbp_plugin.h"

namespace mbp {

class euf_project_plugin : public project_plugin {
public:
euf_project_plugin(ast_manager& m);
~euf_project_plugin() override;

bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; }
family_id get_family_id() override;
bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override { UNREACHABLE(); }

};

};

2 changes: 1 addition & 1 deletion src/qe/mbp/mbp_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ namespace mbp {
else
extract_bools(eval, fmls, i, fml, true);
}
TRACE("qe", tout << fmls << "\n";);
TRACE("qe", tout << "fmls: " << fmls << "\n";);
}

void project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, unsigned idx, expr* fml, bool is_true) {
Expand Down
7 changes: 6 additions & 1 deletion src/qe/mbp/mbp_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Revision History:
#pragma once

#include "ast/ast.h"
#include "ast/ast_pp.h"
#include "util/params.h"
#include "model/model.h"
#include "math/simplex/model_based_opt.h"
Expand All @@ -32,11 +33,12 @@ namespace mbp {

struct def {
expr_ref var, term;
def(const expr_ref& v, expr_ref& t): var(v), term(t) {}
};

class project_plugin {
protected:
ast_manager& m;
private:
expr_mark m_visited;
ptr_vector<expr> m_to_visit;
expr_mark m_bool_visited;
Expand Down Expand Up @@ -110,3 +112,6 @@ namespace mbp {
};
}

inline std::ostream& operator<<(std::ostream& out, mbp::def const& d) {
return out << d.var << " -> " << d.term << "\n";
}
3 changes: 1 addition & 2 deletions src/qe/mbp/mbp_term_graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,8 +1018,7 @@ void term_graph::to_lits(expr_ref_vector &lits, bool all_equalities,
void term_graph::to_lits_qe_lite(expr_ref_vector &lits,
std::function<bool(expr *)> *non_core) {
DEBUG_CODE(for (auto t : m_terms) SASSERT(t->get_repr()););
DEBUG_CODE(for (auto t
: m_terms)
DEBUG_CODE(for (auto t : m_terms)
SASSERT(!t->is_cgr() || t->get_repr()->is_cgr()););
is_non_core not_in_core(non_core);
check_pred contains_nc(not_in_core, m, false);
Expand Down
Loading

0 comments on commit eee96ec

Please sign in to comment.