mirror of
https://github.com/Z3Prover/z3
synced 2026-05-31 14:17:47 +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 c80cfb0b8e3e260aec6dabaf2e686e347b514896.
* 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 aefcd16aaad2cbd4de804c8de47678886eb8ba92.
* 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:
parent
1f855efa09
commit
63f05ff6e6
22 changed files with 4002 additions and 267 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -4,3 +4,6 @@
|
||||||
src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf
|
src/api/dotnet/Properties/AssemblyInfo.cs text eol=crlf
|
||||||
|
|
||||||
.github/workflows/*.lock.yml linguist-generated=true merge=ours
|
.github/workflows/*.lock.yml linguist-generated=true merge=ours
|
||||||
|
|
||||||
|
# Use bd merge for beads JSONL files
|
||||||
|
.beads/issues.jsonl merge=beads
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -115,3 +115,5 @@ genaisrc/genblogpost.genai.mts
|
||||||
*.mts
|
*.mts
|
||||||
# Bazel generated files
|
# Bazel generated files
|
||||||
bazel-*
|
bazel-*
|
||||||
|
# Local issue tracking
|
||||||
|
.beads
|
||||||
|
|
|
||||||
BIN
levelwise.pdf
Normal file
BIN
levelwise.pdf
Normal file
Binary file not shown.
|
|
@ -678,6 +678,169 @@ namespace algebraic_numbers {
|
||||||
isolate_roots(up, roots);
|
isolate_roots(up, roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned sign_variations_at_mpq(upolynomial::upolynomial_sequence const & seq, mpq const & b) {
|
||||||
|
unsigned sz = seq.size();
|
||||||
|
if (sz <= 1)
|
||||||
|
return 0;
|
||||||
|
unsigned r = 0;
|
||||||
|
int sign = 0, prev_sign = 0;
|
||||||
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
|
unsigned psz = seq.size(i);
|
||||||
|
mpz const * p = seq.coeffs(i);
|
||||||
|
sign = static_cast<int>(upm().eval_sign_at(psz, p, b));
|
||||||
|
if (sign == 0)
|
||||||
|
continue;
|
||||||
|
if (prev_sign != 0 && sign != prev_sign)
|
||||||
|
r++;
|
||||||
|
prev_sign = sign;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Isolate the i-th real root of sqf_p (1-based), where sqf_p is square-free and univariate.
|
||||||
|
// Return the root as an algebraic number in r. The polynomial stored in the result is sqf_p.
|
||||||
|
void isolate_kth_root(scoped_upoly const & sqf_p, upolynomial::upolynomial_sequence const & seq, unsigned i, numeral & r) {
|
||||||
|
SASSERT(i > 0);
|
||||||
|
unsigned sz = sqf_p.size();
|
||||||
|
mpz const * p = sqf_p.data();
|
||||||
|
SASSERT(sz > 0);
|
||||||
|
if (sz == 2) {
|
||||||
|
// Linear polynomial ax + b: root is -b/a (always rational).
|
||||||
|
scoped_mpq q(qm());
|
||||||
|
qm().set(q, p[0], p[1]);
|
||||||
|
qm().neg(q);
|
||||||
|
set(r, q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned pos_k = upm().knuth_positive_root_upper_bound(sz, p);
|
||||||
|
unsigned neg_k = upm().knuth_negative_root_upper_bound(sz, p);
|
||||||
|
|
||||||
|
scoped_mpbq lo(bqm()), hi(bqm()), mid(bqm());
|
||||||
|
unsigned vminus = upm().sign_variations_at_minus_inf(seq);
|
||||||
|
unsigned v0 = upm().sign_variations_at_zero(seq);
|
||||||
|
unsigned vplus = upm().sign_variations_at_plus_inf(seq);
|
||||||
|
unsigned le0_cnt = vminus - v0; // roots in (-oo, 0]
|
||||||
|
|
||||||
|
unsigned vlo, vhi;
|
||||||
|
unsigned target;
|
||||||
|
if (i <= le0_cnt) {
|
||||||
|
// Isolate within (-2^neg_k, 0] to keep the interval on the non-positive side.
|
||||||
|
bqm().power(mpbq(2), neg_k, lo);
|
||||||
|
bqm().neg(lo);
|
||||||
|
bqm().set(hi, 0);
|
||||||
|
vlo = vminus;
|
||||||
|
vhi = v0;
|
||||||
|
target = i;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Isolate within (0, 2^pos_k] to keep the interval on the non-negative side.
|
||||||
|
bqm().set(lo, 0);
|
||||||
|
bqm().power(mpbq(2), pos_k, hi);
|
||||||
|
vlo = v0;
|
||||||
|
vhi = vplus;
|
||||||
|
target = i - le0_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity: sqf_p has at least i roots.
|
||||||
|
SASSERT(vlo >= vhi);
|
||||||
|
SASSERT(i <= vminus - vplus);
|
||||||
|
SASSERT(target > 0);
|
||||||
|
SASSERT(target <= vlo - vhi);
|
||||||
|
while (vlo > vhi + 1) {
|
||||||
|
checkpoint();
|
||||||
|
bqm().add(lo, hi, mid);
|
||||||
|
bqm().div2(mid);
|
||||||
|
unsigned vmid = upm().sign_variations_at(seq, mid);
|
||||||
|
unsigned left_cnt = vlo - vmid; // roots in (lo, mid]
|
||||||
|
if (target <= left_cnt) {
|
||||||
|
bqm().set(hi, mid);
|
||||||
|
vhi = vmid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bqm().set(lo, mid);
|
||||||
|
vlo = vmid;
|
||||||
|
target -= left_cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SASSERT(vlo == vhi + 1);
|
||||||
|
|
||||||
|
// If the upper endpoint is exactly a dyadic root, return it as a basic number.
|
||||||
|
if (upm().eval_sign_at(sz, p, hi) == 0) {
|
||||||
|
scoped_mpq q(qm());
|
||||||
|
to_mpq(qm(), hi, q);
|
||||||
|
set(r, q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the isolating interval into a refinable one (or discover a dyadic root on the way).
|
||||||
|
scoped_mpbq a(bqm()), b(bqm());
|
||||||
|
bqm().set(a, lo);
|
||||||
|
bqm().set(b, hi);
|
||||||
|
if (!upm().isolating2refinable(sz, p, bqm(), a, b)) {
|
||||||
|
scoped_mpq q(qm());
|
||||||
|
to_mpq(qm(), a, q);
|
||||||
|
set(r, q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
del(r);
|
||||||
|
r = mk_algebraic_cell(sz, p, a, b, false /* minimal */);
|
||||||
|
SASSERT(acell_inv(*r.to_algebraic()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closest-root isolation for an (integer) univariate polynomial.
|
||||||
|
void isolate_roots_closest_univariate(polynomial_ref const & p, mpq const & s, numeral_vector & roots, svector<unsigned> & indices) {
|
||||||
|
SASSERT(is_univariate(p));
|
||||||
|
SASSERT(roots.empty());
|
||||||
|
indices.reset();
|
||||||
|
|
||||||
|
if (::is_zero(p) || ::is_const(p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Convert to dense univariate form and take the square-free part.
|
||||||
|
scoped_upoly & up = m_isolate_tmp1;
|
||||||
|
scoped_upoly & sqf_p = m_isolate_tmp3;
|
||||||
|
up.reset();
|
||||||
|
sqf_p.reset();
|
||||||
|
upm().to_numeral_vector(p, up);
|
||||||
|
if (up.empty())
|
||||||
|
return;
|
||||||
|
upm().square_free(up.size(), up.data(), sqf_p);
|
||||||
|
if (sqf_p.empty() || upm().degree(sqf_p) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
upolynomial::scoped_upolynomial_sequence seq(upm());
|
||||||
|
upm().sturm_seq(sqf_p.size(), sqf_p.data(), seq);
|
||||||
|
unsigned vminus = upm().sign_variations_at_minus_inf(seq);
|
||||||
|
unsigned vplus = upm().sign_variations_at_plus_inf(seq);
|
||||||
|
if (vminus <= vplus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned vs = sign_variations_at_mpq(seq, s);
|
||||||
|
unsigned total = vminus - vplus;
|
||||||
|
unsigned k = vminus - vs; // #roots in (-oo, s]
|
||||||
|
|
||||||
|
if (upm().eval_sign_at(sqf_p.size(), sqf_p.data(), s) == 0) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
set(roots.back(), s);
|
||||||
|
indices.push_back(k);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// predecessor (<= s)
|
||||||
|
if (k > 0) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
isolate_kth_root(sqf_p, seq, k, roots.back());
|
||||||
|
indices.push_back(k);
|
||||||
|
}
|
||||||
|
// successor (> s)
|
||||||
|
if (k < total) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
isolate_kth_root(sqf_p, seq, k + 1, roots.back());
|
||||||
|
indices.push_back(k + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mk_root(scoped_upoly const & up, unsigned i, numeral & r) {
|
void mk_root(scoped_upoly const & up, unsigned i, numeral & r) {
|
||||||
// TODO: implement version that finds i-th root without isolating all roots.
|
// TODO: implement version that finds i-th root without isolating all roots.
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
|
|
@ -2547,6 +2710,77 @@ namespace algebraic_numbers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void isolate_roots_closest(polynomial_ref const & p, polynomial::var2anum const & x2v, mpq const & s, numeral_vector & roots, svector<unsigned> & indices) {
|
||||||
|
TRACE(isolate_roots, tout << "isolating closest roots of: " << p << " around " << m_qmanager.to_string(s) << "\n";);
|
||||||
|
SASSERT(roots.empty());
|
||||||
|
indices.reset();
|
||||||
|
|
||||||
|
polynomial::manager & ext_pm = p.m();
|
||||||
|
if (ext_pm.is_zero(p) || ext_pm.is_const(p))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ext_pm.is_univariate(p)) {
|
||||||
|
isolate_roots_closest_univariate(p, s, roots, indices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eliminate rationals
|
||||||
|
polynomial_ref p_prime(ext_pm);
|
||||||
|
var2basic x2v_basic(*this, x2v);
|
||||||
|
p_prime = ext_pm.substitute(p, x2v_basic);
|
||||||
|
if (ext_pm.is_zero(p_prime) || ext_pm.is_const(p_prime))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ext_pm.is_univariate(p_prime)) {
|
||||||
|
polynomial::var x = ext_pm.max_var(p_prime);
|
||||||
|
if (x2v.contains(x)) {
|
||||||
|
// The remaining variable is assigned, the actual unassigned variable vanished when we replaced rational values.
|
||||||
|
// So, the polynomial does not have any roots.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isolate_roots_closest_univariate(p_prime, s, roots, indices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: isolate all roots then select closest ones.
|
||||||
|
scoped_numeral_vector all(m_wrapper);
|
||||||
|
isolate_roots(p, x2v, all);
|
||||||
|
if (all.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
scoped_numeral sv(m_wrapper);
|
||||||
|
set(sv, s);
|
||||||
|
|
||||||
|
unsigned lower = UINT_MAX, upper = UINT_MAX;
|
||||||
|
for (unsigned k = 0; k < all.size(); ++k) {
|
||||||
|
auto cmp = compare(all[k], sv);
|
||||||
|
if (cmp <= 0)
|
||||||
|
lower = k;
|
||||||
|
else {
|
||||||
|
upper = k;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lower != UINT_MAX && eq(all[lower], s)) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
set(roots.back(), all[lower]);
|
||||||
|
indices.push_back(lower + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lower != UINT_MAX) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
set(roots.back(), all[lower]);
|
||||||
|
indices.push_back(lower + 1);
|
||||||
|
}
|
||||||
|
if (upper != UINT_MAX) {
|
||||||
|
roots.push_back(numeral());
|
||||||
|
set(roots.back(), all[upper]);
|
||||||
|
indices.push_back(upper + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sign eval_at_mpbq(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, mpbq const & v) {
|
sign eval_at_mpbq(polynomial_ref const & p, polynomial::var2anum const & x2v, polynomial::var x, mpbq const & v) {
|
||||||
scoped_mpq qv(qm());
|
scoped_mpq qv(qm());
|
||||||
to_mpq(qm(), v, qv);
|
to_mpq(qm(), v, qv);
|
||||||
|
|
@ -3011,6 +3245,10 @@ namespace algebraic_numbers {
|
||||||
m_imp->isolate_roots(p, x2v, roots);
|
m_imp->isolate_roots(p, x2v, roots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void manager::isolate_roots_closest(polynomial_ref const & p, polynomial::var2anum const & x2v, mpq const & s, numeral_vector & roots, svector<unsigned> & indices) {
|
||||||
|
m_imp->isolate_roots_closest(p, x2v, s, roots, indices);
|
||||||
|
}
|
||||||
|
|
||||||
void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector<sign> & signs) {
|
void manager::isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots, svector<sign> & signs) {
|
||||||
m_imp->isolate_roots(p, x2v, roots, signs);
|
m_imp->isolate_roots(p, x2v, roots, signs);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,19 @@ namespace algebraic_numbers {
|
||||||
*/
|
*/
|
||||||
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots);
|
void isolate_roots(polynomial_ref const & p, polynomial::var2anum const & x2v, numeral_vector & roots);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Isolate the closest real roots of a multivariate polynomial p around the rational point s.
|
||||||
|
|
||||||
|
The method behaves like isolate_roots(p, x2v, roots) but only returns:
|
||||||
|
- the last root r such that r <= s (if it exists), and
|
||||||
|
- the first root r such that r > s (if it exists),
|
||||||
|
or a single root if s itself is a root.
|
||||||
|
|
||||||
|
The returned roots are sorted increasingly. The associated 1-based root indices
|
||||||
|
(with respect to the full increasing root list) are stored in \c indices.
|
||||||
|
*/
|
||||||
|
void isolate_roots_closest(polynomial_ref const & p, polynomial::var2anum const & x2v, mpq const & s, numeral_vector & roots, svector<unsigned> & indices);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Isolate the roots of the given polynomial, and compute its sign between them.
|
\brief Isolate the roots of the given polynomial, and compute its sign between them.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ namespace polynomial {
|
||||||
m_factor_cache.reset();
|
m_factor_cache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned pid(polynomial * p) const { return m.id(p); }
|
unsigned pid(const polynomial * p) const { return m.id(p); }
|
||||||
|
|
||||||
polynomial * mk_unique(polynomial * p) {
|
polynomial * mk_unique(polynomial * p) {
|
||||||
if (m_in_cache.get(pid(p), false))
|
if (m_in_cache.get(pid(p), false))
|
||||||
|
|
@ -141,6 +141,28 @@ namespace polynomial {
|
||||||
return p_prime;
|
return p_prime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool contains(const polynomial * p) const {
|
||||||
|
return m_in_cache.get(pid(p), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains_chain(polynomial * p, polynomial * q, var x) const {
|
||||||
|
if (!m_in_cache.get(pid(p), false)) {
|
||||||
|
polynomial * const * p2 = m_poly_table.find_core(p);
|
||||||
|
if (!p2)
|
||||||
|
return false;
|
||||||
|
p = *p2;
|
||||||
|
}
|
||||||
|
if (!m_in_cache.get(pid(q), false)) {
|
||||||
|
polynomial * const * q2 = m_poly_table.find_core(q);
|
||||||
|
if (!q2)
|
||||||
|
return false;
|
||||||
|
q = *q2;
|
||||||
|
}
|
||||||
|
unsigned h = hash_u_u(pid(p), pid(q));
|
||||||
|
psc_chain_entry key(p, q, x, h);
|
||||||
|
return m_psc_chain_cache.contains(&key);
|
||||||
|
}
|
||||||
|
|
||||||
void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) {
|
void psc_chain(polynomial * p, polynomial * q, var x, polynomial_ref_vector & S) {
|
||||||
p = mk_unique(p);
|
p = mk_unique(p);
|
||||||
q = mk_unique(q);
|
q = mk_unique(q);
|
||||||
|
|
@ -213,6 +235,14 @@ namespace polynomial {
|
||||||
return m_imp->mk_unique(p);
|
return m_imp->mk_unique(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cache::contains(const polynomial * p) const {
|
||||||
|
return m_imp->contains(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cache::contains_chain(polynomial const * p, polynomial const * q, var x) const {
|
||||||
|
return m_imp->contains_chain(const_cast<polynomial*>(p), const_cast<polynomial*>(q), x);
|
||||||
|
}
|
||||||
|
|
||||||
void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
|
void cache::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) {
|
||||||
m_imp->psc_chain(const_cast<polynomial*>(p), const_cast<polynomial*>(q), x, S);
|
m_imp->psc_chain(const_cast<polynomial*>(p), const_cast<polynomial*>(q), x, S);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,10 @@ namespace polynomial {
|
||||||
manager & m() const;
|
manager & m() const;
|
||||||
manager & pm() const { return m(); }
|
manager & pm() const { return m(); }
|
||||||
polynomial * mk_unique(polynomial * p);
|
polynomial * mk_unique(polynomial * p);
|
||||||
|
bool contains(const polynomial * p) const;
|
||||||
|
bool contains_chain(polynomial const * p, polynomial const * q, var x) const;
|
||||||
void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S);
|
void psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S);
|
||||||
void factor(polynomial const * p, polynomial_ref_vector & distinct_factors);
|
void factor(polynomial const * p, polynomial_ref_vector & distinct_factors);
|
||||||
void reset();
|
void reset();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
z3_add_component(nlsat
|
z3_add_component(nlsat
|
||||||
SOURCES
|
SOURCES
|
||||||
nlsat_clause.cpp
|
nlsat_clause.cpp
|
||||||
|
nlsat_common.cpp
|
||||||
nlsat_evaluator.cpp
|
nlsat_evaluator.cpp
|
||||||
nlsat_explain.cpp
|
nlsat_explain.cpp
|
||||||
nlsat_interval_set.cpp
|
nlsat_interval_set.cpp
|
||||||
|
levelwise.cpp
|
||||||
nlsat_simplify.cpp
|
nlsat_simplify.cpp
|
||||||
nlsat_solver.cpp
|
nlsat_solver.cpp
|
||||||
nlsat_types.cpp
|
nlsat_types.cpp
|
||||||
|
|
|
||||||
1428
src/nlsat/levelwise.cpp
Normal file
1428
src/nlsat/levelwise.cpp
Normal file
File diff suppressed because it is too large
Load diff
56
src/nlsat/levelwise.h
Normal file
56
src/nlsat/levelwise.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include "nlsat_types.h"
|
||||||
|
#include "math/polynomial/polynomial_cache.h"
|
||||||
|
|
||||||
|
namespace nlsat {
|
||||||
|
|
||||||
|
class assignment; // forward declared in nlsat_types.h
|
||||||
|
|
||||||
|
class levelwise {
|
||||||
|
public:
|
||||||
|
struct indexed_root_expr {
|
||||||
|
poly* p;
|
||||||
|
unsigned i;
|
||||||
|
};
|
||||||
|
struct root_function_interval {
|
||||||
|
bool section = false;
|
||||||
|
polynomial_ref l;
|
||||||
|
unsigned l_index; // the low bound root index
|
||||||
|
polynomial_ref u;
|
||||||
|
unsigned u_index; // the upper bound root index
|
||||||
|
bool l_inf() const { return l == nullptr; }
|
||||||
|
bool u_inf() const { return u == nullptr; }
|
||||||
|
bool is_section() const { return section; }
|
||||||
|
bool is_sector() const { return !section; }
|
||||||
|
polynomial_ref& section_poly() {
|
||||||
|
SASSERT(is_section());
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
root_function_interval(polynomial::manager & pm):l(pm), u(pm) {}
|
||||||
|
};
|
||||||
|
// Free pretty-printer declared here so external modules (e.g., nlsat_explain) can
|
||||||
|
// display intervals without depending on levelwise internals.
|
||||||
|
// Implemented in levelwise.cpp
|
||||||
|
friend std::ostream& display(std::ostream& out, solver& s, root_function_interval const& I);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
struct impl;
|
||||||
|
impl* m_impl;
|
||||||
|
public:
|
||||||
|
// Construct with polynomials ps, maximal variable max_x, current sample s, polynomial manager pm, and algebraic-number manager am
|
||||||
|
levelwise(nlsat::solver& solver, polynomial_ref_vector const& ps, var max_x, assignment const& s, pmanager& pm, anum_manager& am, polynomial::cache & cache);
|
||||||
|
~levelwise();
|
||||||
|
|
||||||
|
levelwise(levelwise const&) = delete;
|
||||||
|
levelwise& operator=(levelwise const&) = delete;
|
||||||
|
std_vector<root_function_interval> single_cell();
|
||||||
|
bool failed() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Free pretty-printer (non-member) for levelwise::symbolic_interval
|
||||||
|
std::ostream& display(std::ostream& out, solver& s, levelwise::root_function_interval const& I);
|
||||||
|
|
||||||
|
} // namespace nlsat
|
||||||
123
src/nlsat/nlsat_common.cpp
Normal file
123
src/nlsat/nlsat_common.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2012 Microsoft Corporation
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
nlsat_common.cpp
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
some common routines from nlsat
|
||||||
|
|
||||||
|
Author:
|
||||||
|
|
||||||
|
Lev Nachmanson(levnach@hotmail.com) 2025-October.
|
||||||
|
|
||||||
|
Revision History:
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#include "nlsat/nlsat_common.h"
|
||||||
|
namespace nlsat {
|
||||||
|
|
||||||
|
todo_set::todo_set(polynomial::cache& u, bool canonicalize): m_cache(u), m_set(u.pm()), m_canonicalize(canonicalize) {}
|
||||||
|
|
||||||
|
void todo_set::reset() {
|
||||||
|
pmanager& pm = m_set.m();
|
||||||
|
unsigned sz = m_set.size();
|
||||||
|
for (unsigned i = 0; i < sz; i++) {
|
||||||
|
m_in_set[pm.id(m_set.get(i))] = false;
|
||||||
|
}
|
||||||
|
m_set.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
poly* todo_set::insert(poly* p) {
|
||||||
|
pmanager& pm = m_set.m();
|
||||||
|
if (m_in_set.get(pm.id(p), false))
|
||||||
|
return p;
|
||||||
|
if (m_cache.contains(p)) {
|
||||||
|
// still have to insert in the set
|
||||||
|
m_in_set.setx(pm.id(p), true, false);
|
||||||
|
m_set.push_back(p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
polynomial_ref pinned(pm); // keep canonicalized polynomial alive until it is stored
|
||||||
|
if (m_canonicalize) {
|
||||||
|
// Canonicalize content+sign so scalar multiples share the same representative.
|
||||||
|
if (!pm.is_zero(p) && !pm.is_const(p)) {
|
||||||
|
var x = pm.max_var(p);
|
||||||
|
pm.primitive(p, x, pinned);
|
||||||
|
p = pinned.get();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
pinned = p;
|
||||||
|
p = pm.flip_sign_if_lm_neg(p);
|
||||||
|
pinned = p;
|
||||||
|
}
|
||||||
|
p = m_cache.mk_unique(p);
|
||||||
|
unsigned pid = pm.id(p);
|
||||||
|
if (!m_in_set.get(pid, false)) {
|
||||||
|
m_in_set.setx(pid, true, false);
|
||||||
|
m_set.push_back(p);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool todo_set::contains(poly* p) const {
|
||||||
|
if (!p)
|
||||||
|
return false;
|
||||||
|
pmanager& pm = m_set.m();
|
||||||
|
return m_in_set.get(pm.id(p), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool todo_set::empty() const { return m_set.empty(); }
|
||||||
|
|
||||||
|
// Return max variable in todo_set
|
||||||
|
var todo_set::max_var() const {
|
||||||
|
pmanager& pm = m_set.m();
|
||||||
|
var max = null_var;
|
||||||
|
unsigned sz = m_set.size();
|
||||||
|
for (unsigned i = 0; i < sz; i++) {
|
||||||
|
var x = pm.max_var(m_set.get(i));
|
||||||
|
SASSERT(x != null_var);
|
||||||
|
if (max == null_var || x > max)
|
||||||
|
max = x;
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Remove the maximal polynomials from the set and store
|
||||||
|
them in max_polys. Return the maximal variable
|
||||||
|
*/
|
||||||
|
var todo_set::extract_max_polys(polynomial_ref_vector& max_polys) {
|
||||||
|
max_polys.reset();
|
||||||
|
var x = max_var();
|
||||||
|
pmanager& pm = m_set.m();
|
||||||
|
unsigned sz = m_set.size();
|
||||||
|
unsigned j = 0;
|
||||||
|
for (unsigned i = 0; i < sz; i++) {
|
||||||
|
poly* p = m_set.get(i);
|
||||||
|
var y = pm.max_var(p);
|
||||||
|
SASSERT(y <= x);
|
||||||
|
if (y == x) {
|
||||||
|
max_polys.push_back(p);
|
||||||
|
m_in_set[pm.id(p)] = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_set.set(j, p);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_set.shrink(j);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Wrapper for factorization
|
||||||
|
*/
|
||||||
|
void factor(polynomial_ref & p, polynomial::cache& cache, polynomial_ref_vector & fs) {
|
||||||
|
TRACE(nlsat_factor, tout << "factor\n" << p << "\n";);
|
||||||
|
fs.reset();
|
||||||
|
cache.factor(p.get(), fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
156
src/nlsat/nlsat_common.h
Normal file
156
src/nlsat/nlsat_common.h
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*++
|
||||||
|
Copyright (c) 2025
|
||||||
|
|
||||||
|
Module Name:
|
||||||
|
|
||||||
|
nlsat_common.h
|
||||||
|
|
||||||
|
Abstract:
|
||||||
|
|
||||||
|
Pretty-print helpers for NLSAT components as free functions.
|
||||||
|
These forward to existing solver/pmanager display facilities.
|
||||||
|
|
||||||
|
--*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nlsat/nlsat_assignment.h"
|
||||||
|
#include "nlsat/nlsat_solver.h"
|
||||||
|
#include "nlsat/nlsat_scoped_literal_vector.h"
|
||||||
|
#include "math/polynomial/polynomial_cache.h"
|
||||||
|
|
||||||
|
namespace nlsat {
|
||||||
|
|
||||||
|
// Lightweight set of polynomials that keeps elements unique (by cache id) and
|
||||||
|
// supports extracting all polynomials whose maximal variable is maximal.
|
||||||
|
// Optional canonicalization (primitive + sign) can be enabled per instance.
|
||||||
|
struct todo_set {
|
||||||
|
polynomial::cache& m_cache;
|
||||||
|
polynomial_ref_vector m_set;
|
||||||
|
svector<char> m_in_set;
|
||||||
|
bool m_canonicalize;
|
||||||
|
|
||||||
|
todo_set(polynomial::cache& u, bool canonicalize);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
// Insert polynomial (canonicalizing if requested) and return the cached representative.
|
||||||
|
poly* insert(poly* p);
|
||||||
|
bool contains(poly* p) const;
|
||||||
|
bool contains(polynomial_ref const& p) const { return contains(p.get()); }
|
||||||
|
bool empty() const;
|
||||||
|
// Return max variable in todo_set
|
||||||
|
var max_var() const;
|
||||||
|
/**
|
||||||
|
\brief Remove the maximal polynomials from the set and store
|
||||||
|
them in max_polys. Return the maximal variable
|
||||||
|
*/
|
||||||
|
var extract_max_polys(polynomial_ref_vector& max_polys);
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, pmanager& pm, polynomial_ref const& p, display_var_proc const& proc) {
|
||||||
|
pm.display(out, p, proc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, pmanager& pm, polynomial_ref_vector const& ps, display_var_proc const& proc, char const* delim = "\n") {
|
||||||
|
for (unsigned i = 0; i < ps.size(); ++i) {
|
||||||
|
if (i > 0) out << delim;
|
||||||
|
pm.display(out, ps.get(i), proc);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, polynomial_ref const& p) {
|
||||||
|
return display(out, s.pm(), p, s.display_proc());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, poly* p) {
|
||||||
|
return display(out, s.pm(), polynomial_ref(p, s.pm()), s.display_proc());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, polynomial_ref_vector const& ps, char const* delim = "\n") {
|
||||||
|
return display(out, s.pm(), ps, s.display_proc(), delim);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, literal l) {
|
||||||
|
return s.display(out, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, unsigned n, literal const* ls) {
|
||||||
|
return s.display(out, n, ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, literal_vector const& ls) {
|
||||||
|
return s.display(out, ls);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display(std::ostream& out, solver& s, scoped_literal_vector const& ls) {
|
||||||
|
return s.display(out, ls.size(), ls.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::ostream& display_var(std::ostream& out, solver& s, var x) {
|
||||||
|
return s.display(out, x);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\brief evaluate the given polynomial in the current interpretation.
|
||||||
|
max_var(p) must be assigned in the current interpretation.
|
||||||
|
*/
|
||||||
|
inline ::sign sign(polynomial_ref const & p, assignment & x2v, anum_manager& am) {
|
||||||
|
SASSERT(max_var(p) == null_var || x2v.is_assigned(max_var(p)));
|
||||||
|
auto s = am.eval_sign_at(p, x2v);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether all coefficients of the polynomial `s` (viewed as a polynomial
|
||||||
|
* in its main variable) evaluate to zero under the given assignment `x2v`.
|
||||||
|
* This is exactly the logic used in several places in the nlsat codebase
|
||||||
|
* (e.g. coeffs_are_zeroes_in_factor in nlsat_explain.cpp).
|
||||||
|
*/
|
||||||
|
inline bool coeffs_are_zeroes_on_sample(polynomial_ref const & s, pmanager & pm, assignment & x2v, anum_manager & am) {
|
||||||
|
polynomial_ref c(pm);
|
||||||
|
var x = pm.max_var(s);
|
||||||
|
unsigned n = pm.degree(s, x);
|
||||||
|
for (unsigned j = 0; j <= n; ++j) {
|
||||||
|
c = pm.coeff(s, x, j);
|
||||||
|
if (sign(c, x2v, am) != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Display a vector of algebraic numbers in several commonly useful formats.
|
||||||
|
*
|
||||||
|
* This mirrors the ad-hoc helper that existed in `src/test/algebraic.cpp` so that
|
||||||
|
* solver / explanation code can conveniently dump root sets while debugging.
|
||||||
|
*
|
||||||
|
* For each algebraic number it prints (in order):
|
||||||
|
* - a decimal approximation (10 digits)
|
||||||
|
* - the root object representation (defining polynomial & isolating interval)
|
||||||
|
* - the isolating interval alone
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
inline void display(std::ostream & out, scoped_anum_vector const & rs) {
|
||||||
|
algebraic_numbers::manager & m = rs.m();
|
||||||
|
out << "numbers in decimal:\n";
|
||||||
|
for (const auto & r : rs) {
|
||||||
|
m.display_decimal(out, r, 10);
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
out << "numbers as root objects\n";
|
||||||
|
for (const auto & r : rs) {
|
||||||
|
m.display_root(out, r);
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
out << "numbers as intervals\n";
|
||||||
|
for (const auto & r : rs) {
|
||||||
|
m.display_interval(out, r);
|
||||||
|
out << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\brief Wrapper for factorization
|
||||||
|
*/
|
||||||
|
void factor(polynomial_ref & p, polynomial::cache& cache, polynomial_ref_vector & fs);
|
||||||
|
|
||||||
|
} // namespace nlsat
|
||||||
|
|
@ -17,9 +17,11 @@ Revision History:
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
#include "nlsat/nlsat_explain.h"
|
#include "nlsat/nlsat_explain.h"
|
||||||
|
#include "nlsat/levelwise.h"
|
||||||
#include "nlsat/nlsat_assignment.h"
|
#include "nlsat/nlsat_assignment.h"
|
||||||
#include "nlsat/nlsat_evaluator.h"
|
#include "nlsat/nlsat_evaluator.h"
|
||||||
#include "math/polynomial/algebraic_numbers.h"
|
#include "math/polynomial/algebraic_numbers.h"
|
||||||
|
#include "nlsat/nlsat_common.h"
|
||||||
#include "util/ref_buffer.h"
|
#include "util/ref_buffer.h"
|
||||||
#include "util/mpq.h"
|
#include "util/mpq.h"
|
||||||
|
|
||||||
|
|
@ -32,7 +34,6 @@ namespace nlsat {
|
||||||
|
|
||||||
struct explain::imp {
|
struct explain::imp {
|
||||||
solver & m_solver;
|
solver & m_solver;
|
||||||
assignment const & m_assignment;
|
|
||||||
atom_vector const & m_atoms;
|
atom_vector const & m_atoms;
|
||||||
atom_vector const & m_x2eq;
|
atom_vector const & m_x2eq;
|
||||||
anum_manager & m_am;
|
anum_manager & m_am;
|
||||||
|
|
@ -49,86 +50,26 @@ namespace nlsat {
|
||||||
bool m_factor;
|
bool m_factor;
|
||||||
bool m_add_all_coeffs;
|
bool m_add_all_coeffs;
|
||||||
bool m_add_zero_disc;
|
bool m_add_zero_disc;
|
||||||
bool m_cell_sample;
|
|
||||||
|
|
||||||
|
assignment const & sample() const { return m_solver.sample(); }
|
||||||
struct todo_set {
|
assignment & sample() { return m_solver.sample(); }
|
||||||
polynomial::cache & m_cache;
|
|
||||||
polynomial_ref_vector m_set;
|
|
||||||
svector<char> m_in_set;
|
|
||||||
|
|
||||||
todo_set(polynomial::cache & u):m_cache(u), m_set(u.pm()) {}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
pmanager & pm = m_set.m();
|
|
||||||
unsigned sz = m_set.size();
|
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
|
||||||
m_in_set[pm.id(m_set.get(i))] = false;
|
|
||||||
}
|
|
||||||
m_set.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(poly * p) {
|
|
||||||
pmanager & pm = m_set.m();
|
|
||||||
p = m_cache.mk_unique(p);
|
|
||||||
unsigned pid = pm.id(p);
|
|
||||||
if (m_in_set.get(pid, false))
|
|
||||||
return;
|
|
||||||
m_in_set.setx(pid, true, false);
|
|
||||||
m_set.push_back(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const { return m_set.empty(); }
|
|
||||||
|
|
||||||
// Return max variable in todo_set
|
|
||||||
var max_var() const {
|
|
||||||
pmanager & pm = m_set.m();
|
|
||||||
var max = null_var;
|
|
||||||
unsigned sz = m_set.size();
|
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
|
||||||
var x = pm.max_var(m_set.get(i));
|
|
||||||
SASSERT(x != null_var);
|
|
||||||
if (max == null_var || x > max)
|
|
||||||
max = x;
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\brief Remove the maximal polynomials from the set and store
|
|
||||||
them in max_polys. Return the maximal variable
|
|
||||||
*/
|
|
||||||
var extract_max_polys(polynomial_ref_vector & max_polys) {
|
|
||||||
max_polys.reset();
|
|
||||||
var x = max_var();
|
|
||||||
pmanager & pm = m_set.m();
|
|
||||||
unsigned sz = m_set.size();
|
|
||||||
unsigned j = 0;
|
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
|
||||||
poly * p = m_set.get(i);
|
|
||||||
var y = pm.max_var(p);
|
|
||||||
SASSERT(y <= x);
|
|
||||||
if (y == x) {
|
|
||||||
max_polys.push_back(p);
|
|
||||||
m_in_set[pm.id(p)] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_set.set(j, p);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_set.shrink(j);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// temporary field for store todo set of polynomials
|
// temporary field for store todo set of polynomials
|
||||||
todo_set m_todo;
|
todo_set m_todo;
|
||||||
|
|
||||||
|
// Track polynomials already processed in current projection to avoid cycles
|
||||||
|
todo_set m_processed;
|
||||||
|
|
||||||
// temporary fields for preprocessing core
|
// temporary fields for preprocessing core
|
||||||
scoped_literal_vector m_core1;
|
scoped_literal_vector m_core1;
|
||||||
scoped_literal_vector m_core2;
|
scoped_literal_vector m_core2;
|
||||||
|
|
||||||
|
// Lower-stage polynomials encountered during normalization that need to be projected
|
||||||
|
polynomial_ref_vector m_lower_stage_polys;
|
||||||
|
|
||||||
|
// Store last levelwise input for debugging unsound lemmas
|
||||||
|
polynomial_ref_vector m_last_lws_input_polys;
|
||||||
|
|
||||||
// temporary fields for storing the result
|
// temporary fields for storing the result
|
||||||
scoped_literal_vector * m_result = nullptr;
|
scoped_literal_vector * m_result = nullptr;
|
||||||
svector<char> m_already_added_literal;
|
svector<char> m_already_added_literal;
|
||||||
|
|
@ -136,9 +77,8 @@ namespace nlsat {
|
||||||
evaluator & m_evaluator;
|
evaluator & m_evaluator;
|
||||||
|
|
||||||
imp(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq,
|
imp(solver & s, assignment const & x2v, polynomial::cache & u, atom_vector const & atoms, atom_vector const & x2eq,
|
||||||
evaluator & ev, bool is_sample):
|
evaluator & ev, bool canonicalize):
|
||||||
m_solver(s),
|
m_solver(s),
|
||||||
m_assignment(x2v),
|
|
||||||
m_atoms(atoms),
|
m_atoms(atoms),
|
||||||
m_x2eq(x2eq),
|
m_x2eq(x2eq),
|
||||||
m_am(x2v.am()),
|
m_am(x2v.am()),
|
||||||
|
|
@ -150,10 +90,12 @@ namespace nlsat {
|
||||||
m_factors(m_pm),
|
m_factors(m_pm),
|
||||||
m_factors_save(m_pm),
|
m_factors_save(m_pm),
|
||||||
m_roots_tmp(m_am),
|
m_roots_tmp(m_am),
|
||||||
m_cell_sample(is_sample),
|
m_todo(u, canonicalize),
|
||||||
m_todo(u),
|
m_processed(u, canonicalize),
|
||||||
m_core1(s),
|
m_core1(s),
|
||||||
m_core2(s),
|
m_core2(s),
|
||||||
|
m_lower_stage_polys(m_pm),
|
||||||
|
m_last_lws_input_polys(m_pm),
|
||||||
m_evaluator(ev) {
|
m_evaluator(ev) {
|
||||||
m_simplify_cores = false;
|
m_simplify_cores = false;
|
||||||
m_full_dimensional = false;
|
m_full_dimensional = false;
|
||||||
|
|
@ -162,25 +104,8 @@ namespace nlsat {
|
||||||
m_add_zero_disc = true;
|
m_add_zero_disc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& display(std::ostream & out, polynomial_ref const & p) const {
|
// display helpers moved to free functions in nlsat_common.h
|
||||||
m_pm.display(out, p, m_solver.display_proc());
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& display(std::ostream & out, polynomial_ref_vector const & ps, char const * delim = "\n") const {
|
|
||||||
for (unsigned i = 0; i < ps.size(); ++i) {
|
|
||||||
if (i > 0)
|
|
||||||
out << delim;
|
|
||||||
m_pm.display(out, ps.get(i), m_solver.display_proc());
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& display(std::ostream & out, literal l) const { return m_solver.display(out, l); }
|
|
||||||
std::ostream& display_var(std::ostream & out, var x) const { return m_solver.display(out, x); }
|
|
||||||
std::ostream& display(std::ostream & out, unsigned sz, literal const * ls) const { return m_solver.display(out, sz, ls); }
|
|
||||||
std::ostream& display(std::ostream & out, literal_vector const & ls) const { return display(out, ls.size(), ls.data()); }
|
|
||||||
std::ostream& display(std::ostream & out, scoped_literal_vector const & ls) const { return display(out, ls.size(), ls.data()); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Add literal to the result vector.
|
\brief Add literal to the result vector.
|
||||||
|
|
@ -209,27 +134,6 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
\brief evaluate the given polynomial in the current interpretation.
|
|
||||||
max_var(p) must be assigned in the current interpretation.
|
|
||||||
*/
|
|
||||||
::sign sign(polynomial_ref const & p) {
|
|
||||||
SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p)));
|
|
||||||
auto s = m_am.eval_sign_at(p, m_assignment);
|
|
||||||
TRACE(nlsat_explain, tout << "p: " << p << " var: " << max_var(p) << " sign: " << s << "\n";);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\brief Wrapper for factorization
|
|
||||||
*/
|
|
||||||
void factor(polynomial_ref & p, polynomial_ref_vector & fs) {
|
|
||||||
// TODO: add params, caching
|
|
||||||
TRACE(nlsat_factor, tout << "factor\n" << p << "\n";);
|
|
||||||
fs.reset();
|
|
||||||
m_cache.factor(p.get(), fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Wrapper for psc chain computation
|
\brief Wrapper for psc chain computation
|
||||||
*/
|
*/
|
||||||
|
|
@ -306,14 +210,14 @@ namespace nlsat {
|
||||||
|
|
||||||
void find_cell_roots(polynomial_ref_vector & ps, var y, cell_root_info & info) {
|
void find_cell_roots(polynomial_ref_vector & ps, var y, cell_root_info & info) {
|
||||||
info.reset();
|
info.reset();
|
||||||
SASSERT(m_assignment.is_assigned(y));
|
SASSERT(m_solver.sample().is_assigned(y));
|
||||||
bool lower_inf = true;
|
bool lower_inf = true;
|
||||||
bool upper_inf = true;
|
bool upper_inf = true;
|
||||||
scoped_anum_vector & roots = m_roots_tmp;
|
scoped_anum_vector & roots = m_roots_tmp;
|
||||||
scoped_anum lower(m_am);
|
scoped_anum lower(m_am);
|
||||||
scoped_anum upper(m_am);
|
scoped_anum upper(m_am);
|
||||||
anum const & y_val = m_assignment.value(y);
|
anum const & y_val = m_solver.sample().value(y);
|
||||||
TRACE(nlsat_explain, tout << "adding literals for "; display_var(tout, y); tout << " -> ";
|
TRACE(nlsat_explain, tout << "adding literals for "; m_solver.display_var(tout, y); tout << " -> ";
|
||||||
m_am.display_decimal(tout, y_val); tout << "\n";);
|
m_am.display_decimal(tout, y_val); tout << "\n";);
|
||||||
polynomial_ref p(m_pm);
|
polynomial_ref p(m_pm);
|
||||||
unsigned sz = ps.size();
|
unsigned sz = ps.size();
|
||||||
|
|
@ -322,12 +226,12 @@ namespace nlsat {
|
||||||
if (max_var(p) != y)
|
if (max_var(p) != y)
|
||||||
continue;
|
continue;
|
||||||
roots.reset();
|
roots.reset();
|
||||||
// Variable y is assigned in m_assignment. We must temporarily unassign it.
|
// Variable y is assigned in m_solver.sample(). We must temporarily unassign it.
|
||||||
// Otherwise, the isolate_roots procedure will assume p is a constant polynomial.
|
// Otherwise, the isolate_roots procedure will assume p is a constant polynomial.
|
||||||
m_am.isolate_roots(p, undef_var_assignment(m_assignment, y), roots);
|
m_am.isolate_roots(p, undef_var_assignment(m_solver.sample(), y), roots);
|
||||||
unsigned num_roots = roots.size();
|
unsigned num_roots = roots.size();
|
||||||
TRACE(nlsat_explain,
|
TRACE(nlsat_explain,
|
||||||
tout << "isolated roots for "; display_var(tout, y);
|
tout << "isolated roots for "; m_solver.display_var(tout, y);
|
||||||
tout << " with polynomial: " << p << "\n";
|
tout << " with polynomial: " << p << "\n";
|
||||||
for (unsigned ri = 0; ri < num_roots; ++ri) {
|
for (unsigned ri = 0; ri < num_roots; ++ri) {
|
||||||
m_am.display_decimal(tout << " root[" << (ri+1) << "] = ", roots[ri]);
|
m_am.display_decimal(tout << " root[" << (ri+1) << "] = ", roots[ri]);
|
||||||
|
|
@ -386,6 +290,11 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::sign sign(polynomial_ref const & p) {
|
||||||
|
return ::nlsat::sign(p, m_solver.sample(), m_am);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void add_zero_assumption(polynomial_ref& p) {
|
void add_zero_assumption(polynomial_ref& p) {
|
||||||
// Build a square-free representative of p so that we can speak about
|
// Build a square-free representative of p so that we can speak about
|
||||||
// a specific root that coincides with the current assignment.
|
// a specific root that coincides with the current assignment.
|
||||||
|
|
@ -400,7 +309,7 @@ namespace nlsat {
|
||||||
SASSERT(maxx != null_var);
|
SASSERT(maxx != null_var);
|
||||||
if (maxx == null_var)
|
if (maxx == null_var)
|
||||||
return;
|
return;
|
||||||
SASSERT(m_assignment.is_assigned(maxx));
|
SASSERT(m_solver.sample().is_assigned(maxx));
|
||||||
|
|
||||||
polynomial_ref_vector singleton(m_pm);
|
polynomial_ref_vector singleton(m_pm);
|
||||||
singleton.push_back(q);
|
singleton.push_back(q);
|
||||||
|
|
@ -411,7 +320,7 @@ namespace nlsat {
|
||||||
|
|
||||||
TRACE(nlsat_explain,
|
TRACE(nlsat_explain,
|
||||||
tout << "adding zero-assumption root literal for ";
|
tout << "adding zero-assumption root literal for ";
|
||||||
display_var(tout, maxx); tout << " using root[" << info.m_eq_idx << "] of " << q << "\n";);
|
m_solver.display_var(tout, maxx); tout << " using root[" << info.m_eq_idx << "] of " << q << "\n";);
|
||||||
add_root_literal(atom::ROOT_EQ, maxx, info.m_eq_idx, info.m_eq);
|
add_root_literal(atom::ROOT_EQ, maxx, info.m_eq_idx, info.m_eq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -456,23 +365,23 @@ namespace nlsat {
|
||||||
TRACE(nlsat_explain, tout << "lc: " << lc << " reduct: " << reduct << "\n";);
|
TRACE(nlsat_explain, tout << "lc: " << lc << " reduct: " << reduct << "\n";);
|
||||||
insert_fresh_factors_in_todo(lc);
|
insert_fresh_factors_in_todo(lc);
|
||||||
if (!is_zero(lc) && sign(lc)) {
|
if (!is_zero(lc) && sign(lc)) {
|
||||||
insert_fresh_factors_in_todo(lc);
|
|
||||||
TRACE(nlsat_explain, tout << "lc does no vaninsh\n";);
|
TRACE(nlsat_explain, tout << "lc does no vaninsh\n";);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
add_zero_assumption(lc);
|
|
||||||
if (k == 0) {
|
if (k == 0) {
|
||||||
// all coefficients of p vanished in the current interpretation,
|
// all coefficients of p vanished in the current interpretation,
|
||||||
// and were added as assumptions.
|
// and were added as assumptions.
|
||||||
p = m_pm.mk_zero();
|
p = m_pm.mk_zero();
|
||||||
TRACE(nlsat_explain, tout << "all coefficients of p vanished\n";);
|
TRACE(nlsat_explain, tout << "all coefficients of p vanished\n";);
|
||||||
if (m_add_all_coeffs) {
|
if (m_add_all_coeffs) {
|
||||||
|
add_zero_assumption(lc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TRACE(nlsat_explain, tout << "falling back to add-all-coeffs projection\n";);
|
TRACE(nlsat_explain, tout << "falling back to add-all-coeffs projection\n";);
|
||||||
m_add_all_coeffs = true;
|
m_add_all_coeffs = true;
|
||||||
throw add_all_coeffs_restart();
|
throw add_all_coeffs_restart();
|
||||||
}
|
}
|
||||||
|
add_zero_assumption(lc);
|
||||||
k--;
|
k--;
|
||||||
p = reduct;
|
p = reduct;
|
||||||
}
|
}
|
||||||
|
|
@ -524,6 +433,7 @@ namespace nlsat {
|
||||||
Remark: root atoms are not normalized
|
Remark: root atoms are not normalized
|
||||||
*/
|
*/
|
||||||
literal normalize(literal l, var max) {
|
literal normalize(literal l, var max) {
|
||||||
|
TRACE(nlsat_explain, display(tout << "l:", m_solver, l) << '\n';);
|
||||||
bool_var b = l.var();
|
bool_var b = l.var();
|
||||||
if (b == true_bool_var)
|
if (b == true_bool_var)
|
||||||
return l;
|
return l;
|
||||||
|
|
@ -546,6 +456,8 @@ namespace nlsat {
|
||||||
SASSERT(max_var(p) != null_var);
|
SASSERT(max_var(p) != null_var);
|
||||||
SASSERT(max_var(p) < max);
|
SASSERT(max_var(p) < max);
|
||||||
// factor p is a lower stage polynomial, so we should add assumption to justify p being eliminated
|
// factor p is a lower stage polynomial, so we should add assumption to justify p being eliminated
|
||||||
|
// Also collect it for projection to ensure proper cell construction
|
||||||
|
m_lower_stage_polys.push_back(p);
|
||||||
if (s == 0)
|
if (s == 0)
|
||||||
add_assumption(atom::EQ, p); // add assumption p = 0
|
add_assumption(atom::EQ, p); // add assumption p = 0
|
||||||
else if (a->is_even(i))
|
else if (a->is_even(i))
|
||||||
|
|
@ -613,6 +525,7 @@ namespace nlsat {
|
||||||
stages) from (arithmetic) literals,
|
stages) from (arithmetic) literals,
|
||||||
*/
|
*/
|
||||||
void normalize(scoped_literal_vector & C, var max) {
|
void normalize(scoped_literal_vector & C, var max) {
|
||||||
|
TRACE(nlsat_explain, display(tout << "C:\n", m_solver, C) << '\n';);
|
||||||
unsigned sz = C.size();
|
unsigned sz = C.size();
|
||||||
unsigned j = 0;
|
unsigned j = 0;
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
|
|
@ -699,6 +612,10 @@ namespace nlsat {
|
||||||
\brief Add factors of p to todo
|
\brief Add factors of p to todo
|
||||||
*/
|
*/
|
||||||
void insert_fresh_factors_in_todo(polynomial_ref & p) {
|
void insert_fresh_factors_in_todo(polynomial_ref & p) {
|
||||||
|
// Skip if already processed in this projection (prevents cycles)
|
||||||
|
if (m_processed.contains(p))
|
||||||
|
return;
|
||||||
|
|
||||||
if (is_const(p))
|
if (is_const(p))
|
||||||
return;
|
return;
|
||||||
elim_vanishing(p);
|
elim_vanishing(p);
|
||||||
|
|
@ -706,14 +623,14 @@ namespace nlsat {
|
||||||
return;
|
return;
|
||||||
if (m_factor) {
|
if (m_factor) {
|
||||||
restore_factors _restore(m_factors, m_factors_save);
|
restore_factors _restore(m_factors, m_factors_save);
|
||||||
factor(p, m_factors);
|
factor(p, m_cache, m_factors);
|
||||||
TRACE(nlsat_explain, display(tout << "adding factors of\n", p); tout << "\n" << m_factors << "\n";);
|
TRACE(nlsat_explain, display(tout << "adding factors of\n", m_solver, p); tout << "\n" << m_factors << "\n";);
|
||||||
polynomial_ref f(m_pm);
|
polynomial_ref f(m_pm);
|
||||||
for (unsigned i = 0; i < m_factors.size(); ++i) {
|
for (unsigned i = 0; i < m_factors.size(); ++i) {
|
||||||
f = m_factors.get(i);
|
f = m_factors.get(i);
|
||||||
elim_vanishing(f);
|
elim_vanishing(f);
|
||||||
if (!is_const(f)) {
|
if (!is_const(f) && !m_processed.contains(f)) {
|
||||||
TRACE(nlsat_explain, tout << "adding factor:\n"; display(tout, f); tout << "\n";);
|
TRACE(nlsat_explain, tout << "adding factor:\n"; display(tout, m_solver, f); tout << "\n";);
|
||||||
m_todo.insert(f);
|
m_todo.insert(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -736,10 +653,10 @@ namespace nlsat {
|
||||||
unsigned k_deg = m_pm.degree(p, x);
|
unsigned k_deg = m_pm.degree(p, x);
|
||||||
if (k_deg == 0) continue;
|
if (k_deg == 0) continue;
|
||||||
// p depends on x
|
// p depends on x
|
||||||
TRACE(nlsat_explain, tout << "processing poly of degree " << k_deg << " w.r.t x" << x << ": "; display(tout, p) << "\n";);
|
TRACE(nlsat_explain, tout << "processing poly of degree " << k_deg << " w.r.t x" << x << ": "; m_solver.display(tout, p) << "\n";);
|
||||||
for (int j_coeff_deg = k_deg; j_coeff_deg >= 0; j_coeff_deg--) {
|
for (int j_coeff_deg = k_deg; j_coeff_deg >= 0; j_coeff_deg--) {
|
||||||
coeff = m_pm.coeff(p, x, j_coeff_deg);
|
coeff = m_pm.coeff(p, x, j_coeff_deg);
|
||||||
TRACE(nlsat_explain, tout << " coeff deg " << j_coeff_deg << ": "; display(tout, coeff) << "\n";);
|
TRACE(nlsat_explain, tout << " coeff deg " << j_coeff_deg << ": "; display(tout, m_solver, coeff) << "\n";);
|
||||||
insert_fresh_factors_in_todo(coeff);
|
insert_fresh_factors_in_todo(coeff);
|
||||||
if (!m_add_all_coeffs)
|
if (!m_add_all_coeffs)
|
||||||
break;
|
break;
|
||||||
|
|
@ -766,7 +683,7 @@ namespace nlsat {
|
||||||
// this function also explains the value 0, if met
|
// this function also explains the value 0, if met
|
||||||
bool coeffs_are_zeroes(polynomial_ref &s) {
|
bool coeffs_are_zeroes(polynomial_ref &s) {
|
||||||
restore_factors _restore(m_factors, m_factors_save);
|
restore_factors _restore(m_factors, m_factors_save);
|
||||||
factor(s, m_factors);
|
factor(s, m_cache, m_factors);
|
||||||
unsigned num_factors = m_factors.size();
|
unsigned num_factors = m_factors.size();
|
||||||
m_zero_fs.reset();
|
m_zero_fs.reset();
|
||||||
m_is_even.reset();
|
m_is_even.reset();
|
||||||
|
|
@ -774,7 +691,7 @@ namespace nlsat {
|
||||||
bool have_zero = false;
|
bool have_zero = false;
|
||||||
for (unsigned i = 0; i < num_factors; ++i) {
|
for (unsigned i = 0; i < num_factors; ++i) {
|
||||||
f = m_factors.get(i);
|
f = m_factors.get(i);
|
||||||
if (coeffs_are_zeroes_in_factor(f)) {
|
if (coeffs_are_zeroes_on_sample(f, m_pm, sample(), m_am)) {
|
||||||
have_zero = true;
|
have_zero = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -799,7 +716,7 @@ namespace nlsat {
|
||||||
auto c = polynomial_ref(this->m_pm);
|
auto c = polynomial_ref(this->m_pm);
|
||||||
for (unsigned j = 0; j <= n; ++j) {
|
for (unsigned j = 0; j <= n; ++j) {
|
||||||
c = m_pm.coeff(s, x, j);
|
c = m_pm.coeff(s, x, j);
|
||||||
if (sign(c) != 0)
|
if (nlsat::sign(c, sample(), m_am) != 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -814,7 +731,7 @@ namespace nlsat {
|
||||||
|
|
||||||
psc_chain(p, q, x, S);
|
psc_chain(p, q, x, S);
|
||||||
unsigned sz = S.size();
|
unsigned sz = S.size();
|
||||||
TRACE(nlsat_explain, tout << "computing psc of\n"; display(tout, p); tout << "\n"; display(tout, q); tout << "\n";
|
TRACE(nlsat_explain, tout << "computing psc of\n"; display(tout, m_solver, p); tout << "\n"; display(tout, m_solver, q); tout << "\n";
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
s = S.get(i);
|
s = S.get(i);
|
||||||
tout << "psc: " << s << "\n";
|
tout << "psc: " << s << "\n";
|
||||||
|
|
@ -822,10 +739,16 @@ namespace nlsat {
|
||||||
|
|
||||||
for (unsigned i = 0; i < sz; ++i) {
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
s = S.get(i);
|
s = S.get(i);
|
||||||
TRACE(nlsat_explain, display(tout << "processing psc(" << i << ")\n", s) << "\n";);
|
TRACE(nlsat_explain, display(tout << "processing psc(" << i << ")\n", m_solver, s) << "\n";);
|
||||||
if (is_zero(s)) {
|
if (is_zero(s)) {
|
||||||
TRACE(nlsat_explain, tout << "skipping psc is the zero polynomial\n";);
|
// PSC is identically zero - polynomials share a common factor.
|
||||||
continue;
|
// This can cause unsound lemmas. Fall back to add-all-coeffs projection.
|
||||||
|
TRACE(nlsat_explain, tout << "psc is zero polynomial - polynomials share common factor\n";);
|
||||||
|
if (m_add_all_coeffs)
|
||||||
|
continue;
|
||||||
|
TRACE(nlsat_explain, tout << "falling back to add-all-coeffs projection\n";);
|
||||||
|
m_add_all_coeffs = true;
|
||||||
|
throw add_all_coeffs_restart();
|
||||||
}
|
}
|
||||||
if (is_const(s)) {
|
if (is_const(s)) {
|
||||||
TRACE(nlsat_explain, tout << "done, psc is a constant\n";);
|
TRACE(nlsat_explain, tout << "done, psc is a constant\n";);
|
||||||
|
|
@ -836,11 +759,11 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
TRACE(nlsat_explain,
|
TRACE(nlsat_explain,
|
||||||
tout << "adding v-psc of\n";
|
tout << "adding v-psc of\n";
|
||||||
display(tout, p);
|
display(tout, m_solver, p);
|
||||||
tout << "\n";
|
tout << "\n";
|
||||||
display(tout, q);
|
display(tout, m_solver, q);
|
||||||
tout << "\n---->\n";
|
tout << "\n---->\n";
|
||||||
display(tout, s);
|
display(tout, m_solver, s);
|
||||||
tout << "\n";);
|
tout << "\n";);
|
||||||
// s did not vanish completely, but its leading coefficient may have vanished
|
// s did not vanish completely, but its leading coefficient may have vanished
|
||||||
insert_fresh_factors_in_todo(s);
|
insert_fresh_factors_in_todo(s);
|
||||||
|
|
@ -900,14 +823,14 @@ namespace nlsat {
|
||||||
|
|
||||||
void add_root_literal(atom::kind k, var y, unsigned i, poly * p) {
|
void add_root_literal(atom::kind k, var y, unsigned i, poly * p) {
|
||||||
polynomial_ref pr(p, m_pm);
|
polynomial_ref pr(p, m_pm);
|
||||||
TRACE(nlsat_explain,
|
TRACE(nlsat_explain,
|
||||||
display(tout << "x" << y << " " << k << "[" << i << "](", pr); tout << ")\n";);
|
display(tout << "x" << y << " " << k << "[" << i << "](", m_solver, pr); tout << ")\n";);
|
||||||
|
|
||||||
if (!mk_linear_root(k, y, i, p) &&
|
if (!mk_linear_root(k, y, i, p) &&
|
||||||
!mk_quadratic_root(k, y, i, p)) {
|
!mk_quadratic_root(k, y, i, p)) {
|
||||||
bool_var b = m_solver.mk_root_atom(k, y, i, p);
|
bool_var b = m_solver.mk_root_atom(k, y, i, p);
|
||||||
literal l(b, true);
|
literal l(b, true);
|
||||||
TRACE(nlsat_explain, tout << "adding literal\n"; display(tout, l); tout << "\n";);
|
TRACE(nlsat_explain, tout << "adding literal\n"; display(tout, m_solver, l); tout << "\n";);
|
||||||
add_literal(l);
|
add_literal(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -968,7 +891,7 @@ namespace nlsat {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SASSERT(m_assignment.is_assigned(y));
|
SASSERT(sample().is_assigned(y));
|
||||||
polynomial_ref A(m_pm), B(m_pm), C(m_pm), q(m_pm), p_diff(m_pm), yy(m_pm);
|
polynomial_ref A(m_pm), B(m_pm), C(m_pm), q(m_pm), p_diff(m_pm), yy(m_pm);
|
||||||
A = m_pm.coeff(p, y, 2);
|
A = m_pm.coeff(p, y, 2);
|
||||||
B = m_pm.coeff(p, y, 1);
|
B = m_pm.coeff(p, y, 1);
|
||||||
|
|
@ -1000,6 +923,8 @@ namespace nlsat {
|
||||||
if (!is_const(p)) {
|
if (!is_const(p)) {
|
||||||
TRACE(nlsat_explain, tout << p << "\n";);
|
TRACE(nlsat_explain, tout << p << "\n";);
|
||||||
add_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p);
|
add_assumption(s == 0 ? atom::EQ : (s < 0 ? atom::LT : atom::GT), p);
|
||||||
|
// Also add p to the projection to ensure proper cell construction
|
||||||
|
insert_fresh_factors_in_todo(p);
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
@ -1013,7 +938,7 @@ namespace nlsat {
|
||||||
|
|
||||||
*/
|
*/
|
||||||
void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) {
|
void mk_linear_root(atom::kind k, var y, unsigned i, poly * p, bool mk_neg) {
|
||||||
TRACE(nlsat_explain, display_var(tout, y); m_pm.display(tout << ": ", p, m_solver.display_proc()); tout << "\n");
|
TRACE(nlsat_explain, m_solver.display_var(tout, y); m_pm.display(tout << ": ", p, m_solver.display_proc()); tout << "\n");
|
||||||
polynomial_ref p_prime(m_pm);
|
polynomial_ref p_prime(m_pm);
|
||||||
p_prime = p;
|
p_prime = p;
|
||||||
bool lsign = false;
|
bool lsign = false;
|
||||||
|
|
@ -1104,33 +1029,37 @@ namespace nlsat {
|
||||||
\brief Apply model-based projection operation defined in our paper.
|
\brief Apply model-based projection operation defined in our paper.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void project_original(polynomial_ref_vector & ps, var max_x) {
|
bool levelwise_single_cell(polynomial_ref_vector & ps, var max_x, polynomial::cache & cache) {
|
||||||
if (ps.empty())
|
// Store polynomials for debugging unsound lemmas
|
||||||
return;
|
m_last_lws_input_polys.reset();
|
||||||
m_todo.reset();
|
for (unsigned i = 0; i < ps.size(); i++)
|
||||||
for (poly* p : ps) {
|
m_last_lws_input_polys.push_back(ps.get(i));
|
||||||
m_todo.insert(p);
|
|
||||||
}
|
levelwise lws(m_solver, ps, max_x, sample(), m_pm, m_am, cache);
|
||||||
var x = m_todo.extract_max_polys(ps);
|
auto cell = lws.single_cell();
|
||||||
// Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore
|
if (lws.failed())
|
||||||
if (x < max_x)
|
return false;
|
||||||
add_cell_lits(ps, x);
|
|
||||||
while (true) {
|
TRACE(lws, for (unsigned i = 0; i < cell.size(); i++)
|
||||||
if (all_univ(ps, x) && m_todo.empty()) {
|
display(tout << "I[" << i << "]:", m_solver, cell[i]) << "\n";);
|
||||||
m_todo.reset();
|
// Enumerate all intervals in the computed cell and add literals for each non-trivial interval.
|
||||||
break;
|
// Non-trivial = section, or sector with at least one finite bound (ignore (-oo,+oo)).
|
||||||
|
for (auto const & I : cell) {
|
||||||
|
if (I.is_section()) {
|
||||||
|
SASSERT(I.l && I.l_index);
|
||||||
|
add_root_literal(atom::ROOT_EQ, max_var(I.l.get()), I.l_index, I.l.get());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
TRACE(nlsat_explain, tout << "project loop, processing var "; display_var(tout, x);
|
if (I.l_inf() && I.u_inf())
|
||||||
tout << "\npolynomials\n";
|
continue; // skip whole-line sector
|
||||||
display(tout, ps); tout << "\n";);
|
if (!I.l_inf())
|
||||||
add_lcs(ps, x);
|
add_root_literal(m_full_dimensional ? atom::ROOT_GE :
|
||||||
psc_discriminant(ps, x);
|
atom::ROOT_GT, max_var(I.l.get()), I.l_index, I.l.get());
|
||||||
psc_resultant(ps, x);
|
if (!I.u_inf())
|
||||||
if (m_todo.empty())
|
add_root_literal(m_full_dimensional ? atom::ROOT_LE :
|
||||||
break;
|
atom::ROOT_LT, max_var(I.u.get()), I.u_index, I.u.get());
|
||||||
x = m_todo.extract_max_polys(ps);
|
|
||||||
add_cell_lits(ps, x);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1140,12 +1069,17 @@ namespace nlsat {
|
||||||
* "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection"
|
* "Solving Satisfiability of Polynomial Formulas By Sample - Cell Projection"
|
||||||
* https://arxiv.org/abs/2003.00409
|
* https://arxiv.org/abs/2003.00409
|
||||||
*/
|
*/
|
||||||
|
void collect_to_processed(polynomial_ref_vector & ps) {
|
||||||
|
for (unsigned i = 0; i < ps.size(); ++i)
|
||||||
|
m_processed.insert(ps.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
void project_cdcac(polynomial_ref_vector & ps, var max_x) {
|
void project_cdcac(polynomial_ref_vector & ps, var max_x) {
|
||||||
bool first = true;
|
bool first = true;
|
||||||
if (ps.empty())
|
if (ps.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_todo.reset();
|
m_todo.reset();
|
||||||
|
m_processed.reset();
|
||||||
for (unsigned i = 0; i < ps.size(); ++i) {
|
for (unsigned i = 0; i < ps.size(); ++i) {
|
||||||
polynomial_ref p(m_pm);
|
polynomial_ref p(m_pm);
|
||||||
p = ps.get(i);
|
p = ps.get(i);
|
||||||
|
|
@ -1156,9 +1090,21 @@ namespace nlsat {
|
||||||
for (auto p: m_todo.m_set)
|
for (auto p: m_todo.m_set)
|
||||||
ps.push_back(p);
|
ps.push_back(p);
|
||||||
|
|
||||||
var x = m_todo.extract_max_polys(ps);
|
bool use_all_coeffs = false;
|
||||||
// Remark: after vanishing coefficients are eliminated, ps may not contain max_x anymore
|
|
||||||
|
|
||||||
|
if (m_solver.apply_levelwise()) {
|
||||||
|
bool levelwise_ok = levelwise_single_cell(ps, max_x, m_cache);
|
||||||
|
m_solver.record_levelwise_result(levelwise_ok);
|
||||||
|
if (levelwise_ok)
|
||||||
|
return;
|
||||||
|
// Levelwise failed, use add_all_coeffs mode for fallback projection
|
||||||
|
use_all_coeffs = true;
|
||||||
|
}
|
||||||
|
// Set m_add_all_coeffs for the rest of the projection, restore on function exit
|
||||||
|
flet<bool> _set_all_coeffs(m_add_all_coeffs, use_all_coeffs || m_add_all_coeffs);
|
||||||
|
|
||||||
|
var x = m_todo.extract_max_polys(ps);
|
||||||
|
collect_to_processed(ps);
|
||||||
polynomial_ref_vector samples(m_pm);
|
polynomial_ref_vector samples(m_pm);
|
||||||
if (x < max_x)
|
if (x < max_x)
|
||||||
cac_add_cell_lits(ps, x, samples);
|
cac_add_cell_lits(ps, x, samples);
|
||||||
|
|
@ -1168,8 +1114,8 @@ namespace nlsat {
|
||||||
m_todo.reset();
|
m_todo.reset();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TRACE(nlsat_explain, tout << "project loop, processing var "; display_var(tout, x); tout << "\npolynomials\n";
|
TRACE(nlsat_explain, tout << "project loop, processing var "; m_solver.display_var(tout, x); tout << "\npolynomials\n";
|
||||||
display(tout, ps); tout << "\n";);
|
display(tout, m_solver, ps); tout << "\n";);
|
||||||
if (first) { // The first run is special because x is not constrained by the sample, we cannot surround it by the root functions.
|
if (first) { // The first run is special because x is not constrained by the sample, we cannot surround it by the root functions.
|
||||||
// we make the polynomials in ps delinable
|
// we make the polynomials in ps delinable
|
||||||
add_lcs(ps, x);
|
add_lcs(ps, x);
|
||||||
|
|
@ -1186,17 +1132,14 @@ namespace nlsat {
|
||||||
if (m_todo.empty())
|
if (m_todo.empty())
|
||||||
break;
|
break;
|
||||||
x = m_todo.extract_max_polys(ps);
|
x = m_todo.extract_max_polys(ps);
|
||||||
|
collect_to_processed(ps);
|
||||||
cac_add_cell_lits(ps, x, samples);
|
cac_add_cell_lits(ps, x, samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void project(polynomial_ref_vector & ps, var max_x) {
|
void project(polynomial_ref_vector & ps, var max_x) {
|
||||||
if (m_cell_sample) {
|
project_cdcac(ps, max_x);
|
||||||
project_cdcac(ps, max_x);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
project_original(ps, max_x);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_already_added() const {
|
bool check_already_added() const {
|
||||||
|
|
@ -1291,7 +1234,7 @@ namespace nlsat {
|
||||||
new_lit = l;
|
new_lit = l;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TRACE(nlsat_simplify_core, display(tout << "trying to simplify literal\n", l) << "\nusing equation\n";
|
TRACE(nlsat_simplify_core, display(tout << "trying to simplify literal\n", m_solver, l) << "\nusing equation\n";
|
||||||
m_pm.display(tout, info.m_eq, m_solver.display_proc()); tout << "\n";);
|
m_pm.display(tout, info.m_eq, m_solver.display_proc()); tout << "\n";);
|
||||||
int atom_sign = 1;
|
int atom_sign = 1;
|
||||||
bool modified_lit = false;
|
bool modified_lit = false;
|
||||||
|
|
@ -1374,14 +1317,14 @@ namespace nlsat {
|
||||||
new_lit = m_solver.mk_ineq_literal(new_k, new_factors.size(), new_factors.data(), new_factors_even.data());
|
new_lit = m_solver.mk_ineq_literal(new_k, new_factors.size(), new_factors.data(), new_factors_even.data());
|
||||||
if (l.sign())
|
if (l.sign())
|
||||||
new_lit.neg();
|
new_lit.neg();
|
||||||
TRACE(nlsat_simplify_core, tout << "simplified literal:\n"; display(tout, new_lit) << " " << m_solver.value(new_lit) << "\n";);
|
TRACE(nlsat_simplify_core, tout << "simplified literal:\n"; display(tout, m_solver, new_lit) << " " << m_solver.value(new_lit) << "\n";);
|
||||||
|
|
||||||
if (max_var(new_lit) < max) {
|
if (max_var(new_lit) < max) {
|
||||||
if (m_solver.value(new_lit) == l_true) {
|
if (m_solver.value(new_lit) == l_true) {
|
||||||
TRACE(nlsat_simplify_bug,
|
TRACE(nlsat_simplify_bug,
|
||||||
tout << "literal normalized away because it is already true after rewriting:\n";
|
tout << "literal normalized away because it is already true after rewriting:\n";
|
||||||
display(tout << " original: ", l) << "\n";
|
m_solver.display(tout << " original: ", l) << "\n";
|
||||||
display(tout << " rewritten: ", new_lit) << "\n";
|
m_solver.display(tout << " rewritten: ", new_lit) << "\n";
|
||||||
if (info.m_eq) {
|
if (info.m_eq) {
|
||||||
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
||||||
m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc());
|
m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc());
|
||||||
|
|
@ -1393,8 +1336,8 @@ namespace nlsat {
|
||||||
literal assumption = new_lit;
|
literal assumption = new_lit;
|
||||||
TRACE(nlsat_simplify_bug,
|
TRACE(nlsat_simplify_bug,
|
||||||
tout << "literal replaced by lower-stage assumption due to rewriting:\n";
|
tout << "literal replaced by lower-stage assumption due to rewriting:\n";
|
||||||
display(tout << " original: ", l) << "\n";
|
m_solver.display(tout << " original: ", l) << "\n";
|
||||||
display(tout << " assumption: ", assumption) << "\n";
|
m_solver.display(tout << " assumption: ", assumption) << "\n";
|
||||||
if (info.m_eq) {
|
if (info.m_eq) {
|
||||||
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
||||||
m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc());
|
m_pm.display(tout << " equation used: ", eq_ref, m_solver.display_proc());
|
||||||
|
|
@ -1408,12 +1351,12 @@ namespace nlsat {
|
||||||
literal before = new_lit;
|
literal before = new_lit;
|
||||||
(void)before;
|
(void)before;
|
||||||
new_lit = normalize(new_lit, max);
|
new_lit = normalize(new_lit, max);
|
||||||
TRACE(nlsat_simplify_core, tout << "simplified literal after normalization:\n"; display(tout, new_lit); tout << " " << m_solver.value(new_lit) << "\n";);
|
TRACE(nlsat_simplify_core, tout << "simplified literal after normalization:\n"; m_solver.display(tout, new_lit); tout << " " << m_solver.value(new_lit) << "\n";);
|
||||||
if (new_lit == true_literal || new_lit == false_literal) {
|
if (new_lit == true_literal || new_lit == false_literal) {
|
||||||
TRACE(nlsat_simplify_bug,
|
TRACE(nlsat_simplify_bug,
|
||||||
tout << "normalize() turned rewritten literal into constant value:\n";
|
tout << "normalize() turned rewritten literal into constant value:\n";
|
||||||
display(tout << " original: ", l) << "\n";
|
m_solver.display(tout << " original: ", l) << "\n";
|
||||||
display(tout << " rewritten: ", before) << "\n";
|
m_solver.display(tout << " rewritten: ", before) << "\n";
|
||||||
tout << " result: " << (new_lit == true_literal ? "true" : "false") << "\n";
|
tout << " result: " << (new_lit == true_literal ? "true" : "false") << "\n";
|
||||||
if (info.m_eq) {
|
if (info.m_eq) {
|
||||||
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
polynomial_ref eq_ref(const_cast<poly*>(info.m_eq), m_pm);
|
||||||
|
|
@ -1577,7 +1520,7 @@ namespace nlsat {
|
||||||
poly * eq_p = eq->p(0);
|
poly * eq_p = eq->p(0);
|
||||||
VERIFY(simplify(C, eq_p, max));
|
VERIFY(simplify(C, eq_p, max));
|
||||||
// add equation as an assumption
|
// add equation as an assumption
|
||||||
TRACE(nlsat_simpilfy_core, display(tout << "adding equality as assumption ", literal(eq->bvar(), true)); tout << "\n";);
|
TRACE(nlsat_simpilfy_core, display(tout << "adding equality as assumption ", m_solver, literal(eq->bvar(), true)); tout << "\n";);
|
||||||
add_literal(literal(eq->bvar(), true));
|
add_literal(literal(eq->bvar(), true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1586,33 +1529,52 @@ namespace nlsat {
|
||||||
\brief Main procedure. The explain the given unsat core, and store the result in m_result
|
\brief Main procedure. The explain the given unsat core, and store the result in m_result
|
||||||
*/
|
*/
|
||||||
void main(unsigned num, literal const * ls) {
|
void main(unsigned num, literal const * ls) {
|
||||||
if (num == 0)
|
if (num == 0) {
|
||||||
|
TRACE(nlsat_explain, tout << "num:" << num << "\n";);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
collect_polys(num, ls, m_ps);
|
collect_polys(num, ls, m_ps);
|
||||||
|
|
||||||
|
// Add lower-stage polynomials collected during normalization
|
||||||
|
// These polynomials had assumptions added but need to be projected as well
|
||||||
|
for (unsigned i = 0; i < m_lower_stage_polys.size(); i++) {
|
||||||
|
m_ps.push_back(m_lower_stage_polys.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
var max_x = max_var(m_ps);
|
var max_x = max_var(m_ps);
|
||||||
TRACE(nlsat_explain, tout << "polynomials in the conflict:\n"; display(tout, m_ps); tout << "\n";);
|
TRACE(nlsat_explain, tout << "polynomials in the conflict:\n"; display(tout, m_solver, m_ps); tout << "\n";);
|
||||||
|
|
||||||
|
// Note: levelwise is now called in process2() before normalize()
|
||||||
|
// to ensure it receives the original polynomials
|
||||||
|
|
||||||
elim_vanishing(m_ps);
|
elim_vanishing(m_ps);
|
||||||
TRACE(nlsat_explain, tout << "after elim_vanishing m_ps:\n"; display(tout, m_ps); tout << "\n";);
|
TRACE(nlsat_explain, tout << "after elim_vanishing m_ps:\n"; display(tout, m_solver, m_ps); tout << "\n";);
|
||||||
project(m_ps, max_x);
|
project(m_ps, max_x);
|
||||||
TRACE(nlsat_explain, tout << "after projection\n"; display(tout, m_ps); tout << "\n";);
|
TRACE(nlsat_explain, tout << "after projection\n"; display(tout, m_solver, m_ps); tout << "\n";);
|
||||||
}
|
}
|
||||||
|
|
||||||
void process2(unsigned num, literal const * ls) {
|
void process2(unsigned num, literal const * ls) {
|
||||||
|
// Reset lower-stage polynomials collection
|
||||||
|
m_lower_stage_polys.reset();
|
||||||
|
|
||||||
|
// Try levelwise with ORIGINAL polynomials BEFORE any normalization
|
||||||
|
|
||||||
if (m_simplify_cores) {
|
if (m_simplify_cores) {
|
||||||
|
TRACE(nlsat_explain, tout << "m_simplify_cores is true\n";);
|
||||||
m_core2.reset();
|
m_core2.reset();
|
||||||
m_core2.append(num, ls);
|
m_core2.append(num, ls);
|
||||||
var max = max_var(num, ls);
|
var max = max_var(num, ls);
|
||||||
SASSERT(max != null_var);
|
SASSERT(max != null_var);
|
||||||
TRACE(nlsat_explain, display(tout << "core before normalization\n", m_core2) << "\n";);
|
TRACE(nlsat_explain, display(tout << "core before normalization\n", m_solver, m_core2) << "\n";);
|
||||||
normalize(m_core2, max);
|
normalize(m_core2, max);
|
||||||
TRACE(nlsat_explain, display(tout << "core after normalization\n", m_core2) << "\n";);
|
TRACE(nlsat_explain, display(tout << "core after normalization\n", m_solver, m_core2) << "\n";);
|
||||||
simplify(m_core2, max);
|
simplify(m_core2, max);
|
||||||
TRACE(nlsat_explain, display(tout << "core after simplify\n", m_core2) << "\n";);
|
TRACE(nlsat_explain, display(tout << "core after simplify\n", m_solver, m_core2) << "\n";);
|
||||||
main(m_core2.size(), m_core2.data());
|
main(m_core2.size(), m_core2.data());
|
||||||
m_core2.reset();
|
m_core2.reset();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
TRACE(nlsat_explain, display(tout << "core befor normalization\n", m_core2) << "\n";);
|
TRACE(nlsat_explain, display(tout << "core befor normalization\n", m_solver, m_core2) << "\n";);
|
||||||
main(num, ls);
|
main(num, ls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1674,39 +1636,42 @@ namespace nlsat {
|
||||||
todo.reset(); core.reset();
|
todo.reset(); core.reset();
|
||||||
todo.append(num, ls);
|
todo.append(num, ls);
|
||||||
while (true) {
|
while (true) {
|
||||||
TRACE(nlsat_minimize, tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core) << "\n";);
|
TRACE(nlsat_minimize, tout << "core minimization:\n"; display(tout, m_solver, todo); tout << "\nCORE:\n"; display(tout, m_solver, core) << "\n";);
|
||||||
if (!minimize_core(todo, core))
|
if (!minimize_core(todo, core))
|
||||||
break;
|
break;
|
||||||
std::reverse(todo.begin(), todo.end());
|
std::reverse(todo.begin(), todo.end());
|
||||||
TRACE(nlsat_minimize, tout << "core minimization:\n"; display(tout, todo); tout << "\nCORE:\n"; display(tout, core) << "\n";);
|
TRACE(nlsat_minimize, tout << "core minimization:\n"; display(tout, m_solver, todo); tout << "\nCORE:\n"; display(tout, m_solver, core) << "\n";);
|
||||||
if (!minimize_core(todo, core))
|
if (!minimize_core(todo, core))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TRACE(nlsat_minimize, tout << "core:\n"; display(tout, core););
|
TRACE(nlsat_minimize, tout << "core:\n"; display(tout, m_solver, core););
|
||||||
r.append(core.size(), core.data());
|
r.append(core.size(), core.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void process(unsigned num, literal const * ls) {
|
void process(unsigned num, literal const * ls) {
|
||||||
if (m_minimize_cores && num > 1) {
|
if (m_minimize_cores && num > 1) {
|
||||||
|
TRACE(nlsat_explain, tout << "m_minimize_cores:" << m_minimize_cores << ", num:" << num;);
|
||||||
m_core1.reset();
|
m_core1.reset();
|
||||||
minimize(num, ls, m_core1);
|
minimize(num, ls, m_core1);
|
||||||
process2(m_core1.size(), m_core1.data());
|
process2(m_core1.size(), m_core1.data());
|
||||||
m_core1.reset();
|
m_core1.reset();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
TRACE(nlsat_explain, tout << "directly to process2\n";);
|
||||||
process2(num, ls);
|
process2(num, ls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(unsigned num, literal const * ls, scoped_literal_vector & result) {
|
void compute_conflict_explanation(unsigned num, literal const * ls, scoped_literal_vector & result) {
|
||||||
SASSERT(check_already_added());
|
SASSERT(check_already_added());
|
||||||
SASSERT(num > 0);
|
SASSERT(num > 0);
|
||||||
TRACE(nlsat_explain,
|
TRACE(nlsat_explain,
|
||||||
tout << "[explain] set of literals is infeasible in the current interpretation\n";
|
tout << "the infeasible clause:\n";
|
||||||
display(tout, num, ls) << "\n";
|
display(tout, m_solver, num, ls) << "\n";
|
||||||
m_solver.display_assignment(tout);
|
|
||||||
|
m_solver.display_assignment(tout << "assignment:\n");
|
||||||
);
|
);
|
||||||
if (max_var(num, ls) == 0 && !m_assignment.is_assigned(0)) {
|
if (max_var(num, ls) == 0 && !m_solver.sample().is_assigned(0)) {
|
||||||
TRACE(nlsat_explain, tout << "all literals use unassigned max var; returning justification\n";);
|
TRACE(nlsat_explain, tout << "all literals use unassigned max var; returning justification\n";);
|
||||||
result.reset();
|
result.reset();
|
||||||
return;
|
return;
|
||||||
|
|
@ -1718,7 +1683,7 @@ namespace nlsat {
|
||||||
process(num, ls);
|
process(num, ls);
|
||||||
reset_already_added();
|
reset_already_added();
|
||||||
m_result = nullptr;
|
m_result = nullptr;
|
||||||
TRACE(nlsat_explain, display(tout << "[explain] result\n", result) << "\n";);
|
TRACE(nlsat_explain, display(tout << "[explain] result\n", m_solver, result) << "\n";);
|
||||||
CASSERT("nlsat", check_already_added());
|
CASSERT("nlsat", check_already_added());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1848,12 +1813,12 @@ namespace nlsat {
|
||||||
collect_polys(lits.size(), lits.data(), m_ps);
|
collect_polys(lits.size(), lits.data(), m_ps);
|
||||||
unbounded = true;
|
unbounded = true;
|
||||||
scoped_anum x_val(m_am);
|
scoped_anum x_val(m_am);
|
||||||
x_val = m_assignment.value(x);
|
x_val = sample().value(x);
|
||||||
for (unsigned i = 0; i < m_ps.size(); ++i) {
|
for (unsigned i = 0; i < m_ps.size(); ++i) {
|
||||||
p = m_ps.get(i);
|
p = m_ps.get(i);
|
||||||
scoped_anum_vector & roots = m_roots_tmp;
|
scoped_anum_vector & roots = m_roots_tmp;
|
||||||
roots.reset();
|
roots.reset();
|
||||||
m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots);
|
m_am.isolate_roots(p, undef_var_assignment(sample(), x), roots);
|
||||||
for (unsigned j = 0; j < roots.size(); ++j) {
|
for (unsigned j = 0; j < roots.size(); ++j) {
|
||||||
int s = m_am.compare(x_val, roots[j]);
|
int s = m_am.compare(x_val, roots[j]);
|
||||||
if (s <= 0 && (unbounded || m_am.compare(roots[j], val) <= 0)) {
|
if (s <= 0 && (unbounded || m_am.compare(roots[j], val) <= 0)) {
|
||||||
|
|
@ -1867,8 +1832,8 @@ namespace nlsat {
|
||||||
};
|
};
|
||||||
|
|
||||||
explain::explain(solver & s, assignment const & x2v, polynomial::cache & u,
|
explain::explain(solver & s, assignment const & x2v, polynomial::cache & u,
|
||||||
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool use_cell_sample) {
|
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool canonicalize) {
|
||||||
m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev, use_cell_sample);
|
m_imp = alloc(imp, s, x2v, u, atoms, x2eq, ev, canonicalize);
|
||||||
}
|
}
|
||||||
|
|
||||||
explain::~explain() {
|
explain::~explain() {
|
||||||
|
|
@ -1904,8 +1869,8 @@ namespace nlsat {
|
||||||
m_imp->m_add_zero_disc = f;
|
m_imp->m_add_zero_disc = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void explain::main_operator(unsigned n, literal const * ls, scoped_literal_vector & result) {
|
void explain::compute_conflict_explanation(unsigned n, literal const * ls, scoped_literal_vector & result) {
|
||||||
(*m_imp)(n, ls, result);
|
m_imp->compute_conflict_explanation(n, ls, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void explain::project(var x, unsigned n, literal const * ls, scoped_literal_vector & result) {
|
void explain::project(var x, unsigned n, literal const * ls, scoped_literal_vector & result) {
|
||||||
|
|
@ -1916,6 +1881,16 @@ namespace nlsat {
|
||||||
m_imp->maximize(x, n, ls, val, unbounded);
|
m_imp->maximize(x, n, ls, val, unbounded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void explain::display_last_lws_input(std::ostream& out) {
|
||||||
|
out << "=== POLYNOMIALS PASSED TO LEVELWISE ===\n";
|
||||||
|
for (unsigned i = 0; i < m_imp->m_last_lws_input_polys.size(); i++) {
|
||||||
|
out << " p[" << i << "]: ";
|
||||||
|
m_imp->m_pm.display(out, m_imp->m_last_lws_input_polys.get(i));
|
||||||
|
out << "\n";
|
||||||
|
}
|
||||||
|
out << "=== END LEVELWISE INPUT (" << m_imp->m_last_lws_input_polys.size() << " polynomials) ===\n";
|
||||||
|
}
|
||||||
|
|
||||||
void explain::test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result) {
|
void explain::test_root_literal(atom::kind k, var y, unsigned i, poly* p, scoped_literal_vector & result) {
|
||||||
m_imp->test_root_literal(k, y, i, p, result);
|
m_imp->test_root_literal(k, y, i, p, result);
|
||||||
}
|
}
|
||||||
|
|
@ -1924,29 +1899,29 @@ namespace nlsat {
|
||||||
#ifdef Z3DEBUG
|
#ifdef Z3DEBUG
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
void pp(nlsat::explain::imp & ex, unsigned num, nlsat::literal const * ls) {
|
void pp(nlsat::explain::imp & ex, unsigned num, nlsat::literal const * ls) {
|
||||||
ex.display(std::cout, num, ls);
|
display(std::cout, ex.m_solver, num, ls);
|
||||||
}
|
}
|
||||||
void pp(nlsat::explain::imp & ex, nlsat::scoped_literal_vector & ls) {
|
void pp(nlsat::explain::imp & ex, nlsat::scoped_literal_vector & ls) {
|
||||||
ex.display(std::cout, ls);
|
display(std::cout, ex.m_solver, ls);
|
||||||
}
|
}
|
||||||
void pp(nlsat::explain::imp & ex, polynomial_ref const & p) {
|
void pp(nlsat::explain::imp & ex, polynomial_ref const & p) {
|
||||||
ex.display(std::cout, p);
|
display(std::cout, ex.m_solver, p);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
void pp(nlsat::explain::imp & ex, polynomial::polynomial * p) {
|
void pp(nlsat::explain::imp & ex, polynomial::polynomial * p) {
|
||||||
polynomial_ref _p(p, ex.m_pm);
|
polynomial_ref _p(p, ex.m_pm);
|
||||||
ex.display(std::cout, _p);
|
display(std::cout, ex.m_solver, _p);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
void pp(nlsat::explain::imp & ex, polynomial_ref_vector const & ps) {
|
void pp(nlsat::explain::imp & ex, polynomial_ref_vector const & ps) {
|
||||||
ex.display(std::cout, ps);
|
display(std::cout, ex.m_solver, ps);
|
||||||
}
|
}
|
||||||
void pp_var(nlsat::explain::imp & ex, nlsat::var x) {
|
void pp_var(nlsat::explain::imp & ex, nlsat::var x) {
|
||||||
ex.display(std::cout, x);
|
display_var(std::cout, ex.m_solver, x);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) {
|
void pp_lit(nlsat::explain::imp & ex, nlsat::literal l) {
|
||||||
ex.display(std::cout, l);
|
display(std::cout, ex.m_solver, l);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ namespace nlsat {
|
||||||
imp * m_imp;
|
imp * m_imp;
|
||||||
public:
|
public:
|
||||||
explain(solver & s, assignment const & x2v, polynomial::cache & u,
|
explain(solver & s, assignment const & x2v, polynomial::cache & u,
|
||||||
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool use_cell_sample_proj);
|
atom_vector const& atoms, atom_vector const& x2eq, evaluator & ev, bool canonicalize);
|
||||||
|
|
||||||
~explain();
|
~explain();
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ namespace nlsat {
|
||||||
- s_1, ..., s_m do not contain variable x.
|
- s_1, ..., s_m do not contain variable x.
|
||||||
- s_1, ..., s_m are false in the current interpretation
|
- s_1, ..., s_m are false in the current interpretation
|
||||||
*/
|
*/
|
||||||
void main_operator(unsigned n, literal const * ls, scoped_literal_vector & result);
|
void compute_conflict_explanation(unsigned n, literal const * ls, scoped_literal_vector & result);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -103,6 +103,11 @@ namespace nlsat {
|
||||||
*/
|
*/
|
||||||
void maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded);
|
void maximize(var x, unsigned n, literal const * ls, scoped_anum& val, bool& unbounded);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Print the polynomials that were passed to levelwise in the last call (for debugging).
|
||||||
|
*/
|
||||||
|
void display_last_lws_input(std::ostream& out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Unit test routine.
|
Unit test routine.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ namespace nlsat {
|
||||||
interval_set_manager(anum_manager & m, small_object_allocator & a);
|
interval_set_manager(anum_manager & m, small_object_allocator & a);
|
||||||
|
|
||||||
void set_seed(unsigned s) { m_rand.set_seed(s); }
|
void set_seed(unsigned s) { m_rand.set_seed(s); }
|
||||||
|
unsigned get_seed() const { return m_rand.get_seed(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Return the empty set.
|
\brief Return the empty set.
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ def_module_params('nlsat',
|
||||||
params=(max_memory_param(),
|
params=(max_memory_param(),
|
||||||
('simple_check', BOOL, False, "precheck polynomials using variables sign"),
|
('simple_check', BOOL, False, "precheck polynomials using variables sign"),
|
||||||
('variable_ordering_strategy', UINT, 0, "Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"),
|
('variable_ordering_strategy', UINT, 0, "Variable Ordering Strategy, 0 for none, 1 for BROWN, 2 for TRIANGULAR, 3 for ONLYPOLY"),
|
||||||
('cell_sample', BOOL, True, "cell sample projection"),
|
|
||||||
('lazy', UINT, 0, "how lazy the solver is."),
|
('lazy', UINT, 0, "how lazy the solver is."),
|
||||||
('reorder', BOOL, True, "reorder variables."),
|
('reorder', BOOL, True, "reorder variables."),
|
||||||
('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"),
|
('log_lemmas', BOOL, False, "display lemmas as self-contained SMT formulas"),
|
||||||
|
|
@ -22,6 +21,8 @@ def_module_params('nlsat',
|
||||||
('factor', BOOL, True, "factor polynomials produced during conflict resolution."),
|
('factor', BOOL, True, "factor polynomials produced during conflict resolution."),
|
||||||
('add_all_coeffs', BOOL, False, "add all polynomial coefficients during projection."),
|
('add_all_coeffs', BOOL, False, "add all polynomial coefficients during projection."),
|
||||||
('zero_disc', BOOL, False, "add_zero_assumption to the vanishing discriminant."),
|
('zero_disc', BOOL, False, "add_zero_assumption to the vanishing discriminant."),
|
||||||
('known_sat_assignment_file_name', STRING, "", "the file name of a known solution: used for debugging only")
|
('known_sat_assignment_file_name', STRING, "", "the file name of a known solution: used for debugging only"),
|
||||||
|
('lws', BOOL, True, "apply levelwise."),
|
||||||
|
('lws_spt_threshold', UINT, 2, "minimum both-side polynomial count to apply spanning tree optimization; < 2 disables spanning tree"),
|
||||||
|
('canonicalize', BOOL, True, "canonicalize polynomials.")
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// int tttt = 0;
|
||||||
/*++
|
/*++
|
||||||
Copyright (c) 2012 Microsoft Corporation
|
Copyright (c) 2012 Microsoft Corporation
|
||||||
|
|
||||||
|
|
@ -41,6 +42,7 @@ Revision History:
|
||||||
#include "nlsat/nlsat_simplify.h"
|
#include "nlsat/nlsat_simplify.h"
|
||||||
#include "nlsat/nlsat_simple_checker.h"
|
#include "nlsat/nlsat_simple_checker.h"
|
||||||
#include "nlsat/nlsat_variable_ordering_strategy.h"
|
#include "nlsat/nlsat_variable_ordering_strategy.h"
|
||||||
|
#include "nlsat_solver.h"
|
||||||
|
|
||||||
#define NLSAT_EXTRA_VERBOSE
|
#define NLSAT_EXTRA_VERBOSE
|
||||||
|
|
||||||
|
|
@ -52,7 +54,6 @@ Revision History:
|
||||||
|
|
||||||
namespace nlsat {
|
namespace nlsat {
|
||||||
|
|
||||||
|
|
||||||
typedef chashtable<ineq_atom*, ineq_atom::hash_proc, ineq_atom::eq_proc> ineq_atom_table;
|
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;
|
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_max_conflicts;
|
||||||
unsigned m_lemma_rlimit;
|
unsigned m_lemma_rlimit;
|
||||||
unsigned m_lemma_count;
|
unsigned m_lemma_count;
|
||||||
unsigned m_variable_ordering_strategy;
|
unsigned m_variable_ordering_strategy;
|
||||||
bool m_set_0_more;
|
bool m_set_0_more;
|
||||||
bool m_cell_sample;
|
|
||||||
|
|
||||||
struct stats {
|
struct stats {
|
||||||
unsigned m_simplifications;
|
unsigned m_simplifications;
|
||||||
|
|
@ -239,13 +239,18 @@ namespace nlsat {
|
||||||
unsigned m_decisions;
|
unsigned m_decisions;
|
||||||
unsigned m_stages;
|
unsigned m_stages;
|
||||||
unsigned m_irrational_assignments; // number of irrational witnesses
|
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)); }
|
void reset() { memset(this, 0, sizeof(*this)); }
|
||||||
stats() { reset(); }
|
stats() { reset(); }
|
||||||
};
|
};
|
||||||
// statistics
|
// statistics
|
||||||
stats m_stats;
|
stats m_stats;
|
||||||
std::string m_debug_known_solution_file_name;
|
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):
|
imp(solver& s, ctx& c):
|
||||||
m_ctx(c),
|
m_ctx(c),
|
||||||
m_solver(s),
|
m_solver(s),
|
||||||
|
|
@ -264,7 +269,7 @@ namespace nlsat {
|
||||||
m_simplify(s, m_atoms, m_clauses, m_learned, m_pm),
|
m_simplify(s, m_atoms, m_clauses, m_learned, m_pm),
|
||||||
m_display_var(m_perm),
|
m_display_var(m_perm),
|
||||||
m_display_assumption(nullptr),
|
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_scope_lvl(0),
|
||||||
m_lemma(s),
|
m_lemma(s),
|
||||||
m_lazy_clause(s),
|
m_lazy_clause(s),
|
||||||
|
|
@ -305,8 +310,9 @@ namespace nlsat {
|
||||||
m_check_lemmas = p.check_lemmas();
|
m_check_lemmas = p.check_lemmas();
|
||||||
m_variable_ordering_strategy = p.variable_ordering_strategy();
|
m_variable_ordering_strategy = p.variable_ordering_strategy();
|
||||||
m_debug_known_solution_file_name = p.known_sat_assignment_file_name();
|
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_check_lemmas |= !(m_debug_known_solution_file_name.empty());
|
||||||
m_cell_sample = p.cell_sample();
|
|
||||||
|
|
||||||
m_ism.set_seed(m_random_seed);
|
m_ism.set_seed(m_random_seed);
|
||||||
m_explain.set_simplify_cores(m_simplify_cores);
|
m_explain.set_simplify_cores(m_simplify_cores);
|
||||||
|
|
@ -1016,7 +1022,8 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Setup checker solver and translate atoms/clauses
|
// 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) {
|
auto pconvert = [&](poly* p) {
|
||||||
return convert(m_pm, p, checker.m_pm);
|
return convert(m_pm, p, checker.m_pm);
|
||||||
};
|
};
|
||||||
|
|
@ -1052,7 +1059,9 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// root atom cannot be translated due to variable ordering
|
// 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 {
|
else {
|
||||||
|
|
@ -1079,20 +1088,32 @@ namespace nlsat {
|
||||||
literal nlit(tr[lit.var()], !lit.sign());
|
literal nlit(tr[lit.var()], !lit.sign());
|
||||||
checker.mk_external_clause(1, &nlit, nullptr);
|
checker.mk_external_clause(1, &nlit, nullptr);
|
||||||
}
|
}
|
||||||
|
return true; // Successfully set up the checker
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper: Display unsound lemma failure information
|
// Helper: Display unsound lemma failure information
|
||||||
void display_unsound_lemma(imp& checker, scoped_bool_vars& tr, unsigned n, literal const* cls) {
|
void display_unsound_lemma(imp& checker, scoped_bool_vars& tr, unsigned n, literal const* cls) {
|
||||||
verbose_stream() << "\n";
|
verbose_stream() << "\n";
|
||||||
verbose_stream() << "========== UNSOUND LEMMA DETECTED ==========\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";
|
verbose_stream() << "The following lemma is NOT implied by the original clauses:\n";
|
||||||
display(verbose_stream() << " Lemma: ", n, cls) << "\n\n";
|
display(verbose_stream() << " Lemma: ", n, cls) << "\n\n";
|
||||||
verbose_stream() << "Reason: Found a satisfying assignment where:\n";
|
verbose_stream() << "Reason: Found a satisfying assignment where:\n";
|
||||||
verbose_stream() << " - The original clauses are satisfied\n";
|
verbose_stream() << " - The original clauses are satisfied\n";
|
||||||
verbose_stream() << " - But ALL literals in the lemma are FALSE\n\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
|
// 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);
|
auto lemma_vars = collect_vars_on_clause(n, cls);
|
||||||
for (var x : lemma_vars) {
|
for (var x : lemma_vars) {
|
||||||
if (checker.m_assignment.is_assigned(x)) {
|
if (checker.m_assignment.is_assigned(x)) {
|
||||||
|
|
@ -1120,11 +1141,15 @@ namespace nlsat {
|
||||||
TRACE(nlsat, display(tout << "check lemma: ", n, cls) << "\n";
|
TRACE(nlsat, display(tout << "check lemma: ", n, cls) << "\n";
|
||||||
display(tout););
|
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 {
|
try {
|
||||||
// Create a separate reslimit for the checker with 10 second timeout
|
// Create a separate reslimit for the checker with 10 second timeout
|
||||||
reslimit checker_rlimit;
|
reslimit checker_rlimit;
|
||||||
cancel_eh<reslimit> eh(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);
|
ctx c(checker_rlimit, m_ctx.m_params, m_ctx.m_incremental);
|
||||||
solver solver2(c);
|
solver solver2(c);
|
||||||
|
|
@ -1133,14 +1158,28 @@ namespace nlsat {
|
||||||
checker.m_log_lemmas = false;
|
checker.m_log_lemmas = false;
|
||||||
checker.m_dump_mathematica = false;
|
checker.m_dump_mathematica = false;
|
||||||
checker.m_inline_vars = false;
|
checker.m_inline_vars = false;
|
||||||
|
checker.m_apply_lws = false; // Disable levelwise for checker to avoid recursive issues
|
||||||
|
|
||||||
scoped_bool_vars tr(checker);
|
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();
|
lbool r = checker.check();
|
||||||
if (r == l_undef) // Checker timed out - skip this lemma check
|
if (r == l_undef) {
|
||||||
return;
|
// 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) {
|
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);
|
display_unsound_lemma(checker, tr, n, cls);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
@ -1148,6 +1187,10 @@ namespace nlsat {
|
||||||
catch (...) {
|
catch (...) {
|
||||||
// Ignore exceptions from the checker - just skip this lemma check
|
// 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) {
|
void log_lemma(std::ostream& out, clause const& cls, std::string annotation) {
|
||||||
|
|
@ -1165,7 +1208,7 @@ namespace nlsat {
|
||||||
used_bools[b] = true;
|
used_bools[b] = true;
|
||||||
vars.reset();
|
vars.reset();
|
||||||
this->vars(lit, vars);
|
this->vars(lit, vars);
|
||||||
for (var v : vars)
|
for (var v : vars)
|
||||||
used_vars[v] = true;
|
used_vars[v] = true;
|
||||||
}
|
}
|
||||||
display(out << "(echo \"#" << m_lemma_count++ << ":" << annotation << ":", n, cls) << "\")\n";
|
display(out << "(echo \"#" << m_lemma_count++ << ":" << annotation << ":", n, cls) << "\")\n";
|
||||||
|
|
@ -1182,7 +1225,6 @@ namespace nlsat {
|
||||||
for (unsigned i = 0; i < n; ++i)
|
for (unsigned i = 0; i < n; ++i)
|
||||||
display_smt2(out << "(assert ", ~cls[i]) << ")\n";
|
display_smt2(out << "(assert ", ~cls[i]) << ")\n";
|
||||||
out << "(check-sat)\n(reset)\n";
|
out << "(check-sat)\n(reset)\n";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clause * mk_clause_core(unsigned num_lits, literal const * lits, bool learned, _assumption_set a) {
|
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);
|
collect(assumptions, m_learned);
|
||||||
del_clauses(m_valids);
|
del_clauses(m_valids);
|
||||||
|
|
||||||
if (m_check_lemmas)
|
// Note: Don't check learned clauses here - they are the result of resolution
|
||||||
for (clause* c : m_learned)
|
// and may not be tautologies. Conflict lemmas are checked in resolve_lazy_justification.
|
||||||
check_lemma(c->size(), c->data(), nullptr);
|
|
||||||
|
|
||||||
assumptions.reset();
|
assumptions.reset();
|
||||||
assumptions.append(result);
|
assumptions.append(result);
|
||||||
|
|
@ -2338,8 +2379,8 @@ namespace nlsat {
|
||||||
|
|
||||||
m_lazy_clause.reset();
|
m_lazy_clause.reset();
|
||||||
|
|
||||||
m_explain.main_operator(jst.num_lits(), jst.lits(), m_lazy_clause);
|
m_explain.compute_conflict_explanation(jst.num_lits(), jst.lits(), m_lazy_clause);
|
||||||
for (unsigned i = 0; i < sz; ++i)
|
for (unsigned i = 0; i < sz; i++)
|
||||||
m_lazy_clause.push_back(~jst.lit(i));
|
m_lazy_clause.push_back(~jst.lit(i));
|
||||||
|
|
||||||
// lazy clause is a valid clause
|
// lazy clause is a valid clause
|
||||||
|
|
@ -2359,6 +2400,8 @@ namespace nlsat {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_check_lemmas) {
|
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);
|
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));
|
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()));
|
resolve_clause(b, *(jst.get_clause()));
|
||||||
break;
|
break;
|
||||||
case justification::LAZY:
|
case justification::LAZY:
|
||||||
resolve_lazy_justification(b, *(jst.get_lazy()));
|
|
||||||
|
resolve_lazy_justification(b, *(jst.get_lazy()));
|
||||||
break;
|
break;
|
||||||
case justification::DECISION:
|
case justification::DECISION:
|
||||||
SASSERT(m_num_marks == 0);
|
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";
|
TRACE(nlsat, tout << "new lemma:\n"; display(tout, m_lemma.size(), m_lemma.data()); tout << "\n";
|
||||||
tout << "found_decision: " << found_decision << "\n";);
|
tout << "found_decision: " << found_decision << "\n";);
|
||||||
|
|
||||||
if (m_check_lemmas) {
|
// Note: Don't check m_lemma here - it's the result of resolution
|
||||||
check_lemma(m_lemma.size(), m_lemma.data(), m_lemma_assumptions.get());
|
// and may not be a tautology. Conflict lemmas are checked in resolve_lazy_justification.
|
||||||
}
|
|
||||||
|
|
||||||
// if (m_log_lemmas)
|
// if (m_log_lemmas)
|
||||||
// log_lemma(std::cout, m_lemma.size(), m_lemma.data(), false);
|
// 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 stages", m_stats.m_stages);
|
||||||
st.update("nlsat simplifications", m_stats.m_simplifications);
|
st.update("nlsat simplifications", m_stats.m_simplifications);
|
||||||
st.update("nlsat irrational assignments", m_stats.m_irrational_assignments);
|
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() {
|
void reset_statistics() {
|
||||||
|
|
@ -4373,7 +4418,16 @@ namespace nlsat {
|
||||||
nlsat_params::collect_param_descrs(d);
|
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;
|
return m_imp->m_qm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4626,6 +4680,15 @@ namespace nlsat {
|
||||||
m_imp->m_stats.m_simplifications++;
|
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 {
|
bool solver::has_root_atom(clause const& c) const {
|
||||||
return m_imp->has_root_atom(c);
|
return m_imp->has_root_atom(c);
|
||||||
}
|
}
|
||||||
|
|
@ -4638,5 +4701,8 @@ namespace nlsat {
|
||||||
return (m_imp->m_asm.mk_join(static_cast<imp::_assumption_set>(a), static_cast<imp::_assumption_set>(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; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,7 @@ namespace nlsat {
|
||||||
assumption join(assumption a, assumption b);
|
assumption join(assumption a, assumption b);
|
||||||
|
|
||||||
void inc_simplify();
|
void inc_simplify();
|
||||||
|
void record_levelwise_result(bool success);
|
||||||
void add_bound(bound_constraint const& c);
|
void add_bound(bound_constraint const& c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -244,7 +245,10 @@ namespace nlsat {
|
||||||
// -----------------------
|
// -----------------------
|
||||||
void updt_params(params_ref const & p);
|
void updt_params(params_ref const & p);
|
||||||
static void collect_param_descrs(param_descrs & d);
|
static void collect_param_descrs(param_descrs & d);
|
||||||
|
const assignment& sample() const;
|
||||||
|
assignment& sample();
|
||||||
|
bool apply_levelwise() const;
|
||||||
|
unsigned lws_spt_threshold() const;
|
||||||
void reset();
|
void reset();
|
||||||
void collect_statistics(statistics & st);
|
void collect_statistics(statistics & st);
|
||||||
void reset_statistics();
|
void reset_statistics();
|
||||||
|
|
@ -298,4 +302,3 @@ namespace nlsat {
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Revision History:
|
||||||
#include "util/buffer.h"
|
#include "util/buffer.h"
|
||||||
#include "sat/sat_types.h"
|
#include "sat/sat_types.h"
|
||||||
#include "util/z3_exception.h"
|
#include "util/z3_exception.h"
|
||||||
|
#include <vector>
|
||||||
namespace algebraic_numbers {
|
namespace algebraic_numbers {
|
||||||
class anum;
|
class anum;
|
||||||
class manager;
|
class manager;
|
||||||
|
|
@ -143,6 +143,50 @@ namespace nlsat {
|
||||||
struct eq_proc { bool operator()(root_atom const * a1, root_atom const * a2) const; };
|
struct eq_proc { bool operator()(root_atom const * a1, root_atom const * a2) const; };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief An indexed root expression designates the i-th real root of a (square-free) polynomial p when regarded as
|
||||||
|
a univariate over its maximal variable x after substituting the current values for variables < x.
|
||||||
|
|
||||||
|
It is a lightweight value object used by projection / sample-cell algorithms. It does not own the polynomial.
|
||||||
|
*/
|
||||||
|
struct indexed_root {
|
||||||
|
poly * m_p; // polynomial whose root is referenced (assumed non-null)
|
||||||
|
unsigned m_index; // root index (0-based internally; corresponds to paper's i)
|
||||||
|
var m_var; // the main variable of m_p when treated as univariate
|
||||||
|
indexed_root(): m_p(nullptr), m_index(0), m_var(null_var) {}
|
||||||
|
indexed_root(poly* p, unsigned i, var x): m_p(p), m_index(i), m_var(x) {}
|
||||||
|
poly * p() const { return m_p; }
|
||||||
|
unsigned index() const { return m_index; }
|
||||||
|
var x() const { return m_var; }
|
||||||
|
bool is_null() const { return m_p == nullptr; }
|
||||||
|
// ordering helper (by variable then polynomial id then index) - total but arbitrary
|
||||||
|
struct lt {
|
||||||
|
pmanager & m_pm;
|
||||||
|
lt(pmanager & pm): m_pm(pm) {}
|
||||||
|
bool operator()(indexed_root const & a, indexed_root const & b) const {
|
||||||
|
if (a.m_var != b.m_var) return a.m_var < b.m_var;
|
||||||
|
if (a.m_p != b.m_p) return m_pm.id(a.m_p) < m_pm.id(b.m_p);
|
||||||
|
return a.m_index < b.m_index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct hash_proc {
|
||||||
|
pmanager & m_pm;
|
||||||
|
hash_proc(pmanager & pm): m_pm(pm) {}
|
||||||
|
unsigned operator()(indexed_root const & r) const {
|
||||||
|
return combine(combine(m_pm.id(r.m_p), r.m_index), r.m_var);
|
||||||
|
}
|
||||||
|
static unsigned combine(unsigned a, unsigned b) { return a * 1315423911u + b + (a<<5) + (b>>2); }
|
||||||
|
};
|
||||||
|
struct eq_proc {
|
||||||
|
pmanager & m_pm;
|
||||||
|
eq_proc(pmanager & pm): m_pm(pm) {}
|
||||||
|
bool operator()(indexed_root const & a, indexed_root const & b) const {
|
||||||
|
return a.m_var == b.m_var && a.m_index == b.m_index && a.m_p == b.m_p; }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<indexed_root> indexed_root_vector;
|
||||||
|
|
||||||
inline ineq_atom * to_ineq_atom(atom * a) { SASSERT(a->is_ineq_atom()); return static_cast<ineq_atom*>(a); }
|
inline ineq_atom * to_ineq_atom(atom * a) { SASSERT(a->is_ineq_atom()); return static_cast<ineq_atom*>(a); }
|
||||||
inline root_atom * to_root_atom(atom * a) { SASSERT(a->is_root_atom()); return static_cast<root_atom*>(a); }
|
inline root_atom * to_root_atom(atom * a) { SASSERT(a->is_root_atom()); return static_cast<root_atom*>(a); }
|
||||||
inline ineq_atom const * to_ineq_atom(atom const * a) { SASSERT(a->is_ineq_atom()); return static_cast<ineq_atom const *>(a); }
|
inline ineq_atom const * to_ineq_atom(atom const * a) { SASSERT(a->is_ineq_atom()); return static_cast<ineq_atom const *>(a); }
|
||||||
|
|
|
||||||
1591
src/test/nlsat.cpp
1591
src/test/nlsat.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -551,6 +551,8 @@ X(Global, linear_equation_mk, "linear equation mk")
|
||||||
X(Global, list, "list")
|
X(Global, list, "list")
|
||||||
X(Global, literal_occ, "literal occ")
|
X(Global, literal_occ, "literal occ")
|
||||||
X(Global, lp_core, "lp core")
|
X(Global, lp_core, "lp core")
|
||||||
|
X(Global, lws, "levelwise")
|
||||||
|
X(Global, lws_norm, "levelwise normalize")
|
||||||
X(Global, macro_bug, "macro bug")
|
X(Global, macro_bug, "macro bug")
|
||||||
X(Global, macro_finder, "macro finder")
|
X(Global, macro_finder, "macro finder")
|
||||||
X(Global, macro_insert, "macro insert")
|
X(Global, macro_insert, "macro insert")
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_seed(unsigned s) { m_data = s; }
|
void set_seed(unsigned s) { m_data = s; }
|
||||||
|
unsigned get_seed() const { return m_data; }
|
||||||
|
|
||||||
int operator()() {
|
int operator()() {
|
||||||
return ((m_data = m_data * 214013L + 2531011L) >> 16) & 0x7fff;
|
return ((m_data = m_data * 214013L + 2531011L) >> 16) & 0x7fff;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue