DL-Kalkül: Lokale Variablen vardecls right x0 x 0 = τ , Γ ` hαx iϕ Γ ` hlet x = τ in αiϕ vardecls left x0 hαx iϕ, x 0 = τ , Γ ` ∆ hlet x = τ in αiϕ, Γ ` ∆ x 0 sind neue Variablen (bezeichnen die lokalen Variablen). Dieselben Regeln gelten auch für Boxen. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 197 / 290 Beispiel zur Korrekheit m := x.first, x0 := x.rest; while x0 6= [] do { if x0.first > m then m := x0.first ; x0 := x0.rest } 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 198 / 290 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)) 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 198 / 290 Zur nächsten Aufgabe Invarianten vorher überlegen!!! 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 199 / 290 Korrektheit und Vollständigkeit der Kalküle Satz: Die Programmkalküle • Hoare-Kalkül für partielle Korrektheit • Hoare-Kalkül für totale Korrektheit • Dynamische Logik sind korrekt. Z. B. SP `HC ϕ {α} ψ ⇒ SP |= ϕ {α} ψ. Sie sind alle unvollständig, d. h. SP |= ϕ {α} ψ impliziert nicht, dass SP `HC ϕ {α} ψ gilt. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 200 / 290 Gegenbeispiel zur Vollständigkeit Gegenbeispiel zur Vollständigkeit des Hoare-Kalküls: Nat = data specification nat = 0 | +1 (-1 : nat) end data specification x = 0 ∧ y = y0 { while y 6= 0 do y := y -1; x := x +1 od } x = y0 Schleifeninvariante? x + y = y0 ? Eigentlich schon, aber keine Formel in Nat. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 201 / 290 Unvollständigkeit der Programmkalküle • Das Problem ist, dass man die Invariante mit den vorhandenen Symbolen nicht ausdrücken kann. • Die Invariante INV ist eine spezielle Induktionshypothese Die Behauptung für n lautet: “Nach n Schleifendurchläufen gilt INV”. • Das Problem ist also dasselbe wie bei der Generiertheitsklausel. Satz: Die Programmkalküle sind relativ vollständig: Wenn man alle wahren Aussagen über den nat. Zahlen als Axiome hätte, dann könnte jede wahre Aussage über Programmen bewiesen werden. Intuition: • Der Programmkalkül ist nicht schlimmer“ unvollständig, als die ” Prädikatenlogik es war. Es fehlen keine Programmregeln. • Wenn etwas nicht bewiesen werden kann, dann liegt es nur an fehlenden Hilfsfunktionen/Axiomen für Datenstrukturen. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 202 / 290 Prozeduren und Heuristiken für Programme 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 203 / 290 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 0 sind Ein-Ausgabe-Parameter • s : s 0 heisst auch der (Aufrufs-)Modus der Prozedur • Prozeduren p# ∈ Ps:s 0 sind neuer Bestandteil der Signatur einer Spezifikation • KIV: Deklaration zwischen predicates und variables per: procedures p# s1 × ...× sn : s’1 × ...× s’m; 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 204 / 290 Prozeduren: Semantik • Semantik: Prozeduren sind eine Relation über den Trägern der Parametersorten: [[p#]] ⊆ As × As 0 × As 0 • (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 globale 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 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 205 / 290 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) 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 206 / 290 Regeln für Prozeduraufrufe Falls Prozedurdeklaration p#(y ; z).α gegeben: x y = σ, Γ ` hαz iϕ, ∆ Γ ` hp#(σ; x)iϕ, ∆ x call right y = σ, hαz iϕ, Γ ` ∆ hp#(σ; x)iϕ, Γ ` ∆ 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. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 207 / 290 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) } } 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 208 / 290 Programme als Voraussetzungen: execute call Nützlich bei Induktion, um den Call aus der Induktionsvoraussetzung gegen den gerade aktuellen zu kürzen“. ” execute call Γ ` σ = τ, ∆ y y hp#(σ; x)i(x = y ), ϕx , Γ ` ψz , ∆ hp#(σ; x)iϕ, Γ ` hp#(τ ; z)iψ, ∆ contract call Γ`σ=τ y y hp#(σ; x)i(x = y ), ϕx , ψz , Γ ` ∆ hp#(σ; x)iϕ, hp#(τ ; z)iψ, Γ ` ∆ Regeln gelten (so) nur für funktionale (und damit auch deterministische) Prozeduren (y jeweils neu). Für nichtfunktionale Prozeduren muß in der ersten Prämisse x = z hinzugefügt werden. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 209 / 290 Zwischenzustände einführen: split left Die folgende Regeln wird meist für α = Prozeduraufruf angewandt (x = modifizierte Variablen von α, x 0 neu): split left x0 hαix = x 0 , ϕx , Γ ` ∆ hαiϕ, Γ ` ∆ • Führt einen Zustand x 0 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 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 210 / 290 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. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 211 / 290 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). 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 212 / 290 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. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 213 / 290 Nichtdeterministische Programme 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 214 / 290 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 für x existieren, die ϕ erfüllen, wird β (ohne lokale Variablen) ausgeführt. • ifnone abort kann weggelassen werden (default). 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 215 / 290 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 β 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 216 / 290 Nichtdet. Programme: Semantik Semantik von or: [[α or β]] = [[α]] ∪ [[β]] Semantik von choose: [[choose x with ϕ in α ifnone β]] v (x) a a = {(v , wx ) | es gibt a mit A, vx |= ϕ und (vx , w ) ∈ [[α]]} a ∪ {(v , w ) | (v , w ) ∈ [[β]] und es gibt kein a mit A, vx |= ϕ} 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 217 / 290 Ein Zusatzproblem für die Semantik Was ist die Semantik von skip? Was ist die Semantik von skip or abort? 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 218 / 290 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? 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 218 / 290 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. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 218 / 290 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. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 219 / 290 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 |= ψ. Bemerkung: 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. }. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 220 / 290 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 ψ, ∆ 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 221 / 290 Kalkülregeln für choose choose right y y Γ ` ∃ y .ϕx ∧ hαx i ψ, (∀ x.¬ ϕ) ∧ hβi ψ, ∆ Γ ` hchoose x with ϕ in α ifnone βi ψ, ∆ y y ϕx , Γ ` [α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). 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 222 / 290 Kontrakte, Abstrakte Datentypen und Verfeinerung 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 223 / 290 Kontrakte 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 224 / 290 Vor- und Nachbedingungen, Kontrakte Ein Kontrakt für eine Prozedur p (oder auch für eine Methode) besteht (mindestens) aus • einer Vorbedingung ϕ (präd.log. Formel) • einer Nachbedingung ψ (präd.log. Formel) • der Angabe der input-Parameter (incl. gelesene globale Variablen) • der Angabe der output-Parameter (incl. geschrieben globale Variablen). • Dazu kommt natürlich eine informelle Beschreibung: U.a. Name und Zweck der Operation, Querverweise zu Use Cases, etc. Bemerkung: Für KIV-Prozeduren fehlen die globalen Variablen. Sie müssen als I/O-Parameter addiert werden. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 225 / 290 Vor- und Nachbedingungen, Kontrakte Definition: Eine Methode mit Implementierung mit Rumpf α erfüllt einen Kontrakt, falls die Parameter korrekt gegeben und der Rumpf total korrekt bzgl. Vor- und Nachbedingung ist. Formal also muss gelten: • Im Hoare-Kalkül: ϕ hαi ψ • In Dynamischer Logik: ϕ → hαi ψ Bemerkung:(Vorbedingung in Kontrakten) Wenn die Vorbedingung nicht eingehalten wird, darf beliebiges passieren: Absturz, Exception, Nichtterminierung, reguläres Ergebnis, illegaler Speicher etc. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 226 / 290 Zwei Beispiekontrakte Beispiel 1 (Revertieren einer Liste): • Eingabe und Ausgabe: Liste x • Vorbedingung: x = y • Nachbedingung x = reverse(y) Beispiel 2 (Entfernen eines Element von einem Stack): • Eingabe: Stack st • Ausgabe: Modifizierter Stack st und Element a • Vorbedingung: st = st0 ∧ st 6= emptystack • Nachbedingung st = pop(st0 ) ∧ a = top(st0 ) y und st0 sind logische Variablen, die nur zur Spezifikation benötigt werden. Alles andere sind Programmvariablen. 4. Juni 2012 D. Haneberg: Formale Methoden im Software Engineering 227 / 290