Skip to content

Commit eee96ec

Browse files
bug fixes and cleanup in projection functions
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)
1 parent 0cf2b5f commit eee96ec

12 files changed

+248
-107
lines changed

genaisrc/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# auto-generated
2+
genaiscript.d.ts
3+
tsconfig.json
4+
jsconfig.json

src/muz/spacer/spacer_util.cpp

+42-35
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ void qe_project_z3(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
152152
params_ref p;
153153
p.set_bool("reduce_all_selects", reduce_all_selects);
154154
p.set_bool("dont_sub", dont_sub);
155+
TRACE("qe", tout << "qe-project-z3\n");
155156

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

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

183183
app_ref_vector arith_vars(m);
184184
app_ref_vector array_vars(m);
185+
app_ref_vector other_vars(m);
185186
array_util arr_u(m);
186187
arith_util ari_u(m);
187188
expr_safe_replace bool_sub(m);
@@ -194,8 +195,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
194195
rw(fml);
195196

196197
TRACE("spacer_mbp", tout << "After qe_lite:\n";
197-
tout << mk_pp(fml, m) << "\n"; tout << "Vars:\n"
198-
<< vars;);
198+
tout << mk_pp(fml, m) << "\nVars:" << vars << "\n";);
199199

200200
SASSERT(!m.is_false(fml));
201201

@@ -206,12 +206,13 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
206206
// using model completion
207207
model::scoped_model_completion _sc_(mdl, true);
208208
bool_sub.insert(v, mdl(v));
209-
} else if (arr_u.is_array(v)) {
209+
}
210+
else if (arr_u.is_array(v))
210211
array_vars.push_back(v);
211-
} else {
212-
SASSERT(ari_u.is_int(v) || ari_u.is_real(v));
212+
else if (ari_u.is_int(v) || ari_u.is_real(v))
213213
arith_vars.push_back(v);
214-
}
214+
else
215+
other_vars.push_back(v);
215216
}
216217

217218
// substitute Booleans
@@ -220,8 +221,7 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
220221
// -- bool_sub is not simplifying
221222
rw(fml);
222223
SASSERT(!m.is_false(fml));
223-
TRACE("spacer_mbp", tout << "Projected Booleans:\n"
224-
<< fml << "\n";);
224+
TRACE("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n";);
225225
bool_sub.reset();
226226
}
227227

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

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

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

248-
if (vars.empty()) { break; }
248+
if (vars.empty())
249+
break;
249250
}
250251

251252
// project reals and ints
252253
if (!arith_vars.empty()) {
253-
TRACE("spacer_mbp", tout << "Arith vars:\n" << arith_vars;);
254+
TRACE("spacer_mbp", tout << "Arith vars:" << arith_vars << "\n";);
254255

255256
if (use_native_mbp) {
256257
qe::mbproj mbp(m);
@@ -260,19 +261,19 @@ void qe_project_spacer(ast_manager &m, app_ref_vector &vars, expr_ref &fml,
260261
mbp(true, arith_vars, mdl, fmls);
261262
fml = mk_and(fmls);
262263
SASSERT(arith_vars.empty());
263-
} else {
264+
}
265+
else {
264266
scoped_no_proof _sp(m);
265267
spacer_qe::arith_project(mdl, arith_vars, fml);
266268
}
267269

268-
TRACE("spacer_mbp", tout << "Projected arith vars:\n"
269-
<< fml << "\n";
270-
tout << "Remaining arith vars:\n"
271-
<< arith_vars << "\n";);
270+
TRACE("spacer_mbp", tout << "Projected arith vars: "<< fml << "\n";
271+
tout << "Remaining arith vars:" << arith_vars << "\n";);
272272
SASSERT(!m.is_false(fml));
273273
}
274274

275-
if (!arith_vars.empty()) { mbqi_project(mdl, arith_vars, fml); }
275+
if (!arith_vars.empty())
276+
mbqi_project(mdl, arith_vars, fml);
276277

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

291292
vars.reset();
292-
if (dont_sub && !arith_vars.empty()) { vars.append(arith_vars); }
293+
vars.append(other_vars);
294+
if (dont_sub && !arith_vars.empty())
295+
vars.append(arith_vars);
296+
TRACE("qe", tout << "after projection: " << fml << ": " << vars << "\n");
293297
}
294298

295299
static expr *apply_accessor(ast_manager &m, ptr_vector<func_decl> const &acc,
296300
unsigned j, func_decl *f, expr *c) {
297-
if (is_app(c) && to_app(c)->get_decl() == f) {
301+
if (is_app(c) && to_app(c)->get_decl() == f)
298302
return to_app(c)->get_arg(j);
299-
} else {
303+
else
300304
return m.mk_app(acc[j], c);
301-
}
302305
}
303306

304307
void qe_project(ast_manager &m, app_ref_vector &vars, expr_ref &fml, model &mdl,
305308
bool reduce_all_selects, bool use_native_mbp, bool dont_sub) {
306-
if (use_native_mbp)
307-
qe_project_z3(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
308-
dont_sub);
309-
else
309+
if (!use_native_mbp)
310310
qe_project_spacer(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
311311
dont_sub);
312+
313+
if (!vars.empty())
314+
qe_project_z3(m, vars, fml, mdl, reduce_all_selects, use_native_mbp,
315+
dont_sub);
316+
312317
}
313318

314319
void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
@@ -329,12 +334,14 @@ void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
329334
conjs[i] = arith.mk_le(e1, e2);
330335
if (i + 1 == conjs.size()) {
331336
conjs.push_back(arith.mk_ge(e1, e2));
332-
} else {
337+
}
338+
else {
333339
conjs.push_back(conjs[i + 1].get());
334340
conjs[i + 1] = arith.mk_ge(e1, e2);
335341
}
336342
++i;
337-
} else if ((m.is_eq(e, c, val) && is_app(val) &&
343+
}
344+
else if ((m.is_eq(e, c, val) && is_app(val) &&
338345
dt.is_constructor(to_app(val))) ||
339346
(m.is_eq(e, val, c) && is_app(val) &&
340347
dt.is_constructor(to_app(val)))) {
@@ -346,20 +353,20 @@ void expand_literals(ast_manager &m, expr_ref_vector &conjs) {
346353
conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c),
347354
to_app(val)->get_arg(j)));
348355
}
349-
} else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
350-
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
356+
}
357+
else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
358+
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
351359
rational two(2);
352360
for (unsigned j = 0; j < bv_size; ++j) {
353361
parameter p(j);
354362
expr *e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1),
355363
bv.mk_extract(j, j, c));
356364
if ((r % two).is_zero()) { e = m.mk_not(e); }
357365
r = div(r, two);
358-
if (j == 0) {
366+
if (j == 0)
359367
conjs[i] = e;
360-
} else {
368+
else
361369
conjs.push_back(e);
362-
}
363370
}
364371
}
365372
}

src/qe/mbp/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ z3_add_component(mbp
66
mbp_basic_tg.cpp
77
mbp_datatypes.cpp
88
mbp_dt_tg.cpp
9+
mbp_euf.cpp
910
mbp_qel.cpp
1011
mbp_qel_util.cpp
1112
mbp_plugin.cpp

src/qe/mbp/mbp_basic_tg.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class mbp_basic_tg : public mbp_tg_plugin {
2727
struct impl;
2828
impl *m_impl;
2929

30-
public:
30+
public:
3131
mbp_basic_tg(ast_manager &man, mbp::term_graph &tg, model &mdl,
3232
obj_hashtable<app> &vars_set, expr_sparse_mark &seen);
3333
// iterate through all terms in m_tg and apply all basic MBP rules once

src/qe/mbp/mbp_euf.cpp

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*++
2+
Copyright (c) 2025 Microsoft Corporation
3+
4+
--*/
5+
6+
7+
#include "ast/ast_util.h"
8+
#include "ast/for_each_expr.h"
9+
#include "qe/mbp/mbp_euf.h"
10+
#include "qe/mbp/mbp_term_graph.h"
11+
12+
namespace mbp {
13+
euf_project_plugin::euf_project_plugin(ast_manager& m): project_plugin(m) {
14+
15+
}
16+
17+
euf_project_plugin::~euf_project_plugin() {
18+
19+
}
20+
21+
bool euf_project_plugin::project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) {
22+
return false;
23+
}
24+
25+
family_id euf_project_plugin::get_family_id() {
26+
return basic_family_id;
27+
}
28+
29+
bool euf_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) {
30+
return false;
31+
}
32+
33+
bool euf_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) {
34+
if (vars.empty())
35+
return false;
36+
flatten_and(lits);
37+
expr_mark var_set;
38+
auto is_pure = [&](expr_mark& var_set, expr* v) {
39+
return all_of(subterms::all(expr_ref(v, m)), [&](expr* w) { return !var_set.is_marked(w); });
40+
};
41+
for (auto v : vars)
42+
var_set.mark(v, true);
43+
unsigned has_def = false;
44+
#if 1
45+
// solve trivial equations
46+
for (auto e : lits) {
47+
expr* x = nullptr, *y = nullptr;
48+
if (m.is_eq(e, x, y) && var_set.is_marked(x) && is_pure(var_set, y)) {
49+
vars.erase(to_app(x));
50+
defs.push_back({ expr_ref(x, m), expr_ref(y, m) });
51+
has_def = true;
52+
}
53+
else if (m.is_eq(e, y, x) && var_set.is_marked(x) && is_pure(var_set, y)) {
54+
vars.erase(to_app(x));
55+
defs.push_back({ expr_ref(x, m), expr_ref(y, m) });
56+
has_def = true;
57+
}
58+
}
59+
if (has_def)
60+
return true;
61+
#endif
62+
63+
// check if there is a variable of uninterp sort
64+
if (all_of(vars, [&](expr* v) { return !m.is_uninterp(v->get_sort()); }))
65+
return has_def;
66+
67+
term_graph tg(m);
68+
tg.add_lits(lits);
69+
for (auto v : vars)
70+
if (m.is_uninterp(v->get_sort()))
71+
tg.add_var(v);
72+
73+
//
74+
// now what:
75+
/// walk all subterms of lits.
76+
// push in partitions by value.
77+
// add equations from model
78+
// compute repr from tg.
79+
//
80+
81+
82+
return has_def;
83+
}
84+
85+
}

src/qe/mbp/mbp_euf.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
/*++
3+
Copyright (c) 2025 Microsoft Corporation
4+
5+
--*/
6+
7+
8+
#pragma once
9+
10+
#include "model/model.h"
11+
#include "qe/mbp/mbp_plugin.h"
12+
13+
namespace mbp {
14+
15+
class euf_project_plugin : public project_plugin {
16+
public:
17+
euf_project_plugin(ast_manager& m);
18+
~euf_project_plugin() override;
19+
20+
bool project1(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override;
21+
bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override { return false; }
22+
family_id get_family_id() override;
23+
bool operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override;
24+
bool project(model& model, app_ref_vector& vars, expr_ref_vector& lits, vector<def>& defs) override;
25+
void saturate(model& model, func_decl_ref_vector const& shared, expr_ref_vector& lits) override { UNREACHABLE(); }
26+
27+
};
28+
29+
};
30+

src/qe/mbp/mbp_plugin.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ namespace mbp {
205205
else
206206
extract_bools(eval, fmls, i, fml, true);
207207
}
208-
TRACE("qe", tout << fmls << "\n";);
208+
TRACE("qe", tout << "fmls: " << fmls << "\n";);
209209
}
210210

211211
void project_plugin::extract_bools(model_evaluator& eval, expr_ref_vector& fmls, unsigned idx, expr* fml, bool is_true) {

src/qe/mbp/mbp_plugin.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Revision History:
2121
#pragma once
2222

2323
#include "ast/ast.h"
24+
#include "ast/ast_pp.h"
2425
#include "util/params.h"
2526
#include "model/model.h"
2627
#include "math/simplex/model_based_opt.h"
@@ -32,11 +33,12 @@ namespace mbp {
3233

3334
struct def {
3435
expr_ref var, term;
35-
def(const expr_ref& v, expr_ref& t): var(v), term(t) {}
3636
};
3737

3838
class project_plugin {
39+
protected:
3940
ast_manager& m;
41+
private:
4042
expr_mark m_visited;
4143
ptr_vector<expr> m_to_visit;
4244
expr_mark m_bool_visited;
@@ -110,3 +112,6 @@ namespace mbp {
110112
};
111113
}
112114

115+
inline std::ostream& operator<<(std::ostream& out, mbp::def const& d) {
116+
return out << d.var << " -> " << d.term << "\n";
117+
}

src/qe/mbp/mbp_term_graph.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -1018,8 +1018,7 @@ void term_graph::to_lits(expr_ref_vector &lits, bool all_equalities,
10181018
void term_graph::to_lits_qe_lite(expr_ref_vector &lits,
10191019
std::function<bool(expr *)> *non_core) {
10201020
DEBUG_CODE(for (auto t : m_terms) SASSERT(t->get_repr()););
1021-
DEBUG_CODE(for (auto t
1022-
: m_terms)
1021+
DEBUG_CODE(for (auto t : m_terms)
10231022
SASSERT(!t->is_cgr() || t->get_repr()->is_cgr()););
10241023
is_non_core not_in_core(non_core);
10251024
check_pred contains_nc(not_in_core, m, false);

0 commit comments

Comments
 (0)