Förderjahr 2021 / Stipendien Call #16 / ProjektID: 5761 / Projekt: Automated Software Verification with First-Order Theorem Provers
Before we are able to prove QuickSort correct, we need to take a short dive into mathematical induction.
Induction is a powerful technique in the world of formal logic and automated theorem proving. It enables us to prove properties about recursive functions, inductive data structures, and various mathematical statements. In this article, we'll explore how induction works in the context of first-order theorem provers, with a focus on the popular tool known as Vampire.
The Art of Formalization
Before we delve into induction, we need to formalize the problem or statement we want to prove using first-order logic. This involves defining predicates, functions, and axioms that accurately represent the problem at hand. For instance, if we intend to prove a property about natural numbers, we might define a predicate for "less than or equal to" (≤) and establish axioms that capture the properties of natural numbers. The same goes for structural induction schemes that we will need to prove sorting algorithms on lists: to prove something about a list we need to construct that list of elements with a head element and a tail (the remaining list) on element types that allow for an ordering, e.g. “<“.
The Principle of Mathematical Induction
The Principle of Mathematical Induction is a powerful mathematical proof technique used to prove that a statement holds for all natural numbers (0, 1, 2, 3, and so on). It consists of two key parts:
- Base Case: In this step, you first prove that the statement is true for the smallest natural number (usually 0 or 1). This establishes a foundation for the proof.
- Inductive Step: The inductive step compels us to prove that if the property holds for some element, it also holds for the next element in the sequence. This is usually accomplished by assuming the property holds for an arbitrary element (referred to as the induction hypothesis) and demonstrating that this assumption implies the property's validity for the next element. For instance, if the statement is true for a particular natural number (let's call it "k"), it must also be true for the next natural number, which is (k+1).
By combining these two steps, you demonstrate that the statement is true for all natural numbers, not just the initial one you checked. It's like building a line of dominoes where you show that if one domino falls, the next one will fall, and so on, ensuring they all fall. Mathematical induction is a powerful tool for proving properties for an infinite set of numbers.
Structural Induction for Recursive Data Structures
Structural Induction is a proof technique used to show that a property or statement holds for all members of a recursively defined data structure, such as a list, tree, or other hierarchical structure. We have
- Base Case: Similar to regular induction, you start by proving that the property holds for the smallest or simplest element of the data structure. This is often the base case. For example, if you're working with a list, the base case would be an empty list, and you prove that the property is true for it.
- Inductive Step: The key to structural induction is proving that if the property is true for a particular element of the data structure, it's also true for the structure that's built by adding one more element to it. This element could be a single item added to a list, a new branch added to a tree, or a new element in any recursive structure. You show that if the property holds for the smaller structure, it also holds for the larger structure.
By combining the base case and the inductive step, you demonstrate that the property is true for the entire data structure, no matter how large or complex. It's like proving that if each piece of a puzzle fits together and the first piece fits, then the whole puzzle fits together correctly. Structural induction is a powerful way to prove properties about recursively defined data structures. This is the kind of induction we will need to prove sorting algorithms on lists of arbitrary length correct.
Induction in the First-order Theorem Prover
To leverage induction in a first-order theorem prover like Vampire, we must encode the induction principle as axioms. The base case and the inductive step are translated into logical formulas that the theorem prover can understand. For instance, the base case might be encoded as "P(0)" where "P" represents the property we aim to prove for natural numbers. The inductive step could be encoded as "∀n. P(n) ⇒ P(n+1)."
To do this, first-order theorem provers like Vampire come equipped with built-in support for different kinds of induction. They utilize an induction rule or tactic to apply the encoded induction principle such as mathematical or structural induction above. This rule permits the prover to perform induction on a specific variable or set of variables. The theorem prover is thus able to automate the inductive proof. It generates subgoals based on the base case and the inductive step and tries to prove them. The induction rule is applied iteratively, generating new subgoals until it can no longer produce additional subgoals (which is rarely the case as our state space is unbounded) or more likely until it times out.
Vampire can also tackle induction on inductive data structures. Whether you want to prove properties about lists, trees, or other data structures, you can encode the base cases and inductive steps for these structures and use induction to establish properties about them. However, the challenge is to make the prover find the right instances of induction.
Forschungsinteressen: Automated Software Verification, Program Analysis, Automated Deduction, Theorem Proving, Formal Methods.