passagemath-singular 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.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.
- PySingular.cpython-314-aarch64-linux-gnu.so +0 -0
- passagemath_singular-10.6.31rc3.dist-info/METADATA +183 -0
- passagemath_singular-10.6.31rc3.dist-info/RECORD +490 -0
- passagemath_singular-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_singular-10.6.31rc3.dist-info/top_level.txt +3 -0
- passagemath_singular.libs/libSingular-4-6a2a8666.4.1.so +0 -0
- passagemath_singular.libs/libcddgmp-ac579979.so.0.1.3 +0 -0
- passagemath_singular.libs/libfactory-4-66e33516.4.1.so +0 -0
- passagemath_singular.libs/libflint-81de1160.so.21.0.0 +0 -0
- passagemath_singular.libs/libgf2x-fbd36f80.so.3.0.0 +0 -0
- passagemath_singular.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_singular.libs/libgmp-93ebf16a.so.10.5.0 +0 -0
- passagemath_singular.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_singular.libs/libmpfr-e0f11cf3.so.6.2.1 +0 -0
- passagemath_singular.libs/libntl-0043a3a2.so.44.0.1 +0 -0
- passagemath_singular.libs/libomalloc-0-06512335.9.6.so +0 -0
- passagemath_singular.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- passagemath_singular.libs/libpolys-4-cb7246b5.4.1.so +0 -0
- passagemath_singular.libs/libreadline-28330744.so.8.2 +0 -0
- passagemath_singular.libs/libsingular_resources-4-8c425241.4.1.so +0 -0
- passagemath_singular.libs/libtinfo-f81c2d16.so.6.3 +0 -0
- sage/algebras/all__sagemath_singular.py +3 -0
- sage/algebras/fusion_rings/all.py +19 -0
- sage/algebras/fusion_rings/f_matrix.py +2448 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd +5 -0
- sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +538 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd +3 -0
- sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +331 -0
- sage/algebras/fusion_rings/fusion_double.py +899 -0
- sage/algebras/fusion_rings/fusion_ring.py +1580 -0
- sage/algebras/fusion_rings/poly_tup_engine.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/poly_tup_engine.pxd +24 -0
- sage/algebras/fusion_rings/poly_tup_engine.pyx +579 -0
- sage/algebras/fusion_rings/shm_managers.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/fusion_rings/shm_managers.pxd +24 -0
- sage/algebras/fusion_rings/shm_managers.pyx +780 -0
- sage/algebras/letterplace/all.py +1 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.pxd +18 -0
- sage/algebras/letterplace/free_algebra_element_letterplace.pyx +755 -0
- sage/algebras/letterplace/free_algebra_letterplace.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/free_algebra_letterplace.pxd +35 -0
- sage/algebras/letterplace/free_algebra_letterplace.pyx +914 -0
- sage/algebras/letterplace/letterplace_ideal.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/letterplace/letterplace_ideal.pyx +408 -0
- sage/algebras/quatalg/all.py +2 -0
- sage/algebras/quatalg/quaternion_algebra.py +4778 -0
- sage/algebras/quatalg/quaternion_algebra_cython.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/quatalg/quaternion_algebra_cython.pyx +261 -0
- sage/algebras/quatalg/quaternion_algebra_element.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/algebras/quatalg/quaternion_algebra_element.pxd +29 -0
- sage/algebras/quatalg/quaternion_algebra_element.pyx +2176 -0
- sage/all__sagemath_singular.py +11 -0
- sage/ext_data/all__sagemath_singular.py +1 -0
- sage/ext_data/singular/function_field/core.lib +98 -0
- sage/interfaces/all__sagemath_singular.py +1 -0
- sage/interfaces/singular.py +2835 -0
- sage/libs/all__sagemath_singular.py +1 -0
- sage/libs/singular/__init__.py +1 -0
- sage/libs/singular/decl.pxd +1168 -0
- sage/libs/singular/function.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/function.pxd +87 -0
- sage/libs/singular/function.pyx +1901 -0
- sage/libs/singular/function_factory.py +61 -0
- sage/libs/singular/groebner_strategy.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/groebner_strategy.pxd +22 -0
- sage/libs/singular/groebner_strategy.pyx +582 -0
- sage/libs/singular/option.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/option.pyx +671 -0
- sage/libs/singular/polynomial.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/polynomial.pxd +39 -0
- sage/libs/singular/polynomial.pyx +661 -0
- sage/libs/singular/ring.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/ring.pxd +58 -0
- sage/libs/singular/ring.pyx +893 -0
- sage/libs/singular/singular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/libs/singular/singular.pxd +72 -0
- sage/libs/singular/singular.pyx +1944 -0
- sage/libs/singular/standard_options.py +145 -0
- sage/matrix/all__sagemath_singular.py +1 -0
- sage/matrix/matrix_mpolynomial_dense.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/matrix/matrix_mpolynomial_dense.pxd +7 -0
- sage/matrix/matrix_mpolynomial_dense.pyx +615 -0
- sage/rings/all__sagemath_singular.py +1 -0
- sage/rings/function_field/all__sagemath_singular.py +1 -0
- sage/rings/function_field/derivations_polymod.py +911 -0
- sage/rings/function_field/element_polymod.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/function_field/element_polymod.pyx +406 -0
- sage/rings/function_field/function_field_polymod.py +2611 -0
- sage/rings/function_field/ideal_polymod.py +1775 -0
- sage/rings/function_field/order_polymod.py +1475 -0
- sage/rings/function_field/place_polymod.py +681 -0
- sage/rings/polynomial/all__sagemath_singular.py +1 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.pxd +5 -0
- sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +339 -0
- sage/rings/polynomial/multi_polynomial_libsingular.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/multi_polynomial_libsingular.pxd +30 -0
- sage/rings/polynomial/multi_polynomial_libsingular.pyx +6277 -0
- sage/rings/polynomial/plural.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/rings/polynomial/plural.pxd +48 -0
- sage/rings/polynomial/plural.pyx +3171 -0
- sage/symbolic/all__sagemath_singular.py +1 -0
- sage/symbolic/comparison_impl.pxi +428 -0
- sage/symbolic/constants_c_impl.pxi +178 -0
- sage/symbolic/expression.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/symbolic/expression.pxd +7 -0
- sage/symbolic/expression.pyx +14200 -0
- sage/symbolic/getitem_impl.pxi +202 -0
- sage/symbolic/pynac.pxi +572 -0
- sage/symbolic/pynac_constant_impl.pxi +133 -0
- sage/symbolic/pynac_function_impl.pxi +206 -0
- sage/symbolic/pynac_impl.pxi +2576 -0
- sage/symbolic/pynac_wrap.h +124 -0
- sage/symbolic/series_impl.pxi +272 -0
- sage/symbolic/substitution_map_impl.pxi +94 -0
- sage_wheels/bin/ESingular +0 -0
- sage_wheels/bin/Singular +0 -0
- sage_wheels/bin/TSingular +0 -0
- sage_wheels/lib/singular/MOD/cohomo.la +41 -0
- sage_wheels/lib/singular/MOD/cohomo.so +0 -0
- sage_wheels/lib/singular/MOD/customstd.la +41 -0
- sage_wheels/lib/singular/MOD/customstd.so +0 -0
- sage_wheels/lib/singular/MOD/freealgebra.la +41 -0
- sage_wheels/lib/singular/MOD/freealgebra.so +0 -0
- sage_wheels/lib/singular/MOD/gfanlib.la +41 -0
- sage_wheels/lib/singular/MOD/gfanlib.so +0 -0
- sage_wheels/lib/singular/MOD/gitfan.la +41 -0
- sage_wheels/lib/singular/MOD/gitfan.so +0 -0
- sage_wheels/lib/singular/MOD/interval.la +41 -0
- sage_wheels/lib/singular/MOD/interval.so +0 -0
- sage_wheels/lib/singular/MOD/loctriv.la +41 -0
- sage_wheels/lib/singular/MOD/loctriv.so +0 -0
- sage_wheels/lib/singular/MOD/machinelearning.la +41 -0
- sage_wheels/lib/singular/MOD/machinelearning.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldGeneral.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldIndep.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldQ.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldQ.so +0 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldZp.la +41 -0
- sage_wheels/lib/singular/MOD/p_Procs_FieldZp.so +0 -0
- sage_wheels/lib/singular/MOD/partialgb.la +41 -0
- sage_wheels/lib/singular/MOD/partialgb.so +0 -0
- sage_wheels/lib/singular/MOD/pyobject.la +41 -0
- sage_wheels/lib/singular/MOD/pyobject.so +0 -0
- sage_wheels/lib/singular/MOD/singmathic.la +41 -0
- sage_wheels/lib/singular/MOD/singmathic.so +0 -0
- sage_wheels/lib/singular/MOD/sispasm.la +41 -0
- sage_wheels/lib/singular/MOD/sispasm.so +0 -0
- sage_wheels/lib/singular/MOD/subsets.la +41 -0
- sage_wheels/lib/singular/MOD/subsets.so +0 -0
- sage_wheels/lib/singular/MOD/systhreads.la +41 -0
- sage_wheels/lib/singular/MOD/systhreads.so +0 -0
- sage_wheels/lib/singular/MOD/syzextra.la +41 -0
- sage_wheels/lib/singular/MOD/syzextra.so +0 -0
- sage_wheels/libexec/singular/MOD/change_cost +0 -0
- sage_wheels/libexec/singular/MOD/singularsurf +11 -0
- sage_wheels/libexec/singular/MOD/singularsurf_jupyter +9 -0
- sage_wheels/libexec/singular/MOD/singularsurf_win +10 -0
- sage_wheels/libexec/singular/MOD/solve_IP +0 -0
- sage_wheels/libexec/singular/MOD/surfex +16 -0
- sage_wheels/libexec/singular/MOD/toric_ideal +0 -0
- sage_wheels/share/factory/gftables/10201 +342 -0
- sage_wheels/share/factory/gftables/1024 +37 -0
- sage_wheels/share/factory/gftables/10609 +356 -0
- sage_wheels/share/factory/gftables/11449 +384 -0
- sage_wheels/share/factory/gftables/11881 +398 -0
- sage_wheels/share/factory/gftables/121 +6 -0
- sage_wheels/share/factory/gftables/12167 +408 -0
- sage_wheels/share/factory/gftables/125 +7 -0
- sage_wheels/share/factory/gftables/12769 +428 -0
- sage_wheels/share/factory/gftables/128 +7 -0
- sage_wheels/share/factory/gftables/1331 +47 -0
- sage_wheels/share/factory/gftables/1369 +48 -0
- sage_wheels/share/factory/gftables/14641 +490 -0
- sage_wheels/share/factory/gftables/15625 +523 -0
- sage_wheels/share/factory/gftables/16 +3 -0
- sage_wheels/share/factory/gftables/16129 +540 -0
- sage_wheels/share/factory/gftables/16384 +549 -0
- sage_wheels/share/factory/gftables/16807 +563 -0
- sage_wheels/share/factory/gftables/1681 +58 -0
- sage_wheels/share/factory/gftables/169 +8 -0
- sage_wheels/share/factory/gftables/17161 +574 -0
- sage_wheels/share/factory/gftables/1849 +64 -0
- sage_wheels/share/factory/gftables/18769 +628 -0
- sage_wheels/share/factory/gftables/19321 +646 -0
- sage_wheels/share/factory/gftables/19683 +659 -0
- sage_wheels/share/factory/gftables/2048 +71 -0
- sage_wheels/share/factory/gftables/2187 +75 -0
- sage_wheels/share/factory/gftables/2197 +76 -0
- sage_wheels/share/factory/gftables/2209 +76 -0
- sage_wheels/share/factory/gftables/22201 +742 -0
- sage_wheels/share/factory/gftables/22801 +762 -0
- sage_wheels/share/factory/gftables/2401 +82 -0
- sage_wheels/share/factory/gftables/243 +11 -0
- sage_wheels/share/factory/gftables/24389 +815 -0
- sage_wheels/share/factory/gftables/24649 +824 -0
- sage_wheels/share/factory/gftables/25 +3 -0
- sage_wheels/share/factory/gftables/256 +11 -0
- sage_wheels/share/factory/gftables/26569 +888 -0
- sage_wheels/share/factory/gftables/27 +3 -0
- sage_wheels/share/factory/gftables/27889 +932 -0
- sage_wheels/share/factory/gftables/2809 +96 -0
- sage_wheels/share/factory/gftables/28561 +954 -0
- sage_wheels/share/factory/gftables/289 +12 -0
- sage_wheels/share/factory/gftables/29791 +995 -0
- sage_wheels/share/factory/gftables/29929 +1000 -0
- sage_wheels/share/factory/gftables/3125 +107 -0
- sage_wheels/share/factory/gftables/32 +4 -0
- sage_wheels/share/factory/gftables/32041 +1070 -0
- sage_wheels/share/factory/gftables/32761 +1094 -0
- sage_wheels/share/factory/gftables/32768 +1095 -0
- sage_wheels/share/factory/gftables/343 +14 -0
- sage_wheels/share/factory/gftables/3481 +118 -0
- sage_wheels/share/factory/gftables/361 +14 -0
- sage_wheels/share/factory/gftables/36481 +1218 -0
- sage_wheels/share/factory/gftables/3721 +126 -0
- sage_wheels/share/factory/gftables/37249 +1244 -0
- sage_wheels/share/factory/gftables/38809 +1296 -0
- sage_wheels/share/factory/gftables/39601 +1322 -0
- sage_wheels/share/factory/gftables/4 +3 -0
- sage_wheels/share/factory/gftables/4096 +139 -0
- sage_wheels/share/factory/gftables/44521 +1486 -0
- sage_wheels/share/factory/gftables/4489 +152 -0
- sage_wheels/share/factory/gftables/49 +4 -0
- sage_wheels/share/factory/gftables/4913 +166 -0
- sage_wheels/share/factory/gftables/49729 +1660 -0
- sage_wheels/share/factory/gftables/5041 +170 -0
- sage_wheels/share/factory/gftables/50653 +1691 -0
- sage_wheels/share/factory/gftables/512 +20 -0
- sage_wheels/share/factory/gftables/51529 +1720 -0
- sage_wheels/share/factory/gftables/52441 +1750 -0
- sage_wheels/share/factory/gftables/529 +20 -0
- sage_wheels/share/factory/gftables/5329 +180 -0
- sage_wheels/share/factory/gftables/54289 +1812 -0
- sage_wheels/share/factory/gftables/57121 +1906 -0
- sage_wheels/share/factory/gftables/58081 +1938 -0
- sage_wheels/share/factory/gftables/59049 +1971 -0
- sage_wheels/share/factory/gftables/6241 +210 -0
- sage_wheels/share/factory/gftables/625 +23 -0
- sage_wheels/share/factory/gftables/63001 +2102 -0
- sage_wheels/share/factory/gftables/64 +5 -0
- sage_wheels/share/factory/gftables/6561 +221 -0
- sage_wheels/share/factory/gftables/6859 +231 -0
- sage_wheels/share/factory/gftables/6889 +232 -0
- sage_wheels/share/factory/gftables/729 +27 -0
- sage_wheels/share/factory/gftables/7921 +266 -0
- sage_wheels/share/factory/gftables/8 +3 -0
- sage_wheels/share/factory/gftables/81 +5 -0
- sage_wheels/share/factory/gftables/8192 +276 -0
- sage_wheels/share/factory/gftables/841 +30 -0
- sage_wheels/share/factory/gftables/9 +3 -0
- sage_wheels/share/factory/gftables/9409 +316 -0
- sage_wheels/share/factory/gftables/961 +34 -0
- sage_wheels/share/info/singular.info +191898 -0
- sage_wheels/share/singular/LIB/GND.lib +1359 -0
- sage_wheels/share/singular/LIB/JMBTest.lib +976 -0
- sage_wheels/share/singular/LIB/JMSConst.lib +1363 -0
- sage_wheels/share/singular/LIB/KVequiv.lib +699 -0
- sage_wheels/share/singular/LIB/SingularityDBM.lib +491 -0
- sage_wheels/share/singular/LIB/VecField.lib +1542 -0
- sage_wheels/share/singular/LIB/absfact.lib +959 -0
- sage_wheels/share/singular/LIB/ainvar.lib +730 -0
- sage_wheels/share/singular/LIB/aksaka.lib +419 -0
- sage_wheels/share/singular/LIB/alexpoly.lib +2542 -0
- sage_wheels/share/singular/LIB/algebra.lib +1193 -0
- sage_wheels/share/singular/LIB/all.lib +136 -0
- sage_wheels/share/singular/LIB/arcpoint.lib +514 -0
- sage_wheels/share/singular/LIB/arnold.lib +4553 -0
- sage_wheels/share/singular/LIB/arnoldclassify.lib +2058 -0
- sage_wheels/share/singular/LIB/arr.lib +3486 -0
- sage_wheels/share/singular/LIB/assprimeszerodim.lib +755 -0
- sage_wheels/share/singular/LIB/autgradalg.lib +3361 -0
- sage_wheels/share/singular/LIB/bfun.lib +1964 -0
- sage_wheels/share/singular/LIB/bimodules.lib +774 -0
- sage_wheels/share/singular/LIB/brillnoether.lib +226 -0
- sage_wheels/share/singular/LIB/brnoeth.lib +5017 -0
- sage_wheels/share/singular/LIB/central.lib +2169 -0
- sage_wheels/share/singular/LIB/chern.lib +4162 -0
- sage_wheels/share/singular/LIB/cimonom.lib +571 -0
- sage_wheels/share/singular/LIB/cisimplicial.lib +1835 -0
- sage_wheels/share/singular/LIB/classify.lib +3239 -0
- sage_wheels/share/singular/LIB/classify2.lib +1462 -0
- sage_wheels/share/singular/LIB/classifyMapGerms.lib +1515 -0
- sage_wheels/share/singular/LIB/classify_aeq.lib +3253 -0
- sage_wheels/share/singular/LIB/classifyceq.lib +2092 -0
- sage_wheels/share/singular/LIB/classifyci.lib +1133 -0
- sage_wheels/share/singular/LIB/combinat.lib +91 -0
- sage_wheels/share/singular/LIB/compregb.lib +276 -0
- sage_wheels/share/singular/LIB/control.lib +1636 -0
- sage_wheels/share/singular/LIB/crypto.lib +3795 -0
- sage_wheels/share/singular/LIB/curveInv.lib +667 -0
- sage_wheels/share/singular/LIB/curvepar.lib +1817 -0
- sage_wheels/share/singular/LIB/customstd.lib +100 -0
- sage_wheels/share/singular/LIB/deRham.lib +5979 -0
- sage_wheels/share/singular/LIB/decodegb.lib +2134 -0
- sage_wheels/share/singular/LIB/decomp.lib +1655 -0
- sage_wheels/share/singular/LIB/deflation.lib +872 -0
- sage_wheels/share/singular/LIB/deform.lib +925 -0
- sage_wheels/share/singular/LIB/difform.lib +3055 -0
- sage_wheels/share/singular/LIB/divisors.lib +750 -0
- sage_wheels/share/singular/LIB/dmod.lib +5817 -0
- sage_wheels/share/singular/LIB/dmodapp.lib +3269 -0
- sage_wheels/share/singular/LIB/dmodideal.lib +1211 -0
- sage_wheels/share/singular/LIB/dmodloc.lib +2645 -0
- sage_wheels/share/singular/LIB/dmodvar.lib +818 -0
- sage_wheels/share/singular/LIB/dummy.lib +17 -0
- sage_wheels/share/singular/LIB/elim.lib +1009 -0
- sage_wheels/share/singular/LIB/ellipticcovers.lib +548 -0
- sage_wheels/share/singular/LIB/enumpoints.lib +146 -0
- sage_wheels/share/singular/LIB/equising.lib +2127 -0
- sage_wheels/share/singular/LIB/ffmodstd.lib +2384 -0
- sage_wheels/share/singular/LIB/ffsolve.lib +1289 -0
- sage_wheels/share/singular/LIB/findifs.lib +778 -0
- sage_wheels/share/singular/LIB/finitediff.lib +1768 -0
- sage_wheels/share/singular/LIB/finvar.lib +7989 -0
- sage_wheels/share/singular/LIB/fpadim.lib +2429 -0
- sage_wheels/share/singular/LIB/fpalgebras.lib +1666 -0
- sage_wheels/share/singular/LIB/fpaprops.lib +1462 -0
- sage_wheels/share/singular/LIB/freegb.lib +3853 -0
- sage_wheels/share/singular/LIB/general.lib +1350 -0
- sage_wheels/share/singular/LIB/gfan.lib +1768 -0
- sage_wheels/share/singular/LIB/gitfan.lib +3130 -0
- sage_wheels/share/singular/LIB/gkdim.lib +99 -0
- sage_wheels/share/singular/LIB/gmspoly.lib +589 -0
- sage_wheels/share/singular/LIB/gmssing.lib +1739 -0
- sage_wheels/share/singular/LIB/goettsche.lib +909 -0
- sage_wheels/share/singular/LIB/graal.lib +1366 -0
- sage_wheels/share/singular/LIB/gradedModules.lib +2541 -0
- sage_wheels/share/singular/LIB/graphics.lib +360 -0
- sage_wheels/share/singular/LIB/grobcov.lib +7706 -0
- sage_wheels/share/singular/LIB/groups.lib +1123 -0
- sage_wheels/share/singular/LIB/grwalk.lib +507 -0
- sage_wheels/share/singular/LIB/hdepth.lib +194 -0
- sage_wheels/share/singular/LIB/help.cnf +57 -0
- sage_wheels/share/singular/LIB/hess.lib +1946 -0
- sage_wheels/share/singular/LIB/hnoether.lib +4292 -0
- sage_wheels/share/singular/LIB/hodge.lib +400 -0
- sage_wheels/share/singular/LIB/homolog.lib +1965 -0
- sage_wheels/share/singular/LIB/hyperel.lib +975 -0
- sage_wheels/share/singular/LIB/inout.lib +679 -0
- sage_wheels/share/singular/LIB/integralbasis.lib +6224 -0
- sage_wheels/share/singular/LIB/interval.lib +1418 -0
- sage_wheels/share/singular/LIB/intprog.lib +778 -0
- sage_wheels/share/singular/LIB/invar.lib +443 -0
- sage_wheels/share/singular/LIB/involut.lib +980 -0
- sage_wheels/share/singular/LIB/jacobson.lib +1215 -0
- sage_wheels/share/singular/LIB/kskernel.lib +534 -0
- sage_wheels/share/singular/LIB/latex.lib +3146 -0
- sage_wheels/share/singular/LIB/lejeune.lib +651 -0
- sage_wheels/share/singular/LIB/linalg.lib +2040 -0
- sage_wheels/share/singular/LIB/locnormal.lib +212 -0
- sage_wheels/share/singular/LIB/lrcalc.lib +526 -0
- sage_wheels/share/singular/LIB/makedbm.lib +294 -0
- sage_wheels/share/singular/LIB/mathml.lib +813 -0
- sage_wheels/share/singular/LIB/matrix.lib +1372 -0
- sage_wheels/share/singular/LIB/maxlike.lib +1132 -0
- sage_wheels/share/singular/LIB/methods.lib +212 -0
- sage_wheels/share/singular/LIB/moddiq.lib +322 -0
- sage_wheels/share/singular/LIB/modfinduni.lib +181 -0
- sage_wheels/share/singular/LIB/modnormal.lib +218 -0
- sage_wheels/share/singular/LIB/modprimdec.lib +1278 -0
- sage_wheels/share/singular/LIB/modquotient.lib +269 -0
- sage_wheels/share/singular/LIB/modstd.lib +1024 -0
- sage_wheels/share/singular/LIB/modular.lib +545 -0
- sage_wheels/share/singular/LIB/modules.lib +2561 -0
- sage_wheels/share/singular/LIB/modwalk.lib +609 -0
- sage_wheels/share/singular/LIB/mondromy.lib +1016 -0
- sage_wheels/share/singular/LIB/monomialideal.lib +3851 -0
- sage_wheels/share/singular/LIB/mprimdec.lib +2353 -0
- sage_wheels/share/singular/LIB/mregular.lib +1863 -0
- sage_wheels/share/singular/LIB/multigrading.lib +5629 -0
- sage_wheels/share/singular/LIB/ncHilb.lib +777 -0
- sage_wheels/share/singular/LIB/ncModslimgb.lib +791 -0
- sage_wheels/share/singular/LIB/ncalg.lib +16311 -0
- sage_wheels/share/singular/LIB/ncall.lib +31 -0
- sage_wheels/share/singular/LIB/ncdecomp.lib +468 -0
- sage_wheels/share/singular/LIB/ncfactor.lib +13371 -0
- sage_wheels/share/singular/LIB/ncfrac.lib +1023 -0
- sage_wheels/share/singular/LIB/nchilbert.lib +448 -0
- sage_wheels/share/singular/LIB/nchomolog.lib +759 -0
- sage_wheels/share/singular/LIB/ncloc.lib +361 -0
- sage_wheels/share/singular/LIB/ncpreim.lib +795 -0
- sage_wheels/share/singular/LIB/ncrat.lib +2849 -0
- sage_wheels/share/singular/LIB/nctools.lib +1887 -0
- sage_wheels/share/singular/LIB/nets.lib +1456 -0
- sage_wheels/share/singular/LIB/nfmodstd.lib +1000 -0
- sage_wheels/share/singular/LIB/nfmodsyz.lib +732 -0
- sage_wheels/share/singular/LIB/noether.lib +1106 -0
- sage_wheels/share/singular/LIB/normal.lib +8700 -0
- sage_wheels/share/singular/LIB/normaliz.lib +2226 -0
- sage_wheels/share/singular/LIB/ntsolve.lib +362 -0
- sage_wheels/share/singular/LIB/numerAlg.lib +560 -0
- sage_wheels/share/singular/LIB/numerDecom.lib +2261 -0
- sage_wheels/share/singular/LIB/olga.lib +1933 -0
- sage_wheels/share/singular/LIB/orbitparam.lib +351 -0
- sage_wheels/share/singular/LIB/parallel.lib +319 -0
- sage_wheels/share/singular/LIB/paraplanecurves.lib +3110 -0
- sage_wheels/share/singular/LIB/perron.lib +202 -0
- sage_wheels/share/singular/LIB/pfd.lib +2223 -0
- sage_wheels/share/singular/LIB/phindex.lib +642 -0
- sage_wheels/share/singular/LIB/pointid.lib +673 -0
- sage_wheels/share/singular/LIB/polybori.lib +1430 -0
- sage_wheels/share/singular/LIB/polyclass.lib +525 -0
- sage_wheels/share/singular/LIB/polylib.lib +1174 -0
- sage_wheels/share/singular/LIB/polymake.lib +1902 -0
- sage_wheels/share/singular/LIB/presolve.lib +1533 -0
- sage_wheels/share/singular/LIB/primdec.lib +9576 -0
- sage_wheels/share/singular/LIB/primdecint.lib +1782 -0
- sage_wheels/share/singular/LIB/primitiv.lib +401 -0
- sage_wheels/share/singular/LIB/puiseuxexpansions.lib +1631 -0
- sage_wheels/share/singular/LIB/purityfiltration.lib +960 -0
- sage_wheels/share/singular/LIB/qhmoduli.lib +1561 -0
- sage_wheels/share/singular/LIB/qmatrix.lib +293 -0
- sage_wheels/share/singular/LIB/random.lib +455 -0
- sage_wheels/share/singular/LIB/ratgb.lib +489 -0
- sage_wheels/share/singular/LIB/realclassify.lib +5759 -0
- sage_wheels/share/singular/LIB/realizationMatroids.lib +772 -0
- sage_wheels/share/singular/LIB/realrad.lib +1197 -0
- sage_wheels/share/singular/LIB/recover.lib +2628 -0
- sage_wheels/share/singular/LIB/redcgs.lib +3984 -0
- sage_wheels/share/singular/LIB/reesclos.lib +465 -0
- sage_wheels/share/singular/LIB/resbinomial.lib +2802 -0
- sage_wheels/share/singular/LIB/resgraph.lib +789 -0
- sage_wheels/share/singular/LIB/resjung.lib +820 -0
- sage_wheels/share/singular/LIB/resolve.lib +5110 -0
- sage_wheels/share/singular/LIB/resources.lib +170 -0
- sage_wheels/share/singular/LIB/reszeta.lib +5473 -0
- sage_wheels/share/singular/LIB/ring.lib +1328 -0
- sage_wheels/share/singular/LIB/ringgb.lib +343 -0
- sage_wheels/share/singular/LIB/rinvar.lib +1153 -0
- sage_wheels/share/singular/LIB/rootisolation.lib +1481 -0
- sage_wheels/share/singular/LIB/rootsmr.lib +709 -0
- sage_wheels/share/singular/LIB/rootsur.lib +886 -0
- sage_wheels/share/singular/LIB/rstandard.lib +607 -0
- sage_wheels/share/singular/LIB/rwalk.lib +336 -0
- sage_wheels/share/singular/LIB/sagbi.lib +1353 -0
- sage_wheels/share/singular/LIB/sagbiNormaliz.lib +1622 -0
- sage_wheels/share/singular/LIB/sagbiNormaliz0.lib +1498 -0
- sage_wheels/share/singular/LIB/sagbigrob.lib +449 -0
- sage_wheels/share/singular/LIB/schreyer.lib +321 -0
- sage_wheels/share/singular/LIB/schubert.lib +2551 -0
- sage_wheels/share/singular/LIB/sets.lib +524 -0
- sage_wheels/share/singular/LIB/sheafcoh.lib +1663 -0
- sage_wheels/share/singular/LIB/signcond.lib +437 -0
- sage_wheels/share/singular/LIB/sing.lib +1094 -0
- sage_wheels/share/singular/LIB/sing4ti2.lib +419 -0
- sage_wheels/share/singular/LIB/solve.lib +2243 -0
- sage_wheels/share/singular/LIB/spcurve.lib +1077 -0
- sage_wheels/share/singular/LIB/spectrum.lib +62 -0
- sage_wheels/share/singular/LIB/sresext.lib +757 -0
- sage_wheels/share/singular/LIB/ssi.lib +143 -0
- sage_wheels/share/singular/LIB/standard.lib +2769 -0
- sage_wheels/share/singular/LIB/stanleyreisner.lib +473 -0
- sage_wheels/share/singular/LIB/stdmodule.lib +547 -0
- sage_wheels/share/singular/LIB/stratify.lib +1070 -0
- sage_wheels/share/singular/LIB/surf.lib +506 -0
- sage_wheels/share/singular/LIB/surf_jupyter.lib +223 -0
- sage_wheels/share/singular/LIB/surfacesignature.lib +522 -0
- sage_wheels/share/singular/LIB/surfex.lib +1462 -0
- sage_wheels/share/singular/LIB/swalk.lib +877 -0
- sage_wheels/share/singular/LIB/symodstd.lib +1570 -0
- sage_wheels/share/singular/LIB/systhreads.lib +74 -0
- sage_wheels/share/singular/LIB/tasks.lib +1324 -0
- sage_wheels/share/singular/LIB/tateProdCplxNegGrad.lib +2412 -0
- sage_wheels/share/singular/LIB/teachstd.lib +858 -0
- sage_wheels/share/singular/LIB/template.lib +116 -0
- sage_wheels/share/singular/LIB/toric.lib +1119 -0
- sage_wheels/share/singular/LIB/transformation.lib +116 -0
- sage_wheels/share/singular/LIB/triang.lib +1197 -0
- sage_wheels/share/singular/LIB/tropical.lib +8741 -0
- sage_wheels/share/singular/LIB/tropicalEllipticCovers.lib +2922 -0
- sage_wheels/share/singular/LIB/tropicalNewton.lib +1128 -0
- sage_wheels/share/singular/LIB/tst.lib +1108 -0
- sage_wheels/share/singular/LIB/weierstr.lib +241 -0
- sage_wheels/share/singular/LIB/zeroset.lib +1478 -0
- sage_wheels/share/singular/emacs/.emacs-general +184 -0
- sage_wheels/share/singular/emacs/.emacs-singular +234 -0
- sage_wheels/share/singular/emacs/COPYING +44 -0
- sage_wheels/share/singular/emacs/cmd-cmpl.el +241 -0
- sage_wheels/share/singular/emacs/ex-cmpl.el +1681 -0
- sage_wheels/share/singular/emacs/hlp-cmpl.el +4318 -0
- sage_wheels/share/singular/emacs/lib-cmpl.el +179 -0
- sage_wheels/share/singular/emacs/singular.el +4273 -0
- sage_wheels/share/singular/emacs/singular.xpm +39 -0
- 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
|