3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-17 22:24:20 +00:00

Merge with branch lws (#8498)

* t0

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t1

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t2

* scaffoldin

* scaffolding

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* closer to the paper

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* more scaffolding

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* define symbolic_interval

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* use std::map instead of std::unordered_map

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* more accurate init of the relation between polynomial properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* pass anum_manager to levelwise, crash on sign

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* pass pmanager

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* create free function display functions

* use new display functions

* pass nlsat::solver to levelwise

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add trace tag for levelwise

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* define indexed root expression

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refact lws

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refact lws

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor lws

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* trying to figure out right indices

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* rename explain::main_operator to compute_conflict_explanation

* preprocess the input of levelwise to drop a level

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* ttt

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* renaming

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* rename

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* work on seed_properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* work on seed_properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* work on seed_properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* move a comment

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simplify

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simplify

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* debug

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor and assert _irreducible

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add a display method

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simplify

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simplify

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove erase_from_Q

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* ignore holds properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* got a section

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* introdure mk_prop

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove a parameter

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add parameter to suppress/enable levelwise

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* comment

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fixing factoring and hitting NOT_IMPLEMENTED on ir_ord

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* adding ir_ord

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* produce more literals but creating sat lemmas

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* try iterative factoring

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* new file

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* create irreducible polynomials on init

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add a guard on m_fail

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* process level 0 as well

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove a warning

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* debug

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* prepare to fill the relation

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* filling the relation

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* separate the lower and upper bound root functions

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix an assert statement

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* create a better queue on properties

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* normalize before pushing

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* relax an assert

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* rebase with master

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add stats to track levelwise calls

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* catch and fail on an exception

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix a bug in Rule 4.2

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove debug instruction

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* call levelwise on a correct set of polynomials

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* cosmetics

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* use polynomial_ref instead of poly*

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* do not refactor again multivariate polynomials

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* canonicalize polinomals in todo_set

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* canonicalize polynomials in nlsat

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

* normalize polynomials

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* try not to fail in add_sgn_inv_leading_coeff_for

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* use the cache consistently

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* unsound state

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* unsound state

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* handle the zero case in add_ord_inv_resultant

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* optimizations by using cached psc

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* make normalize optional

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Revert "make normalize optional"

This reverts commit c80cfb0b8e.

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* cleanup and more caching

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* better sort of root functions

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* index bug

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* with resultant calculation ignore one of p and q with a common root

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix the duplicate bug

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

* simplify by removing back propagation

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* hook up different relation build strategies for lws

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* introduce isolate_root_closest

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix a bug with non-adding ldcf

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simple choice of non-vanishing

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* restore choose_non_zero_coeff

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* efficient sort of root functions

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* avoid ldcf with the projective theorem

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* omit some disc

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* use std_vector more

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* avoid a compare call

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* try optimizing build_interval_and_relation

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* discard a discriminant only in the section case

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* refactor

* refactor

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* cache the polynomial roots

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Revert "cache the polynomial roots"

This reverts commit aefcd16aaa.

* ignore const non-null witnesses

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* toward more like SMT-RAT split

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* align with SMT-RAT

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* disables some heuristics in section

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Implement chain noLdcf optimization matching SMT-RAT

Add find_partition_boundary() helper to locate the boundary between
lower and upper root partitions in m_rfunc.

Implement compute_omit_lc_sector_chain() and compute_omit_lc_section_chain()
following SMT-RAT's OneCellCAD.h logic:
- Omit ldcf for extreme of lower chain (index 0) if it appears on upper side
- Omit ldcf for extreme of upper chain (last index) if it appears on lower side

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Restrict noDisc optimization to section_lowest_degree only

Match SMT-RAT behavior: noDisc (discriminant omission for leaves
connected only to section polynomial) is only applied for
sectionHeuristic == 3 (lowest_degree), not for biggest_cell or chain.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Cache partition boundary to avoid repeated algebraic number comparisons

Store the partition boundary (index of first root > sample) in
relation_E after sorting root functions. Use this cached value
in compute_omit_lc_sector_chain() and compute_omit_lc_section_chain()
instead of recomputing via algebraic number comparisons.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor levelwise: consolidate partition indices into m_l_rf/m_u_rf

Replace scattered local l_index/u_index parameters and m_partition_boundary
with two impl members:
- m_l_rf: position of lower bound in m_rel.m_rfunc
- m_u_rf: position of upper bound in m_rel.m_rfunc (UINT_MAX in section case)

This simplifies the code by:
- Removing parameter passing through multiple function calls
- Removing redundant m_partition_boundary from relation_E
- Making the partition state explicit in impl

Also clean up nlsat_explain.cpp to trust root_function_interval invariants:
- Section case: assert l and l_index are valid instead of defensive check
- Sector bounds: !l_inf()/!u_inf() implies valid polynomial and index

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor levelwise: use member variables for per-level state

Replace local variables and function parameters with member variables:
- m_level_ps: polynomials at current level (owned)
- m_level_tags: tags for each polynomial (owned)
- m_witnesses: non-zero coefficient witnesses
- m_poly_has_roots: which polynomials have roots
- m_todo: pointer to todo_set

Functions now use these member variables directly:
- extract_max_tagged() fills m_level_ps/m_level_tags and sets m_level
- process_level() and process_top_level() are now parameterless
- All helper functions use member variables instead of parameters

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor levelwise: change m_todo from pointer to member

- Change m_todo from todo_set* to todo_set
- Initialize m_todo in constructor initializer list
- Use m_todo.reset() in single_cell_work instead of creating local todo_set
- Replace pointer access (m_todo->) with member access (m_todo.)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add dynamic heuristic selection for levelwise projection

Implement weight-based dynamic selection of projection heuristics in
levelwise CAD. The weight function w(p, level) = deg(p, level) estimates
projection complexity, with w(res(a,b)) ≈ w(a) + w(b).

At each level, all three heuristics (biggest_cell, chain, lowest_degree)
are evaluated and the one with minimum estimated resultant weight is
selected. When fewer than 2 root functions exist, the default heuristic
is used since all produce equivalent results.

Add parameter nlsat.lws_dynamic_heuristic (default: true) to enable or
disable dynamic selection. When disabled, the static heuristic from
lws_sector_rel_mode/lws_section_rel_mode is used.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* local optimization

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* call omit_lc only when both bounds are present

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* use std_vector

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove m_level_tags

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* count added lcs in the heuriistic estimates

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add both side spanning tree heuristic

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Fix nlsat projection bug: ensure polynomials with assumptions are also projected

When polynomials are added as assumptions (via add_assumption or ensure_sign),
they must also be added to the projection set (m_todo) to ensure proper cell
construction. Previously, assumptions were added without corresponding projection,
leading to unsound lemmas.

Fixes:
1. In normalize(): collect lower-stage polynomials in m_lower_stage_polys and
   add them to m_ps in main() before projection.
2. In ensure_sign(): call insert_fresh_factors_in_todo(p) after adding assumption.
3. In project_cdcac(): when levelwise fails, use flet to set m_add_all_coeffs=true
   for the fallback projection.

* Remove deprecated project_original and cell_sample parameter

- Remove project_original() function from nlsat_explain.cpp
- Remove m_sample_cell_project member variable
- Simplify project() to just call project_cdcac()
- Remove cell_sample parameter from nlsat_params.pyg
- Update nlsat_solver.cpp to remove cell_sample() references
- Update nlsat_explain.h constructor signature

* Enforce bound polynomial LC protection in compute_omit_lc functions

Move the invariant that bound-defining polynomials must never have their
LC omitted from add_level_projections_sector() into the source functions:
- compute_omit_lc_both_sides()
- compute_omit_lc_chain_extremes()

This centralizes the protection and removes the redundant override check.

* fix the build

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* bug fixes

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* restore a deleted function

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove sector/section stats

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* Simplify levelwise: remove chain/lowest_degree heuristics, unify relation
   mode

     - Remove chain and lowest_degree heuristics, keep only biggest_cell and spanning_tree
     - Unify m_sector_relation_mode and m_section_relation_mode into single m_rel_mode
     - Remove lws_rel_mode, lws_sector_rel_mode, lws_section_rel_mode, lws_dynamic_heuristic params
     - lws_spt_threshold < 2 now disables spanning tree (single tuning parameter)
     - Restore noDisc optimization for spanning_tree leaves connected to boundary
     - Add noDisc for sector with same_boundary_poly (treat like section case)
     - Significant code reduction (~390 lines removed)

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix bug with skipping too many discriminants

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* t

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* simplifications and bug fixes in lws, use static_tree only with sector + different bound polynomials, otherwise us biggest cell

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* bug fixes and cleanup

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add the discriminant in degenerated case

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix a bug with skipping a vanishing discriminant

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove the unsound optimization

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fiddle with heuristics

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* preserve random seed in nlsat_solver::check_lemma

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix a typo in poly_has_roots

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* add lc(p) and disc(p) for a rootless p in section case

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* remove warnings

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* untracking .beads

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix the explosion in m_todo with lws.false

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* fix issue 8397

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* set default to nlsat.lws=false for the merge with master

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

* set nlsat.lws=true by default, enable levelwise

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>

---------

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Lev Nachmanson 2026-02-04 07:52:02 -10:00 committed by GitHub
parent 1f855efa09
commit 63f05ff6e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 4002 additions and 267 deletions

View file

@ -1,3 +1,4 @@
// int tttt = 0;
/*++
Copyright (c) 2012 Microsoft Corporation
@ -41,6 +42,7 @@ Revision History:
#include "nlsat/nlsat_simplify.h"
#include "nlsat/nlsat_simple_checker.h"
#include "nlsat/nlsat_variable_ordering_strategy.h"
#include "nlsat_solver.h"
#define NLSAT_EXTRA_VERBOSE
@ -52,7 +54,6 @@ Revision History:
namespace nlsat {
typedef chashtable<ineq_atom*, ineq_atom::hash_proc, ineq_atom::eq_proc> ineq_atom_table;
typedef chashtable<root_atom*, root_atom::hash_proc, root_atom::eq_proc> root_atom_table;
@ -227,9 +228,8 @@ namespace nlsat {
unsigned m_max_conflicts;
unsigned m_lemma_rlimit;
unsigned m_lemma_count;
unsigned m_variable_ordering_strategy;
unsigned m_variable_ordering_strategy;
bool m_set_0_more;
bool m_cell_sample;
struct stats {
unsigned m_simplifications;
@ -239,13 +239,18 @@ namespace nlsat {
unsigned m_decisions;
unsigned m_stages;
unsigned m_irrational_assignments; // number of irrational witnesses
unsigned m_levelwise_calls;
unsigned m_levelwise_failures;
unsigned m_lws_initial_fail;
void reset() { memset(this, 0, sizeof(*this)); }
stats() { reset(); }
};
// statistics
stats m_stats;
std::string m_debug_known_solution_file_name;
bool m_apply_lws;
bool m_last_conflict_used_lws = false; // Track if last conflict explanation used levelwise
unsigned m_lws_spt_threshold = 3;
imp(solver& s, ctx& c):
m_ctx(c),
m_solver(s),
@ -264,7 +269,7 @@ namespace nlsat {
m_simplify(s, m_atoms, m_clauses, m_learned, m_pm),
m_display_var(m_perm),
m_display_assumption(nullptr),
m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).cell_sample()),
m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator, nlsat_params(c.m_params).canonicalize()),
m_scope_lvl(0),
m_lemma(s),
m_lazy_clause(s),
@ -305,8 +310,9 @@ namespace nlsat {
m_check_lemmas = p.check_lemmas();
m_variable_ordering_strategy = p.variable_ordering_strategy();
m_debug_known_solution_file_name = p.known_sat_assignment_file_name();
m_apply_lws = p.lws();
m_lws_spt_threshold = p.lws_spt_threshold(); // 0 disables spanning tree
m_check_lemmas |= !(m_debug_known_solution_file_name.empty());
m_cell_sample = p.cell_sample();
m_ism.set_seed(m_random_seed);
m_explain.set_simplify_cores(m_simplify_cores);
@ -1016,7 +1022,8 @@ namespace nlsat {
}
// Helper: Setup checker solver and translate atoms/clauses
void setup_checker(imp& checker, scoped_bool_vars& tr, unsigned n, literal const* cls, assumption_set a) {
// Returns false if the lemma cannot be properly translated for checking
bool setup_checker(imp& checker, scoped_bool_vars& tr, unsigned n, literal const* cls, assumption_set a) {
auto pconvert = [&](poly* p) {
return convert(m_pm, p, checker.m_pm);
};
@ -1052,7 +1059,9 @@ namespace nlsat {
}
else {
// root atom cannot be translated due to variable ordering
bv = checker.mk_bool_var();
// Skip lemma check in this case
TRACE(nlsat, tout << "check_lemma: skipping due to untranslatable root atom\n";);
return false;
}
}
else {
@ -1079,20 +1088,32 @@ namespace nlsat {
literal nlit(tr[lit.var()], !lit.sign());
checker.mk_external_clause(1, &nlit, nullptr);
}
return true; // Successfully set up the checker
}
// Helper: Display unsound lemma failure information
void display_unsound_lemma(imp& checker, scoped_bool_vars& tr, unsigned n, literal const* cls) {
verbose_stream() << "\n";
verbose_stream() << "========== UNSOUND LEMMA DETECTED ==========\n";
verbose_stream() << "Levelwise used for this conflict: " << (m_last_conflict_used_lws ? "YES" : "NO") << "\n";
// Print polynomials passed to levelwise
if (m_last_conflict_used_lws) {
m_explain.display_last_lws_input(verbose_stream());
}
verbose_stream() << "The following lemma is NOT implied by the original clauses:\n";
display(verbose_stream() << " Lemma: ", n, cls) << "\n\n";
verbose_stream() << "Reason: Found a satisfying assignment where:\n";
verbose_stream() << " - The original clauses are satisfied\n";
verbose_stream() << " - But ALL literals in the lemma are FALSE\n\n";
// Display sample point (original solver's assignment)
verbose_stream() << "Variable values at SAMPLE point:\n";
display_num_assignment(verbose_stream());
// Display variable values used in the lemma
verbose_stream() << "Variable values in counterexample:\n";
verbose_stream() << "\nVariable values in counterexample:\n";
auto lemma_vars = collect_vars_on_clause(n, cls);
for (var x : lemma_vars) {
if (checker.m_assignment.is_assigned(x)) {
@ -1120,11 +1141,15 @@ namespace nlsat {
TRACE(nlsat, display(tout << "check lemma: ", n, cls) << "\n";
display(tout););
// Save RNG state before check_lemma to ensure determinism
unsigned saved_random_seed = m_random_seed;
unsigned saved_ism_seed = m_ism.get_seed();
try {
// Create a separate reslimit for the checker with 10 second timeout
reslimit checker_rlimit;
cancel_eh<reslimit> eh(checker_rlimit);
scoped_timer timer(10000, &eh);
scoped_timer timer(1000, &eh); // one second
ctx c(checker_rlimit, m_ctx.m_params, m_ctx.m_incremental);
solver solver2(c);
@ -1133,14 +1158,28 @@ namespace nlsat {
checker.m_log_lemmas = false;
checker.m_dump_mathematica = false;
checker.m_inline_vars = false;
checker.m_apply_lws = false; // Disable levelwise for checker to avoid recursive issues
scoped_bool_vars tr(checker);
setup_checker(checker, tr, n, cls, a);
if (!setup_checker(checker, tr, n, cls, a)) {
// Restore RNG state
m_random_seed = saved_random_seed;
m_ism.set_seed(saved_ism_seed);
return; // Lemma contains untranslatable atoms, skip check
}
lbool r = checker.check();
if (r == l_undef) // Checker timed out - skip this lemma check
return;
if (r == l_undef) {
// Restore RNG state
m_random_seed = saved_random_seed;
m_ism.set_seed(saved_ism_seed);
return; // Checker timed out - skip this lemma check
}
if (r == l_true) {
// Before reporting unsound, dump the lemma to see what we're checking
verbose_stream() << "Dumping lemma that internal checker thinks is not a tautology:\n";
verbose_stream() << "Checker levelwise calls: " << checker.m_stats.m_levelwise_calls << "\n";
log_lemma(verbose_stream(), n, cls, true, "internal-check-fail");
display_unsound_lemma(checker, tr, n, cls);
exit(1);
}
@ -1148,6 +1187,10 @@ namespace nlsat {
catch (...) {
// Ignore exceptions from the checker - just skip this lemma check
}
// Restore RNG state after check_lemma
m_random_seed = saved_random_seed;
m_ism.set_seed(saved_ism_seed);
}
void log_lemma(std::ostream& out, clause const& cls, std::string annotation) {
@ -1165,7 +1208,7 @@ namespace nlsat {
used_bools[b] = true;
vars.reset();
this->vars(lit, vars);
for (var v : vars)
for (var v : vars)
used_vars[v] = true;
}
display(out << "(echo \"#" << m_lemma_count++ << ":" << annotation << ":", n, cls) << "\")\n";
@ -1182,7 +1225,6 @@ namespace nlsat {
for (unsigned i = 0; i < n; ++i)
display_smt2(out << "(assert ", ~cls[i]) << ")\n";
out << "(check-sat)\n(reset)\n";
}
clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) {
@ -2120,9 +2162,8 @@ namespace nlsat {
collect(assumptions, m_learned);
del_clauses(m_valids);
if (m_check_lemmas)
for (clause* c : m_learned)
check_lemma(c->size(), c->data(), nullptr);
// Note: Don't check learned clauses here - they are the result of resolution
// and may not be tautologies. Conflict lemmas are checked in resolve_lazy_justification.
assumptions.reset();
assumptions.append(result);
@ -2338,8 +2379,8 @@ namespace nlsat {
m_lazy_clause.reset();
m_explain.main_operator(jst.num_lits(), jst.lits(), m_lazy_clause);
for (unsigned i = 0; i < sz; ++i)
m_explain.compute_conflict_explanation(jst.num_lits(), jst.lits(), m_lazy_clause);
for (unsigned i = 0; i < sz; i++)
m_lazy_clause.push_back(~jst.lit(i));
// lazy clause is a valid clause
@ -2359,6 +2400,8 @@ namespace nlsat {
}
if (m_check_lemmas) {
TRACE(nlsat, tout << "Checking lazy clause with " << m_lazy_clause.size() << " literals:\n";
display(tout, m_lazy_clause.size(), m_lazy_clause.data()) << "\n";);
check_lemma(m_lazy_clause.size(), m_lazy_clause.data(), nullptr);
m_valids.push_back(mk_clause_core(m_lazy_clause.size(), m_lazy_clause.data(), false, nullptr));
}
@ -2547,7 +2590,8 @@ namespace nlsat {
resolve_clause(b, *(jst.get_clause()));
break;
case justification::LAZY:
resolve_lazy_justification(b, *(jst.get_lazy()));
resolve_lazy_justification(b, *(jst.get_lazy()));
break;
case justification::DECISION:
SASSERT(m_num_marks == 0);
@ -2603,9 +2647,8 @@ namespace nlsat {
TRACE(nlsat, tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n";
tout << "found_decision: " << found_decision << "\n";);
if (m_check_lemmas) {
check_lemma(m_lemma.size(), m_lemma.data(), m_lemma_assumptions.get());
}
// Note: Don't check m_lemma here - it's the result of resolution
// and may not be a tautology. Conflict lemmas are checked in resolve_lazy_justification.
// if (m_log_lemmas)
// log_lemma(std::cout, m_lemma.size(), m_lemma.data(), false);
@ -2772,6 +2815,8 @@ namespace nlsat {
st.update("nlsat stages", m_stats.m_stages);
st.update("nlsat simplifications", m_stats.m_simplifications);
st.update("nlsat irrational assignments", m_stats.m_irrational_assignments);
st.update("levelwise calls", m_stats.m_levelwise_calls);
st.update("levelwise failures", m_stats.m_levelwise_failures);
}
void reset_statistics() {
@ -4373,10 +4418,19 @@ namespace nlsat {
nlsat_params::collect_param_descrs(d);
}
unsynch_mpq_manager & solver::qm() {
const assignment &solver::sample() const {
return m_imp->m_assignment;
}
assignment &solver::sample() {
return m_imp->m_assignment;
}
unsynch_mpq_manager &solver::qm()
{
return m_imp->m_qm;
}
anum_manager & solver::am() {
return m_imp->m_am;
}
@ -4626,6 +4680,15 @@ namespace nlsat {
m_imp->m_stats.m_simplifications++;
}
void solver::record_levelwise_result(bool success) {
m_imp->m_stats.m_levelwise_calls++;
m_imp->m_last_conflict_used_lws = success; // Track for unsound lemma reporting
if (!success) {
m_imp->m_stats.m_levelwise_failures++;
// m_imp->m_apply_lws = false; // is it useful to throttle
}
}
bool solver::has_root_atom(clause const& c) const {
return m_imp->has_root_atom(c);
}
@ -4637,6 +4700,9 @@ namespace nlsat {
assumption solver::join(assumption a, assumption b) {
return (m_imp->m_asm.mk_join(static_cast<imp::_assumption_set>(a), static_cast<imp::_assumption_set>(b)));
}
bool solver::apply_levelwise() const { return m_imp->m_apply_lws; }
unsigned solver::lws_spt_threshold() const { return m_imp->m_lws_spt_threshold; }
};