5. Spezifikation und Verifikation von Programmen Programmentwicklung: genaue Kenntnis der gewünschten Leistungen offensichtlich unabdingbar In größeren Systemen: Anforderungsdefinition legt fest: - Funktionalität (was soll Programm leisten?) - technische Randbedingungen (Plattform? Antwortzeitverhalten? - organisatorische Vorgaben (wer benutzt System? Vorkenntnisse?) Softwaretechnik Hier: Spezifikation einfacher Programme hs / fub - alp2-5 1 Spezifikation... beschreibt das sichtbare Verhalten eines Programm von außen, im Detail soll hohes Sprachniveau haben - eindeutig, präzise - kein Interpretationsspielraum - keine Redundanz Formale Sprache ermöglicht Korrektheitsbeweise Umgangssprache bei entsprechender Disziplin möglich Beispiel: Suche einen Wert x in dem Feld a - was wird als Wert erwartet? -... und wenn x nicht in a? - wenn x mehrfachl in a enthalten ist? - enthält a mindestens ein Element? - sind alle a[i], 0 <= i <= a.length - 1 definiert? Korrektheit: nur sinnvolle Frage, wenn Spezifikation formal hs / fub - alp2-5 2 1
Beispiel: Suchproblem oft in prädikatenlogischer Sprache Signatur, hier als Methoden Signatur (Java) Spezifikation.: int search ( int [] a, int x) a Feld der Länge k > 0, a[i] definiert für 0 <= i < k Wert j erfüllt: meist implizit angenommen ( 0 <= j < k : a[j] == x ( i: 0 <= i < j ) a[i]!= x ) mindestens ein Feldelement und überall definiert Programmcode als Spezifikation? ( j = k ( i: 0 <= i < k ) a[i]!= x ) wenn x nicht in a pos :: Eq b => [b] -> b -> Int pos alist x = pos' alist x 0 where pos' [] x s = length alist pos' (y:xs) x s = if (x==y) then s else pos' xs x (s+1) erstes Vorkommen von x hier etwas unübersichtlich, modellierende Spezifikation in funktionaler Sprache aber oft nützlich hs / fub - alp2-5 3 Entwicklung und Vereinfachung des Algorithmus search (int a[int], int x) {int i = 0; while (nicht gefunden und nicht letztes Element) i++; return i; } Spezifikation unübersichtlich - besonders das Prädikat für j: P (j) Q(j) Vereinfachung? Kombination von Bedingungen fehleranfällig verfeinern, Konjunktion entspricht unhandlicher Spezifikation Gesuchtes Element wird immer gefunden! wie das? Vorauss.: Feld hat ein weiteres, zunächst undefiniertes Feldelement: a.length = k+1, a[k] == undef Setze: a[k] = x; search (int a[int], int x) {int i = 0; a[a.length-1]= x; while (a[i]!= x) i++; return i; } a[k] heißt Wächter (sentinel) Beachte: entspricht NICHT ursprünglicher Spezifikation hs / fub - alp2-5 4 2
Spezifikation imperativer Algorithmen Imperativer Algorithmus: Variablen definieren Zustandsraum Z Algorithmus A: Abbildung des Zustandsraum in sich P = true alle Zustände x,y int erlaubt Z P S { y = y + x; x = -x + y; x = x * x } Q = (y = Y + X x = Y*Y x >= 0 ) Q Z Prädikat P definiert die Voraussetzung für Anwendung von S Q definiert den Effekt der Anweisung(en) S Falls ein Wert berechnet wird, auch den spezifizieren (P,Q) charakterisiert Abbildung S: Z -> Z Wirkung (Effekt) auf Zustandsraum hs / fub - alp2-5 5 Voraussetzungen und Effekte Terminologie: Voraussetzungen P, Vorbedingungen, preconditions Effekte Q, Nachbedingungen, postconditions (P,Q) heißt Zusicherung (assertion) aber wie beweist man das? // {P} S {Q} bedeutet: wenn P vor Ausführung von S gilt und S terminiert, dann gilt Q hat den Wert Sprache für P, Q: prädikatenlogische Formeln, elementare Prädikate ( =, >, < usw. ) x=x für eine Variable x: x hat Wert X x : Wert von Variable x vor Ausführung von S weitere Prädikate, wenn nötig (sortiert(a),...) Beispiel: gegeben Feld a[i], i<= 0 < n und a=a. Dann spezifiziert Q den Effekt eines Sortieralgorithmus: Q : perm(a.a) ( i: 0<=i< n-1 : a[i] <= a[i+1] a ist Permutation von A...und wie spezifiziert man perm?? und jetzt gilt: sortiert(a) hs / fub - alp2-5 6 3
Starke und schwache Voraussetzungen Z P Wünschenswert: möglichst schwache Voraussetzungen - sollen für viele Zustände zutreffen möglichst starke Effekte Q - Zielzustände sollen möglichst genau charakterisiert werden S(x) R(x) Schwächste Voraussetzung wäre true: jeder Zustand erlaubt Z S R (P, Q) stärker als (P,Q ) genau dann wenn Ordnungsrelation auf Spezifikationen. Partiell! P P Q Q (d.h. P stärker als P und Q stärker als Q ) Schreibweise: (P, Q ) (P, Q ) hs / fub - alp2-5 7 Eigenschaften von Spezifikationen true: false: von jedem Zustand z erfüllt nicht erfüllbar (P, P): ohne Wirkung ( bzgl P!) (false, Q): nicht anwendbar - es gibt keinen Zustand, der die Voraussetzung erfüllt (true, Q): Effekt erfüllt Q ohne Voraussetzung (P, true): was beliebt ist erlaubt: Effekt spielt keine Rolle (P, false): terminiert nicht, da es keine Anweisung und keinen gültigen Zustand gibt, der die Spezifikation erfüllt Jedes Programm erfüllt diese Spezifikation Wie kann man {P} S { Q} zeigen, das heißt Anweisung(en) S erfüllt/en Spezifikation (P,Q)? x++ erfüllt Spezifikation (x>0, x>0) hs / fub - alp2-5 8 4
Verifikation Formale Semantik der Anweisungen als Voraussetzung Axiomatische Definition der Bedeutung einiger imperativer Anweisungen: {P} ; {P} Axiom der leeren Anweisung: bewirkt nichts {P(e)} x=e {P(x)} Warum nicht anders herum? Beispiel: { prim(7) } x=7; {prim(x)} aber nicht umgekehrt! Also: alles, was für e gilt, gilt nach Ausfürhung der Anweisung für x Zuweisungsaxiom: Wenn vor der Zuweisung P für den Term e gilt, dann nach der Zuweisung P mit e ersetzt durch x. Umgekehrt: P(e) ist die schwächste Vorbedingung, so dass nach der Zuweisung P(x) gilt. hs / fub - alp2-5 9 Weitere Ableitungsregeln {P} S {Q} {P } T {Q } Q P {P} S;T {Q } Ableitungsregel : Prämisse Konsequenz Sequenz: Wenn die Nachbedingung der Anweisung S stärker ist, als die Voraussetzung von T, gilt für S;T die Voraussetzung P und die Nachbedingung Q {P B } S1 {Q} {P B} S2 {Q} {P} if B S1; else S2; {Q} Alternative: Die Auswertung von B darf keine Seiteneffekte haben. Geeignetes Q ist oft: B Q1 B Q2 hs / fub - alp2-5 10 5
Iterationsregel {Inv B} S {Inv} {Inv} while (B) S; end {Inv B} Auch hier darf die Auswertung von B keine Variablen verändern (keine Seiteneffekte!). while (B) S; end statt Java-Notation: while (B) {S;} Diese Klammern haben eine andere Bedeutung! Abweisende Schleife: problematisch ist die gleichzeitige Forderung nach möglichst starker Nachbedingung und möglichst schwacher Voraussetzung - denn in n+1-ter Iteration ist die Vorbedingung die Nachbedingung der n-ten Iteration. hs / fub - alp2-5 11 Korrektheit Korrektheit eines imperativen Programms S bezüglich Spezifikation (P,Q): - für jede Eingabe, die P erfüllt, erfüllt die Ausgabe von S das Prädikat Q - und: S terminiert Algorithmus terminiert nur dann nicht, wenn Iteration (oder Rekursion) nicht abbricht. Deshalb für jede Schleife Termination prüfen: ganze Zahlen Gesucht Funktion term : Zustandsraum -> Z mit den Eigenschaften: {Inv B} while (B) S; end {term(z) < term(z)} und {Inv B} term(z) > C Streng monoton fallend, nach unten beschränkt hs / fub - alp2-5 12 6
Verifikation Prinzipielles Vorgehen bei der Verifikation eines Programms S bezüglich Spezifikation (P,Q): - annotiere jede Anweisung S i von S mit geeigneten Voraussetzungen und Nachbedingungen (als Kommentar) {P} S 1 {P 1 } S 2...{P n-1 } S n {Q} - Beweise schrittweise unter Anwendung der Hoare-Axiome, dass aus P Q folgt, wenn S ausgeführt wurde. Vorwärtsbeweis (P -> P1 ->... -> Q) oft unhandlicher als rückschreitende Beweisführung: finde zu vorgegebener Nachbedingung eine möglichst schwache Voraussetzung ( weakest precondition ) Besser als Verifikation eines fertigen Programms: Programmentwicklung unter Beachtung von Voraussetzung / Konsequenz für Anweisungen / Programmstücke. ---> später hs / fub - alp2-5 13 Beispiel: Verifikation der ganzzahligen Division {x>0 y >0} q = 0; r = x; while (r >= y) r = r-y; statt {...}- Block.! q = q+1; end; {x = q*y+r 0 <= r < y } auch x >= 0 möglich? {x>0 y >0} {x>0 y >0 0=0} q = 0; {x>0 y >0 q=0} {x=x x>0 y >0 q=0} r = x; {x=r x>0 y >0 q=0} Invariante Inv: im Gegensatz zu anderen Formeln nicht herleitbar {x=r x > 0 y > 0 q=0} {x=q*y+r r >= 0} while (r >= y) {x=q*y+r r >= 0 r >= y {x=(q+1)*y + r-y r-y>=0} r = r-y; {x=(q+1)*y + r r>=0} q = q+1; {x=q*y + r r>=0} end; {x=q*y + r r>=0 r < y} {x= q*y+r 0 <= r < y } brauchen wir Voraussetzung y>0? hs / fub - alp2-5 14 7
Termination der ganzzahligen Division Terminationsfunktion: t(z) = r Hier wird y>0 benötigt Zeige: r streng monoton fallend und beschränkt Erweitere Invariante : Inv = Inv y > 0 {x=q*y+r r >= 0 y > 0} B: r >= 0 Dann gilt: { Inv B r-y < R } S {Inv r < R} R: vorheriger Wert von r Inv B => r >= y y > 0 => r > 0 hs / fub - alp2-5 15 Schwächste Vorbedingung Nützlich bei rückschreitender Verifikation (oft einfacher als vorwärtsgerichtete Verifikation, weil zielgerichtet) : Gegeben die Nachbedingung (Konsequenz) Q, gesucht die schwächste Voraussetzung wp = weakest precondition P = wp (S, Q) so dass S Zustand, der P erfüllt, in Zustand überführt, der Q erfüllt. Semantikdefinition äquivalent zu Ableitungsregeln: wp (;, Q) = Q leere Anweisung wp (x=e;, Q(x)) = Q(e) Zuweisung wp(s1;s2;, Q) = wp(s1;, wp(s2;, Q)) Sequenz wp( if (B) S1; else S2;, Q) = (B => wp ( S1;,Q)) B => wp ( S1;,Q) ) Alternative hs / fub - alp2-5 16 8
Iterative Programme Schleifen : rückschreitende Verifikation wenig geeignet stattdessen geeignete Invariante finden wie findet man Invarianten? true ist immer eine Invariante, aber selten hilfreich Konstruktion von Schleifen 1. Nachbedingung Q festlegen ( was ist das Ziel? ) 2. Invariante INV finden 3. Voraussetzung P festlegen und initialisieren, damit INV gilt 4. Bedingung B festlegen 5. Invariante beweisen: INV gilt auch nach Schleifendurchlauf 6. Zeigen: INV B => Q Wenn das nicht gelingt, bessere Invariante suchen, weiter bei 2. hs / fub - alp2-5 17 Wie findet man Invarianten? Heuristiken... A. Konjunktiven Term entfernen Q = Q1 Q2 Beispiel: Suche von Wert x in Feld a, a.length = n, x wird gefunden. Nachbedingung Q: 0 <= i < n ( j: 0<=j< i : a[j]!= x) a[i]=x naheliegend: a[i] = x entfernen, Rest ist Invariante Damit auch B gefunden: Nach Verlassen soll INV a[i] = x gelten, also B = a[i]!= x B. Konstante durch Variable ersetzen Beispiel: Summe der Feldelemente von a mit a.length = n Nachbedingung: Q s = Σ a[i] : 0 <= i < n ersetze Konstante n durch Variable j : INV s = Σ a[i] : 0 <= i < j Damit INV B => Q : B = j >= n oder B = j < n auch die Konstante 0 hätte durch Variable ersetzt werden können! hs / fub - alp2-5 18 9
... wie findet man Invarianten? C. Variablenbereich festlegen Beispiel: lineare Suche in Feld a, a.length = n, gesuchter Wert kommt vor Eine Möglichkeit (s.o., i und j sind Variablen im Programm) INV; 0 <= i < n ( j: 0<=j< i : a[j]!= x) Alternative: sei k der gesuchte Index a[k]=x beachte: k ist ein Wert, keine Variable. Der Wert von k soll nach Durchlaufen der Schleife in der Variablen j stehen. für k gilt also: 0 <= k ( j: 0<=j < k : a[j]!= x) a[k] = x Damit: j nimmt die Werte 0 <= j <= k an Das ist die Invariante! Schranke: k -j Invariante herstellen: j= 0; {0 <= j <=k a[j]!=x} while ( a[j]!= x) j++; {0 <= j <=k a[j]=x} Schranke fällt streng monoton,... nachprüfen. hs / fub - alp2-5 19... wie findet man Invarianten? D. Teile und herrsche / Rekursive Invarianten Beispiel: Quersumme von N qs 0 = 0 qs n = qs (n div 10) + n mod 10 Nachbedingung s = qs(n) n = N INV : qs(n) = s + qs(n) 0 <= n <= N while ( n > 0 ) { s=s+n mod 10; n = n div 10;} Java-Blockklammern Beweis der Invariante Eigenschaft der Quersumme {qs(n) = s + n mod 10 + qs (n div 10) 0 <= n div 10 <= N } => INV s = s + n mod 10; {qs(n) = s + qs(n div 10) 0 <= n div 10 <= N } Zuweisungsaxiom n= n div 10; {qs(n) = s + qs(n) 0 <= n <= N } INV hs / fub - alp2-5 20 10
Schrittweise Programmentwicklung Nutzen der Verifikationsmethodik: Zur Konstruktion von Programmen Dokumentation des Codes und sich von der Richtigkeit überzeugen Systematisches Durchdenken der Bedingungen und Formulieren in anderer Sprache als Programmiersprache hilft Fehler erkennen. (aber nicht alle...was ist z.b. mit Rundungsfehlern der Gleitkommaarithmetik? Beispiel für systematische Programmentwicklung Anforderungsdefinition: Gegeben eine Liste von Tagekursen einer Aktie für n0 Tage. Gesucht: der maximale Spekulationsgewinn, das ist die größte Differenz von zwei Aktienkursen, wobei der Verkaufstag größer als der Tag des Kaufs ist. Spezifikation: (P,Q) mit mindestens 2 Tage P : a.length = n0, 0 <= i < n0 a[i] = A[i] (alle Werte definiert) n0 > 1 // a ist Feld der Größe n0) Q : g = max( a[j] - a[i]) 0 <= i < j < n0 0 <= i < j < n0 hs / fub - alp2-5 21 Fallstudie Programmentwicklung Invariante bestimmen: Methode B: Konstante durch Variable ersetzen, n0 durch Variable n 2 <= n <= n INV : g = max a[j] -a[i], 0 <= i < j < n 2 <= n <= n0 also g enthält immer den maximalen Kursgewinn der ersten n Tage Bedingung : folgt aus INV n = n0 => Q, also n!= n0 Invariante herstellen: g = a[1] -a[0] Erste Teillösung: {n0 >= 2} g = a[1] - a[0]; n = 2; {INV] while (n!=n0) // {INV n! n0} {bestimme max der Differenz für 0 <= i < j <= n und erhöhe n um 1} {INV} hs / fub - alp2-5 22 11
... Fallstudie Programmentwicklung Schleifenrumpf konstruieren: Zerlegen in {Inv n!= n0} berücksichtige n {INV n+1 } n = n+1; {INV} // Invariante, bei der n durch n+1 ersetzt Betrachte INV n+1 : g = max a[j] -a[i], 0 <= i < j < n+1 2 <= n+1 <= n also: g = max (a[j] -a[i]), 0 <= i < j < n+1 = max2 (max (a[j] - a[i]), 0 <= i < j < n, max ( a[n] - a[i], 0 <= i < n) ) = max2 ( max..., a[n] - min a[i], 0<= i < n) ) Verschiedene max-funktionen! Minimum der a [i] merken, 0 <= i <n und zwar in Variable mina Damit : g = max (g, a[n] - mina) --> weitere Invariante: INV = mina = min a[i], 0 <= i < n Erweiterte Invariante: INV ges = INV INV hs / fub - alp2-5 23... Fallstudie Herstellen von INV : mina = min (a[0], a[1]) Herstellen von INV beim Übergang n -> n+1: mina = min a[i], 0 <= i < n+1 also: mina = min (mina, a[n]) Teile zusammenfügen: { n >= 2...} n = 2; mina = a[1] - a[0]; g = a[1] - a[0]; {g = max ( a[j] - a[i] 0 <= i < j < n 2 < n < n0 mina = min a [i], 0 <= i < n } while ( n!=n0) // {INV n!= n0} { g = max ( g, a[n] - mina); mina = min (mina, a[n]); Beachte: lineares Programm n = n+1; {...} Block, keine Zusicherung } {Q : g = max a[j] - a[i], 0 <= i < j < n0 } hs / fub - alp2-5 24 12
Zusammenfassung Spezifikation und Verifikation Semantikbeschreibung dient u.a zur Definition einer Programmiersprache (hier: axiomatisch) Grundlage zur Implementierung von Übersetzer / Interpretierer Programmbeschreibung was tut das Programm? Beschreibung mit Hilfe der semantischen Sprachdefinition entsprechend dem Programmaufbau (als syntaktisch korrekt vorausgesetzt) Programmspezifikation was soll das Programm tun? Präzisierung der Anforderungsdefinition Programmverifikation tut das Programm, was es soll? Nachweis der Verträglichkeit von Spezifikation und Programm(beschreibung) hs / fub - alp2-5 25 Verifizieren oder Testen? Programmfehler: Programm tut nicht das, was es soll oder tut etwas, was es nicht soll Es gibt praktische keine (sehr großen) fehlerfreien Programme häufig völlig unabhängige Entwicklung (Betriebssystem, Anwendungsprogramm ), Probleme im Zusammenwirken der Teile! Für manche Programme ist formale Verifikation ein Qualitätsmerkmal (Zertifizierung von Programmen nach formaler Verifikation, Bsp.: Steuerprogramme in Verkehrsleitsystemen) Begleitende Verifikation vermindert Fehler und unterstützt die Konstruktion von Algorithmen. Ohne Spezifkation keine Verifikation meist aufwendig. Verifikationsprogramme wünschenswert Tests beweisen (ggf.) die Anwesenheit von Fehlern, nie die Abwesenheit (wenn unendlich viele mögliche Eingaben) hs / fub - alp2-5 26 13
Anmerkungen zum Testen Klassifikation: Schnittstellentest (blackbox-test) Ein- / Ausgaberlation auf Konformität zur Spezifikation prüfen Programmabhängiger Test (whitebox-test) Überprüfung möglichst großer Teile aller Pfade durch das Programm Möglichst große Überdeckung (des Programmcodes) wünschenswert Systematische Auswahl von Testfällen: Schnittstellentest Hauptproblem: kombinatorische Explosion der Testfälle, deshalb Vollständigkeit meist illusorisch. pro spezifizierter Bedingung mindestens ein Testfall Randbereiche (ggf. von beiden Seiten) prüfen, Maximal-, Minmalwerte Genügende Anzahl von Normallfällen jeweils gegen Sollwerte vergleichen! Überdeckungstest erwünscht, aber kaum machbar: Wegüberdeckung: jeden Weg einmal durchlaufen Abschwächung: jede Anweisung einmal durchlaufen (Anweisungsüberdeckung) stärker : zweigüberdeckend: jeden Zweig mindestens einmal durchlaufen hs / fub - alp2-5 27 14