title: Investigate noDisc optimization when lb and ub are roots of same polynomial

description:
SMT-RAT does noDisc for leaves connected to the section-defining polynomial in the section case. This might extend to sector spanning_tree mode when lb and ub come from the same polynomial but different root functions.

Example: polynomial p has roots at x=1 and x=3, sample at x=2. Then lb is root 1 of p, ub is root 2 of p. A leaf polynomial q connected only to p might not need disc(q) since Res(q,p) constrains q's roots relative to both bounds.

The previous implementation was unsound because it applied to general leaves connected to any boundary, but discriminants are needed to ensure a polynomial's own roots don't merge/split.

Investigate whether this optimization is valid when lb and ub are different roots of the same polynomial.

status: open

issue_type: task

created_by: Lev Nachmanson


title: Unsound lemma in ppblockterm.t2 with nlsat.check_lemma=true

description:
Running:
build/release/z3 /Users/levnach/dev/data/QF_NIA/20170427-VeryMax/ITS/From_T2__ppblockterm.t2__p7867_terminationG_0.smt2 nlsat.check_lemma=true

exposes an unsound lemma.

The test case should reconstruct the run of levelwise with these parameters:

=== LEVELWISE input polys
p[0]: x16 + x9
p[1]: x16 x24 + x9 x24 + x13 x19 + x5 x19 + x12 x15 + x10 x15 - 2
p[2]: x4
p[3]: x4 x24 + x2 x19 + x11 x15
=== END LEVELWISE INPUT (4 polynomials) ===

Variable values at SAMPLE point:
x0 -> 1
x1 -> 1
x2 -> -1
x3 -> -1
x4 -> 1
x5 -> -1
x6 -> 1
x7 -> 2
x8 -> 1
x9 -> 1
x10 -> -1
x11 -> 1
x12 -> -2
x13 -> -2
x14 -> -2
x15 -> 0
x16 -> 2
x17 -> 0
x18 -> 0
x19 -> 0
x20 -> 0
x21 -> 0
x22 -> 0
x23 -> 0

TODO:
1. Create a test in nlsat.cpp with the same input polynomials and sample point
2. Reproduce the unsound lemma with test-z3
3. Fix the unsound lemma

notes:
Investigation findings:

1. Found and fixed typo bug in poly_has_roots(): was 'i < vec_get(...)' instead of 'vec_get(...)'. This caused leading coefficients to be incorrectly omitted for polynomials with index >= 1 at the top level.

2. Added helper function is_point_inside_cell() to check cell membership.

3. The test still fails - counterexample is inside the cell. The cell's projection at level 19 becomes constant (LC=0 at both sample and counterexample). Need further investigation of how nlsat_explain combines cell with conflict literals.

Key finding from check_lemma output:

The original conflict is:

The lemma says: within cell, either x24 < root[1](p1) OR p3 > 0.

But the checker found a point INSIDE the cell where:
- x24 >= root[1](p1)
- p3 <= 0

This means the cell doesn't preserve the conflict. The issue may be that levelwise treats polynomials for SIGN preservation, but the conflict involves ROOT ATOMS.

status: closed

issue_type: bug

close_reason: Fixed: polynomials without roots at section level need LC+disc for delineability


title: Use commas instead of parentheses in comments

description:
In comments, avoid parenthetical expressions. Use commas for natural flow.

Example:
// Before: at most one root below v and one root above v (or a single root at v for sections).
// After: at most one root below v and one root above v, or a single root at v for sections.

status: tombstone

close_reason: Closed

deleted_by: batch delete

delete_reason: batch delete

original_type: task


title: Refactor add_section_projections to avoid duplicate witness handling code

description:
The if/else branches in add_section_projections have duplicate witness checking logic. Refactor to extract common code:

```cpp
// Current (duplicate logic):
if (is_section_poly) {
    polynomial_ref lc(m_pm);
    unsigned deg = m_pm.degree(p, m_level);
    lc = m_pm.coeff(p, m_level, deg);

            witness = polynomial_ref(m_pm);

    add_projection_for_poly(p, m_level, witness, true, true);
} else {
        request_factorized(witness, inv_req::sign);
}

// Consider restructuring to reduce duplication
```


title: Remove curly braces around single-line if/else statements in levelwise.cpp

description:
Code style cleanup: remove unnecessary curly braces around single-line if/else statements to match Z3's coding style. Example:
```cpp
// Before:
if (both[i].upper_rf < both[p].upper_rf) {
    out_children[i].push_back(p);
} else {
    out_children[p].push_back(i);
}

// After:
if (both[i].upper_rf < both[p].upper_rf)
    out_children[i].push_back(p);
else
    out_children[p].push_back(i);
```


title: Preserve RNG state around check_lemma() in nlsat_solver.cpp

description:
FIXED: Added RNG state preservation around check_lemma() in nlsat_solver.cpp.

Changes:
1. Added get_seed() method to random_gen class in util.h
2. Added get_seed() method to interval_set_manager in nlsat_interval_set.h
3. Modified check_lemma() to save and restore both m_random_seed and m_ism seed

This ensures that lemma checking doesn't affect the solver's randomized decisions, preserving determinism.


title: Replace SMT-RAT references with theoretical explanations in levelwise.cpp

description:
Comments in levelwise.cpp reference SMT-RAT without explaining why the optimizations are correct. Replace these references with theoretical justifications based on:

- ~/Downloads/pre_proj_del.pdf
- ~/Downloads/projective_delineability.pdf

This will make the code self-documenting and not dependent on external implementation references.


title: Test timeouts when lws=false in nlsat_params.pyg

description:
BUG FIXED: Regression in nlsat_explain.cpp fallback projection.

ROOT CAUSE: Extra insert_fresh_factors_in_todo() calls were added in ensure_sign() and normalize() for levelwise support, but these caused polynomial cascade in the fallback projection.

FIX: Made these additions conditional on m_solver.apply_levelwise():
1. ensure_sign(): Only call insert_fresh_factors_in_todo(p) when levelwise is enabled
2. normalize(): Only collect m_lower_stage_polys when levelwise is enabled
3. main(): Only add m_lower_stage_polys to m_ps when levelwise is enabled

The fallback projection (used by master and when lws=false) handles delineability through its own mechanism and doesn't need these extra polynomials.

All 5 previously failing benchmarks now pass with lws=false:
- issue-1658.smt2: 0.01s
- 7489.smt2: 0.56s
- 8099.smt2: 0.07s
- sqrt-bug.smt2: 0.01s
- issue-1694.smt2: 0.01s