0

The standard library's AVL tree implementation uses dependent pairs to store key-value pairs. I have two such pairs whose keys (k and k′) I have shown to be equal (k≡k′). They also contain the same value (v). I'd like to prove that the pairs are equal. Here's the goal:

open import Agda.Primitive

open import Data.Nat
open import Data.Nat.Properties
open import Data.Product
open import Relation.Binary.PropositionalEquality

open import Data.AVL.Indexed <-strictTotalOrder

module Repro (ℓ : Level) (V : Value ℓ) where

Val = Value.family V
V≈ = Value.respects V

proof : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , V≈ k≡k′ v)
proof k k′ v k≡k′ = {!!}

I tried rewriting with k≡k′, which turns k on the LHS into k′ and k≡k′ on the RHS into refl. That's where I get stuck. I feel like I'm missing something obvious, as this seems a pretty basic thing to do.

(As an exercise, I'm trying to prove the standard library's AVL tree insertion correct. Hence my recent obsession with its AVL tree code.)

Update

Hmm. Maybe this isn't as trivial as I thought, at least without knowing more about V. After all, what I currently know is that:

  • There's a way to turn a value of type Val k into a value of type Val k′ - by way of V≈.

  • That V≈ refl takes a Val foo and returns a Val foo.

What I don't know at this point is that V≈ refl is the identity function, which seems to be what I'd need to do my proof.

Update II

If, for example, I knew that V≈ was actually subst Val, then my proof would be:

proof′ : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , subst Val k≡k′ v)
proof′ k k′ v k≡k′ rewrite k≡k′ = cong (k′ ,_) refl

So, I guess my question ultimately is:

  • Can I complete my original proof with what I have?

  • If not, then what property do I need V≈ to have to complete my original proof (i.e., to "restrict it to functions that are like subst")?

Update III

I am providing more context in response to MrO's comment. Instead of stripping down my actual proof, I'll provide something even simpler. Let's prove a special case of AVL tree insertion, which leads to the same problem that I'm facing in the general case.

Let's prove that inserting a value v for key k into an empty tree and then looking up the value for key k yields the value v that we inserted.

Let's get set up:

open import Agda.Primitive using (Level)
open import Data.Maybe using (just)
open import Data.Nat using (ℕ)
open import Data.Nat.Properties using (<-strictTotalOrder ; <-cmp)
open import Data.Product using (proj₂)
open import Function using () renaming (const to constᶠ)
open import Relation.Binary using (tri< ; tri≈ ; tri>)
open import Relation.Binary.PropositionalEquality using (_≡_ ; refl )
open import Relation.Nullary.Negation using (contradiction)

open import Data.AVL.Indexed <-strictTotalOrder

module Simple {ℓ : Level} (V : Value ℓ) where

private
  Val = Value.family V
  V≈  = Value.respects V

Now, a function to create an empty tree:

make-empty : ∀ {l u} → l <⁺ u → Tree V l u 0
make-empty = leaf

And the proof:

proof : ∀ {l u} (k : ℕ) (v : Val k) (l<u : l <⁺ u) (l<k<u : l < k < u) →
  lookup k (proj₂ (insertWith k (constᶠ v) (make-empty l<u) l<k<u)) l<k<u ≡ just v

proof k v l<u l<k<u with <-cmp k k
... | tri< _ p _ = contradiction refl p
... | tri> _ p _ = contradiction refl p
... | tri≈ _ p _ rewrite p = {!!}

The goal that I'm trying to fill in is just (V≈ refl v) ≡ just v.

123omnomnom
  • 294
  • 1
  • 10
  • About your second update: your proof can simply be `proof′ _ _ _ refl = refl` and also instead of using `subst Val k≡k′ v` couldn't you simply use `v` ? I'm confused as to what really needs to be proved in your question. – MrO Jun 30 '20 at 21:36
  • Thank you for looking into this! Let me try to rephrase. `V≈ : x ≡ y → Val x → Val y` is a `subst`-like function for turning a value of type `Val x` into a value of type `Val y`. It is a parameter to the AVL tree module, so I don't know its definition, only its type. I'd like to generalize the proof in my second update from `subst` to `V≈`. As I understand it, the proof in my second update uses the fact that `subst P refl` is the identity function, so that `subst Val k≡k′ v ≡ v`. I can't seem to use this fact for `V≈`, as I don't know that `V≈ refl` is the identity function. – 123omnomnom Jul 01 '20 at 15:23
  • Where all of this comes from: Given an AVL tree with a value `v′ : Val k′` at key `k′ : ℕ`, do a lookup in the tree with a key `k : ℕ` where `k ≡ k′`. The lookup finds the value `v′ : Val k′` in the tree. The AVL code now uses `V≈` to turn `v′` from a `Val k′` into a `Val k` to return. We did the lookup with `k` and not `k′`, so we get back a `Val k` - even though `k ≡ k′`. In my proof about AVL tree insertion I am stuck at `V≈ refl v′ ≡ v′`. It seems to me that I cannot proceed without knowing more about `V≈`. And so I wonder whether I am missing anything. – 123omnomnom Jul 01 '20 at 15:43
  • Could you please add the englobing proof about lookup which you just described to your post ? – MrO Jul 01 '20 at 15:45
  • The second update was meant to say: If we were talking about the specific function `subst` instead of the general function `V≈`, then I'd know how to finish the proof. – 123omnomnom Jul 01 '20 at 15:46
  • The full proof is around 300 lines right now. Let me try to reduce its size by postulating a few things while still giving enough context. Let me see. Thanks again for looking into this. – 123omnomnom Jul 01 '20 at 15:48
  • No worries, I like to look into Agda issues because I often learn new things myself by doing so. – MrO Jul 01 '20 at 15:56
  • As it stands, there is no chance you can prove your property because `V≈` transforms an element of type `Val k` into an element of type `Val k'` but as you noticed there is no reason why these elements should be equal. Maybe by providing the rest of your code we could see if it actually stands or not with the other elements at your disposal. – MrO Jul 01 '20 at 17:03
  • You could postulate, or take as a module parameter, the fact that `V≈` leaves the value unchanged, as follows: `postulate lemma : ∀ {k k′} {v : Val k} → (k≡k′ : k ≡ k′) → v ≡ subst _ (sym k≡k′) (V≈ k≡k′ v)` After that, your proof becomes: ̀`proof : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , V≈ k≡k′ v) proof k .k v refl = cong (k ,_) (lemma refl)` – MrO Jul 01 '20 at 17:12
  • Oh, yes - good idea. I'll ask the module user to pass in an appropriate proof. It's their job already to pass in a `Value`, so if they want a correctness proof for insertion, then I think it's fair to ask them for a proof that `V≈ refl` is the identity for their `Value`. After all, they know the properties of their `Value` best. Would you like to paste your last comments into an answer? I'll accept it then. – 123omnomnom Jul 01 '20 at 17:33
  • I'm not still totally convinced that this is the best we can come up with, but If I don't find anything else, I'll make it an answer. – MrO Jul 01 '20 at 17:34
  • 1
    Thanks! One other thing that I tried to also explore (within the limits of my limited understanding) was some kind of uniqueness or irrelevance property for `Val k`, so that equality of types would imply equality of values. If I understand things correctly, then we wouldn't need to know anything about `V≈ refl` other than the fact that it takes a `Val k` and produces a `Val k`. – 123omnomnom Jul 01 '20 at 17:40
  • After thinking about it for some more time, I decided to post the previous answer. Since the `lookup` function always returns a transformed value using `V≈`, I convinced myself that no such result can be proved without the assumption which I made in my previous comment. – MrO Jul 01 '20 at 19:01

1 Answers1

1

You could postulate, or take as a module parameter, the fact that V≈ leaves the value unchanged, as follows:

postulate lemma : ∀ {k k′} {v : Val k} → (k≡k′ : k ≡ k′) → v ≡ subst _ (sym k≡k′) (V≈ k≡k′ v) 

After that, your proof becomes:

proof : (k k′ : ℕ) → (v : Val k) → (k≡k′ : k ≡ k′) → (k , v) ≡ (k′ , V≈ k≡k′ v) 
proof k ._ _ refl = cong (k ,_) (lemma refl)
MrO
  • 1,291
  • 7
  • 14