Back to Program Verification Index | Previous: (y-04) First-Order Theories | Next: (y-06) Boogie and Boostan
Mental Model for SMT-LIB
- SMT-LIB is the “assembly language” of formal verification. It’s a standardized language that almost all modern SMT solvers (like Z3, CVC5, and SMTInterpol) understand.
- It is based on Sorted First-Order Logic. Everything has a type (a “sort”).
- The syntax uses Prefix Notation (Lisp-style), which makes it easy for machines to parse but requires humans to get used to nested parentheses.
- An SMT Script is a sequence of commands that define the environment, state assumptions, and ask questions about satisfiability.
Introduction to SMT-LIB

SMT-LIB (Satisfiability Modulo Theories Library) provides a standard for theories, logics, and solver interaction. Instead of learning a new language for every tool, we use SMT-LIB as a universal interface.
Prefix Notation (S-Expressions)
In SMT-LIB, operators come before their arguments.
| Standard Math | SMT-LIB (Lisp-style) |
|---|---|
(= (+ x y) 10) | |
(and (>= x 0) (< x 5)) | |
(or (not P) Q) |
The SMT-LIB Script Lifecycle
A typical script follows these steps, using the 4 main commands:
define-fun: Define a function (often used for complex expressions or predicates).assert: State a formula that must be true.check-sat: Ask the solver if there’s an assignment that makes all assertions true.get-model: Ifsat, ask for a concrete assignment to the variables.
Full Lifecycle Example
- Set Logic: Tell the solver which theories you need.
QF_LIA: Quantifier-Free Linear Integer Arithmetic.QF_LRA: Quantifier-Free Linear Real Arithmetic.QF_A: Quantifier-Free Formulas over Arrays.AUFLIA: Arrays, Uninterpreted Functions, and Linear Integer Arithmetic.
- Declare Symbols: Define your constants and functions.
- Assert: State your constraints or the negation of what you want to prove.
- Check-Sat: Ask the solver: “Is there any assignment that makes all assertions true?”
- Get-Model: If the answer is
sat, ask for a concrete example.
Satisfiability vs. Validity
In SMT, the primary operation is (check-sat). However, in verification, we often want to know if a formula is valid (true in all models).
Theorem: A formula is valid if and only if its negation is unsatisfiable.
To prove that is valid, we:
- Assert the negation
(not F). - Run
(check-sat). - If the result is
unsat, then is valid. - If the result is
sat, then is invalid, and the model provided by the solver is a counterexample.
Example: Checking Validity of Contraposition
We want to check if is valid.
; 1. Declare variables
(declare-fun P () Bool)
(declare-fun Q () Bool)
; 2. Define the formula F
; F: (=> (=> P Q) (=> (not Q) (not P)))
; 3. Assert the negation (not F)
(assert (not (=> (=> P Q) (=> (not Q) (not P)))))
; 4. Check satisfiability
(check-sat)
; Result should be 'unsat', proving the original formula is valid.Important SMT Logics

Solvers use these labels to pick the best algorithm for the job:
- QF_LIA: Most common for software (integers, addition, comparisons).
- QF_A: Used for programs with arrays or memory.
- UFLIA: Adds “Uninterpreted Functions,” which allows the solver to reason about function calls it doesn’t have a definition for (relying only on congruence).
🧠 Deep Dive: Integer Division in SMT
SMT solvers support integer division div, but it comes with pitfalls:
- Division by Zero: Most solvers (like Z3) define
(div y 0)to be some fixed but unspecified value. This means(= (div y 0) (div y 0))istrue, even though it looks like an error! - Non-Linearity: If both
yandzare variables,(div y z)makes the formula non-linear, which is undecidable in general.
(declare-fun x () Int)
(declare-fun y () Int)
(declare-fun z () Int)
(assert (= x (div y z))) ; Dangerous if z is a variable!
(check-sat)Summary
- SMT-LIB is the standard interface for talking to solvers.
- Uses Prefix Notation (S-expressions).
- Scripts follow a Declare Assert Check pattern.
- Sorted Logic ensures that we don’t accidentally add a Boolean to an Integer.
- Logics define the specific subset of FOL and theories being used.
Self-Check
- Translate into SMT-LIB prefix notation.
Answer
(and (= (+ x y) 10) (>= x 0)). Each operator comes before its operands, and conjunctions wrap the two atoms withand.
- How do you use
(check-sat)to prove that a formula is valid?
Answer
Assert the negation
(assert (not F))and call(check-sat). If the solver repliesunsat, the negation has no model, so is valid. If it repliessat, the returned model is a concrete counterexample to .
- What logic label would you choose for verifying a program that manipulates integer counters and array indices, and why?
Answer
AUFLIA: Arrays, Uninterpreted Functions, and Linear Integer Arithmetic. It includes the array theory you need for indexed memory, uninterpreted functions for code you do not unfold, and linear integer arithmetic for the counter math.QF_LIAalone would not let you reason about arrays;QF_Aalone would not handle the integer arithmetic.
- Why is
(div y z)dangerous whenzis a variable?
Answer
Two issues. First, integer division by a variable makes the formula non-linear, and non-linear integer arithmetic is undecidable in general, so the solver may time out or give up. Second, division by zero is implementation-defined: Z3 picks some fixed but unspecified value, so
(div y 0)does not raise an error but produces a value the formula then reasons about as if it were real, which can hide real bugs.
- List the four main SMT-LIB commands in the order a typical script uses them.
Answer
define-fun(ordeclare-fun) to introduce symbols,assertto state the constraints,check-satto ask whether they are jointly satisfiable, andget-modelto retrieve a concrete satisfying assignment when the answer issat. The lifecycle is declare, assert, check, model.
Back to Program Verification Index | Previous: (y-04) First-Order Theories | Next: (y-06) Boogie and Boostan | (y) Return to Notes | (y) Return to Home