Lecture 1 · Formal Software Verification

Motivation and Propositional Logic

Connectives, equivalences, natural deduction, and proofs in Lean

Based on How To Prove It with Lean (HTPIwL), chapter 1.

§1.1 Testing shows presence, not absence

  • Software controls aircraft, medical devices, financial systems, and communication networks. Errors cost money and lives.

  • Testing examines finitely many executions of a program that admits infinitely many.

Program testing can be used to show the presence of bugs, but never to show their absence!

E. W. Dijkstra, Notes on Structured Programming, EWD249, 1970.

§1.1 The complementary route

  • State a property of a program as a mathematical proposition.

  • Prove that every execution satisfies it; the proof covers all inputs at once.

  • A proof assistant checks every step against the rules of a formal logic and helps build the proof interactively.

  • In current use: Lean, Rocq (formerly Coq), Isabelle/HOL, Agda. Landmarks: the seL4 microkernel and the CompCert C compiler.

§1.1 Generated code

  • Language models write a growing share of code. Plausible is not the same as correct; the failure mode is hallucination.

  • A machine-checked proof is verified independently of how the code was produced, so wrong code cannot pass.

  • The burden of correctness moves from reading the code to writing the right specification.

§1.2–1.3 Propositions and connectives

  • A proposition is a declarative sentence that is either true or false. In Lean the type Prop classifies them.

  • Connectives build compound propositions: ¬P, P ∧ Q, P ∨ Q, P → Q, P ↔ Q.

  • They are truth-functional, so the value of a compound proposition depends only on the values of its parts.

  • An implication whose antecedent is false is true, whatever its consequent says, because an implication claims nothing about those cases.

  • Disjunction is inclusive, so P ∨ Q also holds when P and Q hold together.

§1.4 Logical equivalence

  • A valuation assigns a truth value to each variable. A tautology is true under every valuation.

  • A ≡ B when A ↔ B is a tautology, that is, the two agree under every valuation.

Name

Equivalence

De Morgan

¬(P ∧ Q) ≡ ¬P ∨ ¬Q

De Morgan

¬(P ∨ Q) ≡ ¬P ∧ ¬Q

Double negation

¬¬P ≡ P

Contrapositive

P → Q ≡ ¬Q → ¬P

Material implication

P → Q ≡ ¬P ∨ Q

The implication is called material because its truth depends only on the truth values of P and Q, not on any connection of meaning between them (Russell, 1903).

§1.4 A truth table verifies equivalence

Second De Morgan law: the columns for ¬(P ∨ Q) and ¬P ∧ ¬Q agree on all four valuations.

P

Q

P ∨ Q

¬(P ∨ Q)

¬P ∧ ¬Q

T

T

T

F

F

T

F

T

F

F

F

T

T

F

F

F

F

F

T

T

Truth tables decide any propositional question, but grow exponentially in the number of variables and do not extend to the quantifiers of Lecture 2. A calculus derives instead of computing.

§1.4 Logical calculi

  • A calculus fixes axioms and inference rules. A derivation applies the rules, no valuation appears in it, and a machine can check it.

  • It is sound when every theorem is a tautology and complete when every tautology is a theorem. Post proved both for the propositional calculus in 1921.

  • An axiomatic calculus has many axioms and one rule. Łukasiewicz and Tarski need three schemes over → and ¬, with modus ponens.

   A → (B → A)
   (A → (B → C)) → ((A → B) → (A → C))
   (¬A → ¬B) → (B → A)
  • Resolution keeps one rule on clauses, which machine provers search with (Robinson, 1965).

  • Natural deduction has no axioms and two rules per connective, one to introduce it and one to eliminate it. This course uses it.

§1.5 Natural deduction: the rules of the game

  • Each rule has premises above a line and a conclusion below, applied one step at a time (Gentzen, 1935).

  • Introduction rules prove a connective; elimination rules use it. Some rules discharge an assumption, marked [P].

   [P]
    ⋮
    Q                   P → Q    P
  ───────  →I          ─────────────  →E
   P → Q                     Q

§1.5 Conjunction and disjunction

   P    Q              P ∧ Q            P ∧ Q
  ───────  ∧I         ───────  ∧E₁     ───────  ∧E₂
   P ∧ Q                 P                Q
     P                 Q                              [P]     [Q]
  ───────  ∨I₁      ───────  ∨I₂          P ∨ Q         ⋮       ⋮
   P ∨ Q             P ∨ Q                              R       R
                                        ──────────────────────────  ∨E
                                                     R

§1.5 Negation and the classical rule

The constant ⊥ is absurdity, and ¬P abbreviates P → ⊥.

   [P]
    ⋮
    ⊥                  P    ¬P               ⊥
  ───────  ¬I         ─────────  ¬E        ─────  ⊥E
    ¬P                    ⊥                   C

The rules above are constructive. Classical logic adds one further rule, equivalently RAA or excluded middle.

   [¬P]
     ⋮
     ⊥
  ─────────  RAA               ───────────  EM
     P                          P ∨ ¬P

§1.6 The syntax of Lean

  • A declaration names a statement and gives its proof. The keyword comes first, then the name, then the hypotheses in parentheses, then the statement after the colon, then the proof after :=.

Term proof

theorem and_swap (P Q : Prop) (h : P Q) : Q P := h.right, h.left
  • fun h => e builds a function, and f a applies one.

  • ⟨a, b⟩ is the anonymous constructor; h.left and h.right take a conjunction apart.

Tactic proof

example (P Q : Prop) (h : P Q) : Q P := P:PropQ:Proph:P QQ P All goals completed! 🐙
  • by enters tactic mode, · focuses one goal, and sorry marks a missing proof.

  • #check prints the type of a term, and -- starts a comment.

  • The logical symbols are unicode, typed with a backslash abbreviation: \to for →, \and for ∧, \or for ∨, \not for ¬, \iff for ↔, \bot for ⊥, \langle and \rangle for ⟨ ⟩, and \. for ·.

§1.7 Natural deduction in Lean

A proof of a proposition is a term whose type is that proposition; an assumption is a variable of that type. Each rule builds or takes apart a term.

Rule

Lean term

Example

assumption

a hypothesis name

h

→I

fun h => e

fun h => h

→E

application

f a

∧I

⟨_, _⟩

⟨ha, hb⟩

∧E₁, ∧E₂

.left, .right

h.left, h.right

∨I₁, ∨I₂

Or.inl, Or.inr

Or.inl h

∨E

Or.elim or match

h.elim f g

¬I

fun h => e into False

fun hnP => hnP hP

¬E

application into False

hnP hP

⊥E

False.elim or absurd

False.elim h

§1.7 Proof terms from the derivation

1. P ∧ Q → Q ∧ P

   [P ∧ Q]        [P ∧ Q]
  ─────────∧E₂   ─────────∧E₁
      Q              P
    ────────────────────── ∧I
          Q ∧ P
  ────────────────────────── →I
       P ∧ Q → Q ∧ P
example (P Q : Prop) : P Q Q P := fun h => h.right, h.left
  • fun h => is the →I discharging P ∧ Q

  • h.right and h.left are ∧E₂ and ∧E₁

  • ⟨_, _⟩ is the ∧I

2. P → P ∨ Q

     [P]
   ───────  ∨I₁
    P ∨ Q
  ───────────  →I
   P → P ∨ Q
example (P Q : Prop) : P P Q := fun h => Or.inl h
  • fun h => is the →I discharging P

  • Or.inl is ∨I₁, choosing the left disjunct

§1.7 The example P → ¬¬P in detail

  • ¬A is A → False. Twice over: ¬¬P is (P → False) → False, and the statement is P → ((P → False) → False).

  • The parentheses are needed. Since → associates to the right, P → P → P → False is a different proposition, and a false one.

The derivation and the goal at each step

   [¬P]   [P]
  ────────────  ¬E
       ⊥
    ────────  ¬I
       ¬¬P
   ─────────────  →I
     P → ¬¬P
⊢ P → ¬¬P

hP : P
⊢ ¬¬P

hP : P, hnP : ¬P
⊢ False

One function per arrow

example (P : Prop) : P ¬¬P := fun (hP : P) => fun (hnP : ¬P) => hnP hP
  • the first fun is the →I that discharges P

  • the second is the ¬I that discharges ¬P; its parameter has type ¬P, not P

  • hnP hP is the ¬E: a negative hypothesis is a function into False, and applying it to hP gives ⊥

§1.8 Proving with tactics

  • A tactic transforms the proof state, the goal together with the hypotheses in scope, one step at a time.

  • by enters tactic mode, and the sequence elaborates to a proof term, so a tactic proof and a term proof yield the same object.

  • exact e closes the goal when the type of e is the goal, and have h : A := e is the forward step, which adds h : A to the context without touching the goal.

  • apply f is the backward step. It applies f to open arguments, unifies the conclusion of the type of f with the goal, and leaves every unsolved premise as a new goal. With goal Q and f : P → Q, the goal becomes P, and the term under construction is hPQ ?p, which exact hP completes into hPQ hP.

example (P Q : Prop) (hPQ : P Q) (hP : P) : Q := P:PropQ:ProphPQ:P QhP:PQ P:PropQ:ProphPQ:P QhP:PP All goals completed! 🐙
⊢ Q       apply hPQ
⊢ P       exact hP

§1.8 Tactics per connective

Connective

Introduce

Eliminate

intro

apply, application

constructor

.left, .right, cases

Or.inl, Or.inr

cases

¬

intro

apply to reach False

(none)

exact False.elim

example (P Q : Prop) : P Q Q P := P:PropQ:PropP Q Q P P:PropQ:Proph:P QQ P cases h with P:PropQ:ProphP:PQ P All goals completed! 🐙 P:PropQ:ProphQ:QQ P All goals completed! 🐙
example (P : Prop) : ¬¬P P := P:Prop¬¬P P P:Proph:¬¬PP P:Proph:¬¬P¬P False P:Proph:¬¬PhnP:¬PFalse All goals completed! 🐙

Introduction tactics build the goal; elimination tactics use a hypothesis. Classical reasoning adds Classical.byContradiction and Classical.em, needed for double negation elimination.

§1.8 Proving with tactics, as a board game

A tactic proof plays out like a board game. Each element of the game names a precise part of the proof.

Board game

Tactic proof

The board

the proof state, the goal together with the hypotheses in scope

Your pieces

the hypotheses you may use

A move

a tactic (intro, apply, exact, cases, constructor, have)

Splitting the board

a tactic that opens several goals; each must be won

Two directions

apply plays backward from the goal, have plays forward from your pieces

The rulebook

the introduction and elimination rules of natural deduction

Winning

every goal closed, and Lean's kernel checks the final proof term

§1.9 Worked example: P ∧ Q → P

Derivation

   [P ∧ Q]
  ──────────  ∧E₁
      P
  ────────────  →I
   P ∧ Q → P

Term mode

example (P Q : Prop) : P Q P := fun h => h.left
  • fun h => is the →I discharging P ∧ Q

  • h.left is ∧E₁, the left projection

Tactic mode

example (P Q : Prop) : P Q P := P:PropQ:PropP Q P P:PropQ:Proph:P QP All goals completed! 🐙
  • intro h is the →I discharging P ∧ Q

  • exact h.left closes the goal by ∧E₁

§1.9 Worked example: ⊥ → P

Derivation

   [⊥]
  ──────  ⊥E
    P
  ────────  →I
   ⊥ → P

Term mode

example (P : Prop) : False P := fun h => False.elim h
  • fun h => is the →I discharging ⊥

  • False.elim h is ⊥E, giving any P

Tactic mode

example (P : Prop) : False P := P:PropFalse P P:Proph:FalseP All goals completed! 🐙
  • intro h is the →I discharging ⊥

  • exact False.elim h closes the goal by ⊥E

§1.9 Worked example: (P → Q) ∧ P → Q

Derivation

   [(P→Q)∧P]           [(P→Q)∧P]
  ───────────── ∧E₁    ───────────── ∧E₂
      P → Q                  P
    ────────────────────────────── →E
                 Q
   ──────────────────────────────── →I
        (P → Q) ∧ P → Q

Term mode

example (P Q : Prop) : (P Q) P Q := fun h => h.left h.right
  • fun h => is the →I discharging (P → Q) ∧ P

  • h.left and h.right are ∧E₁ and ∧E₂

  • the application h.left h.right is →E, modus ponens

Tactic mode

example (P Q : Prop) : (P Q) P Q := P:PropQ:Prop(P Q) P Q P:PropQ:Proph:(P Q) PQ P:PropQ:Proph:(P Q) PP All goals completed! 🐙
  • intro h is the →I discharging (P → Q) ∧ P

  • apply h.left reduces the goal to P by →E

  • exact h.right supplies P by ∧E₂

§1.9 Worked example: P ∨ Q → Q ∨ P

Derivation

               [P]           [Q]
   [P ∨ Q]    ─────── ∨I₂   ─────── ∨I₁
              Q ∨ P         Q ∨ P
  ───────────────────────────────────── ∨E
             Q ∨ P
  ──────────────────────  →I
   P ∨ Q → Q ∨ P

Term mode

example (P Q : Prop) : P Q Q P := fun h => h.elim (fun hP => Or.inr hP) (fun hQ => Or.inl hQ)
  • fun h => is the →I discharging P ∨ Q

  • h.elim is ∨E, one branch per disjunct

  • Or.inr and Or.inl are ∨I, swapping the disjuncts

Tactic mode

example (P Q : Prop) : P Q Q P := P:PropQ:PropP Q Q P P:PropQ:Proph:P QQ P cases h with P:PropQ:ProphP:PQ P All goals completed! 🐙 P:PropQ:ProphQ:QQ P All goals completed! 🐙
  • intro h is the →I discharging P ∨ Q

  • cases h is ∨E, splitting into the two disjuncts

  • each branch closes with Or.inr / Or.inl (∨I)

§1.9 Worked example: ¬¬P → P (classical)

Derivation

   [¬P]  [¬¬P]
  ──────────────  ¬E
        ⊥
    ──────────  RAA
        P
   ───────────────  →I
     ¬¬P → P

Term mode

example (P : Prop) : ¬¬P P := fun h => Classical.byContradiction (fun hnP => h hnP)
  • fun h => is the →I discharging ¬¬P

  • Classical.byContradiction is the classical rule (RAA)

  • fun hnP => h hnP derives ⊥ from ¬P and ¬¬P by ¬E

Tactic mode

example (P : Prop) : ¬¬P P := P:Prop¬¬P P P:Proph:¬¬PP P:Proph:¬¬P¬P False P:Proph:¬¬PhnP:¬PFalse All goals completed! 🐙
  • intro h is the →I discharging ¬¬P

  • apply Classical.byContradiction invokes RAA

  • intro hnP then exact h hnP derive ⊥ by ¬E

Summary

  • A proposition is classified by Prop; the connectives ¬, ∧, ∨, →, ↔ build compound propositions.

  • Truth tables decide propositional questions but grow exponentially; natural deduction applies rules one step at a time and generalizes.

  • Each connective has introduction and elimination rules; some rules discharge assumptions.

  • A proof is a term whose type is the proposition (Curry-Howard): fun h => e for →I, application for →E, ⟨_, _⟩ for ∧I, Or.inl/Or.inr for ∨I.

  • Tactics transform the proof state: intro, exact, apply, cases, constructor, have; by elaborates them to the same proof term.

  • Classical reasoning adds Classical.byContradiction and Classical.em, needed for ¬¬P → P and one De Morgan law.

Exercises: see the lecture notes.