Theory Complex_Roots
section ‹$n$-th roots of complex numbers›
theory Complex_Roots
imports
Complex_Geometry.More_Complex
Algebraic_Numbers.Complex_Algebraic_Numbers
Factor_Algebraic_Polynomial.Roots_via_IA
"HOL-Library.Product_Lexorder"
begin
subsection ‹An algorithm to compute all complex roots of (algebraic) complex numbers›
definition all_croots :: "nat ⇒ complex ⇒ complex list" where
"all_croots n x = (if n = 0 then [] else
if algebraic x then
(let p = min_int_poly x;
q = poly_nth_root n p;
xs = complex_roots_of_int_poly q
in filter (λ y. y^n = x) xs)
else (SOME ys. set ys = {y. y^n = x}))"
lemma all_croots: assumes n0: "n ≠ 0" shows "set (all_croots n x) = {y. y^n = x}"
proof (cases "algebraic x")
case True
hence id: "(if n = 0 then y else if algebraic x then z else u) = z"
for y z u :: "complex list" using n0 by auto
define p where "p = poly_nth_root n (min_int_poly x)"
show ?thesis unfolding Let_def p_def[symmetric] all_croots_def id
proof (standard, force, standard, simp)
fix y
assume y: "y ^n = x"
have "min_int_poly x represents x" using True by auto
from represents_nth_root[OF n0 y this]
have "p represents y" unfolding p_def by auto
thus "y ∈ set (complex_roots_of_int_poly p)"
by (subst complex_roots_of_int_poly, auto)
qed
next
case False
hence id: "(if n = 0 then y else if algebraic x then z else u) = u"
for y z u :: "complex list" using n0 by auto
show ?thesis unfolding Let_def all_croots_def id
by (rule someI_ex, rule finite_list, insert n0, blast)
qed
text ‹TODO: One might change @{const complex_roots_of_int_poly} to @{const complex_roots_of_int_poly3}
in order to avoid an unnecessary factorization of an integer polynomial. However, then
this change already needs to be performed within the definition of @{const all_croots}.›
lift_definition all_croots_part1 :: "nat ⇒ complex ⇒ complex genuine_roots_aux" is
"λ n x. if n = 0 ∨ x = 0 ∨ ¬ algebraic x then (1,[],0, filter_fun_complex 1)
else let p = min_int_poly x;
q = poly_nth_root n p;
zeros = complex_roots_of_int_poly q;
r = Polynomial.monom 1 n - [:x:]
in (r,zeros, n, filter_fun_complex r)"
subgoal for n x
proof (cases "n = 0 ∨ x = 0 ∨ ¬ algebraic x")
case True
thus ?thesis by (simp add: filter_fun_complex)
next
case False
hence *: "algebraic x" "n ≠ 0" "x ≠ 0" by auto
{
fix z
assume zn: "z^n = x"
from *(1) have repr: "min_int_poly x represents x" by auto
from represents_nth_root[OF *(2) zn repr]
have "poly_nth_root n (min_int_poly x) represents z" .
}
moreover have "card {z. z ^ n = x} = n"
by (rule card_nth_roots) (use * in auto)
ultimately show ?thesis using *
by (auto simp: Let_def complex_roots_of_int_poly filter_fun_complex poly_monom)
qed
done
lemma all_croots_code[code]:
"all_croots n x = (if n = 0 then [] else if x = 0 then [0]
else if algebraic x then genuine_roots_impl (all_croots_part1 n x)
else Code.abort (STR ''all_croots invoked on non-algebraic number'') (λ _. all_croots n x))"
proof (cases "n = 0")
case True
thus ?thesis unfolding all_croots_def by simp
next
case n: False
show ?thesis
proof (cases "x = 0")
case x: False
show ?thesis
proof (cases "algebraic x")
case False
with n x show ?thesis by simp
next
case True
define t where "t = ?thesis"
have "t ⟷ filter (λy. y ^ n = x)
(complex_roots_of_int_poly (poly_nth_root n (min_int_poly x)))
= genuine_roots_impl (all_croots_part1 n x)"
unfolding t_def
by (subst all_croots_def[of n x], unfold Let_def, insert n x True, auto)
also have "…" using n x True unfolding genuine_roots_impl_def
by (transfer, simp add: Let_def genuine_roots_def poly_monom)
finally show ?thesis unfolding t_def by simp
qed
next
case x: True
have "set (all_croots n 0) = {0}" unfolding all_croots[OF n] using n by simp
moreover have "distinct (all_croots n 0)" unfolding all_croots_def using n
by (auto intro!: distinct_filter complex_roots_of_int_poly)
ultimately have "all_croots n 0 = [0]"
by (smt (verit, del_insts) distinct.simps(2) distinct_singleton insert_ident list.set_cases list.set_intros(1) list.simps(15) mem_Collect_eq set_empty singleton_conv)
moreover have "?thesis ⟷ all_croots n 0 = [0]" using n x by simp
ultimately show ?thesis by auto
qed
qed
subsection ‹A definition of \emph{the} complex root of a complex number›
text ‹While the definition of the complex root is quite natural and easy,
the main task is a criterion to determine which of all possible roots of a
complex number is the chosen one.›
definition croot :: "nat ⇒ complex ⇒ complex" where
"croot n x = (rcis (root n (cmod x)) (Arg x / of_nat n))"
lemma croot_0[simp]: "croot n 0 = 0" "croot 0 x = 0"
unfolding croot_def by auto
lemma croot_power: assumes n: "n ≠ 0"
shows "(croot n x) ^ n = x"
unfolding croot_def DeMoivre2
by (subst real_root_pow_pos2, insert n, auto simp: rcis_cmod_Arg)
lemma Arg_of_real: "Arg (of_real x) =
(if x < 0 then pi else 0)"
proof (cases "x = 0")
case False
hence "x < 0 ∨ x > 0" by auto
thus ?thesis by (intro cis_Arg_unique, auto
simp: complex_sgn_def scaleR_complex.ctr complex_eq_iff)
qed (auto simp: Arg_def)
lemma Arg_rcis_cis[simp]: assumes "x > 0"
shows "Arg (rcis x y) = Arg (cis y)"
using assms unfolding rcis_def by simp
lemma cis_Arg_1[simp]: "cis (Arg 1) = 1"
using Arg_of_real[of 1] by simp
lemma cis_Arg_power[simp]: assumes "x ≠ 0"
shows "cis (Arg (x ^ n)) = cis (Arg x * real n)"
proof (induct n)
case (Suc n)
show ?case unfolding power.simps
proof (subst cis_arg_mult)
show "cis (Arg x + Arg (x ^ n)) = cis (Arg x * real (Suc n))"
unfolding mult.commute[of "Arg x"] DeMoivre[symmetric]
unfolding power.simps using Suc
by (metis DeMoivre cis_mult mult.commute)
show "x * x ^ n ≠ 0" using assms by auto
qed
qed simp
lemma Arg_croot[simp]: "Arg (croot n x) = Arg x / real n"
proof (cases "n = 0 ∨ x = 0")
case True
thus ?thesis by (auto simp: Arg_def)
next
case False
hence n: "n ≠ 0" and x: "x ≠ 0" by auto
let ?root = "croot n x"
from n have n1: "real n ≥ 1" "real n > 0" "real n ≠ 0" by auto
have bounded: "- pi < Arg x / real n ∧ Arg x / real n ≤ pi"
proof (cases "Arg x < 0")
case True
from Arg_bounded[of x] have "- pi < Arg x" by auto
also have "… ≤ Arg x / real n" using n1 True
by (smt (verit) div_by_1 divide_minus_left frac_le)
finally have one: "- pi < Arg x / real n" .
have "Arg x / real n ≤ 0" using True n1
by (smt (verit) divide_less_0_iff)
also have "… ≤ pi" by simp
finally show ?thesis using one by auto
next
case False
hence ax: "Arg x ≥ 0" by auto
have "Arg x / real n ≤ Arg x" using n1 ax
by (smt (verit) div_by_1 frac_le)
also have "… ≤ pi" using Arg_bounded[of x] by simp
finally have one: "Arg x / real n ≤ pi" .
have "-pi < 0" by simp
also have "… ≤ Arg x / real n" using ax n1 by simp
finally show ?thesis using one by auto
qed
have "Arg ?root = Arg (cis (Arg x / real n))"
unfolding croot_def using x n by simp
also have "… = Arg x / real n"
by (rule cis_Arg_unique, force, insert bounded, auto)
finally show ?thesis .
qed
lemma cos_abs[simp]: "cos (abs x :: real) = cos x"
proof (cases "x < 0")
case True
hence abs: "abs x = - x" by simp
show ?thesis unfolding abs by simp
qed simp
lemma cos_mono_le: assumes "abs x ≤ pi"
and "abs y ≤ pi"
shows "cos x ≤ cos y ⟷ abs y ≤ abs x"
proof -
have "cos x ≤ cos y ⟷ cos (abs x) ≤ cos (abs y)" by simp
also have "… ⟷ abs y ≤ abs x"
by (subst cos_mono_le_eq, insert assms, auto)
finally show ?thesis .
qed
lemma abs_add_2_mult_bound: fixes x :: "'a :: linordered_idom"
assumes xy: "¦x¦ ≤ y"
shows "¦x¦ ≤ ¦x + 2 * of_int i * y¦"
proof (cases "i = 0")
case i: False
let ?oi = "of_int :: int ⇒ 'a"
from xy have y: "y ≥ 0" by auto
consider (pp) "x ≥ 0" "i ≥ 0"
| (nn) "x ≤ 0" "i ≤ 0"
| (pn) "x ≥ 0" "i ≤ 0"
| (np) "x ≤ 0" "i ≥ 0"
by linarith
thus ?thesis
proof cases
case pp
thus ?thesis using y by simp
next
case nn
have "x ≥ x + 2 * ?oi i * y"
using nn y by (simp add: mult_nonneg_nonpos2)
with nn show ?thesis by linarith
next
case pn
with i have "0 ≤ x" "i < 0" by auto
define j where "j = nat (-i) - 1"
define z where "z = x - 2 * y"
define u where "u = 2 * ?oi (nat j) * y"
have u: "u ≥ 0" unfolding u_def using y by auto
have i: "i = - int (Suc j)"
using ‹i < 0› unfolding j_def by simp
have id: "x + 2 * ?oi i * y = z - u"
unfolding i z_def u_def by (simp add: field_simps)
have z: "z ≤ 0" "abs z ≥ x" using xy y pn(1)
unfolding z_def by auto
show ?thesis unfolding id using pn(1) z u by simp
next
case np
with i have "0 ≥ x" "i > 0" by auto
define j where "j = nat i - 1"
have i: "i = int (Suc j)"
using ‹i > 0› unfolding j_def by simp
define u where "u = 2 * ?oi (nat j) * y"
have u: "u ≥ 0" unfolding u_def using y by auto
define z where "z = - x - 2 * y"
have id: "x + 2 * ?oi i * y = - z + u"
unfolding i z_def u_def by (simp add: field_simps)
have z: "z ≤ 0" "abs z ≥ - x" using xy y np(1)
unfolding z_def by auto
show ?thesis unfolding id using np(1) z u by simp
qed
qed simp
lemma abs_eq_add_2_mult: fixes y :: "'a :: linordered_idom"
assumes abs_id: "¦x¦ = ¦x + 2 * of_int i * y¦"
and xy: "- y < x" "x ≤ y"
and i: "i ≠ 0"
shows "x = y ∧ i = -1"
proof -
let ?oi = "of_int :: int ⇒ 'a"
from xy have y: "y > 0" by auto
consider (pp) "x ≥ 0" "i ≥ 0"
| (nn) "x < 0" "i ≤ 0"
| (pn) "x ≥ 0" "i ≤ 0"
| (np) "x < 0" "i ≥ 0"
by linarith
hence "?thesis ∨ x = ?oi (- i) * y"
proof cases
case pp
thus ?thesis using y abs_id xy i by simp
next
case nn
hence "¦x + 2 * ?oi i * y¦ =
- (x + 2 * ?oi i * y)"
using y nn
by (intro abs_of_nonpos add_nonpos_nonpos,
force, simp, intro mult_nonneg_nonpos, auto)
thus ?thesis using y abs_id xy i nn
by auto
next
case pn
with i have "0 ≤ x" "i < 0" by auto
define j where "j = nat (-i) - 1"
define z where "z = x - 2 * y"
define u where "u = 2 * ?oi (nat j) * y"
have u: "u ≥ 0" unfolding u_def using y by auto
have i: "i = - int (Suc j)"
using ‹i < 0› unfolding j_def by simp
have id: "x + 2 * ?oi i * y = z - u"
unfolding i z_def u_def by (simp add: field_simps)
have z: "z ≤ 0" "abs z ≥ x" using xy y pn(1)
unfolding z_def by auto
from abs_id[unfolded id] have "z - u = -x "
using z u pn by auto
from this[folded id] have "x = of_int (-i) * y"
by auto
thus ?thesis by auto
next
case np
with i have "0 ≥ x" "i > 0" by auto
define j where "j = nat i - 1"
have i: "i = int (Suc j)"
using ‹i > 0› unfolding j_def by simp
define u where "u = 2 * ?oi (nat j) * y"
have u: "u ≥ 0" unfolding u_def using y by auto
define z where "z = - x - 2 * y"
have id: "x + 2 * ?oi i * y = - z + u"
unfolding i z_def u_def by (simp add: field_simps)
have z: "z ≤ 0"
using xy y np(1) unfolding z_def by auto
from abs_id[unfolded id] have "- z + u = - x"
using u z np by auto
from this[folded id] have "x = of_int (- i) * y"
by auto
thus ?thesis by auto
qed
thus ?thesis
proof
assume "x = ?oi (- i) * y"
with xy i y
show ?thesis
by (smt (verit, ccfv_SIG) less_le minus_less_iff mult_le_cancel_right2 mult_minus1_right mult_minus_left mult_of_int_commute of_int_hom.hom_one of_int_le_1_iff of_int_minus)
qed
qed
text ‹This is the core lemma. It tells us that @{const croot} will choose the
principal root, i.e. the root with
largest real part and if there are two roots with identical real part, then the largest imaginary part.
This criterion will be crucial for implementing @{const croot}.›
lemma croot_principal: assumes n: "n ≠ 0"
and y: "y ^ n = x"
and neq: "y ≠ croot n x"
shows "Re y < Re (croot n x) ∨ Re y = Re (croot n x) ∧ Im y < Im (croot n x)"
proof (cases "x = 0")
case True
with neq y have False by auto
thus ?thesis ..
next
case x: False
let ?root = "croot n x"
from n have n1: "real n ≥ 1" "real n > 0" "real n ≠ 0" by auto
from x y n have y0: "y ≠ 0" by auto
from croot_power[OF n, of x] y
have id: "?root ^ n = y ^ n" by simp
hence "cmod (?root ^ n) = cmod (y ^ n)" by simp
hence norm_eq: "cmod ?root = cmod y" using n unfolding norm_power
by (meson gr_zeroI norm_ge_zero power_eq_imp_eq_base)
have "cis (Arg y * real n) = cis (Arg (y^n))" by (subst cis_Arg_power[OF y0], simp)
also have "… = cis (Arg x)" using y by simp
finally have ciseq: "cis (Arg y * real n) = cis (Arg x)" by simp
from cis_eq[OF ciseq] obtain i where
"Arg y * real n - Arg x = 2 * real_of_int i * pi"
by auto
hence "Arg y * real n = Arg x + 2 * real_of_int i * pi" by auto
from arg_cong[OF this, of "λ x. x / real n"] n1
have Argy: "Arg y = Arg ?root + 2 * real_of_int i * pi / real n"
by (auto simp: field_simps)
have i0: "i ≠ 0"
proof
assume "i = 0"
hence "Arg y = Arg ?root" unfolding Argy by simp
with norm_eq have "?root = y" by (metis rcis_cmod_Arg)
with neq show False by simp
qed
from y0 have cy0: "cmod y > 0" by auto
from Arg_bounded[of x] have abs_pi: "abs (Arg x) ≤ pi" by auto
have "Re y ≤ Re ?root ⟷ Re y / cmod y ≤ Re ?root / cmod y"
using cy0 unfolding divide_le_cancel by simp
also have cosy: "Re y / cmod y = cos (Arg y)" unfolding cos_arg[OF y0] ..
also have cosrt: "Re ?root / cmod y = cos (Arg ?root)"
unfolding norm_eq[symmetric] by (subst cos_arg, insert norm_eq cy0, auto)
also have "cos (Arg y) ≤ cos (Arg ?root) ⟷ abs (Arg ?root) ≤ abs (Arg y)"
by (rule cos_mono_le, insert Arg_bounded[of y] Arg_bounded[of ?root], auto)
also have "… ⟷ abs (Arg ?root) * real n ≤ abs (Arg y) * real n"
unfolding mult_le_cancel_right using n1 by simp
also have "… ⟷ abs (Arg x) ≤ ¦Arg x + 2 * real_of_int i * pi¦"
unfolding Argy using n1 by (simp add: field_simps)
also have "…" using abs_pi
by (rule abs_add_2_mult_bound)
finally have le: "Re y ≤ Re (croot n x)" .
show ?thesis
proof (cases "Re y = Re (croot n x)")
case False
with le show ?thesis by auto
next
case True
hence "Re y / cmod y = Re ?root / cmod y" by simp
hence "cos (Arg y) = cos (Arg ?root)" unfolding cosy cosrt .
hence "cos (abs (Arg y)) = cos (abs (Arg ?root))" unfolding cos_abs .
from cos_inj_pi[OF _ _ _ _ this]
have "abs (Arg y) = abs (Arg ?root)"
using Arg_bounded[of y] Arg_bounded[of ?root] by auto
hence "abs (Arg y) * real n = abs (Arg ?root) * real n" by simp
hence "abs (Arg x) = ¦Arg x + 2 * real_of_int i * pi¦" unfolding Argy
using n1 by (simp add: field_simps)
from abs_eq_add_2_mult[OF this _ _ ‹i ≠ 0›] Arg_bounded[of x]
have Argx: "Arg x = pi" and i: "i = -1" by auto
have Argy: "Arg y = -pi / real n"
unfolding Argy Arg_croot i Argx by simp
have "Im ?root > Im y ⟷ Im ?root / cmod ?root > Im y / cmod y"
unfolding norm_eq using cy0
by (meson divide_less_cancel divide_strict_right_mono)
also have "… ⟷ sin (Arg ?root) > sin (Arg y)"
by (subst (1 2) sin_arg, insert y0 norm_eq, auto)
also have "… ⟷ sin (- pi / real n) < sin (pi / real n)"
unfolding Argy Arg_croot Argx by simp
also have …
proof -
have "sin (- pi / real n) < 0"
using n1 by (smt (verit) Arg_bounded Argy divide_neg_pos sin_gt_zero sin_minus)
also have "… < sin (pi / real n)"
using n1 calculation by fastforce
finally show ?thesis .
qed
finally show ?thesis using le by auto
qed
qed
lemma croot_unique: assumes n: "n ≠ 0"
and y: "y ^ n = x"
and y_max_Re_Im: "⋀ z. z ^ n = x ⟹
Re z < Re y ∨ Re z = Re y ∧ Im z ≤ Im y"
shows "croot n x = y"
proof (rule ccontr)
assume "croot n x ≠ y"
from croot_principal[OF n y this[symmetric]]
have "Re y < Re (croot n x) ∨
Re y = Re (croot n x) ∧ Im y < Im (croot n x)" .
with y_max_Re_Im[OF croot_power[OF n]]
show False by auto
qed
lemma csqrt_is_croot_2: "csqrt = croot 2"
proof
fix x
show "csqrt x = croot 2 x"
proof (rule sym, rule croot_unique, force, force)
let ?p = "[:-x,0,1:]"
let ?cx = "csqrt x"
have p: "?p = [:?cx,1:] * [:-?cx,1:]"
by (simp add: power2_eq_square[symmetric])
fix y
assume "y^2 = x"
hence "True ⟷ poly ?p y = 0"
by (auto simp: power2_eq_square)
also have "… ⟷ y = - ?cx ∨ y = ?cx"
unfolding p poly_mult mult_eq_0_iff poly_root_factor by auto
finally have "y = - ?cx ∨ y = ?cx" by simp
thus "Re y < Re ?cx ∨ Re y = Re ?cx ∧ Im y ≤ Im ?cx"
proof
assume y: "y = - ?cx"
show ?thesis
proof (cases "Re ?cx = 0")
case False
with csqrt_principal[of x] have "Re ?cx > 0" by simp
thus ?thesis unfolding y by simp
next
case True
with csqrt_principal[of x] have "Im ?cx ≥ 0" by simp
thus ?thesis unfolding y using True by auto
qed
qed auto
qed
qed
lemma croot_via_root_selection: assumes roots: "set ys = { y. y^n = x}"
and n: "n ≠ 0"
shows "croot n x = arg_min_list (λ y. (- Re y, - Im y)) ys"
(is "_ = arg_min_list ?f ys")
proof (rule croot_unique[OF n])
let ?y = "arg_min_list ?f ys"
have rt: "croot n x ^ n = x" using n by (rule croot_power)
hence "croot n x ∈ set ys" unfolding roots by auto
hence ys: "ys ≠ []" by auto
from arg_min_list_in[OF this] have "?y ∈ set ys" by auto
from this[unfolded roots]
show "?y^n = x" by auto
fix z
assume "z^n = x"
hence z: "z ∈ set ys" unfolding roots by auto
from f_arg_min_list_f[OF ys, of ?f] z
have "?f ?y ≤ ?f z" by simp
thus "Re z < Re ?y ∨ Re z = Re ?y ∧ Im z ≤ Im ?y" by auto
qed
lemma croot_impl[code]: "croot n x = (if n = 0 then 0 else
arg_min_list (λ y. (- Re y, - Im y)) (all_croots n x))"
proof (cases "n = 0")
case n0: False
hence id: "(if n = 0 then y else z) = z"
for y z u :: complex by auto
show ?thesis unfolding id Let_def
by (rule croot_via_root_selection[OF _ n0], rule all_croots[OF n0])
qed auto
end