Folien der achten Vorlesung

Werbung
Dynamische Logik
A. Salwicki 1970 (“algorithmische Logik”),
V.R. Pratt 1976, D. Harel 1977
Idee: Erfinde nicht einfach einen komplett neuen Kalkül für neue Objekte
(hier: Hoare-Tripel) sondern bilde Programmformeln und erweitere Kalkül
um diese neuen Formeln.
191
Dynamische Logik: Syntax
Die Menge DLFor(Σ,X ) der DL-Formeln über der Signatur Σ und der
Variablenmenge X enthält
• Alle prädikatenlogischen Formeln von For(Σ,X )
• Für α ∈ DLProg(Σ,X ) und ψ ∈ DLFor(Σ,X ) auch
⋆ [α]ϕ
(„box alpha phi“)
Informelle Bedeutung: „Wenn α terminiert, gilt nachher ϕ“
⋆ hαiϕ („diamond alpha phi“)
Informelle Bedeutung: „α hält, und nachher gilt ϕ“
Klassifikation:
• Dynamische Logik ist eine (Multi-)Modallogik.
• Modallogik mit Formeln ⋄ ϕ (und 2 ϕ) stammen aus der Philosophie:
“ich weiss ϕ” “ich glaube ϕ”, “ϕ ist möglich”
• Temporallogik: “irgendwann wird ϕ wahr sein”
192
Bemerkungen zur Syntax
• Boxen und Diamonds binden stärker als Aussagenlogik:
[α]ψ ∧ χ bedeutet ([α]ψ ) ∧ χ
• Schachtelung möglich:
hαihβiψ bedeutet hαi ( hβiψ )
• Auch gemischte Schachtelung ist erlaubt: [α] ∀ x. ϕ ∧ hβiψ
• Auch in DL betrachtet man Sequenzen, z.B. hαiψ, Γ ⊢ ∆.
• Wenn x in α vorkommt, x0 aber nicht, so besagt
hαi x = x0 : “Am Ende von α hat x den Wert, den x0 jetzt hat”
• hαi x = x0 → hβi x = x0 bedeutet:
“Wann immer α in x einen Wert x0 ausrechnet so tut das auch β ”
Man kann in DL also über Programmähnlichkeit reden
• Es gilt: [α]ψ ↔ ¬ hαi¬ ψ
Man könnte also Box mit Hilfe von Diamond definieren
193
Semantik der Dynamischen Logik
Die Semantik von Formeln der Prädikatenlogik wird erweitert durch
A, v |= [α]ψ
A, v |= hαiψ
:⇔
:⇔
für alle w : (v, w) ∈ [[α]] ⇒ A, w |= ψ .
es gibt w : (v, w) ∈ [[α]] und A, w |= ψ .
Damit gilt
A, v |= ϕ{α}ψ
A, v |= ϕhαi ψ
⇔
⇔
A, v |= ϕ → [α]ψ
A, v |= ϕ → hαiψ ,
Hoare-Tripel sind also spezielle Formeln der Dynamischen Logik.
194
Dynamische Logik: Beispiele
|= [x := 1] x = 1
|= x = x0 → [x := x + 1] x = x0 + 1
|= x = x0 → hx := x + 1i x = x0 + 1
Unterschied Box/Diamond:
|= x = 5 → [abort] x = 5
6|= x = 5 → haborti x = 5
Sequenzen mit Programmen, Programme auch im Antezedent:
|= x = x0 ⊢ [x := x + 1] x = x0 + 1
|= x > 0 ⊢ hwhile x > 1 do x := x -1i x = 1
|= hif x > 0 then y := 1 else aborti y = y0 ⊢ y0 = 1 ∧ x > 0
|= [if x > 0 then y := 1 else abort] y = y0 ⊢ y0 = 1 ∧ x > 0 ∨ x = 0
195
Kalkül für Dynamische Logik
Grundidee: Symbolische Ausführung von Programmen
Betrachte Ausführung des Programms
α1 ; α2 ; . . . ; αn (αi ohne ; (compound) auf top-level)
• Annahme: Initialer Zustand erfüllt Vorbedingung ϕ0
• Berechne ϕ1 , die stärkste Formel, die nach α1 gilt
• Berechne ϕ2 , die stärkste Formel, die nach α1 ; α2 gilt
• . . . solange bis das Programm verschwunden ist
• Zum Schluss: Teste ob ϕn die Nachbedingung impliziert (nur noch PL)
• Gegenüber Hoare-Kalkül: kein Raten von Zwischenformeln bei seq.
Ausführung, Zuweisung vorwärts ausführen!
• Symb. Ausführung geht sowohl im Antezedent als auch im Sukzedent!
196
DL-Kalkül: Normalisierung
normalize right
Γ ⊢ hαihβiψ, ∆
Γ ⊢ hα; βiψ, ∆
Γ ⊢ [α][β]ψ, ∆
Γ ⊢ [α; β]ψ, ∆
hαihβiψ, Γ ⊢ ∆
hα; βiψ, Γ ⊢ ∆
[α][β]ψ, Γ ⊢ ∆
[α; β]ψ, Γ ⊢ ∆
normalize left
In KIV: Nie explizit angewandt, die erste Anweisung wird bei jeder
Regelanwendung (in allen Programmformeln) immer automatisch
abgespalten. KIV rotiert immer die Programmformeln nach vorne.
197
DL-Kalkül: Zuweisungen (1)
assign right (Hoare-Regel in DL)
Γ ⊢ ψxτ , ∆
Γ ⊢ [x := τ ]ψ, ∆
assign right (Dynamische Logik)
′
′
Γ, x′ = τ ⊢ [αxx ]ψxx , ∆
Γ ⊢ [x := τ ][α]ψ, ∆
wobei x′ eine neue Variable ist (bezeichnet den neuen Wert von x)
Beachte: Programmvariablen werden umbenannt!
198
DL-Kalkül: Zuweisungen (2)
• In KIV: assign right kombiniert beide Regeln:
⋆ Original-Hoare-Regel, falls nach der Zuweisung ψ kein Programm
mehr enthält
⋆ Sonst die DL Regel
• Die Regel gilt genau gleich auch für Diamonds statt Boxen
• Die Regel für Zuweisung auf der linken Seite sieht genauso aus:
assign left
′
′
x′ = τ, [αxx ]ψxx , Γ ⊢ ∆
[x := τ ][α]ψ, Γ ⊢ ∆
wobei x′ eine neue Variable ist
199
DL-Kalkül: if
Der Kalkül der dynamischen Logik besteht u. a. aus den Regeln des
Hoare-Kalküls, wie z. B.
if right
Γ, ε ⊢ [α]ψ, ∆ Γ, ¬ ε ⊢ [β]ψ, ∆
Γ ⊢ [if ε then α else β]ψ, ∆
if positive right
Γ, ε ⊢ [α]ψ, ∆ Γ ⊢ ε, ∆
Γ ⊢ [if ε then α else β]ψ, ∆
Analog: if negative right, if left etc.. KIV versucht immer die Tests zu
entscheiden (per Simplifier), damit nur eine Prämisse entsteht.
200
DL-Kalkül: while
invariant right
Γ ⊢ INV , ∆
INV , ε ⊢ [α]INV
INV , ¬ ε ⊢ ψ
Γ ⊢ [while ε do α]ψ, ∆
invariant right
Γ ⊢ INV , ∆
INV , ε, v = t ⊢ hαi(INV ∧ t ≪ v)
Γ ⊢ hwhile ε do αiψ, ∆
INV , ¬ ε ⊢ ψ
KIV: Die Schleifeninvariante INV und im zweiten Fall auch die Schranke t
muss von Hand eingegeben werden (v ist neue Variable)
201
DL-Kalkül: abort
abort right
Γ⊢∆
Γ ⊢ habortiψ, ∆
Γ ⊢ [abort]ψ, ∆
habortiψ, Γ ⊢ ∆
Γ⊢∆
[abort]ψ, Γ ⊢ ∆
abort left
202
DL-Kalkül: skip
skip right
Γ ⊢ ψ, ∆
Γ ⊢ hskipiψ, ∆
Γ ⊢ ψ, ∆
Γ ⊢ [skip]ψ, ∆
ψ, Γ ⊢ ∆
hskipiψ, Γ ⊢ ∆
ψ, Γ ⊢ ∆
[skip]ψ, Γ ⊢ ∆
skip left
203
DL-Kalkül: lokale Variablen
vardecls right
x′
hαx iϕ
′
x = τ, Γ ⊢
Γ ⊢ hlet x = τ in αiϕ
vardecls left
x′
hαx iϕ, x′
= τ, Γ ⊢ ∆
hlet x = τ in αiϕ, Γ ⊢ ∆
x′ sind neue Variablen (bezeichnen die lokalen Variablen). Dieselbe Regel
auch für Boxen.
204
Beispiel zur Korrekheit
Liste nat. Zahlen x:
m := x.first, x0 := x.rest;
while x0 6= [] do
{
if x0.first > m then m := x0.first ;
x0 := x0.rest
}
205
Beispiel zur Korrekheit
Maximum einer nichtleeren Liste nat. Zahlen x:
x 6= []
⊢h
m := x.first, x0 := x.rest;
while x0 6= [] do
{
if x0.first > m then m := x0.first ;
x0 := x0.rest
}
i
m = maxl(x)
wobei maxl([]) = 0, maxl(n + x) = max(n,maxl(x))
205
Zur nächsten Aufgabe
Invarianten vorher überlegen!!!
206
Prozeduren und
Heuristiken für Programme
207
Prozeduren: Syntax
• Neues Programmkonstrukt : Prozeduraufruf p#(t;y)
• p# ist Prozedurname (das # ist übliche KIV-Konvention)
• Terme t der Sorten s sind Eingabe-Parameter
• Paarweise verschiedene Variablen y der Sorten s′ sind
Ein-Ausgabe-Parameter
• s : s′ heisst auch der (Aufrufs-)Modus der Prozedur
• Prozeduren p# ∈ Ps:s′ sind neuer Bestandteil der Signatur einer
Spezifikation
• KIV: Deklaration zwischen predicates und variables per:
procedures p# s1 × ...× sn : s’1 × ...× s’m;
208
Prozeduren: Semantik
• Semantik: Prozeduren sind eine Relation über den Trägern der
Parametersorten: [[p#]] ⊆ As × As′ × As′
• (a, b, c) ∈ [[p]] bedeutet: Die Prozedur p#, aufgerufen mit
⋆ Eingaben a für die Eingabe-Variablen
⋆ Eingaben b für die Ein/Ausgabe-Variablen
terminiert mit Ausgabe c in den Ein/Ausgabe-Variablen
• Damit das stimmt: Kein Zugriff auf globalen Variablen!
Ersatz: Zusätzliche Ein/Ausgabe-Parameter
• Normalfall in KIV: Funktionale Prozeduren:
Ein/Ausgabe-Variablen dienen nur zur Ausgabe:
c (und Terminierung) hängen nicht von b ab.
• Wenn nicht, Schlüsselwort nonfunctional am Ende der
Prozedurdefinition
209
Prozedurdeklarationen
• Möglich: Axiome für Prozeduren (Vor- und Nachbedingung)
• Normalerweise (hinter den axioms) Prozedurdeklarationen
declarations
f#(x; y)
{
if x = 0
then y := 1
else { f#(x -1;y); y := y * x }
}
• Erlaubt: (gegenseitige) Rekursion
• Semantik: Prozeduraufruf erhält “die übliche” Semantik.
Formal: Vereinigung aller tiefenbeschränkten Rekursionen
(analog zu: Vereinigung über beschränkte Zahl von Schl.durchläufen)
210
Regeln für Prozeduraufrufe
Falls Prozedurdeklaration p#(y; z).α gegeben:
x
y = σ, Γ ⊢ hαz iϕ, ∆
Γ ⊢ hp#(σ; x)iϕ, ∆
x
y = σ, hαz iϕ, Γ ⊢ ∆
hp#(σ; x)iϕ, Γ ⊢ ∆
call right
call left
Dabei: y sind die lokalen Variablen auf denen p# rechnet.
Sie dürfen in der Sequenz nicht frei vorkommen (evtl. umbenennen)
Die Regel gilt auch für Boxen statt Diamonds.
211
Ein Beispiel
procedures MAXL# natlist : nat;
MAX# nat, nat : nat;
declaration
MAX#(m,n; n0)
{
if m < n then n0 := n else n0 := m
};
MAXL#(x; n)
{
if x = [] then n := 0
else { MAXL#(x.rest; n); MAX#(n,x.first;n) }
}
212
Programme als Voraussetzungen: execute call
Nützlich bei Induktion, um den Call aus der Induktionsvoraussetzung gegen
den gerade aktuellen zu „kürzen“.
execute call
Γ ⊢ σ = τ, ∆
hp#(σ; x)i(x = y),
y
ϕx , Γ
⊢
y
ψz , ∆
hp#(σ; x)iϕ, Γ ⊢ hp#(τ ; z)iψ, ∆
Gilt (so) nur für funktionale (und damit auch deterministische) Prozeduren
(y neu):
contract call
Γ⊢σ=τ
hp#(σ; z)i(z = x
′
x′
x′
), ϕx , ψy , Γ
hp#(σ; x)iϕ, hp#(τ ; y)iψ, Γ ⊢ ∆
213
⊢∆
Zwischenzustände einführen: split left
Die folgende Regeln wird meist für α = Prozeduraufruf angewandt
(x = modifizierte Variablen von α, x′ neu):
split left
′
x′
, ϕx , Γ
hαix = x
⊢∆
hαiϕ, Γ ⊢ ∆
• Führt einen Zustand x′ am Ende von α ein, über den man “reden” kann.
• Dieser wird bei der Anwendung von Lemmata der Form
hαi x = x0 ⊢ ϕ
als Instanz für x0 gebraucht
214
Einfache Heuristiken für Programme
• symbolic execution: Wendet alle Regeln für Programme an, die keine
Fallunterscheidung ergeben: assign, if positive/negative, skip, abort, let
• split left: Wendet die Regel split left an
• contract and execute: Wendet execute call, contract call an
Im Heuristik-Satz „DL heuristics“ enthalten (zusammen mit simplifier,
quantifier closing, module specific). Kann immer verwendet werden.
215
Fallunterscheidungs-Heuristiken
• conditional right split: wendet if right an
• conditional left split: wendet if left an
• dl case distinction: Fallunterscheidung (conjunction right etc.), aber nur
für Programmformeln
Im Heuristik-Satz „DL Heuristics + Case Splitting“ enthalten. Sollte man
verwenden, wenn Beweisstruktur der Kontrollstruktur der Programme folgt
(meist der Fall).
Heuristik-Satz „DL heuristics + Induction“ enthält zusätzlich Heuristiken für
(noethersche) Induktion (induction, apply ind once).
216
Heuristiken für Prozeduraufrufe
• calls nonrecursive: Führt alle nichtrekursiven Aufrufe aus
• calls concrete: Führt alle Aufrufe aus, die konkrete Parameter haben,
i. e. Terme die höchstens Parametervariablen enthalten
• weak unfold:
⋆ Führt rekursive Prozeduren einmal aus, wenn sie in der
Induktionshypothese vorkommen. Höher in der Aufrufshierarchie
liegende Aufrufe bevorzugt.
⋆ Weitere Aufrufe werden ausgeführt, wenn festgestellt wird, dass
deren Tests so ausgehen, dass kein weiterer rekursiver Aufruf
auftritt.
• unfold: Führt zusätzlich rekursive Prozeduren (einmal) aus, bei denen
der rekursive Aufruf schon in der Sequenz vorkommt
„DL Heuristics“ enthält weak unfold, „DL Heuristics + Induction“ enthält
zusätzlich unfold.
217
Nichtdeterministische
Programme
218
Nichtdet. Programme: Syntax
KIV kennt noch zwei Programmkonstrukte für nichtdeterministische
Programme:
• α or β : Wählt nichtdeterministisch eines der beiden Programme
• choose x with ϕ in α ifnone β
⋆ Bindet lokale Variablen x (wie let) an irgendwelche Werte,
die ϕ erfüllen
⋆ ϕ darf von anderen Programmvariablen als nur x abhängen
⋆ Führt mit den lokalen Variablen α aus.
⋆ Falls überhaupt keine passenden Werte fur x existieren, die ϕ
erfüllen, wird β (ohne lokale Variablen) ausgeführt.
⋆ ifnone abort kann weggelassen werden (default).
219
Beispiele für choose
Beispiele:
• choose n with true in α:
Rät beliebige natürliche Zahl
• choose n with n < m in α ifnone β :
Wählt natürliche Zahl n, die kleiner m ist, und führt α aus.
Wenn m = 0 gilt, wird stattdessen β ausgeführt.
• choose boolvar with true in if boolvar then α else β
Ist äquivalent zu α or β
220
Nichtdet. Programme: Semantik
Semantik von or:
[[α or β ]] = [[α]] ∪ [[β ]]
Semantik von choose:
[[choose x with ϕ in α ifnone β ]]
a
a
v(x)
= {(v, wx ) | es gibt a mit A, vx |= ϕ und (vx , w) ∈ [[α]]}
a
∪ {(v, w) | (v, w) ∈ [[β ]] und es gibt kein a mit A, vx |= ϕ}
221
Ein Zusatzproblem für die Semantik
Was ist die Semantik von skip?
Was ist die Semantik von skip or abort?
222
Ein Zusatzproblem für die Semantik
Was ist die Semantik von skip?
Was ist die Semantik von skip or abort?
Antwort: Beide sind gleich: Identität auf allen Zuständen
Verhalten sich die Programme unterschiedlich?
222
Ein Zusatzproblem für die Semantik
Was ist die Semantik von skip?
Was ist die Semantik von skip or abort?
Antwort: Beide sind gleich: Identität auf allen Zuständen
Verhalten sich die Programme unterschiedlich?
Antwort: Ja, skip terminiert garantiert, skip or abort nicht.
Also: Die relationale Semantik kann nicht ausdrücken, dass ein
nichtdeterministisches Programm garantiert terminiert.
Damit kann es auch die dynamische Logik nicht:
hskip or aborti true besagt, dass es einen terminierenden Ablauf gibt.
222
Garantierte Terminierung
Definieren eine zusätzliche zweite Semantik für Programme: α ↓ ⊆ ST gibt
die Menge der Zustände (ST = Menge der Variablenbelegungen), für die α
garantiert terminiert. Einige Fälle (while und Rekursion sind schwierig) sind:
• abort ↓ = ∅
• skip ↓ = ST
• x := e ↓ = ST
• (α ∨ β) ↓ = α ↓ ∩ β ↓
• (α; β) ↓ = {v | v ∈ α ↓ und für alle w mit (v, w) ∈ [[α]] gilt: w ∈ β ↓}
• choose x with ϕ in α ifnone β ↓ =
a
a
{v | es gibt a mit A, vx |= ϕ und für jedes solche a ist vx ∈ α ↓}
a
∪ { v | es gibt kein a mit A, vx |= ϕ und v ∈ β ↓}
Beachte: Die Definition der garantierten Terminierung von compounds
benutzt die relationale Semantik.
223
Neuer Operator: strong diamond
Wir addieren einen neuen Operator („strong diamond“) zur Logik.
h|α|i ϕ besagt: α terminiert garantiert, und in allen Endzuständen gilt ϕ
Formal: A, v |= h|α|iψ :⇔ v ∈ α ↓ und für alle w : (v, w) ∈ [[α]] gilt: A, w |= ψ .
Der Operator wurde von E.W. Dijkstra 1976 erfunden, und
schreibt sich in der Literatur meist wp(α,ϕ) (von „weakest precondition“). Der
Kalkül heisst deshalb auch wp-Kalkül.
Bemerkung:
Die strong diamond-Klammern bekommt man mit F12
(KIV-Symbol) und dann { bzw. }.
Bemerkung:
224
Kalkülregeln für or
Das Gute an strong diamonds: Für deterministische Programme sind die
Regeln für strong diamonds genau dieselben wie für diamonds.
or right
Γ ⊢ hαi ψ, hβi ψ, ∆
Γ ⊢ hα ∨ βi ψ, ∆
Γ ⊢ [α] ψ, ∆
Γ ⊢ [β] ψ, ∆
Γ ⊢ [α ∨ β] ψ, ∆
Γ ⊢ h|α|i ψ, ∆
Γ ⊢ h|β|i ψ, ∆
Γ ⊢ h|α ∨ β|i ψ, ∆
225
Kalkülregeln für choose
choose right
Γ⊢∃
y
y.ϕx
∧
y
hαx i
ψ, (∀ x.¬ ϕ) ∧ hβi ψ, ∆
Γ ⊢ hchoose x with ϕ in α ifnone βi ψ, ∆
y
ϕx , Γ
y
[αx ]
⊢
ψ, ∆
∀ x.¬ ϕ, Γ ⊢ [β] ψ, ∆
Γ ⊢ [choose x with ϕ in α ifnone β] ψ, ∆
y
y
ϕx , Γ ⊢ h|αx |i ψ, ∆
∀ x.¬ ϕ, Γ ⊢ h|β|i ψ, ∆
Γ ⊢ h|choose x with ϕ in α ifnone β|i ψ, ∆
Die Variablen y sind neue Variablen (für die lokalen Versionen von x).
226
Zuweisungen für heaps
Im folgenden Refinement-Versuch (Theorie in der nächsten Vorlesung)
werden wir Programme schreiben, die auf der Heap-Spezifikation aus
Versuch 2 aufbauen. Der Inhalt von Speicherzellen hat jetzt die Form a × r
(ersetzt das abstrakte ce; Selektoren sind .val und .nxt). Zum
Programmieren gibt es zunächst neue Zuweisungen:
H[r] := a × r0
überschreibt H an der Stelle r mit a × r0. Der entstehende Heap ist also
H[r, a × r0].
Die Zuweisungen H[r].val := a und H[r].nxt := r0 sind analog.
Sie überschreiben nur ein value bzw. einen .nxt -Pointer.
Die Ergebnis-Heaps sind H[r, a × H[r].nxt] und H[r, H[r].val × r0].
227
Nichtdeterminismus für Speicherallokation
In den Programmen wird es nötig sein, eine neue Referenz zu allokieren.
Das geht am einfachsten so:
choose r with ¬ r ∈ H ∧ r 6= null in H[r] := . . .
Beachte: Die Auswahl der neuen Referenz allokiert die Zelle im Speicher
noch nicht. Erst die Zuweisung H[r] := . . . allokiert.
Das Programm ist nichtdeterministisch, deshalb werden strong diamonds
bei der Verifikation benötigt.
228
Herunterladen