In this section, we explain how to exploit the icosahedral symmetry discussed before to solve an arbitrary polynomial of order five (also known as a quintic polynomial).
We begin by returning to a result derived earlier: in the Section on icosahedral symmetry, we showed that the the icosahedral face centres can be grouped into five octohedra:
where
# Check of the above:
from utils import e_simp, reduce_multiply
from sympy import symbols, expand, simplify, discriminant
u, v, e = symbols("u v e")
z_n = e**2 + e**3
z_m = e + e**4
def tI(u, v):
return (
(u**2 + v**2)
* (u**2 - 2 * z_n * u * v - v**2)
* (u**2 - 2 * z_m * u * v - v**2)
)
def T(u, v):
return (
u**30
+ 522 * (u**25 * v**5 - u**5 * v**25)
- 10005 * (u**20 * v**10 + u**10 * v**20)
+ v**30
)
T_eval = e_simp(
expand(reduce_multiply([e ** (15 - 3 * k) * tI(u, v * e**k) for k in range(5)])), e
)
T_eval.equals(T(u, v))TrueFollowing the work on octahedral invariants, it’s usef
What we intend to do next is to demonstrate the existence of a map from points on the complex plane (away from the edge midpoints) to a particular set of roots of a Brioschi quintic, whose Brioschi parameter can also be computed from .
To do this, we begin by considering the polynomial given by
Expanding, we find that
where , as in the previous Section.
# Check of the above:
X = symbols("X")
result = e_simp(
reduce_multiply([X - e ** (15 - 3 * k) * tI(u, v * e**k) for k in range(5)]), e
).collect(X)
result# Check the whole polynomial
def f(u, v):
return u * v * (u**10 + 11 * u**5 * v**5 - v**10)
result.equals(X**5 - 10 * f(u, v) * X**3 + 45 * f(u, v) ** 2 * X - T(u, v))TrueTherefore, if we define the homogeneous coordinates
(which are degree-zero complex functions in and , defined everywhere except for at the edge midpoints) then a second polynomial can be readily constructed as follows:
The polynomial is indeed a Brioschi quintic with Brioschi parameter where
such that
For completeness, we recall the syzygy derived earlier which relates the three icosahedral polynomial invariants , , and :
where
def H(u, v):
return u**20 - 228 * (u**15 * v**5 - u**5 * v**15) + 494 * u**10 * v**10 + v**20
(T(u, v) ** 2 - H(u, v) ** 3).equals(12**3 * f(u, v) ** 5)TrueWe also define a second degree-zero ratio of polynomial invariants, namely
for future use, which conveniently maps the icosahedral vertices, edge midpoints, and face centres to infinity, one, and zero, respectively.
Tschirnhaus Transformation and Principal Quintics¶
In this section, we aim to demonstrate that solving a single quadratic equation allows us to trade any general quintic of the form
for a principal quintic with has the and coefficients equal to zero.
To this end, let’s define a transformation
and show how to solve for such that the polynomial equation with coefficients with .
The polynomial after this transformation is given by the Resolvent, :
# Check of the above:
from sympy import resultant, solve, Eq, Poly, Add
x, p1, p2, p3, p4, p5 = symbols("x p1 p2 p3 p4 p5")
expr = x**5 + p1 * x**4 + p2 * x**3 + p3 * x**2 + p4 * x + p5
Eq(expr, 0)y, A, B = symbols("y A B")
expr2 = Poly(resultant(expr, y - x**2 - A * x - B, x), y).terms()
expr2 = Add(*[b * y ** a[0] for a, b in expr2], evaluate=False)
expr2Solving for the coefficients and , we find
# Check of the above:
y4_coeff = expr2.coeff(y**4)
A_val = solve(y4_coeff, A)[0]
Eq(A, A_val)exprA2 = expr2.coeff(y**4).subs(p1, 0)
exprA2exprB2 = expr2.coeff(y**3).subs(p1, 0).subs(p3, 0).subs(B, 2 * p2 / 5)
exprB2A_vals = solve(exprB2, A)
A_vals[1]exprB = (expr2.coeff(y**3).subs(A, A_val) * p1**2).expand().collect(B)
exprBd = discriminant(exprB, B)
dq4 = symbols("q4")
d.subs(p1, 1).subs(p2, 1).subs(p3, 1).subs(p4, 1 - q4 / 120)B_vals = solve(expr2.coeff(y**3).subs(A, A_val), B)
B_vals[0]p1_part = 3
p2_part = 6
p3_part = 7
p4_part = -51
p5_part = -54B_part = (
B_vals[0]
.subs(p1, p1_part)
.subs(p2, p2_part)
.subs(p3, p3_part)
.subs(p4, p4_part)
.subs(p5, p5_part)
)
B_partA_part = A_val.subs(B, B_part).subs(p1, p1_part).subs(p2, p2_part)
A_partexpr2_part = (
expr2.subs(p1, p1_part)
.subs(p2, p2_part)
.subs(p3, p3_part)
.subs(p4, p4_part)
.subs(p5, p5_part)
.subs(A, A_part)
.subs(B, B_part)
.expand()
.collect(y)
)
expr2_partexpr2_part = (
expr2.subs(p1, p1_part)
.subs(p2, p2_part)
.subs(p3, p3_part)
.subs(p4, p4_part)
.subs(p5, p5_part)
.subs(A, 4)
.subs(B, -8)
.expand()
.collect(y)
)
expr2_partalpha_part = expr2_part.coeff(y, 2) / 5
alpha_partbeta_part = expr2_part.coeff(y, 1) / 5
beta_partgamma_part = expr2_part.coeff(y, 0)
gamma_partB_vals[1].subs(p1, 0).subs(p3, 0) # .subs(p4, 1 - q4*15/2)A_val.subs(B, 2 * p2 / 5)B_vals[1].subs(p1, 1).subs(p2, 1).subs(p3, 1).subs(p4, 1 - q4 * 15 / 2)expr3 = (
expr2.subs(p1, 0)
.subs(p3, 0)
.subs(B, 2 * p2 / 5)
# .subs(A**5, (3 * p2 / 5 - 2 * p4 / p2) ** 2 * A)
# .subs(A**3, (3 * p2 / 5 - 2 * p4 / p2) * A)
# .subs(A**2, 3 * p2 / 5 - 2 * p4 / p2)
.expand()
.collect(y)
)
expr3from sympy import Rational
alpha = expr3.coeff(y**2) * Rational(1, 5)
beta = expr3.coeff(y) * Rational(1, 5)
gamma = expr3.coeff(y, 0)(alpha**4 - beta**3 + alpha * beta * gamma).expand()Therefore, at the expense of a single square root, we can trade any general quintic for a principal quintic of the form
for some , , computable in terms of the original . (The factors of 5 are included for later convenience.)
From Brioschi and Principal Quintics to the Icosahedral Equation¶
Returning to the Brioschi quintic, we wish to show that every Brioschi quintic can be associated with a corresponding principal quintic by transforming the roots as follows:
where is the polynomial invariant for the faces of the -th inscribed octahedron:
(Recall that the roots of the original, Brioschi quintic were given by and so
in terms of the ’s.)
from utils import hessian, z_simp
e, r = symbols("e r")
t_0 = (
(u**2 + v**2)
* (u**2 - 2 * z_n * u * v - v**2)
* (u**2 - 2 * (-1 - z_n) * u * v - v**2)
)
t_r = simplify(z_simp((e ** (15 - 3 * r) * t_0.subs(v, v * e**r)).expand(), z_n))
W_r = simplify(hessian(t_r, u, v).expand() / (-400))Summing over , we find that both and vanish:
# Check of the above:
def sum_over_r(expr, r):
return e_simp(reduce_multiply([expr.subs(r, x) for x in range(5)]), e)
sigma, tau = symbols("sigma tau")
Y_r = sigma * W_r + tau * t_r * W_r
# sum_over_r(Y_r, r).equals(0) and sum_over_r(Y_r**2, r).equals(0)Expanding the polynomial , we find a principal quintic with
# Check of the above:
y = symbols("y")
P_y = e_simp(
reduce_multiply([y - e_simp(Y_r.subs(r, x), e) for x in range(5)]).expand(),
e,
)
# P_y.coeff(y**5), P_y.coeff(y**4), e_simp(P_y.coeff(y**3), e)P_y_y2 = e_simp(P_y.coeff(y**2), e) / 5
P_y_y2.collect(sigma)P_y_y2.equals(
-8 * f(u, v) ** 2 * sigma**3
- T(u, v) * sigma**2 * tau
- 72 * f(u, v) ** 3 * sigma * tau**2
- f(u, v) * T(u, v) * tau**3
)TrueP_y_y1 = e_simp(P_y.coeff(y), e) / 5
P_y_y1.collect(sigma)P_y_y1.equals(
H(u, v) * f(u, v) * sigma**4
- 18 * H(u, v) * f(u, v) ** 2 * sigma**2 * tau**2
- H(u, v) * T(u, v) * sigma * tau**3
- 27 * f(u, v) ** 3 * H(u, v) * tau**4
)TrueP_y_y0 = e_simp(P_y - y**5 - 5 * y**2 * P_y_y2 - 5 * y * P_y_y1, e)
P_y_y0.collect(sigma)P_y_y0.equals(
H(u, v) ** 2
* (
-(sigma**5)
+ 10 * sigma**3 * tau**2 * f(u, v)
- 45 * sigma * tau**4 * f(u, v) ** 2
- tau**5 * T(u, v)
)
)TrueSince we will want the coefficients to have weight zero in , (so that they depend only on the ratio ), we will find it convenient to instead use
since , are explicitly degree-zero in , . From here, we wish to show that, for every choice of defining a principal quintic, there’s a corresponding choice of reproducing the same polynomial. As it turns out, solving for , can be done using radicals (namely, a single square-root), whereas selecting the appropriate will require solving the Icosahedral Equation (to be defined shortly).
Switching to homogenous variables we find
where, as before, .
Combining these equations, we notice that they satisfy
$$
\lambda,\beta + \gamma = -B(z),\mu^2,\alpha ,.
No input for math node
# Check of the above:
K, l, mu, B, alpha, beta, gamma = symbols("K lambda mu B alpha beta gamma")
# where K = 12^3*I(z)
alpha_expr = 1 / K * (8 * l**3 + l**2 * mu - 72 * B * l * mu**2 - B * mu**3)
beta_expr = (
1 / K * (-(l**4) - 18 * B * l**2 * mu**2 - B * l * mu**3 + 27 * B**2 * mu**4)
)
gamma_expr = (
1 / K * (l**5 + 10 * B * l**3 * mu**2 + 45 * B**2 * l * mu**4 + B**2 * mu**5)
)
(l * beta + gamma).subs(beta, beta_expr).subs(gamma, gamma_expr).equals(
(-B * mu**2 * alpha).subs(alpha, alpha_expr)
)TrueSimilarly, we can the compute a second combination and find
# Check of the above:
(l * gamma + mu**2 * B * beta).subs(gamma, gamma_expr).subs(beta, beta_expr).factor()We can obtain the same combination using the and equations as follows:
# Check of the above:
(K * (27 * alpha**2 + (l * alpha + 8 * beta) ** 2 / B / mu**2)).subs(
alpha, alpha_expr
).subs(beta, beta_expr).subs(K, 1728 + 1 / B).factor()Equating these cubic terms, we find a quadratic expression in , from which we can eliminate using eq. mu^2 B and find
# Check of the above:
alpha, beta, gamma = symbols("alpha beta gamma")
l_eqn = (
simplify(
-(beta * l + gamma)
* alpha
* (
27 * alpha**2
+ (l * alpha + 8 * beta) ** 2 / mu**2 / B
- l * gamma
- mu**2 * B * beta
).subs(mu**2, -1 / B * (l * beta + gamma) / alpha)
)
.expand()
.collect(l)
)
l_eqnAt this point, we see that a second square root must be taken to determine in terms of the values in the principal polynomial. Interestingly, the discriminant of the quadratic above,
is proportional to the discriminant of the principal quintic:
# Check of the above:
l_disc = discriminant(l_eqn, l).factor()
l_discdiscriminant(y**5 + 5 * alpha * y**2 + 5 * beta * y + gamma, y).equals(
5**5 / alpha**2 * l_disc
)TrueDenoting the solutions to the quadratic equation for as , we can now write
$$ \mu^2B(z) = -\frac1\alpha\left(\beta,\lambda_\pm+\gamma\right) ,,
No input for math node
which allows us to solve for in terms of the coefficients of the principal quintic: $$
I(z) = I_0 := \frac{(\alpha\lambda_\pm^2-3,\beta\lambda_\pm-3\gamma)^3}{12^3\alpha^2(\alpha\gamma\lambda_\pm-\beta^2\lambda_\pm-\beta\gamma)} ,.
$$
(Note that has scaling dimension of zero in , since , , , and .)
# Check of the above:
mu2B_eqn = l * gamma + mu**2 * B * beta - (l**2 + 3 * B * mu**2) ** 3 / K
K_soln = solve(mu2B_eqn, K)[0].subs(mu**2 * B, -1 / alpha * (beta * l + gamma)).factor()
K_solnOnce is known, the syzygy ensures that (away from the edge midpoints, where ) which allows us to solve for as well (at the expense of one last square root in Eq. (29)):
once we have some satisfying . All in all, the roots of the principal quintic polynomial are given by
Inverting the map to determine (given some computed in terms of , , and ) is the topic to which we now turn our attention.
Solving the Icosahedral Equation¶
The last step is to show how the equation can be inverted to determine where . As we shall see, this can only be achieved up to a Mobiüs transformation, but that is sufficient, since once a single value for is determined, then all five roots are identified using
J, K, L = symbols("J K L")
l = symbols("lambda")
alpha, beta, gamma = symbols("alpha beta gamma")
eqn = J * l**2 + K * l + L
lp, lm = solve(eqn, l)(lp * lm).expand()(lp + lm).expand()l1 = -K * Rational(1, 2) / J
l1Jval = alpha**4 - beta**3 + alpha * beta * gamma
Kval = alpha * gamma**2 - 2 * beta**2 * gamma - 11 * alpha**3 * beta
Lval = 64 * alpha**2 * beta**2 - beta * gamma**2 - 27 * alpha**3 * gammadenom = (
12**3
* alpha**2
* (2 * J) ** 7
* (
(alpha * gamma - beta**2) ** 2 * L / J
- 2 * (alpha * gamma - beta**2) * l1 * beta * gamma
+ beta**2 * gamma**2
)
)
denom = denom.expand().factor()
denomdenom2 = denom.subs(J, Jval).subs(K, Kval).subs(L, Lval).factor()
denom2s2 = (K**2 - 4 * J * L).subs(J, Jval).subs(K, Kval).subs(L, Lval).factor()
s2s = symbols("s")
l2 = Rational(1, 2) / J
lpm = l1 + s * l2
num = (
(alpha * lpm**2 - 3 * beta * lpm - 3 * gamma) ** 3
* (
(alpha * gamma - beta**2) * l1
- beta * gamma
- s * l2 * (alpha * gamma - beta**2)
)
* (2 * J) ** 7
)
num.expand()num2 = num.expand().subs(J, Jval).subs(K, Kval).subs(L, Lval).expand().collect(s)
num2def s_simp(expr, s):
poly = Poly(expr, s)
coeffs = poly.all_coeffs()
while len(coeffs) > 2:
top_factor = coeffs[0]
coeffs[2] += (K**2 - 4 * J * L) * top_factor
coeffs.pop(0)
return Poly.from_list(coeffs, s).as_expr()
num3 = s_simp(num2, s).subs(J, Jval).subs(K, Kval).subs(L, Lval).expand().collect(s)
num3.factor()denom2.factor()num4 = (
num3.factor() / (3 * alpha * gamma - 4 * beta**2) ** 3 / Jval / (-64) / alpha**3
).collect(s)
num4num4.coeff(s, 0).factor()num4.coeff(s, 1).factor()denom3 = (
denom2.factor() / (3 * alpha * gamma - 4 * beta**2) ** 3 / Jval / (-64) / alpha**3
).factor()
denom3num3.coeff(s, 0).factor()num3.coeff(s, 1).factor()A, B = symbols("A B")
alpha_val = A * B**2 - 56 * B**3
beta_val = -5 * A * B**3 + 408 * B**4
gamma_val = 44 * A * B**4 - 4704 * B**5 - B**4
def A_simp(expr, A, B):
poly = Poly(expr, A)
coeffs = poly.all_coeffs()
while len(coeffs) > 2:
top_factor = coeffs[0]
coeffs[2] += (-3 * B) * top_factor
coeffs.pop(0)
return Poly.from_list(coeffs, A).as_expr()
A_simp(
num3.coeff(s, 0)
.subs(alpha, alpha_val)
.subs(beta, beta_val)
.subs(gamma, gamma_val)
.expand(),
A,
B,
).factor()from sympy import sqrt
num_eval_s0 = _.subs(A, sqrt(15)).subs(B, -5).evalf()
num_eval_s0A_simp(
num3.coeff(s, 1)
.subs(alpha, alpha_val)
.subs(beta, beta_val)
.subs(gamma, gamma_val)
.expand(),
A,
B,
).factor()num_eval_s1 = _.subs(A, sqrt(15)).subs(B, -5).evalf()
num_eval_s1s_eval = (
s2.subs(alpha, alpha_val)
.subs(beta, beta_val)
.subs(gamma, gamma_val)
.subs(A, sqrt(15))
.subs(B, -5)
.evalf()
)
s_evalnum_eval_s0 + sqrt(s_eval) * num_eval_s1A_simp(
s2.subs(alpha, alpha_val).subs(beta, beta_val).subs(gamma, gamma_val).expand(),
A,
B,
).factor()A_simp(
num3.coeff(s, 1)
.subs(alpha, alpha_val)
.subs(beta, beta_val)
.subs(gamma, gamma_val)
.expand(),
A,
B,
).coeff(A, 1).factor()A_simp(
(Jval**5)
.subs(alpha, alpha_val)
.subs(beta, beta_val)
.subs(gamma, gamma_val)
.expand(),
A,
B,
).factor()566155008 / 777672808.02381104350228864331230155414357818864626761245655040 / 5322833073958413608701850845831739074772830015979524.473377836848294denom4 = A_simp(
denom3.subs(alpha, alpha_val).subs(beta, beta_val).subs(gamma, gamma_val).expand(),
A,
B,
)
denom4.factor()_.subs(A, sqrt(15)).subs(B, -5).evalf()3136 / 31045.3333333333333d0 = denom4.coeff(A, 0)
d1 = denom4.coeff(A, 1)
(d0**2 - (-3 * B) * d1**2).factor()