Zulip Chat Archive

Stream: general

Topic: Why doesn't the ring tactic solve "0 = -a + a"?


Ching-Tsun Chou (Nov 04 2024 at 04:25):

I was doing the following exercise in section 2.5 of "Mathematics in Lean:

image.png

To my surprise, I found that the ring tactic does not solve the equalities at either step 1 or step 3. Why is that so?

The following is the context of this exercise:

image.png

Kim Morrison (Nov 04 2024 at 04:28):

Because you are working in Nat, which is a semiring not a ring.

Kim Morrison (Nov 04 2024 at 04:28):

Try omega instead.

Ching-Tsun Chou (Nov 04 2024 at 04:30):

Why am I working in Nat? The variables an and b are of type R, which is a StrictOrderedRing.

Kim Morrison (Nov 04 2024 at 04:30):

Please use a #mwe, not a screenshot.

Kim Morrison (Nov 04 2024 at 04:30):

(Looking at your first screenshot there is no information about where the variables live.)

Ching-Tsun Chou (Nov 04 2024 at 04:42):

Here is a MWE:

import Mathlib.Tactic
import Mathlib.Util.Delaborators
import Mathlib.Topology.MetricSpace.Basic

section
variable {R : Type*} [StrictOrderedRing R]
variable (a b c : R)

example (h : a  b) : 0  b - a := by
--  sorry
 calc 0 = -a + a := by simp
      _  -a + b := by apply add_le_add_left h
      _ = b + -a := by rw [add_comm]
      _ = b - a := by { rw [ sub_eq_add_neg] }

end

Ching-Tsun Chou (Nov 04 2024 at 04:43):

My question is: why doesn't the ring tactic solve steps 1 and 3?

Kim Morrison (Nov 04 2024 at 04:43):

Not your question, but

import Mathlib.Tactic
import Mathlib.Util.Delaborators
import Mathlib.Topology.MetricSpace.Basic

section
variable {R : Type*} [StrictOrderedRing R]
variable (a b c : R)

example (h : a  b) : 0  b - a := by
--  sorry
 calc 0 = -a + a := by abel
      _  -a + b := by apply add_le_add_left h
      _ = b + -a := by abel
      _ = b - a := by abel

end

does work.

Ching-Tsun Chou (Nov 04 2024 at 04:48):

That's a good point. But since a ring's + operation forms an abelian group, shouldn't the ring tactic be able to solve such goals as well?

Kyle Miller (Nov 04 2024 at 04:57):

ring likes commutative rings (I'm just copying the italics from the documentation when you hover over ring, it was a good reminder, since this example was puzzling me). Changing it to StrictOrderedCommRing makes it work.

Ching-Tsun Chou (Nov 04 2024 at 04:59):

Well, I tried noncomm_ring. It worked at step 3 but still fails at step 1.

Ching-Tsun Chou (Nov 04 2024 at 05:01):

In fact, noncomm_ring cannot prove "a = a + 0", either.

Yan Yablonovskiy (Nov 04 2024 at 05:11):

Ching-Tsun Chou said:

In fact, noncomm_ring cannot prove "a = a + 0", either.

This is probably irrelevant, but out of my curiosity -- how does Lean know here the 0 is the additive identity for R and not Nat/Int etc? It is inferred correctly from the type of a?

Kyle Miller (Nov 04 2024 at 05:12):

If you hover over 0 in the Infoview, you can see it's actually the more complicated @OfNat.ofNat R 0 Zero.toOfNat0 expression that says in which way 0 : Nat should be interpreted as an element of R.

Kyle Miller (Nov 04 2024 at 05:13):

It's a consequence of the elaboration process, where it does in this case get the type for the right-hand operand of + from the left-hand a

Kyle Miller (Nov 04 2024 at 05:15):

Worse @Ching-Tsun Chou , noncomm_ring doesn't work on that example even for commutative rings.

example {R : Type*} [CommRing R] (a b : R) : a + 0 = a := by noncomm_ring -- fails

Kyle Miller (Nov 04 2024 at 05:16):

Though if you follow its suggestion to use abel it does work.

Ching-Tsun Chou (Nov 04 2024 at 05:23):

Good to know the idiosyncrasies of these tactics.

Johan Commelin (Nov 04 2024 at 07:51):

Ching-Tsun Chou said:

In fact, noncomm_ring cannot prove "a = a + 0", either.

That sounds like a bug to me.

Kim Morrison (Nov 04 2024 at 08:39):

example : a + 0 = a := by noncomm_ring

prints "Try abel instead", so I think it is working fine.

Ching-Tsun Chou (Nov 04 2024 at 16:23):

@Kim Morrison , I don't quite understand your comment. Are you saying that noncomm_ring is not supposed to work on "a + 0 = a"? What exactly is noncomm_ring supposed to be capable of solving?
Furthermore, it seems that noncomm_ring "knows" that abel may work. Why doesn't it just try it?
Sorry for my naïveté. I am a new user.

Kevin Buzzard (Nov 04 2024 at 17:22):

(these are good questions, don't worry!)

Kim Morrison (Nov 05 2024 at 00:59):

I think the general design principle here is that if an "expensive" tactic notices that a "cheap" tactic suffices, it should work, but print a warning suggesting that you change the tactic script (e.g. how simpa using X suggests simp if it closes the goal without using X).

Here, I think noncomm_ring should be printing a warning, not an error (PR very welcome!)

Kim Morrison (Nov 05 2024 at 01:00):

That said, noncomm_ring is not particular expensive, so I guess we could just let it run. I would be a little sad to end up with a whole lot of noncomm_ring proofs in Mathlib where abel would suffice.

Kim Morrison (Nov 05 2024 at 01:00):

I think we should write a algebra? tactic that tries all of ring, noncomm_ring, module, abel, etc, and reports the first one that closes the goal!

Kim Morrison (Nov 05 2024 at 01:01):

(PR welcome! :-)

Ching-Tsun Chou (Nov 05 2024 at 05:12):

Thanks for the explanation! Unfortunately, I'm still a beginner and do not know how to write tactics yet.

Ching-Tsun Chou (Nov 05 2024 at 05:16):

Is there a combinator of some kind that can take a list of tactics and try them one by one until either one of them succeeds or none succeeds and a failure is declared?

Johan Commelin (Nov 05 2024 at 05:38):

Yes, indeed there is. It's called first. See for example Mathlib/Tactic/Ring/RingNF.lean

`(tactic| first | ring1 | try_this ring_nf) -- line 261

Ching-Tsun Chou (Nov 05 2024 at 05:43):

Thanks for the pointer!


Last updated: May 02 2025 at 03:31 UTC