Zulip Chat Archive
Stream: new members
Topic: Working "through" an isomorphism
Philippe Duchon (Oct 16 2025 at 08:22):
It is common in informal mathematics to say something like, "we know these two [groups/rings/whatever] and are isomorphic, and we proved this property in , so it also holds in " (or even to not say it explicitly), but what is the Lean way of doing it formally?
Here is a concrete example (equidivisibility in monoids, which is part of some existing characterizations of free monoids; it seems like a good example since it includes existential and universal quantifiers):
mwe:
import Mathlib
variable (A B: Type) [Monoid A] [Monoid B] (hAB: A ≃* B)
def equidivisible (A: Type) [Monoid A] := ∀ u v u' v': A, u * v = u' * v' →
∃ w, (u' = u * w ∧ v = w * v') ∨ (u = u' * w ∧ v' = w * v)
theorem by_iso (hAB: A ≃* B) (hA: equidivisible A) : equidivisible B := by
intro U V U' V' hUV
sorry
The plan is clear: use the reverse isomorphism hAB.symm to obtain terms of type corresponding to my variables of type , prove the variant of hUV, apply hA to obtain the relevant term of type , and prove that its image by hAB satisfies the required condition. This is already pretty tedious, and it's just with a few quantifiers and a single premise.
Is there a tactic to automate this, or should one do it on a case by case basis, using the API for the relevant algebraic structure (here, monoids)?
Kevin Buzzard (Oct 16 2025 at 08:32):
There was a tactic for this once, but I don't think it ever got ported, because (much to my surprise) this concept turned out to be less common than we thought, and often when we did need it we could deduce it from a more general result. I'll explain the more general result in your case now.
Here's four maths questions. Are any of the following true? (I pretty sure that none of them are, unfortunately). If A injects into B then A equidivisible => B equidivisible. If A surjects onto B then A equidivisible => B equidivisible. If A injects into B then B equidivisible => A equidivisible. If A surjects onto B then B equidivisible => A equidivisible. If any of these are true then such a result would need to go into the API for equidivisibility and one would not expect an isomorphism tactic to magically prove such a result because it lies deeper. However any such result would imply the isomorphism result which you want. In practice in Lean for many examples, one of the four analogous results is true, so we prove that manually and deduce the isomorophism result from it.
Kevin Buzzard (Oct 16 2025 at 08:41):
One person's tedious is another person's "fun problem to distract them because their next job is to plough through their email" (which is even more tedious):
import Mathlib
variable (A B : Type) [Monoid A] [Monoid B]
def equidivisible (A : Type) [Monoid A] := ∀ u v u' v' : A, u * v = u' * v' →
∃ w, (u' = u * w ∧ v = w * v') ∨ (u = u' * w ∧ v' = w * v)
theorem by_iso (e : A ≃* B) (hB : equidivisible B) : equidivisible A := by
intro U V U' V' hUV
obtain ⟨w, hw⟩ := hB (e U) (e V) (e U') (e V') (by simp [← map_mul, hUV])
use (e.symm w)
rcases hw with ⟨hw1a, hw1b⟩ | ⟨hw2a, hw2b⟩
· left
constructor
· apply_fun e.symm at hw1a
simp_all
· apply_fun e.symm at hw1b
simp_all
· right
constructor
· apply_fun e.symm at hw2a
simp_all
· apply_fun e.symm at hw2b
simp_all
Kenny Lau (Oct 16 2025 at 08:49):
Kevin Buzzard said:
There was a tactic for this once, but I don't think it ever got ported, because (much to my surprise) this concept turned out to be less common than we thought, and often when we did need it we could deduce it from a more general result
this seems like a case of local optimisation vs global optimisation, sure for each individual lemma the surjective/injective form is more useful, but as a whole i think we should have transport tactics
Philippe Duchon (Oct 16 2025 at 09:12):
Kevin Buzzard said:
One person's tedious is another person's "fun problem to distract them because their next job is to plough through their email" (which is even more tedious):
Never underestimate the threat of emails when it comes to making people work for you (thanks!).
So maybe tedious wasn't the right word, but it's repetitive enough. And I suspect doing the same for a property with more quantifier alternations would be even more so (though I don't have a good example at hand).
Kevin Buzzard (Oct 16 2025 at 09:30):
Kenny Lau said:
but as a whole i think we should have transport tactics
There is a cost-benefit analysis to be done here; right now tactics are hard to write and there are only a small number of experts who can do it (far far fewer than the number of people who can knock off that proof above). I think the fact that we've got this far without such a tactic is an indication that it's not high priority, and because there are so many more high priority things for the tactic people (did you see field by the way, it's awesome and it just landed) stuff like transport tends not to get done.
IIRC @Kim Morrison even had some prototype transport working that would even prove that compactness transferred along continuous surjections (the thing which I said above that a transport tactic would obviously not do, because surjections aren't bijections in general). Or am I dreaming?
Ruben Van de Velde (Oct 16 2025 at 09:47):
Did you try if grind helped with any of those?
Philippe Duchon (Oct 16 2025 at 09:50):
I just did (I'm totally unfamiliar with grind, having only read about it a bit here), but it doesn't work straight out of the box.
Ruben Van de Velde (Oct 16 2025 at 09:53):
It tends to be good at "quantifier shuffling", but I guess there's a little too much mathematical content
Bryan Wang (Oct 16 2025 at 10:54):
There was a related thread about transport here
Anthony Wang (Oct 16 2025 at 18:02):
Kevin Buzzard said:
One person's tedious is another person's "fun problem to distract them because their next job is to plough through their email" (which is even more tedious):
import Mathlib variable (A B : Type) [Monoid A] [Monoid B] def equidivisible (A : Type) [Monoid A] := ∀ u v u' v' : A, u * v = u' * v' → ∃ w, (u' = u * w ∧ v = w * v') ∨ (u = u' * w ∧ v' = w * v) theorem by_iso (e : A ≃* B) (hB : equidivisible B) : equidivisible A := by intro U V U' V' hUV obtain ⟨w, hw⟩ := hB (e U) (e V) (e U') (e V') (by simp [← map_mul, hUV]) use (e.symm w) rcases hw with ⟨hw1a, hw1b⟩ | ⟨hw2a, hw2b⟩ · left constructor · apply_fun e.symm at hw1a simp_all · apply_fun e.symm at hw1b simp_all · right constructor · apply_fun e.symm at hw2a simp_all · apply_fun e.symm at hw2b simp_all
You can replace the rcases block with just a single line, so it's actually not even that tedious.
theorem by_iso (e : A ≃* B) (hB : equidivisible B) : equidivisible A := by
intro U V U' V' hUV
obtain ⟨w, hw⟩ := hB (e U) (e V) (e U') (e V') (by simp [← map_mul, hUV])
simp [← EmbeddingLike.apply_eq_iff_eq e.symm] at hw
use (e.symm w)
Eric Wieser (Oct 16 2025 at 21:07):
I would guess that e.surjective.exists is useful here too
Aaron Liu (Oct 16 2025 at 21:12):
easy, no stress
import Mathlib
variable (A B : Type) [Monoid A] [Monoid B]
def equidivisible (A : Type) [Monoid A] := ∀ u v u' v' : A, u * v = u' * v' →
∃ w, (u' = u * w ∧ v = w * v') ∨ (u = u' * w ∧ v' = w * v)
theorem by_iso (e : A ≃* B) (hB : equidivisible B) : equidivisible A := by
unfold equidivisible at hB ⊢
simp_rw [e.forall_congr_left, e.exists_congr_left]
simp_rw [← e.symm.injective.eq_iff, map_mul] at hB
exact hB
Kenny Lau (Oct 17 2025 at 00:20):
one could make that into a tactic
Last updated: Dec 20 2025 at 21:32 UTC