mirror of
https://github.com/Z3Prover/z3
synced 2026-07-18 04:55:45 +00:00
Fix NLA optimization regression and relax restore_x
- Relax restore_x() to handle backup/current size mismatches: when backup is shorter (new columns added), call move_non_basic_columns_to_bounds() to find a feasible solution. - Fix 100x performance regression in nonlinear optimization: save LP optimum before check_nla and return it as bound regardless of NLA result, so opt_solver::check_bound() can validate via full re-solve with accumulated NLA lemmas. - Refactor theory_lra::maximize() into three helpers: max_with_lp(), max_with_nl(), and max_result(). - Add mk_gt(theory_var, impq const&) overload for building blockers from saved LP optimum values. - Add BNH multi-objective optimization test (7/7 sat in <1s vs 1/7 in 30s before fix). - Add restore_x test for backup size mismatch handling. Fixes #8890 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
bb11a56a67
commit
6d890fb026
8 changed files with 357 additions and 61 deletions
|
|
@ -564,6 +564,7 @@ void setup_args_parser(argument_parser &parser) {
|
|||
"test rationals using plus instead of +=");
|
||||
parser.add_option_with_help_string("--maximize_term", "test maximize_term()");
|
||||
parser.add_option_with_help_string("--patching", "test patching");
|
||||
parser.add_option_with_help_string("--restore_x", "test restore_x");
|
||||
}
|
||||
|
||||
struct fff {
|
||||
|
|
@ -1765,6 +1766,124 @@ void test_gomory_cut() {
|
|||
|
||||
void test_nla_order_lemma() { nla::test_order_lemma(); }
|
||||
|
||||
void test_restore_x() {
|
||||
std::cout << "testing restore_x" << std::endl;
|
||||
|
||||
// Test 1: backup shorter than current (new variables added after backup)
|
||||
{
|
||||
lar_solver solver;
|
||||
lpvar x = solver.add_var(0, false);
|
||||
lpvar y = solver.add_var(1, false);
|
||||
solver.add_var_bound(x, GE, mpq(0));
|
||||
solver.add_var_bound(x, LE, mpq(10));
|
||||
solver.add_var_bound(y, GE, mpq(0));
|
||||
solver.add_var_bound(y, LE, mpq(10));
|
||||
|
||||
vector<std::pair<mpq, lpvar>> coeffs;
|
||||
coeffs.push_back({mpq(1), x});
|
||||
coeffs.push_back({mpq(1), y});
|
||||
unsigned t = solver.add_term(coeffs, 2);
|
||||
solver.add_var_bound(t, GE, mpq(3));
|
||||
solver.add_var_bound(t, LE, mpq(15));
|
||||
|
||||
auto status = solver.solve();
|
||||
SASSERT(status == lp_status::OPTIMAL);
|
||||
|
||||
// Backup the current solution
|
||||
solver.backup_x();
|
||||
|
||||
// Add a new variable with bounds, making the system larger
|
||||
lpvar z = solver.add_var(3, false);
|
||||
solver.add_var_bound(z, GE, mpq(1));
|
||||
solver.add_var_bound(z, LE, mpq(5));
|
||||
|
||||
// restore_x should detect backup < current and call move_non_basic_columns_to_bounds
|
||||
solver.restore_x();
|
||||
|
||||
// The solver should find a feasible solution
|
||||
status = solver.get_status();
|
||||
SASSERT(status == lp_status::OPTIMAL || status == lp_status::FEASIBLE);
|
||||
std::cout << " test 1 (backup shorter): " << lp_status_to_string(status) << " - PASSED" << std::endl;
|
||||
}
|
||||
|
||||
// Test 2: backup longer than current (columns removed after backup, or pop)
|
||||
{
|
||||
lar_solver solver;
|
||||
lpvar x = solver.add_var(0, false);
|
||||
lpvar y = solver.add_var(1, false);
|
||||
solver.add_var_bound(x, GE, mpq(0));
|
||||
solver.add_var_bound(x, LE, mpq(10));
|
||||
solver.add_var_bound(y, GE, mpq(0));
|
||||
solver.add_var_bound(y, LE, mpq(10));
|
||||
|
||||
vector<std::pair<mpq, lpvar>> coeffs;
|
||||
coeffs.push_back({mpq(1), x});
|
||||
coeffs.push_back({mpq(1), y});
|
||||
unsigned t = solver.add_term(coeffs, 2);
|
||||
solver.add_var_bound(t, GE, mpq(2));
|
||||
|
||||
// Add more variables to make backup larger
|
||||
lpvar z = solver.add_var(3, false);
|
||||
solver.add_var_bound(z, GE, mpq(0));
|
||||
solver.add_var_bound(z, LE, mpq(5));
|
||||
|
||||
auto status = solver.solve();
|
||||
(void)status;
|
||||
SASSERT(status == lp_status::OPTIMAL);
|
||||
|
||||
// Backup with the full system
|
||||
solver.backup_x();
|
||||
|
||||
// restore_x with same-size backup should work fine
|
||||
solver.restore_x();
|
||||
std::cout << " test 2 (same size backup): PASSED" << std::endl;
|
||||
}
|
||||
|
||||
// Test 3: move_non_basic_columns_to_bounds after solve
|
||||
{
|
||||
lar_solver solver;
|
||||
lpvar x = solver.add_var(0, false);
|
||||
lpvar y = solver.add_var(1, false);
|
||||
solver.add_var_bound(x, GE, mpq(1));
|
||||
solver.add_var_bound(x, LE, mpq(10));
|
||||
solver.add_var_bound(y, GE, mpq(1));
|
||||
solver.add_var_bound(y, LE, mpq(10));
|
||||
|
||||
auto status = solver.solve();
|
||||
SASSERT(status == lp_status::OPTIMAL);
|
||||
|
||||
// Add new constraint: x + y >= 5
|
||||
vector<std::pair<mpq, lpvar>> coeffs;
|
||||
coeffs.push_back({mpq(1), x});
|
||||
coeffs.push_back({mpq(1), y});
|
||||
unsigned t = solver.add_term(coeffs, 2);
|
||||
solver.add_var_bound(t, GE, mpq(5));
|
||||
solver.add_var_bound(t, LE, mpq(15));
|
||||
|
||||
// Add another variable
|
||||
lpvar w = solver.add_var(3, false);
|
||||
solver.add_var_bound(w, GE, mpq(2));
|
||||
solver.add_var_bound(w, LE, mpq(8));
|
||||
|
||||
// Solve expanded system, then move non-basic columns to bounds
|
||||
status = solver.solve();
|
||||
SASSERT(status == lp_status::OPTIMAL);
|
||||
solver.move_non_basic_columns_to_bounds();
|
||||
status = solver.get_status();
|
||||
SASSERT(status == lp_status::OPTIMAL || status == lp_status::FEASIBLE);
|
||||
|
||||
// Verify the model satisfies the constraints
|
||||
std::unordered_map<lpvar, mpq> model;
|
||||
solver.get_model(model);
|
||||
SASSERT(model[x] >= mpq(1) && model[x] <= mpq(10));
|
||||
SASSERT(model[y] >= mpq(1) && model[y] <= mpq(10));
|
||||
SASSERT(model[w] >= mpq(2) && model[w] <= mpq(8));
|
||||
std::cout << " test 3 (move_non_basic_columns_to_bounds): " << lp_status_to_string(status) << " - PASSED" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "restore_x tests passed" << std::endl;
|
||||
}
|
||||
|
||||
void test_lp_local(int argn, char **argv) {
|
||||
// initialize_util_module();
|
||||
// initialize_numerics_module();
|
||||
|
|
@ -1792,6 +1911,10 @@ void test_lp_local(int argn, char **argv) {
|
|||
test_patching();
|
||||
return finalize(0);
|
||||
}
|
||||
if (args_parser.option_is_used("--restore_x")) {
|
||||
test_restore_x();
|
||||
return finalize(0);
|
||||
}
|
||||
if (args_parser.option_is_used("-nla_cn")) {
|
||||
#ifdef Z3DEBUG
|
||||
nla::test_cn();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue