Skip to content

Commit 0c48a50

Browse files
Add support for initializing variable values in solver and optimize contexts in Z3
1 parent 342dccd commit 0c48a50

File tree

12 files changed

+98
-9
lines changed

12 files changed

+98
-9
lines changed

RELEASE_NOTES.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,18 @@ Version 4.13.1
1818
The projection is described in paper by Haokun Li and Bican Xia, [Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection](https://arxiv.org/abs/2003.00409). The code ported from https://github.com/hybridSMT/hybridSMT.git
1919

2020
- Add API for providing hints for the solver/optimize contexts for which initial values to attempt to use for variables.
21-
The new API function are Z3_solver_set_initial_value and Z3_optimize_set_initial_value, respectively. Supply these functions with a Boolean or numeric variable, and a value. The solver will then attempt to use these values in the initial phase of search. The feature is aimed at resolving nearly similar problems, or problems with a predicted model and the intent is that restarting the solver based on a near solution can avoid prune the space of constraints that are initially infeasible.
21+
The new API function are Z3_solver_set_initial_value and Z3_optimize_set_initial_value, respectively. Supply these functions with a Boolean or numeric variable, and a value. The solver will then attempt to use these values in the initial phase of search. The feature is aimed at resolving nearly similar problems, or problems with a predicted model and the intent is that restarting the solver based on a near solution can avoid prune the space of constraints that are initially infeasible.
22+
The SMTLIB front-end contains the new command (set-initial-value var value). For example,
23+
(declare-const x Int)
24+
(set-initial-value x 10)
25+
(push)
26+
(assert (> x 0))
27+
(check-sat)
28+
(get-model)
29+
produces a model where x = 10. We use (push) to ensure that z3 doesn't run a
30+
specialized pre-processor that eliminates x, which renders the initialization
31+
without effect.
32+
2233

2334
Version 4.13.0
2435
==============

src/ast/ast.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,14 +1008,19 @@ sort* basic_decl_plugin::join(unsigned n, expr* const* es) {
10081008
}
10091009

10101010
sort* basic_decl_plugin::join(sort* s1, sort* s2) {
1011-
if (s1 == s2) return s1;
1011+
if (s1 == s2)
1012+
return s1;
10121013
if (s1->get_family_id() == arith_family_id &&
10131014
s2->get_family_id() == arith_family_id) {
10141015
if (s1->get_decl_kind() == REAL_SORT) {
10151016
return s1;
10161017
}
10171018
return s2;
10181019
}
1020+
if (s1 == m_bool_sort && s2->get_family_id() == arith_family_id)
1021+
return s2;
1022+
if (s2 == m_bool_sort && s1->get_family_id() == arith_family_id)
1023+
return s1;
10191024
std::ostringstream buffer;
10201025
buffer << "Sorts " << mk_pp(s1, *m_manager) << " and " << mk_pp(s2, *m_manager) << " are incompatible";
10211026
throw ast_exception(buffer.str());

src/ast/converters/generic_model_converter.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,47 @@ generic_model_converter * generic_model_converter::copy(ast_translation & transl
130130
return res;
131131
}
132132

133+
void generic_model_converter::convert_initialize_value(expr_ref& var, expr_ref& value) {
134+
for (auto const& e : m_entries) {
135+
switch (e.m_instruction) {
136+
case HIDE:
137+
break;
138+
case ADD:
139+
if (is_uninterp_const(var) && e.m_f == to_app(var)->get_decl())
140+
convert_initialize_value(e.m_def, var, value);
141+
break;
142+
}
143+
}
144+
}
145+
146+
void generic_model_converter::convert_initialize_value(expr* def, expr_ref& var, expr_ref& value) {
147+
148+
// var = if(c, th, el) = value
149+
// th = value => c = true
150+
// el = value => c = false
151+
expr* c = nullptr, *th = nullptr, *el = nullptr;
152+
if (m.is_ite(def, c, th, el)) {
153+
if (value == th) {
154+
var = c;
155+
value = m.mk_true();
156+
return;
157+
}
158+
if (value == el) {
159+
var = c;
160+
value = m.mk_false();
161+
return;
162+
}
163+
}
164+
165+
// var = def = value
166+
// => def = value
167+
if (is_uninterp(def))
168+
var = def;
169+
170+
171+
}
172+
173+
133174

134175
void generic_model_converter::set_env(ast_pp_util* visitor) {
135176
if (!visitor) {

src/ast/converters/generic_model_converter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class generic_model_converter : public model_converter {
3737
vector<entry> m_entries;
3838

3939
expr_ref simplify_def(entry const& e);
40+
void convert_initialize_value(expr* def, expr_ref& var, expr_ref& value);
4041

4142
public:
4243
generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {}
@@ -61,6 +62,8 @@ class generic_model_converter : public model_converter {
6162

6263
model_converter * translate(ast_translation & translator) override { return copy(translator); }
6364

65+
void convert_initialize_value(expr_ref& var, expr_ref& value) override;
66+
6467
generic_model_converter* copy(ast_translation & translator);
6568

6669
void set_env(ast_pp_util* visitor) override;

src/ast/converters/model_converter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ class concat_model_converter : public concat_converter<model_converter> {
107107
m_c2->get_units(fmls);
108108
m_c1->get_units(fmls);
109109
}
110+
111+
void convert_initialize_value(expr_ref& var, expr_ref& value) override {
112+
m_c2->convert_initialize_value(var, value);
113+
m_c1->convert_initialize_value(var, value);
114+
}
115+
110116

111117
char const * get_name() const override { return "concat-model-converter"; }
112118

src/ast/converters/model_converter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class model_converter : public converter {
8686

8787
virtual void set_env(ast_pp_util* visitor);
8888

89+
virtual void convert_initialize_value(expr_ref& var, expr_ref& value) { }
90+
8991
/**
9092
\brief we are adding a formula to the context of the model converter.
9193
The operator has as side effect of adding definitions as assertions to the

src/cmd_context/basic_cmds.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,7 @@ class set_initial_value_cmd : public cmd {
330330
void set_next_arg(cmd_context& ctx, expr* e) override { if (m_var) m_value = e; else m_var = e; }
331331
void execute(cmd_context& ctx) override {
332332
SASSERT(m_var && m_value);
333-
if (ctx.get_opt())
334-
ctx.get_opt()->initialize_value(m_var, m_value);
335-
else if (ctx.get_solver())
336-
ctx.get_solver()->user_propagate_initialize_value(m_var, m_value);
333+
ctx.set_initial_value(m_var, m_value);
337334
}
338335
};
339336

src/cmd_context/cmd_context.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ cmd_context::~cmd_context() {
629629
finalize_cmds();
630630
finalize_tactic_manager();
631631
m_proof_cmds = nullptr;
632+
m_var2values.reset();
632633
reset(true);
633634
m_mcs.reset();
634635
m_solver = nullptr;
@@ -654,6 +655,8 @@ void cmd_context::set_opt(opt_wrapper* opt) {
654655
m_opt = opt;
655656
for (unsigned i = 0; i < m_scopes.size(); ++i)
656657
m_opt->push();
658+
for (auto const& [var, value] : m_var2values)
659+
m_opt->initialize_value(var, value);
657660
m_opt->set_logic(m_logic);
658661
}
659662

@@ -1874,6 +1877,17 @@ void cmd_context::display_dimacs() {
18741877
}
18751878
}
18761879

1880+
void cmd_context::set_initial_value(expr* var, expr* value) {
1881+
if (get_opt()) {
1882+
get_opt()->initialize_value(var, value);
1883+
return;
1884+
}
1885+
if (get_solver())
1886+
get_solver()->user_propagate_initialize_value(var, value);
1887+
m_var2values.push_back({expr_ref(var, m()), expr_ref(value, m())});
1888+
}
1889+
1890+
18771891
void cmd_context::display_model(model_ref& mdl) {
18781892
if (mdl) {
18791893
if (mc0()) (*mc0())(mdl);

src/cmd_context/cmd_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_
262262
scoped_ptr_vector<builtin_decl> m_extra_builtin_decls; // make sure that dynamically allocated builtin_decls are deleted
263263
dictionary<object_ref*> m_object_refs; // anything that can be named.
264264
dictionary<sexpr*> m_user_tactic_decls;
265+
vector<std::pair<expr_ref, expr_ref>> m_var2values;
265266

266267
dictionary<func_decls> m_func_decls;
267268
obj_map<func_decl, symbol> m_func_decl2alias;
@@ -421,6 +422,7 @@ class cmd_context : public progress_callback, public tactic_manager, public ast_
421422
solver* get_solver() { return m_solver.get(); }
422423
void set_solver(solver* s) { m_solver = s; }
423424
void set_proof_cmds(proof_cmds* pc) { m_proof_cmds = pc; }
425+
void set_initial_value(expr* var, expr* value);
424426

425427
void set_solver_factory(solver_factory * s);
426428
void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; }

src/opt/opt_context.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,9 @@ namespace opt {
311311
}
312312
solver& s = get_solver();
313313
s.assert_expr(m_hard_constraints);
314-
for (auto const& [var, value] : m_scoped_state.m_values) {
314+
for (auto & [var, value] : m_scoped_state.m_values) {
315+
if (m_model_converter)
316+
m_model_converter->convert_initialize_value(var, value);
315317
s.user_propagate_initialize_value(var, value);
316318
}
317319

0 commit comments

Comments
 (0)