passagemath-singular 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-singular might be problematic. Click here for more details.

Files changed (491) hide show
  1. PySingular.cpython-314-x86_64-linux-gnu.so +0 -0
  2. passagemath_singular-10.6.31rc3.dist-info/METADATA +183 -0
  3. passagemath_singular-10.6.31rc3.dist-info/RECORD +491 -0
  4. passagemath_singular-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_singular-10.6.31rc3.dist-info/top_level.txt +3 -0
  6. passagemath_singular.libs/libSingular-4-20aec911.4.1.so +0 -0
  7. passagemath_singular.libs/libcddgmp-21acf0c6.so.0.1.3 +0 -0
  8. passagemath_singular.libs/libfactory-4-fcee31da.4.1.so +0 -0
  9. passagemath_singular.libs/libflint-66e12231.so.21.0.0 +0 -0
  10. passagemath_singular.libs/libgf2x-a4cdec90.so.3.0.0 +0 -0
  11. passagemath_singular.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
  12. passagemath_singular.libs/libgmp-6e109695.so.10.5.0 +0 -0
  13. passagemath_singular.libs/libgsl-cda90e79.so.28.0.0 +0 -0
  14. passagemath_singular.libs/libmpfr-82690d50.so.6.2.1 +0 -0
  15. passagemath_singular.libs/libntl-e6f0d543.so.44.0.1 +0 -0
  16. passagemath_singular.libs/libomalloc-0-5c9e866e.9.6.so +0 -0
  17. passagemath_singular.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  18. passagemath_singular.libs/libpolys-4-5c0a87e0.4.1.so +0 -0
  19. passagemath_singular.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  20. passagemath_singular.libs/libreadline-ea270e21.so.8.2 +0 -0
  21. passagemath_singular.libs/libsingular_resources-4-a1aafc6d.4.1.so +0 -0
  22. passagemath_singular.libs/libtinfo-ceb117d9.so.6.3 +0 -0
  23. sage/algebras/all__sagemath_singular.py +3 -0
  24. sage/algebras/fusion_rings/all.py +19 -0
  25. sage/algebras/fusion_rings/f_matrix.py +2448 -0
  26. sage/algebras/fusion_rings/fast_parallel_fmats_methods.cpython-314-x86_64-linux-gnu.so +0 -0
  27. sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd +5 -0
  28. sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +538 -0
  29. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.cpython-314-x86_64-linux-gnu.so +0 -0
  30. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd +3 -0
  31. sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +331 -0
  32. sage/algebras/fusion_rings/fusion_double.py +899 -0
  33. sage/algebras/fusion_rings/fusion_ring.py +1580 -0
  34. sage/algebras/fusion_rings/poly_tup_engine.cpython-314-x86_64-linux-gnu.so +0 -0
  35. sage/algebras/fusion_rings/poly_tup_engine.pxd +24 -0
  36. sage/algebras/fusion_rings/poly_tup_engine.pyx +579 -0
  37. sage/algebras/fusion_rings/shm_managers.cpython-314-x86_64-linux-gnu.so +0 -0
  38. sage/algebras/fusion_rings/shm_managers.pxd +24 -0
  39. sage/algebras/fusion_rings/shm_managers.pyx +780 -0
  40. sage/algebras/letterplace/all.py +1 -0
  41. sage/algebras/letterplace/free_algebra_element_letterplace.cpython-314-x86_64-linux-gnu.so +0 -0
  42. sage/algebras/letterplace/free_algebra_element_letterplace.pxd +18 -0
  43. sage/algebras/letterplace/free_algebra_element_letterplace.pyx +755 -0
  44. sage/algebras/letterplace/free_algebra_letterplace.cpython-314-x86_64-linux-gnu.so +0 -0
  45. sage/algebras/letterplace/free_algebra_letterplace.pxd +35 -0
  46. sage/algebras/letterplace/free_algebra_letterplace.pyx +914 -0
  47. sage/algebras/letterplace/letterplace_ideal.cpython-314-x86_64-linux-gnu.so +0 -0
  48. sage/algebras/letterplace/letterplace_ideal.pyx +408 -0
  49. sage/algebras/quatalg/all.py +2 -0
  50. sage/algebras/quatalg/quaternion_algebra.py +4778 -0
  51. sage/algebras/quatalg/quaternion_algebra_cython.cpython-314-x86_64-linux-gnu.so +0 -0
  52. sage/algebras/quatalg/quaternion_algebra_cython.pyx +261 -0
  53. sage/algebras/quatalg/quaternion_algebra_element.cpython-314-x86_64-linux-gnu.so +0 -0
  54. sage/algebras/quatalg/quaternion_algebra_element.pxd +29 -0
  55. sage/algebras/quatalg/quaternion_algebra_element.pyx +2176 -0
  56. sage/all__sagemath_singular.py +11 -0
  57. sage/ext_data/all__sagemath_singular.py +1 -0
  58. sage/ext_data/singular/function_field/core.lib +98 -0
  59. sage/interfaces/all__sagemath_singular.py +1 -0
  60. sage/interfaces/singular.py +2835 -0
  61. sage/libs/all__sagemath_singular.py +1 -0
  62. sage/libs/singular/__init__.py +1 -0
  63. sage/libs/singular/decl.pxd +1168 -0
  64. sage/libs/singular/function.cpython-314-x86_64-linux-gnu.so +0 -0
  65. sage/libs/singular/function.pxd +87 -0
  66. sage/libs/singular/function.pyx +1901 -0
  67. sage/libs/singular/function_factory.py +61 -0
  68. sage/libs/singular/groebner_strategy.cpython-314-x86_64-linux-gnu.so +0 -0
  69. sage/libs/singular/groebner_strategy.pxd +22 -0
  70. sage/libs/singular/groebner_strategy.pyx +582 -0
  71. sage/libs/singular/option.cpython-314-x86_64-linux-gnu.so +0 -0
  72. sage/libs/singular/option.pyx +671 -0
  73. sage/libs/singular/polynomial.cpython-314-x86_64-linux-gnu.so +0 -0
  74. sage/libs/singular/polynomial.pxd +39 -0
  75. sage/libs/singular/polynomial.pyx +661 -0
  76. sage/libs/singular/ring.cpython-314-x86_64-linux-gnu.so +0 -0
  77. sage/libs/singular/ring.pxd +58 -0
  78. sage/libs/singular/ring.pyx +893 -0
  79. sage/libs/singular/singular.cpython-314-x86_64-linux-gnu.so +0 -0
  80. sage/libs/singular/singular.pxd +72 -0
  81. sage/libs/singular/singular.pyx +1944 -0
  82. sage/libs/singular/standard_options.py +145 -0
  83. sage/matrix/all__sagemath_singular.py +1 -0
  84. sage/matrix/matrix_mpolynomial_dense.cpython-314-x86_64-linux-gnu.so +0 -0
  85. sage/matrix/matrix_mpolynomial_dense.pxd +7 -0
  86. sage/matrix/matrix_mpolynomial_dense.pyx +615 -0
  87. sage/rings/all__sagemath_singular.py +1 -0
  88. sage/rings/function_field/all__sagemath_singular.py +1 -0
  89. sage/rings/function_field/derivations_polymod.py +911 -0
  90. sage/rings/function_field/element_polymod.cpython-314-x86_64-linux-gnu.so +0 -0
  91. sage/rings/function_field/element_polymod.pyx +406 -0
  92. sage/rings/function_field/function_field_polymod.py +2611 -0
  93. sage/rings/function_field/ideal_polymod.py +1775 -0
  94. sage/rings/function_field/order_polymod.py +1475 -0
  95. sage/rings/function_field/place_polymod.py +681 -0
  96. sage/rings/polynomial/all__sagemath_singular.py +1 -0
  97. sage/rings/polynomial/multi_polynomial_ideal_libsingular.cpython-314-x86_64-linux-gnu.so +0 -0
  98. sage/rings/polynomial/multi_polynomial_ideal_libsingular.pxd +5 -0
  99. sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +339 -0
  100. sage/rings/polynomial/multi_polynomial_libsingular.cpython-314-x86_64-linux-gnu.so +0 -0
  101. sage/rings/polynomial/multi_polynomial_libsingular.pxd +30 -0
  102. sage/rings/polynomial/multi_polynomial_libsingular.pyx +6277 -0
  103. sage/rings/polynomial/plural.cpython-314-x86_64-linux-gnu.so +0 -0
  104. sage/rings/polynomial/plural.pxd +48 -0
  105. sage/rings/polynomial/plural.pyx +3171 -0
  106. sage/symbolic/all__sagemath_singular.py +1 -0
  107. sage/symbolic/comparison_impl.pxi +428 -0
  108. sage/symbolic/constants_c_impl.pxi +178 -0
  109. sage/symbolic/expression.cpython-314-x86_64-linux-gnu.so +0 -0
  110. sage/symbolic/expression.pxd +7 -0
  111. sage/symbolic/expression.pyx +14200 -0
  112. sage/symbolic/getitem_impl.pxi +202 -0
  113. sage/symbolic/pynac.pxi +572 -0
  114. sage/symbolic/pynac_constant_impl.pxi +133 -0
  115. sage/symbolic/pynac_function_impl.pxi +206 -0
  116. sage/symbolic/pynac_impl.pxi +2576 -0
  117. sage/symbolic/pynac_wrap.h +124 -0
  118. sage/symbolic/series_impl.pxi +272 -0
  119. sage/symbolic/substitution_map_impl.pxi +94 -0
  120. sage_wheels/bin/ESingular +0 -0
  121. sage_wheels/bin/Singular +0 -0
  122. sage_wheels/bin/TSingular +0 -0
  123. sage_wheels/lib/singular/MOD/cohomo.la +41 -0
  124. sage_wheels/lib/singular/MOD/cohomo.so +0 -0
  125. sage_wheels/lib/singular/MOD/customstd.la +41 -0
  126. sage_wheels/lib/singular/MOD/customstd.so +0 -0
  127. sage_wheels/lib/singular/MOD/freealgebra.la +41 -0
  128. sage_wheels/lib/singular/MOD/freealgebra.so +0 -0
  129. sage_wheels/lib/singular/MOD/gfanlib.la +41 -0
  130. sage_wheels/lib/singular/MOD/gfanlib.so +0 -0
  131. sage_wheels/lib/singular/MOD/gitfan.la +41 -0
  132. sage_wheels/lib/singular/MOD/gitfan.so +0 -0
  133. sage_wheels/lib/singular/MOD/interval.la +41 -0
  134. sage_wheels/lib/singular/MOD/interval.so +0 -0
  135. sage_wheels/lib/singular/MOD/loctriv.la +41 -0
  136. sage_wheels/lib/singular/MOD/loctriv.so +0 -0
  137. sage_wheels/lib/singular/MOD/machinelearning.la +41 -0
  138. sage_wheels/lib/singular/MOD/machinelearning.so +0 -0
  139. sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.la +41 -0
  140. sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.so +0 -0
  141. sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.la +41 -0
  142. sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.so +0 -0
  143. sage_wheels/lib/singular/MOD/p_Procs_FieldQ.la +41 -0
  144. sage_wheels/lib/singular/MOD/p_Procs_FieldQ.so +0 -0
  145. sage_wheels/lib/singular/MOD/p_Procs_FieldZp.la +41 -0
  146. sage_wheels/lib/singular/MOD/p_Procs_FieldZp.so +0 -0
  147. sage_wheels/lib/singular/MOD/partialgb.la +41 -0
  148. sage_wheels/lib/singular/MOD/partialgb.so +0 -0
  149. sage_wheels/lib/singular/MOD/pyobject.la +41 -0
  150. sage_wheels/lib/singular/MOD/pyobject.so +0 -0
  151. sage_wheels/lib/singular/MOD/singmathic.la +41 -0
  152. sage_wheels/lib/singular/MOD/singmathic.so +0 -0
  153. sage_wheels/lib/singular/MOD/sispasm.la +41 -0
  154. sage_wheels/lib/singular/MOD/sispasm.so +0 -0
  155. sage_wheels/lib/singular/MOD/subsets.la +41 -0
  156. sage_wheels/lib/singular/MOD/subsets.so +0 -0
  157. sage_wheels/lib/singular/MOD/systhreads.la +41 -0
  158. sage_wheels/lib/singular/MOD/systhreads.so +0 -0
  159. sage_wheels/lib/singular/MOD/syzextra.la +41 -0
  160. sage_wheels/lib/singular/MOD/syzextra.so +0 -0
  161. sage_wheels/libexec/singular/MOD/change_cost +0 -0
  162. sage_wheels/libexec/singular/MOD/singularsurf +11 -0
  163. sage_wheels/libexec/singular/MOD/singularsurf_jupyter +9 -0
  164. sage_wheels/libexec/singular/MOD/singularsurf_win +10 -0
  165. sage_wheels/libexec/singular/MOD/solve_IP +0 -0
  166. sage_wheels/libexec/singular/MOD/surfex +16 -0
  167. sage_wheels/libexec/singular/MOD/toric_ideal +0 -0
  168. sage_wheels/share/factory/gftables/10201 +342 -0
  169. sage_wheels/share/factory/gftables/1024 +37 -0
  170. sage_wheels/share/factory/gftables/10609 +356 -0
  171. sage_wheels/share/factory/gftables/11449 +384 -0
  172. sage_wheels/share/factory/gftables/11881 +398 -0
  173. sage_wheels/share/factory/gftables/121 +6 -0
  174. sage_wheels/share/factory/gftables/12167 +408 -0
  175. sage_wheels/share/factory/gftables/125 +7 -0
  176. sage_wheels/share/factory/gftables/12769 +428 -0
  177. sage_wheels/share/factory/gftables/128 +7 -0
  178. sage_wheels/share/factory/gftables/1331 +47 -0
  179. sage_wheels/share/factory/gftables/1369 +48 -0
  180. sage_wheels/share/factory/gftables/14641 +490 -0
  181. sage_wheels/share/factory/gftables/15625 +523 -0
  182. sage_wheels/share/factory/gftables/16 +3 -0
  183. sage_wheels/share/factory/gftables/16129 +540 -0
  184. sage_wheels/share/factory/gftables/16384 +549 -0
  185. sage_wheels/share/factory/gftables/16807 +563 -0
  186. sage_wheels/share/factory/gftables/1681 +58 -0
  187. sage_wheels/share/factory/gftables/169 +8 -0
  188. sage_wheels/share/factory/gftables/17161 +574 -0
  189. sage_wheels/share/factory/gftables/1849 +64 -0
  190. sage_wheels/share/factory/gftables/18769 +628 -0
  191. sage_wheels/share/factory/gftables/19321 +646 -0
  192. sage_wheels/share/factory/gftables/19683 +659 -0
  193. sage_wheels/share/factory/gftables/2048 +71 -0
  194. sage_wheels/share/factory/gftables/2187 +75 -0
  195. sage_wheels/share/factory/gftables/2197 +76 -0
  196. sage_wheels/share/factory/gftables/2209 +76 -0
  197. sage_wheels/share/factory/gftables/22201 +742 -0
  198. sage_wheels/share/factory/gftables/22801 +762 -0
  199. sage_wheels/share/factory/gftables/2401 +82 -0
  200. sage_wheels/share/factory/gftables/243 +11 -0
  201. sage_wheels/share/factory/gftables/24389 +815 -0
  202. sage_wheels/share/factory/gftables/24649 +824 -0
  203. sage_wheels/share/factory/gftables/25 +3 -0
  204. sage_wheels/share/factory/gftables/256 +11 -0
  205. sage_wheels/share/factory/gftables/26569 +888 -0
  206. sage_wheels/share/factory/gftables/27 +3 -0
  207. sage_wheels/share/factory/gftables/27889 +932 -0
  208. sage_wheels/share/factory/gftables/2809 +96 -0
  209. sage_wheels/share/factory/gftables/28561 +954 -0
  210. sage_wheels/share/factory/gftables/289 +12 -0
  211. sage_wheels/share/factory/gftables/29791 +995 -0
  212. sage_wheels/share/factory/gftables/29929 +1000 -0
  213. sage_wheels/share/factory/gftables/3125 +107 -0
  214. sage_wheels/share/factory/gftables/32 +4 -0
  215. sage_wheels/share/factory/gftables/32041 +1070 -0
  216. sage_wheels/share/factory/gftables/32761 +1094 -0
  217. sage_wheels/share/factory/gftables/32768 +1095 -0
  218. sage_wheels/share/factory/gftables/343 +14 -0
  219. sage_wheels/share/factory/gftables/3481 +118 -0
  220. sage_wheels/share/factory/gftables/361 +14 -0
  221. sage_wheels/share/factory/gftables/36481 +1218 -0
  222. sage_wheels/share/factory/gftables/3721 +126 -0
  223. sage_wheels/share/factory/gftables/37249 +1244 -0
  224. sage_wheels/share/factory/gftables/38809 +1296 -0
  225. sage_wheels/share/factory/gftables/39601 +1322 -0
  226. sage_wheels/share/factory/gftables/4 +3 -0
  227. sage_wheels/share/factory/gftables/4096 +139 -0
  228. sage_wheels/share/factory/gftables/44521 +1486 -0
  229. sage_wheels/share/factory/gftables/4489 +152 -0
  230. sage_wheels/share/factory/gftables/49 +4 -0
  231. sage_wheels/share/factory/gftables/4913 +166 -0
  232. sage_wheels/share/factory/gftables/49729 +1660 -0
  233. sage_wheels/share/factory/gftables/5041 +170 -0
  234. sage_wheels/share/factory/gftables/50653 +1691 -0
  235. sage_wheels/share/factory/gftables/512 +20 -0
  236. sage_wheels/share/factory/gftables/51529 +1720 -0
  237. sage_wheels/share/factory/gftables/52441 +1750 -0
  238. sage_wheels/share/factory/gftables/529 +20 -0
  239. sage_wheels/share/factory/gftables/5329 +180 -0
  240. sage_wheels/share/factory/gftables/54289 +1812 -0
  241. sage_wheels/share/factory/gftables/57121 +1906 -0
  242. sage_wheels/share/factory/gftables/58081 +1938 -0
  243. sage_wheels/share/factory/gftables/59049 +1971 -0
  244. sage_wheels/share/factory/gftables/6241 +210 -0
  245. sage_wheels/share/factory/gftables/625 +23 -0
  246. sage_wheels/share/factory/gftables/63001 +2102 -0
  247. sage_wheels/share/factory/gftables/64 +5 -0
  248. sage_wheels/share/factory/gftables/6561 +221 -0
  249. sage_wheels/share/factory/gftables/6859 +231 -0
  250. sage_wheels/share/factory/gftables/6889 +232 -0
  251. sage_wheels/share/factory/gftables/729 +27 -0
  252. sage_wheels/share/factory/gftables/7921 +266 -0
  253. sage_wheels/share/factory/gftables/8 +3 -0
  254. sage_wheels/share/factory/gftables/81 +5 -0
  255. sage_wheels/share/factory/gftables/8192 +276 -0
  256. sage_wheels/share/factory/gftables/841 +30 -0
  257. sage_wheels/share/factory/gftables/9 +3 -0
  258. sage_wheels/share/factory/gftables/9409 +316 -0
  259. sage_wheels/share/factory/gftables/961 +34 -0
  260. sage_wheels/share/info/singular.info +191898 -0
  261. sage_wheels/share/singular/LIB/GND.lib +1359 -0
  262. sage_wheels/share/singular/LIB/JMBTest.lib +976 -0
  263. sage_wheels/share/singular/LIB/JMSConst.lib +1363 -0
  264. sage_wheels/share/singular/LIB/KVequiv.lib +699 -0
  265. sage_wheels/share/singular/LIB/SingularityDBM.lib +491 -0
  266. sage_wheels/share/singular/LIB/VecField.lib +1542 -0
  267. sage_wheels/share/singular/LIB/absfact.lib +959 -0
  268. sage_wheels/share/singular/LIB/ainvar.lib +730 -0
  269. sage_wheels/share/singular/LIB/aksaka.lib +419 -0
  270. sage_wheels/share/singular/LIB/alexpoly.lib +2542 -0
  271. sage_wheels/share/singular/LIB/algebra.lib +1193 -0
  272. sage_wheels/share/singular/LIB/all.lib +136 -0
  273. sage_wheels/share/singular/LIB/arcpoint.lib +514 -0
  274. sage_wheels/share/singular/LIB/arnold.lib +4553 -0
  275. sage_wheels/share/singular/LIB/arnoldclassify.lib +2058 -0
  276. sage_wheels/share/singular/LIB/arr.lib +3486 -0
  277. sage_wheels/share/singular/LIB/assprimeszerodim.lib +755 -0
  278. sage_wheels/share/singular/LIB/autgradalg.lib +3361 -0
  279. sage_wheels/share/singular/LIB/bfun.lib +1964 -0
  280. sage_wheels/share/singular/LIB/bimodules.lib +774 -0
  281. sage_wheels/share/singular/LIB/brillnoether.lib +226 -0
  282. sage_wheels/share/singular/LIB/brnoeth.lib +5017 -0
  283. sage_wheels/share/singular/LIB/central.lib +2169 -0
  284. sage_wheels/share/singular/LIB/chern.lib +4162 -0
  285. sage_wheels/share/singular/LIB/cimonom.lib +571 -0
  286. sage_wheels/share/singular/LIB/cisimplicial.lib +1835 -0
  287. sage_wheels/share/singular/LIB/classify.lib +3239 -0
  288. sage_wheels/share/singular/LIB/classify2.lib +1462 -0
  289. sage_wheels/share/singular/LIB/classifyMapGerms.lib +1515 -0
  290. sage_wheels/share/singular/LIB/classify_aeq.lib +3253 -0
  291. sage_wheels/share/singular/LIB/classifyceq.lib +2092 -0
  292. sage_wheels/share/singular/LIB/classifyci.lib +1133 -0
  293. sage_wheels/share/singular/LIB/combinat.lib +91 -0
  294. sage_wheels/share/singular/LIB/compregb.lib +276 -0
  295. sage_wheels/share/singular/LIB/control.lib +1636 -0
  296. sage_wheels/share/singular/LIB/crypto.lib +3795 -0
  297. sage_wheels/share/singular/LIB/curveInv.lib +667 -0
  298. sage_wheels/share/singular/LIB/curvepar.lib +1817 -0
  299. sage_wheels/share/singular/LIB/customstd.lib +100 -0
  300. sage_wheels/share/singular/LIB/deRham.lib +5979 -0
  301. sage_wheels/share/singular/LIB/decodegb.lib +2134 -0
  302. sage_wheels/share/singular/LIB/decomp.lib +1655 -0
  303. sage_wheels/share/singular/LIB/deflation.lib +872 -0
  304. sage_wheels/share/singular/LIB/deform.lib +925 -0
  305. sage_wheels/share/singular/LIB/difform.lib +3055 -0
  306. sage_wheels/share/singular/LIB/divisors.lib +750 -0
  307. sage_wheels/share/singular/LIB/dmod.lib +5817 -0
  308. sage_wheels/share/singular/LIB/dmodapp.lib +3269 -0
  309. sage_wheels/share/singular/LIB/dmodideal.lib +1211 -0
  310. sage_wheels/share/singular/LIB/dmodloc.lib +2645 -0
  311. sage_wheels/share/singular/LIB/dmodvar.lib +818 -0
  312. sage_wheels/share/singular/LIB/dummy.lib +17 -0
  313. sage_wheels/share/singular/LIB/elim.lib +1009 -0
  314. sage_wheels/share/singular/LIB/ellipticcovers.lib +548 -0
  315. sage_wheels/share/singular/LIB/enumpoints.lib +146 -0
  316. sage_wheels/share/singular/LIB/equising.lib +2127 -0
  317. sage_wheels/share/singular/LIB/ffmodstd.lib +2384 -0
  318. sage_wheels/share/singular/LIB/ffsolve.lib +1289 -0
  319. sage_wheels/share/singular/LIB/findifs.lib +778 -0
  320. sage_wheels/share/singular/LIB/finitediff.lib +1768 -0
  321. sage_wheels/share/singular/LIB/finvar.lib +7989 -0
  322. sage_wheels/share/singular/LIB/fpadim.lib +2429 -0
  323. sage_wheels/share/singular/LIB/fpalgebras.lib +1666 -0
  324. sage_wheels/share/singular/LIB/fpaprops.lib +1462 -0
  325. sage_wheels/share/singular/LIB/freegb.lib +3853 -0
  326. sage_wheels/share/singular/LIB/general.lib +1350 -0
  327. sage_wheels/share/singular/LIB/gfan.lib +1768 -0
  328. sage_wheels/share/singular/LIB/gitfan.lib +3130 -0
  329. sage_wheels/share/singular/LIB/gkdim.lib +99 -0
  330. sage_wheels/share/singular/LIB/gmspoly.lib +589 -0
  331. sage_wheels/share/singular/LIB/gmssing.lib +1739 -0
  332. sage_wheels/share/singular/LIB/goettsche.lib +909 -0
  333. sage_wheels/share/singular/LIB/graal.lib +1366 -0
  334. sage_wheels/share/singular/LIB/gradedModules.lib +2541 -0
  335. sage_wheels/share/singular/LIB/graphics.lib +360 -0
  336. sage_wheels/share/singular/LIB/grobcov.lib +7706 -0
  337. sage_wheels/share/singular/LIB/groups.lib +1123 -0
  338. sage_wheels/share/singular/LIB/grwalk.lib +507 -0
  339. sage_wheels/share/singular/LIB/hdepth.lib +194 -0
  340. sage_wheels/share/singular/LIB/help.cnf +57 -0
  341. sage_wheels/share/singular/LIB/hess.lib +1946 -0
  342. sage_wheels/share/singular/LIB/hnoether.lib +4292 -0
  343. sage_wheels/share/singular/LIB/hodge.lib +400 -0
  344. sage_wheels/share/singular/LIB/homolog.lib +1965 -0
  345. sage_wheels/share/singular/LIB/hyperel.lib +975 -0
  346. sage_wheels/share/singular/LIB/inout.lib +679 -0
  347. sage_wheels/share/singular/LIB/integralbasis.lib +6224 -0
  348. sage_wheels/share/singular/LIB/interval.lib +1418 -0
  349. sage_wheels/share/singular/LIB/intprog.lib +778 -0
  350. sage_wheels/share/singular/LIB/invar.lib +443 -0
  351. sage_wheels/share/singular/LIB/involut.lib +980 -0
  352. sage_wheels/share/singular/LIB/jacobson.lib +1215 -0
  353. sage_wheels/share/singular/LIB/kskernel.lib +534 -0
  354. sage_wheels/share/singular/LIB/latex.lib +3146 -0
  355. sage_wheels/share/singular/LIB/lejeune.lib +651 -0
  356. sage_wheels/share/singular/LIB/linalg.lib +2040 -0
  357. sage_wheels/share/singular/LIB/locnormal.lib +212 -0
  358. sage_wheels/share/singular/LIB/lrcalc.lib +526 -0
  359. sage_wheels/share/singular/LIB/makedbm.lib +294 -0
  360. sage_wheels/share/singular/LIB/mathml.lib +813 -0
  361. sage_wheels/share/singular/LIB/matrix.lib +1372 -0
  362. sage_wheels/share/singular/LIB/maxlike.lib +1132 -0
  363. sage_wheels/share/singular/LIB/methods.lib +212 -0
  364. sage_wheels/share/singular/LIB/moddiq.lib +322 -0
  365. sage_wheels/share/singular/LIB/modfinduni.lib +181 -0
  366. sage_wheels/share/singular/LIB/modnormal.lib +218 -0
  367. sage_wheels/share/singular/LIB/modprimdec.lib +1278 -0
  368. sage_wheels/share/singular/LIB/modquotient.lib +269 -0
  369. sage_wheels/share/singular/LIB/modstd.lib +1024 -0
  370. sage_wheels/share/singular/LIB/modular.lib +545 -0
  371. sage_wheels/share/singular/LIB/modules.lib +2561 -0
  372. sage_wheels/share/singular/LIB/modwalk.lib +609 -0
  373. sage_wheels/share/singular/LIB/mondromy.lib +1016 -0
  374. sage_wheels/share/singular/LIB/monomialideal.lib +3851 -0
  375. sage_wheels/share/singular/LIB/mprimdec.lib +2353 -0
  376. sage_wheels/share/singular/LIB/mregular.lib +1863 -0
  377. sage_wheels/share/singular/LIB/multigrading.lib +5629 -0
  378. sage_wheels/share/singular/LIB/ncHilb.lib +777 -0
  379. sage_wheels/share/singular/LIB/ncModslimgb.lib +791 -0
  380. sage_wheels/share/singular/LIB/ncalg.lib +16311 -0
  381. sage_wheels/share/singular/LIB/ncall.lib +31 -0
  382. sage_wheels/share/singular/LIB/ncdecomp.lib +468 -0
  383. sage_wheels/share/singular/LIB/ncfactor.lib +13371 -0
  384. sage_wheels/share/singular/LIB/ncfrac.lib +1023 -0
  385. sage_wheels/share/singular/LIB/nchilbert.lib +448 -0
  386. sage_wheels/share/singular/LIB/nchomolog.lib +759 -0
  387. sage_wheels/share/singular/LIB/ncloc.lib +361 -0
  388. sage_wheels/share/singular/LIB/ncpreim.lib +795 -0
  389. sage_wheels/share/singular/LIB/ncrat.lib +2849 -0
  390. sage_wheels/share/singular/LIB/nctools.lib +1887 -0
  391. sage_wheels/share/singular/LIB/nets.lib +1456 -0
  392. sage_wheels/share/singular/LIB/nfmodstd.lib +1000 -0
  393. sage_wheels/share/singular/LIB/nfmodsyz.lib +732 -0
  394. sage_wheels/share/singular/LIB/noether.lib +1106 -0
  395. sage_wheels/share/singular/LIB/normal.lib +8700 -0
  396. sage_wheels/share/singular/LIB/normaliz.lib +2226 -0
  397. sage_wheels/share/singular/LIB/ntsolve.lib +362 -0
  398. sage_wheels/share/singular/LIB/numerAlg.lib +560 -0
  399. sage_wheels/share/singular/LIB/numerDecom.lib +2261 -0
  400. sage_wheels/share/singular/LIB/olga.lib +1933 -0
  401. sage_wheels/share/singular/LIB/orbitparam.lib +351 -0
  402. sage_wheels/share/singular/LIB/parallel.lib +319 -0
  403. sage_wheels/share/singular/LIB/paraplanecurves.lib +3110 -0
  404. sage_wheels/share/singular/LIB/perron.lib +202 -0
  405. sage_wheels/share/singular/LIB/pfd.lib +2223 -0
  406. sage_wheels/share/singular/LIB/phindex.lib +642 -0
  407. sage_wheels/share/singular/LIB/pointid.lib +673 -0
  408. sage_wheels/share/singular/LIB/polybori.lib +1430 -0
  409. sage_wheels/share/singular/LIB/polyclass.lib +525 -0
  410. sage_wheels/share/singular/LIB/polylib.lib +1174 -0
  411. sage_wheels/share/singular/LIB/polymake.lib +1902 -0
  412. sage_wheels/share/singular/LIB/presolve.lib +1533 -0
  413. sage_wheels/share/singular/LIB/primdec.lib +9576 -0
  414. sage_wheels/share/singular/LIB/primdecint.lib +1782 -0
  415. sage_wheels/share/singular/LIB/primitiv.lib +401 -0
  416. sage_wheels/share/singular/LIB/puiseuxexpansions.lib +1631 -0
  417. sage_wheels/share/singular/LIB/purityfiltration.lib +960 -0
  418. sage_wheels/share/singular/LIB/qhmoduli.lib +1561 -0
  419. sage_wheels/share/singular/LIB/qmatrix.lib +293 -0
  420. sage_wheels/share/singular/LIB/random.lib +455 -0
  421. sage_wheels/share/singular/LIB/ratgb.lib +489 -0
  422. sage_wheels/share/singular/LIB/realclassify.lib +5759 -0
  423. sage_wheels/share/singular/LIB/realizationMatroids.lib +772 -0
  424. sage_wheels/share/singular/LIB/realrad.lib +1197 -0
  425. sage_wheels/share/singular/LIB/recover.lib +2628 -0
  426. sage_wheels/share/singular/LIB/redcgs.lib +3984 -0
  427. sage_wheels/share/singular/LIB/reesclos.lib +465 -0
  428. sage_wheels/share/singular/LIB/resbinomial.lib +2802 -0
  429. sage_wheels/share/singular/LIB/resgraph.lib +789 -0
  430. sage_wheels/share/singular/LIB/resjung.lib +820 -0
  431. sage_wheels/share/singular/LIB/resolve.lib +5110 -0
  432. sage_wheels/share/singular/LIB/resources.lib +170 -0
  433. sage_wheels/share/singular/LIB/reszeta.lib +5473 -0
  434. sage_wheels/share/singular/LIB/ring.lib +1328 -0
  435. sage_wheels/share/singular/LIB/ringgb.lib +343 -0
  436. sage_wheels/share/singular/LIB/rinvar.lib +1153 -0
  437. sage_wheels/share/singular/LIB/rootisolation.lib +1481 -0
  438. sage_wheels/share/singular/LIB/rootsmr.lib +709 -0
  439. sage_wheels/share/singular/LIB/rootsur.lib +886 -0
  440. sage_wheels/share/singular/LIB/rstandard.lib +607 -0
  441. sage_wheels/share/singular/LIB/rwalk.lib +336 -0
  442. sage_wheels/share/singular/LIB/sagbi.lib +1353 -0
  443. sage_wheels/share/singular/LIB/sagbiNormaliz.lib +1622 -0
  444. sage_wheels/share/singular/LIB/sagbiNormaliz0.lib +1498 -0
  445. sage_wheels/share/singular/LIB/sagbigrob.lib +449 -0
  446. sage_wheels/share/singular/LIB/schreyer.lib +321 -0
  447. sage_wheels/share/singular/LIB/schubert.lib +2551 -0
  448. sage_wheels/share/singular/LIB/sets.lib +524 -0
  449. sage_wheels/share/singular/LIB/sheafcoh.lib +1663 -0
  450. sage_wheels/share/singular/LIB/signcond.lib +437 -0
  451. sage_wheels/share/singular/LIB/sing.lib +1094 -0
  452. sage_wheels/share/singular/LIB/sing4ti2.lib +419 -0
  453. sage_wheels/share/singular/LIB/solve.lib +2243 -0
  454. sage_wheels/share/singular/LIB/spcurve.lib +1077 -0
  455. sage_wheels/share/singular/LIB/spectrum.lib +62 -0
  456. sage_wheels/share/singular/LIB/sresext.lib +757 -0
  457. sage_wheels/share/singular/LIB/ssi.lib +143 -0
  458. sage_wheels/share/singular/LIB/standard.lib +2769 -0
  459. sage_wheels/share/singular/LIB/stanleyreisner.lib +473 -0
  460. sage_wheels/share/singular/LIB/stdmodule.lib +547 -0
  461. sage_wheels/share/singular/LIB/stratify.lib +1070 -0
  462. sage_wheels/share/singular/LIB/surf.lib +506 -0
  463. sage_wheels/share/singular/LIB/surf_jupyter.lib +223 -0
  464. sage_wheels/share/singular/LIB/surfacesignature.lib +522 -0
  465. sage_wheels/share/singular/LIB/surfex.lib +1462 -0
  466. sage_wheels/share/singular/LIB/swalk.lib +877 -0
  467. sage_wheels/share/singular/LIB/symodstd.lib +1570 -0
  468. sage_wheels/share/singular/LIB/systhreads.lib +74 -0
  469. sage_wheels/share/singular/LIB/tasks.lib +1324 -0
  470. sage_wheels/share/singular/LIB/tateProdCplxNegGrad.lib +2412 -0
  471. sage_wheels/share/singular/LIB/teachstd.lib +858 -0
  472. sage_wheels/share/singular/LIB/template.lib +116 -0
  473. sage_wheels/share/singular/LIB/toric.lib +1119 -0
  474. sage_wheels/share/singular/LIB/transformation.lib +116 -0
  475. sage_wheels/share/singular/LIB/triang.lib +1197 -0
  476. sage_wheels/share/singular/LIB/tropical.lib +8741 -0
  477. sage_wheels/share/singular/LIB/tropicalEllipticCovers.lib +2922 -0
  478. sage_wheels/share/singular/LIB/tropicalNewton.lib +1128 -0
  479. sage_wheels/share/singular/LIB/tst.lib +1108 -0
  480. sage_wheels/share/singular/LIB/weierstr.lib +241 -0
  481. sage_wheels/share/singular/LIB/zeroset.lib +1478 -0
  482. sage_wheels/share/singular/emacs/.emacs-general +184 -0
  483. sage_wheels/share/singular/emacs/.emacs-singular +234 -0
  484. sage_wheels/share/singular/emacs/COPYING +44 -0
  485. sage_wheels/share/singular/emacs/cmd-cmpl.el +241 -0
  486. sage_wheels/share/singular/emacs/ex-cmpl.el +1681 -0
  487. sage_wheels/share/singular/emacs/hlp-cmpl.el +4318 -0
  488. sage_wheels/share/singular/emacs/lib-cmpl.el +179 -0
  489. sage_wheels/share/singular/emacs/singular.el +4273 -0
  490. sage_wheels/share/singular/emacs/singular.xpm +39 -0
  491. sage_wheels/share/singular/singular.idx +5002 -0
@@ -0,0 +1,2448 @@
1
+ # sage_setup: distribution = sagemath-singular
2
+ r"""
3
+ The F-Matrix of a Fusion Ring
4
+ """
5
+ # ****************************************************************************
6
+ # Copyright (C) 2019 Daniel Bump <bump at match.stanford.edu>
7
+ # Guillermo Aboumrad <gh_willieab>
8
+ # Travis Scrimshaw <tcscrims at gmail.com>
9
+ # Galit Anikeeva <physicstravels@gmail.com>
10
+ #
11
+ # Distributed under the terms of the GNU General Public License (GPL)
12
+ # https://www.gnu.org/licenses/
13
+ # ****************************************************************************
14
+ from copy import deepcopy
15
+ from ctypes import cast, py_object
16
+ from itertools import product, zip_longest
17
+ from multiprocessing import Pool, cpu_count, set_start_method, shared_memory
18
+ import numpy as np
19
+ from os import getpid, remove
20
+ import pickle
21
+
22
+ from sage.algebras.fusion_rings.fast_parallel_fmats_methods import (
23
+ _backward_subs, _solve_for_linear_terms,
24
+ executor
25
+ )
26
+ from sage.algebras.fusion_rings.poly_tup_engine import (
27
+ apply_coeff_map, constant_coeff,
28
+ compute_known_powers,
29
+ get_variables_degrees, variables,
30
+ poly_to_tup, _tup_to_poly, tup_to_univ_poly,
31
+ _unflatten_coeffs,
32
+ poly_tup_sortkey,
33
+ resize
34
+ )
35
+ from sage.algebras.fusion_rings.shm_managers import KSHandler, FvarsHandler
36
+ from sage.graphs.graph import Graph
37
+ from sage.matrix.constructor import matrix
38
+ from sage.misc.misc import get_main_globals
39
+ from sage.rings.ideal import Ideal
40
+ from sage.structure.sage_object import SageObject
41
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
42
+ from sage.rings.polynomial.polydict import ETuple
43
+ from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics
44
+
45
+
46
+ class FMatrix(SageObject):
47
+ r"""
48
+ An F-matrix for a :class:`FusionRing`.
49
+
50
+ INPUT:
51
+
52
+ - ``FR`` -- a :class:`FusionRing`
53
+ - ``fusion_label`` -- (optional) a string used to label basis elements
54
+ of the :class:`FusionRing` associated to ``self``
55
+ (see :meth:`FusionRing.fusion_labels`)
56
+ - ``var_prefix`` -- (optional) a string indicating the desired prefix
57
+ for variables denoting F-symbols to be solved
58
+ - ``inject_variables`` -- boolean (default: ``False``); whether to inject
59
+ variables (:class:`FusionRing` basis element labels and F-symbols) into
60
+ the global namespace
61
+
62
+ The :class:`FusionRing` or Verlinde algebra is the
63
+ Grothendieck ring of a modular tensor category [BaKi2001]_.
64
+ Such categories arise in conformal field theory or in the
65
+ representation theories of affine Lie algebras, or
66
+ quantum groups at roots of unity. They have applications
67
+ to low dimensional topology and knot theory, to conformal
68
+ field theory and to topological quantum computing. The
69
+ :class:`FusionRing` captures much information about a fusion
70
+ category, but to complete the picture, the F-matrices or
71
+ 6j-symbols are needed. For example these are required in
72
+ order to construct braid group representations. This
73
+ can be done using the :class:`FusionRing` method
74
+ :meth:`FusionRing.get_braid_generators`, which uses
75
+ the F-matrix.
76
+
77
+ We only undertake to compute the F-matrix if the
78
+ :class:`FusionRing` is *multiplicity free* meaning that
79
+ the Fusion coefficients `N^{ij}_k` are bounded
80
+ by 1. For Cartan Types `X_r` and level `k`,
81
+ the multiplicity-free cases are given by the
82
+ following table.
83
+
84
+ +------------------------+----------+
85
+ | Cartan Type | `k` |
86
+ +========================+==========+
87
+ | `A_1` | any |
88
+ +------------------------+----------+
89
+ | `A_r, r\geq 2` | `\leq 2` |
90
+ +------------------------+----------+
91
+ | `B_r, r\geq 2` | `\leq 2` |
92
+ +------------------------+----------+
93
+ | `C_2` | `\leq 2` |
94
+ +------------------------+----------+
95
+ | `C_r, r\geq 3` | `\leq 1` |
96
+ +------------------------+----------+
97
+ | `D_r, r\geq 4` | `\leq 2` |
98
+ +------------------------+----------+
99
+ | `G_2, F_4, E_6, E_7` | `\leq 2` |
100
+ +------------------------+----------+
101
+ | `E_8` | `\leq 3` |
102
+ +------------------------+----------+
103
+
104
+ Beyond this limitation, computation of the F-matrix
105
+ can involve very large systems of equations. A
106
+ rule of thumb is that this code can compute the
107
+ F-matrix for systems with `\leq 14` simple objects
108
+ (primary fields) on a machine with 16 GB of memory.
109
+ (Larger examples can be quite time consuming.)
110
+
111
+ The :class:`FusionRing` and its methods capture much
112
+ of the structure of the underlying tensor category.
113
+ But an important aspect that is not encoded in the
114
+ fusion ring is the associator, which is a homomorphism
115
+ `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` that
116
+ requires an additional tool, the F-matrix or 6j-symbol.
117
+ To specify this, we fix a simple object `D`
118
+ and represent the transformation
119
+
120
+ .. MATH::
121
+
122
+ \text{Hom}(D, (A\otimes B)\otimes C)
123
+ \to \text{Hom}(D, A\otimes(B\otimes C))
124
+
125
+ by a matrix `F^{ABC}_D`. This depends on a pair of
126
+ additional simple objects `X` and `Y`. Indeed, we can
127
+ get a basis for `\text{Hom}(D, (A\otimes B)\otimes C)`
128
+ indexed by simple objects `X` in which the corresponding
129
+ homomorphism factors through `X\otimes C`, and similarly
130
+ `\text{Hom}(D, A\otimes(B\otimes C))` has a basis indexed
131
+ by `Y`, in which the basis vector factors through `A\otimes Y`.
132
+
133
+ See [TTWL2009]_ for an introduction to this topic,
134
+ [EGNO2015]_ Section 4.9 for a precise mathematical
135
+ definition, and [Bond2007]_ Section 2.5 and [Ab2022]_ for discussions
136
+ of how to compute the F-matrix. In addition to
137
+ [Bond2007]_, worked out F-matrices may be found in
138
+ [RoStWa2009]_ and [CHW2015]_.
139
+
140
+ The F-matrix is only determined up to a *gauge*. This
141
+ is a family of embeddings `C \to A\otimes B` for
142
+ simple objects `A, B, C` such that `\text{Hom}(C, A\otimes B)`
143
+ is nonzero. Changing the gauge changes the F-matrix though
144
+ not in a very essential way. By varying the gauge it is
145
+ possible to make the F-matrices unitary, or it is possible
146
+ to make them cyclotomic.
147
+
148
+ Due to the large number of equations we may fail to find a
149
+ Groebner basis if there are too many variables.
150
+
151
+ EXAMPLES::
152
+
153
+ sage: I = FusionRing("E8", 2, conjugate=True)
154
+ sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True)
155
+ sage: f = I.get_fmatrix(inject_variables=True); f
156
+ creating variables fx1..fx14
157
+ Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13
158
+ F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients
159
+
160
+ We have injected two sets of variables to the global namespace.
161
+ We created three variables ``i0, p, s`` to represent the
162
+ primary fields (simple elements) of the :class:`FusionRing`. Creating
163
+ the :class:`FMatrix` factory also created variables
164
+ ``fx1, fx2, ..., fx14`` in order to solve the hexagon and pentagon
165
+ equations describing the F-matrix. Since we called :class:`FMatrix`
166
+ with the parameter ``inject_variables=True``, these have been injected
167
+ into the global namespace. This is not necessary for the code to work
168
+ but if you want to run the code experimentally you may want access
169
+ to these variables.
170
+
171
+ EXAMPLES::
172
+
173
+ sage: f.fmatrix(s, s, s, s)
174
+ [fx10 fx11]
175
+ [fx12 fx13]
176
+
177
+ The F-matrix has not been computed at this stage, so
178
+ the F-matrix `F^{sss}_s` is filled with variables
179
+ ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is
180
+ to solve for these.
181
+
182
+ As explained above The F-matrix `(F^{ABC}_D)_{X, Y}`
183
+ two other variables `X` and `Y`. We have methods to
184
+ tell us (depending on `A, B, C, D`) what the possibilities
185
+ for these are. In this example with `A=B=C=D=s`
186
+ both `X` and `Y` are allowed to be `i_0` or `s`.
187
+
188
+ ::
189
+
190
+ sage: f.f_from(s, s, s, s), f.f_to(s, s, s, s)
191
+ ([i0, p], [i0, p])
192
+
193
+ The last two statements show that the possible values of
194
+ `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`.
195
+
196
+ The F-matrix is computed by solving the so-called
197
+ pentagon and hexagon equations. The *pentagon equations*
198
+ reflect the Mac Lane pentagon axiom in the definition
199
+ of a monoidal category. The hexagon relations
200
+ reflect the axioms of a *braided monoidal category*,
201
+ which are constraints on both the F-matrix and on
202
+ the R-matrix. Optionally, orthogonality constraints
203
+ may be imposed to obtain an orthogonal F-matrix.
204
+
205
+ ::
206
+
207
+ sage: sorted(f.get_defining_equations("pentagons"))[1:3]
208
+ [fx9*fx12 - fx2*fx13, fx4*fx11 - fx2*fx13]
209
+ sage: sorted(f.get_defining_equations("hexagons"))[1:3]
210
+ [fx6 - 1, fx2 + 1]
211
+ sage: sorted(f.get_orthogonality_constraints())[1:3]
212
+ [fx10*fx11 + fx12*fx13, fx10*fx11 + fx12*fx13]
213
+
214
+ There are two methods available to compute an F-matrix.
215
+ The first, :meth:`find_cyclotomic_solution` uses only
216
+ the pentagon and hexagon relations. The second,
217
+ :meth:`find_orthogonal_solution` uses additionally
218
+ the orthogonality relations. There are some differences
219
+ that should be kept in mind.
220
+
221
+ :meth:`find_cyclotomic_solution` currently works only with
222
+ smaller examples. For example the :class:`FusionRing` for `G_2`
223
+ at level 2 is too large. When it is available, this method
224
+ produces an F-matrix whose entries are in the same
225
+ cyclotomic field as the underlying :class:`FusionRing`. ::
226
+
227
+ sage: f.find_cyclotomic_solution()
228
+ Setting up hexagons and pentagons...
229
+ Finding a Groebner basis...
230
+ Solving...
231
+ Fixing the gauge...
232
+ adding equation... fx1 - 1
233
+ adding equation... fx11 - 1
234
+ Done!
235
+
236
+ We now have access to the values of the F-matrix using
237
+ the methods :meth:`fmatrix` and :meth:`fmat`::
238
+
239
+ sage: f.fmatrix(s, s, s, s)
240
+ [(-1/2*zeta128^48 + 1/2*zeta128^16) 1]
241
+ [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)]
242
+ sage: f.fmat(s, s, s, s, p, p)
243
+ (1/2*zeta128^48 - 1/2*zeta128^16)
244
+
245
+ :meth:`find_orthogonal_solution` is much more powerful
246
+ and is capable of handling large cases, sometimes
247
+ quickly but sometimes (in larger cases) after hours of
248
+ computation. Its F-matrices are not always in the
249
+ cyclotomic field that is the base ring of the underlying
250
+ :class:`FusionRing`, but sometimes in an extension field adjoining
251
+ some square roots. When this happens, the :class:`FusionRing` is
252
+ modified, adding an attribute ``_basecoer`` that is
253
+ a coercion from the cyclotomic field to the field
254
+ containing the F-matrix. The field containing the F-matrix
255
+ is available through :meth:`field`. ::
256
+
257
+ sage: f = FusionRing("B3", 2).get_fmatrix()
258
+ sage: f.find_orthogonal_solution(verbose=False, checkpoint=True) # not tested (~100 s)
259
+ sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested
260
+ True
261
+
262
+ sage: f = FusionRing("G2", 2).get_fmatrix()
263
+ sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s)
264
+ sage: f.field() # long time
265
+ Algebraic Field
266
+ """
267
+ def __init__(self, fusion_ring, fusion_label='f', var_prefix='fx', inject_variables=False):
268
+ r"""
269
+ Initialize ``self``.
270
+
271
+ EXAMPLES::
272
+
273
+ sage: f = FusionRing("B3", 2).get_fmatrix()
274
+ sage: TestSuite(f).run(skip='_test_pickling')
275
+ """
276
+ self._FR = fusion_ring
277
+ if inject_variables and (self._FR._fusion_labels is None):
278
+ self._FR.fusion_labels(fusion_label, inject_variables=True)
279
+ if not self._FR.is_multiplicity_free():
280
+ raise NotImplementedError("FMatrix is only available for multiplicity free FusionRings")
281
+ # Set up F-symbols entry by entry
282
+ n_vars = self.findcases()
283
+ self._poly_ring = PolynomialRing(self._FR.field(), n_vars, var_prefix)
284
+ if inject_variables:
285
+ print("creating variables %s%s..%s%s" % (var_prefix, 1, var_prefix, n_vars))
286
+ self._poly_ring.inject_variables(get_main_globals())
287
+ self._idx_to_sextuple, self._fvars = self.findcases(output=True)
288
+
289
+ # Base field attributes
290
+ self._field = self._FR.field()
291
+ r = self._field.defining_polynomial().roots(ring=QQbar, multiplicities=False)[0]
292
+ self._qqbar_embedding = self._field.hom([r], QQbar)
293
+
294
+ # Warm starting
295
+ self._chkpt_status = -1
296
+
297
+ # Multiprocessing attributes
298
+ self.mp_thresh = 10000
299
+ self.pool = None
300
+
301
+ #######################
302
+ # Class utilities #
303
+ #######################
304
+
305
+ def _repr_(self):
306
+ """
307
+ Return a string representation of ``self``.
308
+
309
+ EXAMPLES::
310
+
311
+ sage: FusionRing("B2", 1).get_fmatrix()
312
+ F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients
313
+ """
314
+ return "F-Matrix factory for %s" % self._FR
315
+
316
+ def clear_equations(self):
317
+ r"""
318
+ Clear the list of equations to be solved.
319
+
320
+ EXAMPLES::
321
+
322
+ sage: f = FusionRing("E6", 1).get_fmatrix()
323
+ sage: f.get_defining_equations('hexagons', output=False)
324
+ sage: len(f.ideal_basis)
325
+ 6
326
+ sage: f.clear_equations()
327
+ sage: len(f.ideal_basis) == 0
328
+ True
329
+ """
330
+ self.ideal_basis = []
331
+
332
+ def clear_vars(self):
333
+ r"""
334
+ Reset the F-symbols.
335
+
336
+ EXAMPLES::
337
+
338
+ sage: f = FusionRing("C4", 1).get_fmatrix()
339
+ sage: fvars = f.get_fvars()
340
+ sage: some_key = sorted(fvars)[0]
341
+ sage: fvars[some_key]
342
+ fx0
343
+ sage: fvars[some_key] = 1
344
+ sage: f.get_fvars()[some_key]
345
+ 1
346
+ sage: f.clear_vars()
347
+ sage: f.get_fvars()[some_key]
348
+ fx0
349
+ """
350
+ self._fvars = {t: self._poly_ring.gen(idx) for idx, t in self._idx_to_sextuple.items()}
351
+ self._solved = [False] * self._poly_ring.ngens()
352
+
353
+ def _reset_solver_state(self):
354
+ r"""
355
+ Reset solver state and clear relevant cache.
356
+
357
+ Used to ensure state variables are the same for each
358
+ orthogonal solver run.
359
+
360
+ EXAMPLES::
361
+
362
+ sage: f = FusionRing("G2", 1).get_fmatrix()
363
+ sage: f._reset_solver_state()
364
+ sage: K = f.field()
365
+ sage: len(f._nnz.nonzero_positions())
366
+ 1
367
+ sage: f.find_orthogonal_solution(verbose=False)
368
+ sage: K == f.field()
369
+ False
370
+ sage: f._reset_solver_state()
371
+ sage: K == f.field()
372
+ True
373
+ sage: f.FR()._basecoer is None
374
+ True
375
+ sage: f._poly_ring.base_ring() == K
376
+ True
377
+ sage: sum(f._solved) == 0
378
+ True
379
+ sage: len(f.ideal_basis) == 0
380
+ True
381
+ sage: for k, v in f._ks.items():
382
+ ....: k
383
+ sage: len(f._nnz.nonzero_positions()) == 1
384
+ True
385
+ sage: all(len(x.q_dimension.cache) == 0 for x in f.FR().basis())
386
+ True
387
+ sage: len(f.FR().r_matrix.cache) == 0
388
+ True
389
+ sage: len(f.FR().s_ij.cache) == 0
390
+ True
391
+ """
392
+ self._FR._basecoer = None
393
+ self._field = self._FR.field()
394
+ self._non_cyc_roots = []
395
+ self._poly_ring = self._poly_ring.change_ring(self._field)
396
+ self._chkpt_status = -1
397
+ self.clear_vars()
398
+ self.clear_equations()
399
+ n = self._poly_ring.ngens()
400
+ self._var_degs = [0] * n
401
+ self._kp = {}
402
+ self._ks = KSHandler(n, self._field)
403
+ self._singles = self.get_fvars_by_size(1, indices=True)
404
+ self._nnz = self._get_known_nonz()
405
+
406
+ # Clear relevant caches
407
+ [x.q_dimension.clear_cache() for x in self._FR.basis()]
408
+ self._FR.r_matrix.clear_cache()
409
+ self._FR.s_ij.clear_cache()
410
+
411
+ def fmat(self, a, b, c, d, x, y, data=True):
412
+ r"""
413
+ Return the F-Matrix coefficient `(F^{a, b, c}_d)_{x, y}`.
414
+
415
+ EXAMPLES::
416
+
417
+ sage: fr = FusionRing("G2", 1, fusion_labels=("i0", "t"), inject_variables=True)
418
+ sage: f = fr.get_fmatrix()
419
+ sage: [f.fmat(t, t, t, t, x, y) for x in fr.basis() for y in fr.basis()]
420
+ [fx1, fx2, fx3, fx4]
421
+ sage: f.find_cyclotomic_solution(output=True)
422
+ Setting up hexagons and pentagons...
423
+ Finding a Groebner basis...
424
+ Solving...
425
+ Fixing the gauge...
426
+ adding equation... fx2 - 1
427
+ Done!
428
+ {(t, t, t, i0, t, t): 1,
429
+ (t, t, t, t, i0, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1),
430
+ (t, t, t, t, i0, t): 1,
431
+ (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1),
432
+ (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)}
433
+ sage: [f.fmat(t, t, t, t, x, y) for x in f._FR.basis() for y in f._FR.basis()]
434
+ [(-zeta60^14 + zeta60^6 + zeta60^4 - 1),
435
+ 1,
436
+ (-zeta60^14 + zeta60^6 + zeta60^4 - 1),
437
+ (zeta60^14 - zeta60^6 - zeta60^4 + 1)]
438
+ """
439
+ if (self._FR.Nk_ij(a, b, x) == 0
440
+ or self._FR.Nk_ij(x, c, d) == 0
441
+ or self._FR.Nk_ij(b, c, y) == 0
442
+ or self._FR.Nk_ij(a, y, d) == 0):
443
+ return 0
444
+
445
+ # Some known zero F-symbols
446
+ if a == self._FR.one():
447
+ return 1 if x == b and y == d else 0
448
+
449
+ if b == self._FR.one():
450
+ return 1 if x == a and y == c else 0
451
+
452
+ if c == self._FR.one():
453
+ return 1 if x == d and y == b else 0
454
+
455
+ if data:
456
+ # Better to use try/except for speed. Somewhat trivial, but worth
457
+ # hours when method is called ~10^11 times
458
+ try:
459
+ return self._fvars[a, b, c, d, x, y]
460
+ except KeyError:
461
+ return 0
462
+
463
+ return (a, b, c, d, x, y)
464
+
465
+ def fmatrix(self, a, b, c, d):
466
+ r"""
467
+ Return the F-Matrix `F^{a, b, c}_d`.
468
+
469
+ INPUT:
470
+
471
+ - ``a``, ``b``, ``c``, ``d`` -- basis elements of the associated
472
+ :class:`FusionRing`
473
+
474
+ EXAMPLES::
475
+
476
+ sage: fr = FusionRing("A1", 2, fusion_labels='c', inject_variables=True)
477
+ sage: f = fr.get_fmatrix(new=True)
478
+ sage: f.fmatrix(c1, c1, c1, c1)
479
+ [fx0 fx1]
480
+ [fx2 fx3]
481
+ sage: f.find_cyclotomic_solution(verbose=False);
482
+ adding equation... fx4 - 1
483
+ adding equation... fx10 - 1
484
+ sage: f.f_from(c1, c1, c1, c1)
485
+ [c0, c2]
486
+ sage: f.f_to(c1, c1, c1, c1)
487
+ [c0, c2]
488
+ sage: f.fmatrix(c1, c1, c1, c1)
489
+ [ (1/2*zeta32^12 - 1/2*zeta32^4) (-1/2*zeta32^12 + 1/2*zeta32^4)]
490
+ [ (1/2*zeta32^12 - 1/2*zeta32^4) (1/2*zeta32^12 - 1/2*zeta32^4)]
491
+ """
492
+ X = self.f_from(a, b, c, d)
493
+ Y = self.f_to(a, b, c, d)
494
+ return matrix([[self.fmat(a, b, c, d, x, y) for y in Y] for x in X])
495
+
496
+ def field(self):
497
+ r"""
498
+ Return the base field containing the F-symbols.
499
+
500
+ When ``self`` is initialized, the field is set to be the
501
+ cyclotomic field of the :class:`FusionRing` associated
502
+ to ``self``.
503
+
504
+ The field may change after running :meth:`find_orthogonal_solution`.
505
+ At that point, this method could return the
506
+ associated :class:`FusionRing`'s cyclotomic field, an
507
+ appropriate :func:`NumberField` that was computed on the fly
508
+ by the F-matrix solver, or the :class:`QQbar<AlgebraicField>`.
509
+
510
+ Depending on the ``CartanType`` of ``self``, the solver may need
511
+ to compute an extension field containing certain square roots that
512
+ do not belong to the associated :class:`FusionRing`'s cyclotomic field.
513
+
514
+ In certain cases we revert to :class:`QQbar<AlgebraicField>` because
515
+ the extension field computation does not seem to terminate. See
516
+ :meth:`attempt_number_field_computation` for more details.
517
+
518
+ The method :meth:`get_non_cyclotomic_roots` returns a list of
519
+ roots defining the extension of the :class:`FusionRing`'s
520
+ cyclotomic field needed to contain all F-symbols.
521
+
522
+ EXAMPLES::
523
+
524
+ sage: f = FusionRing("G2", 1).get_fmatrix()
525
+ sage: f.field()
526
+ Cyclotomic Field of order 60 and degree 16
527
+ sage: f.find_orthogonal_solution(verbose=False)
528
+ sage: f.field()
529
+ Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1
530
+ sage: phi = f.get_qqbar_embedding()
531
+ sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()]
532
+ [-0.786151377757423 - 8.92806368517581e-31*I]
533
+
534
+ .. NOTE::
535
+
536
+ Consider using ``self.field().optimized_representation()`` to
537
+ obtain an equivalent :func:`NumberField` with a defining
538
+ polynomial with smaller coefficients, for a more efficient
539
+ element representation.
540
+ """
541
+ return self._field
542
+
543
+ def FR(self):
544
+ r"""
545
+ Return the :class:`FusionRing` associated to ``self``.
546
+
547
+ EXAMPLES::
548
+
549
+ sage: f = FusionRing("D3", 1).get_fmatrix()
550
+ sage: f.FR()
551
+ The Fusion Ring of Type D3 and level 1 with Integer Ring coefficients
552
+ """
553
+ return self._FR
554
+
555
+ def findcases(self, output=False):
556
+ r"""
557
+ Return unknown F-matrix entries.
558
+
559
+ If run with ``output=True``,
560
+ this returns two dictionaries; otherwise it just returns the
561
+ number of unknown values.
562
+
563
+ EXAMPLES::
564
+
565
+ sage: f = FusionRing("G2", 1, fusion_labels=("i0", "t")).get_fmatrix()
566
+ sage: f.findcases()
567
+ 5
568
+ sage: f.findcases(output=True)
569
+ ({0: (t, t, t, i0, t, t),
570
+ 1: (t, t, t, t, i0, i0),
571
+ 2: (t, t, t, t, i0, t),
572
+ 3: (t, t, t, t, t, i0),
573
+ 4: (t, t, t, t, t, t)},
574
+ {(t, t, t, i0, t, t): fx0,
575
+ (t, t, t, t, i0, i0): fx1,
576
+ (t, t, t, t, i0, t): fx2,
577
+ (t, t, t, t, t, i0): fx3,
578
+ (t, t, t, t, t, t): fx4})
579
+ """
580
+ i = 0
581
+ if output:
582
+ idx_map = {}
583
+ ret = {}
584
+ id_anyon = self._FR.one()
585
+ for a, b, c, d in product(self._FR.basis(), repeat=4):
586
+ if a == id_anyon or b == id_anyon or c == id_anyon:
587
+ continue
588
+ for x in self.f_from(a, b, c, d):
589
+ for y in self.f_to(a, b, c, d):
590
+ if output:
591
+ v = self._poly_ring.gen(i)
592
+ ret[(a, b, c, d, x, y)] = v
593
+ idx_map[i] = (a, b, c, d, x, y)
594
+ i += 1
595
+
596
+ return (idx_map, ret) if output else i
597
+
598
+ def f_from(self, a, b, c, d):
599
+ r"""
600
+ Return the possible `x` such that there are morphisms
601
+ `d \to x \otimes c \to (a \otimes b) \otimes c`.
602
+
603
+ INPUT:
604
+
605
+ - ``a``, ``b``, ``c``, ``d`` -- basis elements of the associated
606
+ :class:`FusionRing`
607
+
608
+ EXAMPLES::
609
+
610
+ sage: fr = FusionRing("A1", 3, fusion_labels='a', inject_variables=True)
611
+ sage: f = fr.get_fmatrix()
612
+ sage: f.fmatrix(a1, a1, a2, a2)
613
+ [fx6 fx7]
614
+ [fx8 fx9]
615
+ sage: f.f_from(a1, a1, a2, a2)
616
+ [a0, a2]
617
+ sage: f.f_to(a1, a1, a2, a2)
618
+ [a1, a3]
619
+ """
620
+ return [x for x in self._FR.basis()
621
+ if self._FR.Nk_ij(a, b, x) != 0 and self._FR.Nk_ij(x, c, d) != 0]
622
+
623
+ def f_to(self, a, b, c, d):
624
+ r"""
625
+ Return the possible `y` such that there are morphisms
626
+ `d \to a \otimes y \to a \otimes (b \otimes c)`.
627
+
628
+ INPUT:
629
+
630
+ - ``a``, ``b``, ``c``, ``d`` -- basis elements of the associated
631
+ :class:`FusionRing`
632
+
633
+ EXAMPLES::
634
+
635
+ sage: b22 = FusionRing("B2", 2)
636
+ sage: b22.fusion_labels("b", inject_variables=True)
637
+ sage: B = b22.get_fmatrix()
638
+ sage: B.fmatrix(b2, b4, b2, b4)
639
+ [fx266 fx267 fx268]
640
+ [fx269 fx270 fx271]
641
+ [fx272 fx273 fx274]
642
+ sage: B.f_from(b2, b4, b2, b4)
643
+ [b1, b3, b5]
644
+ sage: B.f_to(b2, b4, b2, b4)
645
+ [b1, b3, b5]
646
+ """
647
+ return [y for y in self._FR.basis()
648
+ if self._FR.Nk_ij(b, c, y) != 0 and self._FR.Nk_ij(a, y, d) != 0]
649
+
650
+ ####################
651
+ # Data getters #
652
+ ####################
653
+
654
+ def get_fvars(self):
655
+ r"""
656
+ Return a dictionary of F-symbols.
657
+
658
+ The keys are sextuples `(a, b, c, d, x, y)` of basis elements of
659
+ ``self.FR()`` and the values are the corresponding F-symbols
660
+ `(F^{a, b, c}_d)_{xy}`.
661
+
662
+ These values reflect the current state of a solver's computation.
663
+
664
+ EXAMPLES::
665
+
666
+ sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True)
667
+ creating variables fx1..fx8
668
+ Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7
669
+ sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)]
670
+ fx0
671
+ sage: f.find_orthogonal_solution(verbose=False)
672
+ sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)]
673
+ 1
674
+ """
675
+ return self._fvars
676
+
677
+ def get_poly_ring(self):
678
+ r"""
679
+ Return the polynomial ring whose generators denote the desired F-symbols.
680
+
681
+ EXAMPLES::
682
+
683
+ sage: f = FusionRing("B6", 1).get_fmatrix()
684
+ sage: f.get_poly_ring()
685
+ Multivariate Polynomial Ring in fx0, ..., fx13 over
686
+ Cyclotomic Field of order 96 and degree 32
687
+ """
688
+ return self._poly_ring
689
+
690
+ # TODO: this method is incredibly slow... improve by keeping track of the cyclotomic polynomials, NOT their roots in QQbar
691
+ def get_non_cyclotomic_roots(self):
692
+ r"""
693
+ Return a list of roots that define the extension of the associated
694
+ :class:`FusionRing`'s base
695
+ :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`,
696
+ containing all the F-symbols.
697
+
698
+ OUTPUT:
699
+
700
+ The list of non-cyclotomic roots is given as a list of elements of the
701
+ field returned by :meth:`field()`.
702
+
703
+ If ``self.field() == self.FR().field()`` then this method
704
+ returns an empty list.
705
+
706
+ EXAMPLES::
707
+
708
+ sage: f = FusionRing("E6", 1).get_fmatrix()
709
+ sage: f.find_orthogonal_solution(verbose=False)
710
+ sage: f.field() == f.FR().field()
711
+ True
712
+ sage: f.get_non_cyclotomic_roots()
713
+ []
714
+ sage: f = FusionRing("G2", 1).get_fmatrix()
715
+ sage: f.find_orthogonal_solution(verbose=False)
716
+ sage: f.field() == f.FR().field()
717
+ False
718
+ sage: phi = f.get_qqbar_embedding()
719
+ sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()]
720
+ [-0.786151377757423 - 8.92806368517581e-31*I]
721
+
722
+ When ``self.field()`` is a ``NumberField``, one may use
723
+ :meth:`get_qqbar_embedding` to embed the resulting values into
724
+ :class:`QQbar<AlgebraicField>`.
725
+ """
726
+ return sorted(set(self._non_cyc_roots))
727
+
728
+ def get_qqbar_embedding(self):
729
+ r"""
730
+ Return an embedding from the base field containing F-symbols (the
731
+ associated :class:`FusionRing`'s
732
+ :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`,
733
+ a :func:`NumberField`, or :class:`QQbar<AlgebraicField>`) into
734
+ :class:`QQbar<AlgebraicField>`.
735
+
736
+ This embedding is useful for getting a better sense for the
737
+ F-symbols, particularly when they are computed as elements of a
738
+ :func:`NumberField`. See also :meth:`get_non_cyclotomic_roots`.
739
+
740
+ EXAMPLES::
741
+
742
+ sage: fr = FusionRing("G2", 1)
743
+ sage: f = fr.get_fmatrix(fusion_label='g', inject_variables=True, new=True)
744
+ creating variables fx1..fx5
745
+ Defining fx0, fx1, fx2, fx3, fx4
746
+ sage: f.find_orthogonal_solution()
747
+ Computing F-symbols for The Fusion Ring of Type G2 and level 1 with Integer Ring coefficients with 5 variables...
748
+ Set up 10 hex and orthogonality constraints...
749
+ Partitioned 10 equations into 2 components of size:
750
+ [4, 1]
751
+ Elimination epoch completed... 0 eqns remain in ideal basis
752
+ Hex elim step solved for 4 / 5 variables
753
+ Set up 0 reduced pentagons...
754
+ Pent elim step solved for 4 / 5 variables
755
+ Partitioned 0 equations into 0 components of size:
756
+ []
757
+ Partitioned 1 equations into 1 components of size:
758
+ [1]
759
+ Computing appropriate NumberField...
760
+ sage: phi = f.get_qqbar_embedding()
761
+ sage: phi(f.fmat(g1, g1, g1, g1, g1, g1)).n()
762
+ -0.618033988749895 + 1.46674215951686e-29*I
763
+ """
764
+ return self._qqbar_embedding
765
+
766
+ def get_coerce_map_from_fr_cyclotomic_field(self):
767
+ r"""
768
+ Return a coercion map from the associated :class:`FusionRing`'s
769
+ cyclotomic field into the base field containing all F-symbols
770
+ (this could be the :class:`FusionRing`'s
771
+ :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`,
772
+ a :func:`NumberField`, or :class:`QQbar<AlgebraicField>`).
773
+
774
+ EXAMPLES::
775
+
776
+ sage: f = FusionRing("G2", 1).get_fmatrix()
777
+ sage: f.find_orthogonal_solution(verbose=False)
778
+ sage: f.FR().field()
779
+ Cyclotomic Field of order 60 and degree 16
780
+ sage: f.field()
781
+ Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1
782
+ sage: phi = f.get_coerce_map_from_fr_cyclotomic_field()
783
+ sage: phi.domain() == f.FR().field()
784
+ True
785
+ sage: phi.codomain() == f.field()
786
+ True
787
+
788
+ When F-symbols are computed as elements of the associated
789
+ :class:`FusionRing`'s base
790
+ :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`,
791
+ we have ``self.field() == self.FR().field()`` and this
792
+ returns the identity map on ``self.field()``. ::
793
+
794
+ sage: f = FusionRing("A2", 1).get_fmatrix()
795
+ sage: f.find_orthogonal_solution(verbose=False)
796
+ sage: phi = f.get_coerce_map_from_fr_cyclotomic_field()
797
+ sage: f.field()
798
+ Cyclotomic Field of order 48 and degree 16
799
+ sage: f.field() == f.FR().field()
800
+ True
801
+ sage: phi.domain() == f.field()
802
+ True
803
+ sage: phi.is_identity()
804
+ True
805
+ """
806
+ # If base field is different from associated FusionRing's CyclotomicField,
807
+ # return coercion map
808
+ try:
809
+ return self._coerce_map_from_cyc_field
810
+ # Otherwise, return identity map CyclotomicField <-> CyclotomicField
811
+ except AttributeError:
812
+ F = self._FR.field()
813
+ return F.hom([F.gen()], F)
814
+
815
+ def get_fvars_in_alg_field(self):
816
+ r"""
817
+ Return F-symbols as elements of the :class:`QQbar<AlgebraicField>`.
818
+
819
+ This method uses the embedding defined by
820
+ :meth:`get_qqbar_embedding` to coerce
821
+ F-symbols into :class:`QQbar<AlgebraicField>`.
822
+
823
+ EXAMPLES::
824
+
825
+ sage: fr = FusionRing("G2", 1)
826
+ sage: f = fr.get_fmatrix(fusion_label='g', inject_variables=True, new=True)
827
+ creating variables fx1..fx5
828
+ Defining fx0, fx1, fx2, fx3, fx4
829
+ sage: f.find_orthogonal_solution(verbose=False)
830
+ sage: f.field()
831
+ Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1
832
+ sage: f.get_fvars_in_alg_field()
833
+ {(g1, g1, g1, g0, g1, g1): 1,
834
+ (g1, g1, g1, g1, g0, g0): 0.61803399? + 0.?e-8*I,
835
+ (g1, g1, g1, g1, g0, g1): -0.7861514? + 0.?e-8*I,
836
+ (g1, g1, g1, g1, g1, g0): -0.7861514? + 0.?e-8*I,
837
+ (g1, g1, g1, g1, g1, g1): -0.61803399? + 0.?e-8*I}
838
+ """
839
+ return {sextuple: self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items()}
840
+
841
+ def get_radical_expression(self):
842
+ """
843
+ Return a radical expression of F-symbols.
844
+
845
+ EXAMPLES::
846
+
847
+ sage: f = FusionRing("G2", 1).get_fmatrix()
848
+ sage: f.FR().fusion_labels("g", inject_variables=True)
849
+ sage: f.find_orthogonal_solution(verbose=False)
850
+ sage: radical_fvars = f.get_radical_expression() # long time (~1.5s)
851
+ sage: radical_fvars[g1, g1, g1, g1, g1, g0] # long time
852
+ -sqrt(1/2*sqrt(5) - 1/2)
853
+ """
854
+ return {sextuple: val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()}
855
+
856
+ #######################
857
+ # Private helpers #
858
+ #######################
859
+
860
+ def _get_known_vals(self):
861
+ r"""
862
+ Construct a dictionary of ``idx``, ``known_val`` pairs used for
863
+ substituting into remaining equations.
864
+
865
+ EXAMPLES::
866
+
867
+ sage: f = FusionRing("D4", 1).get_fmatrix()
868
+ sage: f._reset_solver_state()
869
+ sage: len(f._get_known_vals()) == 0
870
+ True
871
+ sage: f.find_orthogonal_solution(verbose=False)
872
+ sage: len(f._get_known_vals()) == f._poly_ring.ngens()
873
+ True
874
+ """
875
+ return {i: self._fvars[s] for i, s in self._idx_to_sextuple.items() if self._solved[i]}
876
+
877
+ def _get_known_nonz(self):
878
+ r"""
879
+ Construct an :class:`ETuple` indicating positions of
880
+ known nonzero variables.
881
+
882
+ .. NOTE::
883
+
884
+ MUST be called after ``self._ks = _get_known_sq()``.
885
+ This method is called by the constructor of ``self``.
886
+
887
+ EXAMPLES::
888
+
889
+ sage: f = FusionRing("D5", 1).get_fmatrix() # indirect doctest
890
+ sage: f._reset_solver_state()
891
+ sage: f._nnz
892
+ (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
893
+ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100)
894
+ """
895
+ nonz = {idx: 100 for idx in self._singles}
896
+ for idx, _ in self._ks.items():
897
+ nonz[idx] = 100
898
+ return ETuple(nonz, self._poly_ring.ngens())
899
+
900
+ ##############################
901
+ # Variables partitioning #
902
+ ##############################
903
+
904
+ def largest_fmat_size(self):
905
+ r"""
906
+ Get the size of the largest F-matrix `F^{abc}_d`.
907
+
908
+ EXAMPLES::
909
+
910
+ sage: f = FusionRing("B3", 2).get_fmatrix()
911
+ sage: f.largest_fmat_size()
912
+ 4
913
+ """
914
+ return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(), repeat=4))
915
+
916
+ def get_fvars_by_size(self, n, indices=False):
917
+ r"""
918
+ Return the set of F-symbols that are entries of an `n \times n` matrix
919
+ `F^{a, b, c}_d`.
920
+
921
+ INPUT:
922
+
923
+ - ``n`` -- positive integer
924
+ - ``indices`` -- boolean (default: ``False``)
925
+
926
+ If ``indices`` is ``False`` (default),
927
+ this method returns a set of sextuples `(a, b, c, d, x, y)` identifying
928
+ the corresponding F-symbol. Each sextuple is a key in the
929
+ dictionary returned by :meth:`get_fvars`.
930
+
931
+ Otherwise the method returns a list of integer indices that
932
+ internally identify the F-symbols. The ``indices=True`` option is
933
+ meant for internal use.
934
+
935
+ EXAMPLES::
936
+
937
+ sage: f = FusionRing("A2", 2).get_fmatrix(inject_variables=True)
938
+ creating variables fx1..fx287
939
+ Defining fx0, ..., fx286
940
+ sage: f.largest_fmat_size()
941
+ 2
942
+ sage: f.get_fvars_by_size(2)
943
+ {(f2, f2, f2, f4, f1, f1),
944
+ (f2, f2, f2, f4, f1, f5),
945
+ ...
946
+ (f4, f4, f4, f4, f4, f0),
947
+ (f4, f4, f4, f4, f4, f4)}
948
+ """
949
+ var_set = set()
950
+ one = self._FR.one()
951
+ for a, b, c, d in product(self._FR.basis(), repeat=4):
952
+ X = self.f_from(a, b, c, d)
953
+ Y = self.f_to(a, b, c, d)
954
+ if len(X) == n and len(Y) == n:
955
+ for x in X:
956
+ for y in Y:
957
+ # Discard trivial 1x1 F-matrix
958
+ trivial = a == one and x == b and y == d
959
+ trivial |= b == one and x == a and y == c
960
+ trivial |= c == one and x == d and y == b
961
+ if not trivial:
962
+ var_set.add((a, b, c, d, x, y))
963
+ if indices:
964
+ sext_to_idx = {v: k for k, v in self._idx_to_sextuple.items()}
965
+ return {sext_to_idx[fx] for fx in var_set}
966
+ return var_set
967
+
968
+ ############################
969
+ # Checkpoint utilities #
970
+ ############################
971
+
972
+ def save_fvars(self, filename):
973
+ r"""
974
+ Save computed F-symbols for later use.
975
+
976
+ INPUT:
977
+
978
+ - ``filename`` -- string specifying the name of the pickle file
979
+ to be used
980
+
981
+ The current directory is used unless an absolute path to a file in
982
+ a different directory is provided.
983
+
984
+ .. NOTE::
985
+
986
+ This method should only be used *after* successfully running one
987
+ of the solvers, e.g. :meth:`find_cyclotomic_solution` or
988
+ :meth:`find_orthogonal_solution`.
989
+
990
+ When used in conjunction with :meth:`load_fvars`, this method may
991
+ be used to restore state of an :class:`FMatrix` object at the end
992
+ of a successful F-matrix solver run.
993
+
994
+ EXAMPLES::
995
+
996
+ sage: f = FusionRing("A2", 1).get_fmatrix(new=True)
997
+ sage: f.find_orthogonal_solution(verbose=False)
998
+ sage: fvars = f.get_fvars()
999
+ sage: K = f.field()
1000
+ sage: filename = f.get_fr_str() + "_solver_results.pickle"
1001
+ sage: f.save_fvars(filename)
1002
+ sage: del f
1003
+ sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True)
1004
+ sage: f2.load_fvars(filename)
1005
+ sage: fvars == f2.get_fvars()
1006
+ True
1007
+ sage: K == f2.field()
1008
+ True
1009
+ sage: os.remove(filename)
1010
+ """
1011
+ final_state = [self._fvars,
1012
+ self._non_cyc_roots,
1013
+ self.get_coerce_map_from_fr_cyclotomic_field(),
1014
+ self._qqbar_embedding]
1015
+ with open(filename, 'wb') as f:
1016
+ pickle.dump(final_state, f)
1017
+
1018
+ def load_fvars(self, filename):
1019
+ r"""
1020
+ Load previously computed F-symbols from a pickle file.
1021
+
1022
+ See :meth:`save_fvars` for more information.
1023
+
1024
+ EXAMPLES::
1025
+
1026
+ sage: f = FusionRing("A2", 1).get_fmatrix(new=True)
1027
+ sage: f.find_orthogonal_solution(verbose=False)
1028
+ sage: fvars = f.get_fvars()
1029
+ sage: K = f.field()
1030
+ sage: filename = f.get_fr_str() + "_solver_results.pickle"
1031
+ sage: f.save_fvars(filename)
1032
+ sage: del f
1033
+ sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True)
1034
+ sage: f2.load_fvars(filename)
1035
+ sage: fvars == f2.get_fvars()
1036
+ True
1037
+ sage: K == f2.field()
1038
+ True
1039
+ sage: os.remove(filename)
1040
+
1041
+ .. NOTE::
1042
+
1043
+ :meth:`save_fvars`. This method does not work with intermediate
1044
+ checkpoint pickles; it only works with pickles containing *all*
1045
+ F-symbols, i.e. those created by :meth:`save_fvars` and by
1046
+ specifying an optional ``save_results`` parameter for
1047
+ :meth:`find_orthogonal_solution`.
1048
+ """
1049
+ with open(filename, 'rb') as f:
1050
+ self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f)
1051
+ # Update state attributes
1052
+ self._chkpt_status = 7
1053
+ self._solved = [True for v in self._fvars]
1054
+ self._field = self._qqbar_embedding.domain()
1055
+
1056
+ def get_fr_str(self):
1057
+ r"""
1058
+ Auto-generate an identifying key for saving results.
1059
+
1060
+ EXAMPLES::
1061
+
1062
+ sage: f = FusionRing("B3", 1).get_fmatrix()
1063
+ sage: f.get_fr_str()
1064
+ 'B31'
1065
+ """
1066
+ ct = self._FR.cartan_type()
1067
+ return ct.letter + str(ct.n) + str(self._FR.fusion_level())
1068
+
1069
+ def _checkpoint(self, do_chkpt, status, verbose=True):
1070
+ r"""
1071
+ Pickle current solver state.
1072
+
1073
+ EXAMPLES::
1074
+
1075
+ sage: f = FusionRing("A1", 3).get_fmatrix(new=True)
1076
+ sage: f._reset_solver_state()
1077
+ sage: f.get_orthogonality_constraints(output=False)
1078
+ sage: f.get_defining_equations('hexagons', output=False)
1079
+ sage: f.ideal_basis = f._par_graph_gb(verbose=False)
1080
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup
1081
+ sage: f.ideal_basis.sort(key=poly_tup_sortkey)
1082
+ sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler
1083
+ sage: n = f._poly_ring.ngens()
1084
+ sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars)
1085
+ sage: f._triangular_elim(verbose=False)
1086
+ sage: f._update_reduction_params()
1087
+ sage: f._checkpoint(do_chkpt=True, status=2)
1088
+ Checkpoint 2 reached!
1089
+ sage: del f
1090
+ sage: f = FusionRing("A1", 3).get_fmatrix(new=True)
1091
+ sage: f.find_orthogonal_solution(warm_start='fmatrix_solver_checkpoint_A13.pickle')
1092
+ Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables...
1093
+ Set up 121 reduced pentagons...
1094
+ Elimination epoch completed... 18 eqns remain in ideal basis
1095
+ Elimination epoch completed... 5 eqns remain in ideal basis
1096
+ Pent elim step solved for 64 / 71 variables
1097
+ Partitioned 5 equations into 1 components of size:
1098
+ [4]
1099
+ Elimination epoch completed... 0 eqns remain in ideal basis
1100
+ Partitioned 6 equations into 6 components of size:
1101
+ [1, 1, 1, 1, 1, 1]
1102
+ Computing appropriate NumberField...
1103
+ sage: f._chkpt_status == 7
1104
+ True
1105
+ sage: sum(f._solved) == f._poly_ring.ngens()
1106
+ True
1107
+ sage: os.remove("fmatrix_solver_checkpoint_A13.pickle")
1108
+ sage: f = FusionRing("A1", 2).get_fmatrix(new=True)
1109
+ sage: f._reset_solver_state()
1110
+ sage: f.get_orthogonality_constraints(output=False)
1111
+ sage: f.get_defining_equations('hexagons', output=False)
1112
+ sage: f.ideal_basis = f._par_graph_gb(verbose=False)
1113
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey
1114
+ sage: f.ideal_basis.sort(key=poly_tup_sortkey)
1115
+ sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler
1116
+ sage: n = f._poly_ring.ngens()
1117
+ sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars)
1118
+ sage: f._triangular_elim(verbose=False)
1119
+ sage: f._update_reduction_params()
1120
+ sage: f.get_defining_equations('pentagons', output=False)
1121
+ sage: f.ideal_basis.sort(key=poly_tup_sortkey)
1122
+ sage: f._triangular_elim(verbose=False)
1123
+ sage: f._checkpoint(do_chkpt=True, status=4)
1124
+ Checkpoint 4 reached!
1125
+ sage: del f
1126
+ sage: f = FusionRing("A1", 2).get_fmatrix(new=True)
1127
+ sage: f.find_orthogonal_solution(warm_start='fmatrix_solver_checkpoint_A12.pickle')
1128
+ Computing F-symbols for The Fusion Ring of Type A1 and level 2 with Integer Ring coefficients with 14 variables...
1129
+ Partitioned 0 equations into 0 components of size:
1130
+ []
1131
+ Partitioned 2 equations into 2 components of size:
1132
+ [1, 1]
1133
+ sage: f._chkpt_status == 7
1134
+ True
1135
+ sage: sum(f._solved) == f._poly_ring.ngens()
1136
+ True
1137
+ sage: os.remove("fmatrix_solver_checkpoint_A12.pickle")
1138
+ """
1139
+ if not do_chkpt:
1140
+ return
1141
+ filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle"
1142
+ with open(filename, 'wb') as f:
1143
+ pickle.dump([self._fvars, list(self._solved), self._ks, self.ideal_basis, status], f)
1144
+ if verbose:
1145
+ print(f"Checkpoint {status} reached!")
1146
+
1147
+ def _restore_state(self, filename):
1148
+ r"""
1149
+ Load solver state from file. Use this method both for warm-starting
1150
+ :meth:`find_orthogonal_solution` and to load pickled results.
1151
+
1152
+ EXAMPLES::
1153
+
1154
+ sage: f = FusionRing("A1", 2).get_fmatrix(new=True)
1155
+ sage: f._reset_solver_state()
1156
+ sage: f.get_orthogonality_constraints(output=False)
1157
+ sage: f.get_defining_equations('hexagons', output=False)
1158
+ sage: f.ideal_basis = f._par_graph_gb(verbose=False)
1159
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup
1160
+ sage: f.ideal_basis.sort(key=poly_tup_sortkey)
1161
+ sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler
1162
+ sage: n = f._poly_ring.ngens()
1163
+ sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars)
1164
+ sage: f._triangular_elim(verbose=False)
1165
+ sage: f._update_reduction_params()
1166
+ sage: fvars = f._fvars
1167
+ sage: ib = f.ideal_basis
1168
+ sage: solved = f._solved
1169
+ sage: ks = f._ks
1170
+ sage: status = f._chkpt_status
1171
+ sage: f._checkpoint(do_chkpt=True, status=2)
1172
+ Checkpoint 2 reached!
1173
+ sage: del f
1174
+ sage: f = FusionRing("A1", 2).get_fmatrix(new=True)
1175
+ sage: f._reset_solver_state()
1176
+ sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle")
1177
+ sage: for sextuple, fvar in fvars.items():
1178
+ ....: assert fvar == f._fvars[sextuple]
1179
+ ....:
1180
+ sage: ib == f.ideal_basis
1181
+ True
1182
+ sage: ks == f._ks
1183
+ True
1184
+ sage: solved == f._solved
1185
+ True
1186
+ sage: 2 == f._chkpt_status
1187
+ True
1188
+ sage: os.remove("fmatrix_solver_checkpoint_A12.pickle")
1189
+
1190
+ TESTS::
1191
+
1192
+ sage: f = FusionRing("A1", 3).get_fmatrix(new=True)
1193
+ sage: f.find_orthogonal_solution(save_results='test.pickle', verbose=False) # long time
1194
+ sage: del f
1195
+ sage: f = FusionRing("A1", 3).get_fmatrix(new=True)
1196
+ sage: f.find_orthogonal_solution(warm_start='test.pickle') # long time
1197
+ sage: f._chkpt_status == 7 # long time
1198
+ True
1199
+ sage: os.remove("test.pickle") # long time
1200
+ """
1201
+ with open(filename, 'rb') as f:
1202
+ state = pickle.load(f)
1203
+ # Loading saved results pickle
1204
+ if len(state) == 4:
1205
+ self.load_fvars(filename)
1206
+ self._chkpt_status = 7
1207
+ return
1208
+ self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state
1209
+ self._update_reduction_params()
1210
+
1211
+ #################
1212
+ # MapReduce #
1213
+ #################
1214
+
1215
+ def start_worker_pool(self, processes=None):
1216
+ """
1217
+ Initialize a ``multiprocessing`` worker pool for parallel processing,
1218
+ which may be used e.g. to set up defining equations using
1219
+ :meth:`get_defining_equations`.
1220
+
1221
+ This method sets ``self``'s ``pool`` attribute. The worker
1222
+ pool may be used time and again. Upon initialization, each process
1223
+ in the pool attaches to the necessary shared memory resources.
1224
+
1225
+ When you are done using the worker pool, use
1226
+ :meth:`shutdown_worker_pool` to close the pool and properly dispose
1227
+ of shared memory resources.
1228
+
1229
+ INPUT:
1230
+
1231
+ - ``processes`` -- integer indicating the number of workers
1232
+ in the pool; if left unspecified, the number of workers
1233
+ equals the number of processors available
1234
+
1235
+ OUTPUT: boolean; whether a worker pool was successfully initialized
1236
+
1237
+ EXAMPLES::
1238
+
1239
+ sage: f = FusionRing("G2", 1).get_fmatrix(new=True)
1240
+ sage: f.start_worker_pool()
1241
+ sage: he = f.get_defining_equations('hexagons')
1242
+ sage: sorted(he)
1243
+ [fx0 - 1,
1244
+ fx2*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx4^2 + (zeta60^6)*fx4,
1245
+ fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3,
1246
+ fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2,
1247
+ fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1]
1248
+ sage: pe = f.get_defining_equations('pentagons')
1249
+ sage: f.shutdown_worker_pool()
1250
+
1251
+ .. WARNING::
1252
+
1253
+ This method is needed to initialize the worker pool using the
1254
+ necessary shared memory resources. Simply using the
1255
+ ``multiprocessing.Pool`` constructor will not work with our
1256
+ class methods.
1257
+
1258
+ .. WARNING::
1259
+
1260
+ Failure to call :meth:`shutdown_worker_pool` may result in a memory
1261
+ leak, since shared memory resources outlive the process that created
1262
+ them.
1263
+ """
1264
+ try:
1265
+ set_start_method('fork')
1266
+ except RuntimeError:
1267
+ pass
1268
+ if not hasattr(self, '_nnz'):
1269
+ self._reset_solver_state()
1270
+ # Set up shared memory resource handlers
1271
+ n_proc = cpu_count() if processes is None else processes
1272
+ self._pid_list = shared_memory.ShareableList([0] * (n_proc+1))
1273
+ pids_name = self._pid_list.shm.name
1274
+ self._solved = shared_memory.ShareableList(self._solved)
1275
+ s_name = self._solved.shm.name
1276
+ self._var_degs = shared_memory.ShareableList(self._var_degs)
1277
+ vd_name = self._var_degs.shm.name
1278
+ n = self._poly_ring.ngens()
1279
+ self._ks = KSHandler(n, self._field, use_mp=True, init_data=self._ks)
1280
+ ks_names = self._ks.shm.name
1281
+ self._shared_fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name, init_data=self._fvars)
1282
+ fvar_names = self._shared_fvars.shm.name
1283
+ # Initialize worker pool processes
1284
+ args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name)
1285
+
1286
+ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name):
1287
+ """
1288
+ Connect worker process to shared memory resources
1289
+ """
1290
+ fmats_obj = cast(fmats_id, py_object).value
1291
+ fmats_obj._solved = shared_memory.ShareableList(name=solved_name)
1292
+ fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name)
1293
+ n = fmats_obj._poly_ring.ngens()
1294
+ K = fmats_obj._field
1295
+ fmats_obj._fvars = FvarsHandler(n, K, fmats_obj._idx_to_sextuple, name=fvar_names, use_mp=n_proc, pids_name=pids_name)
1296
+ fmats_obj._ks = KSHandler(n, K, name=ks_names, use_mp=True)
1297
+
1298
+ self.pool = Pool(processes=n_proc, initializer=init, initargs=args)
1299
+ self._pid_list[0] = getpid()
1300
+ for i, p in enumerate(self.pool._pool):
1301
+ self._pid_list[i + 1] = p.pid
1302
+ # return True
1303
+
1304
+ def shutdown_worker_pool(self):
1305
+ r"""
1306
+ Shutdown the given worker pool and dispose of shared memory resources
1307
+ created when the pool was set up using :meth:`start_worker_pool`.
1308
+
1309
+ .. WARNING::
1310
+
1311
+ Failure to call this method after using :meth:`start_worker_pool`
1312
+ to create a process pool may result in a memory
1313
+ leak, since shared memory resources outlive the process that
1314
+ created them.
1315
+
1316
+ EXAMPLES::
1317
+
1318
+ sage: f = FusionRing("A1", 3).get_fmatrix(new=True)
1319
+ sage: f.start_worker_pool()
1320
+ sage: he = f.get_defining_equations('hexagons')
1321
+ sage: f.shutdown_worker_pool()
1322
+ """
1323
+ if self.pool is not None:
1324
+ self.pool.close()
1325
+ self.pool = None
1326
+ self._solved.shm.unlink()
1327
+ self._var_degs.shm.unlink()
1328
+ self._ks.shm.unlink()
1329
+ self._shared_fvars.shm.unlink()
1330
+ self._pid_list.shm.unlink()
1331
+ del self.__dict__['_shared_fvars']
1332
+
1333
+ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, mp_thresh=None):
1334
+ r"""
1335
+ Apply the given mapper to each element of the given input iterable and
1336
+ return the results (with no duplicates) in a list.
1337
+
1338
+ INPUT:
1339
+
1340
+ - ``mapper`` -- string specifying the name of a function defined in
1341
+ the ``fast_parallel_fmats_methods`` module
1342
+
1343
+ .. NOTE::
1344
+
1345
+ If ``worker_pool`` is not provided, function maps and reduces on a
1346
+ single process.
1347
+ If ``worker_pool`` is provided, the function attempts to determine
1348
+ whether it should use multiprocessing based on the length of the
1349
+ input iterable. If it can't determine the length of the input
1350
+ iterable then it uses multiprocessing with the default chunksize of
1351
+ `1` unless a chunksize is provided.
1352
+
1353
+ EXAMPLES::
1354
+
1355
+ sage: f = FusionRing("A1", 2).get_fmatrix()
1356
+ sage: f._reset_solver_state()
1357
+ sage: len(f._map_triv_reduce('get_reduced_hexagons', [(0, 1, False)]))
1358
+ 11
1359
+ sage: f.start_worker_pool()
1360
+ sage: mp_params = [(i, f.pool._processes, True) for i in range(f.pool._processes)]
1361
+ sage: len(f._map_triv_reduce('get_reduced_pentagons', mp_params, worker_pool=f.pool, chunksize=1, mp_thresh=0))
1362
+ 33
1363
+ sage: f.shutdown_worker_pool()
1364
+ """
1365
+ if mp_thresh is None:
1366
+ mp_thresh = self.mp_thresh
1367
+ # Compute multiprocessing parameters
1368
+ if worker_pool is not None:
1369
+ try:
1370
+ n = len(input_iter)
1371
+ except (TypeError, ValueError, AttributeError):
1372
+ n = mp_thresh + 1
1373
+ if chunksize is None:
1374
+ chunksize = n // (worker_pool._processes**2) + 1
1375
+ no_mp = worker_pool is None or n < mp_thresh
1376
+ # Map phase
1377
+ input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self)))
1378
+ if no_mp:
1379
+ mapped = map(executor, input_iter)
1380
+ else:
1381
+ mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=chunksize)
1382
+ # Reduce phase
1383
+ results = set()
1384
+ for child_eqns in mapped:
1385
+ if child_eqns is not None:
1386
+ results.update(child_eqns)
1387
+ results = list(results)
1388
+ return results
1389
+
1390
+ ########################
1391
+ # Equations set up #
1392
+ ########################
1393
+
1394
+ def get_orthogonality_constraints(self, output=True):
1395
+ r"""
1396
+ Get equations imposed on the F-matrix by orthogonality.
1397
+
1398
+ INPUT:
1399
+
1400
+ - ``output`` -- boolean
1401
+
1402
+ OUTPUT:
1403
+
1404
+ If ``output=True``, orthogonality constraints are returned as
1405
+ polynomial objects.
1406
+
1407
+ Otherwise, the constraints are appended to ``self.ideal_basis``.
1408
+ They are stored in the internal tuple representation. The
1409
+ ``output=False`` option is meant mostly for internal use by the
1410
+ F-matrix solver.
1411
+
1412
+ EXAMPLES::
1413
+
1414
+ sage: f = FusionRing("B4", 1).get_fmatrix()
1415
+ sage: f.get_orthogonality_constraints()
1416
+ [fx0^2 - 1,
1417
+ fx1^2 - 1,
1418
+ fx2^2 - 1,
1419
+ fx3^2 - 1,
1420
+ fx4^2 - 1,
1421
+ fx5^2 - 1,
1422
+ fx6^2 - 1,
1423
+ fx7^2 - 1,
1424
+ fx8^2 - 1,
1425
+ fx9^2 - 1,
1426
+ fx10^2 + fx12^2 - 1,
1427
+ fx10*fx11 + fx12*fx13,
1428
+ fx10*fx11 + fx12*fx13,
1429
+ fx11^2 + fx13^2 - 1]
1430
+ """
1431
+ eqns = []
1432
+ for tup in product(self._FR.basis(), repeat=4):
1433
+ mat = self.fmatrix(*tup)
1434
+ eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients())
1435
+ if output:
1436
+ return eqns
1437
+ self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns])
1438
+
1439
+ def get_defining_equations(self, option, output=True):
1440
+ r"""
1441
+ Get the equations defining the ideal generated by the hexagon or
1442
+ pentagon relations.
1443
+
1444
+ INPUT:
1445
+
1446
+ - ``option`` -- string determining equations to be set up:
1447
+
1448
+ * ``'hexagons'`` -- get equations imposed on the F-matrix by
1449
+ the hexagon relations in the definition of a braided category
1450
+
1451
+ * ``'pentagons'`` -- get equations imposed on the F-matrix by
1452
+ the pentagon relations in the definition of a monoidal category
1453
+
1454
+ - ``output`` -- boolean (default: ``True``); whether
1455
+ results should be returned, where the equations will be polynomials.
1456
+ Otherwise, the constraints are appended to ``self.ideal_basis``.
1457
+ Constraints are stored in the internal tuple representation. The
1458
+ ``output=False`` option is meant only for internal use by the
1459
+ F-matrix solver. When computing the hexagon equations with the
1460
+ ``output=False`` option, the initial state of the F-symbols is used.
1461
+
1462
+ .. NOTE::
1463
+
1464
+ To set up the defining equations using parallel processing,
1465
+ use :meth:`start_worker_pool` to initialize multiple processes
1466
+ *before* calling this method.
1467
+
1468
+ EXAMPLES::
1469
+
1470
+ sage: f = FusionRing("B2", 1).get_fmatrix()
1471
+ sage: sorted(f.get_defining_equations('hexagons'))
1472
+ [fx7 + 1,
1473
+ fx6 - 1,
1474
+ fx2 + 1,
1475
+ fx0 - 1,
1476
+ fx11*fx12 + (-zeta32^8)*fx13^2 + (zeta32^12)*fx13,
1477
+ fx10*fx12 + (-zeta32^8)*fx12*fx13 + (zeta32^4)*fx12,
1478
+ fx10*fx11 + (-zeta32^8)*fx11*fx13 + (zeta32^4)*fx11,
1479
+ fx10^2 + (-zeta32^8)*fx11*fx12 + (-zeta32^12)*fx10,
1480
+ fx4*fx9 + fx7,
1481
+ fx3*fx8 - fx6,
1482
+ fx1*fx5 + fx2]
1483
+ sage: pe = f.get_defining_equations('pentagons')
1484
+ sage: len(pe)
1485
+ 33
1486
+ """
1487
+ if not hasattr(self, '_nnz'):
1488
+ self._reset_solver_state()
1489
+ n_proc = self.pool._processes if self.pool is not None else 1
1490
+ params = [(child_id, n_proc, output) for child_id in range(n_proc)]
1491
+ eqns = self._map_triv_reduce('get_reduced_' + option, params,
1492
+ worker_pool=self.pool, chunksize=1,
1493
+ mp_thresh=0)
1494
+ if output:
1495
+ F = self._field
1496
+ for i, eq_tup in enumerate(eqns):
1497
+ eqns[i] = _unflatten_coeffs(F, eq_tup)
1498
+ return [self._tup_to_fpoly(p) for p in eqns]
1499
+ self.ideal_basis.extend(eqns)
1500
+
1501
+ ############################
1502
+ # Equations processing #
1503
+ ############################
1504
+
1505
+ def _tup_to_fpoly(self, eq_tup):
1506
+ r"""
1507
+ Assemble a polynomial object from its tuple representation.
1508
+
1509
+ .. WARNING::
1510
+
1511
+ This method avoids implicit casting when constructing a
1512
+ polynomial object, and may therefore lead to SEGFAULTs.
1513
+ It is meant for internal use by the F-matrix solver.
1514
+
1515
+ This method is a left inverse of
1516
+ :meth:`sage.algebras.fusion_rings.poly_tup_engine.poly_to_tup`.
1517
+
1518
+ EXAMPLES::
1519
+
1520
+ sage: f = FusionRing("C3", 1).get_fmatrix()
1521
+ sage: f.start_worker_pool()
1522
+ sage: he = f.get_defining_equations('hexagons')
1523
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup
1524
+ sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he)
1525
+ True
1526
+ sage: f.shutdown_worker_pool()
1527
+ """
1528
+ return _tup_to_poly(eq_tup, parent=self._poly_ring)
1529
+
1530
+ def _update_reduction_params(self, eqns=None):
1531
+ r"""
1532
+ Update reduction parameters that are solver state attributes.
1533
+
1534
+ EXAMPLES::
1535
+
1536
+ sage: f = FusionRing("A1", 3).get_fmatrix()
1537
+ sage: f._reset_solver_state()
1538
+ sage: f.get_orthogonality_constraints(output=False)
1539
+ sage: f.start_worker_pool()
1540
+ sage: f.get_defining_equations('hexagons', output=False)
1541
+ sage: f.ideal_basis = f._par_graph_gb(verbose=False)
1542
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup
1543
+ sage: f.ideal_basis.sort(key=poly_tup_sortkey)
1544
+ sage: f.mp_thresh = 0
1545
+ sage: f._fvars = f._shared_fvars
1546
+ sage: f._triangular_elim(verbose=False) # indirect doctest
1547
+ sage: f.ideal_basis
1548
+ []
1549
+ sage: f.shutdown_worker_pool()
1550
+ """
1551
+ if eqns is None:
1552
+ eqns = self.ideal_basis
1553
+ self._ks.update(eqns)
1554
+ for i, d in enumerate(get_variables_degrees(eqns, self._poly_ring.ngens())):
1555
+ self._var_degs[i] = d
1556
+ self._nnz = self._get_known_nonz()
1557
+ self._kp = compute_known_powers(self._var_degs, self._get_known_vals(), self._field.one())
1558
+
1559
+ def _triangular_elim(self, eqns=None, verbose=True):
1560
+ r"""
1561
+ Perform triangular elimination of linear terms in two-term equations
1562
+ until no such terms exist.
1563
+
1564
+ .. NOTE::
1565
+
1566
+ For optimal usage of triangular elimination, pass in a
1567
+ *sorted* list of equations.
1568
+
1569
+ EXAMPLES::
1570
+
1571
+ sage: f = FusionRing("D3", 1).get_fmatrix()
1572
+ sage: f.get_defining_equations('hexagons', output=False)
1573
+ sage: f.get_orthogonality_constraints(output=False)
1574
+ sage: gb = f._par_graph_gb(verbose=False)
1575
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup
1576
+ sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey)
1577
+ sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler
1578
+ sage: n = f._poly_ring.ngens()
1579
+ sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars)
1580
+ sage: f._triangular_elim()
1581
+ Elimination epoch completed... 0 eqns remain in ideal basis
1582
+ sage: f.ideal_basis
1583
+ []
1584
+ """
1585
+ if eqns is None:
1586
+ eqns = self.ideal_basis
1587
+
1588
+ while True:
1589
+ linear_terms_exist = _solve_for_linear_terms(self, eqns)
1590
+ if not linear_terms_exist:
1591
+ break
1592
+ _backward_subs(self)
1593
+ # Compute new reduction params and update eqns
1594
+ self._update_reduction_params(eqns=eqns)
1595
+ if self.pool is not None and len(eqns) > self.mp_thresh:
1596
+ n = self.pool._processes
1597
+ chunks = [[] for i in range(n)]
1598
+ for i, eq_tup in enumerate(eqns):
1599
+ chunks[i % n].append(eq_tup)
1600
+ eqns = chunks
1601
+ else:
1602
+ eqns = [eqns]
1603
+ eqns = self._map_triv_reduce('update_reduce', eqns, worker_pool=self.pool, mp_thresh=0)
1604
+ eqns.sort(key=poly_tup_sortkey)
1605
+ if verbose:
1606
+ print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns)))
1607
+ self.ideal_basis = eqns
1608
+
1609
+ #####################
1610
+ # Graph methods #
1611
+ #####################
1612
+
1613
+ def equations_graph(self, eqns=None):
1614
+ r"""
1615
+ Construct a graph corresponding to the given equations.
1616
+
1617
+ Every node corresponds to a variable and nodes are connected when
1618
+ the corresponding variables appear together in an equation.
1619
+
1620
+ INPUT:
1621
+
1622
+ - ``eqns`` -- list of polynomials
1623
+
1624
+ Each polynomial is either an object in the ring returned by
1625
+ :meth:`get_poly_ring` or it is a tuple of pairs representing
1626
+ a polynomial using the internal representation.
1627
+
1628
+ If no list of equations is passed, the graph is built from the
1629
+ polynomials in ``self.ideal_basis``. In this case the method assumes
1630
+ the internal representation of a polynomial as a tuple of pairs is
1631
+ used.
1632
+
1633
+ This method is crucial to :meth:`find_orthogonal_solution`. The
1634
+ hexagon equations, obtained using :meth:`get_defining_equations`,
1635
+ define a disconnected graph that breaks up into many small components.
1636
+ The :meth:`find_orthogonal_solution` solver exploits this when
1637
+ undertaking a Groebner basis computation.
1638
+
1639
+ OUTPUT:
1640
+
1641
+ A ``Graph`` object. If a list of polynomial objects was given,
1642
+ the set of nodes in the output graph is the subset polynomial
1643
+ ring generators appearing in the equations.
1644
+
1645
+ If the internal representation was used, the set of nodes is
1646
+ the subset of indices corresponding to polynomial ring generators.
1647
+ This option is meant for internal use by the F-matrix solver.
1648
+
1649
+ EXAMPLES::
1650
+
1651
+ sage: f = FusionRing("A3", 1).get_fmatrix()
1652
+ sage: f.get_poly_ring().ngens()
1653
+ 27
1654
+ sage: he = f.get_defining_equations('hexagons')
1655
+ sage: graph = f.equations_graph(he)
1656
+ sage: graph.connected_components_sizes()
1657
+ [6, 3, 3, 3, 3, 3, 3, 1, 1, 1]
1658
+ """
1659
+ if eqns is None:
1660
+ eqns = self.ideal_basis
1661
+
1662
+ G = Graph()
1663
+ if not eqns:
1664
+ return G
1665
+
1666
+ # Eqns could be a list of poly objects or poly tuples stored in internal repn
1667
+ if isinstance(eqns[0], tuple):
1668
+ G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)])
1669
+ else:
1670
+ G.add_vertices([x for eq in eqns for x in eq.variables()])
1671
+ for eq in eqns:
1672
+ # Eqns could be a list of poly objects or poly tuples stored in internal repn
1673
+ if isinstance(eq, tuple):
1674
+ s = list(variables(eq))
1675
+ else:
1676
+ s = list(eq.variables())
1677
+ for x in s:
1678
+ for y in s:
1679
+ if y != x:
1680
+ G.add_edge(x, y)
1681
+ return G
1682
+
1683
+ def _partition_eqns(self, eqns=None, verbose=True):
1684
+ r"""
1685
+ Partition equations corresponding to edges in a disconnected graph.
1686
+
1687
+ OUTPUT:
1688
+
1689
+ This method returns a dictionary of (c, e) pairs, where
1690
+ c is a tuple denoting a connected component in the graph produced
1691
+ by calling :meth:`equations_graph` with the given ``eqns`` and
1692
+ e is a list of all equations with variables in c.
1693
+
1694
+ EXAMPLES::
1695
+
1696
+ sage: f = FusionRing("C2", 1).get_fmatrix()
1697
+ sage: f.get_defining_equations('hexagons', output=False)
1698
+ sage: partition = f._partition_eqns()
1699
+ Partitioned 11 equations into 5 components of size:
1700
+ [4, 3, 3, 3, 1]
1701
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import variables
1702
+ sage: for c, e in partition.items():
1703
+ ....: assert set(v for eq_tup in e for v in variables(eq_tup)) == set(c)
1704
+ sage: vars_in_partition = set()
1705
+ sage: eqns_in_partition = set()
1706
+ sage: for c, e in partition.items():
1707
+ ....: vars_in_partition.update(c)
1708
+ ....: eqns_in_partition.update(e)
1709
+ sage: vars_in_partition == set(v for eq_tup in f.ideal_basis for v in variables(eq_tup))
1710
+ True
1711
+ sage: eqns_in_partition == set(f.ideal_basis)
1712
+ True
1713
+ sage: from itertools import product
1714
+ sage: for e1, e2 in product(partition.values(), repeat=2):
1715
+ ....: assert e1 == e2 or set(e1).isdisjoint(set(e2))
1716
+ """
1717
+ if eqns is None:
1718
+ eqns = self.ideal_basis
1719
+ graph = self.equations_graph(eqns)
1720
+ partition = {tuple(c): [] for c in graph.connected_components(sort=True)}
1721
+ for eq_tup in eqns:
1722
+ partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0], sort=True))].append(eq_tup)
1723
+ if verbose:
1724
+ print("Partitioned {} equations into {} components of size:".format(len(eqns), graph.connected_components_number()))
1725
+ print(graph.connected_components_sizes())
1726
+ return partition
1727
+
1728
+ def _par_graph_gb(self, eqns=None, term_order='degrevlex', largest_comp=45, verbose=True):
1729
+ r"""
1730
+ Compute a Groebner basis for a list of equations partitioned
1731
+ according to their corresponding graph.
1732
+
1733
+ .. NOTE::
1734
+
1735
+ If the graph has more than 50 components, this method computes the
1736
+ Groebner basis in parallel when a ``worker_pool`` is provided.
1737
+
1738
+ This method will refuse to find a Groebner basis for a component
1739
+ of size larger than 60, since such a calculation does not seem to
1740
+ terminate.
1741
+
1742
+ EXAMPLES::
1743
+
1744
+ sage: f = FusionRing("F4", 1).get_fmatrix()
1745
+ sage: f._reset_solver_state()
1746
+ sage: f.get_orthogonality_constraints(output=False)
1747
+ sage: f.start_worker_pool()
1748
+ sage: f.get_defining_equations('hexagons', output=False)
1749
+ sage: gb = f._par_graph_gb()
1750
+ Partitioned 10 equations into 2 components of size:
1751
+ [4, 1]
1752
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs
1753
+ sage: ret = [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb]
1754
+ sage: ret.sort(); ret
1755
+ [fx4 + (-zeta80^24 + zeta80^16),
1756
+ fx2 - fx3,
1757
+ fx1 + (zeta80^24 - zeta80^16),
1758
+ fx0 - 1,
1759
+ fx3^2 + (zeta80^24 - zeta80^16)]
1760
+ sage: f.shutdown_worker_pool()
1761
+ """
1762
+ if eqns is None:
1763
+ eqns = self.ideal_basis
1764
+ small_comps = []
1765
+ temp_eqns = []
1766
+ for comp, comp_eqns in self._partition_eqns(eqns=eqns, verbose=verbose).items():
1767
+ # Check if component is too large to process
1768
+ if len(comp) > largest_comp:
1769
+ temp_eqns.extend(comp_eqns)
1770
+ else:
1771
+ small_comps.append(comp_eqns)
1772
+ input_iter = zip_longest(small_comps, [], fillvalue=term_order)
1773
+ small_comp_gb = self._map_triv_reduce('compute_gb', input_iter, worker_pool=self.pool, chunksize=1, mp_thresh=50)
1774
+ ret = small_comp_gb + temp_eqns
1775
+ return ret
1776
+
1777
+ def _get_component_variety(self, var, eqns):
1778
+ r"""
1779
+ Translate equations in each connected component to smaller polynomial
1780
+ rings so we can call built-in variety method.
1781
+
1782
+ INPUT:
1783
+
1784
+ - ``var`` -- list of variable indices
1785
+ - ``eqns`` -- list of polynomial equations in the internal
1786
+ tuple of pairs representation
1787
+
1788
+ EXAMPLES::
1789
+
1790
+ sage: f = FusionRing("G2", 2).get_fmatrix(new=True)
1791
+ sage: f.start_worker_pool()
1792
+ sage: f.get_defining_equations('hexagons', output=False) # long time
1793
+ sage: f.shutdown_worker_pool()
1794
+ sage: partition = f._partition_eqns() # long time
1795
+ Partitioned 327 equations into 35 components of size:
1796
+ [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
1797
+ 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1]
1798
+ sage: c = (216, 292, 319)
1799
+ sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup
1800
+ sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time
1801
+ sage: f._get_component_variety(c, eqns) # long time
1802
+ [{216: -1, 292: -1, 319: 1}]
1803
+ """
1804
+ # Define smaller poly ring in component vars
1805
+ R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex')
1806
+
1807
+ # Zip tuples into R and compute Groebner basis
1808
+ idx_map = {old: new for new, old in enumerate(sorted(var))}
1809
+ nvars = len(var)
1810
+ eqns = [_unflatten_coeffs(self._field, eq_tup) for eq_tup in eqns]
1811
+ polys = [_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R) for eq_tup in eqns]
1812
+ var_in_R = Ideal(sorted(polys)).variety(ring=AA)
1813
+
1814
+ # Change back to fmats poly ring and append to temp_eqns
1815
+ inv_idx_map = {v: k for k, v in idx_map.items()}
1816
+ return [{inv_idx_map[i]: value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R]
1817
+
1818
+ #######################
1819
+ # Solution method #
1820
+ #######################
1821
+
1822
+ # TODO: this can probably be improved by constructing a set of defining polynomials
1823
+ # and checking, one by one, if it's irreducible over the current field.
1824
+ # If it is, we construct an extension. Perhaps it's best to go one by one here...
1825
+ def attempt_number_field_computation(self):
1826
+ r"""
1827
+ Based on the ``CartanType`` of ``self`` and data
1828
+ known on March 17, 2021, determine whether to attempt
1829
+ to find a :func:`NumberField` containing all the F-symbols.
1830
+
1831
+ This method is used by :meth:`find_orthogonal_solution`
1832
+ to determine a field containing all F-symbols.
1833
+ See :meth:`field` and :meth:`get_non_cyclotomic_roots`.
1834
+
1835
+ For certain :class:`fusion rings <FusionRing>`, the number field
1836
+ computation does not terminate in reasonable time.
1837
+ In these cases, we report F-symbols as elements
1838
+ of the :class:`QQbar<AlgebraicField>`.
1839
+
1840
+ EXAMPLES::
1841
+
1842
+ sage: f = FusionRing("F4", 2).get_fmatrix()
1843
+ sage: f.attempt_number_field_computation()
1844
+ False
1845
+ sage: f = FusionRing("G2", 1).get_fmatrix()
1846
+ sage: f.attempt_number_field_computation()
1847
+ True
1848
+
1849
+ .. NOTE::
1850
+
1851
+ In certain cases, F-symbols are found in the associated
1852
+ :class:`FusionRing`'s cyclotomic field and a
1853
+ :func:`NumberField` computation is not needed. In these
1854
+ cases this method returns ``True`` but the
1855
+ :meth:`find_orthogonal_solution` solver does *not*
1856
+ undertake a :func:`NumberField` computation.
1857
+ """
1858
+ ct = self._FR.cartan_type()
1859
+ k = self._FR._k
1860
+ # Don't try when k is large and odd for SU(2)_k
1861
+ if ct.letter == 'A':
1862
+ if ct.n == 1 and k >= 9 and k % 2:
1863
+ return False
1864
+ if ct.letter == 'C':
1865
+ if ct.n >= 9 and ct.n % 2 and k == 1:
1866
+ return False
1867
+ if ct.letter == 'E':
1868
+ if ct.n < 8 and k == 2:
1869
+ return False
1870
+ if ct.n == 8 and k == 3:
1871
+ return False
1872
+ if ct.letter == 'F' and k == 2:
1873
+ return False
1874
+ if ct.letter == 'G' and k == 2:
1875
+ return False
1876
+ return True
1877
+
1878
+ def _get_explicit_solution(self, eqns=None, verbose=True):
1879
+ r"""
1880
+ Construct an explicit solution of ``self``.
1881
+
1882
+ When this method is called, the solution is already found in
1883
+ terms of Groeber basis. A few degrees of freedom remain.
1884
+ By specializing the free variables and back substituting, a
1885
+ solution in the base field is now obtained.
1886
+
1887
+ EXAMPLES::
1888
+
1889
+ sage: f = FusionRing("A1", 3).get_fmatrix() # indirect doctest
1890
+ sage: f.find_orthogonal_solution() # long time
1891
+ Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables...
1892
+ Set up 134 hex and orthogonality constraints...
1893
+ Partitioned 134 equations into 17 components of size:
1894
+ [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1]
1895
+ Elimination epoch completed... 10 eqns remain in ideal basis
1896
+ Elimination epoch completed... 0 eqns remain in ideal basis
1897
+ Hex elim step solved for 51 / 71 variables
1898
+ Set up 121 reduced pentagons...
1899
+ Elimination epoch completed... 18 eqns remain in ideal basis
1900
+ Elimination epoch completed... 5 eqns remain in ideal basis
1901
+ Pent elim step solved for 64 / 71 variables
1902
+ Partitioned 5 equations into 1 components of size:
1903
+ [4]
1904
+ Elimination epoch completed... 0 eqns remain in ideal basis
1905
+ Partitioned 6 equations into 6 components of size:
1906
+ [1, 1, 1, 1, 1, 1]
1907
+ Computing appropriate NumberField...
1908
+ """
1909
+ if eqns is None:
1910
+ eqns = self.ideal_basis
1911
+ # Don't add square fixers when warm starting from a late-stage checkpoint
1912
+ if self._chkpt_status < 5:
1913
+ n = self._poly_ring.ngens()
1914
+ one = self._field.one()
1915
+ for fx, rhs in self._ks.items():
1916
+ if not self._solved[fx]:
1917
+ lt = (ETuple({fx: 2}, n), one)
1918
+ eqns.append((lt, (ETuple({}, n), -rhs)))
1919
+ eqns_partition = self._partition_eqns(verbose=verbose)
1920
+
1921
+ F = self._field
1922
+ R = F['x']
1923
+ numeric_fvars = {}
1924
+ non_cyclotomic_roots = []
1925
+ must_change_base_field = False
1926
+ phi = F.hom([F.gen()], F)
1927
+ for comp, part in eqns_partition.items():
1928
+ # If component has only one equation in a single variable, get a root
1929
+ if len(comp) == 1 and len(part) == 1:
1930
+ # Attempt to find cyclotomic root
1931
+ univ_poly = tup_to_univ_poly(part[0], R)
1932
+ roots = univ_poly.roots(multiplicities=False)
1933
+ if roots:
1934
+ numeric_fvars[comp[0]] = roots[0]
1935
+ else:
1936
+ # A real solution is preferred
1937
+ roots = univ_poly.roots(ring=AA, multiplicities=False)
1938
+ if not roots:
1939
+ roots = univ_poly.roots(ring=QQbar, multiplicities=False)
1940
+ non_cyclotomic_roots.append((comp[0], roots[0]))
1941
+ must_change_base_field = True
1942
+ # Otherwise, compute the component variety and select a point to obtain a numerical solution
1943
+ else:
1944
+ sols = self._get_component_variety(comp, part)
1945
+ for fx, rhs in sols[0].items():
1946
+ non_cyclotomic_roots.append((fx, rhs))
1947
+ must_change_base_field = True
1948
+
1949
+ if must_change_base_field:
1950
+ # Attempt to compute smallest number field containing all the F-symbols
1951
+ # If calculation takes too long, we use QQbar as the base field
1952
+ if self.attempt_number_field_computation():
1953
+ if verbose:
1954
+ print("Computing appropriate NumberField...")
1955
+ roots = [self._FR.field().gen()] + [r[1] for r in non_cyclotomic_roots]
1956
+ self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots, minimal=True)
1957
+ else:
1958
+ self._field = QQbar
1959
+ bf_elts = [self._qqbar_embedding(F.gen())]
1960
+ bf_elts += [rhs for fx, rhs in non_cyclotomic_roots]
1961
+ self._qqbar_embedding = lambda x: x
1962
+ self._non_cyc_roots = bf_elts[1:]
1963
+
1964
+ # Embed cyclotomic field into newly constructed base field
1965
+ cyc_gen_as_bf_elt = bf_elts.pop(0)
1966
+ phi = self._FR.field().hom([cyc_gen_as_bf_elt], self._field)
1967
+ self._coerce_map_from_cyc_field = phi
1968
+ numeric_fvars = {k: phi(v) for k, v in numeric_fvars.items()}
1969
+ for i, elt in enumerate(bf_elts):
1970
+ numeric_fvars[non_cyclotomic_roots[i][0]] = elt
1971
+ # Update polynomial ring
1972
+ self._poly_ring = self._poly_ring.change_ring(self._field)
1973
+
1974
+ # Ensure all F-symbols are known
1975
+ for fx in numeric_fvars:
1976
+ self._solved[fx] = True
1977
+ nvars = self._poly_ring.ngens()
1978
+ assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]])
1979
+
1980
+ # Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form)
1981
+ self._fvars = {sextuple: apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()}
1982
+ for fx, rhs in numeric_fvars.items():
1983
+ self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({}, nvars), rhs), )
1984
+ _backward_subs(self, flatten=False)
1985
+ self._fvars = {sextuple: constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()}
1986
+
1987
+ # Update base field attributes
1988
+ self._FR._field = self.field()
1989
+ self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field()
1990
+ if self._FR._basecoer:
1991
+ self._FR.r_matrix.clear_cache()
1992
+
1993
+ def find_orthogonal_solution(self, checkpoint=False, save_results='', warm_start='', use_mp=True, verbose=True):
1994
+ r"""
1995
+ Solve the hexagon and pentagon relations, along with
1996
+ orthogonality constraints, to evaluate an orthogonal F-matrix.
1997
+
1998
+ INPUT:
1999
+
2000
+ - ``checkpoint`` -- boolean (default: ``False``); whether
2001
+ the computation should be checkpointed. Depending on the associated
2002
+ ``CartanType``, the computation may take hours to complete. For
2003
+ large examples, checkpoints are recommended. This method supports
2004
+ "warm" starting, so the calculation may be resumed from a checkpoint,
2005
+ using the ``warm_start`` option.
2006
+
2007
+ Checkpoints store necessary state in the pickle file
2008
+ ``"fmatrix_solver_checkpoint_" + key + ".pickle"``, where ``key``
2009
+ is the result of :meth:`get_fr_str`.
2010
+
2011
+ Checkpoint pickles are automatically deleted when the solver exits
2012
+ a successful run.
2013
+
2014
+ - ``save_results`` -- (optional) a string indicating the name of a
2015
+ pickle file in which to store calculated F-symbols for later use.
2016
+
2017
+ If ``save_results`` is not provided (default), F-matrix results
2018
+ are not stored to file.
2019
+
2020
+ The F-symbols may be saved to file after running the solver using
2021
+ :meth:`save_fvars`.
2022
+
2023
+ - ``warm_start`` -- (optional) a string indicating the name of a pickle
2024
+ file containing checkpointed solver state. This file must have been
2025
+ produced by a previous call to the solver using the ``checkpoint``
2026
+ option.
2027
+
2028
+ If no file name is provided, the calculation begins from scratch.
2029
+
2030
+ - ``use_mp`` -- boolean (default: ``True``); whether to use
2031
+ multiprocessing to speed up calculation. The default value
2032
+ ``True`` is highly recommended, since parallel processing yields
2033
+ results much more quickly.
2034
+
2035
+ - ``verbose`` -- boolean (default: ``True``); whether the
2036
+ solver should print out intermediate progress reports
2037
+
2038
+ OUTPUT:
2039
+
2040
+ This method returns ``None``. If the solver runs successfully, the
2041
+ results may be accessed through various methods, such as
2042
+ :meth:`get_fvars`, :meth:`fmatrix`, :meth:`fmat`, etc.
2043
+
2044
+ EXAMPLES::
2045
+
2046
+ sage: f = FusionRing("B5", 1).get_fmatrix(fusion_label='b', inject_variables=True)
2047
+ creating variables fx1..fx14
2048
+ Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13
2049
+ sage: f.find_orthogonal_solution()
2050
+ Computing F-symbols for The Fusion Ring of Type B5 and level 1 with Integer Ring coefficients with 14 variables...
2051
+ Set up 25 hex and orthogonality constraints...
2052
+ Partitioned 25 equations into 5 components of size:
2053
+ [4, 3, 3, 3, 1]
2054
+ Elimination epoch completed... 0 eqns remain in ideal basis
2055
+ Hex elim step solved for 10 / 14 variables
2056
+ Set up 7 reduced pentagons...
2057
+ Elimination epoch completed... 0 eqns remain in ideal basis
2058
+ Pent elim step solved for 12 / 14 variables
2059
+ Partitioned 0 equations into 0 components of size:
2060
+ []
2061
+ Partitioned 2 equations into 2 components of size:
2062
+ [1, 1]
2063
+ sage: f.fmatrix(b2, b2, b2, b2)
2064
+ [ 1/2*zeta80^30 - 1/2*zeta80^10 -1/2*zeta80^30 + 1/2*zeta80^10]
2065
+ [ 1/2*zeta80^30 - 1/2*zeta80^10 1/2*zeta80^30 - 1/2*zeta80^10]
2066
+ sage: f.fmat(b2, b2, b2, b2, b0, b1)
2067
+ -1/2*zeta80^30 + 1/2*zeta80^10
2068
+
2069
+ Every F-matrix `F^{a, b, c}_d` is orthogonal and in many cases real.
2070
+ We may use :meth:`fmats_are_orthogonal` and :meth:`fvars_are_real`
2071
+ to obtain correctness certificates.
2072
+
2073
+ EXAMPLES::
2074
+
2075
+ sage: f.fmats_are_orthogonal()
2076
+ True
2077
+
2078
+ In any case, the F-symbols are obtained as elements of the associated
2079
+ :class:`FusionRing`'s
2080
+ :class:`Cyclotomic field<sage.rings.number_field.number_field.CyclotomicFieldFactory>`,
2081
+ a computed :func:`NumberField`, or :class:`QQbar<AlgebraicField>`.
2082
+ Currently, the field containing the F-symbols is determined based
2083
+ on the ``CartanType`` associated to ``self``.
2084
+
2085
+ .. SEEALSO::
2086
+
2087
+ :meth:`attempt_number_field_computation`
2088
+ """
2089
+ if self._poly_ring.ngens() == 0:
2090
+ return
2091
+ self._reset_solver_state()
2092
+
2093
+ # Resume computation from checkpoint
2094
+ if warm_start:
2095
+ self._restore_state(warm_start)
2096
+ # Loading from a pickle with solved F-symbols
2097
+ if self._chkpt_status > 5:
2098
+ return
2099
+ if use_mp:
2100
+ self.start_worker_pool()
2101
+ if verbose:
2102
+ print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens()))
2103
+
2104
+ if self._chkpt_status < 1:
2105
+ # Set up hexagon equations and orthogonality constraints
2106
+ self.get_orthogonality_constraints(output=False)
2107
+ self.get_defining_equations('hexagons', output=False)
2108
+ # Report progress
2109
+ if verbose:
2110
+ print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis)))
2111
+
2112
+ # Unzip _fvars and link to shared_memory structure if using multiprocessing
2113
+ if use_mp: # and loads_shared_memory:
2114
+ self._fvars = self._shared_fvars
2115
+ else:
2116
+ n = self._poly_ring.ngens()
2117
+ self._fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, init_data=self._fvars)
2118
+ self._checkpoint(checkpoint, 1, verbose=verbose)
2119
+
2120
+ if self._chkpt_status < 2:
2121
+ # Set up equations graph. Find GB for each component in parallel. Eliminate variables
2122
+ self.ideal_basis = self._par_graph_gb(verbose=verbose)
2123
+ self.ideal_basis.sort(key=poly_tup_sortkey)
2124
+ self._triangular_elim(verbose=verbose)
2125
+ # Report progress
2126
+ if verbose:
2127
+ print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens())))
2128
+ self._checkpoint(checkpoint, 2, verbose=verbose)
2129
+
2130
+ if self._chkpt_status < 3:
2131
+ # Set up pentagon equations in parallel
2132
+ self.get_defining_equations('pentagons', output=False)
2133
+ # Report progress
2134
+ if verbose:
2135
+ print("Set up {} reduced pentagons...".format(len(self.ideal_basis)))
2136
+ self._checkpoint(checkpoint, 3, verbose=verbose)
2137
+
2138
+ if self._chkpt_status < 4:
2139
+ # Simplify and eliminate variables
2140
+ self.ideal_basis.sort(key=poly_tup_sortkey)
2141
+ self._triangular_elim(verbose=verbose)
2142
+ # Report progress
2143
+ if verbose:
2144
+ print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens())))
2145
+ self._checkpoint(checkpoint, 4, verbose=verbose)
2146
+
2147
+ # Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change
2148
+
2149
+ # Set up new equations graph and compute variety for each component
2150
+ if self._chkpt_status < 5:
2151
+ self.ideal_basis = self._par_graph_gb(term_order='lex', verbose=verbose)
2152
+ self.ideal_basis.sort(key=poly_tup_sortkey)
2153
+ self._triangular_elim(verbose=verbose)
2154
+ self._checkpoint(checkpoint, 5, verbose=verbose)
2155
+ self.shutdown_worker_pool()
2156
+
2157
+ # Find numeric values for each F-symbol
2158
+ self._get_explicit_solution(verbose=verbose)
2159
+ # The calculation was successful, so we may delete checkpoints
2160
+ self._chkpt_status = 7
2161
+ self.clear_equations()
2162
+ if checkpoint:
2163
+ remove("fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle")
2164
+ if save_results:
2165
+ self.save_fvars(save_results)
2166
+
2167
+ #########################
2168
+ # Cyclotomic method #
2169
+ #########################
2170
+
2171
+ def _fix_gauge(self, algorithm=""):
2172
+ r"""
2173
+ Fix the gauge by forcing F-symbols not already fixed to equal `1`.
2174
+
2175
+ .. NOTE::
2176
+
2177
+ This method should be used *after* adding hexagon and pentagon
2178
+ equations to ``self.ideal_basis``.
2179
+
2180
+ EXAMPLES::
2181
+
2182
+ sage: f = FusionRing("A3", 1).get_fmatrix()
2183
+ sage: f._reset_solver_state() # long time
2184
+ sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} # long time
2185
+ sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time
2186
+ sage: f.ideal_basis = set(Ideal(eqns).groebner_basis()) # long time
2187
+ sage: _, _ = f._substitute_degree_one() # long time
2188
+ sage: f._fix_gauge() # long time
2189
+ adding equation... fx1 - 1
2190
+ adding equation... fx18 - 1
2191
+ adding equation... fx21 - 1
2192
+ """
2193
+ while not all(self._solved):
2194
+ # Get a variable that has not been fixed
2195
+ # In ascending index order, for consistent results
2196
+ for i, var in enumerate(self._poly_ring.gens()):
2197
+ if not self._solved[i]:
2198
+ break
2199
+
2200
+ # Fix var = 1, substitute, and solve equations
2201
+ self.ideal_basis.add(var - 1)
2202
+ print("adding equation...", var - 1)
2203
+ self.ideal_basis = set(Ideal(list(self.ideal_basis)).groebner_basis(algorithm=algorithm))
2204
+ self._substitute_degree_one()
2205
+ self._update_equations()
2206
+
2207
+ def _substitute_degree_one(self, eqns=None):
2208
+ r"""
2209
+ Substitute known value from linear univariate polynomial and
2210
+ solve, following [Bond2007]_ p.37, for two-term linear equation
2211
+ for one of the variables. See also [Ab2022]_.
2212
+
2213
+ EXAMPLES::
2214
+
2215
+ sage: fr = FusionRing("D3", 1)
2216
+ sage: f = fr.get_fmatrix(inject_variables=True, new=True)
2217
+ creating variables fx1..fx27
2218
+ Defining fx0, ..., fx26
2219
+ sage: f._reset_solver_state()
2220
+ sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()}
2221
+ sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9]
2222
+ sage: _, _ = f._substitute_degree_one()
2223
+ sage: f._fvars[f._var_to_sextuple[fx0]]
2224
+ 8
2225
+ sage: f._fvars[f._var_to_sextuple[fx4]]
2226
+ -fx9
2227
+ """
2228
+ if eqns is None:
2229
+ eqns = self.ideal_basis
2230
+
2231
+ new_knowns = set()
2232
+ useless = set()
2233
+ for eq in eqns:
2234
+ if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved:
2235
+ self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc()
2236
+ # Add variable to set of known values and remove this equation
2237
+ new_knowns.add(eq.lm())
2238
+ useless.add(eq)
2239
+
2240
+ # Update fvars depending on other variables
2241
+ for idx, fx in enumerate(self._poly_ring.gens()):
2242
+ if fx in new_knowns:
2243
+ self._solved[idx] = fx
2244
+ for sextuple, rhs in self._fvars.items():
2245
+ d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved}
2246
+ if d:
2247
+ self._fvars[sextuple] = rhs.subs(d)
2248
+ return new_knowns, useless
2249
+
2250
+ def _update_equations(self):
2251
+ r"""
2252
+ Perform backward substitution on equations in ``self.ideal_basis``.
2253
+
2254
+ EXAMPLES::
2255
+
2256
+ sage: fr = FusionRing("D3", 1)
2257
+ sage: f = fr.get_fmatrix(inject_variables=True, new=True)
2258
+ creating variables fx1..fx27
2259
+ Defining fx0, ..., fx26
2260
+ sage: f._reset_solver_state()
2261
+ sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()}
2262
+ sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2]
2263
+ sage: _, _ = f._substitute_degree_one()
2264
+ sage: f._update_equations()
2265
+ sage: f.ideal_basis
2266
+ {fx3}
2267
+ """
2268
+ special_values = {known: self._fvars[self._var_to_sextuple[known]] for known in self._solved if known}
2269
+ self.ideal_basis = {eq.subs(special_values) for eq in self.ideal_basis}
2270
+ self.ideal_basis.discard(0)
2271
+
2272
+ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, output=False):
2273
+ r"""
2274
+ Solve the hexagon and pentagon relations to evaluate the F-matrix.
2275
+
2276
+ This method (omitting the orthogonality constraints) produces
2277
+ output in the cyclotomic field, but it is very limited in the size
2278
+ of examples it can handle: for example, `G_2` at level 2 is
2279
+ too large for this method. You may use :meth:`find_orthogonal_solution`
2280
+ to solve much larger examples.
2281
+
2282
+ INPUT:
2283
+
2284
+ - ``equations`` -- (optional) a set of equations to be
2285
+ solved; defaults to the hexagon and pentagon equations
2286
+ - ``algorithm`` -- (optional) algorithm to compute Groebner Basis
2287
+ - ``output`` -- boolean (default: ``False``); output a dictionary of
2288
+ F-matrix values; this may be useful to see but may be omitted
2289
+ since this information will be available afterwards via the
2290
+ :meth:`fmatrix` and :meth:`fmat` methods.
2291
+
2292
+ EXAMPLES::
2293
+
2294
+ sage: fr = FusionRing("A2", 1, fusion_labels='a', inject_variables=True)
2295
+ sage: f = fr.get_fmatrix(inject_variables=True)
2296
+ creating variables fx1..fx8
2297
+ Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7
2298
+ sage: f.find_cyclotomic_solution(output=True)
2299
+ Setting up hexagons and pentagons...
2300
+ Finding a Groebner basis...
2301
+ Solving...
2302
+ Fixing the gauge...
2303
+ adding equation... fx4 - 1
2304
+ Done!
2305
+ {(a2, a2, a2, a0, a1, a1): 1,
2306
+ (a2, a2, a1, a2, a1, a0): 1,
2307
+ (a2, a1, a2, a2, a0, a0): 1,
2308
+ (a2, a1, a1, a1, a0, a2): 1,
2309
+ (a1, a2, a2, a2, a0, a1): 1,
2310
+ (a1, a2, a1, a1, a0, a0): 1,
2311
+ (a1, a1, a2, a1, a2, a0): 1,
2312
+ (a1, a1, a1, a0, a2, a2): 1}
2313
+
2314
+ After you successfully run :meth:`find_cyclotomic_solution` you may
2315
+ check the correctness of the F-matrix by running
2316
+ :meth:`get_defining_equations` with ``option='hexagons'`` and
2317
+ ``option='pentagons'``. These should return empty lists
2318
+ of equations.
2319
+
2320
+ EXAMPLES::
2321
+
2322
+ sage: f.get_defining_equations("hexagons")
2323
+ []
2324
+ sage: f.get_defining_equations("pentagons")
2325
+ []
2326
+ """
2327
+ if self._poly_ring.ngens() == 0:
2328
+ return
2329
+ self._reset_solver_state()
2330
+ self._var_to_sextuple = {self._poly_ring.gen(i): s for i, s in self._idx_to_sextuple.items()}
2331
+
2332
+ if equations is None:
2333
+ if verbose:
2334
+ print("Setting up hexagons and pentagons...")
2335
+ equations = self.get_defining_equations("hexagons") + self.get_defining_equations("pentagons")
2336
+ if verbose:
2337
+ print("Finding a Groebner basis...")
2338
+ self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm))
2339
+ if verbose:
2340
+ print("Solving...")
2341
+ self._substitute_degree_one()
2342
+ if verbose:
2343
+ print("Fixing the gauge...")
2344
+ self._fix_gauge(algorithm=algorithm)
2345
+ if verbose:
2346
+ print("Done!")
2347
+ if output:
2348
+ return self._fvars
2349
+
2350
+ #####################
2351
+ # Verifications #
2352
+ #####################
2353
+
2354
+ def fmats_are_orthogonal(self):
2355
+ r"""
2356
+ Verify that all F-matrices are orthogonal.
2357
+
2358
+ This method should always return ``True`` when called after running
2359
+ :meth:`find_orthogonal_solution`.
2360
+
2361
+ EXAMPLES::
2362
+
2363
+ sage: f = FusionRing("D4", 1).get_fmatrix()
2364
+ sage: f.find_orthogonal_solution(verbose=False)
2365
+ sage: f.fmats_are_orthogonal()
2366
+ True
2367
+ """
2368
+ is_orthog = []
2369
+ for a, b, c, d in product(self._FR.basis(), repeat=4):
2370
+ mat = self.fmatrix(a, b, c, d)
2371
+ is_orthog.append(mat.T * mat == matrix.identity(mat.nrows()))
2372
+ return all(is_orthog)
2373
+
2374
+ def fvars_are_real(self):
2375
+ r"""
2376
+ Test whether all F-symbols are real.
2377
+
2378
+ EXAMPLES::
2379
+
2380
+ sage: f = FusionRing("A1", 3).get_fmatrix()
2381
+ sage: f.find_orthogonal_solution(verbose=False) # long time
2382
+ sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework)
2383
+ True
2384
+ """
2385
+ try:
2386
+ for k, v in self._fvars.items():
2387
+ AA(self._qqbar_embedding(v))
2388
+ except ValueError:
2389
+ print("the F-symbol {} (key {}) has a nonzero imaginary part".format(v, k))
2390
+ return False
2391
+ return True
2392
+
2393
+ def certify_pentagons(self, use_mp=True, verbose=False):
2394
+ r"""
2395
+ Obtain a certificate of satisfaction for the pentagon equations,
2396
+ up to floating-point error.
2397
+
2398
+ This method converts the computed F-symbols (available through
2399
+ :meth:`get_fvars`) to native Python floats and then checks whether
2400
+ the pentagon equations are satisfied using floating point arithmetic.
2401
+
2402
+ When ``self.FR().basis()`` has many elements, verifying satisfaction
2403
+ of the pentagon relations exactly using :meth:`get_defining_equations`
2404
+ with ``option="pentagons"`` may take a long time. This method is
2405
+ faster, but it cannot provide mathematical guarantees.
2406
+
2407
+ EXAMPLES::
2408
+
2409
+ sage: f = FusionRing("C3", 1).get_fmatrix()
2410
+ sage: f.find_orthogonal_solution() # long time
2411
+ Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables...
2412
+ Set up 134 hex and orthogonality constraints...
2413
+ Partitioned 134 equations into 17 components of size:
2414
+ [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1]
2415
+ Elimination epoch completed... 10 eqns remain in ideal basis
2416
+ Elimination epoch completed... 0 eqns remain in ideal basis
2417
+ Hex elim step solved for 51 / 71 variables
2418
+ Set up 121 reduced pentagons...
2419
+ Elimination epoch completed... 18 eqns remain in ideal basis
2420
+ Elimination epoch completed... 5 eqns remain in ideal basis
2421
+ Pent elim step solved for 64 / 71 variables
2422
+ Partitioned 5 equations into 1 components of size:
2423
+ [4]
2424
+ Elimination epoch completed... 0 eqns remain in ideal basis
2425
+ Partitioned 6 equations into 6 components of size:
2426
+ [1, 1, 1, 1, 1, 1]
2427
+ Computing appropriate NumberField...
2428
+ sage: f.certify_pentagons() is None # not tested (cypari issue in doctesting framework), long time (~1.5s)
2429
+ True
2430
+ """
2431
+ fvars_copy = deepcopy(self._fvars)
2432
+ self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()}
2433
+ if use_mp:
2434
+ pool = Pool()
2435
+ else:
2436
+ pool = None
2437
+ n_proc = pool._processes if pool is not None else 1
2438
+ params = [(child_id, n_proc, verbose) for child_id in range(n_proc)]
2439
+ pe = self._map_triv_reduce('pent_verify', params, worker_pool=pool, chunksize=1, mp_thresh=0)
2440
+ if np.all(np.isclose(np.array(pe), 0, atol=1e-7)):
2441
+ if verbose:
2442
+ print("Found valid F-symbols for {}".format(self._FR))
2443
+ pe = None
2444
+ else:
2445
+ if verbose:
2446
+ print("Something went wrong. Pentagons remain.")
2447
+ self._fvars = fvars_copy
2448
+ return pe