Zulip Chat Archive

Stream: maths

Topic: tactic or term mode proof?


Kevin Buzzard (Jun 30 2020 at 14:07):

I am writing an API for DVRs. I've just proved the following lemma about commutative rings:

lemma local_of_unique_nonzero_prime (R : Type u) [comm_ring R]
(h : ∃! P : ideal R, P    is_prime P) : local_ring R :=
local_of_unique_max_ideal
begin
  rcases h with P, hPnonzero, hPnot_top, _⟩, hPunique,
  use P,
  split,
  { split, exact hPnot_top,
    apply maximal_of_no_maximal,
    intros M hPM hM,
    apply ne_of_lt hPM,
    symmetry,
    apply hPunique,
    split, apply ne_bot_of_gt hPM,
    exact is_maximal.is_prime hM},
  { intros M hM,
    apply hPunique,
    split,
    { rintro rfl,
      cases hM with hM1 hM2,
      specialize hM2 P (bot_lt_iff_ne_bot.2 hPnonzero),
      exact hPnot_top hM2},
    { exact is_maximal.is_prime hM}}
end

The proof is unremarkable: a unique non-zero prime ideal implies a unique maximal ideal by some fiddling. The proof looks to me like tidy could almost have done it. I use rcases, split, exact, apply, intros etc.

I know that if Mario had written this proof he would then start golfing it and turning it into term mode. Johan has suggested that this sort of thing makes the proofs harder to maintain. What is the argument for termgolfing this proof? It will in future be a PR to mathlib I suspect. @Mario Carneiro ?

Mario Carneiro (Jun 30 2020 at 14:08):

I think that golfing it into term mode doesn't really affect its maintainability in any particular direction

Mario Carneiro (Jun 30 2020 at 14:08):

but it does make it shorter

Mario Carneiro (Jun 30 2020 at 14:14):

there is a small compile time advantage for using less tactics, but it's more of a rounding error. Mostly I think it is just more tidy to get more done in less space

Kevin Buzzard (Jun 30 2020 at 14:27):

lemma local_of_unique_nonzero_prime' (R : Type u) [comm_ring R]
(h : ∃! P : ideal R, P    is_prime P) : local_ring R :=
let P, hPnonzero, hPnot_top, _⟩, hPunique := h in
local_of_unique_max_ideal P, hPnot_top,
  maximal_of_no_maximal _ _ $ λ M hPM hM, ne_of_lt hPM $ (hPunique _ ne_bot_of_gt hPM, is_maximal.is_prime hM).symm,
  λ M hM, hPunique _ ⟨λ (h : M = ), hPnot_top $ hM.2 _ (h.symm  (bot_lt_iff_ne_bot.2 hPnonzero :  < P) : M < P), is_maximal.is_prime hM⟩⟩

I did it (with Kenny helpnig me: rintro rfl -> stupid triangle)

Mario Carneiro (Jun 30 2020 at 14:27):

I got this:

lemma local_of_unique_nonzero_prime (R : Type u) [comm_ring R]
  (h : ∃! P : ideal R, P    is_prime P) : local_ring R :=
local_of_unique_max_ideal begin
  rcases h with P, hPnonzero, hPnot_top, _⟩, hPunique,
  refine P, hPnot_top, _⟩, λ M hM, hPunique _ ⟨_, is_maximal.is_prime hM⟩⟩,
  { refine maximal_of_no_maximal (λ M hPM hM, ne_of_lt hPM _),
    exact (hPunique _ ne_bot_of_gt hPM, is_maximal.is_prime hM).symm },
  { rintro rfl,
    exact hPnot_top (hM.2 P (bot_lt_iff_ne_bot.2 hPnonzero)) },
end

Kevin Buzzard (Jun 30 2020 at 14:28):

Mario Carneiro said:

I think that golfing it into term mode doesn't really affect its maintainability in any particular direction

@Johan Commelin do you agree with this?

Mario Carneiro (Jun 30 2020 at 14:28):

notably it's not entirely term mode because rcases is great

Mario Carneiro (Jun 30 2020 at 14:28):

rintro rfl is great too

Mario Carneiro (Jun 30 2020 at 14:29):

some of the best term mode techniques are in tactic mode

Mario Carneiro (Jun 30 2020 at 14:30):

I think that refine is a nice middle ground here. You can chop a term mode proof into chunks where you have as much term mode proof as fits in one refine line

Kevin Buzzard (Jun 30 2020 at 14:31):

elaboration of local_of_unique_nonzero_prime took 168ms
elaboration of local_of_unique_nonzero_prime' took 31.2ms

Johan Commelin (Jun 30 2020 at 14:31):

I don't have an opinion...

Mario Carneiro (Jun 30 2020 at 14:31):

Like I said: term mode is faster but they are both fast if you aren't using "real" tactics

Mario Carneiro (Jun 30 2020 at 14:32):

You can of course write slow term mode proofs, but all other things being equal term mode proofs are faster

Kevin Buzzard (Jun 30 2020 at 14:32):

This isn't Mario's proof, this is my original proof v my golfification

Mario Carneiro (Jun 30 2020 at 14:33):

I just golfed your proof

Kevin Buzzard (Jun 30 2020 at 14:33):

Mario's proof doesn't compile because he had to guess what maximal_of_no_maximal was

Kenny Lau (Jun 30 2020 at 14:33):

slow term mode proofs happen with unoptimal typeclass inference

Mario Carneiro (Jun 30 2020 at 14:33):

I guessed what maximal_of_no_maximal was

Kevin Buzzard (Jun 30 2020 at 14:33):

/-- If P is not properly contained in any maximal ideal then it is not properly contained
  in any proper ideal -/
lemma maximal_of_no_maximal (R : Type u) [comm_ring R] (P : ideal R)
(hmax :  m : ideal R, P < m  ¬is_maximal m) (J : ideal R) (hPJ : P < J) : J =  :=
begin
  by_contradiction hnonmax,
  rcases exists_le_maximal J hnonmax with M, hM1, hM2,
  exact hmax M (lt_of_lt_of_le hPJ hM2) hM1,
end

Mario Carneiro (Jun 30 2020 at 14:33):

theorem ideal.maximal_of_no_maximal {R} [comm_ring R] {P : ideal R}
  (H :  M, P < M  ¬ is_maximal M) :  (J : ideal R), P < J  J =  := sorry

Mario Carneiro (Jun 30 2020 at 14:34):

Why are R and P explicit?

Kevin Buzzard (Jun 30 2020 at 14:34):

because I wrote it 30 minutes ago and wasn't thinking

Mario Carneiro (Jun 30 2020 at 14:34):

same in this proof

Kevin Buzzard (Jun 30 2020 at 14:34):

OK got it

Kevin Buzzard (Jun 30 2020 at 14:35):

elaboration of local_of_unique_nonzero_prime'' took 80.1ms

Mario Carneiro (Jun 30 2020 at 14:35):

that's my proof?

Kevin Buzzard (Jun 30 2020 at 14:35):

Yes

Kenny Lau (Jun 30 2020 at 14:35):

I guess rcases is the culprit

Mario Carneiro (Jun 30 2020 at 14:35):

I think what you are seeing is that there is overhead per tactic parse and call

Mario Carneiro (Jun 30 2020 at 14:36):

we've seen some of this in kenny's X is slow tests

Mario Carneiro (Jun 30 2020 at 14:37):

I don't think that the timing here is worth sweating over

Kenny Lau (Jun 30 2020 at 14:39):

but isn't tactic parse part of the "parsing" time?

Mario Carneiro (Jun 30 2020 at 14:40):

Honestly I have no idea, profiling times are really inaccurate and hard to interpret

Kevin Buzzard (Jun 30 2020 at 14:41):

And there's no simple way to find out how long something actually takes, right?

Kevin Buzzard (Jun 30 2020 at 14:41):

Anyway, which version goes in mathlib?

Mario Carneiro (Jun 30 2020 at 14:43):

I'm obviously biased toward my own version. I think the full term mode version has a bit too many things going on at once; in particular the type ascriptions suggest that the proof does not really proceed in left to right textual order and needs some elaboration hints

Mario Carneiro (Jun 30 2020 at 14:43):

so that's where I would normally cut it up into multiple refine blocks

Mario Carneiro (Jun 30 2020 at 14:44):

your term mode proof doesn't compile for me, is local_ring a prop now?

Mario Carneiro (Jun 30 2020 at 14:49):

I guess the type ascriptions are there in this proof just to show that you can do rw using the funny arrow

Mario Carneiro (Jun 30 2020 at 14:51):

I think if you have to go out of your way to do something in term mode it's not worth it. It is a space saver if you are only doing apply/intro/split/exact but for most other things it's not worth it

Mario Carneiro (Jun 30 2020 at 14:52):

In particular I think that obtain/rcases looks better than the term mode equivalent using Exists.rec_on or what have you. If you can do it with let <...> or \lam <...> then that's fine, although rcases can do some things that destructuring let/lambda can't

Mario Carneiro (Jun 30 2020 at 14:54):

And rw has no real term mode equivalent (the funny arrow doesn't even come close)

Mario Carneiro (Jun 30 2020 at 14:55):

For the everyday fast compiling mathlib proof I recommend some combination of these tactics. If you venture into other tactics like simp, ring, linarith, or tauto then tactic compile time becomes a serious factor


Last updated: Dec 20 2023 at 11:08 UTC