Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Z3Prover/z3/llms.txt

Use this file to discover all available pages before exploring further.

The array theory in Z3 provides reasoning about arrays with arbitrary index and element types. Arrays follow the theory of extensionality: two arrays are equal if they have the same values at all indices.

Array Sorts

Create an array sort with index and element types:
from z3 import *

# Array from integers to integers
A = Array('A', IntSort(), IntSort())

# Array from bit-vectors to booleans
B = Array('B', BitVecSort(32), BoolSort())

# Array from strings to reals
C = Array('C', StringSort(), RealSort())
C API: Z3_mk_array_sort(c, domain, range) (api_array.cpp:26)

Multi-Dimensional Arrays

Create arrays with multiple indices:
# 2D array: Array[Int, Int, Int]
Matrix = Array('Matrix', IntSort(), IntSort(), IntSort())

# 3D array
Tensor = Array('Tensor', IntSort(), IntSort(), IntSort(), RealSort())
C API: Z3_mk_array_sort_n(c, n, domain, range) (api_array.cpp:37)

Basic Operations

Select

Read a value from an array at a given index:
A = Array('A', IntSort(), IntSort())
x = Int('x')

# Read value at index 5
val = A[5]
# Or using Select explicitly
val = Select(A, 5)

solve(A[5] == 10, A[5] == x)
# [A = [5 -> 10, else -> 0], x = 10]
C API: Z3_mk_select(c, a, i) (api_array.cpp:51) Multi-index: Z3_mk_select_n(c, a, n, idxs) (api_array.cpp:76)

Store

Write a value to an array at a given index:
A = Array('A', IntSort(), IntSort())

# Store 42 at index 5
B = Store(A, 5, 42)
# Or using array notation
B = A[5] = 42  # Not valid Python syntax, use Store

solve(B[5] == 42, B[3] == A[3])
# Store preserves all other values
C API: Z3_mk_store(c, a, i, v) (api_array.cpp:106) Multi-index: Z3_mk_store_n(c, a, n, idxs, v) (api_array.cpp:134)

Array Properties

Extensionality

Arrays are equal if they have the same values at all indices:
A = Array('A', IntSort(), IntSort())
B = Array('B', IntSort(), IntSort())

solve(A == B, A[0] == 1, B[0] == 2)
# unsat - arrays differ at index 0

solve(A == B, ForAll([x], A[x] == B[x]))
# sat - arrays are equal

Default Values

Arrays can have default values for uninitialized indices:
# Create array with default value
A = K(IntSort(), 0)  # All elements are 0

solve(A[5] == 0, A[100] == 0)
# sat
C API: Z3_mk_const_array(c, domain, v) creates array where all elements equal v (api_array.cpp:191)

Array Default Accessor

Get the default value of an array: C API: Z3_mk_array_default(c, array) (api_array.cpp:210)

Advanced Operations

Map

Apply a function to every element of arrays:
A = Array('A', IntSort(), IntSort())
B = Array('B', IntSort(), IntSort())

# Map addition over two arrays
# C = λi. A[i] + B[i]
plus = Function('plus', IntSort(), IntSort(), IntSort())
C = Map(plus, A, B)
C API: Z3_mk_map(c, f, n, args) (api_array.cpp:166)

Array Extensionality

Find an index where two arrays differ:
A = Array('A', IntSort(), IntSort())
B = Array('B', IntSort(), IntSort())

s = Solver()
s.add(A != B)

if s.check() == sat:
    # Get index where arrays differ
    # Use Z3_mk_array_ext in C API
    pass
C API: Z3_mk_array_ext(c, arg1, arg2) returns an index where arrays differ if they’re not equal (api_array.cpp:270)

Set Operations

Z3 implements sets as arrays from elements to booleans:
# Set of integers
S = Set('S', IntSort())
T = Set('T', IntSort())

Empty and Full Sets

# Empty set
empty = EmptySet(IntSort())

# Full set (universal set)
full = FullSet(IntSort())
C API: Z3_mk_empty_set, Z3_mk_full_set (api_array.cpp:247, 256)

Set Membership

S = Set('S', IntSort())
x = Int('x')

solve(IsMember(x, S), x == 5)
# x is in set S
C API: Z3_mk_set_member(c, elem, set) (api_array.cpp:284)

Set Operations

S = Set('S', IntSort())
T = Set('T', IntSort())

# Union
U = Union(S, T)

# Intersection
I = Intersection(S, T)

# Difference
D = SetDifference(S, T)

# Complement
C = SetComplement(S)

# Subset
solve(IsSubset(S, T))
C API:
  • Z3_mk_set_union (api_array.cpp:265)
  • Z3_mk_set_intersect (api_array.cpp:266)
  • Z3_mk_set_difference (api_array.cpp:267)
  • Z3_mk_set_complement (api_array.cpp:268)
  • Z3_mk_set_subset (api_array.cpp:269)

Add/Remove Elements

S = Set('S', IntSort())

# Add element
S2 = SetAdd(S, 5)

# Remove element
S3 = SetDel(S, 5)
C API: Z3_mk_set_add, Z3_mk_set_del (api_array.cpp:288, 292)

Complete Examples

Example 1: Array Update Sequence

from z3 import *

A = Array('A', IntSort(), IntSort())

# Sequence of updates
B = Store(A, 1, 10)
C = Store(B, 2, 20)
D = Store(C, 3, 30)

s = Solver()
s.add(D[1] == 10)
s.add(D[2] == 20)
s.add(D[3] == 30)
s.add(D[4] == A[4])  # Index 4 unchanged

print(s.check())  # sat

Example 2: Array Equality

from z3 import *

A = Array('A', IntSort(), IntSort())
B = Array('B', IntSort(), IntSort())

# Two arrays are equal iff they agree at all indices
x = Int('x')

s = Solver()
s.add(ForAll([x], A[x] == B[x]))
s.add(A[0] == 5)

if s.check() == sat:
    m = s.model()
    # B[0] must also be 5
    print(m.evaluate(B[0]))  # 5

Example 3: Sparse Array Initialization

from z3 import *

# Create array with default value 0
A = K(IntSort(), 0)

# Set specific indices
A = Store(A, 10, 100)
A = Store(A, 20, 200)
A = Store(A, 30, 300)

solve(
    A[10] == 100,
    A[15] == 0,    # Default value
    A[20] == 200,
    A[99] == 0     # Default value
)

Example 4: Multi-Dimensional Array

from z3 import *

# 2D matrix: rows and columns
Matrix = Array('Matrix', IntSort(), IntSort(), IntSort())

s = Solver()

# Set Matrix[1][2] = 42
Matrix = Store(Matrix, 1, 2, 42)

# Set Matrix[3][4] = 99
Matrix = Store(Matrix, 3, 4, 99)

s.add(Select(Matrix, 1, 2) == 42)
s.add(Select(Matrix, 3, 4) == 99)

print(s.check())  # sat

Example 5: Set Operations

from z3 import *

S = Set('S', IntSort())
T = Set('T', IntSort())

x, y, z = Ints('x y z')

s = Solver()

# x is in S
s.add(IsMember(x, S))

# y is in T
s.add(IsMember(y, T))

# z is in both S and T (intersection)
s.add(IsMember(z, Intersection(S, T)))

s.add(x == 5)
s.add(y == 10)
s.add(z == 7)

if s.check() == sat:
    m = s.model()
    print(f"x={m[x]}, y={m[y]}, z={m[z]}")
    # S contains at least {5, 7}
    # T contains at least {7, 10}

Example 6: As-Array Construction

Create an array from a function:
from z3 import *

# Define a function
f = Function('f', IntSort(), IntSort())

# Create array from function
# A[i] = f(i) for all i
A = Lambda([x], f(x))

solve(A[5] == 10, f(5) == 10)
C API: Z3_mk_as_array(c, f) (api_array.cpp:272)

Query Functions

Get Array Sort Information

# Check number of indices
# Get domain and range sorts
C API:
  • Z3_get_array_arity(c, s) - number of indices (api_array.cpp:302)
  • Z3_get_array_sort_domain(c, t) - domain sort (api_array.cpp:315)
  • Z3_get_array_sort_domain_n(c, t, idx) - nth domain sort (api_array.cpp:329)
  • Z3_get_array_sort_range(c, t) - range sort (api_array.cpp:343)

Theory Axioms

The array theory is defined by two key axioms:
  1. Read-over-Write (same index): Select(Store(A, i, v), i) = v
  2. Read-over-Write (different index): i ≠ j ⟹ Select(Store(A, i, v), j) = Select(A, j)
  3. Extensionality: (∀i. Select(A, i) = Select(B, i)) ⟹ A = B

Implementation Notes

  • Arrays are functional and immutable (Store creates a new array)
  • The theory is decidable for quantifier-free formulas
  • Array values are represented symbolically (not stored explicitly)
  • Efficient for sparse updates and reads
  • No built-in bounds checking (arrays are infinite)

References

  • Implementation: src/api/api_array.cpp
  • Theory plugin: src/ast/array_decl_plugin.h
  • Solver: src/smt/theory_array.h

Build docs developers (and LLMs) love