Specification and Verification I
Syntax and semantics:
-
symbol: V1 ,..., Vn
-
e.g. X, R, Q etc.
-
denote arbitrary variables
-
-
expressions : E1, ... , En
-
e.g. X + 1
-
denote values
-
-
statements S1, ... , Sn
-
e.g. X < Y
-
denotes conditions which evaluate to booleans
-
built from
-
booleans
-
relational operators on expressions
-
predicates on expressions
-
e.g. ODD(X), (X+1)2 >= X2
-
-
-
compound statements built from above using logical operators
-
not, and, or, implies, iff
-
-
-
commands C1, ... , Cn
-
denotes, arbitrary commands as follows
-
Commands
-
assignment
-
V := E
-
-
array assignment
-
V(E1) = E2
-
-
sequences
-
C1; ... ; Cn
-
-
blocks
-
begin var V1; ... ; var Vn ; C end
-
semantics:
-
command C is executed
-
variables V1 ... Vn are returned to their value before the block
-
-
-
one armed conditional
-
if S then C
-
-
two armed conditional
-
if S then C1 else C2
-
-
while
-
while S do C
-
-
for
-
for V := E1 until E2 do C
-
semantics
-
if
then execute C with value of V increasing each time etc. -
E1 and E2 are evaluated once to get e1 and e2
-
if e1 or e2 are not numbers or if e2 < e1 then nothing is done
-
if e1 <= e2 the for command is equivalent to
-
-
begin var V; v:=e1; C ; v:=e1+1; C ; ... ; v:=en; C end
Hoare notation
-
{P} C {Q}
-
where
-
P is preconditions
-
C is a program from the language specified above
-
Q is postconditions
-
-
partial correctness specification
-
{P} C {Q}
-
it is not necessary for C to terminate
-
but if C does terminate then Q holds
-
-
total correctness specification
-
[P] C [Q]
-
whenever C is executed in a state satisfying P then C terminates
-
after termination Q holds
-
-
total correctness = partial correctness + termination
auxiliary (or ghost) variables
-
{X=x /\ Y=y} begin R := X; X := Y; Y := R {X=y, Y=x}
-
x and y are auxiliary variables
-
Floyd-Hoare logic
-
|- S means S has a proof
-
statements that have proofs are called theorems
-
-
assignment axiom
-
|- {P[E/V]} V :=E {P}
-
[E/V] is substituting term E for all occurrences of variable V
-
-
it is not
-
|- {P} V :=E {P[V/E]}
-
|- {P} V :=E {P[E/V]}
-
-
does not hold when the E can have side effects on the state
-
-
precondition strengthening
|- P => P' |- {P'} C {Q}
---------------------------------
|- {P} C {Q}
-
-
e.g.
-
|- X=n => X+1=n+1 |- {X+1=n+1} X := X + 1 {X=n+1}
------------------------------------------------------------------------------
|- {X=n} X := X + 1 {X=n+1}
-
postcondition weakening
|- {P} C {Q'} |- Q' => Q
---------------------------------------
|- {P} C {Q}
-
specification conjunction
|- {P1} C {Q1} |- {P2} C {Q2}
-------------------------------------------
|- {P1 /\ P2} C {Q1 /\ Q2}
-
specification disjunction
|- {P1} C {Q1} |- {P2} C {Q2}
-------------------------------------------
|- {P1 \/ P2} C {Q1 \/ Q2}
-
sequencing rule
|- {P} C1 {Q} |- {Q} C2{R}
------------------------------------------
|- {P} C1; C2 {R}
-
the derived sequencing rule
|- P => P1
|- {P1} C1 {Q1} |- Q1 => P2
... ...
|- {Pn} C1 {Qn} |- Qn => Q
----------------------------------------
|- {P} C1 ; ... ; Cn {Q}
-
the block rule
|- {P} C {Q}
--------------------------------------------------
|- {P} begin var V1; ... var Vn ; C end {Q}
where none of V1 ... Vn occur in P or Q
-
the derived block rule
|- P => P1
|- {P1} C1 {Q1} |- Q1 => P2
... ...
|- {Pn} C1 {Qn} |- Qn => Q
-----------------------------------------------------
|- {P} begin var V1 ; ... ; Vn ; C1 ; ... ; Cn {Q}
-
the conditional rules
|- {P /\ S} C {Q} |- P /\ ¬S => Q
---------------------------------------------
|- {P} if S then C {Q}
|- {P /\ S} C1 {Q} |- {P /\ ¬S} C2 {Q}
-------------------------------------------------
|- {P} if S then C1 else C2 {Q}
-
the case rule
?
-
the while rule
|- {P /\ S} C {P}
-----------------------------------
|- {P} while S do C {P /\ ¬ S}
-
-
e.g.
-
|- {X=R+(Y*Q)} begin R:=R-Y; Q:=Q+1 end {X=R+(Y*Q)}
|- {X=R+(Y*Q) /\ Y<=R} begin R:=R-Y; Q:=Q+1 end {X=R+(Y*Q)}
by precondition strengthening
|- {X=R+(Y*Q)}
while Y<=R do
begin R:=R-Y; Q:=Q+1 end
{X=R+(Y*Q) /\ ¬(Y<=R)}
by the while rule
|- {T}
while Y<=R do
begin R:=R-Y; Q:=Q+1 end
{X=R+(Y*Q) /\ R<Y)}
by various other rules
-
the for rule
-
is difficult to define properly
-
given
|- {P} C {P[V+1/V]}
suppose
C contains no assignments to V
then
|- {V=v} C {V=v}
|- {P /\ (V=v)} {P[V+1/V] /\ (V=v)} by specification conjunction
also consider V:=v; C
|- {P[v/V]} V:=v {P /\ (V=v)}
then
|- {P[v/V]} V:=v; C {P[V+1/V] /\ (V=v)} by the sequencing rule
|- P[V+1/V] /\ (V=v) => P[v+1/V] by logic
hence
|- {P[v/V]} V:=v; C {P[v+1/V]} by postcondition weakening
|- {P[e1/V]}V:=e1; C; ... V:= e2; C {P[e2/v]}
which suggests a rule
|- {P} C {P[V+1/V]}
-------------------------------------------------------------
|- {P[E1/V]} for V := E1 until E2 do C {P[E2 + 1/V]}
but it doesn't work, e.g.
since |- {P} X:=Y+1 {P[Y+1/Y]}
|- {X=3} for Y:=3 until 1 do X:=Y+1 {X=2} shouldn't do anything as 3<1
instead try
|- {P} C {P[V+1/V]}
---------------------------------------------------------------------------
|- {P[E1/V] /\ E1 <= E2} for V := E1 until E2 do C {P[E2 + 1/V]}
but
since |- {Y=1} Y:=Y-1 {Y+1=1}
|- {1=1 /\ 1<=1 } for y:=1 until 1 do Y := Y-1 {2=1}
where 1=1 is P[1/Y]
2=1 is P[1+1/Y]
hence end up with
|- {P /\ (E1 <= V) /\ (V <= E2)} C {P[V+1/V]}
---------------------------------------------------------------------------
|- {P[E1/V] /\ E1 <= E2} for V := E1 until E2 do C {P[E2 + 1/V]}
where V is not assigned to in C
any variable occurring in E1 or E2 is not assigned to
-
the for axiom
|- {P /\ (E2 < E1)} for V:=E1 until E2 do C {P}
-
the array assignment axiom
|- {P[A{E1 <- E2}/A]} A(E1):=E2 {P}
where A{E1 <- E2} is an array the same as A except that the value at E1 is E2
i.e. treat an array assignment as a normal assignment
-
the array assignment axioms
|- A{E1 <- E2}(E1) = E2
¬(E1 = E3) => |- A{E1<-E2) = A(E3 )
soundness and completeness
-
soundness
-
where there is a logical proof then that construct is in the language
-
methods of doing this
-
define the language by the axioms and rules of the logic
-
is axiomatic semantics
-
though it is impossible to construct Floyd Hoare logic for some constructs
-
-
prove that the logic fits the language
-
prove the axioms and rules of the logic to be valid
-
have a function
-
Meaning(C, s) = s', the resultant state of executing C in state s
-
{P} C {Q} is then true if
-
whenever
-
P is true in state s, and
-
Meaning(c)(s) = s'
-
-
then
-
Q is true in state s'
-
-
-
-
-
-
-
completeness
-
every true specification can be proved
-
how distinguish incompleteness arising due to incompleteness in the assertion language (e.g. arithmetic)
-
-
it is impossible to give a soundness and completeness system for a language combining
-
procedures as parameters of procedure calls
-
recursion
-
static scopes
-
global variables
-
internal procedures as parameters of procedure calls
-
Mechanising program verification
-
it is impossible in principle to design a decision procedure to decide automatically the truth or falsehood of an arbitrary mathematical statement
-
can mechanise parts of verification
-
it is possible to check whether an arbitrary formal proof is valid
-
backwards proof:
-
from a statement generate subgoals, prove them and combine them
-
-
to prove {P}C {Q}
-
program C is annotated by inserting into it statements expression conditions that are meant to hold at various intermediate points (automation is AI)
-
a set of verification conditions are generated mechanically
-
verification conditions are proved (automation is AI)
-
Annotation
-
annotated command with assertions embedded within it
-
a command is properly annotated if statements have been inserted at the following places
-
before each command Ci in a sequence Ci ; ... ; Cn which is not an assignment command
-
after the DO in while and for commands
-
-
a properly annotated specification {P} C {Q} is one where C is a properly annotated command
Verification condition generation
-
the justifications assume that the verification conditions have been proved
-
“when C is not an assignment command, then if the result holds for the constituent commands of C (this is called the induction hypothesis), then it holds also for C”
-
most of the justifications can be done using the relevant rule as previously, because the VCs are basically the preconditions to these rules
-
-
assignment command {P} V:= E {Q}
-
-
P => Q[E/V]
-
-
justification
-
|- {Q[E/V]} V:=E {Q} by the assignment axiom
|- {P} V:= E {Q} by precondition strengthening using P=>Q[E/V]
-
one armed conditional {P} if S then C {Q}
-
-
(P /\ ¬S) => Q
-
the verification conditions generated by {P /\ S} C {Q}
-
-
justification
-
|- (P /\ ¬S) => Q
|- {P /\ S} C {Q} by the induction hypothesis
|- {P} if S then C {Q} by the one armed conditional rule
-
two armed conditional {P} if S then C1 else C2 {Q}
-
-
the verification conditions generated by {P /\ S} C1 {Q}
-
the verification conditions generated by {P /\ S} C2 {Q}
-
-
justification
-
|- {P /\ S} C1 {Q} by the induction hypothesis
|- {P /\ S} C2 {Q} by the induction hypothesis
|- {P} if S then C1 else C2 {Q} by the two armed conditional rule
-
sequences {P} C1 ; ... ; Cn-1 ; {R} Cn {Q}, where Cn is not an assignment
-
-
the verification conditions generated by {P} C1 ; ... ; Cn-1 {R}
-
the verification conditions generated by {R} Cn {Q}
-
-
justification
-
|- {P} C1 ; ... ; Cn-1 {R} by induction
|- {R} Cn {Q} by induction
|- {P} C1 ; ... ; Cn-1 ; Cn {Q} by the sequencing rule
-
sequences {P} C1 ; ... ; Cn-1 ; V:=E {Q}
-
-
the verification conditions generated by {P} C1 ; ... ; Cn-1 {Q[E/V]}
-
-
justification
-
|- {P}C1 ; ... ; Cn-1 ; {Q[E/V]} by induction
|- {Q[E/V}]} V:=E {Q} by the assignment axiom
|- {P}C1 ; ... ; Cn-1 ; V:=E {Q} by the sequencing rule
-
blocks {P} begin var V1 ; ... ; var Vn ; C end {Q}
-
-
the verification conditions generated by {P} C {Q}
-
the syntactic condition that none of V1 ... V n are in either P or Q
-
-
justification
-
|- {P} C {Q} by induction
|- {P} begin var V1 ; ... ; var Vn ; C end {Q} by the block rule
-
while commands {P} while S do {R} C {Q}
-
-
P => R
-
R /\ ¬S => Q
-
the verification conditions generated by {R /\ S} C {R}
-
-
justification
-
|- {R /\ S} C {R} by induction
|- {R} while S do C {R /\ ¬S} by the while rule
|- {R} while S do C {Q} by the consequence rules
-
for commands {P} for V:= E1 until E2 do {R} C {Q}
-
-
P => R[E1/V]
-
R[E2 + 1/V] => Q
-
P /\ E2 < E1 => Q
-
the verification conditions generated by {R /\ E1 <= V /\ V <= E2} C {R[V+1/V]}
-
the syntactic condition that C doesn't assign to V or any variable occurring in E1or E2
-
-
justification: long and probably unnecessary
-
Total correctness
-
deriving from Floyd Hoare logic
|- {P} C {Q}
---------------
|- [P] C [Q]
if C contains no while commands
-
termination of assignments
-
assumes all function calls/applications in the expression on the RHS terminate
-
that might not be true if
-
the functions are defined recursively
-
erroneous expressions like 1/0 cause problems
-
-
-
[T] X := 1/0 [X = 1/0] is valid, hence
-
1/0 is some number
-
1/0 is some error value
-
-
if have error values have messy laws of arithmetic
-
assume that arithmetic expressions always denote numbers
-
in some cases exactly what number is not specified
-
-
¬(n=0) => (m/n)*n = m
-
while rule for total correctness
-
can use if can prove some non-negative quantity (the variant) decreases with each iteration
-
|- [P /\ S /\ (E=n)] C [P /\ (E<n)] |- P /\ S => 0 <= E
---------------------------------------------------------------------------
|-[P] while S do C [P /\ ¬S]
where
E is the variant (an integer valued expression)
n is an auxiliary variable not occurring in P, C, S or E use to ensure the variant is decreasing
-
-
e.g. |- [Y>0] while Y<=R do begin R:=R-Y; Q:=Q+1 end [T]
-
where
P = Y > 0
S = Y <= R
E = R
C = begin R:=R-Y; Q:=Q+1 end
-
termination specification
-
formalisation of total correctness = partial correctness + termination
-
|- {P} C {Q} |- [P] C [T]
----------------------------------------
|- [P] C [Q]
|- [P] C [Q]
----------------------------------------
|- {P} C {Q} |- [P] C [T]
Verifications conditions for total correctness
-
while commands
-
need to add a variant as an annotation
-
[P] while S do {R}[E] C [Q]
-
R is the invariant: needs to be true each time control reaches it
-
E is the variant
-
-
conditions
-
P => R
-
R /\ ¬S => Q
-
R /\ S => E >= 0
-
the verification conditions generated by [R /\ S /\ (E=n)] C [R /\ (E<n)]
-
the condition that n is a ghost variable not occurring in P, C, S, R, E or Q
-
-
-
other commands
-
exactly the same as partial correctness but with square brackets instead of curly ones
-
Program refinement
-
specifications
-
programming constructs
-
same role as commands
-
not directly executable
-
guaranteed to achieve a given postcondition from a given precondition
-
specification is identified with its set of possible implementations
-
[P, Q] = { C |- [P] C [Q] }
-
-
-
refinement represented as manipulations on sets of ordinary commands
-
a refinement has the form
-

-
each intermediate state is obtained from its predecessor by application of a refinement law
-
-
wide spectrum language: generalised programming language containing
-
pure specifications
-
pure code
-
mixtures of the two
-
-
refinement laws
-
general method of generation for [P, Q] ⊇ ζ C0 ... Cn
-
where
-
words in blue are sets of programs
-
words in green are programs
-
ζ is the generalisation of a construct using programs or sets of programs
-
one such construct is while S do C (which is a program itself)
-
another is if then [P/\S, Q] else [P/\¬S, Q] (which is a set of programs itself)
-
-
-
-
-
-
-
define ζ C0 ... Cn = {ζ C0 ... Cn| C0
C0 ... Cn
Cn}-
e.g. if S then [P /\ S, Q] else [P /\ ¬S, Q] = {if S then C0 else C1 | C0
[P /\ S, Q] /\ C1
[P /\ ¬S, Q]} -
C0 is equivalent to [P0, Q0], for appropriate P0 and Q0
-
-
assume C
ζ C0 ... Cn-
where C is a ζ C0 ... Cn
-
-
deduce C
{ζ C0 ... Cn| C0
[P0, Q0] ... Cn
[Pn, Qn]} (by 1.ii) -
deduce C
{ζ C0 ... Cn| |- [P0] C0 [Q0] ... |- [Pn] Cn [Qn] }-
as Ci
[Pi, Qi] <=> |- [Pi] Ci [Qi]
-
-
use a Hoare Logic rule to combine the [Pi] Ci [Qi] to get
-
-
C
{ζ C0 ... Cn| |- [P] ζ C0 ... Cn [Q] }
-
-
-
deduce C
[P, Q]-
by definition of [P, Q]
-
-
-
-
-
-
logically
-
-
C
ζ C0 ... Cn = {ζ C0 ... Cn| C0
C0 ... Cn
Cn}
<=> C
{ζ C0 ... Cn| C0
[P0, Q0] ... Cn
[Pn, Qn]}
<=> C
{ζ C0 ... Cn| |- [P0] C0 [Q0] ... |- [Pn] Cn [Qn] }
=> C
{ζ C0 ... Cn| |- [P] ζ C0 ... Cn [Q] }
(one way as Hoare rules are not iff)
<=> C
[P, Q]
-
-
our wide spectrum language
-
C0 ; ... ; Cn = {C0 ; ... ; Cn| C0
C0 /\ ... /\ Cn
Cn}
begin var V1 ; ... Vn ; C end = {begin var V1 ; ... Vn ; C end | C
C }
if S then C = {if S then C | C
C}
if S then C1 else C2 = {if S then C1 else C2 | C1
C1 /\ C2
C2}
while S do C = {while S do C | C
C}
-
-
-
NB these wide spectrum commands are monotonic, i.e.
-
if C0 ⊇ C'0 /\ ... /\ Cn ⊇ C'n then C0 ; ... ; Cn ⊇ C'0 ; ... ; C'n
-
-
-
laws
-
skip law: [P, P] ⊇ {skip}
-
assignment law: [P[E/V]] ⊇ {V:=E}
-
derived assignment law: [P, Q] ⊇ {V:=E}
-
if |- P => Q[E/V]
-
-
precondition weakening: [P, Q] ⊇ [R, Q]
-
if |- P => R
-
-
postcondition strengthening: [P, Q] ⊇ [P, R]
-
if |- R => Q
-
-
sequencing law: [P, Q] ⊇ [P, R] ; [R, Q]
-
block law: [P, Q] ⊇ begin var V; [P, Q] end
-
where V does not occur in P or Q
-
-
one armed conditional law: [P, Q] ⊇ if S then [P /\ S, Q]
-
if |- P /\ ¬S => Q
-
-
two armed conditional law: [P, Q] ⊇ if S then [P /\ S, Q] else [P /\ ¬S, Q]
-
while law: [P, P /\ ¬S] ⊇ while S do [P /\ S /\ (E=n), P /\ (E<n)]
-
if |- P /\ S => E >= 0
-
-
-
First order logic
-
notation
-
predicates P(x)
-
¬t
-
t1 /\ t2
-
t1 \/ t2
-
t1 => t2
-
t1 /\ t2
-
∀x . t[x]
-
∃x . t[x]
-
∃!x . t[x] (unique x s.t. t[x])
-
-
statements and formulae regarded as boolean valued (i.e. cannot return a function, etc.)
-
t[x] is just a boolean term containing variable x
-
Higher order logic (HOL)
-
terms (notation)
-
variables
-
range over functions and predicates
-
e.g. x, y, P
-
-
constants
-
stand for fixed values
-
e.g. T, F, 0, 1, ... , +, *, <, <=, ∀, ∃
-
-
function applications
-
t1 t2
-
binary function constants can be infixed (i.e. write t1 + t2 instead of + t1 t2)
-
-
abstractions (lambda terms)
-
denote functions
-
λx . t
-
where x is a variable
-
-
-
-
allows higher order variables
-
e.g. ∀P . [ P(0) /\ (∀n . P(n) => P(n+1)) => ∀n . P(n) ] for induction on natural numbers
-
-
types
-
NB Russell's paradox: P x = ¬(x x) => P P = ¬(P P)
-
types are expressions which denote sets of values
-
atomic types
-
bool
-
ind – individuals
-
num – natural numbers
-
real
-
-
compound types: built from other types
-
if σ, σ1 and σ2 are types then the following are types
-
σ list
-
σ1 -> σ2
-
σ1 x σ2
-
-
-
terms of HOL must be well typed
-
it must be possible to assign a type to each subterm s.t. The following hold
-
-
-
-
-
for every application subterm t1 t2 there are types σ1, σ2 s.t.
-
-
-
-
-
t1 is σ1 -> σ2
-
t2 is σ1
-
t1 t2 is σ2
-
-
-
-
-
for every lambda subterm λx . t there are types σ1, σ2 s.t.
-
-
-
-
-
x is σ1
-
t is σ2
-
λx.t is σ1 -> σ2
-
-
-
-
variables of the same name can be assigned different types, e.g. x:σ1 , x:σ2
-
if they are then they are considered to be separate variables
-
-
polymorphic types
-
type variable
-
can instantiate the type variable with any type
-
e.g. twice = λf. λx. f(f(x)): (α -> α) -> α -> α
-
-
a type which contains a type variable is a polymorphic type
-
otherwise called monomorphic
-
-
-
-
binders: used to syntactic sugar
-
write f x.t instead of f ( λx.t)
-
with quantifiers
-
-
-
-
∀ ∃ are constants
-
∀x.t considered 'user friendly syntax' for ∀ (λx.t), i.e. a function applied to ∀
-
∀ : (α -> bool) -> bool
-
-
-
pairs and tuples
-
(t1, t2) : σ1 x σ2 if t1:σ1 /\ t2:σ2
-
(t1, t2, ... , tn) is an abbreviation for ((...(t1, t2) , ... ), tn)
-
-
lists
-
type σ list
-
nil: σ list
-
cons: σ -> σ list -> σ list
-
-
conditionals
-
cond t t1 t2 = (t -> t1 | t2) = “if t then t1 else t2)
-
-
Hilbert's ε operator
-
ε x. t[x]
-
where
x: σ
t[x]:bool (is a boolean term containing free variable x)
-
-
-
denotes some value of type σ which satisfies t[a] (makes it true)
-
if there is no x:σ which satisfies t[x] then it denotes some fixed unspecified value of type σ
-
-
e.g. ε x. x=2 would be 2
-
e.g. ε x. x<2 would be some unspecified number which is less than 2
-
can define several other things usually taken to be primitive using ε-operator
-
conditionals
-
cond t t1 t2 = ε x. ((t = T) => (x = t1)) /\ ((t = F) => (x = t2))
-
-
-
= ε x. (¬(t = T) \/ (x = t1)) /\ (¬(t = F) \/ (x = t2))
-
-
-
lambda abstraction
-
λx.t = ε f.∀x. f(x) = t
-
-
-
assuming variable f does not occur in the term t
-
-
-
-
“the function f such that f(x) = t for all x”
-
e.g. fact n = ε f.∀n. (f(0) = 1) /\ (f(n+1) = (n+1)*f(n))
-
-
-
-
Axiom of choice
-
set theory
-
if S is a set of non-empty sets, for each non empty X
S then there exists a function Choose s.t. choose(X)
X -
inclusion of Hilbert's ε operator automatically builds this in
-
details
-
sets are not primitive in HOL
-
instead use characteristic functions such as fX
-
maps from items to {T, F}
-
fX(x) = T iff x
X
-
-
have to work on characteristic functions rather than directly on sets
-
in HOL
-
assert there is a function Select s.t. P(Select(P)) = T
-
assuming P is the characteristic function of a non-empty set
-
-
hence Select P = Choose {x | P x = T}
-
they aren't the same as Choose works on sets and Select on characteristic functions
-
-
-
-
ε is a binder for Select s.t. ε: (σ -> bool) -> σ, so if P:(σ -> bool)
-
ε(P) denotes some fixed (but unknown) value x s.t. P(x) = T
-
if no such value exists (i.e.∀x . P(x) = F)then ε(P) denotes some unspecified value in the set, denoted by σ
-
-
-
definitions
-
definitions are axioms of the form
|- c = t
-
where c is a new constant
t is a closed term that doesn't contain c
-
-
function definitions
|- f = λ x1 ... xn . t
can be written as
|- f x1 ... xn = t
-
adding a new definition to the set of existing ones cannot introduce any new inconsistencies
-
-
natural numbers
-
introduce
-
a type num
-
constant 0 : num
-
Suc : num -> num
-
-
postulate Peano's Axioms
-
-
-
there is a number 0
-
there is a function Suc called the successor function s.t. if n is a number then Suc n is a number
-
0 is not the successor of any other number
-
-
-
axiomatic formalisation: |- ∀m . ¬(Suc m = 0)
-
-
-
if two numbers have the same successor then they are equal
-
-
axiomatic formalisation: |- ∀m n . (Suc m = Suc n) => (m = n)
-
-
if a property holds of 0 and if whenever it holds of a number then it also holds of the successor of the number then the property holds of all numbers (mathematical induction)
-
-
axiomatic formalisation:
|- ∀P: num -> bool . [P 0 /\ (∀m . P m => P(Suc m))] => ∀m. P m
-
primitive recursion
-
-
basic primitive recursive functions are given by these axioms:
-
-
Constant function: The 0-ary constant function 0 is primitive recursive.
-
Successor function: The 1-ary successor function Suc is primitive recursive.
-
Projection function: ∀n i . n≥1 /\ 1≤i≤n the n-ary projection function Pin, which returns its i-th argument, is primitive recursive.
-
-
complex primitive recursive functions are obtained by applying the operators given by the axioms:
-
-
Composition:
-
-
given
-
f, a k-ary primitive recursive function
-
k m-ary primitive recursive functions g1,...,gk
-
-
the composition of f with g1,...,gk,
h(x1,...,xm) = f(g1(x1,...,xm),...,gk(x1,...,xm))
is primitive recursive
-
-
Primitive recursion:
-
-
given
-
f, a k-ary primitive recursive function
-
g, a (k+2)-ary primitive recursive function
-
-
the (k+1)-ary function defined as the primitive recursion of f and g
h(0,x1,...,xk) = f(x1,...,xk)
h(S(n),x1,...,xk) = g(h(n,x1,...,xk),n,x1,...,xk)
is primitive recursive.
-
-
-
to enable recursive 'definitions' (e.g. |- 0 + m = m) use Primitive Recursive Theorem
|- ∀x:α . ∀f :α -> num -> α . ∃fun: num->α . (fun 0 = x) /\ (∀m . fun(Suc m) = f (fun m ) m
-
e.g. for +
-
specialise x to be λn.n
-
specialise f to be λf' x' . λn . Suc(f' n)
-
this yields
-
-
|- ∃fun . (fun 0 = x) /\ [m . fun(Suc m) = λf' x' . λn . Suc (f' n) (fun m) m]
<=> |- ∃fun . (fun 0 = x) /\ [fun (Suc m) n = Suc(fun m n)]
FINISH THIS LATER – probably not the most important thing in the world
-
semantics
-
types denote sets
-
terms denote members of these sets
-
if t:σ then we can consider t to denote a member of the set denote by σ
-
type schemes
-
a type scheme σ includes type variables α1, ... αn
-
is not a set (a type) but a class function
-
it is a function from m tuples of sets to sets
-
e.g. if γi represents an arbitrary set (with no type variables) a type scheme represents the relation
γ1 x γ2 x ... x γn x γ'
-
-
e.g. α → α is the class function (relation) such that
-
maps type γ to the set of functions of type γ → γ
γ |→ {f | f : γ → γ}
-
-
-
polymorphic constants
-
these are interpreted as the functions of the interpretations of the type variables in their type
-
e.g. Id: α→α is the function from a type to the identity function on that type
-
-
Semantic embedding
wff ::= True | N wff | C wff wff | D wff wff
-
deep embedding
-
represent wff inside the host logic by values of some type (e.g. twff)
-
define a semantic function in the host logic
-
NB, M : twff
M(True) = T
M(N w) = ¬M(w)
M(C w1 w2) = M(w1) /\ M(w2)
M(D w1 w2) = M(w1) \/ M(w2)
-
-
theorems about the embedded language can be proved, e.g.
∀w1, w2
wff . M(C w1 w2) = M(N (D (N w1) (N w2)))
-
-
shallow embedding
-
set up notational conventions for translating wff's into terms in the host logic
-
the translation is not defined in terms of the host logic
-
-
is “parsing and pretty printing”
[[True]] ~~> “T”
[[N w]] ~~> “¬” concat [[w]]
[[C w1 w2]] ~~> [[w1]] concat “/\” concat [[w2]]
[[D w1 w2]] ~~> [[w1]] concat “\/” concat [[w2]]
-
quantification over wff's is not possible
-
hence only theorems in the embedded language can be be proved
-
-
-
we want to do this for Floyd Hoare logic because then we can derive the logic rules and guarantee their soundness
A shallow embedding of Hoare specifications
E ::= N | V | E1 + E2 | E1 – E2 | E1 x E2 | ...
B ::= E1 = E2 | E1 <= E2
C ::= skip
| V := E
| C1 ; C2
| if B then C1 else C2
| while B do C
where
V ranges over program variables
etc.
-
axioms
|- {P} skip {P} (skip)
|- {P[E/V]} V := E {P} (assignment)
-
rules
|- P => P' |- {P'} C {Q}
--------------------------------- (precondition strengthening)
|- {P} C {Q}
|- {P} C {Q'} |- Q' => Q
--------------------------------------- (postcondition weakening)
|- {P} C {Q}
-
-
NB, the above have theorems in Hoare logic and in pure logic
-
Hoare logic proofs may require pure logic sub-proofs
-
-
|- {P} C1 {Q} |- {Q} C2{R}
------------------------------------------ (sequencing)
|- {P} C1; C2 {R}
|- {P /\ B} C1 {Q} |- {P /\ ¬B} C2 {Q}
------------------------------------------------- (if)
|- {P} if B then C1 else C2 {Q}
|- {P /\ B} C {P}
----------------------------------- (while)
|- {P} while B do C {P /\ ¬ B}
-
-
NB, the above assumes that B is a boolean expression in the language and a formula of the predicate logic
-
-
semantics
-
define Meaning : Commands x States x States
-
this is a partial function, as some things don't terminate, e.g.
Meaning(while T do C)(s) is undefined
-
-
we really do need total functions to do predicate calculus, so instead define c to denote (provide the meaning for) a given Ci as:
-
c(s1, s2) = { T if Meaning(Ci)(S1) = s2
{F otherwise
-
states
-
define the type state = string → num
-
e.g. s = λx . (x = 'X' → 1 | (x = 'Y' → 2 | (x = 'Z' → 3)))
-
-
other denotations: the meanings for E, B and C are provided as
-
e : state → num (this is expressions)
-
b : state → num (this is booleans)
-
c : state x state → bool (this is the c defined previously)
-
-
in the abstract define [[ ... ]] as
[[ T[X1, ... , Xn] ]] = λs . [[ T[s 'X1', ... , s 'Xn'] ]]
where T[X1, ... , Xn] is a higher order logic term
T:σ
[[T]]: state → σ
-
we can define the translation [[ ... ]]
-
where Skip(s1, s2) = (s1 = s2)
-
-
-
-
-
-
-
(e s) means evaluating expression e in environment s
v is representing something like 'A' so this λx is creating a state
remember the conventions from earlier
-
-
-
-
-
-
where Assign(v, e)(s1, s2) = (s2 = Bnd(e, v, s1))
Bnd(e, v, s) = λx . (x = v → (e s) | s x)
where Seq(c1, c2)(s1, s2) =

where if(b, c1, c2)(s1, s2) = b s1 → c1(s1, s2) | c2(s1, s2)
-
[[skip]] ~~> Skip
[[V := E]] ~~> Assign('V', [[ E ]] )
[[C1 ; C2]] ~~> Seq( [[C1]], [[C2]] )
[[if B then C1 else C2]] ~~> If([[B]],[[C1]],[[C2]])
[[while B do C]] ~~> While([[B]], [[C]])
where while(b, c)(s1, s2) =

Iter(0)(b, c)(s1, s2) = F
Iter(n+1)(b, c)(s1, s2) = If(b, Seq(c, Iter(n)(b, c)), Skip)(s1, s2)
-
-
-
NB, that sometimes logical variables must be included in pre and post conditions along with program variables
-
e.g. {X = x, Y = y}
-
define [[ ... ]] such that
[[X = x /\ Y = y]] ~~> λs . (s 'X') = x /\ (s 'Y') = y
-
[[ X ]] ~~> λs . s 'X' i.e. apply 'X' to the state, and get a number back
[[ E1 + E2 ]] ~~> [[ E1 ]] + [[ E2 ]]
etc.
-
-
semantics of partial correctness specifications
-
a partial correctness specification {P} C {Q} denotes
. [[P]] s1 /\ [[C]](s1, s2) => [[Q]] s2 -
Spec(p, c, q) =
. [[P]] s1 /\ [[C]](s1, s2) => [[Q]] s2 -
e.g.
{X = 1} X := X + 1 {X = 2}
denotes
Spec(λs . S 'X' = 1, Assign('X', λs . s 'X' + 1), λs . s 'X' = 2)
-
-
Floyd-Hoare logic as higher order logic
-
theorems
|- ∀p . Spec(p, Skip, p)
|- ∀p v e. Spec((λs.p(Bnd(e, v, s))), Assign(v, e), p)
|- ∀p p' q c . ((∀s. p' s => p s) /\ Spec(p, c, q)) => Spec(p, c, q')
|- ∀p q q' c. (Spec(p, c, q) /\ (∀s. q s => s' s)) => Spec(p, c, q')
|- ∀p q r c1 c2 . Spec((λs . P s /\ b s), c1, q) /\ Spec((λs. P s /\ ¬(b s)), c2, q)
=> Spec(p, If(b, c1, c2), q)
|- ∀p c b . Spec((λs . P s /\ b s), c, p) => Spec(p, while((b, c), (λs. P s /\ ¬(b s)))
-
the theorems can be derived from the previous definitions of Skip etc.
-
the axioms and rules for Floyd-Hoare logic can then be derived
-
-
termination and total correctness
-
since here Hoare logic is presented as a derived logic it's easy to add extensions and modifications without fear of introducing unsoundness
-
e.g. termination assertion
-
note that this means some computation of C starting from state satisfying P will terminate
Halts(p, c) = ∀s1 . P s1 =>
s2 . c(s1, s2) -
-
if commands are deterministic
Det(c) = ∀s s1 s2 . c(s, s1) /\ c(s, s2 ) => s1 = s2
then termination is adequately formalised by halts, hence
Total_spec(p, c, q) = Halts(p, c) /\ Spec(p, c, q)
-
theorems can be proven
-
-
i.e. if there is a variant x which decreases each time then the loop will terminate
-
|- ∀p. Halts(p, skip)
|- ∀p v e . Halts(p, Assign(v, e))
|- ∀p p' c . (∀s . p' s => p s) /\ Halts(p, c) => Halts(p', c)
|- ∀p c1 c2 q . Halts(p, c1) /\ Spec(p, c1, q) /\ Halts(q, c2) => Halts(p, Seq(c1, c2))
|- ∀p c1 c2 b . Halts(p, c1) /\ Halts (p, c2) => Halts(p, If(b, c1, c2))
|- ∀b c x . (∀n . Spec (λs . p s /\ b s /\ s x = n, c, λs . p s /\ s x < n))
/\ Halts (λs . p s /\ b s, c) => Halts(p, While(b, c))
-
-
Other programming logic constructs
-
Vienna development method
-
is a variation on Hoare style specifications
-
reduces the need for auxiliary logical variables
-
e.g.
{X = x /\ Y = y} R := X; X := Y; Y := R {X = y /\ Y = x}
is written as
{T} R := X; X := Y; Y := R {X =
/\ Y =
} (actually like /_ rather than _ ) -
option for defining semantics one
-
more generally
{P[X1 , ... , Xn]} C {Q[X1 , ... , Xn,
,
]}can be thought as an abbreviation for
{P[X1 , ... , Xn] /\ X1 =
... /\ Xn =
} C {Q[X1 , ... , Xn,
,
]} -
this has problems with deducing sequences, e.g.
{T} X := X + 1 ; X := X + 1 {X =
+ 2}being derived from
{T} X := X + 1 {X =
+ 1}
-
-
-
-
option for defining semantics two
-
regard
{P[X1 , ... , Xn]} C {Q[X1 , ... , Xn,
,
]}as
VDM_Spec ([[ P[X1 , ... , Xn] ]], [[C]], [[ Q[X1 , ... , Xn,
,
] ]]2)where
[[ Q[X1 , ... , Xn,
,
] ]]2 = λ(s1, s2) . Q[s2 'X1' , ... , s2 'Xn', s1 'X1', ... , s1 'Xn']
-
-
Aczel's version of the while rule
-
|- {P /\ B} X {P /\ R}
---------------------------------------
|- while B do C {P /\ ¬b /\ R*}
-
-
-
this version of the while rule can be converted into a rule of total correctness by requiring R to be
-
transitive
-
transitive r =
-
-
well founded
Well founded r = ¬
: num→state .∀n . r(f(n), f(n+1))
-
where R* is the reflexive closure of R
R*(s1, s2) =

R0 = λ(s1, s2) . s1 = s2
Rn+1 = Seq(R, Rn)
-
-
-
Dijkstra's weakest preconditions
-
this is used for rigorous program construction
-
not really for post hoc verification
-
-
weakest predicates
-
given p and q as predicates on states (have type state→bool)
-
p is weaker (less constraining) than q if
∀s . q s => p s
-
is written p <= q
-
weakest P = εp . P p /\ ∀p' . P p' => (p <= p')
-
-
-
-
weakest liberal preconditions
-
wlp(C, Q) = the weakest predicate P such that {P} C {Q}
-
wlp(c, q) = weakest (λp . Spec(p, c, q))
-
however it is easier to work with the following (which can be shown from the above)
|- wlp(c, q) = λs. (∀s' . c(s, s') => q s')
-
-


how do you show it? I don't understand why they're
the same, i.e. I don't see the “weakest” requirement in the second
-
-
weakest preconditions
-
wp(C, Q) = the weakest predicate P such that [P] C [Q]
-
wp(c, q) = weakest (λp . Total_Spec(p, c, q))
-
however it is easier to work with the following (which can be shown from the above)
|- wp(c, q) = λs. ((
s' . c(s, s')) /\ ∀s' . c(s, s') => q s')
-
-
-
-
relation to Hoare notation
|- Spec(p, c, q) = ∀s . ( s => wlp(c,q) s)
|- Total_Spec(p, c, q) = ∀s . ( s => wp(c,q) s)
-
if we redefine /\, \/, =>, ¬, T, F and |= so that they can operate on predicates, i.e.
p /\ q = λs . p s /\ q s
p \/ q = λs . p s \/ q s
p => q = λs . p s => q s
¬p = λs . ¬p s
T = λs . T
F = λs . F
|= p = ∀s . p s NB. See this used below
-
we can then (re)write the following theorems
|- Spec(p,c,q) = ∀s . (p => wlp(c, q)) s
= |= p => wlp(c, q)
|- Total_Spec(p,c,q) = ∀s . (p => wp(c, q)) s
= |= p => wp(c, q)
|- ∀c . |= wp(c, f) = F
|- ∀q r c . |= (q => r) => (wp(c, q) => wp(c, r))
|- ∀q r c . |= (wp(c, q) /\ wp(c, r) = wp(c, q /\ r)
|- ∀q r c . |= (wp(c, q) \/ wp(c, r) => wp(c, q \/ r))
|- ∀q r c . Det c => (|= (wp(c, q) \/ wp(c, r) = wp(c, q \/ r)))
-
-
-
|- ∀c . |= wlp(c, f) = λs. ¬
s'.c(s, s')|- ∀q r c . |= (q => r) => (wlp(c, q) => wlp(c, r))
|- ∀q r c . |= (wlp(c, q) /\ wlp(c, r) = wlp(c, q /\ r)
|- ∀q r c . |= (wlp(c, q) \/ wlp(c, r) => wlp(c, q \/ r))
|- ∀q r c . Det c => (|= (wlp(c, q) \/ wlp(c, r) = wlp(c, q \/ r)))
-
inadequacy: sequences
-
attempting to derive Dijkstra's wp law for sequences
-
...
-
-
-
Dynamic logic
-
this is a programming logic which emphasizes an analogy between Hoare logic and modal logic
-
in dynamic logic states of computation are thought of as possible worlds
-
if a command C transforms an initial state s to a final state s' then s' is considered accessible from s
-
-
traditional modal logic would have [] and <> with the interpretations
[]q is true (valid) in s if q is true in all states accessible from s
<>q is true (valid) in s if ¬[]¬q is true in s
-
however in dynamic logic we have [C] and <C> for each command C
-
where ¬ is negation defined on predicates, as above
[c]q = λs. (∀s' . c(s, s') => q s')
<c>q = ¬([c] (¬q))
-
-
example theorems in modal logic are
|- ∀c q . Det c => (|= <c>q => [c]q)
|- (|= [c] q) = Spec(T, c, q)
|- Det c => (|= <c>q) = Total_Spec(T, c, q)
|- Spec(p, c, q) = (|= p => [c]q)
|- Det c => (Total_Spec(p, c, q) = (|= [ => <c> q))
-
-
-
-
|- Det c => (wp(c, q) = <c> q)
|- wlp(c, q) = [c] q
-
-