3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-08-14 01:36:48 +00:00
Commit graph

23121 commits

Author SHA1 Message Date
CEisenhofer
0423723054 Potential improvements on split_set 2026-07-29 13:16:59 +02:00
CEisenhofer
9454d7d24d Update benchmark script 2026-07-27 16:33:10 +02:00
CEisenhofer
c9877081ad Regex fix 2026-07-27 16:13:30 +02:00
CEisenhofer
d1e1d6cb80 seq_monadic refactoring
Bug fix in seq_monadic
2026-07-20 19:57:51 +02:00
CEisenhofer
b611a7f87d Some cleanup in seq_monadic 2026-07-20 19:37:08 +02:00
CEisenhofer
bf3117840e Some more tests for seq_monadic
Bug fix in seq_monadic
Update benchmark script
2026-07-20 19:13:36 +02:00
CEisenhofer
45305b64cb First attempt to integrate seq_monadic in nseq
Bug with missing rewriting for integer side constraints
2026-07-20 18:58:13 +02:00
CEisenhofer
d885580633 Enforce all power terms are internalized at model construction phase 2026-07-20 18:01:31 +02:00
CEisenhofer
386f8528b6 Merge remote-tracking branch 'origin/split_set' into c3 2026-07-20 15:04:53 +02:00
Nikolaj Bjorner
9c21f9e184 Expand shady-parts notes in seq_monadic (N-relative nullability, epsilon handling, STATE_CAP)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 34aa9af0-4977-411d-aaa7-7cb81cc4e9f8
2026-07-19 12:30:30 -07:00
Nikolaj Bjorner
e17fba7e39 Document known shady parts in seq_monadic (witness extraction, epsilon/N handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 34aa9af0-4977-411d-aaa7-7cb81cc4e9f8
2026-07-19 12:20:00 -07:00
Nikolaj Bjorner
b616f714ad Add continuation-regex split service (seq_monadic)
Port seq_monadic to the split_set branch, reimplemented against the
cont_regex / split / split_manager API. Element-sort agnostic: relies on
the derivative engine and th_rewriter, no character-specific reasoning.

- Global derivative-transition graph reused across regexes (intern_state /
  expand_state / build_graph) so states, nullability and cofactor successors
  are computed once.
- embed(r) = <concat(r, epsilon), epsilon>; the epsilon accept-state marks the
  membership (nullable) case uniformly.
- intersect handles general cont_regex, including non-epsilon reach targets N
  (membership BFS + product-reachability paths).
- No budget / reset_pin.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 34aa9af0-4977-411d-aaa7-7cb81cc4e9f8
2026-07-19 12:08:35 -07:00
CEisenhofer
5d110a1b19 Added missing test-file 2026-07-17 16:39:10 +02:00
CEisenhofer
3baad0f171 Fine & Wilf-based elimination of power-vs-power case (disabled by default for now) 2026-07-17 15:59:28 +02:00
CEisenhofer
4f1f3ccc69 Bug and performance fixes 2026-07-16 16:08:51 +02:00
CEisenhofer
a3f0c83be3 2 bug fixes for regex monadic decomposition 2026-07-16 11:46:41 +02:00
CEisenhofer
2cc17b5864 Merge remote-tracking branch 'origin' into c3 2026-07-16 09:33:18 +02:00
Nikolaj Bjorner
ca2ed44951 disable instantiation for inconsistent states
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 20:55:11 -07:00
Nikolaj Bjorner
09ffec52e8 disable instantiation for inconsistent states
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 20:54:23 -07:00
Nikolaj Bjorner
9945e3dc9a add Margus' unfold-fold operation and consolidate range-predicate recognizer/constructor.
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 20:44:46 -07:00
Nikolaj Bjorner
2db625606d fold functionality into seq_range_collapse
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 20:04:48 -07:00
Nikolaj Bjorner
9fb2b491d6 remove relvancy marking code
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 15:17:14 -07:00
Nikolaj Bjorner
661bb13039 change relevancy marking to top-level on inconsistent states
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-15 11:29:20 -07:00
CEisenhofer
9db45a90ca Merge remote-tracking branch 'origin' into c3 2026-07-15 20:23:01 +02:00
CEisenhofer
e0b3da36ec Merge remote-tracking branch 'origin/c3-split-perf' into c3-rewrote-regex-unwinding 2026-07-15 19:39:53 +02:00
CEisenhofer
0a3ce3be50 Make benchmark script run on Linux 2026-07-15 18:02:02 +02:00
CEisenhofer
96241a5025 Model extraction bug 2026-07-15 17:10:51 +02:00
CEisenhofer
325cff40da Some performance fixes 2026-07-15 16:53:16 +02:00
Clemens Eisenhofer
4b46dc788c Fixes for some theoretical bugs 2026-07-15 09:45:04 +02:00
Nikolaj Bjorner
ad063580dc theory_lra: eagerly propagate offset equalities x=y (fixes #10065)
When a term column x - y is fixed to 0 (e.g. from t <= ca and t >= ca),
theory_lra previously discovered the implied equality x = y only lazily via
assume_eqs() during final_check. On the FP fuel-recursive axiom in issue #10065
this discovery is starved by E-matching, which unfolds the recursion and
bit-blasts an exploding FP subproblem before the branch closes.

Add propagate_offset_eq() to detect a fixed 2-variable offset term with opposite
unit-scaled coefficients and propagate the operand equality x = y directly to the
core, so congruence closure merges dependent terms immediately. This mirrors the
offset-row propagation performed by theory_arith (propagate_cheap_eq) and matches
its behavior on this benchmark (timeout -> unsat 0.06s, 2 quant-instantiations).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 726c4e71-03ff-45f6-8322-5253254e1d7e
2026-07-14 22:46:06 -07:00
Nikolaj Bjorner
7c8c6a4df0 fixup pattern inference 2026-07-14 21:01:15 -07:00
Lev Nachmanson
1b39b0e50f
Fix SIGSEGV in nlsat from incorrect algebraic number comparison (#10129)
## Problem

A QF_NIA benchmark (`From_T2__ex16.t2__p22243_terminationG_0.smt2`, run
with `-T:200 model_validate=true`) crashes with SIGSEGV inside nlsat.

## Root cause

In `algebraic_numbers::manager:👿:compare_core`, the
interval-separation workaround computed the isolating intervals of `a`
and `b` with:

```cpp
if (get_interval(a, la, ua, precision) &&
    get_interval(b, lb, ub, precision)) { ... }
```

`&&` short-circuits: when `a` is **rational**, `get_interval(a, ...)`
finds the exact root and returns `false`, so `get_interval(b, ...)`
never runs and `b`'s bounds `lb`/`ub` stay **0**. Those bounds are used
*unconditionally* below the `if` (in the `compare(cell_a, u_b)` /
`compare(cell_b, l_a)` checks), so `a` was effectively compared against
`0`, producing an incorrect and self-inconsistent sign (`compare`
returned `+1` while `<`, `=`, `>` were all false).

Concretely, comparing `c = 39017/131072` (rational) with `d ≈
0.297676176` (root of a quadratic) returned `c > d`, though `c < d`.
Downstream, this made nlsat's `interval_set::is_full` miss full coverage
of ℝ, so `pick_in_complement` was invoked on an empty complement and
read `s->m_intervals[UINT_MAX]` — a crash guarded only by a
release-stripped `SASSERT` (`nlsat_interval_set.cpp`).

## Fix

Compute both intervals unconditionally so `b`'s bounds are always valid
before they are used.

## Validation

- The crashing benchmark now returns `unsat` (verified on both macOS and
a Linux `RelWithDebInfo` build where the SIGSEGV was originally
reproduced under gdb).
- Unit tests pass: `algebraic`, `upolynomial`, `polynomial`, `nlsat`.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-14 17:08:08 -07:00
Nikolaj Bjorner
0790dfd876 Fix use-after-free in spacer hypothesis_reducer::reduce_core (#10123)
reduce_core looped with while (true) and read p = todo.back() with no
empty check, exiting only when it reached a hypothesis-free sub-proof of
false. When hypothesis reduction cannot close all hypotheses on the root
proof, todo drains and todo.back() reads past the end of the vector,
producing a heap-use-after-free (SIGSEGV in Fixedpoint.query with
spacer.keep_proxy=false). Whether the root closes depends on search order,
making the crash nondeterministic / seed-dependent.

Bound the loop by todo emptiness, track the reduced root across cache-hit
pops, and return it if the loop drains without hitting the false-subproof
early return.

Verified on the issue #10123 benchmark: the UAF is eliminated across
spacer.random_seed 0/3/7/13/42/99, all returning unsat.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 726c4e71-03ff-45f6-8322-5253254e1d7e
2026-07-14 15:18:02 -07:00
Nikolaj Bjorner
febe471ea4 put tag back in 2026-07-14 14:10:36 -07:00
Nikolaj Bjorner
24bde17501 debug array models 2026-07-14 13:52:14 -07:00
Nikolaj Bjorner
c57c6e564f fix doc build 2026-07-14 13:52:14 -07:00
Nikolaj Bjorner
1a8e18bc48 hardwire ARM tag to 0 on macosx 2026-07-14 13:52:14 -07:00
Lev Nachmanson
becb995757
lp: avoid heap allocation when relocating coefficients in static_matrix::remove_element (#10115)
## Summary

Optimizes `lp::static_matrix<..>::remove_element`, reported as a hotspot
in
[Z3Prover/bench#3143](https://github.com/Z3Prover/bench/discussions/3143)
(the #1 exclusive-time function, ~19.6%, on
`inputs/issues/iss-5131/bug-1.smt2`).

`remove_element` uses swap-remove but **deep-copied** the relocated tail
coefficient:

```cpp
auto & rc = row_vals[row_offset] = row_vals.back(); // copy from the tail
```

In namespace `lp`, `mpq` is a typedef for the copyable `rational`, so
this copy-assign allocates a fresh bignum whenever the **source (the
tail)** is big — matching the `malloc`/`_int_malloc` entries in the
reported profile. The tail element is `pop_back`'d immediately
afterwards, so the allocation is wasteful.

## Change

A copy-assign allocates only when the **source** is big
(`mpz_manager::set` → `big_set`). So relocate the tail coefficient by
**swapping** exactly in that case — stealing its already-allocated
storage, zero `malloc`. When the tail is small, a plain copy never
allocates and is cheaper than swapping the `mpz` internals; the
destination's size is irrelevant. The column-cell relocation is
unchanged (a `column_cell` carries no coefficient).

Single-file change; no new parameters.

## Benchmarks

A/B produced by toggling the new code path against the original
deep-copy (via a temporary parameter, not included here).

- **rise-runner-2** (initial `is_big()||is_big()` variant): QF_LIA_small
neutral; certora identical outcomes, −1.5% paired solve-time.
- **128-core Linux box**, `run_on_dir.py`, `-max_workers 32` (final
tail-only variant):

| Set | Files | `-T` | Solved (new = orig) | Avg-time ratio new/orig |
Correctness |
|---|---|---|---|---|---|
| QF_LIA (SMT-LIB) | 6947 | 20s | 5817 ≈ 5815 | 1.00000 | identical (±2
timeout-edge) |
| certora | 308 | 120s | 186 = 186 | 0.9977 | identical, 0 unique
timeouts |
| QF_LRA (SMT-LIB 2025) | 1753 | 120s | 1552 = 1552 | 0.9985–0.9991 |
identical, 0 real regressions |

Consistently **correctness-neutral and marginally faster** (~0.1–0.5%)
on large-coefficient LP sets, flat on small-coefficient inputs. The
per-`remove_element` allocation saved is small relative to total solve
time, so the whole-solver delta is a fraction of a percent — a clean
micro-optimization with no downside.

## Validation
- `make`/`ninja` build clean; `test-z3 /a` — 92/92 pass.
- Baseline vs patched output byte-identical on the reported benchmark;
identical solve sets across all three benchmark suites above.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-14 13:04:12 -07:00
Nikolaj Bjorner
2f48e355d8
Add symbolic-modulus congruence rule to nla_divisions (#10119)
Implement check_mod_congruence in nla_divisions: for two mod-atoms
sharing a (possibly symbolic) divisor y, emit the model-guided tautology
div(x,y) - div(s,y) = delta => mod(x,y) - mod(s,y) = (x - s) - delta*y.
This discharges linear congruences over a symbolic modulus that the
nonlinear core did not otherwise isolate. Thread the div(x,y) variable
through add_divisibility (nla_core/nla_solver/nla_divisions) and
register it in theory_lra for symbolic-divisor mod terms.

Solves FStar.BitVector-1 (0.7s) and FStar.Matrix-1 (1.6s), previously
300s timeouts; all 92 unit tests pass.


Copilot-Session: 726c4e71-03ff-45f6-8322-5253254e1d7e

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-14 12:31:17 -07:00
Copilot
82a0d42970
Improve hash mixing to eliminate bitvector-expression hash-table clustering (#10120)
Large Python bitvector workloads were hitting a sharp performance cliff
during `Solver.add(...)`, consistent with severe hash-table clustering
in expression-heavy assertion paths. The issue was sensitive to input
size/alignment, indicating weak low-bit dispersion in hash combination.

- **Hash mixing update (`src/util/hash.h`)**
- Replaced the old `combine_hash(h1, h2)` arithmetic/xor sequence with
stronger mixing:
    - boost-style combine step
    - `hash_u(...)` finalization
- Goal: improve low-bit entropy used by chained hash-table bucket
selection under aligned/high-volume AST patterns.

- **Regression guard and A/B comparison (`src/test/chashtable.cpp`)**
- Added `tst_combine_hash_low_bits()` and invoked it from
`tst_chashtable()`.
- The test stresses aligned first components (`i << 12`) combined with a
fixed seed.
- Added an in-test comparison between the **old** and **new** pairwise
hash combiners and validates:
- reduced collision counts for low-bit projections (8-bit and 16-bit
suffixes),
    - improved low-bit uniformity for 8-bit and 16-bit suffixes,
- reported prefix/suffix uniformity metrics (high/low 8 and 16 bits) for
visibility in test output.

```cpp
static inline unsigned combine_hash(unsigned h1, unsigned h2) {
    h1 ^= h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2);
    return hash_u(h1);
}
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-14 11:51:49 -07:00
Nikolaj Bjorner
c4cb5bbc15 update release.yml and tptp_frontend
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-14 11:16:56 -07:00
Nikolaj Bjorner
e2b9e3a6dc distribute quantifiers over Booleans 2026-07-14 09:57:04 -07:00
Nikolaj Bjorner
46b1c68f59
Update nightly.yml 2026-07-14 09:34:29 -07:00
Nikolaj Bjorner
8f2713bdb1 Add array eta-reduction rewrite: (lambda (x*) (select a x*)) -> a
Sound by array extensionality when a is independent of the bound
variables. Implemented as array_rewriter::mk_lambda_core and wired into
th_rewriter::reduce_quantifier alongside the ground-lambda case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-14 09:24:16 -07:00
Simon Scatton
25e0e6f780
fix(bazel): pin CMake library installs to lib (#10126)
rules_foreign_cc expects libraries under its default lib output
directory. GNUInstallDirs may instead select lib64, causing Bazel to
reject an otherwise successful CMake build because its declared output
is missing.

Share the default CMake arguments between the static and dynamic targets
and set CMAKE_INSTALL_LIBDIR to lib.
2026-07-14 08:30:48 -07:00
Copilot
98c8f2935e
Nightly: prevent test.PyPI publish failure by rewriting unsupported macOS wheel tags (#10122)
The nightly workflow’s `Publish to test.PyPI` job fails because
test.PyPI rejects uploaded macOS wheels tagged `macosx_13_3_*`. This
change keeps the validation publish path working by rewriting
unsupported macOS wheel tags to a supported form during that specific
upload step.

- **Root cause reflected in workflow behavior**
- `publish-test-pypi` currently uploads all artifacts from
`PythonPackages`, including macOS wheels that test.PyPI does not accept.

- **Workflow change (surgical)**
- In `.github/workflows/nightly.yml`, added a pre-upload rewrite step in
`publish-test-pypi` to rename wheel tags from `macosx_13_3_*` to
`macosx_13_*` in `dist/`.
- Left artifact production unchanged; only the filenames used for the
test.PyPI upload are adjusted.

- **Effect on release flow**
- test.PyPI upload continues for sdist + Linux/Windows wheels and now
includes rewritten macOS wheels.
- Nightly macOS artifacts remain built and available through existing
artifact/release paths.

```yaml
- name: Rewrite macOS wheel tags unsupported by test.PyPI
  run: |
    for whl in dist/*-macosx_13_3_*.whl; do
      [ -e "$whl" ] || continue
      mv "$whl" "${whl/macosx_13_3_/macosx_13_}"
    done
    ls -l dist
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-14 08:21:20 -07:00
Nikolaj Bjorner
eae0530675 update version to 4.17.1
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-07-13 18:56:19 -07:00
Copilot
627e19197c
Update RELEASE_NOTES.md for v4.17.0 (PRs #9798–#10116) (#10121)
Appends ~35 release note entries to the `Version 4.17.0` section,
covering the substantial changes since PR #9700 as catalogued in
discussion #10117.

## New features
- `z3regex` Python module bridging Python `re` syntax to Z3 regex ASTs
- `OP_RE_XOR` + bisimulation-based ground regex equivalence (Margus
Veanes)
- `bv_divrem_bounds_tactic` for bounded BV div/rem constraints
- Linear divisibility closure lemma for lp/nla solver
- Pyodide (WASM) wheel support; Bazel versioned shared objects
- rlimit support in fixedpoint/Horn parameters
- HO matching improvements: curry-order, variable shift, lazy MAM
deferral, throttle configs
- Go bindings: concurrent `dec_ref` for GC finalizers

## Bug fixes
- Three optimization soundness fixes (strict optima with delta-rational
/ infinitesimal bounds, unvalidated LP bound)
- Horn clause solver segfault on unused quantified variables
- .NET API memory leak (NativeContext finalizer, delegate lifetime, GC
pressure)
- Parallel solver unsigned overflow in conflict budget escalation
- `psmt`/`smt_parallel` infinite loops on theory-incomplete cubes
- `seq_rewriter` `re.range` empty-language and soundness fixes
- Mod rewriter non-termination on symbolic modulus
- `elim_uncnstr` disabled by `has_type_vars()` flag (issue #6260)
- MBQI timeout regression in HO term enumeration
- `bv2int_translator` assertion violation with `smt.bv.solver=2`

## API fixes
- Java `Sort.create()` returns `EnumSort` (not `DatatypeSort`) for enum
sorts

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-13 18:43:58 -07:00
Copilot
5c0443591d
Align release macOS build targets with nightly’s 13.3 settings (#10118)
The `Release Build` workflow still targeted macOS 13.0 for the x64/arm64
packaging jobs, while the codebase now relies on libc++ functionality
that is only available with a 13.3 deployment target. This updates the
release workflow to use the same macOS target configuration already
applied in `nightly.yml`.

- **Release workflow**
- Raise `MACOSX_DEPLOYMENT_TARGET` from `13.0` to `13.3` for both
`mac-build-x64` and `mac-build-arm64`
- Update the packaging target passed to `mk_unix_dist.py` from
`--os=osx-13.0` to `--os=osx-13.3`

- **Config alignment**
- Bring `release.yml` in sync with the existing nightly macOS fix so
both workflows build against the same minimum macOS version

```yaml
env:
  MACOSX_DEPLOYMENT_TARGET: "13.3"

run: python scripts/mk_unix_dist.py --arch=x64 --os=osx-13.3
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-13 17:51:57 -07:00
Copilot
26ad30bb76
Fix invalid sequence models for seq.foldl results observed through seq.nth (#10111)
`seq.foldl` could produce a concrete sequence model while related
`seq.nth` constraints were still validated against stale or
underconstrained length information, leading to invalid models. In the
reported case, `all` was modeled as `(seq.++ (seq.unit 7) (seq.unit 0))`
while `final = (seq.nth all 0)` remained inconsistent with `final = 6`.

- **Root cause**
- Sequence solutions were propagated as equalities, but parent `seq.len`
terms were not updated when a sequence term was solved.
- As a result, `seq.nth` guard reasoning could miss that a solved
sequence had known in-bounds length.

- **Solver change**
- Extend `theory_seq::add_solution` to collect parent `seq.len`
expressions of a solved term when the solved result is sequence-typed.
- After propagating the solved sequence equality, also propagate the
rewritten length equality for those parent length terms.
- Keep this propagation guarded to sequence results so scalar
`seq.foldl`/`seq.foldli` solutions do not regress from `sat` to
`unknown` under model validation.

- **Regression coverage**
  - Add a focused test for the reported SMT-LIB pattern:
    - `all = seq.foldl(...)`
    - `final = seq.nth all 0`
    - `initial = 0`
    - `final = 6`
- Add focused scalar `seq.foldl`/`seq.foldli` model-validation coverage
for the existing benchmark shapes that must continue returning `sat`.
- The regressions check both that model validation no longer reports an
invalid model for the `seq.nth` case and that scalar fold/foldi cases do
not regress to `unknown`.

- **Effect**
- Solved sequence terms now push enough derived length information for
dependent `seq.nth` constraints to validate against the actual modeled
sequence.
  - Existing scalar fold/foldi solving behavior is preserved.

```smt2
(define-fun all_sums ((prev_sums (Seq Int)) (elem Int)) (Seq Int)
  (seq.++ (seq.unit (+ (seq.nth prev_sums 0) elem)) prev_sums)
)

(assert (= all (seq.foldl all_sums (seq.unit initial) elements)))
(assert (= final (seq.nth all 0)))
(assert (= initial 0))
(assert (= final 6))
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-13 17:33:39 -07:00