test_galoisgroups.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """Tests for computing Galois groups. """
  2. from sympy.abc import x
  3. from sympy.combinatorics.galois import (
  4. S1TransitiveSubgroups, S2TransitiveSubgroups, S3TransitiveSubgroups,
  5. S4TransitiveSubgroups, S5TransitiveSubgroups, S6TransitiveSubgroups,
  6. )
  7. from sympy.polys.domains.rationalfield import QQ
  8. from sympy.polys.numberfields.galoisgroups import (
  9. tschirnhausen_transformation,
  10. galois_group,
  11. _galois_group_degree_4_root_approx,
  12. _galois_group_degree_5_hybrid,
  13. )
  14. from sympy.polys.numberfields.subfield import field_isomorphism
  15. from sympy.polys.polytools import Poly
  16. from sympy.testing.pytest import raises
  17. def test_tschirnhausen_transformation():
  18. for T in [
  19. Poly(x**2 - 2),
  20. Poly(x**2 + x + 1),
  21. Poly(x**4 + 1),
  22. Poly(x**4 - x**3 + x**2 - x + 1),
  23. ]:
  24. _, U = tschirnhausen_transformation(T)
  25. assert U.degree() == T.degree()
  26. assert U.is_monic
  27. assert U.is_irreducible
  28. K = QQ.alg_field_from_poly(T)
  29. L = QQ.alg_field_from_poly(U)
  30. assert field_isomorphism(K.ext, L.ext) is not None
  31. # Test polys are from:
  32. # Cohen, H. *A Course in Computational Algebraic Number Theory*.
  33. test_polys_by_deg = {
  34. # Degree 1
  35. 1: [
  36. (x, S1TransitiveSubgroups.S1, True)
  37. ],
  38. # Degree 2
  39. 2: [
  40. (x**2 + x + 1, S2TransitiveSubgroups.S2, False)
  41. ],
  42. # Degree 3
  43. 3: [
  44. (x**3 + x**2 - 2*x - 1, S3TransitiveSubgroups.A3, True),
  45. (x**3 + 2, S3TransitiveSubgroups.S3, False),
  46. ],
  47. # Degree 4
  48. 4: [
  49. (x**4 + x**3 + x**2 + x + 1, S4TransitiveSubgroups.C4, False),
  50. (x**4 + 1, S4TransitiveSubgroups.V, True),
  51. (x**4 - 2, S4TransitiveSubgroups.D4, False),
  52. (x**4 + 8*x + 12, S4TransitiveSubgroups.A4, True),
  53. (x**4 + x + 1, S4TransitiveSubgroups.S4, False),
  54. ],
  55. # Degree 5
  56. 5: [
  57. (x**5 + x**4 - 4*x**3 - 3*x**2 + 3*x + 1, S5TransitiveSubgroups.C5, True),
  58. (x**5 - 5*x + 12, S5TransitiveSubgroups.D5, True),
  59. (x**5 + 2, S5TransitiveSubgroups.M20, False),
  60. (x**5 + 20*x + 16, S5TransitiveSubgroups.A5, True),
  61. (x**5 - x + 1, S5TransitiveSubgroups.S5, False),
  62. ],
  63. # Degree 6
  64. 6: [
  65. (x**6 + x**5 + x**4 + x**3 + x**2 + x + 1, S6TransitiveSubgroups.C6, False),
  66. (x**6 + 108, S6TransitiveSubgroups.S3, False),
  67. (x**6 + 2, S6TransitiveSubgroups.D6, False),
  68. (x**6 - 3*x**2 - 1, S6TransitiveSubgroups.A4, True),
  69. (x**6 + 3*x**3 + 3, S6TransitiveSubgroups.G18, False),
  70. (x**6 - 3*x**2 + 1, S6TransitiveSubgroups.A4xC2, False),
  71. (x**6 - 4*x**2 - 1, S6TransitiveSubgroups.S4p, True),
  72. (x**6 - 3*x**5 + 6*x**4 - 7*x**3 + 2*x**2 + x - 4, S6TransitiveSubgroups.S4m, False),
  73. (x**6 + 2*x**3 - 2, S6TransitiveSubgroups.G36m, False),
  74. (x**6 + 2*x**2 + 2, S6TransitiveSubgroups.S4xC2, False),
  75. (x**6 + 10*x**5 + 55*x**4 + 140*x**3 + 175*x**2 + 170*x + 25, S6TransitiveSubgroups.PSL2F5, True),
  76. (x**6 + 10*x**5 + 55*x**4 + 140*x**3 + 175*x**2 - 3019*x + 25, S6TransitiveSubgroups.PGL2F5, False),
  77. (x**6 + 6*x**4 + 2*x**3 + 9*x**2 + 6*x - 4, S6TransitiveSubgroups.G36p, True),
  78. (x**6 + 2*x**4 + 2*x**3 + x**2 + 2*x + 2, S6TransitiveSubgroups.G72, False),
  79. (x**6 + 24*x - 20, S6TransitiveSubgroups.A6, True),
  80. (x**6 + x + 1, S6TransitiveSubgroups.S6, False),
  81. ],
  82. }
  83. def test_galois_group():
  84. """
  85. Try all the test polys.
  86. """
  87. for deg in range(1, 7):
  88. polys = test_polys_by_deg[deg]
  89. for T, G, alt in polys:
  90. assert galois_group(T, by_name=True) == (G, alt)
  91. def test_galois_group_degree_out_of_bounds():
  92. raises(ValueError, lambda: galois_group(Poly(0, x)))
  93. raises(ValueError, lambda: galois_group(Poly(1, x)))
  94. raises(ValueError, lambda: galois_group(Poly(x ** 7 + 1)))
  95. def test_galois_group_not_by_name():
  96. """
  97. Check at least one polynomial of each supported degree, to see that
  98. conversion from name to group works.
  99. """
  100. for deg in range(1, 7):
  101. T, G_name, _ = test_polys_by_deg[deg][0]
  102. G, _ = galois_group(T)
  103. assert G == G_name.get_perm_group()
  104. def test_galois_group_not_monic_over_ZZ():
  105. """
  106. Check that we can work with polys that are not monic over ZZ.
  107. """
  108. for deg in range(1, 7):
  109. T, G, alt = test_polys_by_deg[deg][0]
  110. assert galois_group(T/2, by_name=True) == (G, alt)
  111. def test__galois_group_degree_4_root_approx():
  112. for T, G, alt in test_polys_by_deg[4]:
  113. assert _galois_group_degree_4_root_approx(Poly(T)) == (G, alt)
  114. def test__galois_group_degree_5_hybrid():
  115. for T, G, alt in test_polys_by_deg[5]:
  116. assert _galois_group_degree_5_hybrid(Poly(T)) == (G, alt)
  117. def test_AlgebraicField_galois_group():
  118. k = QQ.alg_field_from_poly(Poly(x**4 + 1))
  119. G, _ = k.galois_group(by_name=True)
  120. assert G == S4TransitiveSubgroups.V
  121. k = QQ.alg_field_from_poly(Poly(x**4 - 2))
  122. G, _ = k.galois_group(by_name=True)
  123. assert G == S4TransitiveSubgroups.D4