Skip to content

Commit

Permalink
feat(analysis/special_functions/pow): norm_num extension for rpow (#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
digama0 committed Jan 11, 2022
1 parent 00b8ff6 commit d9430c0
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/algebra/group_power/basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ by { convert pow_two a using 1, exact zpow_coe_nat a 2 }
theorem zpow_neg_one (x : G) : x ^ (-1:ℤ) = x⁻¹ :=
(zpow_neg_succ_of_nat x 0).trans $ congr_arg has_inv.inv (pow_one x)

@[to_additive]
theorem zpow_neg_coe_of_pos (a : G) : ∀ {n : ℕ}, 0 < n → a ^ -(n:ℤ) = (a ^ n)⁻¹
| (n+1) _ := zpow_neg_succ_of_nat _ _

end div_inv_monoid

section group
Expand Down
10 changes: 3 additions & 7 deletions src/algebra/star/chsh.lean
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,9 @@ we prepare some easy lemmas about √2.
-- defeated me. Thanks for the rescue from Shing Tak Lam!
lemma tsirelson_inequality_aux : √2 * √2 ^ 3 = √2 * (2 * √2⁻¹ + 4 * (√2⁻¹ * 2⁻¹)) :=
begin
ring_nf,
rw [mul_assoc, inv_mul_cancel, real.sqrt_eq_rpow, ←real.rpow_nat_cast, ←real.rpow_mul],
{ norm_num,
rw show (2 : ℝ) ^ (2 : ℝ) = (2 : ℝ) ^ (2 : ℕ), by { rw ←real.rpow_nat_cast, norm_num },
norm_num },
{ norm_num, },
{ norm_num, },
ring_nf, field_simp [(@real.sqrt_pos 2).2 (by norm_num)],
convert congr_arg (^2) (@real.sq_sqrt 2 (by norm_num)) using 1;
simp only [← pow_mul]; norm_num,
end

lemma sqrt_two_inv_mul_self : √2⁻¹ * √2⁻¹ = (2⁻¹ : ℝ) :=
Expand Down
103 changes: 103 additions & 0 deletions src/analysis/special_functions/pow.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1592,3 +1592,106 @@ begin
end

end ennreal

namespace norm_num
open tactic

theorem rpow_pos (a b : ℝ) (b' : ℕ) (c : ℝ) (hb : b = b') (h : a ^ b' = c) : a ^ b = c :=
by rw [← h, hb, real.rpow_nat_cast]
theorem rpow_neg (a b : ℝ) (b' : ℕ) (c c' : ℝ)
(a0 : 0 ≤ a) (hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ -b = c' :=
by rw [← hc, ← h, hb, real.rpow_neg a0, real.rpow_nat_cast]

/-- Evaluate `real.rpow a b` where `a` is a rational numeral and `b` is an integer.
(This cannot go via the generalized version `prove_rpow'` because `rpow_pos` has a side condition;
we do not attempt to evaluate `a ^ b` where `a` and `b` are both negative because it comes
out to some garbage.) -/
meta def prove_rpow (a b : expr) : tactic (expr × expr) := do
na ← a.to_rat,
ic ← mk_instance_cache `(ℝ),
match match_sign b with
| sum.inl b := do
(ic, a0) ← guard (na ≥ 0) >> prove_nonneg ic a,
nc ← mk_instance_cache `(ℕ),
(ic, nc, b', hb) ← prove_nat_uncast ic nc b,
(ic, c, h) ← prove_pow a na ic b',
cr ← c.to_rat,
(ic, c', hc) ← prove_inv ic c cr,
pure (c', (expr.const ``rpow_neg []).mk_app [a, b, b', c, c', a0, hb, h, hc])
| sum.inr ff := pure (`(1:ℝ), expr.const ``real.rpow_zero [] a)
| sum.inr tt := do
nc ← mk_instance_cache `(ℕ),
(ic, nc, b', hb) ← prove_nat_uncast ic nc b,
(ic, c, h) ← prove_pow a na ic b',
pure (c, (expr.const ``rpow_pos []).mk_app [a, b, b', c, hb, h])
end

/-- Generalized version of `prove_cpow`, `prove_nnrpow`, `prove_ennrpow`. -/
meta def prove_rpow' (pos neg zero : name) (α β one a b : expr) : tactic (expr × expr) := do
na ← a.to_rat,
icα ← mk_instance_cache α,
icβ ← mk_instance_cache β,
match match_sign b with
| sum.inl b := do
nc ← mk_instance_cache `(ℕ),
(icβ, nc, b', hb) ← prove_nat_uncast icβ nc b,
(icα, c, h) ← prove_pow a na icα b',
cr ← c.to_rat,
(icα, c', hc) ← prove_inv icα c cr,
pure (c', (expr.const neg []).mk_app [a, b, b', c, c', hb, h, hc])
| sum.inr ff := pure (one, expr.const zero [] a)
| sum.inr tt := do
nc ← mk_instance_cache `(ℕ),
(icβ, nc, b', hb) ← prove_nat_uncast icβ nc b,
(icα, c, h) ← prove_pow a na icα b',
pure (c, (expr.const pos []).mk_app [a, b, b', c, hb, h])
end

open_locale nnreal ennreal

theorem cpow_pos (a b : ℂ) (b' : ℕ) (c : ℂ) (hb : b = b') (h : a ^ b' = c) : a ^ b = c :=
by rw [← h, hb, complex.cpow_nat_cast]
theorem cpow_neg (a b : ℂ) (b' : ℕ) (c c' : ℂ)
(hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ -b = c' :=
by rw [← hc, ← h, hb, complex.cpow_neg, complex.cpow_nat_cast]

theorem nnrpow_pos (a : ℝ≥0) (b : ℝ) (b' : ℕ) (c : ℝ≥0)
(hb : b = b') (h : a ^ b' = c) : a ^ b = c :=
by rw [← h, hb, nnreal.rpow_nat_cast]
theorem nnrpow_neg (a : ℝ≥0) (b : ℝ) (b' : ℕ) (c c' : ℝ≥0)
(hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ -b = c' :=
by rw [← hc, ← h, hb, nnreal.rpow_neg, nnreal.rpow_nat_cast]

theorem ennrpow_pos (a : ℝ≥0∞) (b : ℝ) (b' : ℕ) (c : ℝ≥0∞)
(hb : b = b') (h : a ^ b' = c) : a ^ b = c :=
by rw [← h, hb, ennreal.rpow_nat_cast]
theorem ennrpow_neg (a : ℝ≥0∞) (b : ℝ) (b' : ℕ) (c c' : ℝ≥0∞)
(hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ -b = c' :=
by rw [← hc, ← h, hb, ennreal.rpow_neg, ennreal.rpow_nat_cast]

/-- Evaluate `complex.cpow a b` where `a` is a rational numeral and `b` is an integer. -/
meta def prove_cpow : expr → expr → tactic (expr × expr) :=
prove_rpow' ``cpow_pos ``cpow_neg ``complex.cpow_zero `(ℂ) `(ℂ) `(1:ℂ)

/-- Evaluate `nnreal.rpow a b` where `a` is a rational numeral and `b` is an integer. -/
meta def prove_nnrpow : expr → expr → tactic (expr × expr) :=
prove_rpow' ``nnrpow_pos ``nnrpow_neg ``nnreal.rpow_zero `(ℝ≥0) `(ℝ) `(1:ℝ≥0)

/-- Evaluate `ennreal.rpow a b` where `a` is a rational numeral and `b` is an integer. -/
meta def prove_ennrpow : expr → expr → tactic (expr × expr) :=
prove_rpow' ``ennrpow_pos ``ennrpow_neg ``ennreal.rpow_zero `(ℝ≥0∞) `(ℝ) `(1:ℝ≥0∞)

/-- Evaluates expressions of the form `rpow a b`, `cpow a b` and `a ^ b` in the special case where
`b` is an integer and `a` is a positive rational (so it's really just a rational power). -/
@[norm_num] meta def eval_rpow_cpow : expr → tactic (expr × expr)
| `(@has_pow.pow _ _ real.has_pow %%a %%b) := b.to_int >> prove_rpow a b
| `(real.rpow %%a %%b) := b.to_int >> prove_rpow a b
| `(@has_pow.pow _ _ complex.has_pow %%a %%b) := b.to_int >> prove_cpow a b
| `(complex.cpow %%a %%b) := b.to_int >> prove_cpow a b
| `(@has_pow.pow _ _ nnreal.real.has_pow %%a %%b) := b.to_int >> prove_nnrpow a b
| `(nnreal.rpow %%a %%b) := b.to_int >> prove_nnrpow a b
| `(@has_pow.pow _ _ ennreal.real.has_pow %%a %%b) := b.to_int >> prove_ennrpow a b
| `(ennreal.rpow %%a %%b) := b.to_int >> prove_ennrpow a b
| _ := tactic.failed

end norm_num
37 changes: 37 additions & 0 deletions src/tactic/norm_num.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1045,19 +1045,56 @@ meta def prove_pow (a : expr) (na : ℚ) :

end

lemma zpow_pos {α} [div_inv_monoid α] (a : α) (b : ℤ) (b' : ℕ) (c : α)
(hb : b = b') (h : a ^ b' = c) : a ^ b = c := by rw [← h, hb, zpow_coe_nat]
lemma zpow_neg {α} [div_inv_monoid α] (a : α) (b : ℤ) (b' : ℕ) (c c' : α)
(b0 : 0 < b') (hb : b = b') (h : a ^ b' = c) (hc : c⁻¹ = c') : a ^ -b = c' :=
by rw [← hc, ← h, hb, zpow_neg_coe_of_pos _ b0]

/-- Given `a` a rational numeral and `b : ℤ`, returns `(c, ⊢ a ^ b = c)`. -/
meta def prove_zpow (ic zc nc : instance_cache) (a : expr) (na : ℚ) (b : expr) :
tactic (instance_cache × instance_cache × instance_cache × expr × expr) :=
match match_sign b with
| sum.inl b := do
(zc, nc, b', hb) ← prove_nat_uncast zc nc b,
(ic, c, h) ← prove_pow a na ic b',
(ic, c', hc) ← c.to_rat >>= prove_inv ic c,
(ic, p) ← ic.mk_app ``zpow_neg [a, b, b', c, c', hb, h, hc],
pure (ic, zc, nc, c', p)
| sum.inr ff := do
(ic, o) ← ic.mk_app ``has_one.one [],
(ic, p) ← ic.mk_app ``zpow_zero [a],
pure (ic, zc, nc, o, p)
| sum.inr tt := do
(zc, nc, b', hb) ← prove_nat_uncast zc nc b,
(ic, c, h) ← prove_pow a na ic b',
(ic, p) ← ic.mk_app ``zpow_pos [a, b, b', c, hb, h],
pure (ic, zc, nc, c, p)
end

/-- Evaluates expressions of the form `a ^ b`, `monoid.npow a b` or `nat.pow a b`. -/
meta def eval_pow : expr → tactic (expr × expr)
| `(@has_pow.pow %%α _ %%m %%e₁ %%e₂) := do
n₁ ← e₁.to_rat,
c ← infer_type e₁ >>= mk_instance_cache,
match m with
| `(@monoid.has_pow %%_ %%_) := prod.snd <$> prove_pow e₁ n₁ c e₂
| `(@div_inv_monoid.has_pow %%_ %%_) := do
zc ← mk_instance_cache `(ℤ),
nc ← mk_instance_cache `(ℕ),
(prod.snd ∘ prod.snd ∘ prod.snd) <$> prove_zpow c zc nc e₁ n₁ e₂
| _ := failed
end
| `(monoid.npow %%e₁ %%e₂) := do
n₁ ← e₁.to_rat,
c ← infer_type e₁ >>= mk_instance_cache,
prod.snd <$> prove_pow e₁ n₁ c e₂
| `(div_inv_monoid.zpow %%e₁ %%e₂) := do
n₁ ← e₁.to_rat,
c ← infer_type e₁ >>= mk_instance_cache,
zc ← mk_instance_cache `(ℤ),
nc ← mk_instance_cache `(ℕ),
(prod.snd ∘ prod.snd ∘ prod.snd) <$> prove_zpow c zc nc e₁ n₁ e₂
| _ := failed

/-- Given `⊢ p`, returns `(true, ⊢ p = true)`. -/
Expand Down
4 changes: 4 additions & 0 deletions test/norm_num.lean
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ example : (4:real)⁻¹ < 1 := by norm_num
example : ((1:real) / 2)⁻¹ = 2 := by norm_num
example : 2 ^ 17 - 1 = 131071 :=
by {norm_num, tactic.try_for 200 (tactic.result >>= tactic.type_check)}
example : (3 : real) ^ (-2 : ℤ) = 1/9 := by norm_num
example : (-3 : real) ^ (0 : ℤ) = 1 := by norm_num
example : (-3 : real) ^ (-1 : ℤ) = -1/3 := by norm_num
example : (-3 : real) ^ (2 : ℤ) = 9 := by norm_num

example : (1:complex) ≠ 2 := by norm_num
example : (1:complex) / 32 / 7 := by norm_num
Expand Down

0 comments on commit d9430c0

Please sign in to comment.