Presentation is loading. Please wait.

Presentation is loading. Please wait.

Some heuristics for deriving invariant LN sections 6.7 , 6.8

Similar presentations


Presentation on theme: "Some heuristics for deriving invariant LN sections 6.7 , 6.8"— Presentation transcript:

1 Some heuristics for deriving invariant LN sections 6.7 , 6.8

2 Overview Examples of “Replace constant with counter” heuristic
Fibbonaci Longest “X” segment Tail invariant Revisiting array reverse GCD Other examples 2

3 Loop Reduction rule [Init] [IC] [EC] [TC1] [TC2]
P  I // setting up I {* g /\ I *} S {* I *} // invariance I /\ g  Q // exit cond {* I /\ g *} C:=m; S {* m<C *} // m decreasing I /\ g  m > // m bounded below {* P *} while g do S {* Q *} 3

4 Heuristic “replace constant with counter”
i := 0 ; r := true ; while i<n do { r := r /\ (a[i]=0) ; i++ } {* r = (k : 0k<n : a[k]=0) *} 1. Indentify the loop counter The loop iterates on the counter up to some upperbound/lowerbound. 2. Identify this upper/lower bound. If the post-cond is of the form Q(n). Try Q(i) /\ ... as then invariant. Notice that: Q(i) /\ i=n  Q(n) 3. Takes Q(i) as inv, + a range expr over the counter, e.g.   i   4

5 Fibonacci sequence The Fibonacci “sequence” can be generated by: fib 0 = 0 fib 1 = 1 fib (t + 2) = fib (t+1) fib t Nice (but bad execution time)

6 Let’s take a multi-vars example: iterative Fibonacci
Fibonacci’s original problem: rabbit growth problem. If we start if n pairs of rabits, how many rabits we’ll have at the end of the year? Simplified model: Start with one new pair A new pair needs 1 month to become adult Each month each adults pair produce a new pair Our rabbits don’t die :)

7 Iterative Fib  O(n) RabitSim (n:int) : int
{ nNow,nAdult,newPairs,t : int ; t:=1 ; nAdult:=0 ; nNow:=1 ; while t < n do { newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 } ; return nNow }

8 Let’s prove that the two verions are the same
t:=1 ; nAdult:=0 ; nNow:=1 ; while t < n do { newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 } ; return := nNow {* I *} Inv : nNow = fib t /\ 1  t  n term. metric : n-t {* nNow = fib n *} {* return = fib n *} (note multiple variables to accumulate results)

9 Verifying Invariance /\ nAdult = fib (t-1)
{* nNow = fib t /\ 1  t  n /\ t<n *} Not enough info in inv to prove this wp : nNow + nAdult = fib (t+1) /\  t+1  n newPairs := nAdult ; nAdult := nNow ; nNow := nNow + newPairs ; t := t + 1 /\ nNow = fib t /\ nAdult = fib (t-1) the wp of {* nNow = fib t /\ 1  t  n *}

10 A different example: converting tail recursions
Typical recursion: g(x) = if x≤ 0 then x else x + g(x-1) Tail recursion: f (x) = if x≤0 then x else f(x-1) f(2) = f(1) = f(0) = 0 g(2) = 2 + g(1) g(0)

11 Examples last s = #s=1  hd s | last (tail s)
lsum (z,s) = s=[]  z | lsum (z + hd s, tail s) You can calculate sum via lsum: sum s = lsum (0,s)

12 Tail Recursion, general form
A function F of this general form: F x = g x  base x // base case | F ( x) // recursion This is called tail recursion. Can be optimized by: Last recursion returns directly to the first/root call. Use less stack space With iteration  cost no stack space for passing params. Terminates if … ? F : AB. Find a term. metric m : AInt such that:  g x  m ( x) < m x  g x  m x > 0 12

13 Implementing tail recursive function
F x = g x  base x | F ( x) Compute F  With this simple loop  x :=  ; while  g x do x :=  x ; r := base x ; Inv: F x = F  {* true *} Term. Metric : m x bsearch(x,a,N) = bsworker(x,a,0,N) bsworker (x,a,left,right) = // not optimized ... could break if a[left] = x, but let’s not do that let d = right – left m = left + (right – left) /2 in if d <= 1 then left<right /\ a[left]=x else if x<=a[m-1] then bsworker(x,a,left,m) else bsworker(x,a,m,right) base x = F  {* r = F  *} 13

14 Example : GCD Given X,Y > 0, compute gcd X Y.
Straight forward solution: O(X min Y). We can do better than that (Euclides, 320 BC) Approach: try to cast the problem into tail recursion. We will need to explore some properties of common divisors.

15 Some properties of gcd We’ll assume positive integers!
Define : d divides x = k : k>0 : x = k*d d comdiv x,y = d divides x /\ d divides y Theorem : \\ hmm … looks like a “split” If x>y then d comdiv x,y  d comdiv (x-y),y if d comdiv x,y then: (1) x = k1*d, for some k1>0 (2) y = k2*d, for some k2>0 If y>x, then x-y = (k1 – k2)*d ... so d is also a divisor of x-y, so d is comdiv of x-y as well. 15

16 Some properties of gcd Define gcd via this equation :  = gcd x y =  comdiv x,y /\ (e : e comdiv x,y : e   ) Theorem : if x>y then gcd x y = gcd (x-y) y Theorem : gcd x x = x 16

17 Gcd in tail recursion So we can infer this recursive relation for gcd : gcd x y = x = y  x | x > y  gcd (x-y) y | x < y  gcd x (y – x) Implemented by this loop: x,y := X,Y ; while x  y do if x>y then x:=x-y else y:=y-x inv : gcd x y = gcd X Y {* X>0 /\ Y>0 *} term. metric : x+y {* x = gcd X Y *} 17

18 Another example Consider an array a[0..n) of non-negative integers representing a number. Write a program that computes the “value” of the number represented by such an array. a [0..3) 3 1 2 value a 3 = 312 value

19 Possible solutions: Iterative: :i=0 ; r:=0 ; while i<n do {
Recursive: Both are less efficient in computing 10^... :i=0 ; r:=0 ; while i<n do { r := r + a[i] * 10^(n-i) i++ } value [] = 0 value (x:s) = x*10^(length s) + value s

20 Tail recursive definition of “value”
Or, using tail recursion (more efficient): val x s = s=[]  x | /* else */ val (10*x + hd s) (tail s) Note that value s = val 0 s Now you can convert it to an iterative solution

21 Implementation Recall:
val x s = s=[]  x | /* else */ val (10*x + hd s) (tail s) Inv: val x s = val 0 (a[0..n)) /\ 0  i  n {* 0≤n *} x , s := 0 , a[0..n) ; while s[] do { x := 10*x + hd s ; s := tail s } result = x You can further refine this to get rid of the lists. {* result = val 0 (a[0..n)) *} 21

22 Data refinement inv: {* ... /\ s = a[i..n) *}
x , s := 0 , a[0..n) ; while s[] do { x := 10*x + hd s ; s := tail s } result = x i = 0 Introduce new variables Add invariant to express their intention Add statements in order to maintain the new inv. Use the new vars to optimize Drop what you don’t need Repeat i < n a[i] i++ {* result = val 0 (a[0..n)) *} 22

23 Oh, note that foldl is tail recursive...
Well, foldl is tail recursive: Roll out your implementation... Compare it with : foldl f e [] = e foldl f e (x:s) = foldl f (f e x) s F(z) = F(z) SUM [] = 0 SUM (x:s) = x + SUM s This is not TR

24 Tail invariant Previously,our invariants express “what has been computed” : nNow = fib t s = SUM (a[0..i)) Tail invariant has this forms : still to-do = goal The loop shrinks the “todo” part, so that in the end we have: remainder = goal If remainder can be easily calculated and the result is put in a variable r, then in r we would have goal.

25 Tail invariant Or more generally, this form : x  todo = goal When the loop terminates, we again hope that x  todo can be easily calculated, and hence we get the goal. An instance of the above scheme is to reduce the todo part to the unit of , so that in the end we have: x = goal

26 A bit more complicated: Longest Zero Segment
Given an array a (of lenght n), find the longest segment in a containing of only 0’s. We’ll simplify this a bit: just find the length of that longest segment. Naive solution is O(n2); but we can also do it in O(n). 1 3 2

27 Analogous problems Longest “flat” segments Longest increasing segments
Longest segment satisfying predicate X : [T]  bool It will be important that X has a “nice” split property, e.g. : X (x:s) = x  X s // for some  27

28 Some helping concepts TAILS gives all tails of an array : TAILS a i = [ a[p i) | p from [ i] ] SEGS gives the segments; we’ll define it in terms of TAILS: SEGS a = [[]] SEGS a (i+1) = SEGS a i TAILS a (i+1)

29 Specification of “Longest Zero Segment”
This gives the length of the logest zero-segment: LZS a i = MAX [# s | s from SEGS a i, ALLZ s ] ALLZ s = … // list s consists of only zeros # s // length of s (COUNT s) We’ll need a split theorem a[0..i+1) a[0..i) 1 3 2 a[i] LZS a (i+1) = LZS a i max LZTail a (i+1)

30 LZS Split The LZS split theorem: // require i0 LZS a (i+1) = LZS a i max LZTail a (i+1) Longest “zero tail” : LZTail a i = MAX [ # t | t from TAILS a i, ALLZ t ]

31 Specifying and Deriving the program 
Inv: lz = LZS a i /\  i  n term. metric : n - i <initialization> while i<n do { <some statement> ; i := i+1 } {* lz = LZS a n *}

32 Deriving the body {* lz = LZS a i /\ 0in /\ i<n *}
{* lz max LZTail a(i+1) = LZS a i max LZTail a(i+1) *} // wp lz := lz max LZTail a (i+1) // intro solution {* lz = LZS a i max LZTail a(i+1) *} // split {* lz = LZS a (i+1) /\ 0i+1n *} // wp i := i+1 {* lz = LZS a i /\ 0in *}

33 So we obtain … Inv: lz = LZS a i /\ 0  i  n {* n0 *}
/\ lt = LZTail a i Inv: lz = LZS a i /\  i  n {* n0 *} <initialization> while i<n do { lz := lz max LZTail a (i+1) ; i := i+1 } How to compute this efficiently?? Idea: compute it side by side. Extend inv to capture this. {* lz = LZS a n *}

34 Splitting LZTail Split
The LZTail split theorem: // require i0 LZTail a (i+1) = a[i]0  | (LZTail a i) + 1 a[0..i+1) a[0..i) 1 3 2 a[i] LZTail a (i+1) = a[i]  0  | …

35 Continue… {* lz = LZS a i /\ lt = LZTail a i /\ 0  i  n /\ i<n *}
{* a[i]0  0 | lt = a[i]0  0 | LZTail a i + 1 *} lt := a[i]0  0 | lt + 1 {* lt = a[i]0  0 | LZTail a i + 1 *} // split {* lt = LZTail a (i+1) *} lz := lz max LZTail a (i+1) ; i := i+1 replace this with “lt” {* lz = LZS a i /\ lt = LZTail a i /\ … *}

36 Finishing it i:=0 ; lz,lt := 0,0 {* n0 *}
Inv : lz = LZS a i /\ lt = LZTail a i /\  i  n <initialization> while i<n do { if a[i]0 then lt:=0 else lt:=lt+1 ; lz := lz max lt i := i+1 } {* lz = LZS a n *}


Download ppt "Some heuristics for deriving invariant LN sections 6.7 , 6.8"

Similar presentations


Ads by Google