3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-03-22 20:39:11 +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:
Lev Nachmanson 2026-03-10 16:38:08 -10:00
parent bb11a56a67
commit 6d890fb026
8 changed files with 357 additions and 61 deletions

View file

@ -81,10 +81,15 @@ public:
void backup_x() { m_backup_x = m_r_x; }
void restore_x() {
SASSERT(m_backup_x.size() == m_r_A.column_count());
m_r_x = m_backup_x;
unsigned n = m_r_A.column_count();
unsigned backup_sz = m_backup_x.size();
unsigned copy_sz = std::min(backup_sz, n);
for (unsigned j = 0; j < copy_sz; j++)
m_r_x[j] = m_backup_x[j];
}
unsigned backup_x_size() const { return m_backup_x.size(); }
vector<impq> const& r_x() const { return m_r_x; }
impq& r_x(unsigned j) { return m_r_x[j]; }
impq const& r_x(unsigned j) const { return m_r_x[j]; }

View file

@ -467,6 +467,8 @@ namespace lp {
return ret;
}
lp_status lar_solver::solve() {
if (m_imp->m_status == lp_status::INFEASIBLE || m_imp->m_status == lp_status::CANCELLED)
return m_imp->m_status;
@ -2303,16 +2305,6 @@ namespace lp {
return m_imp->m_constraints.add_term_constraint(j, m_imp->m_columns[j].term(), kind, rs);
}
struct lar_solver::scoped_backup {
lar_solver& m_s;
scoped_backup(lar_solver& s) : m_s(s) {
m_s.get_core_solver().backup_x();
}
~scoped_backup() {
m_s.get_core_solver().restore_x();
}
};
void lar_solver::update_column_type_and_bound_with_ub(unsigned j, lp::lconstraint_kind kind, const mpq& right_side, u_dependency* dep) {
SASSERT(column_has_upper_bound(j));
if (column_has_lower_bound(j)) {

View file

@ -72,7 +72,6 @@ class lar_solver : public column_namer {
void clear_columns_with_changed_bounds();
struct scoped_backup;
public:
const indexed_uint_set& columns_with_changed_bounds() const;
void insert_to_columns_with_changed_bounds(unsigned j);
@ -437,7 +436,25 @@ public:
statistics& stats();
void backup_x() { get_core_solver().backup_x(); }
void restore_x() { get_core_solver().restore_x(); }
void restore_x() {
auto& cs = get_core_solver();
unsigned backup_sz = cs.backup_x_size();
unsigned current_sz = cs.m_n();
CTRACE(lar_solver_restore, backup_sz != current_sz,
tout << "restore_x: backup_sz=" << backup_sz
<< " current_sz=" << current_sz << "\n";);
cs.restore_x();
if (backup_sz < current_sz) {
// New columns were added after backup.
// move_non_basic_columns_to_bounds snaps non-basic
// columns to their bounds and finds a feasible solution.
move_non_basic_columns_to_bounds();
}
else {
SASSERT(ax_is_correct());
SASSERT(cs.m_r_solver.calc_current_x_is_feasible_include_non_basis());
}
}
void updt_params(params_ref const& p);
column_type get_column_type(unsigned j) const { return get_core_solver().m_column_types()[j]; }