3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-29 03:48:51 +00:00
Commit graph

22457 commits

Author SHA1 Message Date
Margus Veanes
9fa8adb742 Make derivative union reduction iterative to avoid stack overflow
The previous commit reduced unions by recursively inserting each disjunct
into the other operand, which recurses with depth proportional to the
union width.  On wide range-product unions (z3test 5721 sub#2) that
overflowed the stack (exit 0xC00000FD), turning a timeout into a crash.

Reformulate mk_union_core to flatten both operands into a disjunct set via
an explicit worklist and reduce it with add_union_elem (a bounded loop
applying subsumption, prefix factoring and same-condition ITE merge
against every existing member).  No width-proportional recursion remains.

5731 stays fixed (0.04s), 5728 stays at ~0.02s, 5721 sub#2 no longer
crashes (cleanly times out as before), 92/92 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 10:12:13 +03:00
Margus Veanes
2161f9cfac Merge same-condition ITEs and subsume nested union members in derivative
The antimirov-mode symbolic derivative builds union(ite(c,t1,e1),
ite(c,t2,e2)) when deriving a concat/loop, e.g. delta(a{0,k}.R.ab)
yields a{0,k-1}.(R.ab) and a{0,k}.(R.ab) under the same character
condition c.  derive::mk_union_core only applied subsumption pairwise on
its two direct operands, so the two prefixes -- sitting in separate ITE
branches and nested behind an unrelated union member -- never met in a
common union and the subset collapse a{0,k-1}.X subset a{0,k}.X never
fired.  Each derivative step then produced a fresh a{0,k}.X state,
exploding the emptiness/membership worklist to O(N) states on
loop-intersect-comp regexes (z3test 5731 sub#1/#3 timed out).

mk_union_core now (1) flattens the left operand, (2) inserts a single
disjunct into a union by comparing it against every member, and
(3) merges same-condition ITE alternatives ite(c,t1,_) U ite(c,t2,_) ->
ite(c, t1 U t2, _), bringing the bodies into one union where subsumption
collapses them.

5731 all sub-checks now solve in 0.06s (were timeouts); 5728 stays at
0.16s; 92/92 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 09:48:19 +03:00
Margus Veanes
10d8b8e156 Push complement through union/intersection in symbolic derivative
The derivative's mk_complement_core left complement over a union/intersection
unfolded (e.g. d(comp(S*a)) = comp(S*a U eps)). Across successive derivatives
the complemented argument grew (comp(S*a U eps U ...)), giving every loop /
intersection state a fresh AST identity and defeating the state-graph dead-state
detection, so str.in_re emptiness on loop-intersect-comp regexes timed out.

Apply De Morgan in mk_complement_core so complement is pushed to the leaves:
  ~(A U B) -> ~A & ~B     (keeps ~A invariant; ~(S*a U eps) -> ~(S*a) & .+)
  ~(A & B) -> ~A U ~B     (exposes unions so antimirov non-emptiness can decide
                           each alternative separately)
This matches the form master's inline mk_derivative produces. z3test 5728 now
solves in 0.13s (was timeout) and parts of 5731/5721 are resolved. Full unit
suite: 92 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 09:29:06 +03:00
Margus Veanes
6af8b4a11d Detect empty regex membership via re_is_empty fallback; fix test build
block_if_empty relied solely on the AST-identity state graph for dead-state
detection, which never closes on intersections/complements whose derivative
product states do not canonicalize. For ground regexes, fall back to the
antimirov NFA reachability check (re_is_empty), the same procedure propagate_eq
already uses for re.none equalities. Resolves str.in_re emptiness timeouts on
inter(., comp(.)) regexes (e.g. z3test 5728, parts of 5721).

Also drop a stale is_antimirov_union reference left in seq_regex_bisim.cpp after
the operator removal (test-z3 did not compile) and update a now-stale comment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 08:47:55 +03:00
Nikolaj Bjorner
92b349c669 remove antimirov union and fix is_ground -> re().is_ground. There is ground and there is ground
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 22:20:06 -07:00
Nikolaj Bjorner
2c5aef7d28 change back coalesce_chars to true, a regression
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 21:40:26 -07:00
Nikolaj Bjorner
596f158e44 change back coalesce_chars to true, a regression
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 21:39:54 -07:00
Margus Veanes
eab5ff1eff Fix uninitialized clo/chi read in mk_re_range; drop disabled test block
mk_re_range read clo/chi in the singleton/ordering checks before the
is_empty guard, but an early is_empty (from the length checks) leaves
them uninitialized. Initialize to 0 and move the is_empty return ahead
of the singleton check so uninitialized values are never used.

Also remove the disabled if(false) range-complement block in the
seq_rewriter unit test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 06:58:41 +03:00
Nikolaj Bjorner
5e38eb328d detect singleton range
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 20:49:27 -07:00
Nikolaj Bjorner
98226e1842 disable some unit tests, use smart constructor in seq_range_collapse
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 20:33:14 -07:00
Nikolaj Bjorner
baeaa84bde have it create string ranges
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 20:26:35 -07:00
Nikolaj Bjorner
f614721a92 have it create string ranges
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 20:23:09 -07:00
Nikolaj Bjorner
ee6e4e2821 detect unit('a') in addition to a
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 20:15:13 -07:00
Nikolaj Bjorner
c530faab4e remove deprecated derivative code 2026-06-25 19:40:35 -07:00
Nikolaj Bjorner
110a670ea1 mk-diff
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 18:47:07 -07:00
Nikolaj Bjorner
87c2c14b6a reorder in cmakefile
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 18:47:07 -07:00
Nikolaj Bjorner
2703351f54
Merge branch 'master' into derive-with-ranges 2026-06-25 18:40:44 -07:00
Nikolaj Bjorner
00ed0af3ed rename files to streamline naming
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 18:35:17 -07:00
Margus Veanes
d3fbefbeb9 perf(seq): right-associate concats in derivative; restore union subsumption; fix mk_regex_concat sort bug
The symbolic derivative of a left-associated concat (a*b)*r2 recurses
through the entire left spine, exceeding m_max_depth and emitting stuck
symbolic re.derivative terms that accumulate across NFA states and cause
an exponential blow-up on contains-pattern intersections.  Right-
associate the concat (via derive::mk_concat, a single linear pass that
does not touch the derivative depth counter) before deriving, so the head
stays atomic and recursion is shallow.

Also restore the L(a)subset L(b) subsumption in mk_union_core (collapses
semantically-equal union states in antimirov intersection derivatives),
and fix a latent sort bug in mk_regex_concat where the '..* = .+'
rewrite passed the element sort instead of the regex sort to
mk_full_char, triggering a 're.+' sort-mismatch exception once exercised
by the derivative path.

Result on QF_S/20250410-matching wildcard-matching-regex set: 02 15s->0.6s,
04 15s->0.7s, 05 timeout->4.2s, 59/62 timeout->0.1-0.3s (vs master).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 03:55:21 +03:00
dependabot[bot]
0596c7c634
Bump actions/cache from 5.0.5 to 6.0.0 (#9962)
Bumps [actions/cache](https://github.com/actions/cache) from 5.0.5 to
6.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/releases">actions/cache's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update packages, migrate to ESM by <a
href="https://github.com/Samirat"><code>@​Samirat</code></a> in <a
href="https://redirect.github.com/actions/cache/pull/1760">actions/cache#1760</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/cache/compare/v5...v6.0.0">https://github.com/actions/cache/compare/v5...v6.0.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/cache/blob/main/RELEASES.md">actions/cache's
changelog</a>.</em></p>
<blockquote>
<h3>6.0.0</h3>
<ul>
<li>Updated <code>@actions/cache</code> to ^6.0.1,
<code>@actions/core</code> to ^3.0.1, <code>@actions/exec</code> to
^3.0.0, <code>@actions/io</code> to ^3.0.2</li>
<li>Migrated to ESM module system</li>
<li>Upgraded Jest to v30 and test infrastructure to be ESM
compatible</li>
</ul>
<h3>5.0.4</h3>
<ul>
<li>Bump <code>minimatch</code> to v3.1.5 (fixes ReDoS via globstar
patterns)</li>
<li>Bump <code>undici</code> to v6.24.1 (WebSocket decompression bomb
protection, header validation fixes)</li>
<li>Bump <code>fast-xml-parser</code> to v5.5.6</li>
</ul>
<h3>5.0.3</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v5.0.5 (Resolves: <a
href="https://github.com/actions/cache/security/dependabot/33">https://github.com/actions/cache/security/dependabot/33</a>)</li>
<li>Bump <code>@actions/core</code> to v2.0.3</li>
</ul>
<h3>5.0.2</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v5.0.3 <a
href="https://redirect.github.com/actions/cache/pull/1692">#1692</a></li>
</ul>
<h3>5.0.1</h3>
<ul>
<li>Update <code>@azure/storage-blob</code> to <code>^12.29.1</code> via
<code>@actions/cache@5.0.1</code> <a
href="https://redirect.github.com/actions/cache/pull/1685">#1685</a></li>
</ul>
<h3>5.0.0</h3>
<blockquote>
<p>[!IMPORTANT]
<code>actions/cache@v5</code> runs on the Node.js 24 runtime and
requires a minimum Actions Runner version of <code>2.327.1</code>.
If you are using self-hosted runners, ensure they are updated before
upgrading.</p>
</blockquote>
<h3>4.3.0</h3>
<ul>
<li>Bump <code>@actions/cache</code> to <a
href="https://redirect.github.com/actions/toolkit/pull/2132">v4.1.0</a></li>
</ul>
<h3>4.2.4</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.5</li>
</ul>
<h3>4.2.3</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.3 (obfuscates SAS token in
debug logs for cache entries)</li>
</ul>
<h3>4.2.2</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.2</li>
</ul>
<h3>4.2.1</h3>
<ul>
<li>Bump <code>@actions/cache</code> to v4.0.1</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="2c8a9bd745"><code>2c8a9bd</code></a>
Merge pull request <a
href="https://redirect.github.com/actions/cache/issues/1760">#1760</a>
from actions/samirat/esm_migration_and_package_update</li>
<li><a
href="e9b91fdc3f"><code>e9b91fd</code></a>
Prettier fixes</li>
<li><a
href="e4884b8ff7"><code>e4884b8</code></a>
Rebuild dist</li>
<li><a
href="10baf0191a"><code>10baf01</code></a>
Fixed licenses</li>
<li><a
href="e39b386c90"><code>e39b386</code></a>
Fix test mock return order</li>
<li><a
href="b692820337"><code>b692820</code></a>
PR feedback</li>
<li><a
href="60749128a4"><code>6074912</code></a>
Rebuild dist bundles as ESM to match type:module</li>
<li><a
href="5a912e8b4a"><code>5a912e8</code></a>
Fix lint and jest issues</li>
<li><a
href="b9bf592b98"><code>b9bf592</code></a>
Update documentation for v6 release</li>
<li><a
href="80f777761d"><code>80f7777</code></a>
Update packages, migrate to ESM</li>
<li>See full diff in <a
href="https://github.com/actions/cache/compare/v5.0.5...v6.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/cache&package-manager=github_actions&previous-version=5.0.5&new-version=6.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 18:03:13 -06:00
Margus Veanes
77bce353db Merge remote-tracking branch 'origin/derive-with-ranges' into derive-with-ranges 2026-06-26 00:36:18 +03:00
Margus Veanes
149549b946 wip(seq): clean_leaf cofactors, mk_union_core simplifications, re_is_empty antimirov emptiness check
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 00:36:14 +03:00
Nikolaj Bjorner
618a30f5d8 differentiate A and B derivatives
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-25 14:27:41 -07:00
Lev Nachmanson
57fb719007
lp: gate Gomory-with-dio on genuine dio failures; separate config from runtime state (#9958)
## Summary

Improves the Diophantine (`dio`) integer-feasibility controller in
`int_solver`, and fixes a latent bug where the user's Gomory-cut
configuration could be silently overridden at runtime. Also includes the
earlier `lia_w` work: randomized hammer gates, the `int_hammer_period` /
`random_hammers` parameters, and the linear `dio_calls_period` recovery.

## Motivation

The controller used a **single field** both as the static
`lp.dio_cuts_enable_gomory` parameter and as the live "is Gomory
running" flag. It started running Gomory (and the gcd test) once
`dio_calls_period` crossed a hard-coded `16`. Because `dio_calls_period`
is also driven by the randomized hammer gate, on instances where `dio`
is only intermittently productive the period could be ratcheted past 16
*by chance*, turning on Gomory + gcd and thrashing — e.g. `dillig/20-14`
went from a 100s solve (deterministic) to a 600s timeout (randomized)
purely from this spurious activation.

## Changes

- **Separate config from runtime state.** Split the shared field into
`m_dio_cuts_enable_gomory` (static config, never mutated) and
`m_run_gomory_with_dio` (runtime flag). Toggling the runtime state can
no longer clobber the user's `dio_cuts_enable_gomory` parameter.
- **Trigger on genuine dio failures, not the period proxy.** Running
Gomory-with-dio now starts after a count of **consecutive `undef` dio
returns** (reset on a dio conflict) rather than when the
randomization-inflated period crosses a threshold — robust to
`random_hammers` gate variance.
- **Parameterize the threshold.** New `lp.dio_gomory_enable_period`
(default 16). Set it very large to never auto-start Gomory, so Gomory
follows `dio_cuts_enable_gomory` only.
- **Try `dio` before Gomory** in `check()` so a productive dio conflict
preempts Gomory on dio-dominated instances.

## Evaluation (QF_LIA, full set, 600s, seed 555 paired)

- Dio-before-Gomory: **+33** problems across the 6 `random_hammers x
int_hammer_period` cells (5/6 cells improve).
- New trigger (`dio_gomory_enable_period=32`, random): **6417** vs the
period-16 baseline **6409**; no short-cutoff regression.
- Linear `dio_calls_period` recovery: keeping it on is worth ~+20 vs
off; `decrease=1` slightly ahead of the default 2.

Default behavior (`dio_gomory_enable_period=16`) is byte-for-byte
equivalent to the previous threshold logic.

## Notes

Debug-only tracing used during analysis (the `dio_calls_period_trace`
parameter plus per-hammer / period-evolution verbose output) is **not**
included.

---------

Signed-off-by: Lev Nachmanson <levnach@hotmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 14:21:44 -07:00
Margus Veanes
3d42d997c6 perf(seq): collapse adjacent full_seq factors in regex concatenation
mk_re_concat only merged Sigma* Sigma* when both stars were direct
siblings. Once a literal nests the concatenation, e.g. (Sigma* x Sigma*) ++ Sigma*,
the trailing duplicate Sigma* survived because the left operand is a concat
that ends with Sigma* rather than a bare full_seq. Redundant adjacent stars
then multiply derivative states during bisimulation.

Add two grouping-insensitive rules using the existing
starts_with_full_seq/ends_with_full_seq helpers:
  R ++ Sigma*  -> R   when R ends with Sigma*
  Sigma* ++ R  -> R   when R starts with Sigma*

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 22:56:51 +03:00
Margus Veanes
2ecdd15e0f fix(seq): reject non-character regexes in regex_to_range_predicate
Guard regex_to_range_predicate so it only collapses regexes whose
element sort is a character sort. Previously a regex over a non-char
sequence sort (e.g. (re (Seq Int))) could be silently mis-collapsed
into bogus [0, max_char] ranges. Add a negative unit test covering
re.empty/re.full_char over (Seq Int).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 22:42:26 +03:00
Margus Veanes
ee20c9963b feat(seq::bisim): hoist all ITEs to top in bisim derivative leaves
In the bisimulation equivalence path, derivative leaves are now
extracted via the new seq_rewriter::brz_derivative_cofactors
(derive::derivative_cofactors), which computes the symbolic
derivative and enumerates its reachable leaves in fully ITE-hoisted
normal form: every if-then-else over the input character (including
ones previously buried under a concat or union) is hoisted to the top
via decompose_ite, infeasible minterms are pruned, and unions are kept
intact as single states. Each leaf is therefore a ground regex free of
(:var 0), so its nullability is always decidable.

This replaces collect_leaves (which only split top-level ITEs and left
buried (:var 0) ITEs inside leaves), the root cause of bisim returning
l_undef and falling through to the slow theory solver.

Validation on the regex-equivalence corpus (1523 files, -T:5, 8 workers):
- vs master: total solved 1394 vs 1378 (+16), soft_timeouts 129 vs 145,
  0 soundness disagreements (was 18 -> 5 -> 0).
- vs derive: +242 solved (1394 vs 1152), 25.4% faster on commonly-solved
  files, fixes 18 soundness disagreements, only 6 regressions.
- corpus wall time halved (172s vs 332s/349s).
- All 91 unit tests pass, including seq_regex_bisim.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 08:40:32 +03:00
Margus Veanes
a2b73b0ee6 misc edits of work in progress 2026-06-25 07:54:00 +03:00
Margus Veanes
8fe488721b Stage 3: collapse boolean combinations of char-class regexes
Introduce src/ast/rewriter/regex_range_collapse.{h,cpp}, a translator
between the boolean-combination-of-character-class fragment of regexes
and the range_predicate value type added in Stage 2.

Recognized fragment (translates to range_predicate):
  re.empty, re.full_char, re.range, re.union, re.intersection, re.diff
of operands recursively in the fragment.  Range bounds are accepted in
three encodings: string constant ("a"), seq.unit of a const char
(seq.unit (Char 97)), and length-1 zstring literal.

NOT translated:
  re.complement -- this is sequence-level complement (Sigma* \ L), not
  character-class complement.  Translating it would incorrectly turn
  re.comp(re.range "a" "z") into the character class [^a-z], which would
  drop the empty string and all length>=2 strings.

Hook the translator into seq_rewriter at mk_re_union0, mk_re_union,
mk_re_inter0, mk_re_inter, and mk_re_diff so that boolean combinations
of character classes always reduce to a single canonical range-set
form.  mk_re_complement is intentionally not hooked.

Materialization uses the canonical (seq.unit (Char N)) bound form
(matching the rest of seq_rewriter) and right-associates the union
with operands sorted by expr_id so the result matches the invariant
expected by merge_regex_sets.

Unit tests in src/test/regex_range_collapse.cpp cover the recognized
fragment, the non-translatable cases, and round-trip identity for
multi-range predicates.

Corpus validation on bench/inputs/regex-equivalence (1523 .smt2):
- 0 soundness regressions vs derive baseline.
- Resolves 4 previously-soft-timeout files (now solved correctly).
- Resolves 1 pre-existing wrong answer (mut_0404: master/derive say
  unsat, ground-truth annotation and Stage 3 say sat).
- Wall-time: -2.2% vs Stage-3 starting point, -1.5% vs derive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 07:51:09 +03:00
Margus Veanes
764a689092 Add seq::range_predicate value type with unit tests
Introduce a specialized range-algebra over the unsigned character

domain [0, max_char], with canonical sorted-disjoint-non-adjacent

representation and linear-time union, intersection, complement,

difference, and symmetric difference operations.

This is Stage 1 of the derive-with-ranges plan: the value type only,

with unit tests covering factories, ordering, hashing, hand-picked

instances, and exhaustive de-Morgan / lattice laws over a small

domain (verified by enumerating all 64 subsets).

Integration with seq::derive's path conditions, the OneStep cache,

and the R&psi smart-constructor rewrite are deferred to later stages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-25 07:50:35 +03:00
Nikolaj Bjorner
6fd303c4b9 set the auf flag to false in all cases
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-24 20:38:15 -07:00
Nikolaj Bjorner
9a5089397d better cofactoring 2026-06-24 15:55:30 -07:00
Nikolaj Bjorner
6cc995afef update aw 2026-06-24 14:08:45 -07:00
Lev Nachmanson
d1170d19bd
[snapshot-regression-fix] Bound ho_var term enumeration to fix MBQI timeout regression (iss-5753/small-3.smt2) (#9939)
## Summary

Fixes a Z3 snapshot-regression divergence tracked in Z3Prover/bench
discussion
https://github.com/Z3Prover/bench/discussions/2662.

- **Benchmark:** `iss-5753/small-3.smt2` (Z3Prover/bench corpus,
original issue z3#5753)
- **Regression:** recorded oracle `unsat` (captured 2026-06-05) →
current `master` produces `timeout` at `-T:20`
- **bench workflow run:**
https://github.com/Z3Prover/bench/actions/runs/28078176175

### Divergence

```diff
--- small-3.expected.out (expected)
+++ produced (current z3)
@@ -1 +1 @@
-unsat
+timeout
```

The answer `unsat` is still correct — z3 simply can no longer prove it
within the
budget. This is a **performance regression**, not a wrong/unsound
result.

## Root cause

The benchmark is a quantified Array/Real/Int problem solved via MBQI.
The model
finder's quantifier analyzer (`src/smt/smt_model_finder.cpp`) now
creates a
`ho_var` qinfo for **array-sorted quantified variables used as terms**
(the new
higher-order term-enumeration path introduced in #9908, "Term
enumeration",
commit `5699142f5`). Before that commit such an occurrence set
`m_info->m_is_auf = false` (a safe fallback that left no extra
instantiation
hints), which is the behaviour that was active when the `unsat` oracle
was
captured.

`ho_var::populate_inst_sets` builds instantiation candidates with:

```cpp
for (auto t : tn.enum_terms(srt)) {
    ...
    S->insert(t, generation);
}
```

`term_enumeration::enum_terms` is a **lazy, increasing-cost generator
with no
inherent bound** (see `ast/rewriter/term_enumeration.h`). For this
benchmark the
relevant array sort `(Array Int (Array Int Real))` has many array-valued
uninterpreted productions (`tptpummul`, `trans`, `inv`, ...), so
iterating the
generator to exhaustion inserts an explosively large number of terms
into the
instantiation set, blowing up MBQI instantiation and exhausting the 20s
budget.

Confirmed empirically: running the benchmark with `-v:3` shows
`ho_var::populate_inst_sets: 622 (Array Int (Array Int Real))`, i.e.
this exact
path fires for the divergent benchmark.

Notably, the original commit `5699142f5` declared `unsigned max_count =
20;` for
this very loop but **never applied it**, and that dead variable was
subsequently
removed — leaving the enumeration completely unbounded.

## Fix

Restore the intended bound so at most 20 of the cheapest enumerated
terms are
added to the instantiation set:

```cpp
unsigned max_count = 20;
for (auto t : tn.enum_terms(srt)) {
    if (max_count == 0)
        break;
    --max_count;
    ...
    S->insert(t, generation);
}
```

This is sound (instantiation only ever adds valid ground instances of
the
quantified formula) and is strictly more information than the pre-#9908
baseline
(which added no `ho_var` terms at all), so it does not regress problems
the
feature was meant to help. The change is confined to a single loop in
`src/smt/smt_model_finder.cpp`.

## Validation

Built the checkout in Release mode and re-ran the failing benchmark with
the
same options the snapshot harness uses:

```
$ build/z3 -T:20 inputs/issues/iss-5753/small-3.smt2
unsat        # was: timeout

real    0m0.120s
```

The combined output now matches the recorded `small-3.expected.out`
oracle
**byte-for-byte** (`unsat\n`). A smoke run over a sample of other corpus
benchmarks showed no crashes or new divergences, and a basic
`(check-sat)/(get-model)` sanity check still works.

---

This PR is opened as a **draft** for human review by the automated
snapshot-regression fixer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>




> Generated by [Fix a Z3 snapshot-regression
divergence](https://github.com/Z3Prover/bench/actions/runs/28078343154)
· 2.8K AIC · ⌖ 150.7 AIC · ⊞ 39K ·
[◷](https://github.com/search?q=repo%3AZ3Prover%2Fz3+%22gh-aw-workflow-id%3A+snapshot-regression-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Fix a Z3 snapshot-regression divergence,
engine: copilot, version: 1.0.60, model: claude-opus-4.8, id:
28078343154, workflow_id: snapshot-regression-fixer, run:
https://github.com/Z3Prover/bench/actions/runs/28078343154 -->

<!-- gh-aw-workflow-id: snapshot-regression-fixer -->
<!-- gh-aw-workflow-call-id: Z3Prover/bench/snapshot-regression-fixer
-->

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 11:58:32 -06:00
Copilot
500ca46434
Refresh README build badges to active GitHub Actions workflows (#9936)
README build ribbons had drifted from current workflow reality (e.g.,
removed `pyodide.yml`, disabled workflows still shown). This updates the
badge matrix to reflect active workflows only.

- **Scheduled workflows**
- Replaced deprecated `pyodide.yml` badge with active `pyodide-pypi.yml`
(`Pyodide Wheel (PyPI)`).

- **Disabled workflow cleanup**
- Removed badges for disabled workflows, including `coverage.yml` and
disabled agentic workflows (`a3-python`, `build-warning-fixer`,
`code-conventions-analyzer`, `csa-analysis`, `ostrich-benchmark`,
`tactic-to-simplifier`, `zipt-code-reviewer`).

- **Agentic workflows alignment**
- Kept active lock workflows and refreshed the section to include
currently active entries such as `specbot-crash-analyzer`,
`smtlib-benchmark-finder`, and `tptp-benchmark`.

```md
- | [![Pyodide Build](.../pyodide.yml/badge.svg)](.../pyodide.yml) |
+ | [![Pyodide Wheel (PyPI)](.../pyodide-pypi.yml/badge.svg)](.../pyodide-pypi.yml) |
```

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-23 19:58:43 -06:00
davedets
0adbcaf0d5
Fix clang warnings about casting away const. (#9933)
This is another PR towards the goal of getting Z3 to compile cleanly
when included via FetchContents into clang-tidy, which uses a pretty
strict set of warnings.

This PR adds
```
"-Wcast-qual"
```
to the set of warnings enabled in the build.  This gives warnings like:
```
/Users/daviddetlefs/z3/src/ast/ast.cpp:2897:38: warning: cast from 'app *const *' to 'expr **' drops const qualifier [-Wcast-qual]
```
I fixed these by inserting consts. In some cases, a "const_cast<T>(...)"
was necessary.
2026-06-23 19:57:46 -06:00
Copilot
cd2915ff3c
Remove in-workflow Pyodide builds from nightly and release pipelines (#9935)
Nightly and release workflows were still building Pyodide wheels inline,
despite Pyodide now being handled by a dedicated workflow. This change
removes duplicate Pyodide build work from those pipelines and aligns
artifact assembly with the new split.

- **Workflow graph cleanup**
  - Removed the `pyodide-python` job from:
    - `.github/workflows/nightly.yml`
    - `.github/workflows/release.yml`

- **Packaging dependency updates**
- Updated `python-package.needs` in both workflows to drop
`pyodide-python`, so packaging no longer waits on an in-workflow Pyodide
build.

- **Artifact collection updates**
- Removed `Download Pyodide Build` steps from both `python-package`
jobs, eliminating references to `PyodidePythonBuild`.

```yaml
python-package:
  needs:
    - mac-build-x64
    - mac-build-arm64
    - windows-build-x64
    - windows-build-x86
    - windows-build-arm64
    - manylinux-python-amd64
    - manylinux-python-arm64
    - manylinux-python-riscv64
```

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-23 19:56:19 -06:00
Nikolaj Bjorner
f86a37fdbf remove pyodide build
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-23 17:29:54 -07:00
Copilot
2081918cea
cmake: skip std::atomic link check for Emscripten and single-threaded builds (#9932)
The "Python bindings (Pyodide)" CI job fails at CMake configure time
because Emscripten's cross-compiler cannot pass the `std::atomic` link
tests in `check_link_atomic.cmake`, resulting in a fatal error even
though Pyodide builds are single-threaded and never need `libatomic`.

## Change

- **`cmake/check_link_atomic.cmake`**: guard the entire atomic check
behind `if (NOT (EMSCRIPTEN OR Z3_SINGLE_THREADED))`. Emscripten sets
`EMSCRIPTEN` automatically via `emcmake`; Pyodide builds also pass
`-DZ3_SINGLE_THREADED=TRUE`, so either condition is sufficient to bypass
the check safely.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-23 14:05:02 -06:00
Nikolaj Bjorner
d77fe0b0cd enable distribution of union over intersection
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-23 12:56:23 -07:00
Nikolaj Bjorner
cb3fe3167f rewrite replace_all constraints
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-23 10:45:28 -07:00
Nikolaj Bjorner
f8bf10af4f remove NOT_IMPLEMENTED_YET
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-22 19:00:04 -07:00
Nikolaj Bjorner
cb3d058067 fix build warnings 2026-06-22 18:20:23 -07:00
davedets
86737e11ea
Fix missing field initializers (better). (#9923)
This is another PR towards the goal of getting Z3 to compile cleanly
when included via FetchContents into clang-tidy, which uses a pretty
strict set of warnings.

This one fixes warnings about "missing field initializers" -- struct
initializers that leave some field uninitialized.

In https://github.com/Z3Prover/z3/pull/9904, I tried to do this in the
code. @nunoplopes pointed out flaws with this approach. He outlined a
more ambitious approach to fix the actual problem (use of an "entry"
type when just a "key" type should be sufficient in some places). For
now, though, I think it's doesn't lose anything to just disable the
warning.
2026-06-22 18:48:07 -06:00
Lev Nachmanson
5bba757131
[fixer-selftest] Fix comment typo in elim_unconstrained.cpp (accomodate → accommodate) (#9925)
This is an **automated workflow self-test** of the `fixer-selftest` /
`snapshot-regression-fixer` pipeline running on the self-hosted
**`rise-runner-1`** runner pool. Its sole purpose is to prove the
end-to-end agentic pipeline works: Copilot inference runs, and the
`create-pull-request` safe output can open a real **draft** pull request
on `Z3Prover/z3` using the configured PAT. It is intentionally
build-free.

### Change
- **File:** `src/ast/simplifiers/elim_unconstrained.cpp` (module-header
`/*++ ... */` comment)
- **Before:** `   - it does not accomodate side constraints.`
- **After:** `   - it does not accommodate side constraints.`

A plain spelling correction in a comment: `accomodate` → `accommodate`.
The diff is a single line.

### Why this is safe
The change is **comment-only** — it touches no code, identifiers, string
literals, build files, or tests, and therefore **cannot affect z3's
behaviour or output**. Because of that, no compilation or testing was
performed and **no rebuild is needed** to be confident it is correct.

### For maintainers
This is a genuine, correct fix, so you are welcome to **merge** it.
Equally, you may simply **close** it — the success of this self-test
does not depend on the PR being merged.




> Generated by [Self-test the agentic PR pipeline with a tiny z3 comment
fix](https://github.com/Z3Prover/bench/actions/runs/27987685681) · 299.1
AIC · ⌖ 53.6 AIC · ⊞ 35.5K ·
[◷](https://github.com/search?q=repo%3AZ3Prover%2Fz3+%22gh-aw-workflow-id%3A+fixer-selftest%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Self-test the agentic PR pipeline with a
tiny z3 comment fix, engine: copilot, version: 1.0.60, model:
claude-opus-4.8, id: 27987685681, workflow_id: fixer-selftest, run:
https://github.com/Z3Prover/bench/actions/runs/27987685681 -->

<!-- gh-aw-workflow-id: fixer-selftest -->
<!-- gh-aw-workflow-call-id: Z3Prover/bench/fixer-selftest -->

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-22 15:51:26 -07:00
Lev Nachmanson
2067a227ef
[fixer-selftest] Fix comment typo in solve_eqs.cpp: "reprsents" → "represents" (#9924)
## Automated workflow self-test

This is an **automated workflow self-test** of the `fixer-selftest` /
`snapshot-regression-fixer` pipeline running on the self-hosted
**`rise-runner-1`** pool. Its sole purpose is to verify the end-to-end
agentic pipeline: that Copilot inference runs and that the
`create-pull-request` safe output can open a real **draft** PR on
`Z3Prover/z3` using the configured PAT.

## The fix

A single, objectively-correct spelling fix in a **code comment** (the
header comment block of the file — not code, identifiers, or string
literals).

- **File:** `src/ast/simplifiers/solve_eqs.cpp` (line 23, inside the
`/*++ ... --*/` header comment)
- **Before:** `... where bitset reprsents set of free variables.`
- **After:** `... where bitset represents set of free variables.`

The diff is a single line:

```diff
-1. maintain map FV: term -> bit-set where bitset reprsents set of free variables. Assume the number of variables is bounded.
+1. maintain map FV: term -> bit-set where bitset represents set of free variables. Assume the number of variables is bounded.
```

## Why this is safe

The change is **comment-only** and therefore **cannot affect z3's
behaviour or output** — so it needs **no rebuild and no testing** to be
confident it is correct. Nothing outside the comment text was touched
(no code, no string literals, no identifiers, no whitespace elsewhere).

## For maintainers

This is a genuine, correct fix, so feel free to **merge** it — but you
may also simply **close** it. The self-test's success does not depend on
this PR being merged; it only depends on the PR having been opened.




> Generated by [Self-test the agentic PR pipeline with a tiny z3 comment
fix](https://github.com/Z3Prover/bench/actions/runs/27986232656) · 208.5
AIC · ⌖ 75.1 AIC · ⊞ 35.5K ·
[◷](https://github.com/search?q=repo%3AZ3Prover%2Fz3+%22gh-aw-workflow-id%3A+fixer-selftest%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Self-test the agentic PR pipeline with a
tiny z3 comment fix, engine: copilot, version: 1.0.60, model:
claude-opus-4.8, id: 27986232656, workflow_id: fixer-selftest, run:
https://github.com/Z3Prover/bench/actions/runs/27986232656 -->

<!-- gh-aw-workflow-id: fixer-selftest -->
<!-- gh-aw-workflow-call-id: Z3Prover/bench/fixer-selftest -->

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-22 15:04:32 -07:00
Nikolaj Bjorner
bcdb43451d fix range rewrite again, again
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-22 11:02:18 -07:00
Nikolaj Bjorner
ba1a05ec76 updates
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-22 09:12:38 -07:00
Nikolaj Bjorner
f487af3071 use empty sequence regex instead of empty set regex
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2026-06-21 16:26:05 -07:00
Copilot
c0ec4259e5
Regenerate issue-backlog workflow lock to remove stale gh-aw helper call (#9917)
The `Issue Backlog Processor` workflow’s `agent` job was failing at
runtime because the generated lock workflow referenced a non-existent
gh-aw helper (`merge_awf_model_multipliers.cjs`). This PR updates the
lock file to align with current gh-aw output and eliminate that stale
invocation.

- **Root cause addressed**
- `issue-backlog-processor.lock.yml` contained an outdated step that
executed:
- `node "${RUNNER_TEMP}/gh-aw/actions/merge_awf_model_multipliers.cjs"`
- That module is not present in the runner-provisioned gh-aw action set,
causing `MODULE_NOT_FOUND` and failing the `agent` job.

- **Change made**
- Recompiled the source workflow (`issue-backlog-processor.md`) and
committed the regenerated lock file:
    - `.github/workflows/issue-backlog-processor.lock.yml`
- The regenerated lock no longer includes the stale
`merge_awf_model_multipliers.cjs` call and updates associated gh-aw
metadata/version pins accordingly.

- **Illustrative diff (relevant line)**
```yaml
- GH_AW_MODEL_MULTIPLIERS_PATH="/tmp/gh-aw/model_multipliers.json" node "${RUNNER_TEMP}/gh-aw/actions/merge_awf_model_multipliers.cjs"
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-20 18:59:48 -06:00