* Initial plan
* simplify extract_var_bound in qe_lite_tactic.cpp via operator normalization
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
* Add defensive check for integer type in lhs
Added a defensive check for integer type in lhs before proceeding with inequality checks.
* Update qe_lite_tactic.cpp
* Fix utility function call for integer check
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
Co-authored-by: Nikolaj Bjorner <nbjorner@microsoft.com>
Replace hardcoded -j3 with -j$(nproc) in ci.yml, nightly.yml, and
release.yml to utilize all available cores on GitHub Actions runners.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
update_lower_lex updates m_lower for subsequent objectives with saved
values from the current model. Reset m_lower[i] and m_upper[i] to
their initial values before optimizing each objective so earlier
objectives do not contaminate later ones.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Z3_ast_to_string returns a pointer to an internal buffer that is
overwritten on the next call. Store results in std::string immediately
to avoid reading a stale, garbled buffer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
geometric_lex's update_lower_lex updates m_lower for all subsequent
objectives with saved values from the current model. In box mode this
contaminates later objectives' starting bounds, causing platform-dependent
results. Save and restore m_lower/m_upper across iterations so each
objective starts from a clean state.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In box mode (opt.priority=box), each objective should be optimized
independently. Previously, box() called geometric_opt() which optimizes
all objectives together using a shared disjunction of bounds. This caused
adding/removing an objective to change the optimal values of other
objectives.
Fix: Rewrite box() to optimize each objective in its own push/pop scope
using geometric_lex, ensuring complete isolation between objectives.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
resultant vanishes during a nested isolate_roots call. The mathematical
invariant that the resultant cannot vanish again after recovery does not
hold in all cases, e.g. with certain nonlinear real arithmetic formulas.
The algebraic_exception propagates cleanly through the nlsat solver and
tactic layers which already catch z3_exception.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After qe-light's equation solver (eq_der) eliminates variables from
linear equations, remaining bounded integer quantifiers may still have
non-unit coefficients that prevent Fourier-Motzkin elimination.
Add a bounded quantifier expansion step: when the remaining quantified
integer variables all have explicit finite bounds and the product of
domain sizes is <= 10000, expand the quantifier into a finite
disjunction. This turns e.g. exists y0 in [0,10), y1 in [0,15): P(x,y0,y1)
into P(x,0,0) | P(x,0,1) | ... | P(x,9,14), which is 150 disjuncts.
The SMT solver handles the resulting quantifier-free formula instantly,
whereas the previous QSAT/MBP approach timed out due to weak integer
projections from the (|a|-1)*(|b|-1) slack in Fourier-Motzkin resolution
with non-unit coefficients.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Always print each test's captured output, not just for failures.
This preserves backward compatibility:
- PASS appears on its own line per test, as before
- ASAN/UBSAN reports from any test appear in captured logs
- timeit output is preserved for all tests
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Parallel mode (/j) is now the default. Use /seq to force serial execution.
Child processes are invoked with /seq to prevent recursive parallelism.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Refactor src/test/main.cpp to support parallel test execution:
- Add /j[:N] flag to run tests in parallel using N jobs (default: number of cores)
- Use process-based parallelism: each test runs as a child process,
avoiding thread-safety issues with global state like enable_debug/enable_trace
- Output is captured per-test and printed atomically, so different tests never mix
- Provide summary with pass/fail counts, wall time, and failed test names
- Refactor test list into X-macros for single source of truth
- Fix pre-existing bug where serial /a mode ran each test argc times
Platform support:
- Unix (Linux/macOS/FreeBSD): popen/pclose with WEXITSTATUS
- Windows: _popen/_pclose
- Emscripten: parallel disabled (no threading support)
- Works with both SINGLE_THREAD and multi-threaded builds
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
m_fixed.insert(e) was placed before the check_long_strings guard,
causing check_fixed_length(false, false) to mark variables with
len > 20 as processed without actually decomposing them. The
subsequent check_fixed_length(false, true) then skipped them.
Move the insertion after the guard so variables are only marked
as fixed once they are actually decomposed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The add_axiom optimization that skips adding clauses when a literal is
already true was unsound: the satisfying literal could be retracted by
backtracking, leaving the axiom clause missing. This caused the solver
to miss propagations, e.g., not propagating indexof(a,s) = -1 when
contains(a,s) becomes false after backtracking.
Fix: only skip the clause if the satisfying literal is assigned at
base level (scope 0), where it can never be retracted.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In multi-threaded solving, IF_VERBOSE(0, ...) in found_non_diff_logic_expr
was always acquiring the global g_verbose_mux mutex (since verbosity >= 0 is
always true) while holding it for potentially expensive mk_pp() calls. This
caused catastrophic lock contention when multiple threads internalized atoms.
Change IF_VERBOSE(0, ...) to IF_VERBOSE(2, ...) in both theory_diff_logic_def.h
and theory_dense_diff_logic_def.h. The diagnostic message is still available at
verbosity level 2 (-v:2), but is no longer printed (or locked) at the default
verbosity level, eliminating the contention.
Co-authored-by: levnach <5377127+levnach@users.noreply.github.com>
Test tst_box_mod_opt verifies that maximize (mod (- (* 232 a)) 256)
returns 248 when using box priority with multiple objectives.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Push/pop isolation in maximize_objectives1 (added for #7677) can corrupt
LP column values between objectives. For non-linear objectives like mod,
the LP maximize call may return stale values after a preceding
objective's push/pop cycle.
Fix: save the baseline model before the push/pop loop and use it as a
floor for each objective's value. Extract two helpers:
- maximize_objective_isolated: push/pop + save/restore per objective
- update_from_baseline_model: adopt baseline model value when it is better
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the LP optimizer returns the same blocker expression in successive
iterations of geometric_lex (e.g., due to nonlinear constraints like
mod/to_int preventing the LP from exploring the full feasible region),
the loop now falls back to using the model-based lower bound to push
harder instead of breaking immediately.
This fixes the case where minimize(3*a) incorrectly returned -162
while minimize(a) correctly returned -infinity with the same constraints.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the LP optimizer returns the same blocker expression in successive
iterations of geometric_lex (e.g., due to nonlinear constraints like
mod/to_int preventing the LP from exploring the full feasible region),
the loop now falls back to using the model-based lower bound to push
harder instead of breaking immediately.
This fixes the case where minimize(3*a) incorrectly returned -162
while minimize(a) correctly returned -infinity with the same constraints.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New methods:
- Expr.getNumeralDouble(): retrieve any numeral as a double
- IntNum.getUint(): extract numeral as unsigned 32-bit value
- IntNum.getUint64(): extract numeral as unsigned 64-bit value
- RatNum.getSmall(): numerator/denominator as int64 pair
- RatNum.getRationalInt64(): numerator/denominator (returns null on overflow)
Each is a thin wrapper around the existing Native binding.
Added examples to JavaExample.java covering all new methods.
gh CLI is not available inside AWF so the agent could not download
artifacts. Switch to GitHub MCP actions toolset for artifact URLs
and add helper scripts for download and parsing.