Zulip Chat Archive
Stream: mathlib4
Topic: bug in `positivity`?
Heather Macbeth (Mar 07 2023 at 23:59):
@Gabriel Ebner 's PR !4#2685 rewrites positivity
thanks to some very nice new Qq
functionality. It seems to have broken certain positivity
applications, though. Here is a test case: a positivity
call occurring as a discharger inside solve_by_elim
is broken.
It's not broken when I try to replicate the behaviour with the positivity
call outside the solve_by_elim
application, so I'm having trouble tracking it down further.
import Mathlib.Tactic.SolveByElim
import Mathlib.Tactic.Positivity
open Lean Meta Mathlib
attribute [dummy_label_attr] le_refl add_le_add mul_le_mul_of_nonneg_right
-- works
example {x y : ℤ} (hx : x ≥ 12) : y + x * x ≥ y + 12 * x := by
apply_rules only [hx] using dummy_label_attr
positivity
def PositivityDischarge (g : MVarId) : MetaM (Option (List MVarId)) :=
do withTransparency .reducible (Meta.Positivity.positivity g); pure (some []) <|> pure none
-- works before #2685, fails on #2685 with "not a positivity goal"
example {x y : ℤ} (hx : x ≥ 12) : y + x * x ≥ y + 12 * x := by
solve_by_elim (config := { discharge := PositivityDischarge }) only [hx] using dummy_label_attr
Gabriel Ebner (Mar 08 2023 at 00:05):
This is the goal that positivity
is called on. I'm not sure how this worked before.
example {x y : ℤ} (hx : x ≥ 12) : False := by positivity
Heather Macbeth (Mar 08 2023 at 00:06):
It used to be called on 0 ≤ x
(to make the mul_le_mul_of_nonneg_right
application work).
Heather Macbeth (Mar 08 2023 at 00:07):
That is, apply mul_le_mul_of_nonneg_right
generates two subgoals and positivity
discharged one of them.
Gabriel Ebner (Mar 08 2023 at 00:21):
This doesn't work either:
example {x y : ℤ} (hx : x ≥ 12) : x ≤ 0 := by positivity
Heather Macbeth (Mar 08 2023 at 00:21):
Wait, that's with x negative though!
Gabriel Ebner (Mar 08 2023 at 00:22):
Oh :face_palm:
Gabriel Ebner (Mar 08 2023 at 00:57):
Apparently, it can't prove that two typeclass instances are defeq:
[isDefEq] ❌ CommMonoidWithZero.toZero =?= MonoidWithZero.toZero
Heather Macbeth (Mar 08 2023 at 00:57):
I just reached the same conclusion myself, and had come over to report it :-)
Gabriel Ebner (Mar 08 2023 at 00:57):
Fixed:
import Mathlib.Tactic.Positivity
open Lean Meta Mathlib
attribute [dummy_label_attr] le_refl add_le_add mul_le_mul_of_nonneg_right
def PositivityDischarge (g : MVarId) : MetaM (Option (List MVarId)) := do
withDefault (Meta.Positivity.positivity g)
pure (some [])
example {x y : ℤ} (hx : x ≥ 12) : y + x * x ≥ y + 12 * x := by
solve_by_elim (config := { discharge := PositivityDischarge }) only [hx] using dummy_label_attr
Heather Macbeth (Mar 08 2023 at 00:58):
Thank you! I don't understand the fix, though -- is it the withDefault
? What does that do?
Heather Macbeth (Mar 08 2023 at 00:59):
It replaces the withTransparency .reducible
in my version?
Heather Macbeth (Mar 08 2023 at 01:00):
Or do I want them both?
Gabriel Ebner (Mar 08 2023 at 01:00):
Yes, it's defined as withTransparency .default
Gabriel Ebner (Mar 08 2023 at 01:00):
(There's also withReducible
)
Gabriel Ebner (Mar 08 2023 at 01:01):
The withReducible is too strong, positivity expect(s/ed) the transparency to be default. It compares a lot of type class instances using isDefEq (thus requiring default transparency), and adds withReducible itself.
Mario Carneiro (Mar 08 2023 at 01:02):
is this what that assertInstancesCommute
macro does?
Mario Carneiro (Mar 08 2023 at 01:03):
because if so I would like to remove it from the places it's been added and put assumeInstancesCommute
instead
Gabriel Ebner (Mar 08 2023 at 01:04):
Yes, the assertInstancesCommute macro adds isDefEq calls.
Gabriel Ebner (Mar 08 2023 at 01:04):
because if so I would like to remove it from the places it's been added and put assumeInstancesCommute instead
Why?
Mario Carneiro (Mar 08 2023 at 01:04):
because that's the number one performance issue in these tactics
Mario Carneiro (Mar 08 2023 at 01:05):
plus asserting that the instances themselves are equal is more than needed in most cases
Mario Carneiro (Mar 08 2023 at 01:05):
we only need to know that some projections off the instances are equal
Mario Carneiro (Mar 08 2023 at 01:06):
If this defeq call saved work later that would be a different situation, but here it is pure overhead
Gabriel Ebner (Mar 08 2023 at 01:08):
We don't check that the projections are equal though.
Gabriel Ebner (Mar 08 2023 at 01:08):
The result of isDefEq is also cached.
Mario Carneiro (Mar 08 2023 at 01:08):
no, we just go right ahead and construct the term and it crashes and burns in the kernel if it's not defeq
Mario Carneiro (Mar 08 2023 at 01:10):
I don't think improving the error message when there is an instance diamond is worth the performance degradation in normal uses of the tactic
Mario Carneiro (Mar 08 2023 at 01:10):
because you will be breaking out a debugger when something goes wrong in these tactics in any case
Mario Carneiro (Mar 08 2023 at 01:11):
yes, the result of isDefEq is cached, that's what I tell myself but the profiler doesn't lie, it is very hot code
Gabriel Ebner (Mar 08 2023 at 01:12):
There's plenty of cases with type tags like WithTop
etc. where you can run into noncanonical instances.
Mario Carneiro (Mar 08 2023 at 01:12):
if the instances are not defeq then we're going to fail anyway
Gabriel Ebner (Mar 08 2023 at 01:13):
because if so I would like to remove it from the places it's been added and put assumeInstancesCommute instead
isDefEq is obviously hot, because it also does unification with lots of backtracking, has super expensive nested TC synthesis etc. All of this doesn't happen here.
Mario Carneiro (Mar 08 2023 at 01:13):
I mean, I ran a profiler with and without the isDefEq call and it was a significant improvement
Mario Carneiro (Mar 08 2023 at 01:14):
this is how ring
was originally written after all
Gabriel Ebner (Mar 08 2023 at 01:31):
In any case, there's an assumeInstancesCommute
now.
Last updated: Dec 20 2023 at 11:08 UTC