Zulip Chat Archive

Stream: general

Topic: Inferring setoid instances


Chris Hughes (Jul 17 2018 at 13:10):

I've had a bit of trouble with setoid instances in quotient rings and groups. Changes the brackets around setoid in quotient.induction_on and similar lemmas form [] to {} improves matters a lot. Is there a downside to this approach? There should always only be one possibility for setoid from the type of q right?

lemma quotient.induction_on' {α : Sort u} {s : setoid α} {β : quotient s  Prop}
  (q : quotient s) (h :  (a : α), β a) : β q := quotient.induction_on q h

Reid Barton (Jul 17 2018 at 13:30):

I have also thought the same thing "why not just infer the relation based on the type of q".

Reid Barton (Jul 17 2018 at 13:31):

I'm guessing you have a type (like, a group) on which you have a relation that depends on some other variable (like, a subgroup) which isn't mentioned in the carrier type?

Reid Barton (Jul 17 2018 at 13:32):

I also found these type class arguments annoying to deal with in this kind of situation, although I don't remember what I did about it.
It's possible that switching to a different elaboration strategy fixed my problem, and I didn't look into exactly why.

Reid Barton (Jul 17 2018 at 13:34):

Or maybe I just used quot methods instead. https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/elab_as_eliminator

Reid Barton (Jul 17 2018 at 13:35):

Yes, now I remember wondering whether mixing quotient with quot.induction_on was a sensible thing to do, and then I saw that TPIL does the same thing in the section on quotients.

Chris Hughes (Jul 17 2018 at 14:06):

I'm not sure what you mean by carrier type, but basically it's struggling to find the setoid instances for the standard relation for quotienting by an ideal. It's particularly bad when I have two ideals in my context, but at the moment I only have one and it's still struggling.

Chris Hughes (Jul 17 2018 at 14:14):

Deleting my instance for preimage of a ring_hom is an ideal helps, even though my lemma has nothing to do with preimages or ring_homs.

Reid Barton (Jul 17 2018 at 14:15):

By carrier I mean the type that you're putting an equivalence relation on.

Reid Barton (Jul 17 2018 at 14:17):

in this case, (the underlying type of) the ring

Reid Barton (Jul 17 2018 at 14:24):

The equivalence relation here depends on the ideal I, which cannot be inferred from the ring or from instance synthesis.
Basically, when you have an instance which has non-typeclass variables to the left of the colon which don't also appear to the right of the colon, I don't see how Lean can ever select the instance by type class inference.

Patrick Massot (Jul 17 2018 at 14:26):

We certainly don't want Lean to guess which ideal we want to quotient. And one can always add local setoid instances if we have a whole section of file where the ideal is fixed.

Chris Hughes (Jul 17 2018 at 14:43):

@pat

We certainly don't want Lean to guess which ideal we want to quotient. And one can always add local setoid instances if we have a whole section of file where the ideal is fixed.

Not actually that easy to add a local attribute that depends on a variable.

Patrick Massot (Jul 17 2018 at 21:23):

I see Mario merged your PR (before anyone added your name to the authors list). Did you solve your instance issue?

Mario Carneiro (Jul 17 2018 at 21:24):

oops

Chris Hughes (Jul 17 2018 at 21:25):

Not really. I think in general we shouldn't be using type class inference for quotient rings and groups, and maybe we need some infrastructure to deal with that, like a whole load of new quotient lemmas. But I'm not sure. I usually find a way round it, but it's a constant nuisance

Kevin Buzzard (Jul 17 2018 at 21:30):

Here's another type class inference issue which Keji pointed out to me:

import group_theory.subgroup

#check is_subgroup

example (G : Type) [group G] (H1 H2 : set G) [is_subgroup H1] [is_subgroup H2] : is_subgroup (H1  H2) :=
{ inv_mem := λ g Hyp,is_subgroup.inv_mem Hyp.1,is_subgroup.inv_mem Hyp.2,

}

->

failed to synthesize type class instance for
G : Type,
_inst_1 : group G,
H1 H2 : set G,
_inst_2 : is_subgroup H1,
_inst_3 : is_subgroup H2
⊢ is_submonoid (H1 ∩ H2)

I just wanted to populate the fields of the structure but I couldn't figure out an easy way to do so without proving is_submonoid (H1 ∩ H2) first and making it an instance

Kevin Buzzard (Jul 17 2018 at 21:30):

Is there a way round this?

Patrick Massot (Jul 17 2018 at 21:35):

example (G : Type) [group G] (H1 H2 : set G) [is_subgroup H1] [is_subgroup H2] : is_subgroup (H1  H2) :=
begin
  refine_struct {..},
  sorry, sorry, sorry
end

state after first line looks good to me

Chris Hughes (Jul 17 2018 at 21:54):

I just wanted to populate the fields of the structure but I couldn't figure out an easy way to do so without proving is_submonoid (H1 ∩ H2) first and making it an instance

It's probably good practice to make inter.is_submonoid an instance first anyway.

Kevin Buzzard (Jul 17 2018 at 21:55):

Yeah but I was teaching.

Kevin Buzzard (Jul 17 2018 at 21:55):

I just wanted it to look relatively easy. In the end I re-defined is_subgroup (and didn't import it)

Patrick Massot (Jul 17 2018 at 22:00):

Full proof could be either

example (G : Type) [group G] (H1 H2 : set G) [is_subgroup H1] [is_subgroup H2] : is_subgroup (H1  H2) :=
{ one_mem := is_submonoid.one_mem H1, is_submonoid.one_mem H2,
  mul_mem := λ a b a_in b_in, is_submonoid.mul_mem a_in.1 b_in.1, is_submonoid.mul_mem a_in.2 b_in.2,
  inv_mem := λ g Hyp, is_subgroup.inv_mem Hyp.1,is_subgroup.inv_mem Hyp.2 }

or

example (G : Type) [group G] (H1 H2 : set G) [is_subgroup H1] [is_subgroup H2] : is_subgroup (H1  H2) :=
begin
  refine_struct {..},
  { exact is_submonoid.one_mem H1, is_submonoid.one_mem H2 },
  { intros a b a_in b_in,
    exact is_submonoid.mul_mem a_in.1 b_in.1, is_submonoid.mul_mem a_in.2 b_in.2 },
  { intros g Hyp,
    exact is_subgroup.inv_mem Hyp.1,is_subgroup.inv_mem Hyp.2 }
end

depending whether you want to get tactical or not. I'm not sure I understand your question

Patrick Massot (Jul 17 2018 at 22:01):

The teaching advantage of the tactical way is what I showed in my first answer: Lean tells you want it wants, even putting names on questions

Mario Carneiro (Jul 17 2018 at 22:02):

I'm going to make a rather radical suggestion and suggest that perhaps subgroup G should be a type on its own, like filter

Kevin Buzzard (Jul 17 2018 at 22:03):

I am surprised the first one works! With no structure fields just the {} Lean complains it has no inv_mem and that type class inference fails to prove is_submonoid. I hadn't expected that just declaring the fields anyway would work.

Kevin Buzzard (Jul 17 2018 at 22:05):

In particular Lean doesn't put all names on questions -- you have to look at what is_submonoid wants -- but that's not too hard.

Patrick Massot (Jul 17 2018 at 22:05):

Did you try my suggestion with three sorry?

Patrick Massot (Jul 17 2018 at 22:05):

The crucial part is Simon's refine_struct tactic

Patrick Massot (Jul 17 2018 at 22:06):

Mario, do you mean bundling the subset and its properties?

Mario Carneiro (Jul 17 2018 at 22:07):

yes, and adding a has_mem instance and so on

Chris Hughes (Jul 17 2018 at 22:07):

How does that solve the setoid problem?

Mario Carneiro (Jul 17 2018 at 22:07):

what setoid?

Mario Carneiro (Jul 17 2018 at 22:08):

it solves the typeclass inference problem

Patrick Massot (Jul 17 2018 at 22:08):

he wants quotients by subgroups

Chris Hughes (Jul 17 2018 at 22:08):

The problem about inferrinf setoid instances for quotient groups and rings.

Mario Carneiro (Jul 17 2018 at 22:08):

example?

Chris Hughes (Jul 17 2018 at 22:09):

I don't have an MWE right now, but having two subgroups around means it uses the wrong one sometimes.

Mario Carneiro (Jul 17 2018 at 22:10):

I don't mean MWE, just sketch the problem. I don't see how two quotient groups with different subgroups can be confused

Chris Hughes (Jul 17 2018 at 22:11):

It always tries to use the setoid instance with the subgroup which comes last in the statement of the theorem.

Mario Carneiro (Jul 17 2018 at 22:12):

why are you inferring a setoid instance?

Reid Barton (Jul 17 2018 at 22:13):

Because the setoid argument to quotient.induction_on is a [] argument for some reason

Mario Carneiro (Jul 17 2018 at 22:14):

you could use quot.induction_on...

Chris Hughes (Jul 17 2018 at 22:16):

What do you suggest in place of quotient.mk?

Chris Hughes (Jul 17 2018 at 22:17):

And there's no quot.lift_on₂

Mario Carneiro (Jul 17 2018 at 22:17):

quot.mk of course

Chris Hughes (Jul 17 2018 at 22:18):

Then I have a really long expression.

Mario Carneiro (Jul 17 2018 at 22:18):

You usually want to make custom versions of all these anyway

Chris Hughes (Jul 17 2018 at 22:18):

A has_coe instance seems like a sensible substitute.

Chris Hughes (Jul 17 2018 at 22:19):

How about quotient.exact?

Mario Carneiro (Jul 17 2018 at 22:20):

You can always use @ if you like the quotient version

Mario Carneiro (Jul 17 2018 at 22:21):

just put (id _) in the typeclass slot and it will unify for it instead of use typeclass inference

Chris Hughes (Jul 20 2018 at 17:18):

I had a go at following Mario's recommendation and not using type class inference for setoids for quotient groups. I wanted to find a solution that made it easy, and initially it wasn't.
I used the follwing definitions

section quotients
variables {α : Sort*} {β : Type*} {γ : Type*} {φ : Type*}
  {s₁ : setoid α} {s₂ : setoid β} {s₃ : setoid γ}

@[elab_as_eliminator, reducible]
def quotient.lift_on' (q : quotient s₁) (f : α  φ)
  (h :  a b, @setoid.r α s₁ a b  f a = f b) : φ := quotient.lift_on q f h

@[elab_as_eliminator, reducible]
def quotient.lift_on₂' (q₁ : quotient s₁) (q₂ : quotient s₂) (f : α  β  γ)
  (h :  a₁ a₂ b₁ b₂, @setoid.r α s₁ a₁ b₁  @setoid.r β s₂ a₂ b₂  f a₁ a₂ = f b₁ b₂) : γ :=
quotient.lift_on₂ q₁ q₂ f h

@[elab_as_eliminator]
lemma quotient.induction_on' {p : quotient s₁  Prop} (q : quotient s₁)
  (h :  a, p (quot.mk s₁.1 a)) : p q := quotient.induction_on q h

@[elab_as_eliminator]
lemma quotient.induction_on₂' {p : quotient s₁  quotient s₂  Prop} (q₁ : quotient s₁)
  (q₂ : quotient s₂) (h :  a₁ a₂, p (quot.mk s₁.1 a₁) (@quotient.mk β s₂ a₂)) : p q₁ q₂ :=
quotient.induction_on₂ q₁ q₂ h

@[elab_as_eliminator]
lemma quotient.induction_on₃' {p : quotient s₁  quotient s₂  quotient s₃  Prop}
  (q₁ : quotient s₁) (q₂ : quotient s₂) (q₃ : quotient s₃)
  (h :  a₁ a₂ a₃, p (quot.mk s₁.1 a₁) (quot.mk s₂.1 a₂) (quot.mk s₃.1 a₃)) : p q₁ q₂ q₃ :=
quotient.induction_on₃ q₁ q₂ q₃ h

end quotients

Using these definitions everything was easy. They differ from the library definitions in two ways, the absence of the elab_strategy attribute, not sure what this does, but it makes stuff harder for some reason, and the use of {} instead of [] for setoids. Using quot versions of these lemmas has two problems, one is the elab_strategy attribute, and the other is that quot.lift_on₂ as well as quot.exact are not provable without the relations being equivalence relations.

Mario Carneiro (Jul 20 2018 at 18:19):

Hm, this sounds like reason enough to PR these theorems. (Since we all know that mathlib is collecting patches of core lean theorems.) I actually have no idea what the elab_strategy attribute does, I've never heard of it


Last updated: Dec 20 2023 at 11:08 UTC