Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

757 lines
22 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import gc
import queue
import threading
import pytest
import tvm
import tvm.testing
from tvm import tirx
from tvm.arith import Analyzer, ProofStrength
# The Z3 prover is only consulted at the kSymbolicBound strength so the common
# default path never pays the prover cost.
SB = ProofStrength.SYMBOLIC_BOUND
def _require_z3(analyzer):
if not analyzer.is_z3_enabled:
pytest.skip("Z3 prover is disabled in this build")
def implies(x, y):
return tirx.Or(tirx.Not(x), y)
# ---------------------------------------------------------------------------
# API availability (works regardless of whether Z3 is built)
# ---------------------------------------------------------------------------
def test_z3_capability_query():
# `is_z3_enabled` is the supported way to detect the build configuration.
# The Z3-specific debug/config methods work only when it is True, and raise
# a clear error otherwise.
analyzer = Analyzer()
assert isinstance(analyzer.is_z3_enabled, bool)
if analyzer.is_z3_enabled:
assert isinstance(analyzer.get_smtlib2(), str)
assert isinstance(analyzer.get_z3_stats(), str)
else:
with pytest.raises(RuntimeError):
analyzer.get_smtlib2()
with pytest.raises(RuntimeError):
analyzer.get_z3_stats()
with pytest.raises(RuntimeError):
analyzer.set_z3_timeout_ms(1000)
with pytest.raises(RuntimeError):
analyzer.set_z3_rlimit(0)
def test_z3_context_lifetime_outlives_worker_thread():
_require_z3(Analyzer())
result_queue = queue.Queue()
def worker():
try:
analyzer = Analyzer()
x = tirx.Var("x", "int32")
analyzer.bind(x, tvm.ir.Range(0, 16))
assert analyzer.can_prove(x >= 0, SB)
result_queue.put(("analyzer", analyzer))
except BaseException as err: # pylint: disable=broad-exception-caught
result_queue.put(("error", err))
thread = threading.Thread(target=worker)
thread.start()
thread.join()
kind, payload = result_queue.get_nowait()
if kind == "error":
raise payload
del payload
gc.collect()
# ---------------------------------------------------------------------------
# Examples the native analyzer cannot prove but Z3 can.
#
# Each case asserts both that the native analyzers (kDefault, Z3 gated off)
# fail and that Z3 (kSymbolicBound) succeeds. This demonstrates the added value
# of the Z3 backend and that it is correctly gated behind kSymbolicBound.
# ---------------------------------------------------------------------------
def test_z3_floor_division_identity_constraint():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
expr = ((b - a) // c) * c + a <= b
with analyzer.constraint_scope(tirx.all(a > 0, b > 0, c > 0)):
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_floor_division_identity_via_bind_range():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
analyzer.bind(a, tvm.ir.Range(1, 100000))
analyzer.bind(b, tvm.ir.Range(1, 100000))
analyzer.bind(c, tvm.ir.Range(1, 100000))
expr = ((b - a) // c) * c + a <= b
assert analyzer.can_prove(expr, SB)
def test_z3_multiplication_monotonicity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
d = tirx.Var("d", "int32")
expr = implies(tirx.all(a < b, b < c, a * d < b * d), b * d < c * d)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_nested_floor_division_collapse():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
expr = implies(
tirx.all(a >= 0, a < 128),
a // 128 == (a // 64 * 32 + a % 32 // 16 * 8) // 64,
)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_deeply_nested_floor_division_identity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
expr = implies(
tirx.all(a >= 0, a < 128),
(
a % 16 * 64
+ a // 64 * 32
+ a % 8 // 4 * 32
+ (a % 32 // 16 + a % 2) % 2 * 8
+ 16
- (a // 64 + a % 8 // 4) // 2 * 64
)
// 512
== (
a % 16 * 64
+ a // 64 * 32
+ a % 8 // 4 * 32
+ (a % 32 // 16 + a % 2) % 2 * 8
- (a // 64 + a % 8 // 4) // 2 * 64
)
// 512,
)
assert analyzer.can_prove(expr, SB)
def test_z3_min_max_sum_identity():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
expr = tirx.max(x, y) + tirx.min(x, y) == x + y
assert analyzer.can_prove(expr, SB)
def test_z3_select_absolute_value_nonneg():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
expr = tirx.Select(x >= 0, x, -x) >= 0
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_transitive_inequality():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
expr = implies(tirx.all(a <= b, b <= c), a <= c)
assert analyzer.can_prove(expr, SB)
def test_z3_square_expansion_nonneg():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = (a + b) * (a + b) >= a * a + b * b
with analyzer.constraint_scope(tirx.all(a >= 0, b >= 0)):
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_square_monotonicity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = implies(tirx.all(0 <= a, a <= b), a * a <= b * b)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_strict_multiplication():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
d = tirx.Var("d", "int32")
expr = implies(tirx.all(a < b, d > 0), a * d < b * d)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_floor_division_monotonicity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
expr = implies(tirx.all(a <= b, c > 0), tirx.floordiv(a, c) <= tirx.floordiv(b, c))
assert not analyzer.can_prove(expr)
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(expr, SB)
def test_z3_floor_division_lower_bound():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = implies(b > 0, tirx.floordiv(a, b) * b <= a)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_floor_modulo_range():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = implies(b > 0, tirx.all(0 <= tirx.floormod(a, b), tirx.floormod(a, b) < b))
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_flattened_index_bound():
# Classic index-flattening bound used throughout TVM: for a row index i in
# [0, m) and a column index j in [0, n), the flattened index i * n + j stays
# within [0, m * n).
analyzer = Analyzer()
_require_z3(analyzer)
i = tirx.Var("i", "int32")
j = tirx.Var("j", "int32")
m = tirx.Var("m", "int32")
n = tirx.Var("n", "int32")
expr = tirx.all(0 <= i * n + j, i * n + j < m * n)
with analyzer.constraint_scope(tirx.all(0 <= i, i < m, 0 <= j, j < n, m > 0, n > 0)):
assert not analyzer.can_prove(expr)
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(expr, SB)
def test_z3_modular_combination():
# Native modular_set tracks single-variable moduli, but combining two
# independent modular facts to reason about their sum is left to Z3.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
expr = tirx.floormod(x + y, 2) == 0
with analyzer.constraint_scope(tirx.all(tirx.floormod(x, 6) == 0, tirx.floormod(y, 6) == 0)):
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_square_non_negative():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
assert not analyzer.can_prove(a * a >= 0)
assert analyzer.can_prove(a * a >= 0, SB)
def test_z3_min_max_average_bounds():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
assert not analyzer.can_prove(tirx.max(a, b) * 2 >= a + b)
assert analyzer.can_prove(tirx.max(a, b) * 2 >= a + b, SB)
assert analyzer.can_prove(tirx.min(a, b) * 2 <= a + b, SB)
def test_z3_symbolic_bind_range_with_constraint():
# Combine a symbolic range binding (x in [0, n)) with a constraint on the
# extent to derive a concrete bound on x.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
n = tirx.Var("n", "int32")
analyzer.bind(x, tvm.ir.Range(0, n))
with analyzer.constraint_scope(n <= 8):
assert not analyzer.can_prove(x < 8)
assert analyzer.can_prove(x < 8, SB)
def test_z3_equality_congruence():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = implies(a == b, a * a == b * b)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_integer_strict_transitivity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
# Over the integers, a < b and b < c implies a + 1 < c.
expr = implies(tirx.all(a < b, b < c), a + 1 < c)
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_if_then_else_absolute_value():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
expr = tirx.if_then_else(x >= 0, x, -x) >= 0
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_unsigned_non_negative():
analyzer = Analyzer()
_require_z3(analyzer)
u = tirx.Var("u", "uint32")
assert not analyzer.can_prove(u >= 0)
assert analyzer.can_prove(u >= 0, SB)
def test_z3_unsigned64_non_negative():
# Exercises the special-cased uint64 range handling (UINT64_MAX bound).
analyzer = Analyzer()
_require_z3(analyzer)
u = tirx.Var("u", "uint64")
assert not analyzer.can_prove(u >= 0)
assert analyzer.can_prove(u >= 0, SB)
def test_z3_int64_square_expansion():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int64")
b = tirx.Var("b", "int64")
expr = (a + b) * (a + b) >= a * a + b * b
with analyzer.constraint_scope(tirx.all(a >= 0, b >= 0)):
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_boolean_variable_reasoning():
analyzer = Analyzer()
_require_z3(analyzer)
p = tirx.Var("p", "bool")
q = tirx.Var("q", "bool")
expr = implies(tirx.And(p, q), tirx.Or(p, q))
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_not_equal_from_strict_less():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
expr = implies(x < y, tirx.NE(x, y))
assert not analyzer.can_prove(expr)
assert analyzer.can_prove(expr, SB)
def test_z3_let_expression():
analyzer = Analyzer()
_require_z3(analyzer)
y = tirx.Var("y", "int32")
t = tirx.Var("t", "int32")
let = tirx.Let(t, y * 2, t)
assert not analyzer.can_prove(let == y * 2)
assert analyzer.can_prove(let == y * 2, SB)
def test_z3_cast_preserves_bounds():
analyzer = Analyzer()
_require_z3(analyzer)
s = tirx.Var("s", "int16")
widened = tirx.Cast("int32", s)
assert analyzer.can_prove(widened <= 32767, SB)
assert analyzer.can_prove(widened >= -32768, SB)
def test_z3_bitwise_and_mask_bound():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
assert analyzer.can_prove(tirx.bitwise_and(x, tirx.IntImm("int32", 7)) < 8, SB)
def test_z3_bitwise_and_le_operand():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
analyzer.bind(y, tvm.ir.Range(0, 256))
# Bit-vector reasoning over two variables exceeds the default deterministic
# rlimit; lift it (0 == unlimited, still deterministic) for this proof.
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(tirx.bitwise_and(x, y) <= x, SB)
def test_z3_bitwise_or_ge_operand():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
analyzer.bind(y, tvm.ir.Range(0, 256))
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(tirx.bitwise_or(x, y) >= x, SB)
def test_z3_bitwise_xor_bound():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
y = tirx.Var("y", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
analyzer.bind(y, tvm.ir.Range(0, 256))
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(tirx.bitwise_xor(x, y) < 256, SB)
def test_z3_bitwise_not_identity():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
analyzer.set_z3_rlimit(0)
# Two's complement: ~x == -x - 1.
assert analyzer.can_prove(tirx.bitwise_not(x) == -x - 1, SB)
def test_z3_shift_right_halves():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
analyzer.bind(x, tvm.ir.Range(0, 256))
analyzer.set_z3_rlimit(0)
# For non-negative x, (x >> 1) * 2 <= x.
assert analyzer.can_prove(tirx.shift_right(x, tirx.IntImm("int32", 1)) * 2 <= x, SB)
def test_z3_shift_left_lower_bound():
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
n = tirx.Var("n", "int32")
# Keep operands small so the 32-bit left shift cannot overflow; then
# x << n == x * 2 ** n >= x for x >= 1.
analyzer.bind(x, tvm.ir.Range(1, 16))
analyzer.bind(n, tvm.ir.Range(0, 4))
# Bit-vector shift reasoning exceeds the default deterministic rlimit.
analyzer.set_z3_rlimit(0)
assert analyzer.can_prove(tirx.shift_left(x, n) >= x, SB)
# ---------------------------------------------------------------------------
# Soundness / negative tests (Z3 must NOT prove false predicates)
# ---------------------------------------------------------------------------
def test_z3_negative_unprovable_inequality():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
# a < b does not hold for arbitrary a, b.
assert not analyzer.can_prove(a < b, SB)
# a * a > a is false (e.g. a == 0).
assert not analyzer.can_prove(a * a > a, SB)
def test_z3_truncmod_can_be_negative():
# Regression test for truncated div/mod semantics: TVM Div/Mod round toward
# zero, so truncmod(a, 4) can be negative. A solver that modeled them as
# Euclidean would unsoundly "prove" truncmod(a, 4) >= 0.
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
assert not analyzer.can_prove(tirx.truncmod(a, 4) >= 0, SB)
def test_z3_truncdiv_truncmod_identity():
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
expr = tirx.truncdiv(a, b) * b + tirx.truncmod(a, b) == a
with analyzer.constraint_scope(b != 0):
assert analyzer.can_prove(expr, SB)
def test_z3_floormod_nested_identities():
# Ported from TileLang's test_divmod. Here `%` is floormod: nested floormod
# by opposite-sign divisors collapses to the single-divisor result, while
# the mixed case does not.
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
assert not analyzer.can_prove(a % 2 % -2 - a % 2 == 0, SB)
assert analyzer.can_prove(a % -2 % 2 - a % 2 == 0, SB)
def test_z3_floormod_nonnegative():
# In contrast to truncmod, floormod with a positive divisor is always in
# [0, divisor), which Z3 should be able to prove.
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
assert analyzer.can_prove(tirx.floormod(a, 4) >= 0, SB)
assert analyzer.can_prove(tirx.floormod(a, 4) < 4, SB)
def test_z3_shift_does_not_poison_solver():
# Regression test: evaluating a shift expression must not add permanent
# assertions (such as `b >= 0` / `b < 64`) to the shared solver. Otherwise
# an unrelated, unbounded `b` would be wrongly provable to be < 100.
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
# Touch a shift expression so the prover visits the shift amount `b`.
analyzer.can_prove(tirx.shift_left(a, b) >= 0, SB)
# `b` is otherwise unconstrained, so this must remain unprovable.
assert not analyzer.can_prove(b < 100, SB)
assert not analyzer.can_prove(b >= 0, SB)
def test_z3_constraint_scope_is_popped():
# Constraints entered through a scope must be removed once the scope exits,
# i.e. EnterConstraint's solver.push()/pop() must be balanced.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
with analyzer.constraint_scope(x > 5):
assert analyzer.can_prove(x > 0, SB)
# The constraint is gone; x is unconstrained again.
assert not analyzer.can_prove(x > 0, SB)
def test_z3_opaque_call_is_safe():
# An opaque/unsupported sub-expression is modeled as a fresh free variable.
# It must neither crash nor be provable on its own, yet still be usable as a
# constraint.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
call = tirx.call_extern("int32", "foo", x)
assert not analyzer.can_prove(call > 0, SB)
with analyzer.constraint_scope(call > 0):
assert analyzer.can_prove(call > 0, SB)
assert not analyzer.can_prove(call > 0, SB)
def test_z3_shift_overflow_is_not_proven():
# Z3 models fixed-width shifts via bit-vectors, so it correctly refuses to
# prove `x << n >= x` for an unbounded `x` (a large `x` overflows int32 and
# wraps to a negative value). This guards against unsound shift modeling.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
n = tirx.Var("n", "int32")
analyzer.set_z3_rlimit(0)
expr = implies(tirx.all(x >= 1, n >= 0, n < 8), tirx.shift_left(x, n) >= x)
assert not analyzer.can_prove(expr, SB)
def test_z3_analyzers_are_isolated():
# Analyzers share a thread-local Z3 context but own separate solvers, so
# constraints and bindings in one must never leak into another.
analyzer_a = Analyzer()
analyzer_b = Analyzer()
_require_z3(analyzer_a)
x = tirx.Var("x", "int32")
with analyzer_a.constraint_scope(x > 100):
assert analyzer_a.can_prove(x > 50, SB)
assert not analyzer_b.can_prove(x > 50, SB)
analyzer_c = Analyzer()
analyzer_d = Analyzer()
analyzer_c.bind(x, tvm.ir.Range(0, 10))
assert analyzer_c.can_prove(x < 10, SB)
assert not analyzer_d.can_prove(x < 10, SB)
def test_z3_repeated_can_prove_is_consistent():
# Repeated queries must be stateless: a CanProve call must not pollute the
# solver and change the result of a subsequent call.
analyzer = Analyzer()
_require_z3(analyzer)
x = tirx.Var("x", "int32")
assert analyzer.can_prove(x > 0, SB) == analyzer.can_prove(x > 0, SB)
analyzer.bind(x, tvm.ir.Range(5, 10))
assert analyzer.can_prove(x >= 5, SB)
assert analyzer.can_prove(x >= 5, SB)
def test_z3_is_gated_behind_symbolic_bound():
# The Z3 fallback must not run at the default strength.
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
expr = ((b - a) // c) * c + a <= b
with analyzer.constraint_scope(tirx.all(a > 0, b > 0, c > 0)):
assert not analyzer.can_prove(expr, ProofStrength.DEFAULT)
assert analyzer.can_prove(expr, SB)
# ---------------------------------------------------------------------------
# SMT-LIB2 export
# ---------------------------------------------------------------------------
def test_z3_smtlib2_roundtrip():
z3 = pytest.importorskip("z3")
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
expr = ((b - a) // c) * c + a <= b
solver = z3.Solver()
with analyzer.constraint_scope(tirx.all(a > 0, b > 0, c > 0)):
solver.from_string(analyzer.get_smtlib2(expr))
assert solver.check() == z3.unsat
def test_z3_smtlib2_roundtrip_with_timeout():
z3 = pytest.importorskip("z3")
analyzer = Analyzer()
_require_z3(analyzer)
a = tirx.Var("a", "int32")
b = tirx.Var("b", "int32")
c = tirx.Var("c", "int32")
analyzer.set_z3_timeout_ms(1000)
expr = implies(tirx.all(a > 0, b > 0, c > 0), ((b - a) // c) * c + a <= b)
solver = z3.Solver()
solver.from_string(analyzer.get_smtlib2(expr))
assert solver.check() == z3.unsat
if __name__ == "__main__":
tvm.testing.main()