Zulip Chat Archive
Stream: general
Topic: quotient.hrec_on₂
Sean Leather (Aug 06 2018 at 11:09):
I've got a definition:
def kunion : m₁.nodup_keys → m₂.nodup_keys → multiset (sigma β) := @quotient.hrec_on₂ _ _ _ _ (λ (m₁ m₂ : multiset (sigma β)), m₁.nodup_keys → m₂.nodup_keys → multiset (sigma β)) m₁ m₂ (λ l₁ l₂ (d₁ : l₁.nodup_keys) (d₂ : l₂.nodup_keys), l₁.kunion l₂) $ λ l₁ l₂ l₃ l₄ p₁₃ p₂₄, hfunext (by rw perm_nodup_keys p₁₃) $ λ (d₁ : l₁.nodup_keys) (d₃ : l₃.nodup_keys) _, hfunext (by rw perm_nodup_keys p₂₄) $ λ (d₂ : l₂.nodup_keys) (d₄ : l₄.nodup_keys) _, heq_of_eq $ quotient.sound $ perm_kunion d₂ d₄ p₁₃ p₂₄ local infixr ` k∪ `:67 := kunion
and I want to prove the left and right units:
@[simp] theorem zero_kunion {m : multiset (sigma β)} : ∀ (d : m.nodup_keys), nodup_keys_zero k∪ d = m @[simp] theorem kunion_zero {m : multiset (sigma β)} : ∀ (d : m.nodup_keys), d k∪ nodup_keys_zero = m
I'm stuck on how to proceed. If I use quotient.induction_on m
, I just unfold until I get down to quot.rec_on ↑(hd :: tl)
or quot.rec_on ↑nil
, but I don't know how to go further. (At one point, I believe I even made the simplifier loop infinitely.)
Any suggestions on how to prove these?
Mario Carneiro (Aug 06 2018 at 11:12):
Wow, that's a weird notation
Mario Carneiro (Aug 06 2018 at 11:12):
does it have to be a partial function?
Sean Leather (Aug 06 2018 at 11:15):
does it have to be a partial function?
I don't follow you.
Mario Carneiro (Aug 06 2018 at 11:16):
you could make it return empty when the inputs don't have nodup_keys
Mario Carneiro (Aug 06 2018 at 11:17):
or it could be a roption
if you are worried about the performance cost of checking nodup_keys
Sean Leather (Aug 06 2018 at 11:21):
Sorry, Mario, your use of “it” in multiple places is a bit too vague for me. Are you suggesting I use a different definition for kunion
? If so, what is the type signature you're referring to?
Sean Leather (Aug 06 2018 at 11:28):
Btw, I'm not asking for a completed solution. You're welcome to give me only hints or suggestions. :smile:
Reid Barton (Aug 06 2018 at 11:29):
I think def kunion : multiset (sigma β) → multiset (sigma β) → roption (multiset (sigma β))
and then prove that kunion
is defined exactly when each multiset
is nodup_keys
Sean Leather (Aug 06 2018 at 11:31):
Reid: Hmm, okay, thanks.
Sean Leather (Aug 06 2018 at 11:32):
And what's the advantage to this approach? Is it simplicity of the definition and related theorems or performance or both?
Reid Barton (Aug 06 2018 at 11:32):
Then you can avoid all this dependent eliminator stuff... although it's not clear to me whether your problem is related to this
Sean Leather (Aug 06 2018 at 11:33):
Okay, well, I'll give it a shot.
Chris Hughes (Aug 06 2018 at 11:33):
Just proving at quotient.hrec_on_beta
lemma might help.
Reid Barton (Aug 06 2018 at 11:34):
Yes. Is that ↑
just quotient.mk
?
Sean Leather (Aug 06 2018 at 11:34):
Yes, I think so.
Sean Leather (Aug 06 2018 at 11:34):
Chris: I wondered the same thing. I'm not sure how to start with that.
Reid Barton (Aug 06 2018 at 11:35):
Well, quotient.rec_on
applied to quotient.mk
should reduce...
Chris Hughes (Aug 06 2018 at 11:35):
Or use show
Sean Leather (Aug 06 2018 at 11:37):
I suppose it would look something like:
lemma quot.ind_beta (p : ∀ a, β (quot.mk r a)) (a : α) : (ind p (quot.mk r a) : β (quot.mk r a)) = p a
Mario Carneiro (Aug 06 2018 at 12:00):
that is trivially true, since both sides are propositions
Mario Carneiro (Aug 06 2018 at 12:01):
The advantage of using roption
is avoiding all the hrec
mess. I've had to define partial functions over quotients before, and I wish I'd thought of this then
Sean Leather (Aug 06 2018 at 12:03):
that is trivially true, since both sides are propositions
Are you referring to quot.ind_beta
, which is in the core library, or something else?
The advantage of using
roption
is avoiding all thehrec
mess. I've had to define partial functions over quotients before, and I wish I'd thought of this then
Great! I'm working on it now.
Reid Barton (Aug 06 2018 at 12:08):
I think someone probably copied lift_beta
to ind_beta
without realizing it was rather unnecessary.
Sean Leather (Aug 06 2018 at 12:10):
Ah! I see what you're saying now. I didn't look that closely at ind_beta
.
Sean Leather (Aug 06 2018 at 12:14):
This is definitely a much nicer definition:
def kunion' (m₁ m₂ : multiset (sigma β)) : roption (multiset (sigma β)) := quotient.lift_on₂ m₁ m₂ (λ l₁ l₂, roption.mk (l₁.nodup_keys ∧ l₂.nodup_keys) (λ _, (l₁.kunion l₂ : multiset (sigma β)))) $ λ l₁ l₂ l₃ l₄ p₁₃ p₂₄, roption.ext' (and_congr (perm_nodup_keys p₁₃) (perm_nodup_keys p₂₄)) (λ ⟨d₁, d₂⟩ ⟨d₃, d₄⟩, quotient.sound $ perm_kunion d₂ d₄ p₁₃ p₂₄)
Mario Carneiro (Aug 06 2018 at 12:18):
so now the theorem you want is either m k∪ 0 = some m
or m ∈ m k∪ 0
(they are equivalent)
Sean Leather (Aug 06 2018 at 12:20):
Right.
Mario Carneiro (Aug 06 2018 at 12:22):
and the coercion lemma you want says l₁.nodup_keys → l₂.nodup_keys → ↑l₁ k∪ ↑l₂ = some (l₁.kunion l₂)
Sean Leather (Aug 06 2018 at 12:33):
Got that, thanks. How should I coerce the 0 (multiset.zero
) for kunion' 0 ↑l
?
Sean Leather (Aug 06 2018 at 12:35):
If I do simp [has_zero.zero, multiset.zero]
, lean never ends.
Mario Carneiro (Aug 06 2018 at 12:37):
you can just force it to unfold by applying kunion_coe.trans _
Mario Carneiro (Aug 06 2018 at 12:37):
or you can rewrite with coe_nil_eq_zero
Sean Leather (Aug 06 2018 at 14:05):
The aforementioned theorems:
@[simp] theorem zero_kunion : ∀ (d : m.nodup_keys), kunion' 0 m = roption.some m := quotient.induction_on m $ λ _ d, (kunion_coe nodup_keys_zero d).trans rfl @[simp] theorem kunion_zero : ∀ (d : m.nodup_keys), kunion' m 0 = roption.some m := quotient.induction_on m $ λ _ d, (kunion_coe d nodup_keys_zero).trans (by simp)
Sean Leather (Aug 06 2018 at 14:21):
One last related question: Now that I have an roption
-wrapped multiset
, how should I specify theorems that involve the result of kunion'
? For example, I had:
@[simp] theorem mem_kunion {s : sigma β} : ∀ (d₁ : m₁.nodup_keys) (d₂ : m₂.nodup_keys), disjoint m₁.keys m₂.keys → (s ∈ d₁ k∪ d₂ ↔ s ∈ m₁ ∨ s ∈ m₂) := quotient.induction_on₂ m₁ m₂ $ λ _ _ _ _, mem_kunion_iff
Should this become...?
@[simp] theorem mem_kunion' {s : sigma β} : ∀ (d₁ : m₁.nodup_keys) (d₂ : m₂.nodup_keys), disjoint m₁.keys m₂.keys → ∃ m ∈ kunion' m₁ m₂, s ∈ m ↔ s ∈ m₁ ∨ s ∈ m₂ := quotient.induction_on₂ m₁ m₂ $ λ l₁ l₂ d₁ d₂ dk, ⟨_, roption.eq_some_iff.mp (kunion_coe d₁ d₂), mem_kunion_iff dk⟩
Specifically, I mean: should I use a pattern like ∃ m ∈ kunion' m₁ m₂, ...
for theorems like this, or is there a better way?
Mario Carneiro (Aug 06 2018 at 14:35):
I would take m ∈ kunion' m₁ m₂
as a hypothesis and prove s ∈ m ↔ s ∈ m₁ ∨ s ∈ m₂
Mario Carneiro (Aug 06 2018 at 14:36):
of course this hypothesis eliminates the need for d1 and d2
Mario Carneiro (Aug 06 2018 at 14:37):
alternatively, you can define kunion
as (kunion' m1 m2).get <d1, d2>
and have all your old theorems back
Sean Leather (Aug 06 2018 at 17:07):
True. I'm not sure which is a better definition to work with. If I were using the multiset
interface directly, I would lean towards defining kunion
as (kunion' m1 m2).get <d1, d2>
. But since it's really meant to be the underlying implementation of finmap
, perhaps it's not necessary.
Sean Leather (Aug 06 2018 at 17:08):
Also, do you think I should use roption
+ quotient.lift_on
consistently instead of quotient.hrec_on
? I don't have any more uses of quotient.hrec_on₂
, but I do have a number of uses of quotient.hrec_on
.
Sean Leather (Aug 07 2018 at 07:50):
@Mario Carneiro I'd like to get your thoughts on this :point_up:. I haven't had any problems with quotient.hrec_on
up to now, but maybe things would just be nicer all around if I used roption
more. I'm not sure.
Mario Carneiro (Aug 07 2018 at 07:51):
I think if it works once, it will probably work again
Mario Carneiro (Aug 07 2018 at 07:52):
Also, another option I forgot to mention was to make kunion
a nondependent function, using the fact that nodup_keys
is decidable
Sean Leather (Aug 07 2018 at 07:52):
Just to be clear about what you mean, do you think I should change the defs here to use roption
?
Sean Leather (Aug 07 2018 at 07:53):
(Just search for hrec_on
.)
Mario Carneiro (Aug 07 2018 at 07:53):
Even if we suppose that checking this is expensive, it doesn't matter if you are just using it as an abstract version so you can prove equations about it
Sean Leather (Aug 07 2018 at 07:53):
Also, another option I forgot to mention was to make
kunion
a nondependent function, using the fact thatnodup_keys
is decidable
What do you mean by this?
Mario Carneiro (Aug 07 2018 at 07:55):
define kunion m1 m2 = if h : m1.nodup_keys /\ m2.nodup_keys then (kunion' m1 m2).get h else 0
Sean Leather (Aug 07 2018 at 07:56):
That's an interesting suggestion.
Mario Carneiro (Aug 07 2018 at 07:57):
in fact, even if nodup_keys
wasn't decidable you could make this definition anyway noncomputably and just not use it for evaluation
Sean Leather (Aug 07 2018 at 08:00):
Hmm, yes, I think I like this definition of kunion
. I don't have to pass around the m1.nodup_keys
everywhere.
Sean Leather (Aug 07 2018 at 08:01):
Okay, well, I'll play around with it.
Mario Carneiro (Aug 07 2018 at 08:01):
Yet more alternatively, you could define the subtype. Didn't you have finmap
at one point for this?
Sean Leather (Aug 07 2018 at 08:02):
I decided to go with the structure
for the same reasons finset
is a structure
:
structure finmap (α : Type u) (β : α → Type v) : Type (max u v) := (val : multiset (sigma β)) (nodup_keys : val.nodup_keys)
Sean Leather (Aug 07 2018 at 08:02):
Mainly, for type class instance resolution.
Mario Carneiro (Aug 07 2018 at 08:03):
that's fine, my point was that you can define finmap.rec_on
to encapsulate this definition pattern
Mario Carneiro (Aug 07 2018 at 08:03):
and this way you never have to carry around any proofs since they are embedded in the type
Sean Leather (Aug 07 2018 at 08:04):
You mean the if h : m.nodup_keys ... then ... else ...
pattern?
Mario Carneiro (Aug 07 2018 at 08:04):
no, the roption
or hrec_on
version
Mario Carneiro (Aug 07 2018 at 08:04):
(it doesn't really matter too much which one you use, since it only has to be done once)
Sean Leather (Aug 07 2018 at 08:06):
Oh.... I'm awfully dumb today. So, define a finmap.rec_on
that takes an roption (multiset (sigma β))
to a finmap
?
Sean Leather (Aug 07 2018 at 08:07):
Err, actually the arrow goes the other way...
Sean Leather (Aug 07 2018 at 08:07):
Anyway, it'd be a higher-order function.
Sean Leather (Aug 07 2018 at 08:07):
Yeah, I think I see it.
Mario Carneiro (Aug 07 2018 at 08:08):
finmap.rec_on
takes a finmap A B
, a function list (sigma B) -> C
, and a proof that this function is equal up to permutation when the arguments have nodup_keys
Sean Leather (Aug 07 2018 at 08:08):
Right.
Sean Leather (Aug 07 2018 at 08:10):
So, given that, I would be skipping defining all of the defs and theorems for multiset
and define them for only list
and finmap
?
Mario Carneiro (Aug 07 2018 at 08:13):
right
Mario Carneiro (Aug 07 2018 at 08:13):
You can reconstruct the multiset definitions from the finmap ones by the if ... else 0
trick
Mario Carneiro (Aug 07 2018 at 08:15):
I think multisets are a good stepping stone if you can actually define functions on them, but in your case the functions already have to assume nodup just to be well defined, so they've already jumped to finmap
Sean Leather (Aug 07 2018 at 08:15):
That's true.
Sean Leather (Aug 07 2018 at 08:17):
Okay, I'm convinced.
Mario Carneiro (Aug 07 2018 at 08:20):
I notice you have theorems like s.1 ∈ (m.map_snd f).keys ↔ s.1 ∈ m.keys
with several variations. Why isn't this just (m.map_snd f).keys = m.keys
?
Sean Leather (Aug 07 2018 at 08:21):
Because I use the mem
one in the non-mem
.
Sean Leather (Aug 07 2018 at 08:22):
I suppose I don't have to.
Mario Carneiro (Aug 07 2018 at 08:23):
the proof is just map_comp
Mario Carneiro (Aug 07 2018 at 08:23):
you shouldn't use mem
to try to characterize a multiset, it gets messy
Sean Leather (Aug 07 2018 at 08:25):
The other problem with the map_snd
/keys
theorems is that I wanted to use it in finmap
, but the best I could come up with was using [inhabited (∀ a, β₁ a)]
. I'm not happy with that solution.
Sean Leather (Aug 07 2018 at 08:25):
the proof is just
map_comp
The proof of which?
Mario Carneiro (Aug 07 2018 at 08:27):
I'm really confused about your confusion. It should be provable that (m.map_snd f).keys = m.keys
, this makes the last 7 theorems or so unnecessary and it doesn't require any weird assumptions
Sean Leather (Aug 07 2018 at 08:31):
Okay, I think I see what you're saying. I'll give it a shot.
Mario Carneiro (Aug 07 2018 at 08:31):
wait, just the last 4. The ones about (m.map f).keys
are a bit awkward because f : sigma B1 -> sigma B2
can mingle keys and values in an unpredictable way. How about defining m.map f g
where f : A1 -> A2
and g : ∀ a, B1 a -> B2 (f a)
; then you should be able to prove (m.map f g).keys = m.keys.map f
and life is good
Sean Leather (Aug 07 2018 at 08:33):
Btw, did you mean map_map
instead of map_comp
?
Mario Carneiro (Aug 07 2018 at 08:34):
yes
Mario Carneiro (Aug 07 2018 at 08:34):
it's written backwards for simp lemmas because comp
is dumb
Mario Carneiro (Aug 07 2018 at 08:34):
so the name becomes map_map
instead of map_comp
Sean Leather (Aug 08 2018 at 07:53):
I could only figure out how to do a general 2-arg finmap
recursor using quotient.hrec_on₂
:
protected def lrec_on₂ {γ : Sort*} (f g : finmap α β) (φ : ∀ {l₁ l₂ : list (sigma β)}, l₁.nodup_keys → l₂.nodup_keys → γ) (c : ∀ {l₁ l₂ l₃ l₄} (p₁₃ : l₁ ~ l₃) (p₂₄ : l₂ ~ l₄) d₁ d₂ d₃ d₄, φ d₁ d₂ = φ d₃ d₄) : γ := @quotient.hrec_on₂ _ _ _ _ (λ (m₁ m₂ : multiset (sigma β)), m₁.nodup_keys → m₂.nodup_keys → γ) f.val g.val (λ l₁ l₂ (d₁ : l₁.nodup_keys) (d₂ : l₂.nodup_keys), φ d₁ d₂) (λ l₁ l₂ l₃ l₄ p₁₃ p₂₄, hfunext (by rw list.perm_nodup_keys p₁₃) $ λ d₁ d₃ _, hfunext (by rw list.perm_nodup_keys p₂₄) $ λ d₂ d₄ _, heq_of_eq $ c p₁₃ p₂₄ d₁ d₂ d₃ d₄) f.nodup_keys g.nodup_keys
I have a general 2-arg finmap.lift_on₂
:
protected def lift_on₂ {γ : Type*} (f g : finmap α β) (φ : ∀ {l₁ l₂ : list (sigma β)}, l₁.nodup_keys → l₂.nodup_keys → γ) (c : ∀ {l₁ l₂ l₃ l₄} (p₁₃ : l₁ ~ l₃) (p₂₄ : l₂ ~ l₄) d₁ d₂ d₃ d₄, φ d₁ d₂ = φ d₃ d₄) : roption γ := quotient.lift_on₂ f.val g.val (λ l₁ l₂, roption.mk (l₁.nodup_keys ∧ l₂.nodup_keys) (λ ⟨d₁, d₂⟩, φ d₁ d₂)) (λ l₁ l₂ l₃ l₄ p₁₃ p₂₄, roption.ext' (and_congr (list.perm_nodup_keys p₁₃) (list.perm_nodup_keys p₂₄)) (λ ⟨d₁, d₂⟩ ⟨d₃, d₄⟩, c p₁₃ p₂₄ d₁ d₂ d₃ d₄))
But it seems like the roption.dom
has a pair of lists, so this only seems useful in combination with quotient.induction_on₂
. Is that right? Or can I do better?
Mario Carneiro (Aug 08 2018 at 08:24):
What does the one arg version look like?
Sean Leather (Aug 08 2018 at 08:25):
protected def lrec_on {γ : Sort*} (f : finmap α β) (φ : ∀ {l : list (sigma β)}, l.nodup_keys → γ) (c : ∀ {l₁ l₂} (p : l₁ ~ l₂) d₁ d₂, φ d₁ = φ d₂) : γ := @quotient.hrec_on _ _ (λ (m : multiset (sigma β)), m.nodup_keys → γ) f.val (λ l (d : l.nodup_keys), φ d) (λ l₁ l₂ p, hfunext (by rw list.perm_nodup_keys p) $ λ d₁ d₂ _, heq_of_eq $ c p d₁ d₂) f.nodup_keys
Sean Leather (Aug 08 2018 at 08:30):
There are theorems where quotient.induction_on₂
needs more than just l₁.nodup_keys
and l₂.nodup_keys
:
@[simp] theorem mem_kunion' {s : sigma β} : ∀ (d₁ : m₁.nodup_keys) (d₂ : m₂.nodup_keys), disjoint m₁.keys m₂.keys → ∃ m ∈ kunion' m₁ m₂, s ∈ m ↔ s ∈ m₁ ∨ s ∈ m₂ := quotient.induction_on₂ m₁ m₂ $ λ l₁ l₂ d₁ d₂ dk, ⟨_, roption.eq_some_iff.mp (kunion_coe d₁ d₂), mem_kunion_iff dk⟩
So, I think the above finmap.lift_on₂
definition makes sense.
Mario Carneiro (Aug 08 2018 at 10:10):
I would suggest you state lrec_on
like this:
protected def lrec_on {γ : Sort*} (f : finmap α β) (φ : list (sigma β) → γ) (c : ∀ {l₁ l₂} (p : l₁ ~ l₂), l₁.nodup_keys → l₂.nodup_keys → φ l₁ = φ l₂) : γ :=
Recall that we are trying to avoid partial functions. The function φ
is defined on lists, so there presumably won't be any trouble making arbitrary choices that depend on the order
Mario Carneiro (Aug 08 2018 at 10:11):
Given this it should not be hard to just iterate it twice to get lrec_on₂
Sean Leather (Aug 08 2018 at 12:04):
Okay, so I have the following.
My initial version:
protected def lrec_on {γ : Sort*} (f : finmap α β) (φ : ∀ {l : list (sigma β)}, l.nodup_keys → γ) (c : ∀ {l₁ l₂} (p : l₁ ~ l₂) d₁ d₂, φ d₁ = φ d₂) : γ := @quotient.hrec_on _ _ (λ (m : multiset (sigma β)), m.nodup_keys → γ) f.val (λ l (d : l.nodup_keys), φ d) (λ l₁ l₂ p, hfunext (by rw list.perm_nodup_keys p) $ λ d₁ d₂ _, heq_of_eq $ c p d₁ d₂) f.nodup_keys def erase (f : finmap α β) (a : α) : finmap α β := finmap.lrec_on f (λ l d, ⟨l.kerase a, list.nodup_keys_kerase a d⟩) (λ l₁ l₂ p d₁ d₂, eq_of_veq $ quotient.sound $ list.perm_kerase a d₁ d₂ p)
Your suggestion:
protected def lrec_on' {γ : Sort*} (f : finmap α β) (φ : list (sigma β) → γ) (c : ∀ {l₁ l₂} (p : l₁ ~ l₂), l₁.nodup_keys → l₂.nodup_keys → φ l₁ = φ l₂) : γ := @quotient.hrec_on _ _ (λ (m : multiset (sigma β)), m.nodup_keys → γ) f.val (λ l _, φ l) (λ l₁ l₂ p, hfunext (by simp [list.perm_nodup_keys p]) $ λ d₁ d₂ _, heq_of_eq $ c p d₁ d₂) f.nodup_keys def erase' (f : finmap α β) (a : α) : finmap α β := finmap.lrec_on' f (λ l, ⟨l.kerase a, list.nodup_keys_kerase a _⟩) (λ l₁ l₂ p d₁ d₂, eq_of_veq $ quotient.sound $ list.perm_kerase a d₁ d₂ p)
I can define erase
with lrec_on
, but how do I define erase'
with lrec_on'
?
error: don't know how to synthesize placeholder context: α : Type u, _inst_1 : decidable_eq α, β : α → Type v, f : finmap α β, a : α, l : list (sigma β) ⊢ list.nodup_keys l
Last updated: Dec 20 2023 at 11:08 UTC