Zulip Chat Archive

Stream: mathlib4

Topic: linear_combination for inequalities


Heather Macbeth (Sep 16 2024 at 05:26):

#16841 extends the tactic linear_combination to prove inequalities! From the test file:

example (a b : ) (h1 : a  1) (h2 : b = 1) : (a + b) / 2  1 := by
  linear_combination (h1 + h2) / 2

example {x y : } (hx : x + 3  2) (hy : y + 2 * x  3) : y > 3 := by
  linear_combination hy + 2 * hx

example {x y z : } (h1 : 4 * x + y + 3 * z  25) (h2 : -x + 2 * y + z = 3)
    (h3 : 5 * x + 7 * z = 43) :
    x  4 := by
  linear_combination (14 * h1 - 7 * h2 - 5 * h3) / 38

example {t : } (ht : t  10) : t ^ 2 - 3 * t - 17  5 := by
  linear_combination (t + 7) * ht

Martin Dvořák (Sep 16 2024 at 09:21):

It seems that my duality project is henceforth redundant. JOKE

Heather Macbeth (Sep 17 2024 at 00:15):

Some rudimentary profiling suggests that on "tiny" problems, linear_combination-for-inequalities is as fast as a rewrite or a proof term, and several times faster than linarith:

import Mathlib.Tactic.Linarith
import Mathlib.Tactic.LinearCombination
import Mathlib.Data.Real.Basic

variable {a b : }

-- 730 heartbeats
example (h : a < b) : 0 < b - a := by linarith

-- 223 heartbeats
example (h : a < b) : 0 < b - a := by linear_combination h

-- 278 heartbeats
example (h : a < b) : 0 < b - a := by rwa [sub_pos]

-- 279 heartbeats
example (h : a < b) : 0 < b - a := sub_pos.mpr h

Heather Macbeth (Sep 17 2024 at 00:18):

This kind of goal is my primary motivation for this feature, so I would be very interested in

  • suggestions for measuring this speedup more precisely (on small problems #time has high variance and I am hesitant to draw conclusions)
  • suggestions for speeding up further!

Heather Macbeth (Sep 17 2024 at 00:46):

I thought of one other way to profile small problems: repeat 50 times and #time the whole thing.

I assume this is not the right way to do it? (For the record: 761 ms for 50 identical linariths, 483 ms for 50 identical linear_combinations.)

Kim Morrison (Sep 17 2024 at 03:15):

Use repeat rather than actually repeating. :-)

Kim Morrison (Sep 17 2024 at 03:16):

And you might add a zero or two!

Heather Macbeth (Sep 17 2024 at 03:23):

@Kim Morrison What is the syntax? I see the repeat tactic but this doesn't seem to take a parameter for number of iterations? docs#Lean.Parser.Tactic.tacticRepeat_

Daniel Weber (Sep 17 2024 at 03:28):

I don't know about repeat, but I think you can use iterate (docs#Lean.Parser.Tactic.tacticIterate____)

Heather Macbeth (Sep 17 2024 at 03:39):

OK, that actually seems pretty stable! For 500 iterations, 1.2 seconds for rwa [sub_pos], 5.2 seconds for linear_combination h, 8.3 seconds for linarith only [h]:

import Mathlib.Tactic.Linarith
import Mathlib.Tactic.LinearCombination
import Mathlib.Data.Real.Basic

variable {a b : }

#time
set_option maxRecDepth 1600 in
example (h : a < b) : True := by
  iterate 500
    have H : 0 < b - a := by linear_combination h
    clear H
  trivial

Heather Macbeth (Sep 17 2024 at 03:39):

I assume that when the same tactic call is repeated 500 times, things like typeclass search are cached, so it really is just the tactic execution and typechecking which are being tested?

(Which may not reflect "real-world" conditions ... Is there a way of clearing any cached typeclass searches between iterations?)

Kim Morrison (Sep 17 2024 at 06:31):

(Oops, sorry, iterate!)

Damiano Testa (Sep 17 2024 at 06:58):

There is also docs#Mathlib.CountHeartbeats.tacticCount_heartbeats!_In__ that might be useful.

Heather Macbeth (Sep 17 2024 at 13:28):

Yup, that's how I got these heartbeat counts, but the heartbeat numbers seem not to match my impression of time elapsed.

Heather Macbeth (Nov 10 2024 at 16:37):

The prerequisites for #16841 (linear_combination-for-inequalities) have all been merged, and this tactic is ready for review!

Heather Macbeth (Nov 11 2024 at 06:12):

Merged, thanks to Rob!

How do we feel about large-scale squeezing of linarith to linear_combination? See #18841 for the result of doing this in 100 places: it's a modest speedup. On the other hand, it's a slight loss in readability.

We could also have a policy that is "conditional" rather than absolute; for example the policy could be "linariths which depend on only one hypothesis are fair game for squeezing".

Johan Commelin (Nov 11 2024 at 07:09):

I left some comments. I think squeezing linarith only might be a good rule.

Also, we could rename linear_combination to lincombi if we think that linarith has an unfair advantage for short proofs.

Michael Rothgang (Nov 11 2024 at 11:29):

I also left some comments: TL;DR is that many changes are clear wins imho, some are neutral to good, and a few I'm less sure about (including because of the length).

Michael Rothgang (Nov 11 2024 at 11:30):

I think squeezing which avoids extra haves/several lines is clearly better. If the new code is shorter, I think it's also clearly better.

Heather Macbeth (Nov 11 2024 at 12:54):

Thank you Johan and Michael for going through the diff! But I am realising from your comments that I presented the PR in a way which obscures the linarith/linear_combination discussion, because I also mixed in some golfing which would have worked with either linarith or linear_combination. Let me move that golfing to a separate PR so we have a better basis for the discussion.


Last updated: May 02 2025 at 03:31 UTC