Skip to content

Commit f108364

Browse files
ilanashapirodependabot[bot]CopilotNikolajBjornerlevnach
authored
Updates to param tuning (#8008)
* draft attempt at optimizing cube tree with resolvents. have not tested/ran yet * adding comments * fix bug about needing to bubble resolvent upwards to highest ancestor * fix bug where we need to cover the whole resolvent in the path when bubbling up * clean up comments * Bump actions/checkout from 4 to 5 (#7954) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * close entire tree when sibling resolvent is empty * integrate asms directly into cube tree, remove separate tracking * try to fix bug about redundant resolutions, merging close and try_resolve_upwards into once function * separate the logic again to avoid mutual recursion * [WIP] Add a mutex to warning.cpp to ensure that warning messages from different threads don't interfere (#7963) * Initial plan * Add mutex to warning.cpp for thread safety Co-authored-by: NikolajBjorner <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: NikolajBjorner <[email protected]> * Remove unused variable 'first' in mpz.cpp Removed unused variable 'first' from the function. * fixing the order Signed-off-by: Lev Nachmanson <[email protected]> * fixing the order Signed-off-by: Lev Nachmanson <[email protected]> * fix the order of parameter evaluation Signed-off-by: Lev Nachmanson <[email protected]> * remove AI slop Signed-off-by: Nikolaj Bjorner <[email protected]> * param order Signed-off-by: Lev Nachmanson <[email protected]> * param order Signed-off-by: Lev Nachmanson <[email protected]> * param order evaluation * parameter eval order * parameter evaluation order * param eval * param eval order * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * parameter eval order Signed-off-by: Lev Nachmanson <[email protected]> * Bump github/codeql-action from 3 to 4 (#7971) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3 to 4. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@v3...v4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update Z3_mk_datatype_sort API to accept array of sort parameters and add Z3_mk_polymorphic_datatype (#7966) * Initial plan * Update Z3_mk_datatype_sort API to accept array of parameters Co-authored-by: NikolajBjorner <[email protected]> * Update language bindings for parametric datatype sort API - Python: Updated DatatypeSort() to accept optional params list - OCaml: Added mk_sort_ref_p/mk_sort_ref_ps for parametric datatypes - .NET: Added MkDatatypeSortRef() methods with optional params - Java: Added mkDatatypeSortRef() methods with optional params - All changes maintain backward compatibility Co-authored-by: NikolajBjorner <[email protected]> * Fix .NET build error - rename params to parameters The 'params' keyword is reserved in C#, causing compilation errors. Renamed parameter from 'params' to 'parameters' in MkDatatypeSortRef methods. Co-authored-by: NikolajBjorner <[email protected]> * Add unit test for parametric datatypes Added test_parametric_datatype.cpp to demonstrate polymorphic datatypes. The test creates two concrete instantiations of a generic pair concept: - pair_int_real with fields (first:Int, second:Real) - pair_real_int with fields (first:Real, second:Int) Then verifies that accessors work correctly and type checking is enforced. Co-authored-by: NikolajBjorner <[email protected]> * Implement polymorphic type variables support for parametric datatypes - Modified datatype_decl_plugin to allow type variables as parameters - Updated mk_datatype_decl to extract type variables from field sorts - Type variables are collected in order of first appearance - Revised unit test to use Z3_mk_type_variable for polymorphic datatypes - Test creates pair datatype with type variables alpha and beta - Successfully instantiates with concrete types (pair Int Real) and (pair Real Int) - Verifies accessor types match and equality terms are well-typed - All tests pass Co-authored-by: NikolajBjorner <[email protected]> * Add Z3_mk_polymorphic_datatype API and refactor datatype creation - Added new API Z3_mk_polymorphic_datatype to z3_api.h - Renamed static mk_datatype_decl to api_datatype_decl in api_datatype.cpp - Modified api_datatype_decl to accept explicit type parameters - Updated all callers to use renamed function - Added test_polymorphic_datatype_api demonstrating new API usage - Both tests pass successfully Co-authored-by: NikolajBjorner <[email protected]> * Remove type variable collection logic from constructors Removed the logic for collecting type variables from field sorts based on constructors. * Update comments on parameter handling in api_datatype.cpp Clarify usage of parameters in API documentation. * Fix OCaml build error - use list instead of array for mk_datatype_sort Changed mk_sort_ref to pass empty list [] instead of empty array [||]. Changed mk_sort_ref_p to pass params list directly instead of converting to array. Z3native.mk_datatype_sort expects a list, not an array. Co-authored-by: NikolajBjorner <[email protected]> * Add polymorphic datatype example to C++ examples Added polymorphic_datatype_example() demonstrating: - Creating type variables alpha and beta with Z3_mk_type_variable - Defining parametric Pair datatype with fields of type alpha and beta - Instantiating with concrete types (Pair Int Real) and (Pair Real Int) - Getting constructors and accessors from instantiated datatypes - Creating constants and expressions using the polymorphic types - Verifying type correctness with equality (= (first p1) (second p2)) Co-authored-by: NikolajBjorner <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: NikolajBjorner <[email protected]> Co-authored-by: Nikolaj Bjorner <[email protected]> * trim parametric datatype test Signed-off-by: Nikolaj Bjorner <[email protected]> * restore single cell Signed-off-by: Lev Nachmanson <[email protected]> * restore the method behavior Signed-off-by: Lev Nachmanson <[email protected]> * setting up python tuning experiment, not done * Add finite_set_value_factory for creating finite set values in model generation (#7981) * Initial plan * Add finite_set_value_factory implementation Co-authored-by: NikolajBjorner <[email protected]> * Remove unused dl_decl_plugin variable and include Co-authored-by: NikolajBjorner <[email protected]> * Update copyright and add TODOs in finite_set_value_factory Updated copyright information and added TODO comments for handling in finite_set_value_factory methods. * Update copyright information in finite_set_value_factory.h Updated copyright year from 2006 to 2025. * Implement finite_set_value_factory using array_util to create singleton sets Co-authored-by: NikolajBjorner <[email protected]> * Simplify empty set creation in finite_set_value_factory Refactor finite_set_value_factory to simplify empty set handling and remove array-specific logic. * Change family ID for finite_set_value_factory * Fix build error by restoring array_decl_plugin include and implementation Co-authored-by: NikolajBjorner <[email protected]> * Update finite_set_value_factory.h * Add SASSERT for finite set check in factory Added assertion to check if the sort is a finite set. * Rename member variable from m_util to u * Refactor finite_set_value_factory for value handling * Use register_value instead of direct set insertion Replaced direct insertion into set with register_value calls. * Update finite_set_value_factory.cpp --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: NikolajBjorner <[email protected]> Co-authored-by: Nikolaj Bjorner <[email protected]> * Revert "Add finite_set_value_factory for creating finite set values in model …" (#7985) This reverts commit 05ffc0a. * Update arith_rewriter.cpp fix memory leak introduced by update to ensure determinism * update pythonnn prototyping experiment, need to add a couple more things * add explicit constructors for nightly mac build failure Signed-off-by: Nikolaj Bjorner <[email protected]> * build fixes Signed-off-by: Nikolaj Bjorner <[email protected]> * fixes * fix some more things but now it hangs * change multithread to multiprocess seems to have resolved current deadlock * fix some bugs, it seems to run now * fix logic about checking clauses individually, and add proof prefix clause selection (naively) via the OnClause hook * disable manylinux until segfault is resolved Signed-off-by: Nikolaj Bjorner <[email protected]> * add the "noexcept" keyword to value_score=(value_score&&) declaration * expose a status flag for clauses but every single one is being coded as an assumption... * Add a fast-path to _coerce_exprs. (#7995) When the inputs are already the same sort, we can skip most of the coercion logic and just return. Currently, `_coerce_exprs` is by far the most expensive part of building up many common Z3 ASTs, so this fast-path is a substantial speedup for many use-cases. * Bump actions/setup-node from 5 to 6 (#7994) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v5...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Enabling Control Flow Guard (CFG) by default for MSVC on Windows, with options to disable CFG. (#7988) * Enabling Control Flow Guard by default for MSVC on Windows, with options to disable it. * Fix configuration error for non-MSVC compilers. * Reviewed and updated configuration for Python build and added comment for CFG. * try exponential delay in grobner Signed-off-by: Lev Nachmanson <[email protected]> * throttle grobner method more actively Signed-off-by: Lev Nachmanson <[email protected]> * enable always add all coeffs in nlsat Signed-off-by: Lev Nachmanson <[email protected]> * initial parameter probe thread setup in C++ * more param tuning setup * setting up the param probe solvers and mutation generator * adding the learned clauses from the internalizer * fix some things for clause replay * score the param probes, but i can't figure out how to access the relevant solver statistics fields from the statistics obj * set up pattern to notify batch manager so worker threads can update their params according ly * add a getter for solver stats. it compiles but still everything is untested * bugfix * updates to param tuning * remove the getter for solver statistics since we're getting the vals directly from the context --------- Signed-off-by: dependabot[bot] <[email protected]> Signed-off-by: Lev Nachmanson <[email protected]> Signed-off-by: Nikolaj Bjorner <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Copilot <[email protected]> Co-authored-by: NikolajBjorner <[email protected]> Co-authored-by: Nikolaj Bjorner <[email protected]> Co-authored-by: Lev Nachmanson <[email protected]> Co-authored-by: Nelson Elhage <[email protected]> Co-authored-by: hwisungi <[email protected]>
1 parent 8901f8f commit f108364

File tree

4 files changed

+65
-89
lines changed

4 files changed

+65
-89
lines changed

src/smt/smt_context.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ namespace smt {
137137
scoped_ptr<base_dependent_expr_state> m_fmls;
138138

139139
svector<double> m_lit_scores[2];
140-
vector<literal_vector> m_recorded_clauses;
140+
vector<expr_ref_vector> m_recorded_cubes;
141141

142142

143143
// -----------------------------------
@@ -1302,7 +1302,7 @@ namespace smt {
13021302

13031303
void add_scores(unsigned n, literal const *lits);
13041304

1305-
void record_clause(unsigned n, literal const * lits);
1305+
void record_cube(unsigned n, literal const * lits);
13061306

13071307

13081308
// -----------------------------------

src/smt/smt_internalizer.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -966,10 +966,17 @@ namespace smt {
966966
}
967967

968968
// following the pattern of solver::persist_clause in src/sat/smt/user_solver.cpp
969-
void context::record_clause(unsigned num_lits, literal const *lits) {
970-
literal_vector clause;
971-
clause.append(num_lits, lits);
972-
m_recorded_clauses.push_back(clause);
969+
void context::record_cube(unsigned num_lits, literal const *lits) {
970+
expr_ref_vector cube(m);
971+
for (unsigned i = 0; i < num_lits; ++i) {
972+
literal lit = lits[i];
973+
expr* e = bool_var2expr(lit.var());
974+
if (!e) continue;
975+
if (!lit.sign())
976+
e = m.mk_not(e); // only negate positive literal
977+
cube.push_back(e);
978+
}
979+
m_recorded_cubes.push_back(cube);
973980
}
974981

975982
void context::add_scores(unsigned n, literal const *lits) {
@@ -981,7 +988,8 @@ namespace smt {
981988

982989

983990
void context::undo_mk_bool_var() {
984-
SASSERT(!m_b_internalized_stack.empty());
991+
SASSERT(!m_b_internalized_stack.empty(ue key per literal
992+
m_lit_scores[lit.sign()][v] += 1.));
985993
m_stats.m_num_del_bool_var++;
986994
expr * n = m_b_internalized_stack.back();
987995
unsigned n_id = n->get_id();
@@ -1439,7 +1447,7 @@ namespace smt {
14391447
case CLS_LEARNED:
14401448
dump_lemma(num_lits, lits);
14411449
add_scores(num_lits, lits);
1442-
record_clause(num_lits, lits);
1450+
record_cube(num_lits, lits);
14431451
break;
14441452
default:
14451453
break;

src/smt/smt_parallel.cpp

Lines changed: 37 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -83,50 +83,51 @@ namespace smt {
8383
return r;
8484
}
8585

86-
unsigned parallel::param_generator::replay_proof_prefixes(vector<param_values> const& candidate_param_states, unsigned max_conflicts_epsilon=200) {
86+
std::pair<parallel::param_generator::param_values, bool> parallel::param_generator::replay_proof_prefixes(unsigned max_conflicts_epsilon=200) {
8787
unsigned conflict_budget = m_max_prefix_conflicts + max_conflicts_epsilon;
88-
unsigned best_param_state_idx;
88+
param_values best_param_state;
8989
double best_score;
90+
bool found_better_params = false;
9091

91-
for (unsigned i = 0; i < m_param_probe_contexts.size(); ++i) {
92+
for (unsigned i = 0; i < N; ++i) {
9293
IF_VERBOSE(1, verbose_stream() << " PARAM TUNER: replaying proof prefix in param probe context " << i << "\n");
93-
context *probe_ctx = m_param_probe_contexts[i];
94-
probe_ctx->get_fparams().m_max_conflicts = conflict_budget;
95-
double score = 0.0;
9694

97-
// apply the ith param state to probe_ctx
98-
params_ref p = apply_param_values(candidate_param_states[i]);
99-
probe_ctx->updt_params(p);
100-
101-
// todo: m_recorded_cubes as a expr_ref_vector
95+
// copy prefix solver context to a new probe_ctx for next replay with candidate mutation
96+
scoped_ptr<context> probe_ctx = alloc(context, m, ctx->get_fparams(), m_p);
97+
context::copy(*ctx, *probe_ctx, true);
98+
99+
// apply a candidate (mutated) param state to probe_ctx
100+
// (except for the first iteration, use the current param state)
101+
param_values mutated_param_state = m_param_state;
102+
if (i > 0) {
103+
mutated_param_state = mutate_param_state();
104+
params_ref p = apply_param_values(mutated_param_state);
105+
probe_ctx->updt_params(p);
106+
}
102107

103-
for (auto const& clause : probe_ctx->m_recorded_clauses) {
104-
expr_ref_vector negated_lits(probe_ctx->m);
105-
for (literal lit : clause) {
106-
expr* e = probe_ctx->bool_var2expr(lit.var());
107-
if (!e) continue; // skip if var not yet mapped
108-
if (!lit.sign())
109-
e = probe_ctx->m.mk_not(e); // since bool_var2expr discards sign
110-
negated_lits.push_back(e);
111-
}
108+
probe_ctx->get_fparams().m_max_conflicts = conflict_budget;
109+
double score = 0.0;
112110

113-
// Replay the negated clause
114-
115-
lbool r = probe_ctx->check(negated_lits.size(), negated_lits.data());
111+
// replay the cube (negation of the clause)
112+
for (expr_ref_vector const& cube : probe_ctx->m_recorded_cubes) {
113+
lbool r = probe_ctx->check(cube.size(), cube.data());
116114

117115
unsigned conflicts = probe_ctx->m_stats.m_num_conflicts;
118116
unsigned decisions = probe_ctx->m_stats.m_num_decisions;
119117

120118
score += conflicts + decisions;
121119
}
122120

123-
if (i == 0 || score < best_score) {
121+
if (i > 0 && score < best_score) {
122+
found_better_params = true;
123+
best_param_state = mutated_param_state;
124+
best_score = score;
125+
} else {
124126
best_score = score;
125-
best_param_state_idx = i;
126127
}
127128
}
128129

129-
return best_param_state_idx;
130+
return {best_param_state, found_better_params};
130131
}
131132

132133
void parallel::param_generator::init_param_state() {
@@ -147,7 +148,6 @@ namespace smt {
147148
};
148149

149150
parallel::param_generator::param_values parallel::param_generator::mutate_param_state() {
150-
151151
param_values new_param_values(m_param_state);
152152
unsigned index = ctx->get_random_value() % new_param_values.size();
153153
auto &param = new_param_values[index];
@@ -168,41 +168,23 @@ namespace smt {
168168

169169
void parallel::param_generator::protocol_iteration() {
170170
IF_VERBOSE(1, verbose_stream() << " PARAM TUNER running protocol iteration\n");
171-
ctx->get_fparams().m_max_conflicts = m_max_prefix_conflicts;
172-
173-
// copy current param state to all param probe contexts, before running the next prefix step
174-
// this ensures that each param probe context replays the prefix from the same configuration
175-
176-
// instead just one one context and reset it each time before copy.
177-
for (unsigned i = 0; i < m_param_probe_contexts.size(); ++i) {
178-
context::copy(*ctx, *m_param_probe_contexts[i], true);
179-
}
180171

172+
ctx->get_fparams().m_max_conflicts = m_max_prefix_conflicts;
181173
lbool r = run_prefix_step();
182174

183175
switch (r) {
184176
case l_undef: {
185-
// TODO, change from smt_params to a generic param state representation based on params_ref
186-
// only params_ref have effect on updates.
187-
param_values best_param_state = m_param_state;
188-
vector<param_values> candidate_param_states;
189-
190-
// you can create the mutations on the fly and get the scores
191-
// you don't have to copy all over each tester.
192-
193-
194-
candidate_param_states.push_back(best_param_state); // first candidate param state is current best
195-
while (candidate_param_states.size() <= N) {
196-
candidate_param_states.push_back(mutate_param_state());
197-
}
198-
199-
unsigned best_param_state_idx = replay_proof_prefixes(candidate_param_states);
200-
201-
if (best_param_state_idx != 0) {
202-
m_param_state = candidate_param_states[best_param_state_idx];
177+
auto [best_param_state, found_better_params] = replay_proof_prefixes();
178+
179+
// NOTE: we either need to return a pair from replay_proof_prefixes so we can return a boolean flag indicating whether better params were found.
180+
// or, we have to implement a comparison operator for param_values
181+
// or, we update the param state every single time even if it hasn't changed
182+
// for now, I went with option 1
183+
if (found_better_params) {
184+
m_param_state = best_param_state;
203185
auto p = apply_param_values(m_param_state);
204186
b.set_param_state(p);
205-
IF_VERBOSE(1, verbose_stream() << " PARAM TUNER found better param state at index " << best_param_state_idx << "\n");
187+
IF_VERBOSE(1, verbose_stream() << " PARAM TUNER found better param state\n");
206188
} else {
207189
IF_VERBOSE(1, verbose_stream() << " PARAM TUNER retained current param state\n");
208190
}
@@ -315,11 +297,6 @@ namespace smt {
315297
: p(p), b(p.m_batch_manager), m_p(p.ctx.get_params()), m_l2g(m, p.ctx.m) {
316298
ctx = alloc(context, m, p.ctx.get_fparams(), m_p);
317299
context::copy(p.ctx, *ctx, true);
318-
319-
for (unsigned i = 0; i < N; ++i) {
320-
m_param_probe_contexts.push_back(alloc(context, m, ctx->get_fparams(), m_p));
321-
}
322-
323300
// don't share initial units
324301
ctx->pop_to_base_lvl();
325302
init_param_state();

src/smt/smt_parallel.h

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,12 @@ Revision History:
2121
#include "smt/smt_context.h"
2222
#include "util/search_tree.h"
2323
#include <variant>
24-
// #include "util/util.h"
2524
#include <thread>
2625
#include <mutex>
2726

2827

2928
namespace smt {
3029

31-
inline bool operator==(const smt_params& a, const smt_params& b) {
32-
return a.m_nl_arith_branching == b.m_nl_arith_branching &&
33-
a.m_nl_arith_cross_nested == b.m_nl_arith_cross_nested &&
34-
a.m_nl_arith_delay == b.m_nl_arith_delay &&
35-
a.m_nl_arith_expensive_patching == b.m_nl_arith_expensive_patching &&
36-
a.m_nl_arith_gb == b.m_nl_arith_gb &&
37-
a.m_nl_arith_horner == b.m_nl_arith_horner &&
38-
a.m_nl_arith_horner_frequency == b.m_nl_arith_horner_frequency &&
39-
a.m_nl_arith_optimize_bounds == b.m_nl_arith_optimize_bounds &&
40-
a.m_nl_arith_propagate_linear_monomials == b.m_nl_arith_propagate_linear_monomials &&
41-
a.m_nl_arith_tangents == b.m_nl_arith_tangents;
42-
}
43-
44-
inline bool operator!=(const smt_params& a, const smt_params& b) {
45-
return !(a == b);
46-
}
47-
4830
struct cube_config {
4931
using literal = expr_ref;
5032
static bool literal_is_null(expr_ref const& l) { return l == nullptr; }
@@ -151,9 +133,18 @@ namespace smt {
151133
param_values m_param_state;
152134

153135
params_ref apply_param_values(param_values const &pv) {
154-
return m_p;
136+
params_ref p = m_p;
137+
for (auto const& [k, v] : pv) {
138+
if (std::holds_alternative<unsigned_value>(v)) {
139+
unsigned_value uv = std::get<unsigned_value>(v);
140+
p.set_uint(k, uv.value);
141+
} else if (std::holds_alternative<bool>(v)) {
142+
bool bv = std::get<bool>(v);
143+
p.set_bool(k, bv);
144+
}
145+
}
146+
return p;
155147
}
156-
// todo
157148

158149
private:
159150
void init_param_state();
@@ -164,7 +155,7 @@ namespace smt {
164155
param_generator(parallel &p);
165156
lbool run_prefix_step();
166157
void protocol_iteration();
167-
unsigned replay_proof_prefixes(vector<param_values> const& candidate_param_states, unsigned max_conflicts_epsilon);
158+
std::pair<parallel::param_generator::param_values, bool> replay_proof_prefixes(unsigned max_conflicts_epsilon);
168159

169160
reslimit &limit() {
170161
return m.limit();

0 commit comments

Comments
 (0)