Zulip Chat Archive

Stream: general

Topic: grind failures


Michael Stoll (Nov 27 2025 at 20:05):

(Maybe it is useful to have a thread that collects (perceived) failures of grind.)

I'm surprised that grind does not do the following :

import Mathlib

example {ε : } ( : 0 < ε) : 0 < ε / 2 := by
  try grind -- fails
  linarith -- works
  --  exact half_pos hε -- also works

(Even grind [half_pos] fails here.)

Here are some variants.

import Mathlib.Algebra.Order.Field.Basic

variable {R : Type} [Field R] [LinearOrder R] [IsStrictOrderedRing R]

lemma foo {ε : R} ( : 0 < ε) : 0 < ε / 2 := by
  try grind [half_pos] -- fails
  exact half_pos 

example {ε : R} ( : 0 < ε) : 0 < 2 * ε := by
  grind -- works

example {ε : R} ( : 0 < ε) : 0 < 2⁻¹ * ε := by
  try grind -- fails
  grind [Left.mul_pos] -- succeeds

example : (0 : R) < 2⁻¹ := by
  grind -- works

Michael Stoll (Nov 27 2025 at 20:15):

Another example showing that grind has problems with halves:

import Mathlib

example {a b x : } (ha : a < x / 2) (hb : b < x / 2) : a + b < x := by
  grind -- fails

Ilmārs Cīrulis (Nov 27 2025 at 22:03):

This works:

import Mathlib

example {a b x : } (ha : a < x / 2) (hb : b < x / 2) : a + b < x := by
  have h := add_halves x
  grind [add_lt_add]

But this doesn't:

import Mathlib

example {a b x : } (ha : a < x / 2) (hb : b < x / 2) : a + b < x := by
  grind [add_lt_add, add_halves]

Ilmārs Cīrulis (Nov 27 2025 at 22:04):

Not saying this is failure, more like a curiosity (for me).

Moritz Doll (Nov 28 2025 at 04:17):

Michael Stoll said:

(Maybe it is useful to have a thread that collects (perceived) failures of grind.)

I'm surprised that grind does not do the following :

import Mathlib

example {ε : } ( : 0 < ε) : 0 < ε / 2 := by
  try grind -- fails
  linarith -- works
  --  exact half_pos hε -- also works

(Even grind [half_pos] fails here.)

Here are some variants.

import Mathlib.Algebra.Order.Field.Basic

variable {R : Type} [Field R] [LinearOrder R] [IsStrictOrderedRing R]

lemma foo {ε : R} ( : 0 < ε) : 0 < ε / 2 := by
  try grind [half_pos] -- fails
  exact half_pos 

example {ε : R} ( : 0 < ε) : 0 < 2 * ε := by
  grind -- works

example {ε : R} ( : 0 < ε) : 0 < 2⁻¹ * ε := by
  try grind -- fails
  grind [Left.mul_pos] -- succeeds

example : (0 : R) < 2⁻¹ := by
  grind -- works

all of these can be solved by positivity (at least when you important enough of mathlib), so I don't know whether it is bad that grind fails

Kim Morrison (Nov 28 2025 at 04:48):

I think essentially all of these were fixed a few days ago. Can you try them again on nightly-testing-green and report which, if any, still fail?

Michael Stoll (Nov 28 2025 at 10:37):

Kim Morrison said:

Can you try them again on nightly-testing-green

On live.lean-lang.org with "Lean Nightly":

example {ε : Rat} ( : 0 < ε) : 0 < ε / 2 := by grind

example {ε : Rat} ( : 0 < ε) : 0 < 2⁻¹ * ε := by grind

example {a b x : Rat} (ha : a < x / 2) (hb : b < x / 2) : a + b < x := by grind

all work (but with "Latest Mathlib", they fail).

Kim Morrison (Nov 28 2025 at 11:10):

Great! These features won't be available with "Latest Mathlib" until mid-December.

Michael Stoll (Dec 03 2025 at 20:00):

Here is another example that I expected grind to solve.

import Mathlib

open Real

example {t : } (ht : 0 < t) : -2 * t < 0 := by
  grind -- works

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  grind -- does not work

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  grind [Real.pi_pos] -- does not work

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  have := pi_pos
  grind -- does not work

-- the original problem
example {h t : } (hh : 0 < h) (ht : 0 < t) : -2 * π * t / h < 0 := by
  grind -- does not work

-- simplify by removing `π`
example {h t : } (hh : 0 < h) (ht : 0 < t) : -2 * t / h < 0 := by
  grind -- does not work

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < t / h := by
  grind [div_pos] -- does not work

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < h * t := by
  grind [mul_pos] -- works (but doesn't without `[mul_pos]`)

Jovan Gerbscheid (Dec 05 2025 at 17:44):

Somehow I expected grind to solve this:

example (f : Prop  Nat) (p q : Prop) : f (p  q) = f (q  p) := by grind

Snir Broshi (Dec 05 2025 at 19:17):

#new members > Weirdness with cutsat
#lean4 > possible grind regression?
#mathlib4>simp only + grind failure

Kim Morrison (Dec 08 2025 at 03:45):

Michael Stoll said:

Here is another example that I expected grind to solve.

import Mathlib

open Real

example {t : } (ht : 0 < t) : -2 * t < 0 := by
  grind -- works

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  grind -- does not work

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  grind [Real.pi_pos] -- does not work

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  have := pi_pos
  grind -- does not work

-- the original problem
example {h t : } (hh : 0 < h) (ht : 0 < t) : -2 * π * t / h < 0 := by
  grind -- does not work

-- simplify by removing `π`
example {h t : } (hh : 0 < h) (ht : 0 < t) : -2 * t / h < 0 := by
  grind -- does not work

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < t / h := by
  grind [div_pos] -- does not work

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < h * t := by
  grind [mul_pos] -- works (but doesn't without `[mul_pos]`)

There is no non-linear arithmetic in grind yet.

Bhavik Mehta (Dec 08 2025 at 11:12):

Should mul_pos become a grind lemma? Should we add annotations to make the penultimate example work too?

Michael Stoll (Dec 08 2025 at 11:18):

Kim Morrison said:

There is no non-linear arithmetic in grind yet.

Fair enough. But:

example {t : } (ht : 0 < t) : -2 * π * t < 0 := by
  grind -- does not work

I would consider this a linear problem (π is a constant).

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < t / h := by
  grind [div_pos] -- does not work

example {h t : } (hh : 0 < h) (ht : 0 < t) : 0 < h * t := by
  grind [mul_pos] -- works

Why does the second work, but the first does not?
I'm willing to help grind along, but right now I find it hard to predict what will work.

In any case, are there plans to extend the scope of grind so that it eventually will be able to solve goals like this? (The word "yet" in your message gives me some hope...)

Michael Stoll (Dec 08 2025 at 11:39):

On a more positive note, I'm trying to use grind whenever possible to deal with "obvious" stuff, and I find that it already works quite well. E.g., in my formalization of the Conditional Convergence Theorem, it is used 80 times, about once every 8 lines (including documentation and comments).

Michael Stoll (Dec 08 2025 at 17:02):

Another surprise: #lean4 > grind needs `sub_eq_zero` @ 💬 (where one has to tell grind explicitly to use one_mul).

Michael Stoll (Dec 08 2025 at 22:12):

import Mathlib

example (n : ) : 0 < 2 ^ n := by grind -- fails

Bhavik Mehta (Dec 09 2025 at 00:51):

I often add Nat.pow_pos to grind in projects, with a pattern, to help with this last case:

grind_pattern Nat.pow_pos => a ^ n

example (n : Nat) : 0 < 2 ^ n := by grind

Kim Morrison (Dec 09 2025 at 07:41):

Yes, we should definitely add this with the isValue guard on a.

Kim Morrison (Dec 09 2025 at 07:41):

I don't think in this case it makes sense to think of π as a constant. :-)

Kim Morrison (Dec 09 2025 at 07:42):

Yes, working out how to do non-linear arithmetic with grind, through some combination of annotations, internally defined propagators, and perhaps new grind modules, is on the roadmap for next quarter.

Bhavik Mehta (Dec 09 2025 at 13:02):

Actually, on Lean nightly Michael's last example already succeeds, because there's a pattern for Nat.pow_pos (which is more reasonable than mine) there already


Last updated: Dec 20 2025 at 21:32 UTC