Zulip Chat Archive
Stream: mathlib4
Topic: positivity regression
Yaël Dillies (Sep 15 2023 at 09:40):
I have an issue with docs#Mathlib.Tactic.Positivity.evalMul (which came from @Mario Carneiro's #578). This line means that the extension only works in StrictOrderedSemiring
s, except that docs#mul_nonneg works more generally in ordered semirings.
Yaël Dillies (Sep 15 2023 at 09:41):
This is relevant to me since I am using positivity
to prove goals of the form 0 ≤ f * g
where f g : ι → ℝ
, and ι → ℝ
is not a strictly ordered semiring.
Yaël Dillies (Sep 15 2023 at 09:41):
Is there an easy fix?
Ruben Van de Velde (Sep 15 2023 at 09:59):
So what's the issue? Does weakening that line break its support for 0 <
goals?
Eric Wieser (Sep 15 2023 at 09:59):
Isn't the easy fix replacing that synthesis line?
Ruben Van de Velde (Sep 15 2023 at 10:00):
If that's the case, could we move the synthesis into each of the match arms?
Yaël Dillies (Sep 15 2023 at 10:00):
That's what I would think. But I have no Lean 4 metaprogramming experience, and even less experience with Qq!
Eric Wieser (Sep 15 2023 at 10:00):
I think we might need to tweak the code slightly to look more like what I wrote in #7089
Eric Wieser (Sep 15 2023 at 10:01):
That is, to find the initial instances by unification so that we can call core
on them, before looking for stronger instances in the match arms
Yaël Dillies (Sep 15 2023 at 10:05):
While I'm at it, is there any obvious reason why the following extension doesn't work?
import Mathlib.Data.Rat.Order
import Mathlib.Tactic.Positivity
lemma Rat.num_ne_zero {q : ℚ} : q.num ≠ 0 ↔ q ≠ 0 := Rat.num_eq_zero.not
namespace Mathlib.Meta.Positivity
open Lean Meta Qq Function Rat
alias ⟨_, num_pos_of_pos⟩ := num_pos_iff_pos
alias ⟨_, num_nonneg_of_nonneg⟩ := num_nonneg_iff_zero_le
alias ⟨_, num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `Rat.num a`,
such that `positivity` successfully recognises `a`. -/
@[positivity (Rat.num _)]
def evalRatnum : PositivityExt where eval {_u _α} _zα _pα e := do
let .app _ (a : Q(ℚ)) ← withReducible (whnf e) | throwError "not Rat.num"
let zα' : Q(Zero ℚ) := q(inferInstance)
let pα' : Q(PartialOrder ℚ) := q(inferInstance)
let ra ← core zα' pα' a
match ra with
| .positive pa =>
pure (.positive (q(num_pos_of_pos $pa) : Expr))
| .nonnegative pa =>
pure (.nonnegative (q(num_nonneg_of_nonneg $pa) : Expr))
| .nonzero pa =>
pure (.nonzero (q(num_ne_zero_of_ne_zero $pa) : Expr))
| .none =>
pure .none
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity Rat.den _]
def evalRatden : PositivityExt where eval {_u _α} _zα _pα e := do
let .app _ (a : Q(ℚ)) ← withReducible (whnf e) | throwError "not Rat.den"
pure (.positive (q(den_pos $a) : Expr))
variable {q : ℚ}
example (hq : 0 < q) : 0 < q.num := by positivity -- failed
example (hq : 0 ≤ q) : 0 ≤ q.num := by positivity -- failed
example (hq : q ≠ 0) : q.num ≠ 0 := by positivity -- failed
example : 0 < q.den := by positivity -- failed
end Mathlib.Meta.Positivity
Yaël Dillies (Sep 15 2023 at 10:08):
(actually, feel free to move this to #metaprogramming / tactics, I forgot that stream existed)
Yaël Dillies (Sep 15 2023 at 10:16):
Update: Just added my attempt for Rat.den
, which is even easier but which I still failed miserably :sob:
Eric Wieser (Sep 15 2023 at 10:18):
I'll have a go at those in half an hour
Eric Wieser (Sep 15 2023 at 13:00):
This works:
import Mathlib.Data.Rat.Order
import Mathlib.Tactic.Positivity
namespace Mathlib.Meta.Positivity
open Lean Meta Qq Function Rat
alias ⟨_, num_pos_of_pos⟩ := num_pos_iff_pos
alias ⟨_, num_nonneg_of_nonneg⟩ := num_nonneg_iff_zero_le
alias ⟨_, num_ne_zero_of_ne_zero⟩ := num_ne_zero
/-- The `positivity` extension which identifies expressions of the form `Rat.num a`,
such that `positivity` successfully recognises `a`. -/
@[positivity (Rat.num _)]
def evalRatnum : PositivityExt where eval {_u _α} _zα _pα (e : Q(ℤ)) := do
let ~q(Rat.num $a) := e | throwError "not Rat.num"
let zα' : Q(Zero ℚ) := q(inferInstance)
let pα' : Q(PartialOrder ℚ) := q(inferInstance)
-- TODO: what's the right way to write these `: Expr`s?
match ← core zα' pα' a with
| .positive pa =>
return .positive (q(num_pos_of_pos $pa) : Expr)
| .nonnegative pa =>
return .nonnegative (q(num_nonneg_of_nonneg $pa) : Expr)
| .nonzero pa =>
return .nonzero (q(num_ne_zero_of_ne_zero $pa) : Expr)
| .none =>
pure .none
/-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/
@[positivity Rat.den _]
def evalRatden : PositivityExt where eval {_u _α} _zα _pα (e : Q(ℕ)) := do
let ~q(Rat.den $a) := e | throwError "not Rat.den"
return .positive (q(den_pos $a) :)
variable {q : ℚ}
example (hq : 0 < q) : 0 < q.num := by positivity -- ok!
example (hq : 0 ≤ q) : 0 ≤ q.num := by positivity -- ok!
example (hq : q ≠ 0) : q.num ≠ 0 := by positivity -- ok!
example : 0 < q.den := by positivity -- ok!
end Mathlib.Meta.Positivity
Eric Wieser (Sep 15 2023 at 13:01):
These : Expr
s feel like one of:
- A design flaw in
PositivityExt
- A Qq bug
- Me not knowing what I'm doing with Qq
Yaël Dillies (Sep 15 2023 at 13:05):
So my mistake was the match?
Yaël Dillies (Sep 15 2023 at 13:08):
Aaah, I see. Am I right in thinking it's because I didn't type-ascript e
, so Qq couldn't derive the type-correctness of my subsequent match?
Eric Wieser (Sep 15 2023 at 20:09):
Yes, the match isn't even well-typed if you don't first lie about the type of e
David Renshaw (Sep 15 2023 at 20:13):
in what sense is that ascription a "lie"?
David Renshaw (Sep 15 2023 at 20:14):
My understanding of (e : Q(ℕ))
is that we are asserting that e
is an expression of type ℕ
.
Eric Wieser (Sep 15 2023 at 20:21):
Right, but we've been told it's of type _α : Q(Type u)
(we also aren't told that u = level.zero
)
Eric Wieser (Sep 15 2023 at 20:22):
Which we're ignoring because we "know better" (about the internals of the positivity tactic and how it promises to call us)
Mario Carneiro (Sep 15 2023 at 20:24):
it's more about the fact that your let ~q(Rat.num $a) := e
implies some things about u and alpha which the type system doesn't know
Eric Wieser (Sep 15 2023 at 20:24):
Is it something that the type system could know?
Mario Carneiro (Sep 15 2023 at 20:24):
working on it
Eric Wieser (Sep 15 2023 at 20:25):
It seems like we could refactor positivity extensions somehow so that the match and lookup are simultaneous
Yury G. Kudryashov (Oct 29 2023 at 05:30):
positivity
extension for docs#Real.sqrt doesn't work for me.
Yury G. Kudryashov (Oct 29 2023 at 05:30):
How do I debug it?
Yury G. Kudryashov (Oct 29 2023 at 05:43):
More details:
example (x : ℝ) (hx : 0 < x) : 0 < Real.sqrt x := by positivity -- works
example (x : ℝ) : 0 ≤ Real.sqrt x := by positivity -- fails
Yury G. Kudryashov (Oct 29 2023 at 06:29):
It looks like the function doesn't get past the let ra ← core zα' pα' a
line without (_ : 0 < x)
.
Yury G. Kudryashov (Oct 29 2023 at 06:38):
Probably, the call to core
fails instead of returning none
.
David Renshaw (Oct 29 2023 at 12:48):
David Renshaw (Oct 29 2023 at 12:54):
This fixes it:
diff --git a/Mathlib/Data/Real/Sqrt.lean b/Mathlib/Data/Real/Sqrt.lean
index 970b33638..ca73aab94 100644
--- a/Mathlib/Data/Real/Sqrt.lean
+++ b/Mathlib/Data/Real/Sqrt.lean
@@ -380,7 +380,7 @@ def evalSqrt : PositivityExt where eval {_ _} _zα _pα e := do
let (.app _ (a : Q(Real))) ← whnfR e | throwError "not Real.sqrt"
let zα' ← synthInstanceQ (q(Zero Real) : Q(Type))
let pα' ← synthInstanceQ (q(PartialOrder Real) : Q(Type))
- let ra ← core zα' pα' a
+ let ra ← catchNone <| core zα' pα' a
assertInstancesCommute
match ra with
David Renshaw (Oct 29 2023 at 12:55):
But makes me wonder why the throwNone
exists in the first place
David Renshaw (Oct 29 2023 at 13:02):
Yury G. Kudryashov (Oct 29 2023 at 15:31):
Thanks a lot!
Last updated: Dec 20 2023 at 11:08 UTC