Zulip Chat Archive

Stream: Is there code for X?

Topic: Manipulating inequalities in `ENat`


Anatole Dedecker (Aug 12 2023 at 16:27):

What is the most efficient ways of filling in these sorries? You only realize how useful linarith is when you can't use it...

import Mathlib.Data.ENat.Basic

example {n : } {i : } : True := by
  rcases lt_or_ge (i : ) n with (hin|hin)
  · have hin' : i + 1  n := sorry
    have hin'' : i  n - 1 := sorry
    have hn : n  0 := sorry
    trivial
  · have hin' : i + 1 > n := sorry
    have hin'' : i > n - 1 := sorry
    trivial

Junyan Xu (Aug 13 2023 at 13:03):

The last one isn't true but the others are:

example {n : } {i : } (hin : (i : ) < n) : n  0 := by
  rintro rfl; exact not_lt_bot hin

example {n : } {i : } : ((i : ) < n)  i + 1  n :=
  (ENat.add_one_le_iff <| ENat.coe_ne_top i).symm

example {n : } {i : } (hin : (i : ) < n) : i  n - 1 := by
  cases' n with n
  · exact le_top
  · exact WithTop.coe_le_coe.2 (Nat.le_pred_of_lt <| WithTop.coe_lt_coe.1 hin)

example : ¬  {n : } {i : }, n  (i : )  n - 1 < i :=
  fun h => not_lt_bot (@h 0 0 rfl.le)

Anatole Dedecker (Aug 13 2023 at 13:06):

Oh indeed the last one can't be true

Yury G. Kudryashov (Aug 13 2023 at 20:09):

BTW, see docs#ENat.add_one_le_of_lt

Yury G. Kudryashov (Aug 13 2023 at 20:10):

For i ≤ n - 1, you can also use docs#le_tsub_of_add_le_right

Anatole Dedecker (Aug 13 2023 at 20:21):

Yury G. Kudryashov said:

For i ≤ n - 1, you can also use docs#le_tsub_of_add_le_right

No I can't unfortunately :sob: because addition is not ContravariantClass on ENat. I can use docs#AddLECancellable.le_tsub_of_add_le_right in theory, but there's no API around docs#AddLECancellable in ENat.

Kalle Kytölä (Aug 13 2023 at 20:45):

These are good illustrations of some still common headaches --- and indeed great reminders of how good linarith is when it applies!

Anatole Dedecker (Aug 13 2023 at 20:47):

Yes, I'm starting to understand why you'd want to do probabilities in Real :sweat_smile:

Kalle Kytölä (Aug 13 2023 at 20:51):

ring is also amazing.

Jireh Loreaux (Aug 13 2023 at 21:16):

One thing I would really argue for here: let's develop the necessary API (whatever that may be, possibly including tactics). In general, I'm much more a fan of "do the thing that is mathematically what you want" as opposed to "sacrifice mathematical meaning in order to make formalizing less painful". Of course, if the situations are mathematically equivalent, then make your life easy.

Anatole Dedecker (Aug 13 2023 at 21:25):

I completely agree with you, but when these things pop up while doing interesting maths and suddenly you're stuck for this kind of stupid reasons, you have to admit that it's pretty tempting to just go around. But of course this isn't the right solution.

Anatole Dedecker (Aug 13 2023 at 21:27):

I'm very patient with Lean usually, but for some reason this one is just too much for me :sweat_smile:

Jireh Loreaux (Aug 13 2023 at 21:42):

I totally understand the predicament!

Kevin Buzzard (Aug 13 2023 at 22:13):

@Mario Carneiro is there anything that mathematicians can do in order to make linarith work for PNat? For example I have it in the back of my mind that you once said "there is a missing typeclass" -- have I misremembered though and this was why ring doesn't work on PNat? Here are some examples of failures. What is a route to making these work?

import Mathlib

example : (1 : PNat) + 1 = 2 := by norm_num -- works
example : (1 : PNat) + x = x + 1 := by ring -- fails
example : (1 : PNat) < x + 1 := by linarith -- fails

noncomputable example : LinearOrderedCommSemiring NNReal := inferInstance

example : (1 : NNReal) + 1 = 2 := by norm_num -- works
example : (1 : NNReal) + x = x + 1 := by ring -- works
example : (1 : NNReal)  x + 1 := by linarith -- fails

inductive MyNat
| zero : MyNat
| S : MyNat  MyNat

instance : LinearOrderedCommSemiring MyNat := sorry

example : (2 : MyNat) + 2 = 4 := by norm_num -- works
example : (2 : MyNat) + x = x + 2 := by ring -- works
example (h : (x : MyNat)  y) : 1 + x  1 + y := by linarith -- fails

Mario Carneiro (Aug 13 2023 at 22:22):

the ring issue should be self explanatory, it needs a LinearOrderedCommSemiring instance and PNat isn't

Kevin Buzzard (Aug 13 2023 at 22:26):

But it is something, right? Do we need to write a new ring for PNat or is it a case of making LinearOrderedCommPreSemiring or whatever PNat is and then generalising 50% of the lemmas in ring to this more general typeclass? Or does that have disadvantages, e.g. it makes ring slower in 98% of use cases?

Mario Carneiro (Aug 13 2023 at 22:28):

the linarith issue is that it expects the base ring to be an additive group and MyNat isn't

Mario Carneiro (Aug 13 2023 at 22:29):

"so why does Nat work?" because it has specific support in linarith which works by lifting everything to Int

Mario Carneiro (Aug 13 2023 at 22:31):

Maybe it should be lifting into the AddLocalization instead of specifically Nat -> Int

Mario Carneiro (Aug 13 2023 at 22:33):

I suspect something like that is also how to handle the PNat issue, just lift things into the appropriate completion instead of trying to work in whatever gimped structure PNat is and hope it is good enough to have good normal forms

Kevin Buzzard (Aug 13 2023 at 22:35):

Aah that's a good idea: extend zify to PNat and hope that it's enough for Anatole! Then linarith and ring become available...

Anatole Dedecker (Aug 13 2023 at 23:01):

Except I’m in ENat, not PNat 😅

Anatole Dedecker (Aug 13 2023 at 23:01):

And I don’t think you can hope to embed that in a nice structure

Alex J. Best (Aug 13 2023 at 23:48):

It's still under development (and perhaps a bit overcomplicated right now), but for the Tate's algorithm project (with Huriot and Dahmen) I wrote a tactic for solving linear arithmetic ENat goals, seeings as we had so many of them there.
https://github.com/KisaraBlue/ec-tate-lean/blob/master/ECTate/Tactic/ELinarith.lean
The idea is simple, find all the enats appearing as atoms in the goal and hypotheses, do cases on whether they are infinite or not, simplify (which will reduce all statements with an infinity in to some more straightforward things that should follow from propositional logic at that point), and then with remaining goals with no infinite variables in, pass to the naturals and run the usual linarith. (note this same strategy should also work for ereal, erat etc.)
In fact the presburger-esque theory of extended naturals is decidable (by a similar argument) so in theory there could be a tactic doing much much more than this.
Unfortunately our tactic isn't yet useful widely (i.e copy-pastable) for a couple of reasons, 1. we started the project before mathlib4 got going, so we rolled our own enat, which we still need to remove. 2. the implementation uses a custom version of simp that avoids a bug in the core simp that i need to upstream sometime 3. its not 100% finished yet and presumably not theoretically complete yet, and perhaps a little overcomplicated now who knows. It does work in the nontrivial cases we needed it to.
When the project reaches a more complete state I hope to PR a good version of this to mathlib.

Mario Carneiro (Aug 14 2023 at 01:40):

#6570 implements ring for PNat via lifting to Nat

Damiano Testa (Aug 14 2023 at 05:55):

@Alex J. Best there was a discussion of a similar tactic to what you describe in this thread. I did not really start any serious attempt on it, but the idea is very similar: decide what counts as "junk" value, possibly via an attribute and then let the tactic do the case split and try to simp-away the "trivial" case.

Peter Nelson (Feb 02 2025 at 21:40):

I found this thread in a search - I would love to see this tactic. @Alex J. Best - was there ever any progress on this?

Alex J. Best (Feb 03 2025 at 00:06):

Unfortunately not, it wouldn't be too hard to implement this on top of mathlib I think in the enat case at least, probably as an extension of omega rather than linarith now. but I sadly don't have much time for that myself.

Peter Nelson (Feb 03 2025 at 01:22):

I would love to have the time to learn tactic-writing, but that is a pipe-dream at the moment. Is there anyone who already knows it that would have time to look at this?

Yakov Pechersky (Feb 03 2025 at 05:15):

While there is no tactic, I discovered today docs#AddLECancellable which has many helper lemmas in its protected namespace.

Vasilii Nesterov (Feb 03 2025 at 09:05):

Yes, I am working on a tactic that extends linarith/omega to ENat, and I hope it will also be able to work with other numerical types, such as EReal, NNRat, etc.

Vasilii Nesterov (Feb 03 2025 at 09:16):

In the case of "extended" types like ENat and EReal, I think the solution is to perform cases on each variable, simplify all expressions involving infinities, and then solve the remaining problem using omega/linarith, which will now involve only values from the original, non-extended type.

Peter Nelson (Feb 03 2025 at 11:59):

That sounds great! How close is it to ready?

Vasilii Nesterov (Feb 13 2025 at 17:22):

The PR for PNat and ENat is #21602.

Kim Morrison (Feb 14 2025 at 09:30):

:writing:


Last updated: May 02 2025 at 03:31 UTC