Zulip Chat Archive

Stream: new members

Topic: using quotient.map2


Yakov Pechersky (Feb 10 2022 at 09:18):

In this example, why do I have to reprove the property of my function lifted into the quotient? I would have thought that map2_mk is able to retrieve it from the function itself. The lemma docs#quotient.map₂_mk is tagged @[simp] where it is defined.

import algebra.order.group
import algebra.order.with_zero

variables {S : Type*} [comm_semigroup S]

lemma cancel_left {a b c : S} (h : a * b = a * c) : b = c := sorry

variables (S)
-- we twist the multiplication to simplify refl and symm proofs
-- the definition is equivalent to `(a, a') ≈ (b, b') <-> a * b' = a' * b` due to the commutativity assumption
def mul_pair_setoid : setoid (S × S) :=
{ r := λ p q, p.1 * q.2 = q.1 * p.2,
  iseqv := begin
    refine ⟨λ _, rfl, λ _ _, eq.symm, _⟩,
    intros p q r h h',
    have := congr_arg ((*) p.snd) h',
    rw [mul_left_comm, ←mul_assoc, ←h, mul_comm _ q.snd, mul_comm _ q.snd, ←mul_left_comm,
        mul_assoc] at this,
    rw [cancel_left this, mul_comm],
  end }

variables {S}

local attribute [instance] mul_pair_setoid

@[simp] lemma mul_pair_equiv_iff (p q : S × S) : p ≈ q ↔ p.1 * q.2 = q.1 * p.2 := iff.rfl

lemma mul_pair_left_eq (p : S × S) (x : S) : ⟦(x * p.1, x * p.2)⟧ = ⟦p⟧ :=
by simp only [quotient.eq, mul_pair_equiv_iff, mul_assoc, mul_comm, mul_left_comm]

namespace mul_pair_quotient

local notation `G` := quotient (mul_pair_setoid S)

def mul : G → G → G :=
quotient.map₂ (λ (p q : S × S), (p.1 * q.1, p.2 * q.2)) begin
  rintro ⟨a, a'⟩ ⟨c, c'⟩ hac ⟨b, b'⟩ ⟨d, d'⟩ hbd,
  simp only [mul_pair_equiv_iff, quotient.eq] at hac hbd ⊢,
  rw [mul_left_comm, ←mul_assoc, ←mul_assoc, mul_assoc, hbd, mul_comm c', hac],
  simp [mul_left_comm, mul_assoc, mul_comm]
end

instance : has_mul G := ⟨mul⟩

-- why do I have to provide the proof again?
@[simp] lemma mk_mul : ∀ (p q : S × S), ⟦p⟧ * ⟦q⟧ = ⟦(p.1 * q.1, p.2 * q.2)⟧ :=
quotient.map₂_mk _ begin
  rintro ⟨a, a'⟩ ⟨c, c'⟩ hac ⟨b, b'⟩ ⟨d, d'⟩ hbd,
  simp only [mul_pair_equiv_iff, quotient.eq] at hac hbd ⊢,
  rw [mul_left_comm, ←mul_assoc, ←mul_assoc, mul_assoc, hbd, mul_comm c', hac],
  simp [mul_left_comm, mul_assoc, mul_comm]
end

end mul_pair_quotient

Eric Wieser (Feb 10 2022 at 09:34):

This works:

@[simp] lemma mk_mul : ∀ (p q : S × S), ⟦p⟧ * ⟦q⟧ = ⟦(p.1 * q.1, p.2 * q.2)⟧ :=
begin
  change (∀ p q, mul ⟦p⟧ ⟦q⟧ = _),
  refine quotient.map₂_mk _ _,
end

Yakov Pechersky (Feb 10 2022 at 09:35):

All of those are "term-like" tactics, so why doesn't just the refine step work?

Eric Wieser (Feb 10 2022 at 09:37):

This also works:

@[simp] lemma mk_mul : ∀ (p q : S × S), ⟦p⟧ * ⟦q⟧ = ⟦(p.1 * q.1, p.2 * q.2)⟧ :=
begin
  dunfold has_mul.mul,
  refine quotient.map₂_mk _ _,
end

Eric Wieser (Feb 10 2022 at 09:37):

The elaborator can't infer the implicit arguments correctly for you unless you unfold things in the right order for it, it seems

Yakov Pechersky (Feb 10 2022 at 09:38):

Thanks. This is helpful for the API I am building, but I still don't grok what's "wrong"

Eric Wieser (Feb 10 2022 at 09:38):

(as an aside, note that (λ (p q : S × S), (p.1 * q.1, p.2 * q.2)) is just (*))

Alex J. Best (Feb 10 2022 at 09:39):

And if you really want term mode:

@[simp] lemma mk_mul' : ∀ (p q : S × S), ⟦p⟧ * ⟦q⟧ = ⟦(p.1 * q.1, p.2 * q.2)⟧ :=
assume p q,
show mul ⟦p⟧ ⟦q⟧ = _, from quotient.map₂_mk _ _ p q

Yakov Pechersky (Feb 10 2022 at 09:40):

Term mode or tactic mode, I don't understand why show _, from _ is necessary for the unification(?) to occur

Eric Wieser (Feb 10 2022 at 09:42):

My guess is that it unfolds things in the wrong order

Eric Wieser (Feb 10 2022 at 09:42):

Here's a version without show:

@[simp] lemma mk_mul : ∀ (p q : S × S), ⟦p⟧ * ⟦q⟧ = ⟦(p.1 * q.1, p.2 * q.2)⟧ :=
(quotient.map₂_mk _ _ : ∀ p q : S × S, mul ⟦p⟧ ⟦q⟧ = _)

Last updated: Feb 28 2026 at 14:05 UTC