Verification Tactics
ImandraX introduces a unified language for tactics and tacticals, based on the by annotation. This approach allows users to structure proofs through a combination of smaller tactics and more sophisticated tacticals, enabling modular, reusable proof strategies. Additionally, ImandraX includes a rich library of built-in tactics and tacticals, encompassing a broad spectrum of proof methods and strategies, from highly automated (auto, unroll, etc.) to surgical ([%expand ...] and exact).
lemma test_8 x y =
x=7 && y=6+1 ==> g x y = 1
[@@by intros
@> [%cases (x=y), (x>7)]
@>| [(expr_simplify
@> [%replace y]
@> esimp
@> [%expand "g"]
@> [%replace x]
@> esimp);
([%skip "hello from subgoal 2"]
@> [%skip]
@> skip
@> [%skip "hello again from subgoal 2"]
@> auto);
(expr_simplify
@> [%replace x]
@> [%replace y]
@> [%expand (g 7 7)]
@> simp)
]
]
auto
auto apply Imandra’s inductive waterfall
proof strategy, which combines simplification
(including automatic subgoaling, conditional rewriting and forward-chaining
using previously proved lemmas, decision procedures for datatypes and
arithmetic, etc.), and may decide to do induction. This is the most common way
to prove a theorem in Imandra.
Rule Classes
Theorems may be installed as rules, which instructs Imandra to apply them in certain ways during subsequent proof attempts. The development of an appropriate collection of rules can be used to “teach” Imandra how to reason in a new domain.
[@@rw]or[@@rewrite]: install theorem as a rewrite rule[@@permutative]: restrict rewrite rule as permutative[@@fc]: install theorem as a forward chaining rule[@@elim]or[@@elimination]: install theorem as an elimination rule[@@gen]or[@@generalization]: install theorem as a generalization rule
induct
induct is auto with some parameters exposed for controlling it.
~smt:<smt_solver>- use the given SMT solver for the induction, for example, “~smt:”z3”. Pass~smt:"None"to use the best available solver.~on_fun:<func_name>- perform functional induction using an induction scheme derived from<func_name>~on_vars:<args>- perform structural induction on the arguments<args>if given, else on a heuristically chosen collection of variables. The types of the induction variables must be algebraic datatypes / variant types. An additive approach (linear in the total number of constructors) is taken for combining the schemes of individual variables into a composite induction scheme.~max_induct:<n>- set the maximum induction depth to<n>.
Other Common Tactics
intros
intros takes a goal with implications and conjunctions H |- (A && B) ==> C and returns the new goal H, A, B |- C. This is typically the first tactic in a chain when the goal has premises that need to be introduced as hypotheses.
lemma foo x y = x > 0 && y > 0 ==> x + y > 0 [@@by intros @> auto]
simp / simplify
Apply simplification to the goal. There are several variants:
simp- Quick and simple simplifier, always returns 1 goalsimplify ()- Full waterfall simplifier with all active rules[%simp rule1, rule2]- Simplify using specific rewrite rules[%simp_only rule1, rule2]- Simplify using ONLY the given rules (strict mode)
lemma rev_rev x = List.rev (List.rev x) = x [@@by auto] [@@rw]
(* Using specific rules *)
lemma foo x y = List.length (List.rev (x @ y)) = List.length x + List.length y
[@@by [%simp rev_len, len_append]]
unroll
unroll n performs bounded model checking via SMT, unrolling recursive definitions up to n steps. This is useful for goals that can be discharged by finite exploration.
(* Unroll with default SMT solver *)
verify (fun x -> x < 10 ==> f x >= 0) [@@by unroll 100]
(* Unroll with specific solver *)
[@@by unroll ~smt:"z3" 50]
Arithmetic Decision Procedures
arith- Decision procedure for linear (real and integer) arithmeticnonlin ()- SMT solver with non-linear arithmetic enabled
lemma linear_example x y = x + y >= x [@@by intros @> arith]
lemma quadratic_example x = x * x >= 0 [@@by nonlin ()]
cases
[%cases t1, t2, ..., tk] performs case analysis on boolean conditions, generating k+1 subgoals: one for each case (with the case as hypothesis) and one for when all cases are false.
lemma abs_nonneg x = abs x >= 0
[@@by [%cases x >= 0] @>| [auto; auto]]
expand
[%expand "f"] unfolds the definition of function f. Use [%expand (f x y)] to expand a specific application.
let square x = x * x
lemma square_pos x = x <> 0 ==> square x > 0
[@@by intros @> [%expand "square"] @> nonlin ()]
use
[%use lemma_name args] instantiates a previously proven theorem and adds it as a hypothesis to the current goal.
lemma pow_pos_helper b n = b > 0 ==> pow b n > 0 [@@by auto]
lemma foo x = x > 0 ==> pow x 5 + 1 > 1
[@@by intros @> [%use pow_pos_helper x 5] @> auto]
Combinators
Tactics support monadic operations, enabling clean and modular chaining of reasoning steps. Operators like @>, <|>, and @>>| allow for intuitive composition and conditional application of tactics.
@>:t @> t'expectstto produce exactly one sub-goal, and usest'to prove this subgoal.<|>:t1 <|> t2is theor tacticalwhich appliest1and uses its result if it succeeds, and otherwise appliest2ift1had failed@>>|:t1 @>>| t2appliest1and then appliest2to allt1-generated subgoals.@>|:t @>| [t1; t2; ...; tn]expectstto producensubgoals, and , and usest_ito solve thei-th goal.
Reference for All Tactics
quote_term- Signature:
'a -> 'a term - Doc:
quote_term (1 + 2)is a quotation that will not evaluate to anything but the term “1 + 2”. It can then be provided as-is to a tactic.
- Signature:
combine_proofs- Signature:
proof list -> proof -> proof - Doc: Given a proof
pofbox A1, ..., box An |- box Bandhyps, proofs of|- box A_irespectively, return a proof of|- box B
- Signature:
skip- Signature:
t - Doc:
skipis a no-op from a reasoning perspective (goal will be unchanged). This can be useful when needing to put a place-holder tactic in, e.g., a list of tactics for subgoals when using@>|, e.g.,... @|> [...; skip; ...].
- Signature:
See the variant skip_msg which is like skip but also prints a message.
Note the notation [%skip "skip message"] can be used, with [%skip]
emitting no message.
skip_msg- Signature:
string -> t - Doc:
skip_msg "msg"is likeskip(a reasoning no-op) but also prints the string “msg”. This can be useful for tactic debugging.
- Signature:
Note the notation [%skip "skip message"] can be used, with [%skip]
emitting no message.
ground_eval- Signature:
?max_cache:int -> unit -> t
- Signature:
qs- Signature:
t - Doc: A very fast, incomplete SMT solver for simple QF_UFDT/LIA/LRA goals.
- Signature:
cc- Signature:
t
- Signature:
simp- Signature:
t - Doc: Quick and simple simplifier. This always returns 1 goal.
- Signature:
simp_all- Signature:
t - Doc: Quick and simple simplifier on hypotheses and conclusion. This always returns 1 goal.
- Signature:
ctx_simplify- Signature:
t - Doc: Contextual simplifcation
- Signature:
expr_simplify- Signature:
t - Doc: Moral equivalent of Z3’s Expr.simplify applied to each hypothesis and conclusion in the goal.
- Signature:
or_left- Signature:
t - Doc: Takes
G ?- a || b, returnsG ?- a
- Signature:
or_right- Signature:
t - Doc: Takes
G ?- a || b, returnsG ?- b
- Signature:
split_and- Signature:
t - Doc: Takes
G ?- a && b, returnsG ?- aandG ?- b
- Signature:
exact- Signature:
t - Doc: Takes
G, t ?- t, ..., succeeds with no further goal. Otherwise fails.
- Signature:
trivial- Signature:
t - Doc: Succeeds if a goal is trivially true due to having
falsein the hypotheses,truein the conclusion, or the exact same term in the hypotheses and conclusion (subsuming theexacttactic).
- Signature:
intros- Signature:
t - Doc:
introstakes a goalH |- (A && B) ==> Cand returns the new goalH, A, B |- C.
- Signature:
unintros- Signature:
int list -> t - Doc:
unintros [1;3;4]takes a goalH0; H1; ...; H7 |- Cand turns it intoH0; H2; H5; H6; H7 |- (H1 && H3 && H4) ==> C. In that sense it’s the inverse ofintros.
- Signature:
swap- Signature:
int -> t - Doc:
swap itakes a goalH0; ...; Hk |- C0; ...; Ckandswapsa literal (either a hypothesis or a conclusion term) over the sequent line (|-), negating it in the process.
- Signature:
If i>=0, then hypothesis i (Hi) is negated and made a conclusion. If
i<0, then conclusion abs(i+1) (C(abs(i+1))) is negated and made a
hypothesis.
drop- Signature:
int -> t - Doc:
drop idrops a hypothesis or conclusion, using the same literal addressing scheme asswap.
- Signature:
expand- Signature:
?index:int -> ?all:bool -> string -> t - Doc:
expand "foo"expands the first occurrence offoo.expand ~index:3 "foo"expands the index 3 (i.e., fourth) occurrence offoo.expand ~all:true "foo"expands all current instances offoo. Instances introduced through this expansion process will not be recursively expanded.
- Signature:
Note the notation [%expand "foo"] can also be used, with either function
names or terms which will be automatically quoted (see expand_term).
expand_term- Signature:
?index:int -> ?all:bool -> 'a term -> t - Doc:
expand_term (quote_term (foo x))expands the first occurrence offoo x.expand_term ~index:3 (quote_term (foo x))expands the index 3 (i.e., fourth) occurrence offoo x.expand_term ~all:true (quote_term (foo x))expands all current instances offoo. Instances introduced through this expansion process will not be recursively expanded.
- Signature:
Note the notation [%expand (foo x)] handles term quoting automatically.
replace- Signature:
'a term -> t - Doc:
replace (quote_term x)uses an equality hypothesisx=tto replacexbyt.
- Signature:
normalize- Signature:
?rules:identifier list -> ?strict:bool -> ?basis:identifier list -> ?inline:bool -> 'a term -> t - Doc:
normalize (quote_term t)normalizes a given term under hypotheses of the current goal, replacing the term with its normalized version if it appears in the goal, and adding the hypothesest = normalized_tif the target term does not appear already in the goal.
- Signature:
Normalization applies all active rewrite rules, forward chaining rules, expands all enabled non-recursive function definitions and includes the speculative expansion and simplification of recursive functions in order to take advantage of inductive hypotheses, in the same manner as the waterfall simplifier.
The rules and strict parameters behave the same as with the simplify
tactic, restricting which rewrite rules and function definitions may be
applied and/or expanded. The basis parameter acts as a restriction on
which function definitions can be expanded (basis members are not
expanded, unless they are explicitly given via rules with strict:true.
Note the notation [%normalize t] which normalizes term t with default
arguments given to normalize.
generalize- Signature:
'a term -> string -> t - Doc:
generalize (quote_term t) gen_namegeneralizes termtby replacing it with a fresh Skolem of the appropriate type throughout the goal. There are some restrictions on the namegen_nameused for the new general term: no terms can already appear in the goal with the same name, and the term should follow the basic syntactic rules of IML/OCaml identifiers (begin with a lowercase alphabetical character or_, etc.).
- Signature:
cases- Signature:
bool term list -> t - Doc:
cases [(quote_term t1);...;(quote_term tk)]case-splits on the given cases, returning k+1 subgoals consisting of one for each case (with the case added as a hypothesis), and one additional subgoal for the ‘negative’ case in which all t1,…,tk are false. The terms must be boolean-valued.
- Signature:
Note the notation [%cases t1, t2, ..., tk] will handle term quoting
automatically.
subgoal- Signature:
bool term -> t - Doc:
subgoal (quote_term t)assumes a term and adds the correctness of this assumption as a subgoal. This effectively applies thecutrule of sequent calculus to produce two subgoals: one in which the term has been assumed, and another in which the term must be proved under the current goal context. The term must be boolean-valued.
- Signature:
Note the notation [%subgoal t] will handle term quoting automatically.
lift_ifs- Signature:
t - Doc: Lift if-then-else expressions to the top-level and split accordingly into
subgoals. This does a limited amount of feasibility checking to eliminate
obviously true subgoals (infeasible
ifbranches under the current context) while lifting.
- Signature:
flatten- Signature:
t - Doc: Disjunctive flattening.
- Signature:
smolsmt- Signature:
t - Doc: A small and simple SMT solver for QF_UF goals.
- Signature:
unroll- Signature:
?smt:string -> ?query_timeout:int -> ?no_asm_query_timeout:int -> int -> t - Doc:
unroll ~smt:"z3" 42does 42 rounds of unrolling with SMT solver named “z3”. Pass?smt:Noneto use the best available SMT solver.
- Signature:
arith- Signature:
t - Doc: A decision procedure for linear (real and integer) arithmetic.
- Signature:
nonlin- Signature:
?smt:string -> unit -> t - Doc: An SMT solver with non-linear arithmetic enabled. Pass
Noneto use the best available SMT solver.
- Signature:
auto- Signature:
t - Doc: Auto tactic, Imandra’s flagship automated inductive waterfall proof procedure, taking into account all known active (rw,fc,elim,gen) rules, incorporating conditional rewriting with back-chaining, speculative expansion of recursive functions and symbolic execution, forward-chaining for simplification contexts, subgoaling, congruence closure, tautology detection, destructor elimination, generalization and automated induction.
- Signature:
induction- Signature:
?id:identifier -> ?vars:string list -> unit -> t - Doc: Induction tactic: Synthesize an induction scheme for the goal and apply
it. This does not invoke the induction waterfall (as in
auto).
- Signature:
simplify- Signature:
?smt:string -> ?backchain_limit:int -> ?rules:identifier list -> ?strict:bool -> ?basis:identifier list -> unit -> t - Doc: Full simplifier (the waterfall simplifier). Pass
?smt:Noneto use the best available SMT solver. The parameterrulesis an optional list of IDs of rewrite rules. If given, only these rewrite rules will be used during simplification. The parameter~strictwhentrueand given in conjunction withrulesfurther restricts the simplifier to only use definitional rules (function definitions, both recursive and non-recursive) and rewrite rules which are explicitly present in theruleslist. The parameter~backchain_limitcontrols the recursive depth of back-chaining on hypotheses that is allowed during the relieving of rewrite rule guards (hypotheses).
- Signature:
Note the syntax [%simplify foo, bar, baz] which expands to simplify
~rules:[[%id foo]; [%id bar]; [%id baz]] ().
Note that [%simp ...] is an alias for [%simplify ...].
Note the syntax [%simplify_only ...] and its alias [%simp_only ...]
which are like [%simp ...] with ~strict:true.
induct- Signature:
?smt:string -> ?on_fun:identifier -> ?on_vars:string list -> ?otf:bool -> ?max_induct:int -> ?do_not:string list -> unit -> t - Doc: Induction using the waterfall. Use like this:
induct ~smt:"z3" ~on_vars:["x"; "y"] ()for structural induction, orinduct ~on_fun:[%id f] ()for function induction with functionf
- Signature:
use- Signature:
bool term -> t - Doc: Use an existing theorem to add an additional hypothesis to the current
goal. For example
use (quote_term (add_is_commutative x y))takes a goalA ?- Band returns a goal(x+y)=(y+x), A ?- B.
- Signature:
Note the notation [%use foo x y z] handles term quoting automatically.
enumerate- Signature:
string list -> t - Doc: A tactic that enumerates all values of finite-type variables.
- Signature:
par_solve- Signature:
(t term * goal) list -> proof list res - Doc: Primitive to solve subgoals in parallel
- Signature: