Zulip Chat Archive

Stream: lean4

Topic: Define a equivalence between SimpleQueue and EfficientQueue


Bernardo Borges (Feb 22 2024 at 17:51):

Following the CS 1951x Course that covers The Hitchhiker's Guide to Logical Verification book, we arrive at chapter 5, functional programming. We have already defined a SimpleQueue with these properties:

* `SimpleQueue.emp : SimpleQueue α`, an empty queue
* `SimpleQueue.enq : SimpleQueue α  α  SimpleQueue α`, a function that
  enqueues a value onto an existing queue (i.e., `SimpleQueue.enq sq x` should
  evaluate to the queue resulting from sticking `x` at the back of the queue
  represented by `sq`)
* `SimpleQueue.deq : SimpleQueue α  Option (α × SimpleQueue α)`, a function
  that dequeues from an existing queue. This should return `none` if called on
  an empty queue; otherwise, it should return `some` applied to a tuple
  containing the dequeued element in the first position and the rest of the
  queue in the second

Now we want to prove that another implementation called EfficientQueue is equivalent to the naive approach (It uses two lists), meaning I have to define
def Equiv {α : Type} : SimpleQueue α → EfficientQueue α → Prop
However, I am not sure what this should look like, as I never "returned a Prop" before. As a next step, I want to understand how am I supposed to use this to prove the base case, that two empty queues of both types are equivalent theorem emp_equiv {α : Type} : Equiv (α := α) SimpleQueue.emp EfficientQueue.emp.

This is my attempt:

def Equiv {α : Type} : SimpleQueue α  EfficientQueue α  Prop :=
  λ sq eq => match sq.deq with
   | none         => eq.deq = none
   | some (x,sq') => match eq.deq with
      | none => False
      | some (y,eq') => x = y  (Equiv sq' eq')
decreasing_by sorry

Note the sorry for termination. Is this the right track? I am open to suggestions!

James Gallicchio (Feb 22 2024 at 19:57):

I think in this case the easiest thing to do is something like

def SimpleQueue.toList : SimpleQueue α -> List α := ...
def EfficientQueue.toList : EfficientQueue α -> List α := ...

def Equiv {α : Type} (sq : SimpleQueue α) (eq : EfficientQueue α) : Prop :=
  sq.toList = eq.toList

to avoid the recursive definition and get access to all the nice List lemmas :)

James Gallicchio (Feb 22 2024 at 19:58):

(and then you can prove stuff like EfficientQueue.emp.toList = [] as lemmas, from which the equivalences should be straightforward)

Bernardo Borges (Feb 23 2024 at 04:40):

Interesting, from this suggestion I implemented the conversion:

def toList : SimpleQueue α  List α := id

def toList : EfficientQueue α  List α
| ([],xs)    => xs
| (xs, ys) => xs.reverse ++ ys

This way the proof for equivalence for empty queues is rfl:

theorem emp_equiv {α : Type} :
  Equiv (α := α) SimpleQueue.emp EfficientQueue.emp := by rfl

Now the complex part is to prove the equivalence for enqueuing:

theorem enq_equiv {α : Type} :
   (sq : SimpleQueue α) (eq : EfficientQueue α) (x : α),
  Equiv sq eq  Equiv (sq.enq x) (eq.enq x) := sorry

Any ideas here ?

James Gallicchio (Feb 23 2024 at 05:37):

You should then be able to show that (sq.enq x).toList = sq.toList ++ [x] (or whichever side it's getting enqueued at) as a lemma, same for eq, and then the enq_equiv lemma is a matter of unfolding definitions and doing some rewrites


Last updated: May 02 2025 at 03:31 UTC