BISIMULATION ON STREAMS
A bisimulation is a relation ~ on stream such that:
if s1~s2 then (head s1) = (head s2) and (tail s1) ~ (tail s2)
Coinduction Principle:
Bisimulation implies equality:
if ~ is a bisimulation and s1 ~ s2, then s1 = s2
> import Streams
In the file Streams.lhs we defined the stream of natural numbers
in two different ways, nat and nat'.
We want to prove that the two streams are equal.
We do it by using the coinduction principle.
Let's define a bisimulation relation:
s1 ~ s2 if there is a natural number n such that
s1 = n:(s1+1) and s2 = unfold id (+1) n
Proof that it is a bisimulation:
Suppose s1 ~ s2, then
head s1 = head (n:(s1+1)) = n
head s2 = head (unfold id (+1) n) = id n = n
therefore (head s1) = (head s2);
tail s1 = s1+1 = (n:(s1+1))+1
= (n+1) : (s1+1)+1
= (n+1) : (tail s1)+1
tail s2 = tail (unfold id (+1) n)
= unfold id (+1) (n+1)
therefore (tail s1) ~ (tail s2)
In conclusion ~ is a bisimulation.
Since nat ~ nat', we have, by the coinduction principle,
nat = nat'.
Hint:
Very often, as in the example above, the bisimulation that we need
relates generalizations of the definition of the two streams that
we want to equate.
In the example, we generalized the relation by replacing the starting
point 0 with an arbitrary number n.
Exercise:
Make yourself familiar with this proof technique by proving that
fac = fac' and fib = fib'.
A third way of defining the same stream uses the traditional
recursive form of the Fibonacci function:
> rfib :: Integer -> Integer
> rfib 0 = 0
> rfib 1 = 1
> rfib n = rfib (n-1) + rfib (n-2)
We want to prove that
map rfib [0..] = fib
We can do it again by using the coinduction principle.
The relation we are using is:
s1 ~ s2 iff there are two numbers a and b such that
s1 = a : b : s1 + (tail s1) and
s2!!0 = a and s2!!1 = b and s2!!n = s2!!(n-1)+s2!!(n-2) for n>1
Verification that ~ is a bisimulation.
Suppose s1 ~ s2 (with parameters a and b)
head s1 = a = s2!!0 = head s2
tail s1 = b : s1 + (tail s1)
= b : (head s1 : tail s1) + (b : s1 + (tail s1))
= b : (a+b) : tail s1 + s1 + tail s1
= b : (a+b) : tail s1 + tail (tail s1)
(tail s2)!!0 = s2!!1 = b
(tail s2)!!1 = s2!!2 = s2!!1 + s2!!0 = b + a
(tail s2)!!n = s2!!(n+1) = s2!!n + s2!!(n-1)
= (tail s2)!!(n-1) + (tail s2)!!(n-2) if n>1
Therefore (tail s1)~(tail s2) with parameters b and a+b.
In conclusion ~ is a bisimulation, therefore it implies equality
by the coinduction principle.
We can show that fib ~ map rfib [0..] with parameters 0 and 1:
fib = 0 : 1 : fib + (tail fib) by definition
(map rfib [0..])!!0 = rfib 0 = 0
(map rfib [0..])!!1 = rfib 1 = 1
(map rfib [0..])!!n = rfib n for n>1
= rfib (n-1) + rfib (n-2)
= (map rfib [0..])!!(n-1) + (map rfib [0..])!!(n-2)
In conclusion, fib = map rfib [0..] by the coinduction principle.
Exercise.
The definition of rfib is, as is well-known, very inefficient.
On the other hand fib is an efficient way of computing it.
We can give an efficient version of the recursive definition by:
> re2fib :: Integer -> (Integer,Integer)
> re2fib 0 = (0,1)
> re2fib n = let (a,b) = re2fib (n-1) in (b,a+b)
> refib :: [Integer]
> refib = map (fst.re2fib) [0..]
Prove, using the coinduction principle, that refib = fib.
Final exercise.
Prove that the following streams are equal: al0 = alt1 = alt2.
> power :: [Integer] -> [Integer] -> [Integer]
> power = zipWith (^)
> alt0 :: [Integer]
> alt0 = power (-1) nat
> alt1 :: [Integer]
> alt1 = 1:alt1*(-1)
> alt2 :: [Integer]
> alt2 = fibt ^ 2 - fib * fibtt
> fibt = tail fib
> fibtt = tail fibt