mirror of
https://github.com/Z3Prover/z3
synced 2026-04-15 08:44:10 +00:00
When solve-eqs eliminates a variable x (= a - b) that appears as a factor in a nonlinear product x*y, the product splits into a*y - b*y. The NLA solver then reasons about a*y and b*y independently, losing the tight bounds that x had. This can cause severe performance degradation (e.g., timeout on a QF_UFNIA verification condition that solves in 3s without solve-eqs). The Horner module's cross-nested factoring already recovers the factored form y*(a-b), and interval_from_term (fixed in the previous commit) finds the LP column for (a-b) with its tight bounds. However, only Horner's zero-exclusion check used this — the rest of the NLA solver (order lemmas, tangent planes, bounds propagation) continued reasoning about the split monomials independently. This commit adds a new mechanism: when Horner discovers that a linear sub-expression maps to a bounded LP column, it introduces a new monomial pairing that column with the shared factor. For example, if y*(a-b) is discovered and (a-b) maps to LP column j with bounds [L,U], we create a new monomial m := y*j via add_mul_def and assert the equality m = a*y - b*y via literals. This allows all NLA modules to generate lemmas using j's tight bounds. The feature is gated by smt.arith.nl.horner_max_new_monomials (default 2, 0 to disable). On the motivating benchmark, this changes simplify+propagate-values+solve-eqs+smt from timeout (30s) to UNSAT in ~15s with no regressions on other configurations. Files changed: - horner.cpp: introduce_monomials_from_term_columns() and find_binary_monic() - horner.h: m_introduced_monomials dedup set - nla_intervals.cpp/h: m_term_columns to record interval_from_term discoveries - smt_params_helper.pyg: arith.nl.horner_max_new_monomials parameter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
55 lines
1.4 KiB
C++
55 lines
1.4 KiB
C++
/*++
|
|
Copyright (c) 2017 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
<name>
|
|
|
|
Abstract:
|
|
|
|
<abstract>
|
|
|
|
Author:
|
|
Nikolaj Bjorner (nbjorner)
|
|
Lev Nachmanson (levnach)
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
#pragma once
|
|
|
|
#include "math/lp/nla_common.h"
|
|
#include "math/lp/nla_intervals.h"
|
|
#include "math/lp/nex.h"
|
|
#include "math/lp/cross_nested.h"
|
|
#include "util/uint_set.h"
|
|
|
|
namespace nla {
|
|
class core;
|
|
|
|
|
|
class horner : common {
|
|
nex_creator::sum_factory m_row_sum;
|
|
unsigned m_row_index;
|
|
svector<std::pair<lpvar, lpvar>> m_introduced_monomials; // track what we've already created
|
|
public:
|
|
typedef intervals::interval interv;
|
|
horner(core *core);
|
|
bool horner_lemmas();
|
|
template <typename T> // T has an iterator of (coeff(), var())
|
|
bool lemmas_on_row(const T&);
|
|
template <typename T> bool row_is_interesting(const T&) const;
|
|
|
|
|
|
intervals::interval interval_of_sum_with_deps(const nex_sum*);
|
|
intervals::interval interval_of_sum_no_term_with_deps(const nex_sum*);
|
|
void set_var_interval_with_deps(lpvar j, intervals::interval&) const;
|
|
bool lemmas_on_expr(cross_nested&, nex_sum*);
|
|
|
|
template <typename T> // T has an iterator of (coeff(), var())
|
|
bool row_has_monomial_to_refine(const T&) const;
|
|
bool interval_from_term_with_deps(const nex* e, intervals::interval&) const;
|
|
void introduce_monomials_from_term_columns();
|
|
}; // end of horner
|
|
}
|