Skip to content

Commit 8050059

Browse files
dwrenshaeric-wieser
andcommitted
feat: implement rpow norm_num extension (#9893)
* Implements a norm_num extension for `a ^ b` where `a` and `b` are reals. Unlike in the mathlib3 version, there is no restriction on the positivity of `a`. * Moves commented-out tests from test/norm_num_ext.lean into a new file test/norm_num_rpow.lean, to keep the dependencies of norm_num_ext.lean lightweight. Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
1 parent b4f6049 commit 8050059

File tree

4 files changed

+102
-73
lines changed

4 files changed

+102
-73
lines changed

Mathlib/Analysis/SpecialFunctions/Pow/Real.lean

Lines changed: 76 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -905,56 +905,82 @@ section Tactics
905905
/-!
906906
## Tactic extensions for real powers
907907
-/
908-
909-
910-
-- namespace NormNum
911-
912-
-- open Tactic
913-
914-
-- theorem rpow_pos (a b : ℝ) (b' : ℕ) (c : ℝ) (hb : (b' : ℝ) = b) (h : a ^ b' = c) :
915-
-- a ^ b = c := by
916-
-- rw [← h, ← hb, Real.rpow_nat_cast]
917-
-- #align norm_num.rpow_pos NormNum.rpow_pos
918-
919-
-- theorem rpow_neg (a b : ℝ) (b' : ℕ) (c c' : ℝ) (a0 : 0 ≤ a) (hb : (b' : ℝ) = b) (h : a ^ b' = c)
920-
-- (hc : c⁻¹ = c') : a ^ (-b) = c' := by
921-
-- rw [← hc, ← h, ← hb, Real.rpow_neg a0, Real.rpow_nat_cast]
922-
-- #align norm_num.rpow_neg NormNum.rpow_neg
923-
924-
-- /-- Evaluate `Real.rpow a b` where `a` is a rational numeral and `b` is an integer.
925-
-- (This cannot go via the generalized version `prove_rpow'` because `rpow_pos` has a
926-
-- side condition; we do not attempt to evaluate `a ^ b` where `a` and `b` are both negative because
927-
-- it comes out to some garbage.) -/
928-
-- unsafe def prove_rpow (a b : expr) : tactic (expr × expr) := do
929-
-- let na ← a.to_rat
930-
-- let ic ← mk_instance_cache q(ℝ)
931-
-- match match_sign b with
932-
-- | Sum.inl b => do
933-
-- let (ic, a0) ← guard (na ≥ 0) >> prove_nonneg ic a
934-
-- let nc ← mk_instance_cache q(ℕ)
935-
-- let (ic, nc, b', hb) ← prove_nat_uncast ic nc b
936-
-- let (ic, c, h) ← prove_pow a na ic b'
937-
-- let cr ← c
938-
-- let (ic, c', hc) ← prove_inv ic c cr
939-
-- pure (c', (expr.const `` rpow_neg []).mk_app [a, b, b', c, c', a0, hb, h, hc])
940-
-- | Sum.inr ff => pure (q((1 : ℝ)), expr.const `` Real.rpow_zero [] a)
941-
-- | Sum.inr tt => do
942-
-- let nc ← mk_instance_cache q(ℕ)
943-
-- let (ic, nc, b', hb) ← prove_nat_uncast ic nc b
944-
-- let (ic, c, h) ← prove_pow a na ic b'
945-
-- pure (c, (expr.const `` rpow_pos []).mk_app [a, b, b', c, hb, h])
946-
-- #align norm_num.prove_rpow norm_num.prove_rpow
947-
948-
-- /-- Evaluates expressions of the form `rpow a b` and `a ^ b` in the special case where
949-
-- `b` is an integer and `a` is a positive rational (so it's really just a rational power). -/
950-
-- @[norm_num]
951-
-- unsafe def eval_rpow : expr → tactic (expr × expr)
952-
-- | q(@Pow.pow _ _ Real.hasPow $(a) $(b)) => b.to_int >> prove_rpow a b
953-
-- | q(Real.rpow $(a) $(b)) => b.to_int >> prove_rpow a b
954-
-- | _ => tactic.failed
955-
-- #align norm_num.eval_rpow norm_num.eval_rpow
956-
957-
-- end NormNum
908+
namespace Mathlib.Meta.NormNum
909+
910+
open Lean.Meta Qq
911+
912+
theorem isNat_rpow_pos {a b : ℝ} {nb ne : ℕ}
913+
(pb : IsNat b nb) (pe' : IsNat (a ^ nb) ne) :
914+
IsNat (a ^ b) ne := by
915+
rwa [pb.out, rpow_nat_cast]
916+
917+
theorem isNat_rpow_neg {a b : ℝ} {nb ne : ℕ}
918+
(pb : IsInt b (Int.negOfNat nb)) (pe' : IsNat (a ^ (Int.negOfNat nb)) ne) :
919+
IsNat (a ^ b) ne := by
920+
rwa [pb.out, Real.rpow_int_cast]
921+
922+
theorem isInt_rpow_pos {a b : ℝ} {nb ne : ℕ}
923+
(pb : IsNat b nb) (pe' : IsInt (a ^ nb) (Int.negOfNat ne)) :
924+
IsInt (a ^ b) (Int.negOfNat ne) := by
925+
rwa [pb.out, rpow_nat_cast]
926+
927+
theorem isInt_rpow_neg {a b : ℝ} {nb ne : ℕ}
928+
(pb : IsInt b (Int.negOfNat nb)) (pe' : IsInt (a ^ (Int.negOfNat nb)) (Int.negOfNat ne)) :
929+
IsInt (a ^ b) (Int.negOfNat ne) := by
930+
rwa [pb.out, Real.rpow_int_cast]
931+
932+
theorem isRat_rpow_pos {a b : ℝ} {nb : ℕ}
933+
{num : ℤ} {den : ℕ}
934+
(pb : IsNat b nb) (pe' : IsRat (a^nb) num den) :
935+
IsRat (a^b) num den := by
936+
rwa [pb.out, rpow_nat_cast]
937+
938+
theorem isRat_rpow_neg {a b : ℝ} {nb : ℕ}
939+
{num : ℤ} {den : ℕ}
940+
(pb : IsInt b (Int.negOfNat nb)) (pe' : IsRat (a^(Int.negOfNat nb)) num den) :
941+
IsRat (a^b) num den := by
942+
rwa [pb.out, Real.rpow_int_cast]
943+
944+
/-- Evaluates expressions of the form `a ^ b` when `a` and `b` are both reals.-/
945+
@[norm_num (_ : ℝ) ^ (_ : ℝ)]
946+
def evalRPow : NormNumExt where eval {u α} e := do
947+
let .app (.app f (a : Q(ℝ))) (b : Q(ℝ)) ← Lean.Meta.whnfR e | failure
948+
guard <|← withNewMCtxDepth <| isDefEq f q(HPow.hPow (α := ℝ) (β := ℝ))
949+
haveI' : u =QL 0 := ⟨⟩
950+
haveI' : $α =Q ℝ := ⟨⟩
951+
haveI' h : $e =Q $a ^ $b := ⟨⟩
952+
h.check
953+
let (rb : Result b) ← derive (α := q(ℝ)) b
954+
match rb with
955+
| .isBool .. | .isRat _ .. => failure
956+
| .isNat sβ nb pb =>
957+
match ← derive q($a ^ $nb) with
958+
| .isBool .. => failure
959+
| .isNat sα' ne' pe' =>
960+
assumeInstancesCommute
961+
haveI' : $sα' =Q AddGroupWithOne.toAddMonoidWithOne := ⟨⟩
962+
return .isNat sα' ne' q(isNat_rpow_pos $pb $pe')
963+
| .isNegNat sα' ne' pe' =>
964+
assumeInstancesCommute
965+
return .isNegNat sα' ne' q(isInt_rpow_pos $pb $pe')
966+
| .isRat sα' qe' nume' dene' pe' =>
967+
assumeInstancesCommute
968+
return .isRat sα' qe' nume' dene' q(isRat_rpow_pos $pb $pe')
969+
| .isNegNat sβ nb pb =>
970+
match ← derive q($a ^ (-($nb : ℤ))) with
971+
| .isBool .. => failure
972+
| .isNat sα' ne' pe' =>
973+
assumeInstancesCommute
974+
return .isNat sα' ne' q(isNat_rpow_neg $pb $pe')
975+
| .isNegNat sα' ne' pe' =>
976+
let _ := q(instRingReal)
977+
assumeInstancesCommute
978+
return .isNegNat sα' ne' q(isInt_rpow_neg $pb $pe')
979+
| .isRat sα' qe' nume' dene' pe' =>
980+
assumeInstancesCommute
981+
return .isRat sα' qe' nume' dene' q(isRat_rpow_neg $pb $pe')
982+
983+
end Mathlib.Meta.NormNum
958984

959985
end Tactics
960986

Mathlib/NumberTheory/Bertrand.lean

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -88,26 +88,20 @@ theorem real_main_inequality {x : ℝ} (n_large : (512 : ℝ) ≤ x) :
8888
exact (h.right_le_of_le_left'' h1 ((h1.trans h2).trans_le h0) h2 h0 (h4.trans h3)).trans h4
8989
refine' ⟨18, 512, by norm_num1, by norm_num1, n_large, _, _⟩
9090
· have : sqrt (2 * 18) = 6 := (sqrt_eq_iff_mul_self_eq_of_pos (by norm_num1)).mpr (by norm_num1)
91-
rw [hf, log_nonneg_iff, this]
92-
rw [one_le_div] <;> norm_num1
93-
apply le_trans _ (le_mul_of_one_le_left _ _) <;> norm_num1
94-
apply Real.rpow_le_rpow <;> norm_num1
95-
apply rpow_nonneg; norm_num1
96-
apply rpow_pos_of_pos; norm_num1
97-
apply hf' 18; norm_num1
91+
rw [hf _ (by norm_num1), log_nonneg_iff (by positivity), this, one_le_div (by norm_num1)]
9892
norm_num1
9993
· have : sqrt (2 * 512) = 32 :=
10094
(sqrt_eq_iff_mul_self_eq_of_pos (by norm_num1)).mpr (by norm_num1)
101-
rw [hf, log_nonpos_iff (hf' _ _), this, div_le_one] <;> norm_num1
102-
· conv in 512 => equals 2 ^ 9 => norm_num1
103-
conv in 1024 => equals 2 ^ 10 => norm_num1
104-
conv in 32 => rw [← Nat.cast_ofNat]
105-
rw [rpow_nat_cast, ← pow_mul, ← pow_add]
106-
conv in 4 => equals 2 ^ (2 : ℝ) => rw [rpow_two]; norm_num1
107-
rw [← rpow_mul, ← rpow_nat_cast]
108-
apply rpow_le_rpow_of_exponent_le
109-
all_goals norm_num1
110-
· apply rpow_pos_of_pos four_pos
95+
rw [hf _ (by norm_num1), log_nonpos_iff (hf' _ (by norm_num1)), this,
96+
div_le_one (by positivity)]
97+
conv in 512 => equals 2 ^ 9 => norm_num1
98+
conv in 2 * 512 => equals 2 ^ 10 => norm_num1
99+
conv in 32 => rw [← Nat.cast_ofNat]
100+
rw [rpow_nat_cast, ← pow_mul, ← pow_add]
101+
conv in 4 => equals 2 ^ (2 : ℝ) => rw [rpow_two]; norm_num1
102+
rw [← rpow_mul, ← rpow_nat_cast]
103+
apply rpow_le_rpow_of_exponent_le
104+
all_goals norm_num1
111105
#align bertrand.real_main_inequality Bertrand.real_main_inequality
112106

113107
end Bertrand

test/norm_num_ext.lean

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,6 @@ example : Nat.fib 65 = 17167680177565 := by norm_num1
323323
example : Nat.fib 100 + Nat.fib 101 = Nat.fib 102 := by norm_num1
324324
example : Nat.fib 1000 + Nat.fib 1001 = Nat.fib 1002 := by norm_num1
325325

326-
/-
327-
example : (2 : ℝ) ^ (3 : ℝ) = 8 := by norm_num
328-
example : (1 : ℝ) ^ (20 : ℝ) = 1 := by norm_num
329-
example : (2 : ℝ) ^ (-3 : ℝ) = 1/8 := by norm_num
330-
-/
331-
332326
section big_operators
333327

334328
variable {α : Type _} [CommRing α]

test/norm_num_rpow.lean

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/-
2+
Copyright (c) 2021 Mario Carneiro All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Mario Carneiro, David Renshaw
5+
-/
6+
import Mathlib.Analysis.SpecialFunctions.Pow.Real
7+
8+
example : (2 : ℝ) ^ (3 : ℝ) = 8 := by norm_num1
9+
example : (1 : ℝ) ^ (20 : ℝ) = 1 := by norm_num1
10+
example : (-2 : ℝ) ^ (3 : ℝ) = -8 := by norm_num1
11+
example : (1/5 : ℝ) ^ (2 : ℝ) = 1/25 := by norm_num1
12+
example : (-1/3 : ℝ) ^ (-3 : ℝ) = -27 := by norm_num1
13+
example : (1/2 : ℝ) ^ (-3 : ℝ) = 8 := by norm_num1
14+
example : (2 : ℝ) ^ (-3 : ℝ) = 1/8 := by norm_num1
15+
example : (-2 : ℝ) ^ (-3 : ℝ) = -1/8 := by norm_num1

0 commit comments

Comments
 (0)