snappy 3.2__cp311-cp311-macosx_10_12_x86_64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (503) hide show
  1. snappy/CyOpenGL.cpython-311-darwin.so +0 -0
  2. snappy/SnapPy.cpython-311-darwin.so +0 -0
  3. snappy/SnapPy.ico +0 -0
  4. snappy/SnapPy.png +0 -0
  5. snappy/SnapPyHP.cpython-311-darwin.so +0 -0
  6. snappy/__init__.py +760 -0
  7. snappy/app.py +605 -0
  8. snappy/app_menus.py +372 -0
  9. snappy/browser.py +998 -0
  10. snappy/cache.py +25 -0
  11. snappy/canonical.py +249 -0
  12. snappy/cusps/__init__.py +38 -0
  13. snappy/cusps/cusp_area_matrix.py +101 -0
  14. snappy/cusps/cusp_areas_from_matrix.py +173 -0
  15. snappy/cusps/maximal_cusp_area_matrix.py +136 -0
  16. snappy/cusps/test.py +21 -0
  17. snappy/cusps/trig_cusp_area_matrix.py +63 -0
  18. snappy/database.py +454 -0
  19. snappy/db_utilities.py +79 -0
  20. snappy/decorated_isosig.py +710 -0
  21. snappy/dev/__init__.py +0 -0
  22. snappy/dev/extended_ptolemy/__init__.py +8 -0
  23. snappy/dev/extended_ptolemy/closed.py +106 -0
  24. snappy/dev/extended_ptolemy/complexVolumesClosed.py +149 -0
  25. snappy/dev/extended_ptolemy/direct.py +42 -0
  26. snappy/dev/extended_ptolemy/extended.py +406 -0
  27. snappy/dev/extended_ptolemy/giac_helper.py +43 -0
  28. snappy/dev/extended_ptolemy/giac_rur.py +129 -0
  29. snappy/dev/extended_ptolemy/gluing.py +46 -0
  30. snappy/dev/extended_ptolemy/phc_wrapper.py +220 -0
  31. snappy/dev/extended_ptolemy/printMatrices.py +70 -0
  32. snappy/dev/vericlosed/__init__.py +1 -0
  33. snappy/dev/vericlosed/computeApproxHyperbolicStructureNew.py +159 -0
  34. snappy/dev/vericlosed/computeApproxHyperbolicStructureOrb.py +90 -0
  35. snappy/dev/vericlosed/computeVerifiedHyperbolicStructure.py +111 -0
  36. snappy/dev/vericlosed/gimbalLoopFinder.py +130 -0
  37. snappy/dev/vericlosed/hyperbolicStructure.py +313 -0
  38. snappy/dev/vericlosed/krawczykCertifiedEdgeLengthsEngine.py +165 -0
  39. snappy/dev/vericlosed/oneVertexTruncatedComplex.py +122 -0
  40. snappy/dev/vericlosed/orb/__init__.py +1 -0
  41. snappy/dev/vericlosed/orb/orb_solution_for_snappea_finite_triangulation_mac +0 -0
  42. snappy/dev/vericlosed/parseVertexGramMatrixFile.py +47 -0
  43. snappy/dev/vericlosed/polishApproxHyperbolicStructure.py +61 -0
  44. snappy/dev/vericlosed/test.py +54 -0
  45. snappy/dev/vericlosed/truncatedComplex.py +176 -0
  46. snappy/dev/vericlosed/verificationError.py +58 -0
  47. snappy/dev/vericlosed/verifyHyperbolicStructureEngine.py +177 -0
  48. snappy/doc/_images/SnapPy-196.png +0 -0
  49. snappy/doc/_images/geodesics.jpg +0 -0
  50. snappy/doc/_images/m004_paper_plane_on_systole.jpg +0 -0
  51. snappy/doc/_images/m125_paper_plane.jpg +0 -0
  52. snappy/doc/_images/mac.png +0 -0
  53. snappy/doc/_images/o9_00000_systole_paper_plane.jpg +0 -0
  54. snappy/doc/_images/o9_00000_systole_paper_plane_closer.jpg +0 -0
  55. snappy/doc/_images/plink-action.png +0 -0
  56. snappy/doc/_images/ubuntu.png +0 -0
  57. snappy/doc/_images/win7.png +0 -0
  58. snappy/doc/_sources/additional_classes.rst.txt +40 -0
  59. snappy/doc/_sources/bugs.rst.txt +14 -0
  60. snappy/doc/_sources/censuses.rst.txt +51 -0
  61. snappy/doc/_sources/credits.rst.txt +75 -0
  62. snappy/doc/_sources/development.rst.txt +259 -0
  63. snappy/doc/_sources/index.rst.txt +182 -0
  64. snappy/doc/_sources/installing.rst.txt +247 -0
  65. snappy/doc/_sources/manifold.rst.txt +6 -0
  66. snappy/doc/_sources/manifoldhp.rst.txt +46 -0
  67. snappy/doc/_sources/news.rst.txt +355 -0
  68. snappy/doc/_sources/other.rst.txt +25 -0
  69. snappy/doc/_sources/platonic_census.rst.txt +20 -0
  70. snappy/doc/_sources/plink.rst.txt +102 -0
  71. snappy/doc/_sources/ptolemy.rst.txt +66 -0
  72. snappy/doc/_sources/ptolemy_classes.rst.txt +42 -0
  73. snappy/doc/_sources/ptolemy_examples1.rst.txt +298 -0
  74. snappy/doc/_sources/ptolemy_examples2.rst.txt +363 -0
  75. snappy/doc/_sources/ptolemy_examples3.rst.txt +301 -0
  76. snappy/doc/_sources/ptolemy_examples4.rst.txt +61 -0
  77. snappy/doc/_sources/ptolemy_prelim.rst.txt +105 -0
  78. snappy/doc/_sources/screenshots.rst.txt +21 -0
  79. snappy/doc/_sources/snap.rst.txt +87 -0
  80. snappy/doc/_sources/snappy.rst.txt +28 -0
  81. snappy/doc/_sources/spherogram.rst.txt +103 -0
  82. snappy/doc/_sources/todo.rst.txt +47 -0
  83. snappy/doc/_sources/triangulation.rst.txt +11 -0
  84. snappy/doc/_sources/tutorial.rst.txt +49 -0
  85. snappy/doc/_sources/verify.rst.txt +210 -0
  86. snappy/doc/_sources/verify_internals.rst.txt +79 -0
  87. snappy/doc/_static/SnapPy-horizontal-128.png +0 -0
  88. snappy/doc/_static/SnapPy.ico +0 -0
  89. snappy/doc/_static/_sphinx_javascript_frameworks_compat.js +123 -0
  90. snappy/doc/_static/basic.css +925 -0
  91. snappy/doc/_static/css/badge_only.css +1 -0
  92. snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
  93. snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
  94. snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
  95. snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
  96. snappy/doc/_static/css/fonts/fontawesome-webfont.eot +0 -0
  97. snappy/doc/_static/css/fonts/fontawesome-webfont.svg +2671 -0
  98. snappy/doc/_static/css/fonts/fontawesome-webfont.ttf +0 -0
  99. snappy/doc/_static/css/fonts/fontawesome-webfont.woff +0 -0
  100. snappy/doc/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
  101. snappy/doc/_static/css/fonts/lato-bold-italic.woff +0 -0
  102. snappy/doc/_static/css/fonts/lato-bold-italic.woff2 +0 -0
  103. snappy/doc/_static/css/fonts/lato-bold.woff +0 -0
  104. snappy/doc/_static/css/fonts/lato-bold.woff2 +0 -0
  105. snappy/doc/_static/css/fonts/lato-normal-italic.woff +0 -0
  106. snappy/doc/_static/css/fonts/lato-normal-italic.woff2 +0 -0
  107. snappy/doc/_static/css/fonts/lato-normal.woff +0 -0
  108. snappy/doc/_static/css/fonts/lato-normal.woff2 +0 -0
  109. snappy/doc/_static/css/theme.css +4 -0
  110. snappy/doc/_static/doctools.js +156 -0
  111. snappy/doc/_static/documentation_options.js +13 -0
  112. snappy/doc/_static/file.png +0 -0
  113. snappy/doc/_static/fonts/Lato/lato-bold.eot +0 -0
  114. snappy/doc/_static/fonts/Lato/lato-bold.ttf +0 -0
  115. snappy/doc/_static/fonts/Lato/lato-bold.woff +0 -0
  116. snappy/doc/_static/fonts/Lato/lato-bold.woff2 +0 -0
  117. snappy/doc/_static/fonts/Lato/lato-bolditalic.eot +0 -0
  118. snappy/doc/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
  119. snappy/doc/_static/fonts/Lato/lato-bolditalic.woff +0 -0
  120. snappy/doc/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
  121. snappy/doc/_static/fonts/Lato/lato-italic.eot +0 -0
  122. snappy/doc/_static/fonts/Lato/lato-italic.ttf +0 -0
  123. snappy/doc/_static/fonts/Lato/lato-italic.woff +0 -0
  124. snappy/doc/_static/fonts/Lato/lato-italic.woff2 +0 -0
  125. snappy/doc/_static/fonts/Lato/lato-regular.eot +0 -0
  126. snappy/doc/_static/fonts/Lato/lato-regular.ttf +0 -0
  127. snappy/doc/_static/fonts/Lato/lato-regular.woff +0 -0
  128. snappy/doc/_static/fonts/Lato/lato-regular.woff2 +0 -0
  129. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
  130. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
  131. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
  132. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
  133. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
  134. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
  135. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
  136. snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
  137. snappy/doc/_static/jquery.js +2 -0
  138. snappy/doc/_static/js/badge_only.js +1 -0
  139. snappy/doc/_static/js/theme.js +1 -0
  140. snappy/doc/_static/js/versions.js +228 -0
  141. snappy/doc/_static/language_data.js +199 -0
  142. snappy/doc/_static/minus.png +0 -0
  143. snappy/doc/_static/plus.png +0 -0
  144. snappy/doc/_static/pygments.css +75 -0
  145. snappy/doc/_static/searchtools.js +620 -0
  146. snappy/doc/_static/snappy_furo.css +33 -0
  147. snappy/doc/_static/snappy_sphinx_rtd_theme.css +42 -0
  148. snappy/doc/_static/sphinx_highlight.js +154 -0
  149. snappy/doc/additional_classes.html +1500 -0
  150. snappy/doc/bugs.html +132 -0
  151. snappy/doc/censuses.html +427 -0
  152. snappy/doc/credits.html +181 -0
  153. snappy/doc/development.html +384 -0
  154. snappy/doc/genindex.html +1331 -0
  155. snappy/doc/index.html +262 -0
  156. snappy/doc/installing.html +346 -0
  157. snappy/doc/manifold.html +3452 -0
  158. snappy/doc/manifoldhp.html +180 -0
  159. snappy/doc/news.html +388 -0
  160. snappy/doc/objects.inv +0 -0
  161. snappy/doc/other.html +161 -0
  162. snappy/doc/platonic_census.html +375 -0
  163. snappy/doc/plink.html +210 -0
  164. snappy/doc/ptolemy.html +254 -0
  165. snappy/doc/ptolemy_classes.html +1144 -0
  166. snappy/doc/ptolemy_examples1.html +409 -0
  167. snappy/doc/ptolemy_examples2.html +471 -0
  168. snappy/doc/ptolemy_examples3.html +414 -0
  169. snappy/doc/ptolemy_examples4.html +195 -0
  170. snappy/doc/ptolemy_prelim.html +248 -0
  171. snappy/doc/py-modindex.html +165 -0
  172. snappy/doc/screenshots.html +141 -0
  173. snappy/doc/search.html +135 -0
  174. snappy/doc/searchindex.js +1 -0
  175. snappy/doc/snap.html +202 -0
  176. snappy/doc/snappy.html +181 -0
  177. snappy/doc/spherogram.html +1211 -0
  178. snappy/doc/todo.html +166 -0
  179. snappy/doc/triangulation.html +1584 -0
  180. snappy/doc/tutorial.html +159 -0
  181. snappy/doc/verify.html +330 -0
  182. snappy/doc/verify_internals.html +1235 -0
  183. snappy/drilling/__init__.py +456 -0
  184. snappy/drilling/barycentric.py +103 -0
  185. snappy/drilling/constants.py +5 -0
  186. snappy/drilling/crush.py +270 -0
  187. snappy/drilling/cusps.py +125 -0
  188. snappy/drilling/debug.py +242 -0
  189. snappy/drilling/epsilons.py +6 -0
  190. snappy/drilling/exceptions.py +55 -0
  191. snappy/drilling/moves.py +620 -0
  192. snappy/drilling/peripheral_curves.py +210 -0
  193. snappy/drilling/perturb.py +188 -0
  194. snappy/drilling/shorten.py +36 -0
  195. snappy/drilling/subdivide.py +274 -0
  196. snappy/drilling/test.py +23 -0
  197. snappy/drilling/test_cases.py +126 -0
  198. snappy/drilling/tracing.py +351 -0
  199. snappy/exceptions.py +26 -0
  200. snappy/export_stl.py +120 -0
  201. snappy/exterior_to_link/__init__.py +2 -0
  202. snappy/exterior_to_link/barycentric_geometry.py +463 -0
  203. snappy/exterior_to_link/exceptions.py +6 -0
  204. snappy/exterior_to_link/geodesic_map.json +14408 -0
  205. snappy/exterior_to_link/hyp_utils.py +112 -0
  206. snappy/exterior_to_link/link_projection.py +323 -0
  207. snappy/exterior_to_link/main.py +197 -0
  208. snappy/exterior_to_link/mcomplex_with_expansion.py +261 -0
  209. snappy/exterior_to_link/mcomplex_with_link.py +687 -0
  210. snappy/exterior_to_link/mcomplex_with_memory.py +162 -0
  211. snappy/exterior_to_link/pl_utils.py +491 -0
  212. snappy/exterior_to_link/put_in_S3.py +156 -0
  213. snappy/exterior_to_link/rational_linear_algebra.py +123 -0
  214. snappy/exterior_to_link/rational_linear_algebra_wrapped.py +135 -0
  215. snappy/exterior_to_link/simplify_to_base_tri.py +114 -0
  216. snappy/exterior_to_link/stored_moves.py +475 -0
  217. snappy/exterior_to_link/test.py +31 -0
  218. snappy/filedialog.py +28 -0
  219. snappy/geometric_structure/__init__.py +212 -0
  220. snappy/geometric_structure/cusp_neighborhood/__init__.py +3 -0
  221. snappy/geometric_structure/cusp_neighborhood/complex_cusp_cross_section.py +697 -0
  222. snappy/geometric_structure/cusp_neighborhood/cusp_cross_section_base.py +484 -0
  223. snappy/geometric_structure/cusp_neighborhood/exceptions.py +42 -0
  224. snappy/geometric_structure/cusp_neighborhood/real_cusp_cross_section.py +298 -0
  225. snappy/geometric_structure/cusp_neighborhood/tiles_for_cusp_neighborhood.py +159 -0
  226. snappy/geometric_structure/cusp_neighborhood/vertices.py +32 -0
  227. snappy/geometric_structure/geodesic/__init__.py +0 -0
  228. snappy/geometric_structure/geodesic/add_core_curves.py +152 -0
  229. snappy/geometric_structure/geodesic/avoid_core_curves.py +369 -0
  230. snappy/geometric_structure/geodesic/canonical_keys.py +52 -0
  231. snappy/geometric_structure/geodesic/check_away_from_core_curve.py +60 -0
  232. snappy/geometric_structure/geodesic/constants.py +6 -0
  233. snappy/geometric_structure/geodesic/exceptions.py +22 -0
  234. snappy/geometric_structure/geodesic/fixed_points.py +93 -0
  235. snappy/geometric_structure/geodesic/geodesic_start_point_info.py +435 -0
  236. snappy/geometric_structure/geodesic/graph_trace_helper.py +67 -0
  237. snappy/geometric_structure/geodesic/line.py +30 -0
  238. snappy/geometric_structure/geodesic/multiplicity.py +127 -0
  239. snappy/geometric_structure/geodesic/tiles_for_geodesic.py +101 -0
  240. snappy/geometric_structure/test.py +22 -0
  241. snappy/gui.py +121 -0
  242. snappy/horoviewer.py +443 -0
  243. snappy/hyperboloid/__init__.py +212 -0
  244. snappy/hyperboloid/distances.py +245 -0
  245. snappy/hyperboloid/horoball.py +19 -0
  246. snappy/hyperboloid/line.py +35 -0
  247. snappy/hyperboloid/point.py +9 -0
  248. snappy/hyperboloid/triangle.py +29 -0
  249. snappy/info_icon.gif +0 -0
  250. snappy/infowindow.py +65 -0
  251. snappy/isometry_signature.py +382 -0
  252. snappy/len_spec/__init__.py +596 -0
  253. snappy/len_spec/geodesic_info.py +110 -0
  254. snappy/len_spec/geodesic_key_info_dict.py +117 -0
  255. snappy/len_spec/geodesic_piece.py +143 -0
  256. snappy/len_spec/geometric_structure.py +182 -0
  257. snappy/len_spec/geometry.py +80 -0
  258. snappy/len_spec/length_spectrum_geodesic_info.py +170 -0
  259. snappy/len_spec/spine.py +206 -0
  260. snappy/len_spec/test.py +24 -0
  261. snappy/len_spec/test_cases.py +69 -0
  262. snappy/len_spec/tile.py +275 -0
  263. snappy/len_spec/word.py +86 -0
  264. snappy/manifolds/HTWKnots/alternating.gz +0 -0
  265. snappy/manifolds/HTWKnots/nonalternating.gz +0 -0
  266. snappy/manifolds/__init__.py +3 -0
  267. snappy/math_basics.py +176 -0
  268. snappy/matrix.py +525 -0
  269. snappy/number.py +657 -0
  270. snappy/numeric_output_checker.py +345 -0
  271. snappy/pari.py +41 -0
  272. snappy/phone_home.py +57 -0
  273. snappy/polyviewer.py +259 -0
  274. snappy/ptolemy/__init__.py +17 -0
  275. snappy/ptolemy/component.py +103 -0
  276. snappy/ptolemy/coordinates.py +2290 -0
  277. snappy/ptolemy/fieldExtensions.py +153 -0
  278. snappy/ptolemy/findLoops.py +473 -0
  279. snappy/ptolemy/geometricRep.py +59 -0
  280. snappy/ptolemy/homology.py +165 -0
  281. snappy/ptolemy/magma/default.magma_template +229 -0
  282. snappy/ptolemy/magma/radicalsOfPrimaryDecomposition.magma_template +79 -0
  283. snappy/ptolemy/manifoldMethods.py +395 -0
  284. snappy/ptolemy/matrix.py +350 -0
  285. snappy/ptolemy/numericalSolutionsToGroebnerBasis.py +113 -0
  286. snappy/ptolemy/polynomial.py +857 -0
  287. snappy/ptolemy/processComponents.py +173 -0
  288. snappy/ptolemy/processFileBase.py +247 -0
  289. snappy/ptolemy/processFileDispatch.py +46 -0
  290. snappy/ptolemy/processMagmaFile.py +392 -0
  291. snappy/ptolemy/processRurFile.py +150 -0
  292. snappy/ptolemy/ptolemyGeneralizedObstructionClass.py +102 -0
  293. snappy/ptolemy/ptolemyObstructionClass.py +64 -0
  294. snappy/ptolemy/ptolemyVariety.py +1029 -0
  295. snappy/ptolemy/ptolemyVarietyPrimeIdealGroebnerBasis.py +140 -0
  296. snappy/ptolemy/reginaWrapper.py +698 -0
  297. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
  298. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
  299. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
  300. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
  301. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
  302. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
  303. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
  304. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
  305. snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
  306. snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
  307. snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c0.magma_out.bz2 +0 -0
  308. snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
  309. snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
  310. snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c1.magma_out.bz2 +0 -0
  311. snappy/ptolemy/rur.py +545 -0
  312. snappy/ptolemy/solutionsToPrimeIdealGroebnerBasis.py +277 -0
  313. snappy/ptolemy/test.py +1126 -0
  314. snappy/ptolemy/testing_files/3_1__sl2_c0.magma_out.bz2 +0 -0
  315. snappy/ptolemy/testing_files/3_1__sl2_c1.magma_out.bz2 +0 -0
  316. snappy/ptolemy/testing_files/4_1__sl2_c0.magma_out.bz2 +0 -0
  317. snappy/ptolemy/testing_files/4_1__sl2_c1.magma_out.bz2 +0 -0
  318. snappy/ptolemy/testing_files/4_1__sl3_c0.magma_out.bz2 +0 -0
  319. snappy/ptolemy/testing_files/4_1__sl4_c0.magma_out.bz2 +0 -0
  320. snappy/ptolemy/testing_files/4_1__sl4_c1.magma_out.bz2 +0 -0
  321. snappy/ptolemy/testing_files/5_2__sl2_c0.magma_out.bz2 +0 -0
  322. snappy/ptolemy/testing_files/5_2__sl2_c1.magma_out.bz2 +0 -0
  323. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
  324. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
  325. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
  326. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
  327. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
  328. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
  329. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
  330. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
  331. snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c0.magma_out +95 -0
  332. snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c1.magma_out +95 -0
  333. snappy/ptolemy/testing_files/m015__sl3_c0.magma_out.bz2 +0 -0
  334. snappy/ptolemy/testing_files/m135__sl2_c0.magma_out.bz2 +0 -0
  335. snappy/ptolemy/testing_files/m135__sl2_c1.magma_out.bz2 +0 -0
  336. snappy/ptolemy/testing_files/m135__sl2_c2.magma_out.bz2 +0 -0
  337. snappy/ptolemy/testing_files/m135__sl2_c3.magma_out.bz2 +0 -0
  338. snappy/ptolemy/testing_files/m135__sl2_c4.magma_out.bz2 +0 -0
  339. snappy/ptolemy/testing_files/m135__sl2_c5.magma_out.bz2 +0 -0
  340. snappy/ptolemy/testing_files/m135__sl2_c6.magma_out.bz2 +0 -0
  341. snappy/ptolemy/testing_files/m135__sl2_c7.magma_out.bz2 +0 -0
  342. snappy/ptolemy/testing_files/s000__sl2_c0.magma_out.bz2 +0 -0
  343. snappy/ptolemy/testing_files/s000__sl2_c1.magma_out.bz2 +0 -0
  344. snappy/ptolemy/testing_files/t00000__sl2_c0.magma_out.bz2 +0 -0
  345. snappy/ptolemy/testing_files/t00000__sl2_c1.magma_out.bz2 +0 -0
  346. snappy/ptolemy/testing_files/v0000__sl2_c0.magma_out.bz2 +0 -0
  347. snappy/ptolemy/testing_files/v0000__sl2_c1.magma_out.bz2 +0 -0
  348. snappy/ptolemy/testing_files/v0000__sl2_c2.magma_out.bz2 +0 -0
  349. snappy/ptolemy/testing_files/v0000__sl2_c3.magma_out.bz2 +0 -0
  350. snappy/ptolemy/testing_files_generalized/m003__sl2_c0.magma_out.bz2 +0 -0
  351. snappy/ptolemy/testing_files_generalized/m003__sl2_c1.magma_out.bz2 +0 -0
  352. snappy/ptolemy/testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
  353. snappy/ptolemy/testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
  354. snappy/ptolemy/testing_files_generalized/m004__sl2_c0.magma_out.bz2 +0 -0
  355. snappy/ptolemy/testing_files_generalized/m004__sl2_c1.magma_out.bz2 +0 -0
  356. snappy/ptolemy/testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
  357. snappy/ptolemy/testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
  358. snappy/ptolemy/testing_files_rur/m052__sl3_c0.rur.bz2 +0 -0
  359. snappy/ptolemy/utilities.py +236 -0
  360. snappy/raytracing/__init__.py +64 -0
  361. snappy/raytracing/additional_horospheres.py +64 -0
  362. snappy/raytracing/additional_len_spec_choices.py +63 -0
  363. snappy/raytracing/cohomology_fractal.py +197 -0
  364. snappy/raytracing/eyeball.py +123 -0
  365. snappy/raytracing/finite_raytracing_data.py +237 -0
  366. snappy/raytracing/finite_viewer.py +590 -0
  367. snappy/raytracing/geodesic_tube_info.py +174 -0
  368. snappy/raytracing/geodesics.py +246 -0
  369. snappy/raytracing/geodesics_window.py +258 -0
  370. snappy/raytracing/gui_utilities.py +293 -0
  371. snappy/raytracing/hyperboloid_navigation.py +556 -0
  372. snappy/raytracing/hyperboloid_utilities.py +234 -0
  373. snappy/raytracing/ideal_raytracing_data.py +592 -0
  374. snappy/raytracing/inside_viewer.py +974 -0
  375. snappy/raytracing/pack.py +22 -0
  376. snappy/raytracing/raytracing_data.py +126 -0
  377. snappy/raytracing/raytracing_view.py +454 -0
  378. snappy/raytracing/shaders/Eye.png +0 -0
  379. snappy/raytracing/shaders/NonGeometric.png +0 -0
  380. snappy/raytracing/shaders/__init__.py +101 -0
  381. snappy/raytracing/shaders/fragment.glsl +1744 -0
  382. snappy/raytracing/test.py +29 -0
  383. snappy/raytracing/tooltip.py +146 -0
  384. snappy/raytracing/upper_halfspace_utilities.py +98 -0
  385. snappy/raytracing/view_scale_controller.py +98 -0
  386. snappy/raytracing/zoom_slider/__init__.py +263 -0
  387. snappy/raytracing/zoom_slider/inward.png +0 -0
  388. snappy/raytracing/zoom_slider/inward18.png +0 -0
  389. snappy/raytracing/zoom_slider/outward.png +0 -0
  390. snappy/raytracing/zoom_slider/outward18.png +0 -0
  391. snappy/raytracing/zoom_slider/test.py +20 -0
  392. snappy/sage_helper.py +117 -0
  393. snappy/settings.py +409 -0
  394. snappy/shell.py +53 -0
  395. snappy/snap/__init__.py +114 -0
  396. snappy/snap/character_varieties.py +375 -0
  397. snappy/snap/find_field.py +372 -0
  398. snappy/snap/fundamental_polyhedron.py +569 -0
  399. snappy/snap/generators.py +39 -0
  400. snappy/snap/interval_reps.py +81 -0
  401. snappy/snap/kernel_structures.py +128 -0
  402. snappy/snap/mcomplex_base.py +18 -0
  403. snappy/snap/nsagetools.py +702 -0
  404. snappy/snap/peripheral/__init__.py +1 -0
  405. snappy/snap/peripheral/dual_cellulation.py +219 -0
  406. snappy/snap/peripheral/link.py +127 -0
  407. snappy/snap/peripheral/peripheral.py +159 -0
  408. snappy/snap/peripheral/surface.py +522 -0
  409. snappy/snap/peripheral/test.py +35 -0
  410. snappy/snap/polished_reps.py +335 -0
  411. snappy/snap/shapes.py +152 -0
  412. snappy/snap/slice_obs_HKL.py +668 -0
  413. snappy/snap/t3mlite/__init__.py +2 -0
  414. snappy/snap/t3mlite/arrow.py +243 -0
  415. snappy/snap/t3mlite/corner.py +22 -0
  416. snappy/snap/t3mlite/edge.py +172 -0
  417. snappy/snap/t3mlite/face.py +37 -0
  418. snappy/snap/t3mlite/files.py +211 -0
  419. snappy/snap/t3mlite/homology.py +53 -0
  420. snappy/snap/t3mlite/linalg.py +419 -0
  421. snappy/snap/t3mlite/mcomplex.py +1499 -0
  422. snappy/snap/t3mlite/perm4.py +320 -0
  423. snappy/snap/t3mlite/setup.py +12 -0
  424. snappy/snap/t3mlite/simplex.py +199 -0
  425. snappy/snap/t3mlite/spun.py +297 -0
  426. snappy/snap/t3mlite/surface.py +519 -0
  427. snappy/snap/t3mlite/test.py +20 -0
  428. snappy/snap/t3mlite/test_vs_regina.py +86 -0
  429. snappy/snap/t3mlite/tetrahedron.py +109 -0
  430. snappy/snap/t3mlite/vertex.py +42 -0
  431. snappy/snap/test.py +134 -0
  432. snappy/snap/utilities.py +288 -0
  433. snappy/test.py +209 -0
  434. snappy/test_cases.py +263 -0
  435. snappy/testing.py +131 -0
  436. snappy/tiling/__init__.py +2 -0
  437. snappy/tiling/canonical_key_dict.py +59 -0
  438. snappy/tiling/dict_based_set.py +79 -0
  439. snappy/tiling/floor.py +49 -0
  440. snappy/tiling/hyperboloid_dict.py +54 -0
  441. snappy/tiling/iter_utils.py +78 -0
  442. snappy/tiling/lifted_tetrahedron.py +22 -0
  443. snappy/tiling/lifted_tetrahedron_set.py +54 -0
  444. snappy/tiling/real_hash_dict.py +164 -0
  445. snappy/tiling/test.py +23 -0
  446. snappy/tiling/tile.py +215 -0
  447. snappy/tiling/triangle.py +33 -0
  448. snappy/tkterminal.py +920 -0
  449. snappy/twister/__init__.py +20 -0
  450. snappy/twister/main.py +646 -0
  451. snappy/twister/surfaces/S_0_1 +3 -0
  452. snappy/twister/surfaces/S_0_2 +3 -0
  453. snappy/twister/surfaces/S_0_4 +7 -0
  454. snappy/twister/surfaces/S_0_4_Lantern +8 -0
  455. snappy/twister/surfaces/S_1 +3 -0
  456. snappy/twister/surfaces/S_1_1 +4 -0
  457. snappy/twister/surfaces/S_1_2 +5 -0
  458. snappy/twister/surfaces/S_1_2_5 +6 -0
  459. snappy/twister/surfaces/S_2 +6 -0
  460. snappy/twister/surfaces/S_2_1 +8 -0
  461. snappy/twister/surfaces/S_2_heeg +10 -0
  462. snappy/twister/surfaces/S_3 +8 -0
  463. snappy/twister/surfaces/S_3_1 +10 -0
  464. snappy/twister/surfaces/S_4_1 +12 -0
  465. snappy/twister/surfaces/S_5_1 +14 -0
  466. snappy/twister/surfaces/heeg_fig8 +9 -0
  467. snappy/twister/twister_core.cpython-311-darwin.so +0 -0
  468. snappy/upper_halfspace/__init__.py +146 -0
  469. snappy/upper_halfspace/ideal_point.py +26 -0
  470. snappy/verify/__init__.py +13 -0
  471. snappy/verify/canonical.py +542 -0
  472. snappy/verify/complex_volume/__init__.py +18 -0
  473. snappy/verify/complex_volume/adjust_torsion.py +86 -0
  474. snappy/verify/complex_volume/closed.py +168 -0
  475. snappy/verify/complex_volume/compute_ptolemys.py +90 -0
  476. snappy/verify/complex_volume/cusped.py +56 -0
  477. snappy/verify/complex_volume/extended_bloch.py +201 -0
  478. snappy/verify/cusp_translations.py +85 -0
  479. snappy/verify/edge_equations.py +80 -0
  480. snappy/verify/exceptions.py +254 -0
  481. snappy/verify/hyperbolicity.py +224 -0
  482. snappy/verify/interval_newton_shapes_engine.py +523 -0
  483. snappy/verify/interval_tree.py +400 -0
  484. snappy/verify/krawczyk_shapes_engine.py +518 -0
  485. snappy/verify/maximal_cusp_area_matrix/__init__.py +46 -0
  486. snappy/verify/maximal_cusp_area_matrix/cusp_tiling_engine.py +419 -0
  487. snappy/verify/maximal_cusp_area_matrix/cusp_translate_engine.py +153 -0
  488. snappy/verify/real_algebra.py +286 -0
  489. snappy/verify/shapes.py +25 -0
  490. snappy/verify/short_slopes.py +200 -0
  491. snappy/verify/square_extensions.py +1005 -0
  492. snappy/verify/test.py +78 -0
  493. snappy/verify/upper_halfspace/__init__.py +9 -0
  494. snappy/verify/upper_halfspace/extended_matrix.py +100 -0
  495. snappy/verify/upper_halfspace/finite_point.py +283 -0
  496. snappy/verify/upper_halfspace/ideal_point.py +426 -0
  497. snappy/verify/volume.py +128 -0
  498. snappy/version.py +2 -0
  499. snappy-3.2.dist-info/METADATA +58 -0
  500. snappy-3.2.dist-info/RECORD +503 -0
  501. snappy-3.2.dist-info/WHEEL +5 -0
  502. snappy-3.2.dist-info/entry_points.txt +2 -0
  503. snappy-3.2.dist-info/top_level.txt +28 -0
@@ -0,0 +1,2290 @@
1
+ from .component import ZeroDimensionalComponent
2
+ from .rur import RUR
3
+ from . import matrix
4
+ from . import findLoops
5
+ from . import utilities
6
+ from ..sage_helper import _within_sage
7
+ from ..pari import Gen, pari
8
+ import re
9
+
10
+
11
+ class PtolemyCannotBeCheckedError(Exception):
12
+ def __init__(self):
13
+ msg = (
14
+ "Use .cross_ratios().check_against_manifold(...) since checking "
15
+ "Ptolemy coordinates for non-trivial generalized obstruction "
16
+ "class is not supported.")
17
+ Exception.__init__(self, msg)
18
+
19
+
20
+ class LogToCloseToBranchCutError(Exception):
21
+ """
22
+ An exception raised when taking log(-x) for some real number x
23
+ Due to numerical inaccuracies, we cannot know in this case whether to take
24
+ -Pi or Pi as imaginary part.
25
+ """
26
+ pass
27
+
28
+
29
+ class RelationViolationError(Exception):
30
+ """
31
+ An exception raised when some supposed relation doesn't hold exactly
32
+ or numerical.
33
+ """
34
+
35
+ def __init__(self, value, epsilon, comment):
36
+ self.value = value
37
+ self.epsilon = epsilon
38
+ self.comment = comment
39
+
40
+ def __str__(self):
41
+ r = self.comment + " is violated, "
42
+ r += "difference is %s" % self.value
43
+ if self.epsilon is None:
44
+ return r + " (exact values)"
45
+ return r + " (epsilon = %s)" % self.epsilon
46
+
47
+
48
+ class NotPU21Representation:
49
+ """
50
+ Returned by is_pu_2_1_representation if cross ratios do not fulfill
51
+ conditions to be a PU(2,1)-representation.
52
+ Contains the reason why cross ratios fail to do so.
53
+ Cast to bool evaluates to False.
54
+ """
55
+
56
+ def __init__(self, reason):
57
+ self.reason = reason
58
+
59
+ def __repr__(self):
60
+ return "NotPU21Representation(reason = %r)" % self.reason
61
+
62
+ def __bool__(self):
63
+ return False
64
+
65
+ __nonzero__ = __bool__ # backwards compatibility python 2x
66
+
67
+
68
+ class NumericalMethodError(Exception):
69
+ def __init__(self, method):
70
+ self.method = method
71
+
72
+ def __str__(self):
73
+ return "Method %s only supported for numerical values" % self.method
74
+
75
+
76
+ class ExactMethodError(Exception):
77
+ def __init__(self, method):
78
+ self.method = method
79
+
80
+ def __str__(self):
81
+ return "Method %s only supported for exact values" % self.method
82
+
83
+
84
+ def _check_relation(value, epsilon, comment):
85
+ if epsilon is None:
86
+ if not value == 0:
87
+ raise RelationViolationError(value, epsilon, comment)
88
+ else:
89
+ if not abs(value) < epsilon:
90
+ raise RelationViolationError(value, epsilon, comment)
91
+
92
+
93
+ class PtolemyCoordinates(dict):
94
+ """
95
+ Represents a solution of a Ptolemy variety as python dictionary.
96
+
97
+ === Examples ===
98
+
99
+ Construct solution from magma output:
100
+
101
+ >>> from snappy.ptolemy.processMagmaFile import _magma_output_for_4_1__sl3, solutions_from_magma
102
+ >>> from snappy import Manifold
103
+ >>> solutions = solutions_from_magma(_magma_output_for_4_1__sl3)
104
+ >>> solution = solutions[2]
105
+
106
+ Access a Ptolemy coordinate:
107
+
108
+ >>> solution['c_2100_0']
109
+ 1
110
+
111
+ >>> solution.number_field()
112
+ x^2 + x + 1
113
+
114
+ Solution is always 0 dimensional:
115
+
116
+ >>> solution.dimension
117
+ 0
118
+
119
+ Check that it is really a solution, exactly:
120
+
121
+ >>> solution.check_against_manifold()
122
+
123
+ If the solution was not created through the ptolemy module
124
+ and thus not associated to a manifold, we need to explicitly
125
+ specify one:
126
+
127
+ >>> myDict = {}
128
+ >>> for key, value in solution.items():
129
+ ... myDict[key] = value
130
+ >>> mysolution = PtolemyCoordinates(myDict)
131
+ >>> M = Manifold("4_1")
132
+ >>> solution.check_against_manifold(M)
133
+
134
+ Turn into (Galois conjugate) numerical solutions:
135
+
136
+ >>> old_precision = pari.set_real_precision(100) # with high precision
137
+ >>> numerical_solutions = solution.numerical()
138
+
139
+ Check that it is a solution, numerically:
140
+
141
+ >>> numerical_solutions[0].check_against_manifold(M, 1e-80)
142
+ >>> pari.set_real_precision(old_precision) # reset pari engine
143
+ 100
144
+
145
+ Compute cross ratios from the Ptolemy coordinates (cross ratios
146
+ according to SnapPy convention, see help(solution.cross_ratios):
147
+
148
+ >>> cross = solution.cross_ratios()
149
+ >>> cross['z_0001_0']
150
+ Mod(-x, x^2 + x + 1)
151
+
152
+ Compute volumes:
153
+
154
+ >>> volumes = cross.volume_numerical()
155
+
156
+ Check that volume is 4 times the geometric one:
157
+
158
+ >>> volume = volumes[0].abs()
159
+ >>> diff = abs(4 * M.volume() - volume)
160
+ >>> diff < 1e-9
161
+ True
162
+
163
+ Compute flattenings:
164
+
165
+ >>> flattenings = solution.flattenings_numerical()
166
+
167
+ Compute complex volumes:
168
+
169
+ >>> cvols = [flattening.complex_volume() for flattening in flattenings]
170
+ >>> volume = cvols[0].real().abs()
171
+ >>> chernSimons = cvols[0].imag()
172
+ >>> diff = abs(4 * M.volume() - volume)
173
+ >>> diff < 1e-9
174
+ True
175
+
176
+ >>> from snappy import pari
177
+ >>> normalized = chernSimons * 6 / (pari('Pi')**2)
178
+
179
+ Check that Chern Simons is zero up to 6 torsion:
180
+
181
+ >>> normalized - normalized.round() < 1e-9
182
+ True
183
+ """
184
+
185
+ def __init__(self, d, is_numerical=True, py_eval_section=None,
186
+ manifold_thunk=lambda : None,
187
+ non_trivial_generalized_obstruction_class=False):
188
+
189
+ self._manifold_thunk = manifold_thunk
190
+
191
+ self._is_numerical = is_numerical
192
+ self.dimension = 0
193
+
194
+ self._non_trivial_generalized_obstruction_class = (
195
+ non_trivial_generalized_obstruction_class)
196
+ processed_dict = d
197
+
198
+ if py_eval_section is not None:
199
+ # process the extra information that is given by
200
+ # ptolemyVariety's py_eval_section
201
+
202
+ processed_dict = py_eval_section['variable_dict'](d)
203
+ if py_eval_section.get(
204
+ 'non_trivial_generalized_obstruction_class'):
205
+ self._non_trivial_generalized_obstruction_class = True
206
+
207
+ # Caches the matrices that label the short and long edges
208
+ # of the truncated simplices building the manifold
209
+ self._edge_cache = {}
210
+
211
+ # Caches the images of a fundamental group generator
212
+ self._matrix_cache = []
213
+ self._inverse_matrix_cache = []
214
+
215
+ super().__init__(processed_dict)
216
+
217
+ def __repr__(self):
218
+ dict_repr = dict.__repr__(self)
219
+ return "PtolemyCoordinates(%s, is_numerical = %r, ...)" % (
220
+ dict_repr, self._is_numerical)
221
+
222
+ def _repr_pretty_(self, p, cycle):
223
+ if cycle:
224
+ p.text('PtolemyCoordinates(...)')
225
+ else:
226
+ with p.group(4, 'PtolemyCoordinates(',')'):
227
+ p.breakable()
228
+ p.pretty(dict(self))
229
+ p.text(',')
230
+ p.breakable()
231
+ p.text('is_numerical = %r, ...' % self._is_numerical)
232
+
233
+ def get_manifold(self):
234
+ """
235
+ Get the manifold for which this structure represents a solution
236
+ to the Ptolemy variety.
237
+ """
238
+
239
+ return self._manifold_thunk()
240
+
241
+ def num_tetrahedra(self):
242
+ """
243
+ The number of tetrahedra for which we have Ptolemy coordinates.
244
+ """
245
+ return _num_tetrahedra(self)
246
+
247
+ def N(self):
248
+ """
249
+ Get the *N* where these coordinates are for SL/PSL(*N*, **C**)-representations.
250
+ """
251
+
252
+ N, has_obstruction = _N_and_has_obstruction_for_ptolemys(self)
253
+ return N
254
+
255
+ def has_obstruction(self):
256
+ """
257
+ Whether the Ptolemy variety has legacy obstruction class that
258
+ modifies the Ptolemy relation to
259
+ """
260
+ N, has_obstruction = _N_and_has_obstruction_for_ptolemys(self)
261
+ return has_obstruction
262
+
263
+ def number_field(self):
264
+ """
265
+ For an exact solution, return the number_field spanned by the
266
+ Ptolemy coordinates. If number_field is Q, return None.
267
+ """
268
+
269
+ if self._is_numerical:
270
+ raise ExactMethodError("number_field")
271
+
272
+ return _get_number_field(self)
273
+
274
+ def numerical(self):
275
+ """
276
+ Turn exact solutions into numerical solutions using pari.
277
+
278
+ Take an exact solution:
279
+
280
+ >>> from snappy.ptolemy.processMagmaFile import _magma_output_for_4_1__sl3, solutions_from_magma
281
+ >>> solutions = solutions_from_magma(_magma_output_for_4_1__sl3)
282
+ >>> solution = solutions[2]
283
+
284
+ Turn into a numerical solution:
285
+
286
+ >>> old_precision = pari.set_real_precision(100) # with high precision
287
+ >>> numerical_solutions = solution.numerical()
288
+ >>> pari.set_real_precision(old_precision) # reset pari engine
289
+ 100
290
+
291
+ Pick one of the Galois conjugates:
292
+
293
+ >>> numerical_solution = numerical_solutions[0]
294
+ >>> value = numerical_solution['c_1110_0']
295
+ """
296
+
297
+ if self._is_numerical:
298
+ return self
299
+ return ZeroDimensionalComponent(
300
+ [ PtolemyCoordinates(
301
+ d, is_numerical=True,
302
+ manifold_thunk=self._manifold_thunk,
303
+ non_trivial_generalized_obstruction_class=(
304
+ self._non_trivial_generalized_obstruction_class))
305
+ for d in _to_numerical(self) ])
306
+
307
+ def to_PUR(self):
308
+ """
309
+ If any Ptolemy coordinates are given as Rational Univariate
310
+ Representation, convert them to Polynomial Univariate Representation and
311
+ return the result.
312
+
313
+ See to_PUR of RUR.
314
+
315
+ This conversion might lead to very large coefficients.
316
+ """
317
+
318
+ return PtolemyCoordinates(
319
+ _apply_to_RURs(self, RUR.to_PUR),
320
+ is_numerical=self._is_numerical,
321
+ manifold_thunk=self._manifold_thunk,
322
+ non_trivial_generalized_obstruction_class=(
323
+ self._non_trivial_generalized_obstruction_class))
324
+
325
+ def multiply_terms_in_RUR(self):
326
+ """
327
+ If a Ptolemy coordinate is given as Rational Univariate Representation
328
+ with numerator and denominator being a product, multiply the terms and
329
+ return the result.
330
+
331
+ See multiply_terms of RUR.
332
+
333
+ This loses information about how the numerator and denominator are
334
+ factorised.
335
+ """
336
+
337
+ return PtolemyCoordinates(
338
+ _apply_to_RURs(self, RUR.multiply_terms),
339
+ is_numerical=self._is_numerical,
340
+ manifold_thunk=self._manifold_thunk,
341
+ non_trivial_generalized_obstruction_class=(
342
+ self._non_trivial_generalized_obstruction_class))
343
+
344
+ def multiply_and_simplify_terms_in_RUR(self):
345
+ """
346
+ If a Ptolemy coordinate is given as Rational Univariate Representation
347
+ with numerator and denominator being a product, multiply the terms,
348
+ reduce the fraction and return the result.
349
+
350
+ See multiply_and_simplify_terms of RUR.
351
+
352
+ This loses information about how the numerator and denominator are
353
+ factorised.
354
+
355
+ """
356
+
357
+ return PtolemyCoordinates(
358
+ _apply_to_RURs(self, RUR.multiply_and_simplify_terms),
359
+ is_numerical=self._is_numerical,
360
+ manifold_thunk=self._manifold_thunk,
361
+ non_trivial_generalized_obstruction_class=(
362
+ self._non_trivial_generalized_obstruction_class))
363
+
364
+ def cross_ratios(self):
365
+ """
366
+ Compute cross ratios from Ptolemy coordinates. The cross ratios are
367
+ according to the SnapPy convention, so we have::
368
+
369
+ z = 1 - 1/zp, zp = 1 - 1/zpp, zpp = 1 - 1/z
370
+
371
+ where::
372
+
373
+ z is at the edge 01 and equal to s0 * s1 * (c_1010 * c_0101) / (c_1001 * c_0110)
374
+ zp is at the edge 02 and equal to - s0 * s2 * (c_1001 * c_0110) / (c_1100 * c_0011)
375
+ zpp is at the edge 03 and equal to s0 * s3 * (c_1100 * c_0011) / (c_0101 * c_1010).
376
+
377
+ Note that this is different from the convention used in
378
+ Garoufalidis, Goerner, Zickert:
379
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
380
+ https://arxiv.org/abs/1207.6711
381
+
382
+ Take an exact solution:
383
+
384
+ >>> from snappy.ptolemy.processMagmaFile import _magma_output_for_4_1__sl3, solutions_from_magma
385
+ >>> solutions = solutions_from_magma(_magma_output_for_4_1__sl3)
386
+ >>> solution = solutions[2]
387
+
388
+ Turn into cross Ratios:
389
+
390
+ >>> crossRatios = solution.cross_ratios()
391
+
392
+ Get a cross ratio:
393
+
394
+ >>> crossRatios['zp_0010_0']
395
+ Mod(-x, x^2 + x + 1)
396
+
397
+ Check the relationship between cross ratios:
398
+
399
+ >>> crossRatios['z_0010_0'] == 1 - 1 / crossRatios['zp_0010_0']
400
+ True
401
+
402
+ >>> crossRatios['zp_0010_0'] == 1 - 1 / crossRatios['zpp_0010_0']
403
+ True
404
+
405
+ >>> crossRatios['zpp_0010_0'] == 1 - 1 / crossRatios['z_0010_0']
406
+ True
407
+
408
+ Get information about what one can do with cross ratios
409
+ """
410
+ return CrossRatios(_ptolemy_to_cross_ratio(self)[0],
411
+ is_numerical=self._is_numerical,
412
+ manifold_thunk=self._manifold_thunk)
413
+
414
+ def cross_ratios_numerical(self):
415
+ """
416
+ Turn exact solutions into numerical and then compute cross ratios.
417
+ See numerical() and cross_ratios().
418
+ """
419
+
420
+ if self._is_numerical:
421
+ return self.cross_ratios()
422
+ else:
423
+ return ZeroDimensionalComponent(
424
+ [num.cross_ratios() for num in self.numerical()])
425
+
426
+ def flattenings_numerical(self):
427
+ """
428
+ Turn into numerical solutions and compute flattenings, see
429
+ help(snappy.ptolemy.coordinates.Flattenings)
430
+ Also see numerical()
431
+
432
+ Get Ptolemy coordinates.
433
+
434
+ >>> from snappy.ptolemy.processMagmaFile import _magma_output_for_4_1__sl3, solutions_from_magma
435
+ >>> solutions = solutions_from_magma(_magma_output_for_4_1__sl3)
436
+ >>> solution = solutions[2]
437
+
438
+ Compute a numerical solution
439
+
440
+ >>> flattenings = solution.flattenings_numerical()
441
+
442
+ Get more information with help(flattenings[0])
443
+ """
444
+
445
+ if self._is_numerical:
446
+ # Used as a factor when taking log's to shift the branch slightly
447
+ # from the standard branch cut at the negative real line
448
+ branch_factor = 1
449
+
450
+ # Try different branch cuts 1000 times
451
+ for i in range(1000):
452
+ try:
453
+ # get the dictionary containing flattenings
454
+ # and the evenN that describes in what
455
+ # flavor of the Extended Bloch group the result lives in
456
+ d, evenN = _ptolemy_to_cross_ratio(
457
+ self,
458
+ branch_factor,
459
+ self._non_trivial_generalized_obstruction_class,
460
+ as_flattenings=True)
461
+
462
+ return Flattenings(d,
463
+ manifold_thunk=self._manifold_thunk,
464
+ evenN=evenN)
465
+ except LogToCloseToBranchCutError:
466
+ # Values to close to the branch cut, just multiply
467
+ # by a small offset
468
+ branch_factor *= pari('exp(0.0001 * I)')
469
+
470
+ raise Exception("Could not find non-ambiguous branch cut for log")
471
+ else:
472
+ return ZeroDimensionalComponent(
473
+ [num.flattenings_numerical() for num in self.numerical()])
474
+
475
+ def volume_numerical(self, drop_negative_vols=False):
476
+ """
477
+ Turn into (Galois conjugate) numerical solutions and compute volumes.
478
+ If already numerical, only return the one volume.
479
+ See numerical().
480
+
481
+ If drop_negative_vols = True is given as optional argument,
482
+ only return non-negative volumes.
483
+ """
484
+ if self._is_numerical:
485
+ return self.cross_ratios().volume_numerical()
486
+ else:
487
+ vols = ZeroDimensionalComponent(
488
+ [num.volume_numerical() for num in self.numerical()])
489
+ if drop_negative_vols:
490
+ return [vol for vol in vols if vol > -1e-12]
491
+ return vols
492
+
493
+ def complex_volume_numerical(self,
494
+ drop_negative_vols=False,
495
+ with_modulo=False):
496
+ """
497
+ Turn into (Galois conjugate) numerical solutions and compute complex
498
+ volumes. If already numerical, return the volume.
499
+
500
+ Complex volume is defined up to i*pi**2/6.
501
+
502
+ See numerical(). If drop_negative_vols = True is given as optional
503
+ argument, only return complex volumes with non-negative real part.
504
+ """
505
+
506
+ if self._is_numerical:
507
+ return self.flattenings_numerical().complex_volume(
508
+ with_modulo=with_modulo)
509
+ else:
510
+ cvols = ZeroDimensionalComponent(
511
+ [ num.flattenings_numerical().complex_volume(
512
+ with_modulo=with_modulo)
513
+ for num in self.numerical()])
514
+ if drop_negative_vols:
515
+ return [cvol for cvol in cvols if cvol.real() > -1e-12]
516
+ return cvols
517
+
518
+ def _coordinate_at_tet_and_point(self, tet, pt):
519
+ """
520
+ Given the index of a tetrahedron and a quadruple (any iterable) of
521
+ integer to mark an integral point on that tetrahedron, returns the
522
+ associated Ptolemy coordinate.
523
+ If this is a vertex Ptolemy coordinate, always return 1 without
524
+ checking for it in the dictionary.
525
+ """
526
+
527
+ # Handle the vertex Ptolemy coordinate case
528
+ if sum(pt) in pt:
529
+ return 1
530
+
531
+ # Normal case
532
+ return self['c_%d%d%d%d' % tuple(pt) + '_%d' % tet]
533
+
534
+ def _get_obstruction_variable(self, face, tet):
535
+ """
536
+ Get the obstruction variable sigma_face for the given face and
537
+ tetrahedron. Return 1 if there is no such obstruction class.
538
+ """
539
+
540
+ key = "s_%d_%d" % (face, tet)
541
+ return self.get(key, +1) # Default to 1 if no obstruction class given
542
+
543
+ @staticmethod
544
+ def _three_perm_sign(v0, v1, v2):
545
+ """
546
+ Returns the sign of the permutation necessary to bring the three
547
+ elements v0, v1, v2 in order.
548
+ """
549
+ if v0 < v2 and v2 < v1:
550
+ return -1
551
+ if v1 < v0 and v0 < v2:
552
+ return -1
553
+ if v2 < v1 and v1 < v0:
554
+ return -1
555
+ return +1
556
+
557
+ def diamond_coordinate(self, tet, v0, v1, v2, pt):
558
+ """
559
+ Returns the diamond coordinate for tetrahedron with index tet
560
+ for the face with vertices v0, v1, v2 (integers between 0 and 3) and
561
+ integral point pt (quadruple adding up to N-2).
562
+
563
+ See Definition 10.1:
564
+ Garoufalidis, Goerner, Zickert:
565
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
566
+ https://arxiv.org/abs/1207.6711
567
+ """
568
+
569
+ # Integral points that are indices of Ptolemy coordinates
570
+ pt_v0_v0 = [ a + 2 * _kronecker_delta(v0, i)
571
+ for i, a in enumerate(pt) ]
572
+ pt_v0_v1 = [ a + _kronecker_delta(v0, i) + _kronecker_delta(v1, i)
573
+ for i, a in enumerate(pt) ]
574
+ pt_v0_v2 = [ a + _kronecker_delta(v0, i) + _kronecker_delta(v2, i)
575
+ for i, a in enumerate(pt) ]
576
+ pt_v1_v2 = [ a + _kronecker_delta(v1, i) + _kronecker_delta(v2, i)
577
+ for i, a in enumerate(pt) ]
578
+
579
+ # Ptolemy coordinates involved
580
+ c_pt_v0_v0 = self._coordinate_at_tet_and_point(tet, pt_v0_v0)
581
+ c_pt_v0_v1 = self._coordinate_at_tet_and_point(tet, pt_v0_v1)
582
+ c_pt_v0_v2 = self._coordinate_at_tet_and_point(tet, pt_v0_v2)
583
+ c_pt_v1_v2 = self._coordinate_at_tet_and_point(tet, pt_v1_v2)
584
+
585
+ # Obstruction variable
586
+ # See Definition 9.23 of
587
+ # Garoufalidis, Thurston, Zickert
588
+ # The Complex Volume of SL(n,C)-Representations of 3-Manifolds
589
+ # httpss://arxiv.org/abs/1111.2828
590
+ face = next(iter(set(range(4)) - {v0, v1, v2}))
591
+ obstruction = self._get_obstruction_variable(face, tet)
592
+
593
+ # The epsilon permutation sign from Definition 10.1 of
594
+ # Garoufalidis, Goerner, Zickert:
595
+ # Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
596
+ # https://arxiv.org/abs/1207.6711
597
+ s = PtolemyCoordinates._three_perm_sign(v0, v1, v2)
598
+
599
+ # The equation from the same Definition
600
+ return - (obstruction * s *
601
+ (c_pt_v0_v0 * c_pt_v1_v2) /
602
+ (c_pt_v0_v1 * c_pt_v0_v2))
603
+
604
+ def ratio_coordinate(self, tet, v0, v1, pt):
605
+ """
606
+ Returns the ratio coordinate for tetrahedron with index tet
607
+ for the edge from v0 to v1 (integers between 0 and 3) and integral
608
+ point pt (quadruple adding up N-1) on the edge.
609
+
610
+ See Definition 10.2:
611
+ Garoufalidis, Goerner, Zickert:
612
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
613
+ https://arxiv.org/abs/1207.6711
614
+
615
+ Note that this definition turned out to have the wrong sign. Multiply
616
+ the result by -1 if v1 < v0 and N is even.
617
+ """
618
+
619
+ # Integral points on the edge
620
+ pt_v0 = [ a + _kronecker_delta(v0, i) for i, a in enumerate(pt) ]
621
+ pt_v1 = [ a + _kronecker_delta(v1, i) for i, a in enumerate(pt) ]
622
+
623
+ # Ptolemy coordinates at those integral points
624
+ c_pt_v0 = self._coordinate_at_tet_and_point(tet, pt_v0)
625
+ c_pt_v1 = self._coordinate_at_tet_and_point(tet, pt_v1)
626
+
627
+ # Sign
628
+ s = (-1) ** pt[v1]
629
+
630
+ if v1 < v0 and (self.N() % 2 == 0):
631
+ s *= -1
632
+
633
+ # Equation from Definition 10.2
634
+ return s * c_pt_v1 / c_pt_v0
635
+
636
+ def _get_identity_matrix(self):
637
+
638
+ # Get N
639
+ N = self.N()
640
+
641
+ return [[_kronecker_delta(i, j) for i in range(N)] for j in range(N)]
642
+
643
+ def long_edge(self, tet, v0, v1, v2):
644
+ """
645
+ The matrix that labels a long edge from v0 to v1 (integers between 0
646
+ and 3) of a (doubly) truncated simplex corresponding to an ideal
647
+ tetrahedron with index tet.
648
+
649
+ This matrix was labeled alpha^{v0v1v2} (v2 does not matter for non
650
+ double-truncated simplex) in Figure 18 of
651
+ Garoufalidis, Goerner, Zickert:
652
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
653
+ https://arxiv.org/abs/1207.6711
654
+
655
+ It is computed using equation 10.4. Note that the ratio coordinate
656
+ is different from the definition in the paper (see ratio_coordinate).
657
+
658
+ The resulting matrix is given as a python list of lists.
659
+ """
660
+
661
+ # Key for the cache
662
+ key = 'long_%d_%d%d' % (tet, v0, v1)
663
+
664
+ # Fill cache if necessary
665
+ if key not in self._edge_cache:
666
+
667
+ # Get N
668
+ N = self.N()
669
+
670
+ # Start with the 0 matrix
671
+ m = [[0 for i in range(N)] for j in range(N)]
672
+
673
+ # Traverse the edge to fill the counter diagonal elements
674
+ # with the ratio coordinates
675
+ for c in range(N):
676
+ r = N - 1 - c
677
+ pt = [ r * _kronecker_delta(v0, i) + c * _kronecker_delta(v1, i)
678
+ for i in range(4) ]
679
+ m[r][c] = self.ratio_coordinate(tet, v0, v1, pt)
680
+
681
+ # Set in cache
682
+ self._edge_cache[key] = m
683
+
684
+ # Return
685
+ return self._edge_cache[key]
686
+
687
+ def middle_edge(self, tet, v0, v1, v2):
688
+ """
689
+ The matrix that labels a middle edge on the face v0, v1, v2 (integers
690
+ between 0 and 3) of a doubly truncated simplex (or a short edge of the
691
+ truncated simplex) corresponding to an ideal tetrahedron with index
692
+ tet.
693
+
694
+ This matrix was labeled beta^{v0v1v2} in Figure 18 of
695
+ Garoufalidis, Goerner, Zickert:
696
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
697
+ https://arxiv.org/abs/1207.6711
698
+
699
+ It is computed using equation 10.4.
700
+
701
+ The resulting matrix is given as a python list of lists.
702
+ """
703
+
704
+ # Key for the cache
705
+ key = 'middle_%d_%d%d%d' % (tet, v0, v1, v2)
706
+
707
+ # Fill cache if necessary
708
+ if key not in self._edge_cache:
709
+
710
+ # Get N
711
+ N = self.N()
712
+
713
+ # Start with identity
714
+ m = self._get_identity_matrix()
715
+
716
+ # Compute the product in equation 10.4
717
+ for a0, a1, a2 in utilities.triples_with_fixed_sum_iterator(N - 2):
718
+
719
+ # Get integral point for diamond coordinate
720
+ pt = [ a1 * _kronecker_delta(v0, i) +
721
+ a2 * _kronecker_delta(v1, i) +
722
+ a0 * _kronecker_delta(v2, i) for i in range(4) ]
723
+
724
+ # Compute diamond coordinate
725
+ diamond = self.diamond_coordinate(tet, v0, v1, v2, pt)
726
+
727
+ # Multiply result with the x matrix
728
+ m = matrix.matrix_mult(m, _X(N, a1 + 1, diamond))
729
+
730
+ # Fill cache
731
+ self._edge_cache[key] = m
732
+
733
+ return self._edge_cache[key]
734
+
735
+ def short_edge(self, tet, v0, v1, v2):
736
+ """
737
+ Returns the identity. This is because we can think of the matrices
738
+ given by Ptolemy coordinates of living on truncated simplices which
739
+ can be though of as doubly truncated simplices where all short edges
740
+ are collapsed, hence labeled by the identity.
741
+
742
+ See equation 10.4 in
743
+ Garoufalidis, Goerner, Zickert:
744
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
745
+ https://arxiv.org/abs/1207.6711
746
+ """
747
+
748
+ # Key for the cache
749
+ key = 'short'
750
+
751
+ # Fill cache if necessary
752
+ if key not in self._edge_cache:
753
+
754
+ # Get N
755
+ N = self.N()
756
+
757
+ # Take the identity matrix
758
+ m = self._get_identity_matrix()
759
+
760
+ # Fill cache
761
+ self._edge_cache[key] = m
762
+
763
+ return self._edge_cache[key]
764
+
765
+ def _init_matrix_and_inverse_cache(self):
766
+ # Fill the caches of matrices corresponding to the
767
+ # fundamental group generators and their inverses
768
+
769
+ if self._matrix_cache and self._inverse_matrix_cache:
770
+ return
771
+
772
+ # Compute all the matrices for the generators and there inverses
773
+ # The short edges of the doubly truncated simplices are all identities
774
+ # (they will be collapset in the truncated simplex), thus we give them
775
+ # no penalty.
776
+ self._matrix_cache, self._inverse_matrix_cache = (
777
+ findLoops.images_of_original_generators(self,
778
+ penalties=(0, 1, 1)))
779
+
780
+ def evaluate_word(self, word, G=None):
781
+ """
782
+ Given a word in the generators of the fundamental group,
783
+ compute the corresponding matrix. By default, these are the
784
+ generators of the unsimplified presentation of the fundamental
785
+ group. An optional SnapPy fundamental group can be given if the
786
+ words are in generators of a different presentation, e.g.,
787
+ c.evaluate_word(word, M.fundamental_group(True)) to
788
+ evaluate a word in the simplified presentation returned by
789
+ M.fundamental_group(True).
790
+
791
+ For now, the matrix is returned as list of lists.
792
+ """
793
+
794
+ # Init the matrices corresponding to generators
795
+ self._init_matrix_and_inverse_cache()
796
+
797
+ return findLoops.evaluate_word(
798
+ self._get_identity_matrix(),
799
+ self._matrix_cache,
800
+ self._inverse_matrix_cache,
801
+ word,
802
+ G)
803
+
804
+ def _testing_assert_identity(self, m,
805
+ allow_sign_if_obstruction_class=False):
806
+
807
+ N = self.N()
808
+
809
+ null = [[0 for i in range(N)] for j in range(N)]
810
+ identity = self._get_identity_matrix()
811
+
812
+ if allow_sign_if_obstruction_class and self.has_obstruction():
813
+
814
+ if not (matrix.matrix_add(m, identity) == null or
815
+ matrix.matrix_sub(m, identity) == null):
816
+ raise Exception("Cocycle condition violated: %s" % m)
817
+
818
+ else:
819
+
820
+ if not matrix.matrix_sub(m, identity) == null:
821
+ raise Exception("Cocycle condition violated: %s" % m)
822
+
823
+ def _testing_check_cocycles(self):
824
+ for tet in range(self.num_tetrahedra()):
825
+ # Check middle edges is inverse when direction reversed
826
+ for v in [(0,1,2),(0,1,3),(0,2,1),(0,2,3),(0,3,1),(0,3,2),
827
+ (1,0,2),(1,0,3),(1,2,0),(1,2,3),(1,3,0),(1,3,2),
828
+ (2,0,1),(2,0,3),(2,1,0),(2,1,3),(2,3,0),(2,3,1),
829
+ (3,0,1),(3,0,2),(3,1,0),(3,1,2),(3,2,0),(3,2,1)]:
830
+ m1 = self.middle_edge(tet,v[0],v[1],v[2])
831
+ m2 = self.middle_edge(tet,v[0],v[2],v[1])
832
+ self._testing_assert_identity(
833
+ matrix.matrix_mult(m1, m2))
834
+
835
+ # Check long edges is inverse when direction reversed
836
+ for v in [(0,1,2), (0,2,1), (0,3,1),
837
+ (1,0,2), (1,2,0), (1,3,0),
838
+ (2,0,1), (2,1,0), (2,3,0),
839
+ (3,0,1), (3,1,0), (3,2,0)]:
840
+ m1 = self.long_edge(tet,v[0],v[1],v[2])
841
+ m2 = self.long_edge(tet,v[1],v[0],v[2])
842
+ self._testing_assert_identity(
843
+ matrix.matrix_mult(m1, m2))
844
+
845
+ # Check triangle for each vertex
846
+ for v in [(0,1,2,3), (1,2,3,0), (2,3,0,1), (3,0,1,2)]:
847
+ m1 = self.middle_edge(tet, v[0], v[1], v[2])
848
+ m2 = self.middle_edge(tet, v[0], v[2], v[3])
849
+ m3 = self.middle_edge(tet, v[0], v[3], v[1])
850
+
851
+ self._testing_assert_identity(
852
+ matrix.matrix_mult(
853
+ m1, matrix.matrix_mult(m2, m3)))
854
+
855
+ # Check hexagon for each face
856
+ for v in [(0,1,2), (0,1,3), (0,2,3), (1,2,3)]:
857
+ m1 = self.middle_edge(tet,v[0],v[1],v[2])
858
+ m2 = self.long_edge( tet,v[0],v[2],v[1])
859
+ m3 = self.middle_edge(tet,v[2],v[0],v[1])
860
+ m4 = self.long_edge( tet,v[2],v[1],v[0])
861
+ m5 = self.middle_edge(tet,v[1],v[2],v[0])
862
+ m6 = self.long_edge( tet,v[1],v[0],v[2])
863
+ self._testing_assert_identity(
864
+ matrix.matrix_mult(
865
+ m1,
866
+ matrix.matrix_mult(
867
+ m2,
868
+ matrix.matrix_mult(
869
+ m3,
870
+ matrix.matrix_mult(
871
+ m4,
872
+ matrix.matrix_mult(m5,m6))))), True)
873
+
874
+ def check_against_manifold(self, M=None, epsilon=None):
875
+ """
876
+ Checks that the given solution really is a solution to the Ptolemy
877
+ variety of a manifold. See help(ptolemy.PtolemyCoordinates) for
878
+ more example.
879
+
880
+ === Arguments ===
881
+
882
+ * M --- manifold to check this for
883
+ * epsilon --- maximal allowed error when checking the relations, use
884
+ None for exact comparison.
885
+ """
886
+
887
+ if M is None:
888
+ M = self.get_manifold()
889
+
890
+ if M is None:
891
+ raise Exception("Need to give manifold")
892
+
893
+ if self._non_trivial_generalized_obstruction_class:
894
+ raise PtolemyCannotBeCheckedError()
895
+
896
+ num_tets = self.num_tetrahedra()
897
+ N, has_obstruction_class = _N_and_has_obstruction_for_ptolemys(self)
898
+
899
+ if not M.num_tetrahedra() == num_tets:
900
+ raise Exception("Number tetrahedra not matching")
901
+
902
+ if has_obstruction_class:
903
+ # check cocycle condition
904
+ for tet in range(num_tets):
905
+ _check_relation(
906
+ self._get_obstruction_variable(0, tet) *
907
+ self._get_obstruction_variable(1, tet) *
908
+ self._get_obstruction_variable(2, tet) *
909
+ self._get_obstruction_variable(3, tet) - 1,
910
+ epsilon,
911
+ "Obstruction cocycle")
912
+ # check identified faces
913
+ for dummy_sign, power, var1, var2 in (
914
+ M._ptolemy_equations_identified_face_classes()):
915
+ _check_relation(
916
+ self[var1] - self[var2],
917
+ epsilon,
918
+ "Identification of face classes")
919
+
920
+ # Check identified Ptolemy coordinates
921
+ for sign, power, var1, var2 in (
922
+ M._ptolemy_equations_identified_coordinates(N)):
923
+ _check_relation(
924
+ self[var1] - sign * self[var2],
925
+ epsilon,
926
+ "Identification of Ptolemy coordinates")
927
+
928
+ # Check Ptolemy relationship
929
+ for tet in range(num_tets):
930
+ for index in utilities.quadruples_with_fixed_sum_iterator(N - 2):
931
+
932
+ def get_ptolemy_coordinate(addl_index):
933
+ total_index = matrix.vector_add(index, addl_index)
934
+ key = "c_%d%d%d%d" % tuple(total_index) + "_%d" % tet
935
+ return self[key]
936
+
937
+ s0 = self._get_obstruction_variable(0, tet)
938
+ s1 = self._get_obstruction_variable(1, tet)
939
+ s2 = self._get_obstruction_variable(2, tet)
940
+ s3 = self._get_obstruction_variable(3, tet)
941
+
942
+ rel = ( s0 * s1 * get_ptolemy_coordinate((1,1,0,0))
943
+ * get_ptolemy_coordinate((0,0,1,1))
944
+ - s0 * s2 * get_ptolemy_coordinate((1,0,1,0))
945
+ * get_ptolemy_coordinate((0,1,0,1))
946
+ + s0 * s3 * get_ptolemy_coordinate((1,0,0,1))
947
+ * get_ptolemy_coordinate((0,1,1,0)))
948
+
949
+ _check_relation(rel,
950
+ epsilon,
951
+ "Ptolemy relation")
952
+
953
+ def is_geometric(self, epsilon=1e-6):
954
+ """
955
+ Returns true if all shapes corresponding to this solution have positive
956
+ imaginary part.
957
+
958
+ If the solutions are exact, it returns true if one of the corresponding
959
+ numerical solutions is geometric.
960
+
961
+ An optional epsilon can be given. An imaginary part of a shape is
962
+ considered positive if it is larger than this epsilon.
963
+ """
964
+
965
+ if self._is_numerical:
966
+ return self.cross_ratios().is_geometric(epsilon)
967
+ else:
968
+ for numerical_sol in self.numerical():
969
+ if numerical_sol.is_geometric(epsilon):
970
+ return True
971
+ return False
972
+
973
+
974
+ class Flattenings(dict):
975
+ """
976
+ Represents a flattening assigned to each edge of a simplex as dictionary.
977
+
978
+ We assign to each pair of parallel edges of each simplex a triple (w, z, p)
979
+ such that::
980
+
981
+ w = log(z) + p * (2 * pi * i / N) where N is fixed and even.
982
+
983
+ For N = 2, the three triples belonging to a simplex form a combinatorial
984
+ flattening (w0, w1, w2) as defined in Definition 3.1 in
985
+ Walter D. Neumann, Extended Bloch group and the Cheeger-Chern-Simons class
986
+ https://arxiv.org/abs/math.GT/0307092
987
+
988
+ For N > 2, the three triples form a generalized combinatorial flattening
989
+ (w0, w1, w2) that gives an element in the generalized Extended Bloch group
990
+ which is the Extended Bloch group corresponding to the Riemann surface
991
+ given by::
992
+
993
+ u1 * e^w0 + u2 * e^w1 = 1
994
+
995
+ where u1^N = u2^N = 1.
996
+
997
+ A representation in SL(n,C) and SL(n,C)/{+1,-1} with n even gives an element
998
+ in the generalized Extended Bloch group for N = 2.
999
+ A representation in PSL(n,C) with n even in the group for N = n.
1000
+ A representation in PSL(n,C) with n odd in the group for N = 2 * n.
1001
+
1002
+ This work has not been published yet.
1003
+
1004
+ If f is a flattening, then in the notation of Neumann, the value of::
1005
+
1006
+ f['z_xxxx_y'] is (w0, z, p)
1007
+ f['zp_xxxx_y'] is (w1, z', q)
1008
+ f['zpp_xxxx_y'] is (w2, z'', r).
1009
+ """
1010
+
1011
+ def __init__(self, d, manifold_thunk=lambda : None, evenN=2):
1012
+ super().__init__(d)
1013
+ self._is_numerical = True
1014
+ self._manifold_thunk = manifold_thunk
1015
+ self.dimension = 0
1016
+
1017
+ # The N for which we get the generalized Extended Bloch group
1018
+ self._evenN = evenN
1019
+
1020
+ def __repr__(self):
1021
+ dict_repr = dict.__repr__(self)
1022
+ return "Flattenings(%s, ...)" % dict_repr
1023
+
1024
+ def _repr_pretty_(self, p, cycle):
1025
+ if cycle:
1026
+ p.text('Flattenings(...)')
1027
+ else:
1028
+ with p.group(4, 'Flattenings(',')'):
1029
+ p.breakable()
1030
+ p.pretty(dict(self))
1031
+ p.text(', ...')
1032
+
1033
+ def get_manifold(self):
1034
+ """
1035
+ Get the manifold for which this structure represents a flattening.
1036
+ """
1037
+
1038
+ return self._manifold_thunk()
1039
+
1040
+ def num_tetrahedra(self):
1041
+ """
1042
+ The number of tetrahedra for which we have cross ratios.
1043
+ """
1044
+ return _num_tetrahedra(self)
1045
+
1046
+ def N(self):
1047
+ """
1048
+ Get the N such that these cross ratios are for
1049
+ SL/PSL(N,C)-representations.
1050
+ """
1051
+
1052
+ return _N_for_shapes(self)
1053
+
1054
+ @classmethod
1055
+ def from_tetrahedra_shapes_of_manifold(cls, M):
1056
+ """
1057
+ Takes as argument a manifold and produces (weak) flattenings using
1058
+ the tetrahedra_shapes of the manifold M.
1059
+
1060
+ >>> from snappy import Manifold
1061
+ >>> M = Manifold("5_2")
1062
+ >>> flattenings = Flattenings.from_tetrahedra_shapes_of_manifold(M)
1063
+ >>> flattenings.check_against_manifold(M)
1064
+ >>> flattenings.check_against_manifold()
1065
+ """
1066
+
1067
+ PiI = pari('Pi * I')
1068
+
1069
+ num_tets = M.num_tetrahedra()
1070
+
1071
+ z_cross_ratios = M.tetrahedra_shapes(
1072
+ part='rect', dec_prec=pari.get_real_precision())
1073
+
1074
+ all_cross_ratios = sum(
1075
+ [ [z, 1 / (1-z), 1 - 1/z] for z in z_cross_ratios], [])
1076
+
1077
+ log_all_cross_ratios = [ z.log() for z in all_cross_ratios ]
1078
+
1079
+ def flattening_condition(r):
1080
+ return (3 * r * [0]
1081
+ + 3 * [1]
1082
+ + 3 * (num_tets - r - 1) * [0])
1083
+
1084
+ flattening_conditions = [
1085
+ flattening_condition(r) for r in range(num_tets)]
1086
+
1087
+ try: # works for snappy.SnapPy.SimpleMatrix
1088
+ equations = M.gluing_equations().data
1089
+ except AttributeError: # works Sage's matrix class
1090
+ equations = [
1091
+ [ int(c) for c in row] for row in M.gluing_equations().rows()]
1092
+
1093
+ all_equations = equations + flattening_conditions
1094
+
1095
+ u, v, d_mat = matrix.smith_normal_form(all_equations)
1096
+
1097
+ extra_cols = len(all_equations[0]) - len(all_equations)
1098
+
1099
+ d = [d_mat[r][r + extra_cols] for r in range(len(d_mat))]
1100
+
1101
+ # errors to the gluing equations and flattening condition
1102
+ # when using the logarithms without adding p * pi * i as complex
1103
+ # numbers
1104
+ errors = matrix.matrix_mult_vector(all_equations,
1105
+ log_all_cross_ratios)
1106
+
1107
+ # divide by pi * i and turn into integers
1108
+ int_errors = [ (x / PiI).real().round() for x in errors ]
1109
+
1110
+ int_errors_in_other_basis = matrix.matrix_mult_vector(u, int_errors)
1111
+
1112
+ def quotient(x, y):
1113
+ if x == 0 and y == 0:
1114
+ return 0
1115
+
1116
+ assert x % y == 0, "%s %s" % (x, y)
1117
+ return x / y
1118
+
1119
+ flattenings_in_other_basis = (
1120
+ extra_cols * [0] +
1121
+ [ - quotient(x, y)
1122
+ for x, y in zip(int_errors_in_other_basis, d) ])
1123
+
1124
+ flattenings = matrix.matrix_mult_vector(v, flattenings_in_other_basis)
1125
+
1126
+ assert (matrix.matrix_mult_vector(all_equations, flattenings) ==
1127
+ [-x for x in int_errors])
1128
+
1129
+ keys = sum([ ['z_0000_%d' % i,
1130
+ 'zp_0000_%d' % i,
1131
+ 'zpp_0000_%d' % i] for i in range(num_tets)],[])
1132
+
1133
+ Mcopy = M.copy()
1134
+
1135
+ return Flattenings(
1136
+ {k: (log + PiI * p, z, p)
1137
+ for k, log, z, p in zip(keys, log_all_cross_ratios,
1138
+ all_cross_ratios, flattenings)},
1139
+ manifold_thunk=lambda : Mcopy)
1140
+
1141
+ def get_order(self):
1142
+ """
1143
+ Returns the number N. This flattening represents an element in the
1144
+ generalized Extended Bloch group for the Riemann surface given by
1145
+ u1 * e^w0 + u2 * e^w1 = 1 where u1^N = u2^N = 1.
1146
+ """
1147
+
1148
+ return self._evenN
1149
+
1150
+ def get_zpq_triple(self, key_z):
1151
+ """
1152
+ Gives a flattening as triple [z;p,q] representing an element
1153
+ in the generalized Extended Bloch group similar to the way the
1154
+ triple [z;p,q] is used in Lemma 3.2 in
1155
+ Walter D. Neumann, Extended Bloch group and the Cheeger-Chern-Simons class
1156
+ https://arxiv.org/abs/math.GT/0307092
1157
+ """
1158
+ if not key_z[:2] == 'z_':
1159
+ raise Exception("Need to be called with cross ratio variable z_....")
1160
+ key_zp = 'zp_' + key_z[2:]
1161
+
1162
+ w, z, p = self[key_z]
1163
+ wp, zp, q_canonical_branch_cut = self[key_zp]
1164
+
1165
+ # Note that the q in l(z;p,q) and in Definition 3.1 are different if
1166
+ # z is on the real axis and > 1!!!
1167
+ # Thus we need to compute the q again here according to the formula
1168
+ # for l(z;p,q)
1169
+
1170
+ pari_z = _convert_to_pari_float(z)
1171
+
1172
+ f = pari('2 * Pi * I') / self._evenN
1173
+
1174
+ q_dilog_branch_cut = ((wp + (1-pari_z).log()) / f).round()
1175
+
1176
+ return (z, p, q_dilog_branch_cut)
1177
+
1178
+ def complex_volume(self, with_modulo=False):
1179
+ """
1180
+ Compute complex volume. The complex volume is defined only up to
1181
+ some multiple of m where m = i * pi**2/6 for PSL(2,C) and SL(N,C)
1182
+ and m = i * pi**2/18 for PSL(3,C).
1183
+
1184
+ When called with with_modulo = True, gives a pair
1185
+ (volume, m)
1186
+ """
1187
+
1188
+ if self._evenN == 2:
1189
+ m = pari('Pi^2/6')
1190
+ else:
1191
+ m = pari('Pi^2/18')
1192
+
1193
+ sum_L_functions = sum(
1194
+ [
1195
+ _L_function(
1196
+ self.get_zpq_triple(key), self._evenN)
1197
+ for key in list(self.keys())
1198
+ if key[:2] == 'z_' ])
1199
+
1200
+ cvol = sum_L_functions / pari('I')
1201
+ vol = cvol.real()
1202
+ cs = cvol.imag() % m
1203
+
1204
+ if cs > m/2 + pari('1e-12'):
1205
+ cs = cs - m
1206
+
1207
+ cvol = vol + cs * pari('I')
1208
+
1209
+ if with_modulo:
1210
+ if self._evenN not in [2, 6]:
1211
+ raise Exception("Unknown torsion")
1212
+
1213
+ return cvol, m * pari('I')
1214
+ return cvol
1215
+
1216
+ def check_against_manifold(self, M=None, epsilon=1e-10):
1217
+ """
1218
+ Checks that the flattening really is a solution to the logarithmic
1219
+ PGL(N,C) gluing equations of a manifold. Usage similar to
1220
+ check_against_manifold of Ptolemy Coordinates, see
1221
+ help(ptolemy.Coordinates) for similar examples.
1222
+
1223
+ === Arguments ===
1224
+
1225
+ M --- manifold to check this for
1226
+ epsilon --- maximal allowed error when checking the equations
1227
+ """
1228
+
1229
+ if M is None:
1230
+ M = self.get_manifold()
1231
+
1232
+ if M is None:
1233
+ raise Exception("Need to give manifold")
1234
+
1235
+ f = pari('2 * Pi * I') / self._evenN
1236
+
1237
+ for w, z, p in list(self.values()):
1238
+ _check_relation(
1239
+ w - (z.log() + f * p),
1240
+ epsilon,
1241
+ "Flattening relation w == log(z) + PiI * p")
1242
+
1243
+ for k in list(self.keys()):
1244
+ if k[:2] == 'z_':
1245
+ w, z, p = self[k]
1246
+ wp, zp, q = self['zp_'+k[2:]]
1247
+ wpp, zpp, r = self['zpp_'+k[2:]]
1248
+ _check_relation(
1249
+ w + wp + wpp,
1250
+ epsilon,
1251
+ "Flattening relation w0 + w1 + w2 == 0")
1252
+
1253
+ some_z = list(self.keys())[0]
1254
+ variable_name, index, tet_index = some_z.split('_')
1255
+ if variable_name not in ['z', 'zp', 'zpp']:
1256
+ raise Exception("Variable not z, zp, or, zpp")
1257
+ if len(index) != 4:
1258
+ raise Exception("Not 4 indices")
1259
+ N = sum([int(x) for x in index]) + 2
1260
+
1261
+ matrix_with_explanations = M.gluing_equations_pgl(
1262
+ N, equation_type='all')
1263
+
1264
+ matrix = matrix_with_explanations.matrix
1265
+ rows = matrix_with_explanations.explain_rows
1266
+ cols = matrix_with_explanations.explain_columns
1267
+
1268
+ for row in range(len(rows)):
1269
+ s = 0
1270
+ for col in range(len(cols)):
1271
+ flattening_variable = cols[col]
1272
+ w, z, p = self[flattening_variable]
1273
+ s = s + w
1274
+ _check_relation(
1275
+ s,
1276
+ epsilon,
1277
+ "Gluing equation %s" % rows[row])
1278
+
1279
+
1280
+ class CrossRatios(dict):
1281
+ """
1282
+ Represents assigned shape parameters/cross ratios as
1283
+ dictionary. The cross ratios are according to SnapPy convention, so we
1284
+ have::
1285
+
1286
+ z = 1 - 1/zp, zp = 1 - 1/zpp, zpp = 1 - 1/z
1287
+
1288
+ where::
1289
+
1290
+ z is at the edge 01 and equal to s0 * s1 * (c_1010 * c_0101) / (c_1001 * c_0110)
1291
+ zp is at the edge 02 and equal to s0 * s2 * (c_1001 * c_0110) / (c_1100 * c_0011)
1292
+ zpp is at the edge 03 and equal to s0 * s3 * (c_1100 * c_0011) / (c_0101 * c_1010).
1293
+
1294
+ Note that this is different from the convention used in
1295
+ Garoufalidis, Goerner, Zickert:
1296
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1297
+ https://arxiv.org/abs/1207.6711
1298
+ """
1299
+
1300
+ def __init__(self, d, is_numerical=True, manifold_thunk=None):
1301
+ super().__init__(d)
1302
+ self._is_numerical = is_numerical
1303
+ self._manifold_thunk = manifold_thunk
1304
+
1305
+ # Caches the matrices that label the short and long edges
1306
+ # of the truncated simplices building the manifold
1307
+ self._edge_cache = {}
1308
+
1309
+ # Caches the images of a fundamental group generator
1310
+ self._matrix_cache = []
1311
+ self._inverse_matrix_cache = []
1312
+
1313
+ self.dimension = 0
1314
+
1315
+ @staticmethod
1316
+ def from_snappy_manifold(M, dec_prec=None, bits_prec=None,
1317
+ intervals=False):
1318
+ """
1319
+ Constructs an assignment of shape parameters/cross ratios using
1320
+ the tetrahehdra_shapes method of a given SnapPy manifold. The optional
1321
+ parameters are the same as that of tetrahedra_shapes.
1322
+ """
1323
+
1324
+ shapes = M.tetrahedra_shapes('rect', dec_prec=dec_prec,
1325
+ bits_prec=bits_prec,
1326
+ intervals=intervals)
1327
+ d = {}
1328
+ for i, shape in enumerate(shapes):
1329
+ d['z_0000_%d' % i] = shape
1330
+ d['zp_0000_%d' % i] = 1 / (1 - shape)
1331
+ d['zpp_0000_%d' % i] = 1 - 1 / shape
1332
+
1333
+ return CrossRatios(d, is_numerical=True,
1334
+ manifold_thunk=lambda M=M: M)
1335
+
1336
+ def __repr__(self):
1337
+ dict_repr = dict.__repr__(self)
1338
+ return "CrossRatios(%s, is_numerical = %r, ...)" % (
1339
+ dict_repr, self._is_numerical)
1340
+
1341
+ def _repr_pretty_(self, p, cycle):
1342
+ if cycle:
1343
+ p.text('CrossRatios(...)')
1344
+ else:
1345
+ with p.group(4, 'CrossRatios(',')'):
1346
+ p.breakable()
1347
+ p.pretty(dict(self))
1348
+ p.text(',')
1349
+ p.breakable()
1350
+ p.text('is_numerical = %r, ...' % self._is_numerical)
1351
+
1352
+ def get_manifold(self):
1353
+ """
1354
+ Get the manifold for which this structure represents a solution
1355
+ to the gluing equations.
1356
+ """
1357
+
1358
+ return self._manifold_thunk()
1359
+
1360
+ def num_tetrahedra(self):
1361
+ """
1362
+ The number of tetrahedra for which we have cross ratios.
1363
+ """
1364
+ return _num_tetrahedra(self)
1365
+
1366
+ def N(self):
1367
+ """
1368
+ Get the N such that these cross ratios are for
1369
+ SL/PSL(N,C)-representations.
1370
+ """
1371
+
1372
+ return _N_for_shapes(self)
1373
+
1374
+ def numerical(self):
1375
+ """
1376
+ Turn exact solutions into numerical solutions using pari. Similar to
1377
+ numerical() of PtolemyCoordinates. See help(ptolemy.PtolemyCoordinates)
1378
+ for example.
1379
+ """
1380
+ if self._is_numerical:
1381
+ return self
1382
+ return ZeroDimensionalComponent([
1383
+ CrossRatios(d, is_numerical=True,
1384
+ manifold_thunk=self._manifold_thunk)
1385
+ for d in _to_numerical(self) ])
1386
+
1387
+ def to_PUR(self):
1388
+ """
1389
+ If any Ptolemy coordinates are given as Rational Univariate
1390
+ Representation, convert them to Polynomial Univariate Representation and
1391
+ return the result.
1392
+
1393
+ See to_PUR of RUR.
1394
+
1395
+ This conversion might lead to very large coefficients.
1396
+ """
1397
+
1398
+ return CrossRatios(
1399
+ _apply_to_RURs(self, RUR.to_PUR),
1400
+ is_numerical=self._is_numerical,
1401
+ manifold_thunk=self._manifold_thunk)
1402
+
1403
+ def multiply_terms_in_RUR(self):
1404
+ """
1405
+ If a cross ratio is given as Rational Univariate Representation
1406
+ with numerator and denominator being a product, multiply the terms and
1407
+ return the result.
1408
+
1409
+ See multiply_terms of RUR.
1410
+
1411
+ This loses information about how the numerator and denominator are
1412
+ factorised.
1413
+ """
1414
+
1415
+ return CrossRatios(
1416
+ _apply_to_RURs(self, RUR.multiply_terms),
1417
+ is_numerical=self._is_numerical,
1418
+ manifold_thunk=self._manifold_thunk)
1419
+
1420
+ def multiply_and_simplify_terms_in_RUR(self):
1421
+ """
1422
+ If a cross ratio is given as Rational Univariate Representation
1423
+ with numerator and denominator being a product, multiply the terms,
1424
+ reduce the fraction and return the result.
1425
+
1426
+ See multiply_and_simplify_terms of RUR.
1427
+
1428
+ This loses information about how the numerator and denominator are
1429
+ factorised.
1430
+
1431
+ """
1432
+
1433
+ return CrossRatios(
1434
+ _apply_to_RURs(self, RUR.multiply_and_simplify_terms),
1435
+ is_numerical=self._is_numerical,
1436
+ manifold_thunk=self._manifold_thunk)
1437
+
1438
+ def volume_numerical(self, drop_negative_vols=False):
1439
+ """
1440
+ Turn into (Galois conjugate) numerical solutions and compute volumes.
1441
+ If already numerical, only compute the one volume.
1442
+ See numerical().
1443
+
1444
+ If drop_negative_vols = True is given as optional argument,
1445
+ only return non-negative volumes.
1446
+ """
1447
+ if self._is_numerical:
1448
+ return sum([_volume(z) for key, z in list(self.items()) if 'z_' in key])
1449
+ else:
1450
+ vols = ZeroDimensionalComponent(
1451
+ [num.volume_numerical() for num in self.numerical()])
1452
+ if drop_negative_vols:
1453
+ return [vol for vol in vols if vol > -1e-12]
1454
+ return vols
1455
+
1456
+ @staticmethod
1457
+ def _cyclic_three_perm_sign(v0, v1, v2):
1458
+ """
1459
+ Returns +1 or -1. It is +1 if and only if (v0, v1, v2) is in the
1460
+ orbit of (0, 1, 2) under the A4-action.
1461
+ """
1462
+
1463
+ for t in [(v0,v1,v2), (v1,v2,v0), (v2,v0,v1)]:
1464
+ if t in [(0,1,2), (1,3,2), (2,3,0), (3,1,0)]:
1465
+ return +1
1466
+ return -1
1467
+
1468
+ def _shape_at_tet_point_and_edge(self, tet, pt, edge):
1469
+ """
1470
+ Given the index of a tetrahedron and two quadruples (any iterabel) of
1471
+ integers, give the cross ratio at that integral point and edge of that
1472
+ tetrahedron.
1473
+ This method translates the SnapPy conventions of labeling simplices
1474
+ and the conventions in Definition 4.2 of
1475
+
1476
+ Garoufalidis, Goerner, Zickert:
1477
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1478
+ https://arxiv.org/abs/1207.6711
1479
+ """
1480
+
1481
+ postfix = '_%d%d%d%d' % tuple(pt) + '_%d' % tet
1482
+
1483
+ if tuple(edge) in [(1,1,0,0), (0,0,1,1)]:
1484
+ return self['z' + postfix]
1485
+
1486
+ if tuple(edge) in [(1,0,1,0), (0,1,0,1)]:
1487
+ return self['zp' + postfix]
1488
+
1489
+ if tuple(edge) in [(1,0,0,1), (0,1,1,0)]:
1490
+ return self['zpp' + postfix]
1491
+
1492
+ raise Exception("Invalid edge " + str(edge))
1493
+
1494
+ def x_coordinate(self, tet, pt):
1495
+ """
1496
+ Returns the X-coordinate for the tetrahedron with index tet
1497
+ at the point pt (quadruple of integers adding up to N).
1498
+
1499
+ See Definition 10.9:
1500
+ Garoufalidis, Goerner, Zickert:
1501
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1502
+ https://arxiv.org/abs/1207.6711
1503
+ """
1504
+
1505
+ result = 1
1506
+
1507
+ for v0 in range(4):
1508
+ for v1 in range(v0 + 1, 4):
1509
+ e = [ _kronecker_delta(v0, i) +
1510
+ _kronecker_delta(v1, i) for i in range(4) ]
1511
+ p = [ x1 - x2 for x1, x2 in zip(pt, e) ]
1512
+ if all(x >= 0 for x in p):
1513
+ result *= self._shape_at_tet_point_and_edge(tet, p, e)
1514
+
1515
+ return -result
1516
+
1517
+ def _get_identity_matrix(self):
1518
+
1519
+ # Get N
1520
+ N = self.N()
1521
+
1522
+ return [[_kronecker_delta(i, j) for i in range(N)] for j in range(N)]
1523
+
1524
+ def long_edge(self, tet, v0, v1, v2):
1525
+ """
1526
+ The matrix that labels a long edge starting at vertex (v0, v1, v2)
1527
+ of a doubly truncated simplex corresponding to the ideal tetrahedron
1528
+ with index tet.
1529
+
1530
+ This matrix was labeled alpha^{v0v1v2} in Figure 18 of
1531
+ Garoufalidis, Goerner, Zickert:
1532
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1533
+ https://arxiv.org/abs/1207.6711
1534
+
1535
+ It is computed using equation 10.22.
1536
+
1537
+ The resulting matrix is given as a python list of lists.
1538
+ """
1539
+
1540
+ # Key for cache
1541
+ key = 'long_edge'
1542
+
1543
+ # Fill cache if necessary
1544
+ if key not in self._edge_cache:
1545
+
1546
+ # Get N
1547
+ N = self.N()
1548
+
1549
+ # It is just the counter diagonal matrix
1550
+ m = [ [ _kronecker_delta(i+j, N-1) for i in range(N) ]
1551
+ for j in range(N)]
1552
+
1553
+ # Set in cache
1554
+ self._edge_cache[key] = m
1555
+
1556
+ return self._edge_cache[key]
1557
+
1558
+ def middle_edge(self, tet, v0, v1, v2):
1559
+ """
1560
+ The matrix that labels a middle edge starting at vertex (v0, v1, v2)
1561
+ of a doubly truncated simplex corresponding to the ideal tetrahedron
1562
+ with index tet.
1563
+
1564
+ This matrix was labeled beta^{v0v1v2} in Figure 18 of
1565
+ Garoufalidis, Goerner, Zickert:
1566
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1567
+ https://arxiv.org/abs/1207.6711
1568
+
1569
+ It is computed using equation 10.22.
1570
+
1571
+ The resulting matrix is given as a python list of lists.
1572
+ """
1573
+
1574
+ # Key for the cache
1575
+ key = 'middle_%d_%d%d%d' % (tet, v0, v1, v2)
1576
+
1577
+ # Fill cache if necessary
1578
+ if key not in self._edge_cache:
1579
+
1580
+ # Get N
1581
+ N = self.N()
1582
+
1583
+ # The epsilon permutation sign
1584
+ sgn = CrossRatios._cyclic_three_perm_sign(v0, v1, v2)
1585
+
1586
+ # Start with identity
1587
+ m = self._get_identity_matrix()
1588
+
1589
+ for k in range(1, N):
1590
+ # Compute first product
1591
+ prod1 = self._get_identity_matrix()
1592
+ for i in range(1, N - k + 1):
1593
+ prod1 = matrix.matrix_mult(prod1, _X(N, i, 1))
1594
+
1595
+ # Compute second product
1596
+ prod2 = self._get_identity_matrix()
1597
+ for i in range(1, N - k):
1598
+ pt = [ k * _kronecker_delta(v2, j) +
1599
+ i * _kronecker_delta(v0, j) +
1600
+ (N-k-i) * _kronecker_delta(v1, j)
1601
+ for j in range(4) ]
1602
+
1603
+ # Note that the sgn is different from the paper
1604
+ # because we are using SnapPy conventions for
1605
+ # cross ratios here
1606
+
1607
+ prod2 = matrix.matrix_mult(
1608
+ prod2,
1609
+ _H(N, i, self.x_coordinate(tet, pt) ** -sgn))
1610
+
1611
+ m = matrix.matrix_mult(m,
1612
+ matrix.matrix_mult(prod1, prod2))
1613
+
1614
+ # Matrix from Equation 10.1
1615
+ dpm = [ [ - (-1) ** (N - i) * _kronecker_delta(i, j)
1616
+ for i in range(N) ]
1617
+ for j in range(N) ]
1618
+
1619
+ m = matrix.matrix_mult(m, dpm)
1620
+
1621
+ # Set in cache
1622
+ self._edge_cache[key] = m
1623
+
1624
+ return self._edge_cache[key]
1625
+
1626
+ def short_edge(self, tet, v0, v1, v2):
1627
+ """
1628
+ The matrix that labels a long edge starting at vertex (v0, v1, v2)
1629
+ of a doubly truncated simplex corresponding to the ideal tetrahedron
1630
+ with index tet.
1631
+
1632
+ This matrix was labeled gamma^{v0v1v2} in Figure 18 of
1633
+ Garoufalidis, Goerner, Zickert:
1634
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
1635
+ https://arxiv.org/abs/1207.6711
1636
+
1637
+ It is computed using equation 10.22.
1638
+
1639
+ The resulting matrix is given as a python list of lists.
1640
+ """
1641
+
1642
+ # Key for the cache
1643
+ key = 'short_%d_%d%d%d' % (tet, v0, v1, v2)
1644
+
1645
+ # Fill cache if necessary
1646
+ if key not in self._edge_cache:
1647
+
1648
+ edge = [ _kronecker_delta(v0, i) +
1649
+ _kronecker_delta(v1, i) for i in range(4) ]
1650
+
1651
+ # The epsilon permutation sign
1652
+ sgn = CrossRatios._cyclic_three_perm_sign(v0, v1, v2)
1653
+
1654
+ # Get N
1655
+ N = self.N()
1656
+
1657
+ # Start with identity
1658
+ m = self._get_identity_matrix()
1659
+
1660
+ # Compute the product in equation 10.22
1661
+ for a0 in range(N-1):
1662
+ a1 = N - 2 - a0
1663
+ pt = [ a0 * _kronecker_delta(v0, i) +
1664
+ a1 * _kronecker_delta(v1, i) for i in range(4) ]
1665
+
1666
+ cross_ratio = self._shape_at_tet_point_and_edge(tet, pt, edge)
1667
+
1668
+ # Multiply result with the H matrix
1669
+
1670
+ # Note that the sgn is different from the paper
1671
+ # because we are using SnapPy conventions for
1672
+ # cross ratios here
1673
+
1674
+ m = matrix.matrix_mult(m, _H(N, a0 + 1, cross_ratio ** sgn))
1675
+
1676
+ # Fill cache
1677
+ self._edge_cache[key] = m
1678
+
1679
+ return self._edge_cache[key]
1680
+
1681
+ def _init_matrix_and_inverse_cache(self):
1682
+ # Fill the caches of matrices corresponding to the
1683
+ # fundamental group generators and their inverses
1684
+
1685
+ if self._matrix_cache and self._inverse_matrix_cache:
1686
+ return
1687
+
1688
+ # Compute all the matrices for the generators and there inverses
1689
+ # The long edges of the doubly truncated simplex are all unit
1690
+ # counter-diagonal so they do not increase the
1691
+ # size of any polynomial coefficients. We thus don't give them penalty.
1692
+ self._matrix_cache, self._inverse_matrix_cache = (
1693
+ findLoops.images_of_original_generators(self,
1694
+ penalties=(0, 1, 1)))
1695
+
1696
+ def evaluate_word(self, word, G=None):
1697
+ """
1698
+ Given a word in the generators of the fundamental group,
1699
+ compute the corresponding matrix. By default, these are the
1700
+ generators of the unsimplified presentation of the fundamental
1701
+ group. An optional SnapPy fundamental group can be given if the
1702
+ words are in generators of a different presentation, e.g.,
1703
+ c.evaluate_word(word, M.fundamental_group(True)) to
1704
+ evaluate a word in the simplified presentation returned by
1705
+ M.fundamental_group(True).
1706
+
1707
+ For now, the matrix is returned as list of lists.
1708
+ """
1709
+
1710
+ # Init the matrices corresponding to generators
1711
+ self._init_matrix_and_inverse_cache()
1712
+
1713
+ return findLoops.evaluate_word(
1714
+ self._get_identity_matrix(),
1715
+ self._matrix_cache,
1716
+ self._inverse_matrix_cache,
1717
+ word,
1718
+ G)
1719
+
1720
+ def check_against_manifold(self, M=None, epsilon=None):
1721
+ """
1722
+ Checks that the given solution really is a solution to the PGL(N,C) gluing
1723
+ equations of a manifold. Usage similar to check_against_manifold of
1724
+ PtolemyCoordinates. See help(ptolemy.PtolemtyCoordinates) for example.
1725
+
1726
+ === Arguments ===
1727
+
1728
+ M --- manifold to check this for
1729
+ epsilon --- maximal allowed error when checking the relations, use
1730
+ None for exact comparison.
1731
+ """
1732
+ if M is None:
1733
+ M = self.get_manifold()
1734
+
1735
+ if M is None:
1736
+ raise Exception("Need to give manifold")
1737
+
1738
+ some_z = list(self.keys())[0]
1739
+ variable_name, index, tet_index = some_z.split('_')
1740
+ if variable_name not in ['z', 'zp', 'zpp']:
1741
+ raise Exception("Variable not z, zp, or, zpp")
1742
+ if len(index) != 4:
1743
+ raise Exception("Not 4 indices")
1744
+ N = sum([int(x) for x in index]) + 2
1745
+
1746
+ matrix_with_explanations = M.gluing_equations_pgl(
1747
+ N, equation_type='all')
1748
+
1749
+ matrix = matrix_with_explanations.matrix
1750
+ rows = matrix_with_explanations.explain_rows
1751
+ cols = matrix_with_explanations.explain_columns
1752
+
1753
+ for row in range(len(rows)):
1754
+ product = 1
1755
+ for col in range(len(cols)):
1756
+ cross_ratio_variable = cols[col]
1757
+ cross_ratio_value = self[cross_ratio_variable]
1758
+ product = product * (cross_ratio_value ** matrix[row,col])
1759
+ _check_relation(
1760
+ product - 1,
1761
+ epsilon,
1762
+ "Gluing equation %s" % rows[row])
1763
+
1764
+ def induced_representation(self, N):
1765
+ """
1766
+ Given a PSL(2,C) representation constructs the induced representation
1767
+ for the given N.
1768
+ The induced representation is in SL(N,C) if N is odd and
1769
+ SL(N,C) / {+1,-1} if N is even and is described in the Introduction of
1770
+ Garoufalidis, Thurston, Zickert
1771
+ The Complex Volume of SL(n,C)-Representations of 3-Manifolds
1772
+ https://arxiv.org/abs/1111.2828
1773
+
1774
+ There is a canonical group homomorphism SL(2,C)->SL(N,C) coming from
1775
+ the the natural SL(2,C)-action on the vector space Sym^{N-1}(C^2).
1776
+ This homomorphisms decends to a homomorphism from PSL(2,C) if one
1777
+ divides the right side by {+1,-1} when N is even.
1778
+ Composing a representation with this homomorphism gives the induced
1779
+ representation.
1780
+ """
1781
+
1782
+ num_tetrahedra = self.num_tetrahedra()
1783
+
1784
+ if self.N() != 2:
1785
+ raise Exception(
1786
+ "Cross ratios need to come from a PSL(2,C) representation")
1787
+
1788
+ def key_value_pair(v, t, index):
1789
+ new_key = v + '_%d%d%d%d' % tuple(index) + '_%d' % t
1790
+ old_key = v + '_0000' + '_%d' % t
1791
+ return (new_key, self[old_key])
1792
+
1793
+ d = dict(
1794
+ [ key_value_pair(v, t, index)
1795
+ for v in ['z', 'zp', 'zpp']
1796
+ for t in range(num_tetrahedra)
1797
+ for index in utilities.quadruples_with_fixed_sum_iterator(N-2)])
1798
+
1799
+ return CrossRatios(d,
1800
+ is_numerical=self._is_numerical,
1801
+ manifold_thunk=self._manifold_thunk)
1802
+
1803
+ def is_real(self, epsilon):
1804
+ """
1805
+ Returns True if all cross ratios are real (have absolute imaginary
1806
+ part < epsilon where epsilon is given as argument).
1807
+ This means that the corresponding representation is in PSL(N,R).
1808
+ """
1809
+
1810
+ if not self._is_numerical:
1811
+ raise NumericalMethodError("is_real")
1812
+
1813
+ for v in self.values():
1814
+ if v.imag().abs() > epsilon:
1815
+ return False
1816
+ return True
1817
+
1818
+ def is_induced_from_psl2(self, epsilon=None):
1819
+ """
1820
+ For each simplex and each edges, checks that all cross ratios of that
1821
+ simplex that are parallel to that each are the same (maximal absolute
1822
+ difference is the epsilon given as argument).
1823
+ This means that the corresponding representation is induced by a
1824
+ PSL(2,C) representation.
1825
+ """
1826
+
1827
+ # Create an auxiliary dictionary containing one z, zp, zpp per tet
1828
+ d = { }
1829
+
1830
+ for key, value in self.items():
1831
+ variable_name, index, tet_index = key.split('_')
1832
+ if variable_name not in ['z', 'zp', 'zpp']:
1833
+ raise Exception("Variable not z, zp, or, zpp")
1834
+ if len(index) != 4:
1835
+ raise Exception("Not 4 indices")
1836
+
1837
+ # The key in the auxiliary dictionary
1838
+ short_key = variable_name + '_' + tet_index
1839
+
1840
+ # Get the old value in the auxiliary dictionary
1841
+ old_value = d.setdefault(short_key, value)
1842
+
1843
+ if epsilon is None:
1844
+ if value != old_value:
1845
+ return False
1846
+ else:
1847
+ if (value - old_value).abs() > epsilon:
1848
+ return False
1849
+
1850
+ return True
1851
+
1852
+ def is_pu_2_1_representation(self, epsilon, epsilon2=None):
1853
+ r"""
1854
+ Returns True if the representation is also a
1855
+ PU(2,1)-representation. This uses Proposition 3.5 and the
1856
+ remark following that proposition in [FKR2013]_.
1857
+
1858
+ If a condition given in that Proposition is violated, the method returns
1859
+ an object whose Boolean value is still False and that indicates which condition
1860
+ was violated. Thus, this method can still be used in ``if`` statements.
1861
+
1862
+ The method tests the following complex equalities and inequalities:
1863
+
1864
+ * the three complex equations given in (3.5.1) of [FKR2013]_.
1865
+ * the inequality z\ :sub:`ijl` :math:`\\not=` -1.
1866
+
1867
+ **Remark:** It does not check whether all z\ :sub:`ij` * z\ :sub:`ji` are real or
1868
+ not as these are still valid CR configurations, see the remark following
1869
+ Proposition 3.5.
1870
+
1871
+ The user has to supply an epsilon: an equality/inequality is considered
1872
+ to be true if and only if the absolute value | LHS - RHS | of difference between the
1873
+ left and right hand side is less/greater than epsilon.
1874
+
1875
+ The user can supply another parameter, epsilon2. If any | LHS - RHS | is in
1876
+ the interval [epsilon, epsilon2], this method fails with an exception
1877
+ as the value of | LHS - RHS | is an ambiguous interval where
1878
+ it is unclear whether inequality fails to hold because it truly does
1879
+ hold or just because of numerical noise.
1880
+ """
1881
+
1882
+ def is_zero(val):
1883
+ if val.abs() < epsilon:
1884
+ return True
1885
+ if epsilon2:
1886
+ if not epsilon2 < val.abs():
1887
+ raise Exception(
1888
+ "Ambiguous error when determining whether a "
1889
+ "condition was fulfilled or nor: %s" % val)
1890
+ return False
1891
+
1892
+ def mainCondition(key_zij, key_zji, key_zkl, key_zlk):
1893
+
1894
+ lhs = (self[key_zij] * self[key_zji])
1895
+ rhs = (self[key_zkl] * self[key_zlk]).conj()
1896
+
1897
+ if not is_zero(lhs - rhs):
1898
+ reason = "%s * %s = conjugate(%s * %s) not fulfilled" % (
1899
+ key_zij, key_zji, key_zkl, key_zlk)
1900
+ return NotPU21Representation(reason)
1901
+
1902
+ return True
1903
+
1904
+ def tripleRatioCondition(key_zji, key_zki, key_zli):
1905
+
1906
+ tripleRatio = self[key_zji] * self[key_zki] * self[key_zli]
1907
+
1908
+ if is_zero(tripleRatio - 1):
1909
+ reason = 'Triple ratio %s * %s * %s = 1' % (
1910
+ key_zji, key_zki, key_zli)
1911
+ return NotPU21Representation(reason)
1912
+
1913
+ return True
1914
+
1915
+ if self.N() != 3:
1916
+ raise Exception("PU(2,1)-representations only allowed for N = 3")
1917
+
1918
+ if not self._is_numerical:
1919
+ raise NumericalMethodError("is_pu_2_1_representation")
1920
+
1921
+ for t in range(self.num_tetrahedra()):
1922
+
1923
+ m0 = mainCondition("z_1000_%d" % t, "z_0100_%d" % t,
1924
+ "z_0010_%d" % t, "z_0001_%d" % t)
1925
+ if not m0:
1926
+ return m0
1927
+
1928
+ m1 = mainCondition("zp_1000_%d" % t, "zp_0010_%d" % t,
1929
+ "zp_0100_%d" % t, "zp_0001_%d" % t)
1930
+ if not m1:
1931
+ return m1
1932
+
1933
+ m2 = mainCondition("zpp_1000_%d" % t, "zpp_0001_%d" % t,
1934
+ "zpp_0100_%d" % t, "zpp_0010_%d" % t)
1935
+ if not m2:
1936
+ return m2
1937
+
1938
+ t0 = tripleRatioCondition( "z_0100_%d" % t,
1939
+ "zp_0010_%d" % t,
1940
+ "zpp_0001_%d" % t)
1941
+ if not t0:
1942
+ return t0
1943
+
1944
+ t1 = tripleRatioCondition( "z_1000_%d" % t,
1945
+ "zp_0001_%d" % t,
1946
+ "zpp_0010_%d" % t)
1947
+ if not t1:
1948
+ return t1
1949
+
1950
+ t2 = tripleRatioCondition( "z_0001_%d" % t,
1951
+ "zp_1000_%d" % t,
1952
+ "zpp_0100_%d" % t)
1953
+ if not t2:
1954
+ return t2
1955
+
1956
+ t3 = tripleRatioCondition( "z_0010_%d" % t,
1957
+ "zp_0100_%d" % t,
1958
+ "zpp_1000_%d" % t)
1959
+ if not t3:
1960
+ return t3
1961
+
1962
+ return True
1963
+
1964
+ def is_geometric(self, epsilon=1e-6):
1965
+ """
1966
+ Returns true if all shapes corresponding to this solution have positive
1967
+ imaginary part.
1968
+
1969
+ If the solutions are exact, it returns true if one of the corresponding
1970
+ numerical solutions is geometric.
1971
+
1972
+ An optional epsilon can be given. An imaginary part of a shape is
1973
+ considered positive if it is larger than this epsilon.
1974
+ """
1975
+
1976
+ if self._is_numerical:
1977
+ for v in self.values():
1978
+ if not v.imag() > 0:
1979
+ return False
1980
+ return True
1981
+ else:
1982
+ for numerical_sol in self.numerical():
1983
+ if numerical_sol.is_geometric(epsilon):
1984
+ return True
1985
+ return False
1986
+
1987
+
1988
+ def _ptolemy_to_cross_ratio(solution_dict,
1989
+ branch_factor=1,
1990
+ non_trivial_generalized_obstruction_class=False,
1991
+ as_flattenings=False):
1992
+
1993
+ N, has_obstruction = _N_and_has_obstruction_for_ptolemys(solution_dict)
1994
+ num_tets = _num_tetrahedra(solution_dict)
1995
+
1996
+ if N % 2:
1997
+ evenN = 2 * N
1998
+ else:
1999
+ evenN = N
2000
+
2001
+ if not non_trivial_generalized_obstruction_class:
2002
+ evenN = 2
2003
+
2004
+ if as_flattenings:
2005
+ f = pari('2 * Pi * I') / evenN
2006
+
2007
+ def compute_cross_ratios_and_flattenings(tet, index):
2008
+ def get_ptolemy_coordinate(addl_index):
2009
+ total_index = matrix.vector_add(index, addl_index)
2010
+ key = "c_%d%d%d%d" % tuple(total_index) + "_%d" % tet
2011
+ return solution_dict[key]
2012
+
2013
+ def get_obstruction_variable(face):
2014
+ key = "s_%d_%d" % (face, tet)
2015
+ return solution_dict[key]
2016
+
2017
+ c1010 = get_ptolemy_coordinate((1,0,1,0))
2018
+ c1001 = get_ptolemy_coordinate((1,0,0,1))
2019
+ c0110 = get_ptolemy_coordinate((0,1,1,0))
2020
+ c0101 = get_ptolemy_coordinate((0,1,0,1))
2021
+ c1100 = get_ptolemy_coordinate((1,1,0,0))
2022
+ c0011 = get_ptolemy_coordinate((0,0,1,1))
2023
+
2024
+ z = (c1010 * c0101) / (c1001 * c0110)
2025
+ zp = - (c1001 * c0110) / (c1100 * c0011)
2026
+ zpp = (c1100 * c0011) / (c1010 * c0101)
2027
+
2028
+ if has_obstruction:
2029
+ s0 = get_obstruction_variable(0)
2030
+ s1 = get_obstruction_variable(1)
2031
+ s2 = get_obstruction_variable(2)
2032
+ s3 = get_obstruction_variable(3)
2033
+ z = s0 * s1 * z
2034
+ zp = s0 * s2 * zp
2035
+ zpp = s0 * s3 * zpp
2036
+
2037
+ variable_end = '_%d%d%d%d' % tuple(index) + '_%d' % tet
2038
+
2039
+ if as_flattenings:
2040
+ def make_triple(w, z):
2041
+ z = _convert_to_pari_float(z)
2042
+ return (w, z, ((w - z .log()) / f).round())
2043
+
2044
+ w = _compute_flattening(c1010, c0101, c1001, c0110,
2045
+ branch_factor, evenN)
2046
+ wp = _compute_flattening(c1001, c0110, c1100, c0011,
2047
+ branch_factor, evenN)
2048
+ wpp = _compute_flattening(c1100, c0011, c1010, c0101,
2049
+ branch_factor, evenN)
2050
+
2051
+ return [
2052
+ ('z' + variable_end, make_triple(w ,z )),
2053
+ ('zp' + variable_end, make_triple(wp ,zp )),
2054
+ ('zpp' + variable_end, make_triple(wpp,zpp)) ]
2055
+
2056
+ else:
2057
+ return [
2058
+ ('z' + variable_end, z),
2059
+ ('zp' + variable_end, zp),
2060
+ ('zpp' + variable_end, zpp) ]
2061
+
2062
+ return dict(
2063
+ sum([compute_cross_ratios_and_flattenings(tet,index)
2064
+ for tet in range(num_tets)
2065
+ for index in utilities.quadruples_with_fixed_sum_iterator(N - 2)],
2066
+ [])), evenN
2067
+
2068
+
2069
+ def _num_tetrahedra(solution_dict):
2070
+ return max( [ int(key.split('_')[-1])
2071
+ for key in solution_dict.keys() ] ) + 1
2072
+
2073
+
2074
+ def _N_for_shapes(solution_dict):
2075
+
2076
+ def get_N(key):
2077
+ m = re.match(r'zp{0,2}_(\d{4})_\d+$', key)
2078
+ if not m:
2079
+ raise Exception("Not a valid shape key: '%s'" % key)
2080
+ return sum([int(char) for char in m.group(1)]) + 2
2081
+
2082
+ l = [ get_N(key) for key in solution_dict.keys() ]
2083
+ if not len(set(l)) == 1:
2084
+ raise Exception("Shape keys for different N")
2085
+
2086
+ return l[0]
2087
+
2088
+
2089
+ def _N_and_has_obstruction_for_ptolemys(solution_dict):
2090
+
2091
+ def get_N(key):
2092
+ m = re.match(r'c_(\d{4})_\d+$', key)
2093
+ if not m:
2094
+ raise Exception("Not a valid Ptolemy key: '%s'" % key)
2095
+ return sum([int(char) for char in m.group(1)])
2096
+
2097
+ has_obstruction = False
2098
+
2099
+ l = set()
2100
+ for key in solution_dict.keys():
2101
+ if re.match(r's_\d_\d+$', key):
2102
+ has_obstruction = True
2103
+ else:
2104
+ l.add(get_N(key))
2105
+
2106
+ if not len(l) == 1:
2107
+ raise Exception("Ptolemy keys for different N")
2108
+
2109
+ for N in l:
2110
+ return N, has_obstruction
2111
+
2112
+
2113
+ def _get_number_field(d):
2114
+ for value in d.values():
2115
+
2116
+ if isinstance(value, RUR):
2117
+ nf = value.number_field()
2118
+ if nf:
2119
+ return nf
2120
+
2121
+ if type(value) == Gen and value.type() == 't_POLMOD':
2122
+ return value.mod()
2123
+
2124
+ return None
2125
+
2126
+
2127
+ def _evaluate_at_root(p, root):
2128
+
2129
+ if type(p) == Gen and p.type() == 't_POLMOD':
2130
+ return p.lift().substpol('x', root)
2131
+
2132
+ if isinstance(p, RUR):
2133
+ return p.evaluate_at_root(root)
2134
+
2135
+ return p
2136
+
2137
+
2138
+ def _to_numerical(d):
2139
+
2140
+ number_field = _get_number_field(d)
2141
+
2142
+ if number_field is None:
2143
+ roots = [ pari(0) ]
2144
+ else:
2145
+ # Bug in cypari: pari(str(number_field)).polroots()
2146
+ # gives less precision
2147
+ roots = pari('polroots(%s)' % number_field)
2148
+
2149
+ def evaluate_all_for_root(root):
2150
+
2151
+ def evaluate_key_for_root(key, value):
2152
+
2153
+ v = _evaluate_at_root(value, root)
2154
+
2155
+ if key[:2] == 'z_':
2156
+ z = v
2157
+ zp = 1 / (1 - z)
2158
+ zpp = 1 - 1 / z
2159
+
2160
+ return [(key, z),
2161
+ ('zp_' + key[2:], zp),
2162
+ ('zpp_' + key[2:], zpp)]
2163
+ elif key[:3] == 'zp_' or key[:4] == 'zpp_':
2164
+ return []
2165
+ else:
2166
+ return [(key, v)]
2167
+
2168
+ return dict(sum(
2169
+ [ evaluate_key_for_root(key, value)
2170
+ for key, value in d.items() ], []))
2171
+
2172
+ return [ evaluate_all_for_root(root) for root in roots ]
2173
+
2174
+
2175
+ def _apply_to_RURs(d, RUR_method):
2176
+
2177
+ def _apply_to_RUR(v):
2178
+ if isinstance(v, RUR):
2179
+ return RUR_method(v)
2180
+ return v
2181
+
2182
+ return {k: _apply_to_RUR(v) for k, v in d.items()}
2183
+
2184
+
2185
+ def _convert_to_pari_float(z):
2186
+
2187
+ if type(z) == Gen and z.type() in ['t_INT', 't_FRAC']:
2188
+ return z * pari('1.0')
2189
+
2190
+ return pari(z)
2191
+
2192
+
2193
+ def _compute_flattening(a, b, c, d, branch_factor, N=2):
2194
+
2195
+ PiMinusEpsilon = pari(3.141592)
2196
+
2197
+ def safe_log(z):
2198
+
2199
+ l = (branch_factor * z**N).log()
2200
+
2201
+ if l.imag().abs() > PiMinusEpsilon:
2202
+ raise LogToCloseToBranchCutError()
2203
+
2204
+ return l / N
2205
+
2206
+ a = _convert_to_pari_float(a)
2207
+ b = _convert_to_pari_float(b)
2208
+ c = _convert_to_pari_float(c)
2209
+ d = _convert_to_pari_float(d)
2210
+
2211
+ w = safe_log(a) + safe_log(b) - safe_log(c) - safe_log(d)
2212
+
2213
+ return w
2214
+
2215
+ # bug in pari
2216
+
2217
+
2218
+ def _dilog(z):
2219
+ return pari("dilog(%s)" % z)
2220
+
2221
+
2222
+ def _L_function(zpq_triple, evenN=2):
2223
+
2224
+ z, p, q = zpq_triple
2225
+
2226
+ z = _convert_to_pari_float(z)
2227
+ p = _convert_to_pari_float(p)
2228
+ q = _convert_to_pari_float(q)
2229
+
2230
+ f = pari('2 * Pi * I') / evenN
2231
+ Pi2 = pari('Pi * Pi')
2232
+
2233
+ return ( _dilog(z)
2234
+ + (z.log() + p * f) * ((1-z).log() + q * f) / 2
2235
+ - Pi2 / 6)
2236
+
2237
+
2238
+ def _volume(z):
2239
+
2240
+ z = _convert_to_pari_float(z)
2241
+
2242
+ return (1-z).arg() * z.abs().log() + _dilog(z).imag()
2243
+
2244
+
2245
+ def _kronecker_delta(i, j):
2246
+ """
2247
+ Kronecker Delta, returns 1 if and only if i and j are equal, other 0.
2248
+ """
2249
+
2250
+ if i == j:
2251
+ return 1
2252
+ else:
2253
+ return 0
2254
+
2255
+
2256
+ def _X(N, k, v):
2257
+ """
2258
+ Returns the NxN matrix with off-diagonal entry v at position k, that
2259
+ is the entry at row k and column k+1 is v.
2260
+
2261
+ See (10.2) of
2262
+ Garoufalidis, Goerner, Zickert:
2263
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
2264
+ https://arxiv.org/abs/1207.6711
2265
+ """
2266
+
2267
+ m = [[_kronecker_delta(i,j) for i in range(N)] for j in range(N)]
2268
+ m[k-1][k] = v
2269
+ return m
2270
+
2271
+
2272
+ def _H(N, k, x):
2273
+ """
2274
+ Returns the NxN diagonal matrix where the first k diagonal entries are x
2275
+ and all other entries are 1.
2276
+
2277
+ See (10.1) of
2278
+ Garoufalidis, Goerner, Zickert:
2279
+ Gluing Equations for PGL(n,C)-Representations of 3-Manifolds
2280
+ https://arxiv.org/abs/1207.6711
2281
+ """
2282
+
2283
+ def _entry(i, j):
2284
+ if i != j:
2285
+ return 0
2286
+ if i < k:
2287
+ return x
2288
+ return 1
2289
+
2290
+ return [[_entry(i,j) for i in range(N)] for j in range(N)]