Interactive Theorem Proving Course
Interactive Theorem Proving Course
2 / 321
Part I
Introduction
Motivation
4 / 321
Famous Bugs
Fun to read
http://www.cs.tau.ac.il/~nachumd/verify/horror.html
https://en.wikipedia.org/wiki/List_of_software_bugs
5 / 321
Proof
6 / 321
Mathematical vs. Formal Proof
7 / 321
Detail Level of Formal Proof
8 / 321
Automated vs Manual (Formal) Proof
Fully Manual Proof
very tedious; one has to grind through many trivial but detailed proofs
easy to make mistakes
hard to keep track of all assumptions and preconditions
hard to maintain, if something changes (see Ariane V)
Automated Proof
amazing success in certain areas
but still often infeasible for interesting problems
hard to get insights in case a proof attempt fails
even if it works, it is often not that automated
I run automated tool for a few days
I abort, change command line arguments to use different heuristics
I run again and iterate till you find a set of heuristics that prove it fully
automatically in a few seconds
9 / 321
Interactive Proofs
10 / 321
Typical Interactive Proof Activities
provide precise definitions of concepts
state properties of these concepts
prove these properties
I human provides insight and structure
I computer does book-keeping and automates simple proofs
build and use libraries of formal definitions and proofs
I formalisations of mathematical theories like
F lists, sets, bags, . . .
F real numbers
F probability theory
I specifications of real-world artefacts like
F processors
F programming languages
F network protocols
I reasoning tools
12 / 321
Which theorem prover is the best one? :-)
13 / 321
Part II
Organisational Matters
Aims of this Course
Aims
introduction to interactive theorem proving (ITP)
being able to evaluate whether a problem can benefit from ITP
hands-on experience with HOL
learn how to build a formal model
learn how to express and prove important properties of such a model
learn about basic conformance testing
use a theorem prover on a small project
Required Prerequisites
some experience with functional programming
knowing Standard ML syntax
basic knowledge about logic (e. g. First Order Logic)
15 / 321
Dates
16 / 321
Exercises
17 / 321
Practical Sessions
very informal
main purpose: work on exercises
I I have a look and provide feedback
I you can ask questions
I I might sometimes explain things not covered in the lectures
I I might provide some concrete tips and tricks
I you can also discuss with each other
attendance not required, but highly recommended
I exception: session on 21st April
only requirement: turn up long enough to hand in exercises
you need to bring your own computer
18 / 321
Handing-in Exercises
19 / 321
Passing the ITP Course
20 / 321
Communication
21 / 321
Part III
23 / 321
LCF - Logic of Computable Functions II
24 / 321
LCF Approach
25 / 321
LCF Approach II
26 / 321
LCF Style Systems
There are now many interactive theorem provers out there that use an
approach similar to that of Edinburgh LCF.
HOL family
I HOL theorem prover
I HOL Light
I HOL Zero
I Proof Power
I ...
Isabelle
Nuprl
Coq
...
27 / 321
History of HOL
1979 Edinburgh LCF by Milner, Gordon, et al.
1981 Mike Gordon becomes lecturer in Cambridge
1985 Cambridge LCF
I Larry Paulson and Gèrard Huet
I implementation of ML compiler
I powerful simplifier
I various improvements and extensions
1988 HOL
I Mike Gordon and Keith Hanna
I adaption of Cambridge LCF to classical higher order logic
I intention: hardware verification
1990 HOL90
reimplementation in SML by Konrad Slind at University of Calgary
1998 HOL98
implementation in Moscow ML and new library and theory mechanism
since then HOL Kananaskis releases, called informally HOL 4
28 / 321
Family of HOL
Edinburgh LCF
ProofPower
commercial version of HOL88 by Roger
Jones, Rob Arthan et al. Cambridge LCF
HOL Light
lean CAML / OCaml port by John Harrison HOL88
HOL Zero
Isabelle/HOL
trustworthy proof checker by Mark Adams hol90
ProofPower
Isabelle
I 1990 by Larry Paulson HOL Light
I meta-theorem prover that supports
multiple logics
I however, mainly HOL used, ZF a little hol98 HOL Zero
I nowadays probably the most widely used
HOL system
I originally designed for software verification HOL4
29 / 321
Part IV
HOL’s Logic
HOL Logic
the HOL theorem prover uses a version of classical higher order logic:
classical higher order predicate calculus with
terms from the typed lambda calculus (i. e. simple type theory)
this sounds complicated, but is intuitive for SML programmers
(S)ML and HOL logic designed to fit each other
if you understand SML, you understand HOL logic
Ambiguity Warning
The acronym HOL refers to both the HOL interactive theorem prover and
the HOL logic used by it. It’s also a common abbreviation for higher order
logic in general.
31 / 321
Types
SML datatype for types
I Type Variables (’a, α, ’b, β, . . .)
Type variables are implicitly universally quantified. Theorems
containing type variables hold for all instantiations of these. Proofs
using type variables can be seen as proof schemata.
I Atomic Types (c)
Atomic types denote fixed types. Examples: num, bool, unit
I Compound Types ((σ1 , . . . , σn )op)
op is a type operator of arity n and σ1 , . . . , σn argument types. Type
operators denote operations for constructing types.
Examples: num list or ’a # ’b.
I Function Types (σ1 → σ2 )
σ1 → σ2 is the type of total functions from σ1 to σ2 .
types are never empty in HOL, i. e.
for each type at least one value exists
all HOL functions are total
32 / 321
Terms
33 / 321
Terms II
34 / 321
Free and Bound Variables / Alpha Equivalence
35 / 321
Theorems
36 / 321
HOL Light Kernel
37 / 321
HOL Light Inferences I
REFL
`t=t Γ`s=t
x not free in Γ
ABS
Γ`s=t Γ ` λx. s = λx. t
∆`t=u
TRANS
Γ∪∆`s =u BETA
` (λx. t)x = t
Γ`s=t
∆`u=v
ASSUME
types fit {p} ` p
COMB
Γ ∪ ∆ ` s(u) = t(v )
38 / 321
HOL Light Inferences II
Γ`p⇔q ∆`p
EQ MP
Γ∪∆`q
Γ`p ∆`q
DEDUCT ANTISYM RULE
(Γ − {q}) ∪ (∆ − {p}) ` p ⇔ q
Γ[x1 , . . . , xn ] ` p[x1 , . . . , xn ]
INST
Γ[t1 , . . . , tn ] ` p[t1 , . . . , tn ]
Γ[α1 , . . . , αn ] ` p[α1 , . . . , αn ]
INST TYPE
Γ[γ1 , . . . , γn ] ` p[γ1 , . . . , γn ]
39 / 321
HOL Light Axioms and Definition Principles
3 axioms needed
ETA AX | − (λx. t x) = t
SELECT AX | − P x =⇒ P((@)P))
INFINITY AX predefined type ind is infinite
definition principle for constants
I constants can be introduced as abbreviations
I constraint: no free vars and no new type vars
definition principle for types
I new types can be defined as non-empty subtypes of existing types
both principles
I lead to conservative extensions
I preserve consistency
40 / 321
HOL Light derived concepts
41 / 321
Multiple Kernels
42 / 321
HOL Logic Summary
43 / 321
Part V
45 / 321
Installing HOL
webpage: https://hol-theorem-prover.org
HOL supports two SML implementations
I Moscow ML (http://mosml.org)
I PolyML (http://www.polyml.org)
I recommend using PolyML
please use emacs with
I hol-mode
I sml-mode
I hol-unicode, if you want to type Unicode
please install recent revision from git repo or Kananaskis 11 release
documentation found on HOL webpage and with sources
46 / 321
General Architecture
47 / 321
Filenames
*Script.sml — HOL proof script file
I script files contain definitions and proof scripts
I executing them results in HOL searching and checking proofs
I this might take very long
I resulting theorems are stored in *Theory.{sml|sig} files
*Theory.{sml|sig} — HOL theory
I auto-generated by corresponding script file
I load quickly, because they don’t search/check proofs
I do not edit theory files
*Syntax.{sml|sig} — syntax libraries
I contain syntax related functions
I i. e. functions to construct and destruct terms and types
*Lib.{sml|sig} — general libraries
*Simps.{sml|sig} — simplifications
selftest.sml — selftest for current directory
48 / 321
Directory Structure
bin — HOL binaries
src — HOL sources
examples — HOL examples
I interesting projects by various people
I examples owned by their developer
I coding style and level of maintenance differ a lot
help — sources for reference manual
I after compilation home of reference HTML page
Manual — HOL manuals
I Tutorial
I Description
I Reference (PDF version)
I Interaction
I Quick (cheat pages)
I Style-guide
I ...
49 / 321
Unicode
HOL supports both Unicode and pure ASCII input and output
advantages of Unicode compared to ASCII
I easier to read (good fonts provided)
I no need to learn special ASCII syntax
disadvanges of Unicode compared to ASCII
I harder to type (even with hol-unicode.el)
I less portable between systems
whether you like Unicode is highly a matter of personal taste
HOL’s policy
I no Unicode in HOL’s source directory src
I Unicode in examples directory examples is fine
I recommend turning Unicode output off initially
I this simplifies learning the ASCII syntax
I no need for special fonts
I it is easier to copy and paste terms from HOL’s output
50 / 321
Where to find help?
reference manual
I available as HTML pages, single PDF file and in-system help
description manual
Style-guide (still under development)
HOL webpage (https://hol-theorem-prover.org)
mailing-list hol-info
DB.match and DB.find
*Theory.sig and selftest.sml files
ask someone, e. g. me :-) (tuerk@kth.se)
51 / 321
Part VI
Forward Proofs
Kernel too detailed
53 / 321
Common Terms and Types
Unicode ASCII
type vars α, β, . . . ’a, ’b, . . .
type annotated term term:type term:type
true T T
false F F
negation ¬b ~b
conjunction b1 ∧ b2 b1 /\ b2
disjunction b1 ∨ b2 b1 \/ b2
implication b1 =⇒ b2 b1 ==> b2
equivalence b1 ⇐⇒ b2 b1 <=> b2
disequation v1 6= v2 v1 <> v2
all-quantification ∀x. P x !x. P x
existential quantification ∃x. P x ?x. P x
Hilbert’s choice operator @x. P x @x. P x
55 / 321
Creating Terms
Term Parser
Use special quotation provided by unquote.
Operator Precedence
It is easy to misjudge the binding strength of certain operators. Therefore
use plenty of parenthesis.
56 / 321
Creating Terms II
57 / 321
Inference Rules for Equality
Γ`s=t
GSYM
REFL Γ`t=s
`t=t
Γ`s=t
Γ`s=t
∆`t=u
x not free in Γ TRANS
ABS Γ∪∆`s =u
Γ ` λx. s = λx.t
Γ`p⇔q ∆`p
Γ`s=t EQ MP
∆`u=v Γ∪∆`q
types fit
MK COMB
Γ ∪ ∆ ` s(u) = t(v ) BETA
` (λx. t)x = t
58 / 321
Inference Rules for free Variables
Γ[x1 , . . . , xn ] ` p[x1 , . . . , xn ]
INST
Γ[t1 , . . . , tn ] ` p[t1 , . . . , tn ]
Γ[α1 , . . . , αn ] ` p[α1 , . . . , αn ]
INST TYPE
Γ[γ1 , . . . , γn ] ` p[γ1 , . . . , γn ]
59 / 321
Inference Rules for Implication
Γ ` p =⇒ q
∆`p
MP, MATCH MP
Γ∪∆`q Γ`p
DISCH
Γ − {q} ` q =⇒ p
Γ`p=q
EQ IMP RULE
Γ ` p =⇒ q Γ ` q =⇒ p
UNDISCH
Γ ` q =⇒ p Γ ∪ {q} ` p
Γ ` p =⇒ q Γ ` p =⇒ F
NOT INTRO
∆ ` q =⇒ p
IMP ANTISYM RULE
Γ ` ~p
Γ∪∆`p =q
Γ ` ~p
NOT ELIM
Γ ` p =⇒ q Γ ` p =⇒ F
∆ ` q =⇒ r
IMP TRANS
Γ ∪ ∆ ` p =⇒ r
60 / 321
Inference Rules for Conjunction / Disjunction
Γ`p
DISJ1
Γ`p ∆`q Γ`p ∨ q
CONJ
Γ∪∆`p ∧ q
Γ`q
DISJ2
Γ`p ∧ q Γ`p ∨ q
CONJUNCT1
Γ`p
Γ`p∨q
Γ`p ∧ q ∆1 ∪ {p} ` r
CONJUNCT2
Γ`q ∆2 ∪ {q} ` r
DISJ CASES
Γ ∪ ∆1 ∪ ∆2 ` r
61 / 321
Inference Rules for Quantifiers
Γ ` p[u/x]
EXISTS
Γ`p x not free in Γ Γ ` ∃x. p
GEN
Γ ` ∀x. p
Γ ` ∃x. p
Γ ` ∀x. p ∆ ∪ {p[u/x]} ` r
SPEC
Γ ` p[u/x] u not free in Γ, ∆, p and r
CHOOSE
Γ∪∆`r
62 / 321
Forward Proofs
63 / 321
Forward Proofs — Example I
64 / 321
Forward Proofs — Example II
65 / 321
Forward Proofs — Example II cont.
66 / 321
Part VII
Backward Proofs
Motivation I
let’s prove !A B. A /\ B <=> B /\ A
(* Show |- A /\ B ==> B /\ A *)
val thm1a = ASSUME ‘‘A /\ B‘‘;
val thm1b = CONJ (CONJUNCT2 thm1a) (CONJUNCT1 thm1a);
val thm1 = DISCH ‘‘A /\ B‘‘ thm1b
(* Show |- B /\ A ==> A /\ B *)
val thm2a = ASSUME ‘‘B /\ A‘‘;
val thm2b = CONJ (CONJUNCT2 thm2a) (CONJUNCT1 thm2a);
val thm2 = DISCH ‘‘B /\ A‘‘ thm2b
(* Add quantifiers *)
val thm4 = GENL [‘‘A:bool‘‘, ‘‘B:bool‘‘] thm3
we want to prove
I !A B. A /\ B <=> B /\ A
all-quantifiers can easily be added later, so let’s get rid of them
I A /\ B <=> B /\ A
now we have an equivalence, let’s show 2 implications
I A /\ B ==> B /\ A
I B /\ A ==> A /\ B
we have an implication, so we can use the precondition as an
assumption
I using A /\ B show B /\ A
I A /\ B ==> B /\ A
69 / 321
Motivation III - thinking backwards
70 / 321
Motivation IV
common practise
I think backwards to find proof
I write found proof down in forward style
often switch between backward and forward style within a proof
Example: induction proof
I backward step: induct on . . .
I forward steps: prove base case and induction case
whether to use forward or backward proofs depend on
I support by the interactive theorem prover you use
F HOL 4 and close family: emphasis on backward proof
F Isabelle/HOL: emphasis on forward proof
F Coq : emphasis on backward proof
I your way of thinking
I the theorem you try to prove
71 / 321
HOL Implementation of Backward Proofs
in HOL
I proof tactics / backward proofs used for most user-level proofs
I forward proofs used usually for writing automation
backward proofs are implemented by tactics in HOL
I decomposition into subgoals implemented in SML
I SML datastructures used to keep track of all open subgoals
I forward proof used to construct theorems
to understand backward proofs in HOL we need to look at
I goal — SML datatype for proof obligations
I goalStack — library for keeping track of goals
I tactic — SML type for functions performing backward proofs
72 / 321
Goals
Example Goals
Goal Theorem
([‘‘A‘‘, ‘‘B‘‘], ‘‘A /\ B‘‘) {A, B} |- A /\ B
([‘‘B‘‘, ‘‘A‘‘], ‘‘A /\ B‘‘) {A, B} |- A /\ B
([‘‘B /\ A‘‘], ‘‘A /\ B‘‘) {B /\ A} |- A /\ B
([], ‘‘(B /\ A) ==> (A /\ B)‘‘) |- (B /\ A) ==> (A /\ B)
73 / 321
Tactics
74 / 321
Tactic Example — CONJ TAC
t ≡ conj1 /\ conj2
Γ`p ∆`q asl ` conj1 asl ` conj2
CONJ
Γ∪∆`p ∧ q asl ` t
75 / 321
Tactic Example — EQ TAC
t ≡ lhs = rhs
Γ ` p =⇒ q asl ` lhs ==> rhs
∆ ` q =⇒ p asl ` rhs ==> lhs
IMP ANTISYM RULE
Γ∪∆`p =q asl ` t
76 / 321
proofManagerLib / goalStack
77 / 321
Tactic Proof Example I
Previous Goalstack
-
User Action
g ‘!A B. A /\ B <=> B /\ A‘;
New Goalstack
Initial goal:
!A B. A /\ B <=> B /\ A
: proof
78 / 321
Tactic Proof Example II
Previous Goalstack
Initial goal:
!A B. A /\ B <=> B /\ A
: proof
User Action
e GEN_TAC;
e GEN_TAC;
New Goalstack
A /\ B <=> B /\ A
: proof
79 / 321
Tactic Proof Example III
Previous Goalstack
A /\ B <=> B /\ A
: proof
User Action
e EQ_TAC;
New Goalstack
B /\ A ==> A /\ B
A /\ B ==> B /\ A
: proof
80 / 321
Tactic Proof Example IV
Previous Goalstack
B /\ A ==> A /\ B
A /\ B ==> B /\ A : proof
User Action
e STRIP_TAC;
New Goalstack
B /\ A
------------------------------------
0. A
1. B
81 / 321
Tactic Proof Example V
Previous Goalstack
B /\ A
------------------------------------
0. A
1. B
User Action
e CONJ_TAC;
New Goalstack
A
------------------------------------
0. A
1. B
B
------------------------------------
0. A
1. B
82 / 321
Tactic Proof Example VI
Previous Goalstack
A
------------------------------------
0. A
1. B
B
------------------------------------
0. A
1. B
User Action
e (ACCEPT_TAC (ASSUME ‘‘B:bool‘‘));
e (ACCEPT_TAC (ASSUME ‘‘A:bool‘‘));
New Goalstack
B /\ A ==> A /\ B
: proof
83 / 321
Tactic Proof Example VII
Previous Goalstack
B /\ A ==> A /\ B
: proof
User Action
e STRIP_TAC;
e (ASM_REWRITE_TAC[]);
New Goalstack
Initial goal proved.
|- !A B. A /\ B <=> B /\ A:
proof
84 / 321
Tactic Proof Example VIII
Previous Goalstack
Initial goal proved.
|- !A B. A /\ B <=> B /\ A:
proof
User Action
val thm = top_thm();
Result
val thm =
|- !A B. A /\ B <=> B /\ A:
thm
85 / 321
Tactic Proof Example IX
Combined Tactic
val thm = prove (‘‘!A B. A /\ B <=> B /\ A‘‘,
GEN_TAC >> GEN_TAC >>
EQ_TAC >| [
STRIP_TAC >>
STRIP_TAC >| [
ACCEPT_TAC (ASSUME ‘‘B:bool‘‘),
ACCEPT_TAC (ASSUME ‘‘A:bool‘‘)
],
STRIP_TAC >>
ASM_REWRITE_TAC[]
]);
Result
val thm =
|- !A B. A /\ B <=> B /\ A:
thm
86 / 321
Tactic Proof Example X
Cleaned-up Tactic
val thm = prove (‘‘!A B. A /\ B <=> B /\ A‘‘,
REPEAT GEN_TAC >>
EQ_TAC >> (
REPEAT STRIP_TAC >>
ASM_REWRITE_TAC []
));
Result
val thm =
|- !A B. A /\ B <=> B /\ A:
thm
87 / 321
Summary Backward Proofs
in HOL most user-level proofs are tactic-based
I automation often written in forward style
I low-level, basic proofs written in forward style
I nearly everything else is written in backward (tactic) style
there are many different tactics
in the lecture only the most basic ones will be discussed
you need to learn about tactics on your own
I good starting point: Quick manual
I learning finer points takes a lot of time
I exercises require you to read up on tactics
often there are many ways to prove a statement, which tactics to use
depends on
I personal way of thinking
I personal style and preferences
I maintainability, clarity, elegance, robustness
I ...
88 / 321
Part VIII
Basic Tactics
Syntax of Tactics in HOL
originally tactics were written all in capital letters with underscores
Example: ALL TAC
since 2010 more and more tactics have overloaded lower-case syntax
Example: all tac
sometimes, the lower-case version is shortened
Example: REPEAT, rpt
sometimes, there is special syntax
Example: THEN, \\, >>
which one to use is mostly a matter of personal taste
I all-capital names are hard to read and type
I however, not for all tactics there are lower-case versions
I mixed lower- and upper-case tactics are even harder to read
I often shortened lower-case name is not speaking
90 / 321
Some Basic Tactics
91 / 321
Tacticals
tacticals are SML functions that combine tactics to form new tactics
common workflow
I develop large tactic interactively
I using goalStack and editor support to execute tactics one by one
I combine tactics manually with tacticals to create larger tactics
I finally end up with one large tactic that solves your goal
I use prove or store thm instead of goalStack
make sure to clearly mark proof structure by e. g.
I use indentation
I use parentheses
I use appropriate connectives
I ...
goalStack commands like e or g should not appear in your final proof
92 / 321
Some Basic Tacticals
93 / 321
Basic Rewrite Tactics
94 / 321
Case-Split and Induction Tactics
95 / 321
Assumption Tactics
96 / 321
Decision Procedure Tactics
97 / 321
Subgoal Tactics
98 / 321
Term Fragments / Term Quotations
99 / 321
Importance of Exercises
100 / 321
Tactical Proof - Example I - Slide 1
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
Actions
run g ‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘
this is done by hol-mode
move cursor inside term and press M-h g
(menu-entry HOL - Goalstack - New goal)
101 / 321
Tactical Proof - Example I - Slide 2
Current Goal
!l. LENGTH (l ++ l) = 2 * LENGTH l
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (l ++ l) = 2 * LENGTH l‘‘,
GEN_TAC
Actions
run e GEN TAC
this is done by hol-mode
mark line with GEN TAC and press M-h e
(menu-entry HOL - Goalstack - Apply tactic)
102 / 321
Tactical Proof - Example I - Slide 3
Current Goal
LENGTH (l ++ l) = 2 * LENGTH l
Actions
run DB.print match [] ‘‘LENGTH ( ++ )‘‘
this is done via hol-mode
press M-h m and enter term pattern
(menu-entry HOL - Misc - DB match)
this finds the theorem listTheory.LENGTH APPEND
|- !l1 l2. LENGTH (l1 ++ l2) = LENGTH l1 + LENGTH l2
103 / 321
Tactical Proof - Example I - Slide 4
Current Goal
LENGTH (l ++ l) = 2 * LENGTH l
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
GEN_TAC >>
REWRITE_TAC[listTheory.LENGTH APPEND]
Actions
connect the new tactic with tactical >> (THEN)
use hol-mode to expand the new tactic
104 / 321
Tactical Proof - Example I - Slide 5
Current Goal
LENGTH l + LENGTH l = 2 * LENGTH l
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
GEN_TAC >>
REWRITE_TAC[listTheory.LENGTH APPEND]
Actions
DB.match finds theorem arithmeticTheory.TIMES2
press M-h b and undo last tactic expansion
(menu-entry HOL - Goalstack - Back up)
105 / 321
Tactical Proof - Example I - Slide 6
Current Goal
LENGTH (l ++ l) = 2 * LENGTH l
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
GEN_TAC >>
REWRITE_TAC[listTheory.LENGTH APPEND, arithmeticTheory.TIMES2]);
Actions
add TIMES2 to the list of theorems used by rewrite tactic
use hol-mode to expand the extended rewrite tactic
goal is solved, so let’s add closing parenthesis and semicolon
106 / 321
Tactical Proof - Example I - Slide 7
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
GEN_TAC >>
REWRITE_TAC[listTheory.LENGTH APPEND, arithmeticTheory.TIMES2]);
107 / 321
Tactical Proof - Example II - Slide 1
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘!x1 x2 x3 l1 l2 l3.
(MEM x1 l1 /\ MEM x2 l2 /\ MEM x3 l3) /\
((x1 <= x2) /\ (x2 <= x3) /\ x3 <= SUC x1) ==>
~(ALL_DISTINCT (l1 ++ l2 ++ l3))‘‘,
108 / 321
Tactical Proof - Example II - Slide 2
Current Goal
!x1 x2 x3 l1 l2 l3.
(MEM x1 l1 /\ MEM x2 l2 /\ MEM x3 l3) /\
x1 <= x2 /\ x2 <= x3 /\ x3 <= SUC x1 ==>
~ALL_DISTINCT (l1 ++ l2 ++ l3)
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘!x1 x2 x3 l1 l2 l3.
(MEM x1 l1 /\ MEM x2 l2 /\ MEM x3 l3) /\
((x1 <= x2) /\ (x2 <= x3) /\ x3 <= SUC x1) ==>
~(ALL_DISTINCT (l1 ++ l2 ++ l3))‘‘,
REPEAT STRIP TAC
109 / 321
Tactical Proof - Example II - Slide 2
Current Goal
!x1 x2 x3 l1 l2 l3.
(MEM x1 l1 /\ MEM x2 l2 /\ MEM x3 l3) /\
x1 <= x2 /\ x2 <= x3 /\ x3 <= SUC x1 ==>
~ALL_DISTINCT (l1 ++ l2 ++ l3)
Proof Script
val LENGTH_APPEND_SAME = prove (
‘‘!l. LENGTH (APPEND l l) = 2 * LENGTH l‘‘,
REPEAT STRIP TAC
Actions
add REPEAT STRIP TAC to proof script
expand this tactic using hol-mode
110 / 321
Tactical Proof - Example II - Slide 3
Current Goal
F
------------------------------------
0. MEM x1 l1 4. x2 <= x3
1. MEM x2 l2 5. x3 <= SUC x1
2. MEM x3 l3 6. ALL_DISTINCT (l1 ++ l2 ++ l3)
3. x1 <= x2
oops, we did too much, we would like to keep ALL DISTINCT in goal
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘...‘‘,
REPEAT GEN TAC >> STRIP TAC
Actions
undo REPEAT STRIP TAC (M-h b)
expand more fine-tuned strip tactic
111 / 321
Tactical Proof - Example II - Slide 4
Current Goal
~ALL_DISTINCT (l1 ++ l2 ++ l3)
------------------------------------
0. MEM x1 l1 3. x1 <= x2
1. MEM x2 l2 4. x2 <= x3
2. MEM x3 l3 5. x3 <= SUC x1
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘...‘‘,
REPEAT GEN TAC >> STRIP TAC >>
REWRITE TAC[listTheory.ALL_DISTINCT APPEND, listTheory.MEM APPEND]
112 / 321
Tactical Proof - Example II - Slide 5
Current Goal
~((ALL_DISTINCT l1 /\ ALL_DISTINCT l2 /\ !e. MEM e l1 ==> ~MEM e l2) /\
ALL_DISTINCT l3 /\ !e. MEM e l1 \/ MEM e l2 ==> ~MEM e l3)
------------------------------------
0. MEM x1 l1 3. x1 <= x2
1. MEM x2 l2 4. x2 <= x3
2. MEM x3 l3 5. x3 <= SUC x1
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘...‘‘,
REPEAT GEN TAC >> STRIP TAC >>
REWRITE TAC[listTheory.ALL_DISTINCT APPEND, listTheory.MEM APPEND] >>
‘(x2 = x1) \/ (x2 = x3)‘ by DECIDE_TAC
113 / 321
Tactical Proof - Example II - Slide 6
Current Goals — 2 subgoals, one for each disjunct
~((ALL_DISTINCT l1 /\ ALL_DISTINCT l2 /\ !e. MEM e l1 ==> ~MEM e l2) /\
ALL_DISTINCT l3 /\ !e. MEM e l1 \/ MEM e l2 ==> ~MEM e l3)
------------------------------------
0. MEM x1 l1 4. x2 <= x3
1. MEM x2 l2 5. x3 <= SUC x1
2. MEM x3 l3 6a. x2 = x1
3. x1 <= x2 6b. x2 = x3
Proof Script
val NOT_ALL_DISTINCT_LEMMA = prove (‘‘...‘‘,
REPEAT GEN TAC >> STRIP TAC >>
REWRITE TAC[listTheory.ALL_DISTINCT APPEND, listTheory.MEM APPEND] >>
‘(x2 = x1) \/ (x2 = x3)‘ by DECIDE_TAC >> (
METIS TAC[]
));
114 / 321
Tactical Proof - Example II - Slide 7
115 / 321
Part IX
Induction Proofs
Mathematical Induction
117 / 321
Structural Induction Theorems
118 / 321
Other Induction Theorems
there are many induction theorems in HOL
I datatype definitions lead to induction theorems
I recursive function definitions produce corresponding induction theorems
I recursive relation definitions give rise to induction theorems
I many are manually defined
Examples
|- !P. P [] /\ (!l. P l ==> !x. P (SNOC x l)) ==> !l. P l
|- !P. P FEMPTY /\
(!f. P f ==> !x y. x NOTIN FDOM f ==> P (f |+ (x,y))) ==> !f. P f
|- !P. P {} /\
(!s. FINITE s /\ P s ==> !e. e NOTIN s ==> P (e INSERT s)) ==>
!s. FINITE s ==> P s
119 / 321
Induction (and Case-Split) Tactics
the tactic Induct (or Induct on) is usually used to start induction
proofs
it looks at the type of the quantifier (or its argument) and applies the
default induction theorem for this type
this is usually what one needs
other (non default) induction theorems can be applied via
INDUCT THEN or HO MATCH MP TAC
similarish Cases on picks and applies default case-split theorems
120 / 321
Induction Proof - Example I - Slide 1
Proof Script
val REVERSE_APPEND = prove (
‘‘!l1 l2. REVERSE (l1 ++ l2) = REVERSE l2 ++ REVERSE l1‘‘,
Induct
121 / 321
Induction Proof - Example I - Slide 2
induction step:
!h l2. REVERSE (h::l1 ++ l2) = REVERSE l2 ++ REVERSE (h::l1)
-----------------------------------------------------------
!l2. REVERSE (l1 ++ l2) = REVERSE l2 ++ REVERSE l1
Proof Script
val REVERSE_APPEND = prove (‘‘
!l1 l2. REVERSE (l1 ++ l2) = REVERSE l2 ++ REVERSE l1‘‘,
Induct >| [
REWRITE_TAC[REVERSE_DEF, APPEND, APPEND_NIL],
ASM_REWRITE_TAC[REVERSE_DEF, APPEND, APPEND_ASSOC]
]);
122 / 321
Induction Proof - Example II - Slide 2
Proof Script
val REVERSE_REVERSE = prove (
‘‘!l. REVERSE (REVERSE l) = l‘‘,
Induct
123 / 321
Induction Proof - Example II - Slide 2
induction step:
!h. REVERSE (REVERSE (h::l1)) = h::l1
--------------------------------------------
REVERSE (REVERSE l) = l
Proof Script
val REVERSE_REVERSE = prove (
‘‘!l. REVERSE (REVERSE l) = l‘‘,
Induct >| [
REWRITE_TAC[REVERSE_DEF],
ASM_REWRITE_TAC[REVERSE_DEF, REVERSE_APPEND, APPEND]
]);
124 / 321
Part X
Basic Definitions
Definitional Extensions
126 / 321
Axiomatic Extensions
127 / 321
Oracles
128 / 321
Oracles II
Common oracle-tags
I DISK THM — theorem was written to disk and read again
I HolSatLib — proved by MiniSat
I HolSmtLib — proved by external SMT solver
I fast proof — proof was skipped to compile a theory rapidly
I cheat — we cheated :-)
cheating via e. g. the cheat tactic means skipping proofs
it can be helpful during proof development
I test whether some lemmata allow you finishing the proof
I skip lengthy but boring cases and focus on critical parts first
I experiment with exact form of invariants
I ...
cheats should be removed reasonable quickly
HOL warns about cheats and skipped proofs
129 / 321
Pitfalls of Definitional Approach
130 / 321
Specifications
> val EO SPEC = new specification ("EO SPEC", ["EVEN", "ODD"], EVEN ODD EXISTS);
val EO SPEC = |- EVEN 0 /\ ~ODD 0 /\ (!n. EVEN (SUC n) <=> ODD n) /\
(!n. ODD (SUC n) <=> EVEN n)
131 / 321
Definitions
132 / 321
Restrictions for Definitions
133 / 321
Underspecified Functions
134 / 321
Primitive Type Definitions
135 / 321
Primitive Type Definitions - Example 1
136 / 321
Primitive Type Definitions - Example 2
val it =
|- (!a. abs_dlist (rep_dlist a) = a) /\
(!r. ALL_DISTINCT r <=> (rep_dlist (abs_dlist r) = r))
137 / 321
Primitive Definition Principles Summary
138 / 321
Functional Programming
139 / 321
Functional Programming Example
140 / 321
Datatype Package
the Datatype package allows to define SML style datatypes easily
there is support for
I algebraic datatypes
I record types
I mutually recursive types
I ...
many constants are automatically introduced
I constructors
I case-split constant
I size function
I field-update and accessor functions for records
I ...
many theorems are derived and stored in current theory
I injectivity and distinctness of constructors
I nchotomy and structural induction theorems
I rewrites for case-split, size and record update functions
I ...
141 / 321
Datatype Package - Example I
142 / 321
Datatype Package - Example I - Derived Theorems 1
btree distinct
|- !a2 a1 a0 a. Leaf a <> Node a0 a1 a2
btree 11
|- (!a a’. (Leaf a = Leaf a’) <=> (a = a’)) /\
(!a0 a1 a2 a0’ a1’ a2’.
(Node a0 a1 a2 = Node a0’ a1’ a2’) <=>
(a0 = a0’) /\ (a1 = a1’) /\ (a2 = a2’))
btree nchotomy
|- !bb. (?a. bb = Leaf a) \/ (?b b1 b0. bb = Node b b1 b0)
btree induction
|- !P. (!a. P (Leaf a)) /\
(!b b0. P b /\ P b0 ==> !b1. P (Node b b1 b0)) ==>
!b. P b
143 / 321
Datatype Package - Example I - Derived Theorems 2
144 / 321
Datatype Package - Example II
145 / 321
Datatype Package - Example II - Derived Theorems
my enum nchotomy
|- !P. P E1 /\ P E2 /\ P E3 ==> !a. P a
my enum distinct
|- E1 <> E2 /\ E1 <> E3 /\ E2 <> E3
my enum2num thm
|- (my_enum2num E1 = 0) /\ (my_enum2num E2 = 1) /\ (my_enum2num E3 = 2)
146 / 321
Datatype Package - Example III
147 / 321
Datatype Package - Example III - Derived Theorems
rgb nchotomy
|- !rr. ?n n0 n1. rr = rgb n n0 n1
rgb r fupd
|- !f n n0 n1. rgb n n0 n1 with r updated_by f = rgb (f n) n0 n1
148 / 321
Datatype Package - Example IV
nested record types are not allowed
however, mutual recursive types can mitigate this restriction
149 / 321
Datatype Package - No support for Co-Algebraic Types
150 / 321
Datatype Package - Discussion
151 / 321
Total Functional Language (TFL) package
152 / 321
Well-Founded Relations
153 / 321
Well-Founded Recursion
154 / 321
Define - Initial Examples
Simple Definitions
> val DOUBLE_def = Define ‘DOUBLE n = n + n‘
val DOUBLE_def =
|- !n. DOUBLE n = n + n:
thm
155 / 321
Define discussion
156 / 321
Define - More Examples
157 / 321
Primitive Definitions
Examples
val IS_SORTED_primitive_def =
|- IS_SORTED =
WFREC (@R. WF R /\ !x1 xs x2. R (x2::xs) (x1::x2::xs))
(\IS_SORTED a.
case a of
[] => I T
| [x1] => I T
| x1::x2::xs => I (x1 < x2 /\ IS_SORTED (x2::xs)))
158 / 321
Induction Theorems
Example
val IS_SORTED_ind = |- !P.
((!x1 x2 xs. P (x2::xs) ==> P (x1::x2::xs)) /\
P [] /\
(!v. P [v])) ==>
!v. P v
159 / 321
Define failing
160 / 321
Termination in HOL
161 / 321
Termination in HOL II
162 / 321
Termination in HOL III
one can define ”non-terminating” functions in HOL
however, one cannot do so (easily) with Define
Execution Order
There is no ”execution order”. One can easily define a complicated constant function:
(myk : num -> num) (n:num) = (let x = myk (n+1) in 0)
Unsound Definitions
A function f : num -> num with the following property cannot be defined in HOL unless HOL
has an inconsistancy:
!n. f n = ((f n) + 1)
163 / 321
Manual Termination Proofs I
164 / 321
Manual Termination Proofs II
165 / 321
Manual Termination Proof Example 1
Equation(s) :
[...] |- qsort ord [] = []
[...] |- qsort ord (x::rst) =
qsort ord (FILTER ($~ o ord x) rst) ++ [x] ++
qsort ord (FILTER (ord x) rst)
Induction : ...
Termination conditions :
0. !rst x ord. R (ord,FILTER (ord x) rst) (ord,x::rst)
1. !rst x ord. R (ord,FILTER ($~ o ord x) rst) (ord,x::rst)
2. WF R
166 / 321
Manual Termination Proof Example 2
Initial goal:
?R.
WF R /\
(!rst x ord. R (ord,FILTER (ord x) rst) (ord,x::rst)) /\
(!rst x ord. R (ord,FILTER ($~ o ord x) rst) (ord,x::rst))
167 / 321
Manual Termination Proof Example 2
Initial goal:
?R.
WF R /\
(!rst x ord. R (ord,FILTER (ord x) rst) (ord,x::rst)) /\
(!rst x ord. R (ord,FILTER ($~ o ord x) rst) (ord,x::rst))
1 subgoal :
> ...
167 / 321
Manual Termination Proof Example 3
val qsort_def =
|- (qsort ord [] = []) /\
(qsort ord (x::rst) =
qsort ord (FILTER ($~ o ord x) rst) ++ [x] ++
qsort ord (FILTER (ord x) rst))
val qsort_ind =
|- !P. (!ord. P ord []) /\
(!ord x rst.
P ord (FILTER (ord x) rst) /\
P ord (FILTER ($~ o ord x) rst) ==>
P ord (x::rst)) ==>
!v v1. P v v1
168 / 321
Part XI
Good Definitions
Importance of Good Definitions
170 / 321
Importance of Good Definitions — Clarity I
171 / 321
Importance of Good Definitions — Clarity II
172 / 321
Importance of Good Definitions — Clarity III
173 / 321
Importance of Good Definitions — Proofs
174 / 321
How to come up with good definitions
175 / 321
Good Definitions in Functional Programming
Objectives
clarity (readability, maintainability)
performance (runtime speed, memory usage, ...)
General Advice
use the powerful type-system
use many small function definitions
encode invariants in types and function signatures
176 / 321
Good Definitions – no number encodings
many programmers familiar with C encode everything as a number
enumeration types are very cheap in SML and HOL
use them instead
Example Enumeration Types
In C the result of an order comparison is an integer with 3 equivalence classes: 0, negative and
positive integers. In SML and HOL, it is better to use a variant type.
177 / 321
Good Definitions — Isomorphic Types
178 / 321
Good Definitions — Record Types I
179 / 321
Good Definitions — Record Types II
using records
I introduces field names
I provides automatically defined accessor and update functions
I leads to better type-checking error messages
records improve readability
I accessors and update functions lead to shorter code
I field names act as documentation
records improve maintainability
I improved error messages
I much easier to add extra fields
180 / 321
Good Definitions — Encoding Invariants
try to encode as many invariants as possible in the types
this allows the type-checker to ensure them for you
you don’t have to check them manually any more
your code becomes more robust and clearer
type connection_info = {
state : connection_state,
server : inet_address,
last_ping_time : time option,
last_ping_id : int option,
session_id : string option,
when_initiated : time option,
when_disconnected : time option
}
181 / 321
Good Definitions — Encoding Invariants II
datatype connection_state =
Connected of connected
| Disconnected of disconneted
| Connecting of connecting;
type connection_info = {
state : connection_state,
server : inet_address
}
182 / 321
Good Definitions in HOL
Objectives
clarity (readability)
good for proofs
performance (good for automation, easily evaluatable, ...)
General Advice
same advice as for functional programming applies
use even smaller definitions
I introduce auxiliary definitions for important function parts
I use extra definitions for important constants
I ...
tiny definitions
I allow keeping proof state small by unfolding only needed ones
I allow many small lemmata
I improve maintainability
183 / 321
Good Definitions in HOL II
Technical Issues
write definitions such that they work well with HOL’s tools
this requires you to know HOL well
a lot of experience is required
general advice
I avoid explicit case-expressions
I prefer curried functions
Example
val ZIP_GOOD_def = Define ‘(ZIP (x::xs) (y::ys) = (x,y)::(ZIP xs ys)) /\
(ZIP _ _ = [])‘
184 / 321
Good Definitions in HOL III
185 / 321
Formal Sanity
Formal Sanity
to ensure correctness test your definitions via e. g. EVAL
in HOL testing means symbolic evaluation, i. e. proving lemmata
formally proving sanity check lemmata is very beneficial
I they should express core properties of your definition
I thereby they check your intuition against your actual definitions
I these lemmata are often useful for following proofs
I using them improves robustness and maintainability of your
development
I highly recommend using formal sanity checks
186 / 321
Formal Sanity Example I
187 / 321
Formal Sanity Example II 1
> val ZIP_def = Define ‘
(ZIP [] ys = []) /\ (ZIP xs [] = []) /\
(ZIP (x::xs) (y::ys) = (x, y)::(ZIP xs ys))‘
val ZIP_def =
|- (!ys. ZIP [] ys = []) /\ (!v3 v2. ZIP (v2::v3) [] = []) /\
(!ys y xs x. ZIP (x::xs) (y::ys) = (x,y)::ZIP xs ys)
val ZIP_def =
|- (!xs. ZIP xs [] = []) /\ (!v3 v2. ZIP [] (v2::v3) = []) /\
(!ys y xs x. ZIP (x::xs) (y::ys) = (x,y)::ZIP xs ys0
188 / 321
Formal Sanity Example II 2
val ZIP_def =
|- (!ys. ZIP [] ys = []) /\ (!v3 v2. ZIP (v2::v3) [] = []) /\
(!ys y xs x. ZIP (x::xs) (y::ys) = (x,y)::ZIP xs ys)
191 / 321
Example: Embedding of Propositional Logic I
192 / 321
Example: Embedding of Propositional Logic II
193 / 321
Shallow vs. Deep Embeddings
Shallow Deep
quick and easy to build can reason about syntax
extensions are simple allows verified
implementations
sometimes tricky to define
I e. g. bound variables
194 / 321
Example: Embedding of Propositional Logic III
with deep embedding one can easily formalise syntactic properties like
I Which variables does a propositional formula contain?
I Is a formula in negation-normal-form (NNF)?
with shallow embeddings
I syntactic concepts can’t be defined in HOL
I however, they can be defined in SML
I no proofs about them possible
val _ = Define ‘
(IS_NNF (d_not d_true) = T) /\ (IS_NNF (d_not (d_var v)) = T) /\
(IS_NNF (d_not _) = F) /\
(IS_NNF d_true = T) /\ (IS_NNF (d_var v) = T) /\
(IS_NNF (d_and p1 p2) = (IS_NNF p1 /\ IS_NNF p2)) /\
(IS_NNF (d_or p1 p2) = (IS_NNF p1 /\ IS_NNF p2)) /\
(IS_NNF (d_implies p1 p2) = (IS_NNF p1 /\ IS_NNF p2))‘
195 / 321
Verified vs. Verifying Program
196 / 321
Summary Deep vs. Shallow Embeddings
197 / 321
Part XIII
Rewriting
Rewriting in HOL
199 / 321
Semantic Foundations
Γ`s=t
∆`u=v Γ`s=t
types fit x not free in Γ
COMB ABS
Γ ∪ ∆ ` s(u) = t(v ) Γ ` λx. s = λx. t
Γ`s=t
REFL
∆`t=u `t=t
TRANS
Γ∪∆`s =u
these rules allow us to replace any subterm with an equal one
this is the core of rewriting
200 / 321
Conversions
Example
> BETA CONV ‘‘(\x. SUC x) y‘‘
val it = |- (\x. SUC x) y = SUC y
201 / 321
Conversionals
202 / 321
Depth Conversionals
203 / 321
REWR CONV
it remains to rewrite terms at top-level
this is achieved by REWR CONV
given a term t and a theorem |- t1 = t2, REWR CONV t thm
I searches an instantiation of term and type variables such that t1
becomes α-equivalent to t
I fails, if no instantiation is found
I otherwise, instantiate the theorem and get |- t1’ = t2’
I return theorem |- t = t2’
Example
term LENGTH [1;2;3], theorem |- LENGTH ((x:’a)::xs) = SUC (LENGTH xs)
found type instantiation: [‘‘:’a‘‘ |-> ‘‘:num‘‘]
found term instantiation: [‘‘x:num‘‘ |-> ‘‘1‘‘; ‘‘xs‘‘ |-> ‘‘[2;3]‘‘]
returned theorem: |- LENGTH [1;2;3] = SUC (LENGTH [2;3])
204 / 321
Term Matching
205 / 321
Examples Term Matching
it is often very annoying that the last match in the list above fails
it prevents us for example rewriting !y. (2 = y) /\ Q y to
(!y. (2=y)) /\ (!y. Q y)
Can we do better? Yes, with higher order (term) matching.
206 / 321
Higher Order Term Matching
term matching searches for substitutions such that t org becomes
α-equivalent to t goal
higher order term matching searches for substitutions such that
t org becomes t subst such that the βη-normalform of t subst is
α-equivalent equivalent to βη-normalform of t goal, i. e.
higher order term matching is aware of the semantics of λ
208 / 321
Rewrite Library
209 / 321
Rewrite Library II
REWRITE CONV
REWRITE RULE
REWRITE TAC
ASM REWRITE TAC
ONCE REWRITE TAC
PURE REWRITE TAC
PURE ONCE REWRITE TAC
...
210 / 321
Ho Rewrite Library
211 / 321
Examples Rewrite and Ho Rewrite Library
212 / 321
Summary Rewrite and Ho Rewrite Library
213 / 321
Term Rewriting Systems
214 / 321
Term Rewriting Systems — Termination
Theory
choose well-founded order ≺
for each rewrite theorem |- t1 = t2 ensure t2 ≺ t1
Practice
informally define for yourself what simpler means
ensure each rewrite makes terms simpler
good heuristics
I subterms are simpler than whole term
I use an order on functions
215 / 321
Termination — Subterm examples
the right hand side should not use extra vars, throwing parts away is
usually simpler
I !x xs. (SNOC x xs = []) = F
I !x xs. LENGTH (x::xs) = SUC (LENGTH xs)
I !n x xs. DROP (SUC n) (x::xs) = DROP n xs
216 / 321
Termination — use simpler terms
unclear example
I |- !L. REVERSE L = REV L []
217 / 321
Termination — Normalforms
218 / 321
Termination — Problematic rewrite rules
219 / 321
Rewrites working together
rewrite rules should not compete with each other
if a term ta can be rewritten to ta1 and ta2 applying different
rewrite rules, then ta1 and ta2 it should be possible to further
rewrite them both to a common tb
this can often be achieved by adding extra rewrite rules
Example
Assume we have the rewrite rules |- DOUBLE n = n + n and
|- EVEN (DOUBLE n) = T.
With these the term EVEN (DOUBLE 2) can be rewritten to
T or
EVEN (2 + 2).
To avoid a hard to predict result, EVEN (2+2) should be rewritten to T.
Adding an extra rewrite rule |- EVEN (n + n) = T achieves this.
220 / 321
Rewrites working together II
221 / 321
computeLib
222 / 321
compset
223 / 321
EVAL
224 / 321
simpLib
225 / 321
Basic Usage I
226 / 321
Basic Usage II
227 / 321
Basic Simplifier Examples
228 / 321
FULL SIMP TAC Example
Current GoalStack
P (SUC (SUC x0)) (SUC (SUC y0))
------------------------------------
0. SUC y1 = y2
1. x1 = SUC x0
2. y1 = SUC y0
3. SUC x1 = x2
Action
FULL_SIMP_TAC std_ss []
Resulting GoalStack
P (SUC (SUC x0)) y2
------------------------------------
0. SUC (SUC y0) = y2
1. x1 = SUC x0
2. y1 = SUC y0
3. SUC x1 = x2
229 / 321
REV FULL SIMP TAC Example
Current GoalStack
P (SUC (SUC x0)) y2
------------------------------------
0. SUC (SUC y0) = y2
1. x1 = SUC x0
2. y1 = SUC y0
3. SUC x1 = x2
Action
REV_FULL_SIMP_TAC std_ss []
Resulting GoalStack
P x2 y2
------------------------------------
0. SUC (SUC y0) = y2
1. x1 = SUC x0
2. y1 = SUC y0
3. SUC (SUC x0) = x2
230 / 321
Common simpsets
231 / 321
Common simpset-fragments
232 / 321
Build-In Conversions and Decision Procedures
233 / 321
Examples I
234 / 321
Higher Order Rewriting
Examples
> SIMP_CONV std_ss [FORALL_AND_THM] ‘‘!x. P x /\ Q /\ R x‘‘
val it = |- (!x. P x /\ Q /\ R x) <=>
(!x. P x) /\ Q /\ (!x. R x)
235 / 321
Context
236 / 321
Context Examples
237 / 321
Conditional Rewriting I
238 / 321
Conditional Rewriting Example
239 / 321
Conditional Rewriting Example II
240 / 321
Conditional Rewriting II
241 / 321
Conditional Rewriting Pitfalls I
if the pattern is too general, the simplifier becomes very slow
consider the following, trivial but hopefully educational example
Looping example
> val my_thm = prove (‘‘~P ==> (P = F)‘‘, PROVE_TAC[])
> time (SIMP_CONV std_ss [my_thm]) ‘‘P1 /\ P2 /\ P3 /\ ... /\ P10‘‘
runtime: 0.84000s, gctime: 0.02400s, systime: 0.02400s.
Exception- UNCHANGED raised
242 / 321
Conditional Rewriting Pitfalls II
good conditional rewrites |- c ==> (l = r) should mention only
variables in c that appear in l
if c contains extra variables x1 ... xn, the conditional rewrite
engine has to search instantiations for them
this mean that conditional rewriting is trying discharge the
precondition ?x1 ... xn. c
the simplifier is usually not able to find such instances
Transitivity
> val P_def = Define ‘P x y = x < y‘;
> val my_thm = prove (‘‘!x y z. P x y ==> P y z ==> P x z‘‘, ...)
> SIMP_CONV arith_ss [my_thm] ‘‘P 2 3 /\ P 3 4 ==> P 2 4‘‘
Exception- UNCHANGED raised
243 / 321
Conditional Rewriting Pitfalls III
let’s look in detail why SIMP CONV did not make progress above
> set_trace "simplifier" 2;
> SIMP_CONV arith_ss [my_thm] ‘‘P 2 3 /\ P 3 4 ==> P 2 4‘‘
[468000]: more context: |- !x y z. P x y ==> P y z ==> P x z
[468000]: New rewrite: |- (?y. P x y /\ P y z) ==> (P x z <=> T)
...
[584000]: more context: [.] |- P 2 3 /\ P 3 4
[584000]: New rewrite: [.] |- P 2 3 <=> T
[584000]: New rewrite: [.] |- P 3 4 <=> T
[588000]: rewriting P 2 4 with |- (?y. P x y /\ P y z) ==> (P x z <=> T)
[588000]: trying to solve: ?y. P 2 y /\ P y 4
[588000]: rewriting P 2 y with |- (?y. P x y /\ P y z) ==> (P x z <=> T)
[592000]: trying to solve: ?y’. P 2 y’ /\ P y’ y
...
[596000]: looping - cut
...
[608000]: looping - stack limit reached
...
[640000]: couldn’t solve: ?y. P 2 y /\ P y 4
Exception- UNCHANGED raised
244 / 321
Conditional vs. Unconditional Rewrite Rules
drop example
DROP LENGTH NIL is a useful rewrite rule:
|- !l. DROP (LENGTH l) l = []
in proofs, one needs to be careful though to preserve exactly this form
I one should not (partly) evaluate LENGTH l or modify l somehow
with the conditional rewrite rule DROP LENGTH TOO LONG one does
not need to be as careful
|- !l n. LENGTH l <= n ==> (DROP n l = [])
I the simplifier can use simplify the precondition using information about
LENGTH and even arithmetic decision procedures
245 / 321
Special Rewrite Forms
some theorems given in the list of rewrites to the simplifier are used
for special purposes
there are marking functions that mark these theorems
I Once : thm -> thm use given theorem at most once
I Ntimes : thm -> int -> thm use given theorem at most the given
number of times
I AC : thm -> thm -> thm use given associativity and commutativity
theorems for AC rewriting
I Cong : thm -> thm use given theorem as a congruence rule
these special forms are easy ways to add this information to a simpset
it can be directly set in a simpset as well
246 / 321
Example Once
247 / 321
Stateful Simpset
Example
> SIMP_CONV (srw_ss()) [] ‘‘case [] of [] => (2 + 4)‘‘
val it = |- (case [] of [] => 2 + 4 | v::v1 => ARB) = 6
248 / 321
Discussion on Stateful Simpset
249 / 321
Adding Own Conversions
Example
val WORD_ADD_ss =
simpLib.std_conv_ss
{conv = CHANGED_CONV WORD_ADD_CANON_CONV,
name = "WORD_ADD_CANON_CONV",
pats = [‘‘words$word_add (w:’a word) y‘‘]}
250 / 321
Summary Simplifier
Warning
The simplifier is very powerful. Make sure you understand it and are in
control when using it. Otherwise your proofs easily become lengthy,
convoluted and hard to maintain.
251 / 321
Part XIV
253 / 321
Relations II
R x y RTC R x y RTC R y z
RTC R x y RTC R x x RTC R x z
if the rules are monoton, a least and a greatest fix point exists
(Knaster-Tarski theorem)
least fixpoints give rise to inductive relations
greatest fixpoints give rise to coinductive relations
254 / 321
(Co)inductive Relations in HOL
255 / 321
Example: Transitive Reflexive Closure
256 / 321
Example: Transitive Reflexive Closure II
257 / 321
Example: EVEN
val EVEN_REL_cases =
|- !a0. EVEN_REL a0 <=> (a0 = 0) \/ ?n. (a0 = n + 2) /\ EVEN_REL n
val EVEN_REL_rules =
|- EVEN_REL 0 /\ !n. EVEN_REL n ==> EVEN_REL (n + 2)
258 / 321
Example: Dummy Relations
> val (DF_rules, DF_ind, DF_cases) = Hol_reln
‘(!n. DF (n+1) ==> (DF n))‘
val DT_coind =
|- !DT’. (!a0. DT’ a0 ==> DT’ (a0 + 1)) ==> !a0. DT’ a0 ==> DT a0
val DF_ind =
|- !DF’. (!n. DF’ (n + 1) ==> DF’ n) ==> !a0. DF a0 ==> DF’ a0
260 / 321
Quotient Types Example
261 / 321
Quotient Types Summary
262 / 321
Pattern Matching / Case Expressions
263 / 321
TFL / Define
we have already used top-level pattern matches with the TFL package
Define is able to handle them
I all the semantic complexity is taken care of
I no special syntax or functions remain
I no special rewrite rules, reasoning tools needed afterwards
Define produces a set of equations
this is the recommended way of using pattern matching in HOL
Example
> val ZIP_def = Define ‘(ZIP (x::xs) (y::ys) = (x,y)::(ZIP xs ys)) /\
(ZIP [] [] = [])‘
val ZIP_def = |- (!ys y xs x. ZIP (x::xs) (y::ys) = (x,y)::ZIP xs ys) /\
(ZIP [] [] = [])
264 / 321
Case Expressions
sometimes one does not want to use this compilation by TFL
I one wants to use pattern-matches somewhere nested in a term
I one might not want to introduce a new constant
I one might want to avoid using TFL for technical reasons
in such situations, case-expressions can be used
their syntax is similar to the syntax used by SML
Example
> val ZIP_def = Define ‘ZIP xs ys = case (xs, ys) of
(x::xs, y::ys) => (x,y)::(ZIP xs ys)
| ([], []) => []‘
val ZIP_def = |- !ys xs. ZIP xs ys =
case (xs,ys) of
([],[]) => []
| ([],v4::v5) => ARB
| (x::xs’,[]) => ARB
| (x::xs’,y::ys’) => (x,y)::ZIP xs’ ys’
265 / 321
Case Expressions II
Example
val ZIP_def = |- !ys xs. ZIP xs ys =
pair_CASE (xs,ys)
(\v v1.
list_CASE v (list_CASE v1 [] (\v4 v5. ARB))
(\x xs’. list_CASE v1 ARB (\y ys’. (x,y)::ZIP xs’ ys’))):
266 / 321
Case Expression Issues
267 / 321
Case Expression Issues II
technical issues
I it is tricky to reason about decision trees
I rewrite rules about case-constants needs to be fetched from TypeBase
F alternative srw ss often does more than wanted
I partially evaluated decision-trees are not pretty printed nicely any more
underspecified functions
I decision trees are exhaustive
I they list underspecified cases explicitly with value ARB
I this can be lengthy
I Define in contrast hides underspecified cases
268 / 321
Case Expression Example I
ONCE_REWRITE_TAC [ZIP_def]
Current Goal
!l1 l2.
(LENGTH l1 = LENGTH l2) ==>
(((case (l1,l2) of
([],[]) => []
| ([],v4::v5) => ARB
| (x::xs’,[]) => ARB
| (x::xs’,y::ys’) => (x,y)::ZIP xs’ ys’) =
[]) <=> (l1 = []) /\ (l2 = []))
269 / 321
Case Expression Example IIa – partial evaluation
Current Goal
!l1 l2.
(LENGTH l1 = LENGTH l2) ==>
(((case l1 of
[] => (case l2 of [] => [] | v4::v5 => ARB)
| x::xs’ => case l2 of [] => ARB | y::ys’ => (x,y)::ZIP xs’ ys’) =
[]) <=> (l1 = []) /\ (l2 = []))
270 / 321
Case Expression Example IIb — following tree structure
Current Goal
!l2.
(LENGTH [] = LENGTH l2) ==>
(((case ([],l2) of
([],[]) => []
| ([],v4::v5) => ARB
| (x::xs’,[]) => ARB
| (x::xs’,y::ys’) => (x,y)::ZIP xs’ ys’) =
[]) <=> (l2 = []))
271 / 321
Case Expression Summary
272 / 321
Part XV
Maintainable Proofs
Motivation
proofs are hopefully still used in a few weeks, months or even years
often the environment changes slightly during the lifetime of a proof
I your definitions change slightly
I your own lemmata change (e. g. become more general)
I used libraries change
I HOL changes
F automation becomes more powerful
F rewrite rules in certain simpsets change
F definition packages produce slightly different theorems
F autogenerated variable-names change
F ...
even if HOL and used libraries are stable, proofs often go through
several iterations
often they are adapted by someone else than the original author
therefore it is important that proofs are easily maintainable
274 / 321
Nice Properties of Proofs
275 / 321
Formatting
276 / 321
Formatting Example I
277 / 321
Formatting Example II
278 / 321
Formatting Example II 2
279 / 321
Formatting Example II 3
Alternative Good Example Subgoals
Alternative good formatting using THENL
prove (‘‘!l1 l2. l1 <> [] ==> (LENGTH l2 < LENGTH (l1 ++ l2))‘‘,
Cases >| [
REWRITE_TAC[],
280 / 321
Some basic advice
use semicoli after each declaration
I if exception is raised during interactive processing (e. g. by a failing
proof), previous successful declarations are kept
I it sometimes leads to better error messages in case of parsing errors
use plenty of parentheses to make structure very clear
don’t ignore parser warnings
I especially warnings about multiple possible parse trees are likely to lead
to unstable proofs
I understand why such warnings occur and make sure there is no problem
format your development well
I use indentation
I use linebreaks at sensible points
I don’t use overlong lines
I ...
don’t use open in middle of files
personal opinion: avoid unicode in source files
281 / 321
KISS and Premature Optimisation
282 / 321
Too much abstraction
prove (‘‘!l1 l2. l1 <> [] ==> (LENGTH l2 < LENGTH (l1 ++ l2))‘‘,
some proof using ABSTRACT_LEMMA
)
283 / 321
Too clever tactics
284 / 321
Too Clever Tactics Example I
Bad Example Subgoals
prove (‘‘!l1 l2. l1 <> [] ==> (LENGTH l2 < LENGTH (l1 ++ l2))‘‘,
Cases >> (
REWRITE_TAC[listTheory.LENGTH, listTheory.LENGTH_APPEND] >>
REPEAT STRIP_TAC >>
DECIDE_TAC
))
prove (‘‘!l1 l2. l1 <> [] ==> (LENGTH l2 < LENGTH (l1 ++ l2))‘‘,
Cases >| [
REWRITE_TAC[],
285 / 321
Too Clever Tactics Example II
Bad Example
val oadd_def = Define ‘(oadd (SOME n1) (SOME n2) = (SOME (n1 + n2))) /\
(oadd _ _ = NONE)‘;
val osub_def = Define ‘(osub (SOME n1) (SOME n2) = (SOME (n1 - n2))) /\
(osub _ _ = NONE)‘;
val omul_def = Define ‘(omul (SOME n1) (SOME n2) = (SOME (n1 * n2))) /\
(omul _ _ = NONE)‘;
val onum_NONE_TAC =
Cases_on ‘o1‘ >> Cases_on ‘o2‘ >>
SIMP_TAC std_ss [oadd_def, osub_def, omul_def];
286 / 321
Too Clever Tactics Example II
Good Example
val obin_def = Define ‘(obin op (SOME n1) (SOME n2) = (SOME (op n1 n2))) /\
(obin _ _ _ = NONE)‘;
val oadd_def = Define ‘oadd = obin $+‘;
val osub_def = Define ‘osub = obin $-‘;
val omul_def = Define ‘omul = obin $*‘;
287 / 321
Use many subgoals and lemmata
288 / 321
Subgoal Example
289 / 321
Subgoal Example II
First Version
val IS_WEAK_SUBLIST_FILTER_REFL = store_thm ("IS_WEAK_SUBLIST_FILTER_REFL",
‘‘!l. IS_WEAK_SUBLIST_FILTER l l‘‘,
REWRITE_TAC[IS_WEAK_SUBLIST_FILTER_def] >>
Induct_on ‘l‘ >- (
Q.EXISTS_TAC ‘[]‘ >>
SIMP_TAC list_ss [FILTER_BY_BOOLS_REWRITES]
) >>
FULL_SIMP_TAC std_ss [] >>
GEN_TAC >>
Q.EXISTS_TAC ‘T::bl‘ >>
ASM_SIMP_TAC list_ss [FILTER_BY_BOOLS_REWRITES])
290 / 321
Subgoal Example III
Subgoal Version
val IS_WEAK_SUBLIST_FILTER_REFL = store_thm ("IS_WEAK_SUBLIST_FILTER_REFL",
‘‘!l. IS_WEAK_SUBLIST_FILTER l l‘‘,
GEN_TAC >>
REWRITE_TAC[IS_WEAK_SUBLIST_FILTER_def] >>
‘FILTER_BY_BOOLS (REPLICATE (LENGTH l) T) l = l‘ suffices_by (
METIS_TAC[LENGTH_REPLICATE]
) >>
Induct_on ‘l‘ >> (
ASM_SIMP_TAC list_ss [FILTER_BY_BOOLS_REWRITES, REPLICATE]
))
291 / 321
Subgoal Example IV
Lemma Version
val FILTER_BY_BOOLS_REPL_T = store_thm ("FILTER_BY_BOOLS_REPL_T",
‘‘!l. FILTER_BY_BOOLS (REPLICATE (LENGTH l) T) l = l‘‘,
Induct >> ASM_REWRITE_TAC [REPLICATE, FILTER_BY_BOOLS_REWRITES, LENGTH]);
292 / 321
Avoid Autogenerated Names
293 / 321
Autogenerated Names Example
Bad Example
prove (‘‘!l. 1 < LENGTH l ==> (?x1 x2 l’. l = x1::x2::l’)‘‘,
GEN_TAC >>
Cases_on ‘l‘ >> SIMP_TAC list_ss [] >>
Cases_on ‘t‘ >> SIMP_TAC list_ss [])
Good Example
prove (‘‘!l. 1 < LENGTH l ==> (?x1 x2 l’. l = x1::x2::l’)‘‘,
GEN_TAC >>
Cases_on ‘l‘ >> SIMP_TAC list_ss [] >>
rename1 ‘LENGTH l2‘ >>
Cases_on ‘l2‘ >> SIMP_TAC list_ss [])
294 / 321
Part XVI
Overview of HOL 4
Overview of HOL 4
296 / 321
HOL Bare Source Directories
The following source directories are the very basis of HOL. They are
required to build hol.bare.
src/portableML – common stuff for PolyML and MoscowML
src/prekernel
src/0 – Standard Kernel
src/logging-kernel – Logging Kernel
src/experimental-kernel – Experimental Kernel
src/postkernel
src/opentheory
src/parse
src/bool
src/1
src/proofman
297 / 321
HOL Basic Directories I
On top of hol.bare, there are many basic theories and tools. These are
all required for building the main hol executable.
src/compute – fast ground term rewriting
src/HolSat – SAT solver interfaces
src/taut – propositional proofs using HolSat
src/marker – marking terms
src/q – parsing support
src/combin – combinators
src/lite – some simple lib with various stuff
src/refute – refutation prover, normal forms
src/metis – first order resolution prover
src/meson – first order model elimination prover
298 / 321
HOL Basic Directories II
src/simp – simplifier
src/holyhammer – tool for finding Metis proofs
src/tactictoe – machine learning tool for finding proofs
src/IndDef – (co)inductive relation definitions
src/basicProof – library containing proof tools
src/relation – relations and order theory
src/one – unit type theory
src/pair – tuples
src/sum – sum types
src/tfl – defining terminating functions
src/option – option types
299 / 321
HOL Basic Directories III
bossLib is one central library. It loads all basic theories and libraries and
provides convenient wrappers for the most common tools.
300 / 321
HOL More Theories I
Besides the basic libraries and theories that are required and loaded by
hol, there are many more developements in HOL’s source directory.
src/sort – sorting lists
src/string – strings
src/TeX – exporting LaTeX code
src/res quan – restricted quantifiers
src/quotient – quotient type package
src/finite map – finite map theory
src/bag – bags a. k. a. multisets
src/n-bit – machine words
301 / 321
HOL More Theories II
302 / 321
HOL More Theories III
303 / 321
HOL Selected Examples I
The directory examples hosts many theories and libraries as well. There is
not always a clear distinction between an example and a development in
src. However, in general examples are more specialised and often larger.
They are not required to follow HOL’s coding style as much as
developments in src.
304 / 321
HOL Selected Examples II
305 / 321
HOL Selected Examples III
306 / 321
Concluding Remarks
307 / 321
Part XVII
309 / 321
HOL 4
310 / 321
HOL Omega
311 / 321
HOL Light
312 / 321
Isabelle
313 / 321
Isabelle / HOL - Logic
logic of Isabelle / HOL very similar to HOL’s logic
Imeta logic leads to meta level quantification and object level
quantification
+ type classes
+ powerful module system
+ existential variables
I ...
314 / 321
Isabelle / HOL - Engineering
315 / 321
Isabelle / HOL - Isar
316 / 321
Isabelle / HOL - Drawbacks
317 / 321
Coq
318 / 321
Coq - Logic
319 / 321
Coq - Drawbacks
320 / 321
Summary
321 / 321