snappy 3.3__cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. snappy/CyOpenGL.cpython-310-aarch64-linux-gnu.so +0 -0
  2. snappy/SnapPy.cpython-310-aarch64-linux-gnu.so +0 -0
  3. snappy/SnapPy.ico +0 -0
  4. snappy/SnapPy.png +0 -0
  5. snappy/SnapPyHP.cpython-310-aarch64-linux-gnu.so +0 -0
  6. snappy/__init__.py +534 -0
  7. snappy/app.py +604 -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 +280 -0
  13. snappy/cusps/cusp_area_matrix.py +98 -0
  14. snappy/cusps/cusp_areas_from_matrix.py +96 -0
  15. snappy/cusps/maximal_cusp_area_matrix.py +136 -0
  16. snappy/cusps/short_slopes_for_cusp.py +217 -0
  17. snappy/cusps/test.py +22 -0
  18. snappy/cusps/trig_cusp_area_matrix.py +63 -0
  19. snappy/database.py +454 -0
  20. snappy/db_utilities.py +79 -0
  21. snappy/decorated_isosig.py +717 -0
  22. snappy/dev/__init__.py +0 -0
  23. snappy/dev/extended_ptolemy/__init__.py +8 -0
  24. snappy/dev/extended_ptolemy/closed.py +106 -0
  25. snappy/dev/extended_ptolemy/complexVolumesClosed.py +149 -0
  26. snappy/dev/extended_ptolemy/direct.py +42 -0
  27. snappy/dev/extended_ptolemy/extended.py +406 -0
  28. snappy/dev/extended_ptolemy/giac_helper.py +43 -0
  29. snappy/dev/extended_ptolemy/giac_rur.py +129 -0
  30. snappy/dev/extended_ptolemy/gluing.py +46 -0
  31. snappy/dev/extended_ptolemy/phc_wrapper.py +220 -0
  32. snappy/dev/extended_ptolemy/printMatrices.py +70 -0
  33. snappy/dev/vericlosed/__init__.py +1 -0
  34. snappy/dev/vericlosed/computeApproxHyperbolicStructureNew.py +159 -0
  35. snappy/dev/vericlosed/computeApproxHyperbolicStructureOrb.py +90 -0
  36. snappy/dev/vericlosed/computeVerifiedHyperbolicStructure.py +111 -0
  37. snappy/dev/vericlosed/gimbalLoopFinder.py +130 -0
  38. snappy/dev/vericlosed/hyperbolicStructure.py +313 -0
  39. snappy/dev/vericlosed/krawczykCertifiedEdgeLengthsEngine.py +165 -0
  40. snappy/dev/vericlosed/oneVertexTruncatedComplex.py +122 -0
  41. snappy/dev/vericlosed/orb/__init__.py +1 -0
  42. snappy/dev/vericlosed/orb/orb_solution_for_snappea_finite_triangulation_mac +0 -0
  43. snappy/dev/vericlosed/parseVertexGramMatrixFile.py +47 -0
  44. snappy/dev/vericlosed/polishApproxHyperbolicStructure.py +61 -0
  45. snappy/dev/vericlosed/test.py +54 -0
  46. snappy/dev/vericlosed/truncatedComplex.py +176 -0
  47. snappy/dev/vericlosed/verificationError.py +58 -0
  48. snappy/dev/vericlosed/verifyHyperbolicStructureEngine.py +177 -0
  49. snappy/doc/_images/SnapPy-196.png +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 +52 -0
  61. snappy/doc/_sources/credits.rst.txt +81 -0
  62. snappy/doc/_sources/development.rst.txt +261 -0
  63. snappy/doc/_sources/index.rst.txt +215 -0
  64. snappy/doc/_sources/installing.rst.txt +249 -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 +425 -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 +906 -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 +149 -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 +192 -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 +635 -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 +453 -0
  152. snappy/doc/credits.html +184 -0
  153. snappy/doc/development.html +385 -0
  154. snappy/doc/doc-latest/additional_classes.html +1500 -0
  155. snappy/doc/doc-latest/bugs.html +132 -0
  156. snappy/doc/doc-latest/censuses.html +453 -0
  157. snappy/doc/doc-latest/credits.html +184 -0
  158. snappy/doc/doc-latest/development.html +385 -0
  159. snappy/doc/doc-latest/genindex.html +1349 -0
  160. snappy/doc/doc-latest/index.html +287 -0
  161. snappy/doc/doc-latest/installing.html +346 -0
  162. snappy/doc/doc-latest/manifold.html +3632 -0
  163. snappy/doc/doc-latest/manifoldhp.html +180 -0
  164. snappy/doc/doc-latest/news.html +438 -0
  165. snappy/doc/doc-latest/objects.inv +0 -0
  166. snappy/doc/doc-latest/other.html +160 -0
  167. snappy/doc/doc-latest/platonic_census.html +376 -0
  168. snappy/doc/doc-latest/plink.html +210 -0
  169. snappy/doc/doc-latest/ptolemy.html +253 -0
  170. snappy/doc/doc-latest/ptolemy_classes.html +1144 -0
  171. snappy/doc/doc-latest/ptolemy_examples1.html +409 -0
  172. snappy/doc/doc-latest/ptolemy_examples2.html +471 -0
  173. snappy/doc/doc-latest/ptolemy_examples3.html +414 -0
  174. snappy/doc/doc-latest/ptolemy_examples4.html +195 -0
  175. snappy/doc/doc-latest/ptolemy_prelim.html +248 -0
  176. snappy/doc/doc-latest/py-modindex.html +165 -0
  177. snappy/doc/doc-latest/screenshots.html +141 -0
  178. snappy/doc/doc-latest/search.html +135 -0
  179. snappy/doc/doc-latest/searchindex.js +1 -0
  180. snappy/doc/doc-latest/snap.html +202 -0
  181. snappy/doc/doc-latest/snappy.html +181 -0
  182. snappy/doc/doc-latest/spherogram.html +1346 -0
  183. snappy/doc/doc-latest/todo.html +166 -0
  184. snappy/doc/doc-latest/triangulation.html +1676 -0
  185. snappy/doc/doc-latest/tutorial.html +159 -0
  186. snappy/doc/doc-latest/verify.html +330 -0
  187. snappy/doc/doc-latest/verify_internals.html +1235 -0
  188. snappy/doc/genindex.html +1349 -0
  189. snappy/doc/index.html +287 -0
  190. snappy/doc/installing.html +346 -0
  191. snappy/doc/manifold.html +3632 -0
  192. snappy/doc/manifoldhp.html +180 -0
  193. snappy/doc/news.html +438 -0
  194. snappy/doc/objects.inv +0 -0
  195. snappy/doc/other.html +160 -0
  196. snappy/doc/platonic_census.html +376 -0
  197. snappy/doc/plink.html +210 -0
  198. snappy/doc/ptolemy.html +253 -0
  199. snappy/doc/ptolemy_classes.html +1144 -0
  200. snappy/doc/ptolemy_examples1.html +409 -0
  201. snappy/doc/ptolemy_examples2.html +471 -0
  202. snappy/doc/ptolemy_examples3.html +414 -0
  203. snappy/doc/ptolemy_examples4.html +195 -0
  204. snappy/doc/ptolemy_prelim.html +248 -0
  205. snappy/doc/py-modindex.html +165 -0
  206. snappy/doc/screenshots.html +141 -0
  207. snappy/doc/search.html +135 -0
  208. snappy/doc/searchindex.js +1 -0
  209. snappy/doc/snap.html +202 -0
  210. snappy/doc/snappy.html +181 -0
  211. snappy/doc/spherogram.html +1346 -0
  212. snappy/doc/todo.html +166 -0
  213. snappy/doc/triangulation.html +1676 -0
  214. snappy/doc/tutorial.html +159 -0
  215. snappy/doc/verify.html +330 -0
  216. snappy/doc/verify_internals.html +1235 -0
  217. snappy/drilling/__init__.py +456 -0
  218. snappy/drilling/barycentric.py +103 -0
  219. snappy/drilling/constants.py +5 -0
  220. snappy/drilling/crush.py +270 -0
  221. snappy/drilling/cusps.py +125 -0
  222. snappy/drilling/debug.py +242 -0
  223. snappy/drilling/epsilons.py +6 -0
  224. snappy/drilling/exceptions.py +55 -0
  225. snappy/drilling/moves.py +620 -0
  226. snappy/drilling/peripheral_curves.py +210 -0
  227. snappy/drilling/perturb.py +188 -0
  228. snappy/drilling/shorten.py +36 -0
  229. snappy/drilling/subdivide.py +274 -0
  230. snappy/drilling/test.py +23 -0
  231. snappy/drilling/test_cases.py +132 -0
  232. snappy/drilling/tracing.py +351 -0
  233. snappy/exceptions.py +26 -0
  234. snappy/export_stl.py +120 -0
  235. snappy/exterior_to_link/__init__.py +2 -0
  236. snappy/exterior_to_link/barycentric_geometry.py +463 -0
  237. snappy/exterior_to_link/exceptions.py +6 -0
  238. snappy/exterior_to_link/geodesic_map.json +14408 -0
  239. snappy/exterior_to_link/hyp_utils.py +112 -0
  240. snappy/exterior_to_link/link_projection.py +323 -0
  241. snappy/exterior_to_link/main.py +198 -0
  242. snappy/exterior_to_link/mcomplex_with_expansion.py +261 -0
  243. snappy/exterior_to_link/mcomplex_with_link.py +687 -0
  244. snappy/exterior_to_link/mcomplex_with_memory.py +162 -0
  245. snappy/exterior_to_link/pl_utils.py +491 -0
  246. snappy/exterior_to_link/put_in_S3.py +156 -0
  247. snappy/exterior_to_link/rational_linear_algebra.py +130 -0
  248. snappy/exterior_to_link/rational_linear_algebra_wrapped.py +135 -0
  249. snappy/exterior_to_link/simplify_to_base_tri.py +114 -0
  250. snappy/exterior_to_link/stored_moves.py +475 -0
  251. snappy/exterior_to_link/test.py +31 -0
  252. snappy/filedialog.py +28 -0
  253. snappy/geometric_structure/__init__.py +212 -0
  254. snappy/geometric_structure/cusp_neighborhood/__init__.py +3 -0
  255. snappy/geometric_structure/cusp_neighborhood/complex_cusp_cross_section.py +691 -0
  256. snappy/geometric_structure/cusp_neighborhood/cusp_cross_section_base.py +480 -0
  257. snappy/geometric_structure/cusp_neighborhood/exceptions.py +41 -0
  258. snappy/geometric_structure/cusp_neighborhood/real_cusp_cross_section.py +294 -0
  259. snappy/geometric_structure/cusp_neighborhood/tiles_for_cusp_neighborhood.py +156 -0
  260. snappy/geometric_structure/cusp_neighborhood/vertices.py +35 -0
  261. snappy/geometric_structure/geodesic/__init__.py +0 -0
  262. snappy/geometric_structure/geodesic/add_core_curves.py +152 -0
  263. snappy/geometric_structure/geodesic/avoid_core_curves.py +369 -0
  264. snappy/geometric_structure/geodesic/canonical_representatives.py +52 -0
  265. snappy/geometric_structure/geodesic/check_away_from_core_curve.py +60 -0
  266. snappy/geometric_structure/geodesic/constants.py +6 -0
  267. snappy/geometric_structure/geodesic/exceptions.py +22 -0
  268. snappy/geometric_structure/geodesic/fixed_points.py +106 -0
  269. snappy/geometric_structure/geodesic/geodesic_start_point_info.py +435 -0
  270. snappy/geometric_structure/geodesic/graph_trace_helper.py +67 -0
  271. snappy/geometric_structure/geodesic/line.py +30 -0
  272. snappy/geometric_structure/geodesic/multiplicity.py +127 -0
  273. snappy/geometric_structure/geodesic/tiles_for_geodesic.py +128 -0
  274. snappy/geometric_structure/test.py +22 -0
  275. snappy/gui.py +121 -0
  276. snappy/horoviewer.py +443 -0
  277. snappy/hyperboloid/__init__.py +212 -0
  278. snappy/hyperboloid/distances.py +259 -0
  279. snappy/hyperboloid/horoball.py +19 -0
  280. snappy/hyperboloid/line.py +35 -0
  281. snappy/hyperboloid/point.py +9 -0
  282. snappy/hyperboloid/triangle.py +29 -0
  283. snappy/info_icon.gif +0 -0
  284. snappy/infowindow.py +65 -0
  285. snappy/isometry_signature.py +389 -0
  286. snappy/len_spec/__init__.py +609 -0
  287. snappy/len_spec/geodesic_info.py +129 -0
  288. snappy/len_spec/geodesic_key_info_dict.py +116 -0
  289. snappy/len_spec/geodesic_piece.py +146 -0
  290. snappy/len_spec/geometric_structure.py +182 -0
  291. snappy/len_spec/geometry.py +136 -0
  292. snappy/len_spec/length_spectrum_geodesic_info.py +185 -0
  293. snappy/len_spec/spine.py +128 -0
  294. snappy/len_spec/test.py +24 -0
  295. snappy/len_spec/test_cases.py +69 -0
  296. snappy/len_spec/tile.py +276 -0
  297. snappy/len_spec/word.py +86 -0
  298. snappy/manifolds/HTWKnots/alternating.gz +0 -0
  299. snappy/manifolds/HTWKnots/nonalternating.gz +0 -0
  300. snappy/manifolds/__init__.py +3 -0
  301. snappy/margulis/__init__.py +332 -0
  302. snappy/margulis/cusp_neighborhood_neighborhood.py +66 -0
  303. snappy/margulis/geodesic_neighborhood.py +152 -0
  304. snappy/margulis/margulis_info.py +21 -0
  305. snappy/margulis/mu_from_neighborhood_pair.py +175 -0
  306. snappy/margulis/neighborhood.py +29 -0
  307. snappy/margulis/test.py +22 -0
  308. snappy/math_basics.py +187 -0
  309. snappy/matrix.py +525 -0
  310. snappy/number.py +657 -0
  311. snappy/numeric_output_checker.py +345 -0
  312. snappy/pari.py +41 -0
  313. snappy/phone_home.py +57 -0
  314. snappy/polyviewer.py +259 -0
  315. snappy/ptolemy/__init__.py +17 -0
  316. snappy/ptolemy/component.py +103 -0
  317. snappy/ptolemy/coordinates.py +2290 -0
  318. snappy/ptolemy/fieldExtensions.py +153 -0
  319. snappy/ptolemy/findLoops.py +473 -0
  320. snappy/ptolemy/geometricRep.py +59 -0
  321. snappy/ptolemy/homology.py +165 -0
  322. snappy/ptolemy/magma/default.magma_template +229 -0
  323. snappy/ptolemy/magma/radicalsOfPrimaryDecomposition.magma_template +79 -0
  324. snappy/ptolemy/manifoldMethods.py +395 -0
  325. snappy/ptolemy/matrix.py +350 -0
  326. snappy/ptolemy/numericalSolutionsToGroebnerBasis.py +113 -0
  327. snappy/ptolemy/polynomial.py +856 -0
  328. snappy/ptolemy/processComponents.py +173 -0
  329. snappy/ptolemy/processFileBase.py +247 -0
  330. snappy/ptolemy/processFileDispatch.py +46 -0
  331. snappy/ptolemy/processMagmaFile.py +392 -0
  332. snappy/ptolemy/processRurFile.py +150 -0
  333. snappy/ptolemy/ptolemyGeneralizedObstructionClass.py +102 -0
  334. snappy/ptolemy/ptolemyObstructionClass.py +64 -0
  335. snappy/ptolemy/ptolemyVariety.py +995 -0
  336. snappy/ptolemy/ptolemyVarietyPrimeIdealGroebnerBasis.py +140 -0
  337. snappy/ptolemy/reginaWrapper.py +698 -0
  338. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
  339. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
  340. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
  341. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
  342. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
  343. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
  344. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
  345. snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
  346. snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
  347. snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
  348. snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c0.magma_out.bz2 +0 -0
  349. snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
  350. snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
  351. snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c1.magma_out.bz2 +0 -0
  352. snappy/ptolemy/rur.py +545 -0
  353. snappy/ptolemy/solutionsToPrimeIdealGroebnerBasis.py +277 -0
  354. snappy/ptolemy/test.py +1126 -0
  355. snappy/ptolemy/testing_files/3_1__sl2_c0.magma_out.bz2 +0 -0
  356. snappy/ptolemy/testing_files/3_1__sl2_c1.magma_out.bz2 +0 -0
  357. snappy/ptolemy/testing_files/4_1__sl2_c0.magma_out.bz2 +0 -0
  358. snappy/ptolemy/testing_files/4_1__sl2_c1.magma_out.bz2 +0 -0
  359. snappy/ptolemy/testing_files/4_1__sl3_c0.magma_out.bz2 +0 -0
  360. snappy/ptolemy/testing_files/4_1__sl4_c0.magma_out.bz2 +0 -0
  361. snappy/ptolemy/testing_files/4_1__sl4_c1.magma_out.bz2 +0 -0
  362. snappy/ptolemy/testing_files/5_2__sl2_c0.magma_out.bz2 +0 -0
  363. snappy/ptolemy/testing_files/5_2__sl2_c1.magma_out.bz2 +0 -0
  364. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
  365. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
  366. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
  367. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
  368. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
  369. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
  370. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
  371. snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
  372. snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c0.magma_out +95 -0
  373. snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c1.magma_out +95 -0
  374. snappy/ptolemy/testing_files/m015__sl3_c0.magma_out.bz2 +0 -0
  375. snappy/ptolemy/testing_files/m135__sl2_c0.magma_out.bz2 +0 -0
  376. snappy/ptolemy/testing_files/m135__sl2_c1.magma_out.bz2 +0 -0
  377. snappy/ptolemy/testing_files/m135__sl2_c2.magma_out.bz2 +0 -0
  378. snappy/ptolemy/testing_files/m135__sl2_c3.magma_out.bz2 +0 -0
  379. snappy/ptolemy/testing_files/m135__sl2_c4.magma_out.bz2 +0 -0
  380. snappy/ptolemy/testing_files/m135__sl2_c5.magma_out.bz2 +0 -0
  381. snappy/ptolemy/testing_files/m135__sl2_c6.magma_out.bz2 +0 -0
  382. snappy/ptolemy/testing_files/m135__sl2_c7.magma_out.bz2 +0 -0
  383. snappy/ptolemy/testing_files/s000__sl2_c0.magma_out.bz2 +0 -0
  384. snappy/ptolemy/testing_files/s000__sl2_c1.magma_out.bz2 +0 -0
  385. snappy/ptolemy/testing_files/t00000__sl2_c0.magma_out.bz2 +0 -0
  386. snappy/ptolemy/testing_files/t00000__sl2_c1.magma_out.bz2 +0 -0
  387. snappy/ptolemy/testing_files/v0000__sl2_c0.magma_out.bz2 +0 -0
  388. snappy/ptolemy/testing_files/v0000__sl2_c1.magma_out.bz2 +0 -0
  389. snappy/ptolemy/testing_files/v0000__sl2_c2.magma_out.bz2 +0 -0
  390. snappy/ptolemy/testing_files/v0000__sl2_c3.magma_out.bz2 +0 -0
  391. snappy/ptolemy/testing_files_generalized/m003__sl2_c0.magma_out.bz2 +0 -0
  392. snappy/ptolemy/testing_files_generalized/m003__sl2_c1.magma_out.bz2 +0 -0
  393. snappy/ptolemy/testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
  394. snappy/ptolemy/testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
  395. snappy/ptolemy/testing_files_generalized/m004__sl2_c0.magma_out.bz2 +0 -0
  396. snappy/ptolemy/testing_files_generalized/m004__sl2_c1.magma_out.bz2 +0 -0
  397. snappy/ptolemy/testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
  398. snappy/ptolemy/testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
  399. snappy/ptolemy/testing_files_rur/m052__sl3_c0.rur.bz2 +0 -0
  400. snappy/ptolemy/utilities.py +236 -0
  401. snappy/raytracing/__init__.py +64 -0
  402. snappy/raytracing/additional_horospheres.py +64 -0
  403. snappy/raytracing/additional_len_spec_choices.py +63 -0
  404. snappy/raytracing/cohomology_fractal.py +197 -0
  405. snappy/raytracing/eyeball.py +124 -0
  406. snappy/raytracing/finite_raytracing_data.py +237 -0
  407. snappy/raytracing/finite_viewer.py +590 -0
  408. snappy/raytracing/geodesic_tube_info.py +174 -0
  409. snappy/raytracing/geodesics.py +246 -0
  410. snappy/raytracing/geodesics_window.py +258 -0
  411. snappy/raytracing/gui_utilities.py +293 -0
  412. snappy/raytracing/hyperboloid_navigation.py +556 -0
  413. snappy/raytracing/hyperboloid_utilities.py +234 -0
  414. snappy/raytracing/ideal_raytracing_data.py +592 -0
  415. snappy/raytracing/inside_viewer.py +974 -0
  416. snappy/raytracing/pack.py +22 -0
  417. snappy/raytracing/raytracing_data.py +126 -0
  418. snappy/raytracing/raytracing_view.py +454 -0
  419. snappy/raytracing/shaders/Eye.png +0 -0
  420. snappy/raytracing/shaders/NonGeometric.png +0 -0
  421. snappy/raytracing/shaders/__init__.py +101 -0
  422. snappy/raytracing/shaders/fragment.glsl +1744 -0
  423. snappy/raytracing/test.py +29 -0
  424. snappy/raytracing/tooltip.py +146 -0
  425. snappy/raytracing/upper_halfspace_utilities.py +98 -0
  426. snappy/raytracing/view_scale_controller.py +98 -0
  427. snappy/raytracing/zoom_slider/__init__.py +263 -0
  428. snappy/raytracing/zoom_slider/inward.png +0 -0
  429. snappy/raytracing/zoom_slider/inward18.png +0 -0
  430. snappy/raytracing/zoom_slider/outward.png +0 -0
  431. snappy/raytracing/zoom_slider/outward18.png +0 -0
  432. snappy/raytracing/zoom_slider/test.py +20 -0
  433. snappy/sage_helper.py +119 -0
  434. snappy/settings.py +407 -0
  435. snappy/shell.py +53 -0
  436. snappy/snap/__init__.py +117 -0
  437. snappy/snap/character_varieties.py +375 -0
  438. snappy/snap/find_field.py +372 -0
  439. snappy/snap/fox_milnor.py +271 -0
  440. snappy/snap/fundamental_polyhedron.py +569 -0
  441. snappy/snap/generators.py +39 -0
  442. snappy/snap/interval_reps.py +81 -0
  443. snappy/snap/kernel_structures.py +128 -0
  444. snappy/snap/mcomplex_base.py +18 -0
  445. snappy/snap/nsagetools.py +716 -0
  446. snappy/snap/peripheral/__init__.py +1 -0
  447. snappy/snap/peripheral/dual_cellulation.py +219 -0
  448. snappy/snap/peripheral/link.py +127 -0
  449. snappy/snap/peripheral/peripheral.py +159 -0
  450. snappy/snap/peripheral/surface.py +522 -0
  451. snappy/snap/peripheral/test.py +35 -0
  452. snappy/snap/polished_reps.py +335 -0
  453. snappy/snap/shapes.py +152 -0
  454. snappy/snap/slice_obs_HKL/__init__.py +194 -0
  455. snappy/snap/slice_obs_HKL/basics.py +236 -0
  456. snappy/snap/slice_obs_HKL/direct.py +217 -0
  457. snappy/snap/slice_obs_HKL/poly_norm.py +212 -0
  458. snappy/snap/slice_obs_HKL/rep_theory.py +424 -0
  459. snappy/snap/t3mlite/__init__.py +2 -0
  460. snappy/snap/t3mlite/arrow.py +243 -0
  461. snappy/snap/t3mlite/corner.py +22 -0
  462. snappy/snap/t3mlite/edge.py +172 -0
  463. snappy/snap/t3mlite/face.py +37 -0
  464. snappy/snap/t3mlite/files.py +211 -0
  465. snappy/snap/t3mlite/homology.py +53 -0
  466. snappy/snap/t3mlite/linalg.py +419 -0
  467. snappy/snap/t3mlite/mcomplex.py +1499 -0
  468. snappy/snap/t3mlite/perm4.py +320 -0
  469. snappy/snap/t3mlite/setup.py +12 -0
  470. snappy/snap/t3mlite/simplex.py +199 -0
  471. snappy/snap/t3mlite/spun.py +297 -0
  472. snappy/snap/t3mlite/surface.py +519 -0
  473. snappy/snap/t3mlite/test.py +20 -0
  474. snappy/snap/t3mlite/test_vs_regina.py +86 -0
  475. snappy/snap/t3mlite/tetrahedron.py +109 -0
  476. snappy/snap/t3mlite/vertex.py +42 -0
  477. snappy/snap/test.py +139 -0
  478. snappy/snap/utilities.py +288 -0
  479. snappy/test.py +213 -0
  480. snappy/test_cases.py +263 -0
  481. snappy/testing.py +131 -0
  482. snappy/tiling/__init__.py +2 -0
  483. snappy/tiling/dict_based_set.py +79 -0
  484. snappy/tiling/floor.py +49 -0
  485. snappy/tiling/hyperboloid_dict.py +54 -0
  486. snappy/tiling/iter_utils.py +78 -0
  487. snappy/tiling/lifted_tetrahedron.py +22 -0
  488. snappy/tiling/lifted_tetrahedron_set.py +54 -0
  489. snappy/tiling/quotient_dict.py +70 -0
  490. snappy/tiling/real_hash_dict.py +164 -0
  491. snappy/tiling/test.py +23 -0
  492. snappy/tiling/tile.py +224 -0
  493. snappy/tiling/triangle.py +33 -0
  494. snappy/tkterminal.py +920 -0
  495. snappy/twister/__init__.py +20 -0
  496. snappy/twister/main.py +646 -0
  497. snappy/twister/surfaces/S_0_1 +3 -0
  498. snappy/twister/surfaces/S_0_2 +3 -0
  499. snappy/twister/surfaces/S_0_4 +7 -0
  500. snappy/twister/surfaces/S_0_4_Lantern +8 -0
  501. snappy/twister/surfaces/S_1 +3 -0
  502. snappy/twister/surfaces/S_1_1 +4 -0
  503. snappy/twister/surfaces/S_1_2 +5 -0
  504. snappy/twister/surfaces/S_1_2_5 +6 -0
  505. snappy/twister/surfaces/S_2 +6 -0
  506. snappy/twister/surfaces/S_2_1 +8 -0
  507. snappy/twister/surfaces/S_2_heeg +10 -0
  508. snappy/twister/surfaces/S_3 +8 -0
  509. snappy/twister/surfaces/S_3_1 +10 -0
  510. snappy/twister/surfaces/S_4_1 +12 -0
  511. snappy/twister/surfaces/S_5_1 +14 -0
  512. snappy/twister/surfaces/heeg_fig8 +9 -0
  513. snappy/twister/twister_core.cpython-310-aarch64-linux-gnu.so +0 -0
  514. snappy/upper_halfspace/__init__.py +146 -0
  515. snappy/upper_halfspace/ideal_point.py +29 -0
  516. snappy/verify/__init__.py +13 -0
  517. snappy/verify/canonical.py +542 -0
  518. snappy/verify/complex_volume/__init__.py +18 -0
  519. snappy/verify/complex_volume/adjust_torsion.py +86 -0
  520. snappy/verify/complex_volume/closed.py +168 -0
  521. snappy/verify/complex_volume/compute_ptolemys.py +90 -0
  522. snappy/verify/complex_volume/cusped.py +56 -0
  523. snappy/verify/complex_volume/extended_bloch.py +201 -0
  524. snappy/verify/cusp_translations.py +85 -0
  525. snappy/verify/edge_equations.py +80 -0
  526. snappy/verify/exceptions.py +254 -0
  527. snappy/verify/hyperbolicity.py +224 -0
  528. snappy/verify/interval_newton_shapes_engine.py +523 -0
  529. snappy/verify/interval_tree.py +400 -0
  530. snappy/verify/krawczyk_shapes_engine.py +518 -0
  531. snappy/verify/real_algebra.py +286 -0
  532. snappy/verify/shapes.py +25 -0
  533. snappy/verify/square_extensions.py +1005 -0
  534. snappy/verify/test.py +72 -0
  535. snappy/verify/volume.py +128 -0
  536. snappy/version.py +2 -0
  537. snappy-3.3.dist-info/METADATA +58 -0
  538. snappy-3.3.dist-info/RECORD +541 -0
  539. snappy-3.3.dist-info/WHEEL +6 -0
  540. snappy-3.3.dist-info/entry_points.txt +2 -0
  541. snappy-3.3.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)))