Skip to content

Commit e7e3867

Browse files
hrmacbethfpvandoorn
andcommitted
feat: assorted positivity extensions (#3907)
Positivity extensions for `NonnegHomClass` (this includes `AbsoluteValue` and `Seminorm`), `IsAbsoluteValue`, norm, the `NNReal`-to-`Real` coercion, factorials, square roots, distance (in a metric space), and diameter. I tried to do these "properly" using Qq but I hit various errors I couldn't fix -- see https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/Qq.20doesn't.20know.20that.20two.20things.20have.20the.20same.20type for some examples. cc @dwrensha Co-authored-by: Floris van Doorn <fpvdoorn@gmail.com>
1 parent 036470e commit e7e3867

File tree

12 files changed

+150
-108
lines changed

12 files changed

+150
-108
lines changed

Mathlib/Algebra/Order/AbsoluteValue.lean

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,14 @@ variable {R : Type _} [Semiring R] (abv : R → S) [IsAbsoluteValue abv]
308308
lemma abv_nonneg (x) : 0 ≤ abv x := abv_nonneg' x
309309
#align is_absolute_value.abv_nonneg IsAbsoluteValue.abv_nonneg
310310

311+
open Lean Meta Mathlib Meta Positivity Qq in
312+
/-- The `positivity` extension which identifies expressions of the form `abv a`. -/
313+
@[positivity (_ : α)]
314+
def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα _pα e := do
315+
let (.app f a) ← whnfR e | throwError "not abv ·"
316+
let pa' ← mkAppM ``abv_nonneg #[f, a]
317+
pure (.nonnegative pa')
318+
311319
lemma abv_eq_zero {x} : abv x = 0 ↔ x = 0 := abv_eq_zero'
312320
#align is_absolute_value.abv_eq_zero IsAbsoluteValue.abv_eq_zero
313321

Mathlib/Algebra/Order/Hom/Basic.lean

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Authors: Yaël Dillies
1010
-/
1111

1212
import Mathlib.Algebra.GroupPower.Order
13+
import Mathlib.Tactic.Positivity.Basic
1314

1415
/-!
1516
# Algebraic order homomorphism classes
@@ -154,17 +155,19 @@ theorem le_map_div_add_map_div [Group α] [AddCommSemigroup β] [LE β] [MulLEAd
154155
#align le_map_div_add_map_div le_map_div_add_map_div
155156
-- #align le_map_sub_add_map_sub le_map_sub_add_map_sub -- Porting note: TODO: `to_additive` clashes
156157

157-
--namespace Mathlib.Meta.Positivity
158+
namespace Mathlib.Meta.Positivity
158159

159-
--Porting note: tactic extension commented as decided in the weekly porting meeting
160-
-- /-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/
161-
-- @[positivity _ _]
162-
-- unsafe def positivity_map : expr → tactic strictness
163-
-- | expr.app (quote.1 ⇑(%%f)) (quote.1 (%%ₓa)) => nonnegative <$> mk_app `` map_nonneg [f, a]
164-
-- | _ => failed
165-
-- #align tactic.positivity_map tactic.positivity_map
160+
open Lean Meta Qq Function
166161

167-
--end Mathlib.Meta.Positivity
162+
/-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/
163+
@[positivity FunLike.coe _ _]
164+
def evalMap : PositivityExt where eval {_ β} _ _ e := do
165+
let .app (.app _ f) a ← whnfR e
166+
| throwError "not ↑f · where f is of NonnegHomClass"
167+
let pa ← mkAppOptM ``map_nonneg #[none, none, β, none, none, none, f, a]
168+
pure (.nonnegative pa)
169+
170+
end Mathlib.Meta.Positivity
168171

169172
/-! ### Group (semi)norms -/
170173

Mathlib/Analysis/LocallyConvex/WithSeminorms.lean

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,6 @@ theorem WithSeminorms.T1_of_separating (hp : WithSeminorms p)
344344
rw [← isOpen_compl_iff, hp.isOpen_iff_mem_balls]
345345
rintro x (hx : x ≠ 0)
346346
cases' h x hx with i pi_nonzero
347-
-- Porting note: the following line shouldn't be needed, but otherwise `positivity` fails later
348-
have : p i x ≥ 0 := map_nonneg _ _
349347
refine' ⟨{i}, p i x, by positivity, subset_compl_singleton_iff.mpr _⟩
350348
rw [Finset.sup_singleton, mem_ball, zero_sub, map_neg_eq_map, not_lt]
351349
#align with_seminorms.t1_of_separating WithSeminorms.T1_of_separating

Mathlib/Analysis/Normed/Group/Basic.lean

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -490,21 +490,26 @@ theorem norm_nonneg' (a : E) : 0 ≤ ‖a‖ := by
490490
#align norm_nonneg' norm_nonneg'
491491
#align norm_nonneg norm_nonneg
492492

493-
/- porting note: meta code, do not port
494-
section
495-
496-
open Tactic Tactic.Positivity
497-
498-
/-- Extension for the `positivity` tactic: norms are nonnegative. -/
499-
@[positivity]
500-
unsafe def _root_.tactic.positivity_norm : expr → tactic strictness
501-
| q(‖$(a)‖) =>
502-
nonnegative <$> mk_app `` norm_nonneg [a] <|> nonnegative <$> mk_app `` norm_nonneg' [a]
503-
| _ => failed
504-
#align tactic.positivity_norm tactic.positivity_norm
505-
506-
end
507-
-/
493+
namespace Mathlib.Meta.Positivity
494+
495+
open Lean Meta Qq Function
496+
497+
/-- Extension for the `positivity` tactic: multiplicative norms are nonnegative, via
498+
`norm_nonneg'`. -/
499+
@[positivity Norm.norm _]
500+
def evalMulNorm : PositivityExt where eval {_ _} _zα _pα e := do
501+
let .app _ a ← whnfR e | throwError "not ‖ ⬝ ‖"
502+
let p ← mkAppM ``norm_nonneg' #[a]
503+
pure (.nonnegative p)
504+
505+
/-- Extension for the `positivity` tactic: additive norms are nonnegative, via `norm_nonneg`. -/
506+
@[positivity Norm.norm _]
507+
def evalAddNorm : PositivityExt where eval {_ _} _zα _pα e := do
508+
let .app _ a ← whnfR e | throwError "not ‖ ⬝ ‖"
509+
let p ← mkAppM ``norm_nonneg #[a]
510+
pure (.nonnegative p)
511+
512+
end Mathlib.Meta.Positivity
508513

509514
@[to_additive (attr := simp) norm_zero]
510515
theorem norm_one' : ‖(1 : E)‖ = 0 := by rw [← dist_one_right, dist_self]

Mathlib/Analysis/Normed/Group/Seminorm.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ theorem mul_bddBelow_range_add {p q : GroupSeminorm E} {x : E} :
398398
0, by
399399
rintro _ ⟨x, rfl⟩
400400
dsimp
401-
exact add_nonneg (map_nonneg _ _) (map_nonneg _ _)⟩ -- porting note: was `positivity`
401+
positivity
402402
#align group_seminorm.mul_bdd_below_range_add GroupSeminorm.mul_bddBelow_range_add
403403
#align add_group_seminorm.add_bdd_below_range_add AddGroupSeminorm.add_bddBelow_range_add
404404

@@ -608,8 +608,7 @@ theorem add_bddBelow_range_add {p q : NonarchAddGroupSeminorm E} {x : E} :
608608
0, by
609609
rintro _ ⟨x, rfl⟩
610610
dsimp
611-
exact add_nonneg (map_nonneg _ _) (map_nonneg _ _)⟩
612-
-- porting note: was `positivity`
611+
positivity⟩
613612
#align nonarch_add_group_seminorm.add_bdd_below_range_add NonarchAddGroupSeminorm.add_bddBelow_range_add
614613

615614
end AddCommGroup

Mathlib/Analysis/Seminorm.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,8 +460,7 @@ variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] {p q : Seminorm
460460
theorem bddBelow_range_add : BddBelow (range fun u => p u + q (x - u)) :=
461461
0, by
462462
rintro _ ⟨x, rfl⟩
463-
-- Porting note: the following was previously `dsimp; positivity`
464-
exact add_nonneg (map_nonneg _ _) (map_nonneg _ _)⟩
463+
dsimp ; positivity⟩
465464
#align seminorm.bdd_below_range_add Seminorm.bddBelow_range_add
466465

467466
noncomputable instance instInf : Inf (Seminorm 𝕜 E) where

Mathlib/Data/Real/NNReal.lean

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,27 +1072,23 @@ theorem cast_natAbs_eq_nnabs_cast (n : ℤ) : (n.natAbs : ℝ≥0) = nnabs n :=
10721072

10731073
end Real
10741074

1075-
/-
1076-
namespace Tactic
1075+
namespace Mathlib.Meta.Positivity
10771076

1078-
open Positivity
1077+
open Lean Meta Qq Function
10791078

10801079
private theorem nnreal_coe_pos {r : ℝ≥0} : 0 < r → 0 < (r : ℝ) :=
10811080
NNReal.coe_pos.2
1082-
#align tactic.nnreal_coe_pos tactic.nnreal_coe_pos
10831081

10841082
/-- Extension for the `positivity` tactic: cast from `ℝ≥0` to `ℝ`. -/
1085-
@[positivity]
1086-
unsafe def positivity_coe_nnreal_real : expr → tactic strictness
1087-
| q(@coe _ _ $(inst) $(a)) => do
1088-
unify inst q(@coeToLift _ _ <| @coeBase _ _ NNReal.Real.hasCoe)
1089-
let strictness_a ← core a
1090-
match strictness_a with
1091-
| positive p => positive <$> mk_app `` nnreal_coe_pos [p]
1092-
| _ => nonnegative <$> mk_app `` NNReal.coe_nonneg [a]
1093-
| e =>
1094-
pp e >>= fail ∘ format.bracket "The expression " " is not of the form `(r : ℝ)` for `r : ℝ≥0`"
1095-
#align tactic.positivity_coe_nnreal_real tactic.positivity_coe_nnreal_real
1096-
1097-
end Tactic
1098-
-/
1083+
@[positivity NNReal.toReal _]
1084+
def evalNNRealtoReal : PositivityExt where eval {_ _} _zα _pα e := do
1085+
let (.app _ (a : Q(NNReal))) ← whnfR e | throwError "not NNReal.toReal"
1086+
let zα' ← synthInstanceQ (q(Zero NNReal) : Q(Type))
1087+
let pα' ← synthInstanceQ (q(PartialOrder NNReal) : Q(Type))
1088+
let ra ← core zα' pα' a
1089+
assertInstancesCommute
1090+
match ra with
1091+
| .positive pa => pure (.positive (q(nnreal_coe_pos $pa) : Expr))
1092+
| _ => pure (.nonnegative (q(NNReal.coe_nonneg $a) : Expr))
1093+
1094+
end Mathlib.Meta.Positivity

Mathlib/Data/Real/Sqrt.lean

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ theorem sqrt_div (x y : ℝ≥0) : sqrt (x / y) = sqrt x / sqrt y :=
124124
theorem continuous_sqrt : Continuous sqrt := sqrt.continuous
125125
#align nnreal.continuous_sqrt NNReal.continuous_sqrt
126126

127+
@[simp] theorem sqrt_pos : 0 < sqrt x ↔ 0 < x := by simp [pos_iff_ne_zero]
128+
129+
alias sqrt_pos ↔ _ sqrt_pos_of_pos
130+
127131
end NNReal
128132

129133
namespace Real
@@ -350,29 +354,41 @@ theorem sqrt_pos : 0 < sqrt x ↔ 0 < x :=
350354
alias sqrt_pos ↔ _ sqrt_pos_of_pos
351355
#align real.sqrt_pos_of_pos Real.sqrt_pos_of_pos
352356

353-
/-
354-
section
357+
end Real
355358

356-
Porting note: todo: restore positivity plugin
357-
open Tactic Tactic.Positivity
359+
namespace Mathlib.Meta.Positivity
360+
361+
open Lean Meta Qq Function
362+
363+
/-- Extension for the `positivity` tactic: a square root of a strictly positive nonnegative real is
364+
positive. -/
365+
@[positivity NNReal.sqrt _]
366+
def evalNNRealSqrt : PositivityExt where eval {_ _} _zα _pα e := do
367+
let (.app _ (a : Q(NNReal))) ← whnfR e | throwError "not NNReal.sqrt"
368+
let zα' ← synthInstanceQ (q(Zero NNReal) : Q(Type))
369+
let pα' ← synthInstanceQ (q(PartialOrder NNReal) : Q(Type))
370+
let ra ← core zα' pα' a
371+
assertInstancesCommute
372+
match ra with
373+
| .positive pa => pure (.positive (q(NNReal.sqrt_pos_of_pos $pa) : Expr))
374+
| _ => failure -- this case is dealt with by generic nonnegativity of nnreals
358375

359376
/-- Extension for the `positivity` tactic: a square root is nonnegative, and is strictly positive if
360377
its input is. -/
361-
@[positivity]
362-
unsafe def _root_.tactic.positivity_sqrt : expr → tactic strictness
363-
| q(Real.sqrt $(a)) => do
364-
(-- if can prove `0 < a`, report positivity
365-
do
366-
let positive pa ← core a
367-
positive <$> mk_app `` sqrt_pos_of_pos [pa]) <|>
368-
nonnegative <$> mk_app `` sqrt_nonneg [a]
369-
|-- else report nonnegativity
370-
_ =>
371-
failed
372-
#align tactic.positivity_sqrt tactic.positivity_sqrt
373-
374-
end
375-
-/
378+
@[positivity Real.sqrt _]
379+
def evalSqrt : PositivityExt where eval {_ _} _zα _pα e := do
380+
let (.app _ (a : Q(Real))) ← whnfR e | throwError "not Real.sqrt"
381+
let zα' ← synthInstanceQ (q(Zero Real) : Q(Type))
382+
let pα' ← synthInstanceQ (q(PartialOrder Real) : Q(Type))
383+
let ra ← core zα' pα' a
384+
assertInstancesCommute
385+
match ra with
386+
| .positive pa => pure (.positive (q(Real.sqrt_pos_of_pos $pa) : Expr))
387+
| _ => pure (.nonnegative (q(Real.sqrt_nonneg $a) : Expr))
388+
389+
end Mathlib.Meta.Positivity
390+
391+
namespace Real
376392

377393
@[simp]
378394
theorem sqrt_mul (hx : 0 ≤ x) (y : ℝ) : sqrt (x * y) = sqrt x * sqrt y := by

Mathlib/Tactic/Positivity/Basic.lean

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Authors: Mario Carneiro, Heather Macbeth, Yaël Dillies
55
-/
66
import Std.Lean.Parser
77
import Mathlib.Data.Int.Order.Basic
8+
import Mathlib.Data.Nat.Factorial.Basic
89
import Mathlib.Tactic.Positivity.Core
910
import Mathlib.Algebra.GroupPower.Order
1011
import Mathlib.Algebra.Order.Field.Basic
@@ -392,3 +393,9 @@ def evalIntCast : PositivityExt where eval {u α} _zα _pα e := do
392393
def evalNatSucc : PositivityExt where eval {_u _α} _zα _pα e := do
393394
let (.app _ (a : Q(Nat))) ← withReducible (whnf e) | throwError "not Nat.succ"
394395
pure (.positive (q(Nat.succ_pos $a) : Expr))
396+
397+
/-- Extension for Nat.factorial. -/
398+
@[positivity Nat.factorial _]
399+
def evalFactorial: PositivityExt where eval {_ _} _ _ e := do
400+
let .app _ (a : Q(Nat)) ← whnfR e | throwError "not Nat.factorial"
401+
pure (.positive (q(Nat.factorial_pos $a) : Expr))

Mathlib/Topology/MetricSpace/Basic.lean

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -274,21 +274,20 @@ theorem dist_nonneg {x y : α} : 0 ≤ dist x y :=
274274
dist_nonneg' dist dist_self dist_comm dist_triangle
275275
#align dist_nonneg dist_nonneg
276276

277-
/-
278-
section
277+
namespace Mathlib.Meta.Positivity
279278

280-
open Tactic Tactic.Positivity
281-
-- porting note: todo: restore `positivity` plugin
279+
open Lean Meta Qq Function
282280

283281
/-- Extension for the `positivity` tactic: distances are nonnegative. -/
284-
@[positivity]
285-
unsafe def _root_.tactic.positivity_dist : expr → tactic strictness
286-
| q(dist $(a) $(b)) => nonnegative <$> mk_app `` dist_nonneg [a, b]
287-
| _ => failed
288-
#align tactic.positivity_dist tactic.positivity_dist
282+
@[positivity Dist.dist _ _]
283+
def evalDist : PositivityExt where eval {_ _} _zα _pα e := do
284+
let .app (.app _ a) b ← whnfR e | throwError "not dist"
285+
let p ← mkAppOptM ``dist_nonneg #[none, none, a, b]
286+
pure (.nonnegative p)
289287

290-
end
291-
-/
288+
end Mathlib.Meta.Positivity
289+
290+
example {x y : α} : 0 ≤ dist x y := by positivity
292291

293292
@[simp] theorem abs_dist {a b : α} : |dist a b| = dist a b := abs_of_nonneg dist_nonneg
294293
#align abs_dist abs_dist
@@ -2777,21 +2776,18 @@ theorem exists_local_min_mem_ball [ProperSpace α] [TopologicalSpace β]
27772776

27782777
end Metric
27792778

2780-
/-
2781-
Porting note: restore positivity extension
2782-
namespace Tactic
2779+
namespace Mathlib.Meta.Positivity
27832780

2784-
open Positivity
2781+
open Lean Meta Qq Function
27852782

27862783
/-- Extension for the `positivity` tactic: the diameter of a set is always nonnegative. -/
2787-
@[positivity]
2788-
unsafe def positivity_diam : expr → tactic strictness
2789-
| q(Metric.diam $(s)) => nonnegative <$> mk_app `` Metric.diam_nonneg [s]
2790-
| e => pp e >>= fail ∘ format.bracket "The expression " " is not of the form `metric.diam s`"
2791-
#align tactic.positivity_diam tactic.positivity_diam
2784+
@[positivity Metric.diam _]
2785+
def evalDiam : PositivityExt where eval {_ _} _zα _pα e := do
2786+
let .app _ s ← whnfR e | throwError "not Metric.diam"
2787+
let p ← mkAppOptM ``Metric.diam_nonneg #[none, none, s]
2788+
pure (.nonnegative p)
27922789

2793-
end Tactic
2794-
-/
2790+
end Mathlib.Meta.Positivity
27952791

27962792
theorem comap_dist_right_atTop_le_cocompact (x : α) :
27972793
comap (fun y => dist y x) atTop ≤ cocompact α := by

0 commit comments

Comments
 (0)