snappy 3.2__cp313-cp313-macosx_11_0_arm64.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-313-darwin.so +0 -0
  2. snappy/SnapPy.cpython-313-darwin.so +0 -0
  3. snappy/SnapPy.ico +0 -0
  4. snappy/SnapPy.png +0 -0
  5. snappy/SnapPyHP.cpython-313-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-313-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,1499 @@
1
+ # $Id: mcomplex.py,v 1.14 2009/08/20 15:58:58 t3m Exp $
2
+ # t3m - software for studying triangulated 3-manifolds
3
+ # Copyright (C) 2002 Marc Culler, Nathan Dunfield and others
4
+ #
5
+ # This program is distributed under the terms of the
6
+ # GNU General Public License, version 2 or later, as published by
7
+ # the Free Software Foundation. See the file GPL.txt for details.
8
+
9
+ from .simplex import *
10
+ from .tetrahedron import Tetrahedron
11
+ from .corner import Corner
12
+ from .arrow import Arrow
13
+ from .face import Face
14
+ from .edge import Edge
15
+ from .vertex import Vertex
16
+ from .surface import Surface, SpunSurface, ClosedSurface, ClosedSurfaceInCusped
17
+ from .perm4 import Perm4, inv
18
+ from . import files
19
+ from . import linalg
20
+ from . import homology
21
+ import sys
22
+ import random
23
+ import io
24
+
25
+ try:
26
+ import snappy
27
+ except ImportError:
28
+ snappy = None
29
+
30
+ VERBOSE = 0
31
+
32
+ # Globals needed for normal surfaces:
33
+
34
+ # The height shift dictionaries for the three quad types.
35
+ # Shift[ tet edge ] is a tuple of the shifts of the three quad
36
+ # types (Q03, Q13, Q23) along that edge.
37
+ #
38
+ # That is the first entry E01:(-1, 1, 0) means that Q03 shifts
39
+ # by -1 along E01, Q13 shifts by +1 along E01 and Q23 doesn't
40
+ # shift.
41
+
42
+ Shift = {E01:(-1,1,0), E02:(1,0,-1), E21:(0,-1,1),
43
+ E32:(-1,1,0), E31:(1,0,-1), E03:(0,-1,1)}
44
+
45
+ # The solution vector templates for the four vertex types.
46
+
47
+ VertexVector = {V0:(1,0,0,0), V1:(0,1,0,0),
48
+ V2:(0,0,1,0), V3:(0,0,0,1)}
49
+
50
+
51
+ def edge_and_arrow(edge_or_arrow):
52
+ """
53
+ Given and edge or an arrow, returns the corresponding compatible
54
+ (edge, arrow) pair.
55
+ """
56
+ if isinstance(edge_or_arrow, Edge):
57
+ edge = edge_or_arrow
58
+ arrow = Arrow(edge.Corners[0].Subsimplex,
59
+ LeftFace[edge.Corners[0].Subsimplex],
60
+ edge.Corners[0].Tetrahedron)
61
+ else:
62
+ if not isinstance(edge_or_arrow, Arrow):
63
+ raise ValueError('Input edge_or_arrow is neither')
64
+ arrow = edge_or_arrow.copy()
65
+ edge = arrow.axis()
66
+ return edge, arrow
67
+
68
+
69
+ class Insanity(Exception):
70
+ pass
71
+
72
+
73
+ class Mcomplex:
74
+ """
75
+ An Mcomplex is a union of tetrahedra with faces identified in
76
+ pairs. The edges (vertices) are equivalence classes under the
77
+ induced equivalence relation on the set of edges (vertices) of the
78
+ tetrahedra.
79
+
80
+ >>> T = Mcomplex([Tetrahedron()])
81
+ >>> len(T), len(T.Vertices)
82
+ (1, 4)
83
+ >>> T = Mcomplex('m004')
84
+ >>> len(T)
85
+ 2
86
+ >>> tet_data = [([0,1,0,1], [(2,1,0,3), (0,3,2,1), (2,1,0,3), (0,1,3,2)]),
87
+ ... ([1,1,0,0], [(1,0,2,3), (1,0,2,3), (0,1,3,2), (0,3,2,1)])]
88
+ >>> S = Mcomplex(tet_data)
89
+ >>> len(S)
90
+ 2
91
+ """
92
+ def __init__(self, tetrahedron_list=None):
93
+ if tetrahedron_list is None:
94
+ tetrahedron_list = []
95
+ elif isinstance(tetrahedron_list, str) and snappy is None:
96
+ tetrahedron_list = tets_from_data(files.read_SnapPea_file(file_name=tetrahedron_list))
97
+ elif snappy:
98
+ if isinstance(tetrahedron_list, str):
99
+ tetrahedron_list = snappy.Triangulation(tetrahedron_list,
100
+ remove_finite_vertices=False)
101
+ if hasattr(tetrahedron_list, '_get_tetrahedra_gluing_data'):
102
+ tetrahedron_list = tets_from_data(
103
+ tetrahedron_list._get_tetrahedra_gluing_data())
104
+ if isinstance(tetrahedron_list, (list, tuple)):
105
+ if len(tetrahedron_list) > 0 and not isinstance(tetrahedron_list[0], Tetrahedron):
106
+ tetrahedron_list = tets_from_data(tetrahedron_list)
107
+
108
+ self.Tetrahedra = tetrahedron_list
109
+ self.Edges = []
110
+ self.Faces = []
111
+ self.Vertices = []
112
+ self.NormalSurfaces = []
113
+ self.AlmostNormalSurfaces = []
114
+ self.build()
115
+
116
+ def copy(self, base_arrow=None):
117
+ new_tets = []
118
+ new_to_old = {}
119
+ old_to_new = {}
120
+ for tet in self.Tetrahedra:
121
+ new_tet = Tetrahedron()
122
+ old_to_new[tet] = new_tet
123
+ new_to_old[new_tet] = tet
124
+ new_tets.append(new_tet)
125
+ for new_tet in new_tets:
126
+ for face in TwoSubsimplices:
127
+ new_tet.attach(face,
128
+ old_to_new[new_to_old[new_tet].Neighbor[face]],
129
+ new_to_old[new_tet].Gluing[face].tuple())
130
+ if base_arrow is None:
131
+ return self.__class__(new_tets)
132
+ else:
133
+ new_arrow = base_arrow.copy()
134
+ new_arrow.Tetrahedron = old_to_new[base_arrow.Tetrahedron]
135
+ return (self.__class__(new_tets), new_arrow)
136
+
137
+ def build(self):
138
+ for i in range(len(self.Tetrahedra)):
139
+ self.Tetrahedra[i].Index = i
140
+ self.build_face_classes()
141
+ self.build_edge_classes()
142
+ self.build_vertex_classes()
143
+ self.build_one_skeleton()
144
+ self.LinkGenera = [vertex.link_genus() for vertex in self.Vertices]
145
+
146
+ def rebuild(self):
147
+ for tet in self.Tetrahedra:
148
+ tet.clear_Class()
149
+ for face in self.Faces:
150
+ face.erase()
151
+ for edge in self.Edges:
152
+ edge.erase()
153
+ for vertex in self.Vertices:
154
+ vertex.erase()
155
+ self.Faces = []
156
+ self.Edges = []
157
+ self.Vertices = []
158
+ self.build()
159
+
160
+ def add_tet(self, tet):
161
+ self.Tetrahedra.append(tet)
162
+
163
+ def clear_tet(self,tet):
164
+ """
165
+ Remove the face, edge and vertex classes of a tetrahedron.
166
+ This should destroy the faces, edges and vertices that meet
167
+ the tetrahedron. A call to build_face_classes,
168
+ build_edge_classes or build_vertex_classes will then rebuild
169
+ the neighborhood without having to rebuild the whole manifold.
170
+ """
171
+ for two_subsimplex in TwoSubsimplices:
172
+ face = tet.Class[two_subsimplex]
173
+ if face is not None:
174
+ face.erase()
175
+ try:
176
+ self.Faces.remove(face)
177
+ except ValueError:
178
+ pass
179
+
180
+ for one_subsimplex in OneSubsimplices:
181
+ edge = tet.Class[one_subsimplex]
182
+ if edge is not None:
183
+ edge.erase()
184
+ try:
185
+ self.Edges.remove(edge)
186
+ except ValueError:
187
+ pass
188
+
189
+ for zero_subsimplex in ZeroSubsimplices:
190
+ vertex = tet.Class[zero_subsimplex]
191
+ if vertex is not None:
192
+ vertex.erase()
193
+ try:
194
+ self.Vertices.remove(vertex)
195
+ except ValueError:
196
+ pass
197
+
198
+ def delete_tet(self, tet):
199
+ """
200
+ Clear a tetrahedron, then remove it from the Tetrahedron list.
201
+ """
202
+ self.clear_tet(tet)
203
+ tet.erase()
204
+ self.Tetrahedra.remove(tet)
205
+
206
+ def new_arrow(self):
207
+ """
208
+ Add one new tetrahedron and return one of its arrows.
209
+ """
210
+ tet = Tetrahedron()
211
+ self.add_tet(tet)
212
+ return Arrow(E01,F3,tet)
213
+
214
+ def new_arrows(self,n):
215
+ return [self.new_arrow() for i in range(n)]
216
+
217
+ def new_tet(self):
218
+ tet = Tetrahedron()
219
+ self.add_tet(tet)
220
+ return tet
221
+
222
+ def new_tets(self,n):
223
+ return [self.new_tet() for i in range(n)]
224
+
225
+ def _triangulation_data(self):
226
+ ans = []
227
+ # We don't assume that the indices of the Tetraheda are equal
228
+ # to range(len(self))
229
+ tet_to_index = {T:i for i, T in enumerate(self.Tetrahedra)}
230
+ for T in self.Tetrahedra:
231
+ neighbors, perms = [], []
232
+ for v in TwoSubsimplices:
233
+ if T.Neighbor[v] is None:
234
+ neighbor, perm = None, None
235
+ else:
236
+ neighbor = tet_to_index[T.Neighbor[v]]
237
+ perm = T.Gluing[v].tuple()
238
+ neighbors.append(neighbor)
239
+ perms.append(perm)
240
+ ans.append((neighbors, perms))
241
+ return ans
242
+
243
+ def __len__(self):
244
+ """
245
+ Return the number of tetrahedra
246
+ """
247
+ return len(self.Tetrahedra)
248
+
249
+ def __getitem__(self, index):
250
+ """
251
+ M[i] refers to the ith Tetrahedron of the mcomplex M.
252
+ """
253
+ return self.Tetrahedra[index]
254
+
255
+ def info(self, out=sys.stdout):
256
+ """
257
+ M.info() describes the Mcomplex.
258
+ """
259
+ try:
260
+ out.write( "Mcomplex with %d Tetrahedra\n\n" % len(self) )
261
+ for tet in self.Tetrahedra:
262
+ tet.info(out)
263
+ out.write("\nEdges:\n")
264
+ for edge in self.Edges:
265
+ edge.info(out)
266
+ except OSError:
267
+ pass
268
+
269
+ def build_edge_classes(self):
270
+ """
271
+ Construct the edge classes and compute valences.
272
+ """
273
+ for tet in self.Tetrahedra:
274
+ for one_subsimplex in OneSubsimplices:
275
+ if ( tet.Class[one_subsimplex] is None ):
276
+ newEdge = Edge()
277
+ self.Edges.append(newEdge)
278
+ first_arrow = Arrow(one_subsimplex, RightFace[one_subsimplex], tet)
279
+ a = first_arrow.copy()
280
+ sanity_check = 0
281
+ boundary_hits = 0
282
+ while 1:
283
+ # Walk around the edge.
284
+ if sanity_check > 6*len(self.Tetrahedra):
285
+ raise Insanity('Bad gluing data: could not construct edge link.')
286
+ # Record the corners and edge classes as we go.
287
+ newEdge._add_corner(a)
288
+ a.Tetrahedron.Class[a.Edge] = newEdge
289
+ if a.next() is None:
290
+ # We hit the boundary!
291
+ # Go back to the beginning and walk to the right.
292
+ # If this is our second boundary hit, we are done.
293
+ if not boundary_hits == 0:
294
+ newEdge.RightBdryArrow = a.copy()
295
+ # Make sure the corner list goes from right to left.
296
+ newEdge.Corners.reverse()
297
+ break
298
+ else:
299
+ boundary_hits = 1
300
+ newEdge.LeftBdryArrow = a.copy()
301
+ newEdge.IntOrBdry = 'bdry'
302
+ a = first_arrow.copy()
303
+ a.reverse()
304
+ # Don't record the first corner twice.
305
+ del newEdge.Corners[0]
306
+ # Reverse the corner list since we are now going right.
307
+ newEdge.Corners.reverse()
308
+ else:
309
+ # Stop if we get back to where we started.
310
+ if a == first_arrow:
311
+ newEdge.IntOrBdry = 'int'
312
+ break
313
+ sanity_check = sanity_check + 1
314
+ self.EdgeValences = [edge.valence() for edge in self.Edges]
315
+ for i in range(len(self.Edges)):
316
+ self.Edges[i].Index = i
317
+
318
+ def build_vertex_classes(self):
319
+ for tet in self.Tetrahedra:
320
+ for zero_subsimplex in ZeroSubsimplices:
321
+ if ( tet.Class[zero_subsimplex] is None ):
322
+ newVertex = Vertex()
323
+ self.Vertices.append(newVertex)
324
+ self.walk_vertex(newVertex,zero_subsimplex,tet)
325
+ for i in range(len(self.Vertices)):
326
+ self.Vertices[i].Index = i
327
+
328
+ def walk_vertex(self,vertex,zero_subsimplex,tet):
329
+ if (tet.Class[zero_subsimplex] is not None ):
330
+ return
331
+ else:
332
+ tet.Class[zero_subsimplex] = vertex
333
+ vertex.Corners.append(Corner(tet,zero_subsimplex))
334
+ for two_subsimplex in TwoSubsimplices:
335
+ if ( is_subset(zero_subsimplex,two_subsimplex)
336
+ and
337
+ tet.Gluing[two_subsimplex] is not None):
338
+ self.walk_vertex(vertex,
339
+ tet.Gluing[two_subsimplex].image(zero_subsimplex),
340
+ tet.Neighbor[two_subsimplex])
341
+
342
+ def build_one_skeleton(self):
343
+ """
344
+ Construct the 1-skeleton, i.e. record which edges are
345
+ connected to which vertices. This assumes that Edges and Vertices
346
+ have already been built.
347
+ """
348
+ for edge in self.Edges:
349
+ tet = edge.Corners[0].Tetrahedron
350
+ one_subsimplex = edge.Corners[0].Subsimplex
351
+ tail = tet.Class[Tail[one_subsimplex]]
352
+ head = tet.Class[Head[one_subsimplex]]
353
+ edge.Vertices = [tail , head]
354
+ tail.Edges.append(edge)
355
+ head.Edges.append(edge)
356
+ if edge.IntOrBdry == 'bdry':
357
+ tail.IntOrBdry = 'bdry'
358
+ head.IntOrBdry = 'bdry'
359
+ for vertex in self.Vertices:
360
+ if vertex.IntOrBdry == '':
361
+ vertex.IntOrBdry = 'int'
362
+
363
+ def build_face_classes(self):
364
+ """
365
+ Construct the faces.
366
+ """
367
+ for tet in self.Tetrahedra:
368
+ for two_subsimplex in TwoSubsimplices:
369
+ if ( tet.Class[two_subsimplex] is None ):
370
+ newFace = Face()
371
+ self.Faces.append(newFace)
372
+ newFace.Corners.append(Corner(tet,two_subsimplex))
373
+ tet.Class[two_subsimplex] = newFace
374
+ othertet = tet.Neighbor[two_subsimplex]
375
+ if othertet:
376
+ newFace.IntOrBdry = 'int'
377
+ othersubsimplex = tet.Gluing[two_subsimplex].image(two_subsimplex)
378
+ newFace.Corners.append(Corner(othertet, othersubsimplex))
379
+ othertet.Class[othersubsimplex] = newFace
380
+ else:
381
+ newFace.IntOrBdry = 'bdry'
382
+ for i in range(len(self.Faces)):
383
+ self.Faces[i].Index = i
384
+
385
+ def orient(self):
386
+ """
387
+ The simplification moves below assume that the Mcomplex is oriented.
388
+ Yes, oriented, not just orientable. An Mcomplex has been oriented if
389
+ all of the gluing permutations are odd. The orient method walks through
390
+ the manifold reorienting tetrahedra to try to get all of the gluing
391
+ permutations to be odd. Returns True on success, False if the manifold is
392
+ not orientable.
393
+ """
394
+ for tet in self.Tetrahedra:
395
+ tet.Checked = 0
396
+ self.walk_and_orient(self[0], 1)
397
+ self.rebuild()
398
+ return self.is_oriented()
399
+
400
+ def is_oriented(self):
401
+ for tet in self.Tetrahedra:
402
+ for two_subsimplex in TwoSubsimplices:
403
+ if (not tet.Neighbor[two_subsimplex] is None
404
+ and tet.Gluing[two_subsimplex].sign() == 0):
405
+ return False
406
+ return True
407
+
408
+ def walk_and_orient(self, tet, sign):
409
+ if tet.Checked == 1:
410
+ return
411
+ tet.Checked = 1
412
+ if sign == 0:
413
+ tet.reverse()
414
+ for ssimp in TwoSubsimplices:
415
+ if tet.Neighbor[ssimp] is not None:
416
+ self.walk_and_orient(tet.Neighbor[ssimp], tet.Gluing[ssimp].sign())
417
+
418
+ def build_matrix(self):
419
+ """
420
+ Convention is that the ordered quads are (Q03, Q13, Q23).
421
+ """
422
+ int_edges = [edge for edge in self.Edges if edge.IntOrBdry == 'int']
423
+ self.QuadMatrix = linalg.Matrix(len(int_edges), 3*len(self))
424
+ for edge in int_edges:
425
+ for corner in edge.Corners:
426
+ i = int_edges.index(edge)
427
+ j = corner.Tetrahedron.Index
428
+ for k in range(3):
429
+ self.QuadMatrix[i,3*j+k] += Shift[corner.Subsimplex][k]
430
+ self.build_vertex_incidences()
431
+
432
+ def build_vertex_incidences(self):
433
+ for vertex in self.Vertices:
434
+ vertex.IncidenceVector = linalg.Vector( 4*len(self) )
435
+ for corner in vertex.Corners:
436
+ j = corner.Tetrahedron.Index
437
+ vertex.IncidenceVector[4*j:4*j+4] += VertexVector[corner.Subsimplex]
438
+
439
+ def find_normal_surfaces(self, modp=0, print_progress=False,
440
+ algorithm='FXrays'):
441
+ """
442
+ Convention is that the ordered quads are (Q03, Q13, Q23).
443
+ """
444
+ self.NormalSurfaces = []
445
+ self.build_matrix()
446
+ if algorithm == 'FXrays':
447
+ try:
448
+ import FXrays
449
+ except ImportError:
450
+ raise ImportError("You need to install the FXrays module"
451
+ "if you want to find normal surfaces.")
452
+ coeff_list = FXrays.find_Xrays(self.QuadMatrix.nrows(),
453
+ self.QuadMatrix.ncols(),
454
+ self.QuadMatrix.entries(), modp,
455
+ print_progress=print_progress)
456
+
457
+ elif algorithm == 'regina':
458
+ T = self.regina_triangulation()
459
+ import regina
460
+ coeff_list = []
461
+ tets = range(len(self))
462
+ surfaces = regina.NNormalSurfaceList.enumerate(T, regina.NS_QUAD)
463
+ for i in range(surfaces.getNumberOfSurfaces()):
464
+ S = surfaces.getSurface(i)
465
+ coeff_vector = [int(S.getQuadCoord(tet, quad).stringValue())
466
+ for tet in tets for quad in (2, 1, 0)]
467
+ coeff_list.append(coeff_vector)
468
+
469
+ else:
470
+ raise ValueError("Algorithm must be in {'FXrays', 'regina'}")
471
+
472
+ for coeff_vector in coeff_list:
473
+ if max(self.LinkGenera) == 0:
474
+ self.NormalSurfaces.append(ClosedSurface(self, coeff_vector))
475
+ elif self.LinkGenera.count(1) == len(self.LinkGenera):
476
+ self.NormalSurfaces.append(SpunSurface(self, coeff_vector))
477
+ else:
478
+ self.NormalSurfaces.append(Surface(self, coeff_vector))
479
+
480
+ def normal_surface_info(self, out=sys.stdout):
481
+ try:
482
+ for surface in self.NormalSurfaces:
483
+ out.write("-------------------------------------\n\n")
484
+ surface.info(self, out)
485
+ out.write('\n')
486
+ except OSError:
487
+ pass
488
+
489
+ def almost_normal_surface_info(self, out=sys.stdout):
490
+ try:
491
+ for surface in self.AlmostNormalSurfaces:
492
+ out.write("-------------------------------------\n\n")
493
+ surface.info(self, out)
494
+ out.write('\n')
495
+ except OSError:
496
+ pass
497
+
498
+ # Simplification Moves
499
+ #
500
+ # The simplification moves require that the list of edge classes
501
+ # be up to date. Edge classes are recomputed as part of each
502
+ # move. The vertex classes are not used, nor are they updated, by
503
+ # these moves, with the exception of randomize.
504
+
505
+ def _face_permits_two_to_three(self, a, b):
506
+ S, T = a.Tetrahedron, b.Tetrahedron
507
+ if S is None:
508
+ return False, 'Tetrahedron not attached to face'
509
+ if S == T:
510
+ return False, 'Two tetrahedra are the same'
511
+ return True, None
512
+
513
+ def _two_to_three_move_hook(self, old_arrow, new_arrows):
514
+ pass
515
+
516
+ def two_to_three(self, face_or_arrow, tet=None,
517
+ return_arrow=False, must_succeed=False,
518
+ unsafe_mode=False):
519
+ """
520
+ Perform a 2-to-3 Pachner move on the face specified by
521
+ (face_or_arrow, tet), replacing the two tetrahedra with three
522
+ tetrahedra around an edge.
523
+
524
+ Returns ``True`` or ``False`` depending on whether the
525
+ requested move succeeded. When ``must_succeed`` is ``True``,
526
+ it instead raises an exception if the requested move is
527
+ topologically impossible.
528
+
529
+ When ``unsafe_mode`` is ``True`` it does not rebuild the edge
530
+ classes; in any mode, it does not rebuild the vertex classes.
531
+ """
532
+
533
+ if isinstance(face_or_arrow, Arrow):
534
+ assert tet is None
535
+ arrow = face_or_arrow
536
+ a = arrow.copy()
537
+ else:
538
+ arrow = None
539
+ a = Arrow(PickAnEdge[face_or_arrow], face_or_arrow, tet)
540
+
541
+ a = a.copy()
542
+ b = a.glued()
543
+ if not unsafe_mode:
544
+ possible, reason = self._face_permits_two_to_three(a, b)
545
+ if not possible:
546
+ if must_succeed:
547
+ raise ValueError(reason)
548
+ return False
549
+ a_orig = a.copy()
550
+ new = self.new_arrows(3)
551
+ for i in range(3):
552
+ new[i].glue(new[(i + 1) % 3])
553
+ a.reverse()
554
+ for c in new:
555
+ c.opposite().glue(a.glued())
556
+ c.reverse().glue(b.glued())
557
+ a.rotate(-1)
558
+ b.rotate(1)
559
+ for c in new:
560
+ c.reverse()
561
+ c.opposite()
562
+ self._two_to_three_move_hook(a_orig, new)
563
+ self.delete_tet(a.Tetrahedron)
564
+ self.delete_tet(b.Tetrahedron)
565
+ if not unsafe_mode:
566
+ self.build_edge_classes()
567
+ if VERBOSE:
568
+ print('2->3')
569
+ print(self.EdgeValences)
570
+ if return_arrow:
571
+ return new[1].north_head().get_arrow()
572
+ else:
573
+ return True
574
+
575
+ def _edge_permits_three_to_two(self, edge):
576
+ if not edge.IntOrBdry == 'int':
577
+ return False, 'Cannot do move on exterior edge'
578
+ if edge.valence() != 3:
579
+ return False, 'Edge has valence %d not 3' % edge.valence()
580
+ if not edge.distinct():
581
+ return False, 'Tets around edge are not distinct'
582
+ return True, None
583
+
584
+ def _three_to_two_move_hook(self, old_arrow, new_arrows):
585
+ pass
586
+
587
+ def three_to_two(self, edge_or_arrow, return_arrow=False,
588
+ must_succeed=False, unsafe_mode=False):
589
+ """
590
+ Replaces the star of an edge of valence 3 by two tetrahedra.
591
+
592
+ Options and return value are the same as ``two_to_three``.
593
+ """
594
+ edge, a = edge_and_arrow(edge_or_arrow)
595
+ if not unsafe_mode:
596
+ possible, reason = self._edge_permits_three_to_two(edge)
597
+ if not possible:
598
+ if must_succeed:
599
+ raise ValueError(reason)
600
+ return False
601
+
602
+ a_orig = a.copy()
603
+ b = self.new_arrow()
604
+ c = self.new_arrow()
605
+ b.glue(c)
606
+ b_orig = b.copy()
607
+ b.reverse()
608
+ b_to_return = b.copy()
609
+ for i in range(3):
610
+ b.glue(a.opposite().glued())
611
+ c.glue(a.reverse().glued())
612
+ b.rotate(-1)
613
+ c.rotate(1)
614
+ a.reverse().opposite().next()
615
+
616
+ self._three_to_two_move_hook(a_orig, (b_orig, b, c))
617
+ if unsafe_mode:
618
+ tet0 = a_orig.Tetrahedron
619
+ tet1 = a_orig.next().Tetrahedron
620
+ tet2 = a_orig.next().Tetrahedron
621
+ self.delete_tet(tet0)
622
+ self.delete_tet(tet1)
623
+ self.delete_tet(tet2)
624
+ else:
625
+ for corner in edge.Corners:
626
+ self.delete_tet(corner.Tetrahedron)
627
+ if not unsafe_mode:
628
+ self.build_edge_classes()
629
+ if VERBOSE:
630
+ print('3->2')
631
+ print(self.EdgeValences)
632
+ if return_arrow:
633
+ return b_to_return
634
+ return True
635
+
636
+ def _arrow_permits_two_to_zero(self, arrow):
637
+ edge = arrow.axis()
638
+ if not edge.IntOrBdry == 'int':
639
+ return False, 'Cannot do move on exterior edge'
640
+ if edge.valence() != 2:
641
+ return False, 'Edge has valence %d not 2' % edge.valence()
642
+ if not edge.distinct():
643
+ return False, 'Tets around edge are not distinct'
644
+ if arrow.equator() == arrow.glued().equator():
645
+ return False, 'Edges opposite the valence 2 edge are the same'
646
+ # You'd think we should exclude the following, but bizarrely
647
+ # everything is fine when this happens, which is quite
648
+ # frequently in some settings.
649
+ #
650
+ # T0 = edge.Corners[0].Tetrahedron
651
+ # T1 = edge.Corners[1].Tetrahedron
652
+ # if (T0 in T0.Neighbor.values()) or (T1 in T1.Neighbor.values()):
653
+ # return False, 'One tet is glued to itself'
654
+ return True, None
655
+
656
+ def _two_to_zero_hook(self, old_arrow):
657
+ pass
658
+
659
+ def two_to_zero(self, edge_or_arrow, must_succeed=False, unsafe_mode=False):
660
+ """
661
+ Flatten the star of an edge of valence 2 to eliminate two
662
+ tetrahedra.
663
+
664
+ Options and return value are the same as ``two_to_three``.
665
+ """
666
+ edge, a = edge_and_arrow(edge_or_arrow)
667
+ b = a.glued()
668
+
669
+ possible, reason = self._arrow_permits_two_to_zero(a)
670
+ if not possible:
671
+ if must_succeed:
672
+ raise ValueError(reason)
673
+ return False
674
+
675
+ self._two_to_zero_hook(a)
676
+ a.opposite().glued().reverse().glue(b.opposite().glued())
677
+ a.reverse().glued().reverse().glue(b.reverse().glued())
678
+
679
+ for corner in edge.Corners:
680
+ self.delete_tet(corner.Tetrahedron)
681
+ if not unsafe_mode:
682
+ self.build_edge_classes()
683
+ if VERBOSE:
684
+ print('2->0')
685
+ print(self.EdgeValences)
686
+ return True
687
+
688
+ def zero_to_two(self, arrow1, gap):
689
+ """
690
+ Blow up two adjacent faces into a pair of tetrahedra. The
691
+ faces are specified by passing an arrow specifying the first
692
+ face and an integer n. The second face is obtained by
693
+ reversing the arrow and applying next() n times. Thus there
694
+ are n faces between the two that are involved in the blow up.
695
+ Returns ``True`` on success, ``False`` if the move cannot be
696
+ performed.
697
+ """
698
+ arrow2 = arrow1.copy().reverse()
699
+ count = 0
700
+ while count < gap:
701
+ if arrow2.next() is None:
702
+ return False
703
+ count = count + 1
704
+ # Do we *also* need the old test, which was
705
+ # b.Tetrahedron == arrow1.Tetrahedron ?
706
+ if arrow1.face_class() == arrow2.face_class():
707
+ return 0
708
+ a = arrow1.glued()
709
+ b = arrow2.glued()
710
+ c = self.new_arrows(2)
711
+ c[0].glue(c[1])
712
+ c[1].glue(c[0])
713
+ c[0].opposite().glue(a)
714
+ c[0].reverse().glue(b)
715
+ c[1].opposite().glue(arrow1.reverse())
716
+ c[1].reverse().glue(arrow2.reverse())
717
+ self.clear_tet(arrow1.Tetrahedron)
718
+ self.clear_tet(arrow2.Tetrahedron)
719
+ self.build_edge_classes()
720
+ if VERBOSE:
721
+ print('0->2')
722
+ print(self.EdgeValences)
723
+ return True
724
+
725
+ def _edge_permits_four_to_four(self, edge):
726
+ if not edge.IntOrBdry == 'int':
727
+ return False, 'Cannot do move on exterior edge'
728
+ if edge.valence() != 4:
729
+ return False, 'Edge has valence %d not 4' % edge.valence()
730
+ if not edge.distinct():
731
+ return False, 'Tets around edge are not distinct'
732
+ return True, None
733
+
734
+ def _four_to_four_move_hook(self, old_arrow, new_arrows):
735
+ pass
736
+
737
+ def four_to_four(self, edge_or_arrow, must_succeed=False, unsafe_mode=False):
738
+ """
739
+ Replace an edge of valence 4 by another diagonal of the
740
+ octahedron formed by the star of the edge. There are two
741
+ choices for this diagonal. If you care which one is used then
742
+ pass an arrow representing the edge of valence four. The head
743
+ of the arrow will be an endpoint of the new diagonal. If you
744
+ don't care, just pass an edge. The choice of diagonal will
745
+ then be made randomly.
746
+
747
+ Options and return value are the same as ``two_to_three``.
748
+ """
749
+ edge, a = edge_and_arrow(edge_or_arrow)
750
+ a_orig = a.copy()
751
+
752
+ possible, reason = self._edge_permits_four_to_four(edge)
753
+ if not possible:
754
+ if must_succeed:
755
+ raise ValueError(reason)
756
+ return False
757
+
758
+ c = self.new_arrows(4)
759
+ c_orig = [x.copy() for x in c]
760
+ for i in range(4):
761
+ c[i].glue(c[(i + 1) % 4])
762
+ b = a.glued().reverse()
763
+ c[0].opposite().glue(a.rotate(1).glued())
764
+ c[1].opposite().glue(b.rotate(-1).glued())
765
+ c[2].opposite().glue(b.rotate(-1).glued())
766
+ c[3].opposite().glue(a.rotate(1).glued())
767
+ a.rotate(1).reverse().next()
768
+ b.rotate(-1).reverse().next()
769
+ c[0].reverse().glue(a.rotate(-1).glued())
770
+ c[1].reverse().glue(b.rotate(1).glued())
771
+ c[2].reverse().glue(b.rotate(1).glued())
772
+ c[3].reverse().glue(a.rotate(-1).glued())
773
+
774
+ self._four_to_four_move_hook(a_orig, c_orig)
775
+ for corner in edge.Corners:
776
+ self.delete_tet(corner.Tetrahedron)
777
+
778
+ if not unsafe_mode:
779
+ self.build_edge_classes()
780
+ if VERBOSE:
781
+ print('4->4')
782
+ print(self.EdgeValences)
783
+
784
+ return True
785
+
786
+ def attack_valence_one(self):
787
+ """
788
+ Modify the triangulation near a valence 1 edge, creating a
789
+ valence 2 edge that can likely be eliminated, reducing the
790
+ number of tetrahedra by one.
791
+ """
792
+ if len(self) == 1:
793
+ return False
794
+ for e in self.Edges:
795
+ if e.valence() == 1:
796
+ corner = e.Corners[0]
797
+ tet = corner.Tetrahedron
798
+ sub = corner.Subsimplex
799
+ other_faces = [face for face in TwoSubsimplices
800
+ if not is_subset(sub, face)]
801
+ assert len(other_faces) == 2
802
+ face = other_faces[0]
803
+ self.two_to_three(face, tet, must_succeed=True)
804
+ return True
805
+ return False
806
+
807
+ def eliminate_valence_two(self):
808
+ """
809
+ Perform a single ``two_to_zero`` move on a valence 2 edge, if
810
+ any such is possible.
811
+ """
812
+ did_simplify = False
813
+ progress = True
814
+ while progress:
815
+ progress = False
816
+ for edge in self.Edges:
817
+ if edge.valence() == 2:
818
+ if self.two_to_zero(edge):
819
+ progress, did_simplify = True, True
820
+ break
821
+ return did_simplify
822
+
823
+ def eliminate_valence_three(self):
824
+ """
825
+ Perform a single ``three_to_two`` move on a valence 3 edge, if
826
+ any such is possible.
827
+ """
828
+ did_simplify = False
829
+ progress = True
830
+ while progress:
831
+ progress = False
832
+ for edge in self.Edges:
833
+ if edge.valence() == 3:
834
+ if self.three_to_two(edge):
835
+ progress, did_simplify = True, True
836
+ break
837
+ return did_simplify
838
+
839
+ def easy_simplify(self):
840
+ """
841
+ Perform moves eliminating edges of valence 1, 2, and 3,
842
+ monotonically reducing the number of tetrahedra until no
843
+ further such moves are possible. Returns whether or not the
844
+ number of tetrahedra was reduced.
845
+
846
+ >>> M = Mcomplex('zLALvwvMwLzzAQPQQkbcbeijmoomvwuvust'
847
+ ... 'wwytxtyxyahkswpmakguadppmrssxbkoxsi')
848
+ >>> M.easy_simplify()
849
+ True
850
+ >>> len(M)
851
+ 1
852
+ >>> M.rebuild(); M.isosig()
853
+ 'bkaagj'
854
+ """
855
+
856
+ init_tet = len(self)
857
+ progress = True
858
+ while progress:
859
+ curr_tet = len(self)
860
+ while self.attack_valence_one():
861
+ pass
862
+ while self.eliminate_valence_two() | self.eliminate_valence_three():
863
+ pass
864
+ progress = len(self) < curr_tet
865
+
866
+ return len(self) < init_tet
867
+
868
+ def jiggle(self):
869
+ """
870
+ Do a random ``four_to_four`` move if one is possible.
871
+ """
872
+ fours = [edge for edge in self.Edges
873
+ if edge.valence() == 4 and edge.IntOrBdry == 'int']
874
+ if len(fours) == 0:
875
+ return False
876
+ return self.four_to_four(random.choice(fours))
877
+
878
+ JIGGLE_LIMIT = 6
879
+
880
+ def simplify(self, jiggle_limit=None):
881
+ """
882
+ Try to simplify the triangulation using only moves that do not
883
+ increase the total number of tetrahedra, using a combination
884
+ of ``jiggle`` and ``easy_simplify``.
885
+
886
+ When ``jiggle_limit`` is ``None``, it defaults to
887
+ ``self.JIGGLE_LIMIT`` which is typically 6.
888
+ """
889
+
890
+ if jiggle_limit is None:
891
+ jiggle_limit = self.JIGGLE_LIMIT
892
+
893
+ init_tet = len(self)
894
+ for j in range(jiggle_limit):
895
+ self.easy_simplify()
896
+ if not self.jiggle():
897
+ break
898
+ self.eliminate_valence_two()
899
+ return len(self) < init_tet
900
+
901
+ def blowup(self, n):
902
+ """
903
+ Do ``n`` randomly chosen ``two_to_three`` moves.
904
+ """
905
+ for i in range(n):
906
+ rand_tet = self[ random.randint(0, len(self) - 1) ]
907
+ rand_face = TwoSubsimplices[random.randint(0,3)]
908
+ self.two_to_three(rand_face, rand_tet)
909
+ self.eliminate_valence_two()
910
+ return len(self)
911
+
912
+ def blowup2(self, n):
913
+ """
914
+ Create ``n`` edges of valence 2 in random places, removing valence
915
+ 3 edges whenever they appear.
916
+ """
917
+
918
+ for i in range(n):
919
+ rand_edge = self.Edges[ random.randint(0, len(self.Edges) - 1) ]
920
+ j = random.randint(0, len(rand_edge.Corners) - 1)
921
+ k = random.randint(0, len(rand_edge.Corners) - 1 - j)
922
+ one_subsimplex = rand_edge.Corners[j].Subsimplex
923
+ two_subsimplex = LeftFace[one_subsimplex]
924
+ a = Arrow(one_subsimplex, two_subsimplex,
925
+ rand_edge.Corners[j].Tetrahedron)
926
+ self.zero_to_two(a, k)
927
+ self.eliminate_valence_three()
928
+ return len(self)
929
+
930
+ BLOW_UP_MULTIPLE = 6
931
+
932
+ def randomize(self, blow_up_multiple=None):
933
+ """
934
+ Do ``blow_up_multiple`` times the current number of tetrahedra
935
+ random ``two_to_three`` moves, and then ``simplify``.
936
+
937
+ If ``blow_up_multiple`` is ``None``, it defaults to
938
+ ``self.BLOW_UP_MULTIPLE`` which is typically 6.
939
+
940
+ Unlike the other simplification methods, this one rebuilds the
941
+ vertices.
942
+ """
943
+ if blow_up_multiple is None:
944
+ blow_up_multiple = self.BLOW_UP_MULTIPLE
945
+ self.blowup(blow_up_multiple * len(self))
946
+ self.simplify()
947
+ self.rebuild()
948
+ return len(self)
949
+
950
+ def bdry_neighbor(self, arrow):
951
+ """
952
+ Find a boundary face adjoining a given boundary face.
953
+ Given an Arrow representing a boundary face, return the Arrow
954
+ representing the boundary face that shares the Arrow's Edge.
955
+ """
956
+ if arrow.next() is not None:
957
+ raise Insanity("That boundary face is not on the boundary!")
958
+ edge = arrow.Tetrahedron.Class[arrow.Edge]
959
+ if edge.LeftBdryArrow == arrow:
960
+ return edge.RightBdryArrow
961
+ else:
962
+ return edge.LeftBdryArrow
963
+
964
+ def add_fan(self, edge, n):
965
+ """
966
+ Adds a fan of ``n`` tetrahedra onto a boundary edge and rebuilds.
967
+ """
968
+ if not edge.IntOrBdry == 'bdry':
969
+ return 0
970
+ a = edge.LeftBdryArrow
971
+ b = edge.RightBdryArrow.reverse()
972
+ if n == 0:
973
+ a.glue(b)
974
+ return 1
975
+ new = self.new_arrows(n)
976
+ a.glue( new[0] )
977
+ for j in range(len(new) - 1):
978
+ new[j].glue( new[j + 1] )
979
+ new[-1].glue( b )
980
+ self.rebuild()
981
+ return 1
982
+
983
+ def split_star(self,edge):
984
+ """
985
+ Subdivides the star of an edge e. If the edge has an embedded
986
+ star then this operation first subdivides the edge, producing
987
+ one new vertex and two new edges. Next each tetrahedron which
988
+ meets the edge is divided into two tetrahedra along a face
989
+ which is the join of the new vertex to the edge opposite to e.
990
+ The edge e must not be self-adjacent in any 2-simplex for this
991
+ operation to be possible. However, it is allowed for a
992
+ tetrahedron to have two opposite edges identified to e. In
993
+ this case the tetrahedron is split into four tetrahedra,
994
+ forming the join of two segments of length 2. In order to
995
+ deal with this situation we work our way around the edge
996
+ making the identifications as we go. The first time that we
997
+ encounter a corner of a certain tetrahedron it gets split into
998
+ two. Those two are glued into place and may be encountered
999
+ later in the process, at which time each of them get split in
1000
+ two.
1001
+
1002
+ Returns an arrow associated to the "top half" of the original edge
1003
+ and the "first" tetrahedron adjacent to that edge, or 0 if the edge
1004
+ is self-adjacent.
1005
+ """
1006
+
1007
+ if edge.selfadjacent():
1008
+ return 0
1009
+ # Collect the garbage as we go -- some of the new tets may
1010
+ # turn into garbage later on.
1011
+ garbage = []
1012
+ # Remember where we started.
1013
+ first_arrow = edge.get_arrow().next()
1014
+ first_bottom,first_top = self.new_arrows(2)
1015
+ a = first_arrow.copy()
1016
+ bottom = first_bottom.copy()
1017
+ top = first_top.copy()
1018
+ # Work around the edge.
1019
+ while 1:
1020
+ garbage.append(a.Tetrahedron)
1021
+ # Glue the two new tetrahedra together.
1022
+ bottom.glue(top)
1023
+ # Attach the top face of our new pair.
1024
+ a.opposite()
1025
+ above = a.glued()
1026
+ if above.is_null():
1027
+ # This may mean that our first tetrahedron had opposite edges attached
1028
+ # to our edge. We are splitting it for the second time, which will
1029
+ # cause first_top and first_bottom to get dumped onto the garbage heap.
1030
+ # We have to create a new first_top or first_bottom here, or we won't
1031
+ # be able to close everything up at the end. (On the other hand, we
1032
+ # may just have found a boundary face.)
1033
+ check = a.copy().opposite().reverse()
1034
+ new_first = top.copy().opposite().reverse()
1035
+ if check == first_top:
1036
+ first_top = new_first
1037
+ elif check == first_bottom:
1038
+ first_bottom = new_first
1039
+ else:
1040
+ top.glue(above)
1041
+ # Attach the bottom face of the new pair.
1042
+ bottom.reverse()
1043
+ a.reverse()
1044
+ below = a.glued()
1045
+ if below.is_null():
1046
+ # See comment above.
1047
+ check = a.copy().opposite().reverse()
1048
+ new_first = bottom.copy().opposite().reverse()
1049
+ if check == first_bottom:
1050
+ first_bottom = new_first
1051
+ elif check == first_top:
1052
+ first_top = new_first
1053
+ else:
1054
+ bottom.glue(below)
1055
+ bottom.reverse()
1056
+ # Now move on around the edge.
1057
+ a.reverse()
1058
+ a.opposite()
1059
+ a.next()
1060
+ if a == first_arrow:
1061
+ break
1062
+ next_bottom, next_top = self.new_arrows(2)
1063
+ top.opposite()
1064
+ bottom.opposite()
1065
+ top.glue(next_top)
1066
+ bottom.glue(next_bottom)
1067
+ top = next_top.opposite()
1068
+ bottom = next_bottom.opposite()
1069
+ # OK. We are back to the beginning. Close it up.
1070
+ top.opposite()
1071
+ bottom.opposite()
1072
+ top.glue(first_top.opposite())
1073
+ bottom.glue(first_bottom.opposite())
1074
+ # Clean up the garbage.
1075
+ for tet in garbage:
1076
+ self.delete_tet(tet)
1077
+ self.rebuild()
1078
+ return first_top
1079
+
1080
+ def smash_star(self, edge):
1081
+ """
1082
+ If an edge joins distinct vertices and has an embedded open
1083
+ star then the following method will smash each 3-simplex in
1084
+ the star down to a 2-simplex, and smash the edge to a vertex,
1085
+ reducing the number of vertices by 1. Returns ``True`` on
1086
+ success, ``False`` on failure.
1087
+ """
1088
+ if not edge.distinct() or edge.Vertices[0] == edge.Vertices[1]:
1089
+ return False
1090
+ start = edge.get_arrow()
1091
+ a = start.copy()
1092
+ garbage = []
1093
+ while 1:
1094
+ garbage.append(a.Tetrahedron)
1095
+ top = a.opposite().glued()
1096
+ bottom = a.reverse().glued().reverse()
1097
+ bottom.glue(top)
1098
+ a.reverse().opposite().next()
1099
+ if a == start:
1100
+ break
1101
+ for tet in garbage:
1102
+ self.delete_tet(tet)
1103
+ self.rebuild()
1104
+ return True
1105
+
1106
+ def smash_all_edges(self):
1107
+ """
1108
+ Collapse edges to reduce the number of vertices as much as
1109
+ possible. Returns whether the number of vertices has been
1110
+ reduced to one.
1111
+ """
1112
+ success = True
1113
+ while len(self.Vertices) > 1 and success:
1114
+ success = False
1115
+ edges = sorted(self.Edges, key=lambda E:E.valence(), reverse=True)
1116
+ edges = self.Edges
1117
+ for edge in edges:
1118
+ if self.smash_star(edge):
1119
+ success = True
1120
+ break
1121
+
1122
+ return len(self.Vertices) == 1
1123
+
1124
+ def replace_star(self, arrow, top_arrows, bottom_arrows):
1125
+ """
1126
+ This method takes an arrow and replaces its star with
1127
+ other_complex attaching that complex via top_arrows and
1128
+ bottom_arrows where: Let a be the arrow defining the same
1129
+ directed edge as arrow which is the ith such arrow counting
1130
+ around the star. Then a.glued() is glued to top_arrow[i] and
1131
+ a.reverse().glued() is glued to bottom_arrow[i].
1132
+
1133
+ NOTE: If it fails, you need to delete any tets that you were
1134
+ trying to add.
1135
+ """
1136
+
1137
+ edge = arrow.Tetrahedron.Class[arrow.Edge]
1138
+ a = arrow.copy().opposite()
1139
+
1140
+ # check to make sure that the replacement will work
1141
+
1142
+ if not edge.IntOrBdry == 'int':
1143
+ return None
1144
+ if not edge.distinct():
1145
+ return None
1146
+ valence = edge.valence()
1147
+ if len(top_arrows) != valence or len(bottom_arrows) != valence:
1148
+ return None
1149
+
1150
+ # Attach other_complex to manifold replace star of arrow
1151
+ #
1152
+ # It's important that we do things incrementally as follows in
1153
+ # case two outside faces of the star are glued together.
1154
+
1155
+ for i in range(valence):
1156
+ top_arrows[i].glue(a.glued())
1157
+ a.reverse()
1158
+ bottom_arrows[i].glue(a.glued())
1159
+ a.reverse()
1160
+
1161
+ # Now advance a to represent the same edge but in the next
1162
+ # tetrahedra in the star.
1163
+ a.opposite()
1164
+ a.next()
1165
+ a.opposite()
1166
+
1167
+ # Delete old star
1168
+
1169
+ for corner in edge.Corners:
1170
+ self.delete_tet(corner.Tetrahedron)
1171
+
1172
+ # Rebuild mcomplex
1173
+ self.build_edge_classes()
1174
+ self.orient()
1175
+
1176
+ return True
1177
+
1178
+ def suspension_of_polygon(self, num_sides_of_polygon):
1179
+ """
1180
+ This method adds the suspension of a triangulation of a
1181
+ polygon to self.Tetrahedra and returns::
1182
+
1183
+ (top_arrows, bottom_arrows)
1184
+
1185
+ Currently the choice of triangulation of the polygon is one
1186
+ that is the cone over an edge. Probably this should be
1187
+ generalized. top_arrows and bottom arrows are for gluing in
1188
+ this complex via the method ``replace_star``.
1189
+ """
1190
+ top_tets = self.new_tets(num_sides_of_polygon - 2)
1191
+ bottom_tets = self.new_tets(num_sides_of_polygon - 2)
1192
+ n = len(top_tets)
1193
+
1194
+ # glue top and bottom together
1195
+ for i in range(n):
1196
+ top_tets[i].attach( F3, bottom_tets[i], (0, 2, 1, 3) )
1197
+
1198
+ # glue each tet to its neighbor
1199
+
1200
+ for i in range(n-1):
1201
+ top_tets[i].attach(F0, top_tets[i+1], (1, 0, 2, 3) )
1202
+ bottom_tets[i].attach(F0, bottom_tets[i+1], (2, 1 , 0, 3) )
1203
+
1204
+ # make arrows
1205
+
1206
+ top_arrows = [ Arrow( comp(E13), F1, top_tets[0]) ]
1207
+ bottom_arrows = [ Arrow( comp(E23), F2, bottom_tets[0]) ]
1208
+ for i in range(n):
1209
+ top_arrows.append(Arrow( comp(E23), F2, top_tets[i]))
1210
+ bottom_arrows.append(Arrow( comp(E13), F1, bottom_tets[i]))
1211
+
1212
+ top_arrows.append(Arrow(comp(E03), F0, top_tets[i]))
1213
+ bottom_arrows.append(Arrow(comp(E03), F0, bottom_tets[i]))
1214
+
1215
+ return (top_arrows, bottom_arrows)
1216
+
1217
+ def save(self, filename, format="snappy"):
1218
+ """
1219
+ Nontypical example showing saving to a string buffer:
1220
+
1221
+ >>> import io
1222
+ >>> buffer = io.StringIO()
1223
+ >>> T = Mcomplex('v3551')
1224
+ >>> T.save(buffer, 'snappy')
1225
+ >>> T.save(buffer, 'geo')
1226
+ >>> T.save(buffer, 'spine')
1227
+ >>> len(buffer.getvalue())
1228
+ 1936
1229
+ """
1230
+ if not hasattr(filename, 'write'):
1231
+ file = open(filename, 'w')
1232
+ close = True
1233
+ else:
1234
+ file = filename
1235
+ close = False
1236
+ if format == "snappy":
1237
+ files.write_SnapPea_file(self, file)
1238
+ elif format == "geo":
1239
+ files.write_geo_file(self, file)
1240
+ elif format == "spine":
1241
+ files.write_spine_file(self, file)
1242
+ if close:
1243
+ file.close()
1244
+
1245
+ def _snappea_file_contents(self):
1246
+ data = io.StringIO()
1247
+ data.name = 'from_t3m'
1248
+ files.write_SnapPea_file(self, data)
1249
+ return data.getvalue()
1250
+
1251
+ def snappy_triangulation(self, remove_finite_vertices=True):
1252
+ """
1253
+ >>> Mcomplex('4_1').snappy_manifold().homology()
1254
+ Z
1255
+
1256
+ WARNING: Code implicitly assumes all vertex links are orientable.
1257
+ """
1258
+ # We don't assume that the indices of the Tetraheda are equal
1259
+ # to range(len(self))
1260
+ tet_to_index = {T:i for i, T in enumerate(self.Tetrahedra)}
1261
+
1262
+ # Initially set all to -1, which corresponds to a finite vertex
1263
+ to_cusp_index = {vertex:-1 for vertex in self.Vertices}
1264
+ torus_cusps = 0
1265
+ for vertex in self.Vertices:
1266
+ g = vertex.link_genus()
1267
+ if g > 1:
1268
+ raise ValueError('Link of vertex has genus more than 1.')
1269
+ if g == 1:
1270
+ to_cusp_index[vertex] = torus_cusps
1271
+ torus_cusps += 1
1272
+
1273
+ tet_data, cusp_indices, peripheral_curves = [], [], []
1274
+
1275
+ for tet in self.Tetrahedra:
1276
+ neighbors, perms = [], []
1277
+ for face in TwoSubsimplices:
1278
+ if tet.Neighbor[face] is None:
1279
+ raise ValueError('SnapPy triangulations cannot have boundary')
1280
+
1281
+ neighbor = tet_to_index[tet.Neighbor[face]]
1282
+ perm = tet.Gluing[face].tuple()
1283
+ neighbors.append(neighbor)
1284
+ perms.append(perm)
1285
+ tet_data.append((neighbors, perms))
1286
+
1287
+ cusp_indices.append([to_cusp_index[tet.Class[vert]]
1288
+ for vert in ZeroSubsimplices])
1289
+ if hasattr(tet, 'PeripheralCurves'):
1290
+ for curve in tet.PeripheralCurves:
1291
+ for sheet in curve:
1292
+ one_curve_data = []
1293
+ for v in ZeroSubsimplices:
1294
+ for f in TwoSubsimplices:
1295
+ one_curve_data.append(sheet[v][f])
1296
+ peripheral_curves.append(one_curve_data)
1297
+ else:
1298
+ for i in range(4):
1299
+ peripheral_curves.append(16*[0])
1300
+
1301
+ M = snappy.Triangulation('empty')
1302
+ M._from_tetrahedra_gluing_data(tetrahedra_data=tet_data,
1303
+ num_or_cusps=torus_cusps,
1304
+ num_nonor_cusps=0,
1305
+ cusp_indices=cusp_indices,
1306
+ peripheral_curves=peripheral_curves,
1307
+ remove_finite_vertices=remove_finite_vertices)
1308
+ return M
1309
+
1310
+ def snappy_manifold(self):
1311
+ return self.snappy_triangulation().with_hyperbolic_structure()
1312
+
1313
+ def isosig(self):
1314
+ contents = self._snappea_file_contents()
1315
+ T = snappy.Triangulation(contents, remove_finite_vertices=False)
1316
+ return T.triangulation_isosig(decorated=False)
1317
+
1318
+ def regina_triangulation(self):
1319
+ """
1320
+ >>> M = Mcomplex('K14n1234')
1321
+ >>> try:
1322
+ ... T = M.regina_triangulation()
1323
+ ... assert M.isosig() == T.isoSig()
1324
+ ... except ImportError:
1325
+ ... pass
1326
+ """
1327
+ try:
1328
+ import regina
1329
+ except ImportError:
1330
+ raise ImportError('Regina module not available')
1331
+
1332
+ T = regina.Triangulation3()
1333
+ regina_tets = {tet:T.newTetrahedron() for tet in self}
1334
+ self.rebuild()
1335
+ for face in self.Faces:
1336
+ if face.IntOrBdry == 'int':
1337
+ corner = face.Corners[0]
1338
+ tet0 = corner.Tetrahedron
1339
+ face0 = corner.Subsimplex
1340
+ tet1 = tet0.Neighbor[face0]
1341
+ perm = tet0.Gluing[face0]
1342
+
1343
+ r_tet0 = regina_tets[tet0]
1344
+ r_tet1 = regina_tets[tet1]
1345
+ r_face = FaceIndex[face0]
1346
+ r_perm = regina.Perm4(*perm.tuple())
1347
+ r_tet0.join(r_face, r_tet1, r_perm)
1348
+
1349
+ return T
1350
+
1351
+ def boundary_maps(self):
1352
+ """
1353
+ The boundary maps in the homology chain complex of the
1354
+ underlying cell-complex of a Mcomplex.
1355
+
1356
+ >>> M = Mcomplex('o9_12345')
1357
+ >>> len(M.boundary_maps()) == 3
1358
+ True
1359
+ """
1360
+ return homology.boundary_maps(self)
1361
+
1362
+ def isomorphisms_to(self, other, orientation_preserving=False, at_most_one=False):
1363
+ """
1364
+ Return the list of isomorphisms between the MComplexes M and N.
1365
+ If `at_most_one` is `True`, only returns the first one found (but
1366
+ still as a list).
1367
+
1368
+ >>> tri_data = [([0,1,0,1], [(2,1,0,3), (0,3,2,1), (2,1,0,3), (0,1,3,2)]),
1369
+ ... ([1,1,0,0], [(1,0,2,3), (1,0,2,3), (0,1,3,2), (0,3,2,1)])]
1370
+ >>> M = Mcomplex(tri_data)
1371
+ >>> N = Mcomplex(M.isosig())
1372
+ >>> isos = M.isomorphisms_to(N); len(isos)
1373
+ 4
1374
+ >>> isos[0]
1375
+ {0: [tet0, (0, 2, 1, 3)], 1: [tet1, (0, 2, 1, 3)]}
1376
+ >>> len(M.isomorphisms_to(N, orientation_preserving=True))
1377
+ 2
1378
+ >>> M.two_to_three(Arrow(E01, F3, M[0])); M.rebuild()
1379
+ True
1380
+ >>> len(M), len(N)
1381
+ (3, 2)
1382
+ >>> M.isomorphisms_to(N)
1383
+ []
1384
+ >>> F = Mcomplex('m004')
1385
+ >>> N.isomorphisms_to(F)
1386
+ []
1387
+ >>> N = Mcomplex(M.isosig())
1388
+ >>> M.isomorphisms_to(N, at_most_one=True)[0]
1389
+ {0: [tet1, (0, 2, 3, 1)], 1: [tet2, (0, 2, 3, 1)], 2: [tet0, (0, 3, 1, 2)]}
1390
+ >>> M = Mcomplex(tri_data)
1391
+ >>> M.two_to_three(Arrow(E01, F3, M[0])); M.two_to_three(Arrow(E01, F3, M[1]))
1392
+ True
1393
+ True
1394
+ >>> M.rebuild()
1395
+ >>> len(M) == 4
1396
+ True
1397
+ >>> N = Mcomplex(M.isosig())
1398
+ >>> M.isomorphisms_to(N, at_most_one=True)[0] # doctest: +NORMALIZE_WHITESPACE
1399
+ {0: [tet0, (1, 3, 0, 2)], 1: [tet1, (3, 0, 1, 2)],
1400
+ 2: [tet3, (2, 0, 3, 1)], 3: [tet2, (3, 1, 2, 0)]}
1401
+ """
1402
+ M, N = self, other
1403
+ if not isinstance(N, Mcomplex):
1404
+ raise ValueError('The other triangulation must be an Mcomplex')
1405
+
1406
+ if len(M) != len(N):
1407
+ return []
1408
+ t_M0 = M[0]
1409
+
1410
+ if orientation_preserving:
1411
+ if not (M.is_oriented() and N.is_oriented()):
1412
+ raise ValueError('Asked for orientation preserving isomorphisms '
1413
+ 'of unoriented triangulations')
1414
+ permutations = list(Perm4.A4()) # even perms only
1415
+ else:
1416
+ permutations = list(Perm4.S4())
1417
+
1418
+ isomorphisms = []
1419
+ # We will try and build an isomorphism from M to N that sends t_M0 to t_N0
1420
+ for t_N0 in N:
1421
+ # for each way t_M can be identified with t_N
1422
+ for perm in permutations:
1423
+ # initially the map is not defined
1424
+ iso = {k:None for k in range(len(M))}
1425
+ # set up first map t_M -> t_N
1426
+ # temporary way of encoding gluing.
1427
+ iso[0] = [t_N0, perm]
1428
+ tet_queue = [t_M0]
1429
+ while tet_queue != []:
1430
+ t_M = tet_queue.pop()
1431
+ t_N = iso[t_M.Index][0]
1432
+ perm = iso[t_M.Index][1]
1433
+
1434
+ # Now, for each face F of t_0, package tet that meets
1435
+ # t_0 along F in list neighbors
1436
+ neighbors_M = [t_M.Neighbor[face] for face in TwoSubsimplices]
1437
+ # Need info in N too.
1438
+ neighbors_N = [t_N.Neighbor[perm.image(face)] for face in TwoSubsimplices]
1439
+
1440
+ # record gluings for each face
1441
+ gluings_M = [t_M.Gluing[face] for face in TwoSubsimplices]
1442
+ gluings_N = [t_N.Gluing[perm.image(face)] for face in TwoSubsimplices]
1443
+ # check compatibility
1444
+ maps = [gluings_N[k]*perm*inv(gluings_M[k]) for k in [0,1,2,3]]
1445
+
1446
+ # now we try and update iso and hope there are no
1447
+ # incompatibilities
1448
+ for i in range(len(neighbors_M)):
1449
+ t = neighbors_M[i]
1450
+ s = neighbors_N[i]
1451
+ map = maps[i]
1452
+
1453
+ if iso[t.Index] is not None:
1454
+ if iso[t.Index][0] != s or iso[t.Index][1].tuple() != map.tuple():
1455
+ # not an iso!
1456
+ iso = {k:None for k in range(len(M))} # reset iso
1457
+ tet_queue = [] # clear queue
1458
+ break
1459
+ else:
1460
+ # iso[t] hasn't been set, so we set it and
1461
+ # move forward with it on the queue
1462
+ iso[t.Index] = [s,map]
1463
+ tet_queue = tet_queue + [t]
1464
+
1465
+ # did we succeed, or do we need to reset everything
1466
+ if None not in list(iso.values()): # we succeed!
1467
+ isomorphisms.append(iso.copy())
1468
+ if at_most_one:
1469
+ return isomorphisms
1470
+ # otherwise, we failed and the loop goes on
1471
+
1472
+ return isomorphisms
1473
+
1474
+
1475
+ def tets_from_data(fake_tets):
1476
+ """
1477
+ Takes a list where the ith element represents the gluing data
1478
+ for the ith tetraherda::
1479
+
1480
+ ( [Neighbors], [Glueings] )
1481
+
1482
+ and creates the corresponding glued Tetraherda.
1483
+ """
1484
+ fake_tets = fake_tets
1485
+ num_tets = len(fake_tets)
1486
+ tets = [Tetrahedron() for i in range(num_tets)]
1487
+ for i in range(num_tets):
1488
+ neighbors, perms = fake_tets[i]
1489
+ for k in range(4):
1490
+ tets[i].attach(TwoSubsimplices[k], tets[neighbors[k]], perms[k])
1491
+ return tets
1492
+
1493
+
1494
+ def read_geo_file(filename):
1495
+ return Mcomplex(tets_from_data(files.read_geo_file(filename)))
1496
+
1497
+
1498
+ def read_SnapPea_file(filename):
1499
+ return Mcomplex(tets_from_data(files.read_SnapPea_file(filename)))