1 Introduction
Imandra is a modern computational logic theorem prover built around a pure, higherorder subset of OCaml. Mathematical models and conjectures are written as executable OCaml programs, and Imandra may be used to reason about them, combining models, proofs and counterexamples in a unified computational environment. Imandra is designed to bridge the gap between decision procedures such as SMT [z3_prover], semiautomatic inductive provers of the BoyerMoore family like ACL2 [boyermooreacl, kaufmann1996acl2], and interactive proof assistants for typed higherorder logics [gordonmelhamhol, nipkowpaulsonwenzel:2002, harrisonhollight, cade92pvs]. Our goal is to build a friendly, easy to use system by leveraging strong automation in proof search that can also robustly provide counterexamples for false conjectures. Imandra has novel features supporting largescale industrial applications, including a seamless integration of bounded and unbounded verification, firstclass computable counterexamples, efficiently executable models and a cloudnative architecture supporting live multiuser collaboration. Imandra is already in use by major companies in the financial sector, including Goldman Sachs, Itiviti and OneChronos [passmore2017formal].
An online version may be found at https://try.imandra.ai.
2 Logic
Imandra’s logic is built on a mechanized formal semantics for a pure, higherorder subset of OCaml. Foundationally, the subset of OCaml Imandra supports (called the ‘Imandra Modelling Language’) corresponds to a (specializable) computational fragment of HOL equivalent to multisorted firstorder logic with induction up to extended with theories of datatypes, integer and real arithmetic. Theorems are implicitly universally quantified and expressed as Booleanvalued functions. Proving a theorem establishes that the corresponding function always evaluates to true. As in PRA (Primitive Recursive Arithmetic) and BoyerMoore logics, existential goals are expressed with explicit computable Skolem functions [goodsteinrnt, skolempra, boyermooreacl].
2.1 Definitional Principle
Users work with Imandra by incrementally extending its logical world through definitions of types, functions, modules and theorems. Each extension is governed by a definitional principle designed to maintain the consistency of Imandra’s current logical theory through a discipline of conservative extensions. Types must be proved wellfounded. Functions must be proved terminating. These termination proofs play a dual role: Their structure is mined in order to instruct Imandra how to construct induction principles tailored to the recursive function being admitted when it later appears in conjectures.
Imandra’s definitional principle is built upon the ordinals up to . Ordinals are encoded as a datatype (Ordinal.t) in Imandra using a variant of Cantor normal form, and the wellfoundedness of Ordinal.(<<) — the strict lessthan relation on Ordinal.t values — is an axiom of Imandra’s logic.
To prove a function terminating, an ordinalvalued measure is required. Measures can often be inferred (e.g., for structural recursions) and may be specified by the user. To establish termination, all recursive calls of are collected together with their guards, and their arguments must be proved to conditionally map to strictly smaller ordinals via the measure. Imandra provides a shorthand annotation for specifying lexicographic orders (@@adm), and explicit measure functions may be given using the @@measure annotation.
Example 1 (Ackermann)
We can define the Ackermann function and prove it terminating with the attribute [@@adm m,n] which maps ack m n to the ordinal . Alternatively, we could use [@@measure Ordinal.(pair (of_int m) (of_int n))] to give an explicit measure via helper functions in Imandra’s Ordinal module.
2.2 Lifting, Specialization and Monomorphization
Imandra definitions may be polymorphic and higherorder. However, once Imandra is tasked with determining the truth value of a conjecture, the goal and its transitive dependencies are transformed into a family of ground, monomorphic firstorder (recursive) definitions. These transformations include lambda lifting, specialization and monomorphization. Imandra’s supported fragment of OCaml is designed so that all admitted definitions may be transformed in this way.
Example 3
To prove the following higherorder theorem
we obtain a set of lower level definitions, where the anonymous function was lifted, the type list was monomorphised, and map and length were specialised:
3 Unrolling of Recursive Functions
A major feature of Imandra is its ability to automatically search for proofs and counterexamples in a logic with recursive functions. When a counterexample is found, it is reflected as a firstclass value in Imandra’s runtime and can be directly computed with and run through the model being analysed. In fact, the statement verify (fun x > …) does not try any inductive proving unless requested; the default strategy is recursive function unrolling for a fixed number of steps, a form of bounded symbolic modelchecking.
Our core unrolling algorithm is similar in spirit to the work of Suter et al. [suter2011satisfiability] but with crucial strategic differences. In essence, Imandra uses the assumption mechanism of SMT to block all Boolean assignments that involve the evaluation of a (currently) uninterpreted ground instance of a recursive function. A refinement loop, based on extraction of unsatcores from this set of assumptions, then expands (interprets) the function calls one by one until a model is found, an empty unsatcore is obtained, or a maximal number of steps is reached.
Definition 1 (Function template)
A function template for is a set of tuples such that the body of contains a call to under the path .
Example 4

has as template

has as template
We use what we call reachability literals to prevent the SMT solver from picking assignments that use function calls that are not expanded yet. A reachability literal is a Boolean atom that doesn’t appear in the original problem, and that we associate to a given function call regardless of where it occurs. This is to be contrasted with Suter et al.’s notion of control literals associated with individual occurrences of function calls within the expanded body of another function call. We denote by the unique reachability literal for .
The main search loop is presented in Figure 2, where is the body of (i.e. ) and means is a proper subterm of . We start with initialized to the original goal, and the queue containing function calls in the goal (computed by calls_of_term). Each iteration of the loop starts by checking validity under the assumption that all reachability literals in are false (line 2). If no model is found, we pick an unexpanded function call from the unsat core (line 2). Selection must be fair: all function calls must eventually be picked.
To expand , the corresponding reachability literal becomes true, we instantiate the body of on , and use subcalls_of_call to compute the set of subcalls along with their control path within (using ’s template). For each occurring under path inside , we need to block models that would make valid until gets expanded. The assertions delegate to SMT the work of tracking which paths are forbidden. This way, expanding one function call might lead to many paths becoming “unlocked” at once.
4 Induction
Imandra has extensive support for automated induction built principally around Imandra’s inductive waterfall^{1}^{1}1More details about Imandra’s waterfall and rule classes may be found in our online documentation at https://docs.imandra.ai.. This combines techniques such as symbolic execution, lemmabased conditional rewriting, forwardchaining, generalization and the automatic synthesis of goalspecific induction principles. Induction principle synthesis depends upon data computed about a function’s termination obtained when it was admitted via our definitional principle. Imandra’s waterfall is deeply inspired by the pioneering work of BoyerMoore [kaufmann1996acl2, boyermooreacl], and is in many ways a “lifting” of the BoyerMoore waterfall to our typed, higherorder setting.
Imandra’s waterfall contains a simplifier which automatically makes use of previously proved lemmas. Once proved, lemmas may be installed as rewrite, forwardchaining, elimination or generalization rules. Imandra gives users feedback in order to help them design efficient collections of rules. With a good collection of rules (especially rewrite rules), it is hoped that “most” useful theorems over a given domain will be provable by simplification alone, and induction will only be applied as a last resort. In these cases, the subsequent waterfall moves are designed to prepare the simplified conjecture for induction (via, e.g., generalization) before goalspecific induction principles are synthesized.
Imandra’s inductive waterfall plays an important role in what we believe to be a robust verification strategy for applying Imandra to realworld systems. Recall that all Imandra goals may be subjected to bounded verification via unrolling (cf. Sec 3
). In practice, we almost always attack a goal by unrolling first, attempting to verify it up to a bound before we consider trying to prove it by induction. Typically, for realworld systems, models and conjectures will have flaws, and unrolling will uncover many counterexamples, confusions and mistakes. As all models are executable and all counterexamples are reflected in Imandra’s runtime, they can be directly run through models facilitating rapid investigation. It is typically only after iterating on models and conjectures until all (bounded) counterexamples have been eliminated that we consider trying to prove them by induction. Imandra’s support for counterexamples also plays another important role: as a filter on heuristic waterfall steps such as generalization.
5 Architecture and User Interfaces
Imandra is developed in OCaml and integrates with its compiler libraries. Arbitrary OCaml code may interact with Imandra models and counterexamples through the use of Imandra’s program mode and reflection machinery. Imandra integrates with Z3 [z3_prover] for checking satisfiability of various classes of ground formulas. Imandra has a clientserver architecture: (i) the client parses and executes models with an integrated toplevel; (ii) the server, typically in the cloud, performs all reasoning. Imandra’s user interfaces include:
 Command line

for power users, with tabcompletion, hints, and colorful messages. This interface is similar in some ways to OCaml’s utop.
 Jupyter notebooks

hosted online or via local installation through Docker [ipython2007]. This presents Imandra through interactive notebooks in the browser.
 VSCode plugin

where documents are checked on the fly and errors are underlined in the spirit of Isabelle’s Prover IDE [wenzelproveride].
6 Conclusion
Imandra is an industrialstrength reasoning system combining ideas from SMT, BoyerMoore inductive provers, and ITPs for typed higherorder logics. Imandra delivers an extremely high degree of automation and has novel techniques such as reflected computable counterexamples that we now believe are indispensible for the effective industrial application of automated reasoning. We are encouraged by Imandra’s success in mainstream finance [passmore2017formal], and share a deep conviction that further advances in automation and UI — driven in large part by meeting the demands of industrial users — will lead to a (nearterm) future in which automated reasoning is a widely adopted foundational technology.