180 likes | 296 Vues
This document explores the implementation and verification of Abstract Data Types (ADTs) using arrays, focusing on stacks, queues, and lists. It discusses the necessity of validating stack axioms even when represented in array form and how to construct axioms relevant to arrays. Key functions like `array(A, I)` and `change(A, I, V)` are introduced, along with strategies for translating ADT functions into practical implementations. The verification process is illustrated using provers and examples from logic circuits to enhance comprehension of recursive definitions and their efficient realization.
E N D
CSE 3341.03 Winter 2008Introduction to Program Verification implementing ADTs
verifying ADT implementations (ch. 6) • example: if we implement stacks with arrays, we need to verify that the stack axioms are still valid. • need a description of arrays which allows us to prove equations involving array expressions
motivation • why implement stacks (and queues and lists?) using arrays?
array axiom • unbounded arrays, array index ≥ 0. • what axioms do we need? just one! • 2 functions: array and change • array(A, I) returns the value of the array A at location I corresponds to A[I] • change(A, I, V) returns an array which has the value V at I. • corresponds to A[I] = V • combine into an axiom? array(change(Array(I, Value), J) = if(J=I, Value, array(Array, J))
general strategy • translate ADT functions into implemented (non-abstract) functions -- for clarity & convenience e. g. pop(S) ->> popA(S) • delete original ADT axioms, add axioms for the ADT used in implementation (e. g. array axiom) • what happens to rules for dup, over, etc.?
implementation in terms of the target ADT • e. g., a stack = array and # of values on the stack (= index) = [Array, Index] nil = [nilA, 0] nilA is the array component (with no elements) of the implementation of the empty stack)
array axiom • unbounded arrays, array index ≥ 0. • what axioms do we need? just one! • 2 functions: array and change • array(I) returns the value of the array at I • change(A, I, V) returns an array which has the value V at I. • combine into an axiom? • array(change(Array(I, Value), J) = if(J=I, Value, array(Array, J))
general strategy • translate ADT functions to implemented (non-abstract) functions -- for clarity & convenience • e. g., pop(S) ->> popA(S) • delete original ADT axioms • add axioms for ADT used in implementation (e. g. array axiom)
implementation in terms of the target ADT • e. g. a stack = array & # of values on the stack (= index) • define getter functions to access the parts of the implementation e. g. a([A, I]) ->> A, i([A, I]) ->> I. • define ADT functions in terms of new ADT topA(Stack) ->> array([a(S), i(S)]). popA(Stack) ->> [a(Stack), i(Stack) - 1] pushA(X, Stack) ->> [change(a(Stack), i(Stack)+1, X), i(Stack)+1]? • in English?
verification using prover • use prover with trace on and theory files try to verify the original ADT axioms • e. g. pop(push(x,s)) = s |: theory('stackA.simp'). % what goes in here? |: theory('array.simp'). % what goes in here?
tracing . . . pop(push(x,s))=s popA(push(x,s))=s [a(push(x,s)),i([change(a(s),i(s)+1,x),i(s)+1])-1]=s . . . [change(a(s),i(s)+1,x),i(s)]=s * Cannot prove [change(a(s),i(s)+1,x),i(s)]=s. easy to add a simplification rule to prove the identity what's the general case? how would you prove it?
exercise 6.6: implementing queues • a key line: firstA(Q) = array(a(Q), 1) mathematically (abstractly), first needs a recursive definition, if defined in terms of the ADT functions but can be implemented efficiently (without recursion), using arrays. • that's the purpose of translating queues into something built from arrays.
verifying a logic circuit: Section 6.3 • a different kind of implementation: • objects (gates) connected to form a (storage-free) logic circuit • each kind of gate has a definition: e.g. or_gate(V, W, X) ->> X iff (V or W). • this is not the only way to do it • chosen to make the “wiring” easier, but the logic is more complicated
how else might we represent the circuit? • try just using functions: e. g., Z = or(or( . . ), and(. . . )) the nesting makes it harder to translate without error from the diagram to a Boolean expression
vote circuit: p. 55 • what’s the purpose of the function int(X) ->> if(X, 1, 0) ? • what’s the purpose of the implementation term? to specify the gates and connections of a circuit in the equality to be verified: implementation(a, b, c, z) implies z = majority(a, b, c).
Skolem constants • the implementation predicate implicitly introduces existentially quantified variables: • e. g., for all A, B, there exists a d such that d(A, B) = and_gate(A, B) or more simply: and_gate(A, B, d).
exercise 6.9: • what’s “half” mean in the context of a “half-adder” ? • anyone not sure of what the gate symbols mean? • what are the outputs supposed to be (arithmetically)? • what portion of the exercise is more or less mechanical?
what is being verified? • what part of the exercise requires some understanding & insight? • identifying the intended goal as a function of the inputs to the circuit: 2*Carry + Sum = A + B