snappy 3.1.1__cp38-cp38-win_amd64.whl → 3.2__cp38-cp38-win_amd64.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.
- snappy/CyOpenGL.cp38-win_amd64.pyd +0 -0
- snappy/SnapPy.cp38-win_amd64.pyd +0 -0
- snappy/SnapPyHP.cp38-win_amd64.pyd +0 -0
- snappy/__init__.py +299 -402
- snappy/app.py +70 -20
- snappy/browser.py +18 -17
- snappy/canonical.py +249 -0
- snappy/{verify/cusp_shapes.py → cusps/__init__.py} +8 -18
- snappy/cusps/cusp_area_matrix.py +101 -0
- snappy/{verify/cusp_areas.py → cusps/cusp_areas_from_matrix.py} +23 -39
- snappy/cusps/maximal_cusp_area_matrix.py +136 -0
- snappy/cusps/test.py +21 -0
- snappy/cusps/trig_cusp_area_matrix.py +63 -0
- snappy/database.py +10 -9
- snappy/decorated_isosig.py +337 -114
- snappy/dev/extended_ptolemy/complexVolumesClosed.py +40 -7
- snappy/dev/extended_ptolemy/extended.py +3 -3
- snappy/dev/extended_ptolemy/phc_wrapper.py +10 -10
- snappy/dev/vericlosed/oneVertexTruncatedComplex.py +1 -1
- snappy/doc/_images/m004_paper_plane_on_systole.jpg +0 -0
- snappy/doc/_images/m125_paper_plane.jpg +0 -0
- snappy/doc/_images/o9_00000_systole_paper_plane.jpg +0 -0
- snappy/doc/_images/o9_00000_systole_paper_plane_closer.jpg +0 -0
- snappy/doc/_sources/additional_classes.rst.txt +40 -40
- snappy/doc/_sources/bugs.rst.txt +14 -14
- snappy/doc/_sources/censuses.rst.txt +51 -51
- snappy/doc/_sources/credits.rst.txt +75 -75
- snappy/doc/_sources/development.rst.txt +259 -239
- snappy/doc/_sources/index.rst.txt +182 -115
- snappy/doc/_sources/installing.rst.txt +247 -264
- snappy/doc/_sources/manifold.rst.txt +6 -6
- snappy/doc/_sources/manifoldhp.rst.txt +46 -46
- snappy/doc/_sources/news.rst.txt +355 -283
- snappy/doc/_sources/other.rst.txt +25 -25
- snappy/doc/_sources/platonic_census.rst.txt +20 -20
- snappy/doc/_sources/plink.rst.txt +102 -102
- snappy/doc/_sources/ptolemy.rst.txt +66 -66
- snappy/doc/_sources/ptolemy_classes.rst.txt +42 -42
- snappy/doc/_sources/ptolemy_examples1.rst.txt +298 -297
- snappy/doc/_sources/ptolemy_examples2.rst.txt +363 -363
- snappy/doc/_sources/ptolemy_examples3.rst.txt +301 -301
- snappy/doc/_sources/ptolemy_examples4.rst.txt +61 -61
- snappy/doc/_sources/ptolemy_prelim.rst.txt +105 -105
- snappy/doc/_sources/screenshots.rst.txt +21 -21
- snappy/doc/_sources/snap.rst.txt +87 -87
- snappy/doc/_sources/snappy.rst.txt +28 -28
- snappy/doc/_sources/spherogram.rst.txt +103 -103
- snappy/doc/_sources/todo.rst.txt +47 -47
- snappy/doc/_sources/triangulation.rst.txt +11 -11
- snappy/doc/_sources/tutorial.rst.txt +49 -49
- snappy/doc/_sources/verify.rst.txt +210 -150
- snappy/doc/_sources/verify_internals.rst.txt +79 -90
- snappy/doc/_static/basic.css +924 -902
- snappy/doc/_static/css/badge_only.css +1 -1
- snappy/doc/_static/css/theme.css +1 -1
- snappy/doc/_static/doctools.js +1 -1
- snappy/doc/_static/documentation_options.js +12 -13
- snappy/doc/_static/fonts/Lato/lato-bold.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-bold.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-italic.woff2 +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.eot +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.ttf +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.woff +0 -0
- snappy/doc/_static/fonts/Lato/lato-regular.woff2 +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
- snappy/doc/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
- snappy/doc/_static/js/versions.js +228 -0
- snappy/doc/_static/language_data.js +199 -199
- snappy/doc/_static/pygments.css +74 -73
- snappy/doc/_static/searchtools.js +125 -71
- snappy/doc/_static/snappy_furo.css +33 -33
- snappy/doc/_static/snappy_sphinx_rtd_theme.css +42 -42
- snappy/doc/_static/sphinx_highlight.js +13 -3
- snappy/doc/additional_classes.html +1499 -1330
- snappy/doc/bugs.html +131 -134
- snappy/doc/censuses.html +426 -445
- snappy/doc/credits.html +180 -183
- snappy/doc/development.html +383 -363
- snappy/doc/genindex.html +1330 -1409
- snappy/doc/index.html +261 -206
- snappy/doc/installing.html +345 -363
- snappy/doc/manifold.html +3451 -2839
- snappy/doc/manifoldhp.html +179 -182
- snappy/doc/news.html +387 -329
- snappy/doc/objects.inv +0 -0
- snappy/doc/other.html +160 -162
- snappy/doc/platonic_census.html +374 -377
- snappy/doc/plink.html +209 -212
- snappy/doc/ptolemy.html +253 -255
- snappy/doc/ptolemy_classes.html +1143 -1146
- snappy/doc/ptolemy_examples1.html +408 -410
- snappy/doc/ptolemy_examples2.html +470 -473
- snappy/doc/ptolemy_examples3.html +413 -416
- snappy/doc/ptolemy_examples4.html +194 -197
- snappy/doc/ptolemy_prelim.html +247 -250
- snappy/doc/py-modindex.html +164 -167
- snappy/doc/screenshots.html +140 -142
- snappy/doc/search.html +134 -137
- snappy/doc/searchindex.js +1 -1
- snappy/doc/snap.html +201 -204
- snappy/doc/snappy.html +180 -182
- snappy/doc/spherogram.html +1210 -1213
- snappy/doc/todo.html +165 -168
- snappy/doc/triangulation.html +1583 -1474
- snappy/doc/tutorial.html +158 -161
- snappy/doc/verify.html +329 -275
- snappy/doc/verify_internals.html +1234 -1691
- snappy/drilling/__init__.py +153 -235
- snappy/drilling/barycentric.py +103 -0
- snappy/drilling/constants.py +0 -2
- snappy/drilling/crush.py +56 -130
- snappy/drilling/cusps.py +12 -6
- snappy/drilling/debug.py +2 -1
- snappy/drilling/exceptions.py +7 -40
- snappy/drilling/moves.py +302 -243
- snappy/drilling/perturb.py +63 -37
- snappy/drilling/shorten.py +36 -0
- snappy/drilling/subdivide.py +0 -5
- snappy/drilling/test.py +23 -0
- snappy/drilling/test_cases.py +126 -0
- snappy/drilling/tracing.py +9 -37
- snappy/exceptions.py +18 -5
- snappy/exterior_to_link/barycentric_geometry.py +2 -4
- snappy/exterior_to_link/main.py +8 -7
- snappy/exterior_to_link/mcomplex_with_link.py +2 -2
- snappy/exterior_to_link/rational_linear_algebra.py +1 -1
- snappy/exterior_to_link/rational_linear_algebra_wrapped.py +1 -1
- snappy/exterior_to_link/test.py +21 -33
- snappy/geometric_structure/__init__.py +212 -0
- snappy/geometric_structure/cusp_neighborhood/__init__.py +3 -0
- snappy/geometric_structure/cusp_neighborhood/complex_cusp_cross_section.py +697 -0
- snappy/geometric_structure/cusp_neighborhood/cusp_cross_section_base.py +484 -0
- snappy/geometric_structure/cusp_neighborhood/exceptions.py +42 -0
- snappy/geometric_structure/cusp_neighborhood/real_cusp_cross_section.py +298 -0
- snappy/geometric_structure/cusp_neighborhood/tiles_for_cusp_neighborhood.py +159 -0
- snappy/geometric_structure/cusp_neighborhood/vertices.py +32 -0
- snappy/geometric_structure/geodesic/__init__.py +0 -0
- snappy/geometric_structure/geodesic/add_core_curves.py +152 -0
- snappy/geometric_structure/geodesic/avoid_core_curves.py +369 -0
- snappy/geometric_structure/geodesic/canonical_keys.py +52 -0
- snappy/geometric_structure/geodesic/check_away_from_core_curve.py +60 -0
- snappy/geometric_structure/geodesic/constants.py +6 -0
- snappy/geometric_structure/geodesic/exceptions.py +22 -0
- snappy/{drilling → geometric_structure/geodesic}/fixed_points.py +34 -9
- snappy/{drilling/geodesic_info.py → geometric_structure/geodesic/geodesic_start_point_info.py} +139 -180
- snappy/geometric_structure/geodesic/graph_trace_helper.py +67 -0
- snappy/geometric_structure/geodesic/line.py +30 -0
- snappy/geometric_structure/geodesic/multiplicity.py +127 -0
- snappy/geometric_structure/geodesic/tiles_for_geodesic.py +101 -0
- snappy/geometric_structure/test.py +22 -0
- snappy/gui.py +23 -13
- snappy/horoviewer.py +7 -7
- snappy/hyperboloid/__init__.py +96 -31
- snappy/hyperboloid/distances.py +245 -0
- snappy/hyperboloid/horoball.py +19 -0
- snappy/hyperboloid/line.py +35 -0
- snappy/hyperboloid/point.py +9 -0
- snappy/hyperboloid/triangle.py +29 -0
- snappy/isometry_signature.py +382 -0
- snappy/len_spec/__init__.py +596 -0
- snappy/len_spec/geodesic_info.py +110 -0
- snappy/len_spec/geodesic_key_info_dict.py +117 -0
- snappy/len_spec/geodesic_piece.py +143 -0
- snappy/len_spec/geometric_structure.py +182 -0
- snappy/len_spec/geometry.py +80 -0
- snappy/len_spec/length_spectrum_geodesic_info.py +170 -0
- snappy/len_spec/spine.py +206 -0
- snappy/len_spec/test.py +24 -0
- snappy/len_spec/test_cases.py +69 -0
- snappy/len_spec/tile.py +275 -0
- snappy/len_spec/word.py +86 -0
- snappy/math_basics.py +39 -13
- snappy/matrix.py +52 -9
- snappy/number.py +12 -6
- snappy/numeric_output_checker.py +2 -3
- snappy/pari.py +8 -4
- snappy/phone_home.py +2 -1
- snappy/polyviewer.py +8 -8
- snappy/ptolemy/__init__.py +1 -1
- snappy/ptolemy/component.py +2 -2
- snappy/ptolemy/coordinates.py +25 -25
- snappy/ptolemy/findLoops.py +9 -9
- snappy/ptolemy/manifoldMethods.py +27 -29
- snappy/ptolemy/polynomial.py +50 -57
- snappy/ptolemy/processFileBase.py +60 -0
- snappy/ptolemy/ptolemyVariety.py +109 -41
- snappy/ptolemy/reginaWrapper.py +4 -4
- snappy/ptolemy/rur.py +1 -1
- snappy/ptolemy/solutionsToPrimeIdealGroebnerBasis.py +9 -9
- snappy/ptolemy/test.py +99 -54
- snappy/ptolemy/utilities.py +1 -1
- snappy/raytracing/__init__.py +64 -0
- snappy/raytracing/additional_horospheres.py +64 -0
- snappy/raytracing/additional_len_spec_choices.py +63 -0
- snappy/raytracing/cohomology_fractal.py +0 -3
- snappy/raytracing/eyeball.py +123 -0
- snappy/raytracing/finite_raytracing_data.py +17 -17
- snappy/raytracing/finite_viewer.py +15 -15
- snappy/raytracing/geodesic_tube_info.py +93 -63
- snappy/raytracing/geodesics.py +94 -64
- snappy/raytracing/geodesics_window.py +56 -34
- snappy/raytracing/gui_utilities.py +21 -6
- snappy/raytracing/hyperboloid_navigation.py +29 -4
- snappy/raytracing/hyperboloid_utilities.py +73 -73
- snappy/raytracing/ideal_raytracing_data.py +121 -91
- snappy/raytracing/inside_viewer.py +199 -66
- snappy/raytracing/pack.py +22 -0
- snappy/raytracing/raytracing_data.py +37 -25
- snappy/raytracing/raytracing_view.py +70 -65
- snappy/raytracing/shaders/Eye.png +0 -0
- snappy/raytracing/shaders/NonGeometric.png +0 -0
- snappy/raytracing/shaders/__init__.py +39 -3
- snappy/raytracing/shaders/fragment.glsl +451 -133
- snappy/raytracing/test.py +29 -0
- snappy/raytracing/tooltip.py +146 -0
- snappy/raytracing/upper_halfspace_utilities.py +42 -9
- snappy/sage_helper.py +67 -134
- snappy/settings.py +90 -77
- snappy/shell.py +2 -0
- snappy/snap/character_varieties.py +2 -2
- snappy/snap/find_field.py +4 -3
- snappy/snap/fundamental_polyhedron.py +2 -2
- snappy/snap/kernel_structures.py +5 -1
- snappy/snap/nsagetools.py +9 -8
- snappy/snap/peripheral/dual_cellulation.py +4 -3
- snappy/snap/peripheral/peripheral.py +2 -2
- snappy/snap/peripheral/surface.py +5 -5
- snappy/snap/peripheral/test.py +1 -1
- snappy/snap/polished_reps.py +8 -8
- snappy/snap/slice_obs_HKL.py +16 -14
- snappy/snap/t3mlite/arrow.py +3 -3
- snappy/snap/t3mlite/edge.py +3 -3
- snappy/snap/t3mlite/homology.py +2 -2
- snappy/snap/t3mlite/mcomplex.py +3 -3
- snappy/snap/t3mlite/simplex.py +12 -0
- snappy/snap/t3mlite/spun.py +18 -17
- snappy/snap/t3mlite/test_vs_regina.py +4 -4
- snappy/snap/test.py +37 -53
- snappy/snap/utilities.py +4 -5
- snappy/test.py +121 -138
- snappy/test_cases.py +263 -0
- snappy/testing.py +131 -0
- snappy/tiling/__init__.py +2 -0
- snappy/tiling/canonical_key_dict.py +59 -0
- snappy/tiling/dict_based_set.py +79 -0
- snappy/tiling/floor.py +49 -0
- snappy/tiling/hyperboloid_dict.py +54 -0
- snappy/tiling/iter_utils.py +78 -0
- snappy/tiling/lifted_tetrahedron.py +22 -0
- snappy/tiling/lifted_tetrahedron_set.py +54 -0
- snappy/tiling/real_hash_dict.py +164 -0
- snappy/tiling/test.py +23 -0
- snappy/tiling/tile.py +215 -0
- snappy/tiling/triangle.py +33 -0
- snappy/tkterminal.py +113 -84
- snappy/twister/main.py +1 -7
- snappy/twister/twister_core.cp38-win_amd64.pyd +0 -0
- snappy/upper_halfspace/__init__.py +78 -17
- snappy/verify/__init__.py +3 -7
- snappy/verify/{verifyCanonical.py → canonical.py} +78 -70
- snappy/verify/complex_volume/adjust_torsion.py +1 -2
- snappy/verify/complex_volume/closed.py +13 -13
- snappy/verify/complex_volume/cusped.py +6 -6
- snappy/verify/complex_volume/extended_bloch.py +5 -8
- snappy/verify/{cuspTranslations.py → cusp_translations.py} +1 -1
- snappy/verify/edge_equations.py +80 -0
- snappy/verify/exceptions.py +0 -55
- snappy/verify/{verifyHyperbolicity.py → hyperbolicity.py} +3 -3
- snappy/verify/interval_newton_shapes_engine.py +7 -5
- snappy/verify/interval_tree.py +5 -5
- snappy/verify/krawczyk_shapes_engine.py +17 -18
- snappy/verify/maximal_cusp_area_matrix/__init__.py +7 -74
- snappy/verify/maximal_cusp_area_matrix/cusp_tiling_engine.py +3 -4
- snappy/verify/maximal_cusp_area_matrix/cusp_translate_engine.py +1 -1
- snappy/verify/{realAlgebra.py → real_algebra.py} +1 -1
- snappy/verify/shapes.py +5 -3
- snappy/verify/short_slopes.py +39 -41
- snappy/verify/{squareExtensions.py → square_extensions.py} +14 -11
- snappy/verify/test.py +57 -60
- snappy/verify/upper_halfspace/extended_matrix.py +1 -1
- snappy/verify/upper_halfspace/finite_point.py +3 -4
- snappy/verify/upper_halfspace/ideal_point.py +9 -9
- snappy/verify/volume.py +2 -2
- snappy/version.py +2 -2
- {snappy-3.1.1.dist-info → snappy-3.2.dist-info}/METADATA +14 -10
- snappy-3.2.dist-info/RECORD +503 -0
- {snappy-3.1.1.dist-info → snappy-3.2.dist-info}/WHEEL +1 -1
- {snappy-3.1.1.dist-info → snappy-3.2.dist-info}/top_level.txt +6 -1
- snappy/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/__pycache__/browser.cpython-38.pyc +0 -0
- snappy/__pycache__/cache.cpython-38.pyc +0 -0
- snappy/__pycache__/database.cpython-38.pyc +0 -0
- snappy/__pycache__/db_utilities.cpython-38.pyc +0 -0
- snappy/__pycache__/decorated_isosig.cpython-38.pyc +0 -0
- snappy/__pycache__/exceptions.cpython-38.pyc +0 -0
- snappy/__pycache__/export_stl.cpython-38.pyc +0 -0
- snappy/__pycache__/filedialog.cpython-38.pyc +0 -0
- snappy/__pycache__/gui.cpython-38.pyc +0 -0
- snappy/__pycache__/horoviewer.cpython-38.pyc +0 -0
- snappy/__pycache__/math_basics.cpython-38.pyc +0 -0
- snappy/__pycache__/matrix.cpython-38.pyc +0 -0
- snappy/__pycache__/number.cpython-38.pyc +0 -0
- snappy/__pycache__/numeric_output_checker.cpython-38.pyc +0 -0
- snappy/__pycache__/pari.cpython-38.pyc +0 -0
- snappy/__pycache__/polyviewer.cpython-38.pyc +0 -0
- snappy/__pycache__/sage_helper.cpython-38.pyc +0 -0
- snappy/__pycache__/version.cpython-38.pyc +0 -0
- snappy/doc/_sources/verify_canon.rst.txt +0 -90
- snappy/doc/_static/jquery-3.6.0.js +0 -10881
- snappy/doc/_static/js/html5shiv-printshiv.min.js +0 -4
- snappy/doc/_static/js/html5shiv.min.js +0 -4
- snappy/doc/_static/underscore-1.13.1.js +0 -2042
- snappy/doc/_static/underscore.js +0 -6
- snappy/doc/verify_canon.html +0 -304
- snappy/drilling/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/constants.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/crush.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/cusps.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/debug.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/epsilons.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/exceptions.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/fixed_points.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/geodesic_info.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/geodesic_tube.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/geometric_structure.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/line.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/moves.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/peripheral_curves.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/perturb.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/quotient_space.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/spatial_dict.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/subdivide.cpython-38.pyc +0 -0
- snappy/drilling/__pycache__/tracing.cpython-38.pyc +0 -0
- snappy/drilling/geodesic_tube.py +0 -441
- snappy/drilling/geometric_structure.py +0 -366
- snappy/drilling/line.py +0 -122
- snappy/drilling/quotient_space.py +0 -94
- snappy/drilling/spatial_dict.py +0 -128
- snappy/exterior_to_link/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/barycentric_geometry.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/exceptions.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/hyp_utils.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/link_projection.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/main.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/mcomplex_with_expansion.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/mcomplex_with_link.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/mcomplex_with_memory.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/pl_utils.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/put_in_S3.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/rational_linear_algebra.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/simplify_to_base_tri.cpython-38.pyc +0 -0
- snappy/exterior_to_link/__pycache__/stored_moves.cpython-38.pyc +0 -0
- snappy/hyperboloid/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/manifolds/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/component.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/coordinates.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/fieldExtensions.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/findLoops.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/homology.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/manifoldMethods.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/matrix.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/numericalSolutionsToGroebnerBasis.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/polynomial.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/processComponents.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/processFileBase.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/processFileDispatch.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/processMagmaFile.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/processRurFile.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/ptolemyGeneralizedObstructionClass.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/ptolemyObstructionClass.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/ptolemyVariety.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/ptolemyVarietyPrimeIdealGroebnerBasis.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/rur.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/solutionsToPrimeIdealGroebnerBasis.cpython-38.pyc +0 -0
- snappy/ptolemy/__pycache__/utilities.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/character_varieties.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/fundamental_polyhedron.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/interval_reps.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/kernel_structures.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/mcomplex_base.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/nsagetools.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/polished_reps.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/shapes.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/slice_obs_HKL.cpython-38.pyc +0 -0
- snappy/snap/__pycache__/utilities.cpython-38.pyc +0 -0
- snappy/snap/peripheral/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/snap/peripheral/__pycache__/dual_cellulation.cpython-38.pyc +0 -0
- snappy/snap/peripheral/__pycache__/link.cpython-38.pyc +0 -0
- snappy/snap/peripheral/__pycache__/peripheral.cpython-38.pyc +0 -0
- snappy/snap/peripheral/__pycache__/surface.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/arrow.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/corner.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/edge.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/face.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/files.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/homology.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/linalg.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/mcomplex.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/perm4.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/simplex.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/spun.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/surface.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/tetrahedron.cpython-38.pyc +0 -0
- snappy/snap/t3mlite/__pycache__/vertex.cpython-38.pyc +0 -0
- snappy/togl/__init__.py +0 -3
- snappy/togl/darwin-tk8.6/Togl2.1/LICENSE +0 -28
- snappy/togl/darwin-tk8.6/Togl2.1/libTogl2.1.dylib +0 -0
- snappy/togl/darwin-tk8.6/Togl2.1/pkgIndex.tcl +0 -5
- snappy/togl/darwin-tk8.7/Togl2.1/LICENSE +0 -28
- snappy/togl/darwin-tk8.7/Togl2.1/libTogl2.1.dylib +0 -0
- snappy/togl/darwin-tk8.7/Togl2.1/pkgIndex.tcl +0 -5
- snappy/togl/linux2-x86_64-tk8.6/Togl2.1/LICENSE +0 -28
- snappy/togl/linux2-x86_64-tk8.6/Togl2.1/libTogl2.1.so +0 -0
- snappy/togl/linux2-x86_64-tk8.6/Togl2.1/pkgIndex.tcl +0 -5
- snappy/togl/win32VC-tk8.6/Togl2.1/LICENSE +0 -28
- snappy/togl/win32VC-tk8.6/Togl2.1/Togl21.dll +0 -0
- snappy/togl/win32VC-tk8.6/Togl2.1/Togl21.lib +0 -0
- snappy/togl/win32VC-tk8.6/Togl2.1/pkgIndex.tcl +0 -6
- snappy/togl/win32VC-x86_64-tk8.6/Togl2.1/LICENSE +0 -28
- snappy/togl/win32VC-x86_64-tk8.6/Togl2.1/Togl21.dll +0 -0
- snappy/togl/win32VC-x86_64-tk8.6/Togl2.1/Togl21.lib +0 -0
- snappy/togl/win32VC-x86_64-tk8.6/Togl2.1/pkgIndex.tcl +0 -6
- snappy/twister/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/twister/__pycache__/main.cpython-38.pyc +0 -0
- snappy/upper_halfspace/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/upper_halfspace/__pycache__/ideal_point.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/cuspCrossSection.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/cuspTranslations.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/cusp_areas.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/cusp_shapes.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/exceptions.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/interval_newton_shapes_engine.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/interval_tree.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/krawczyk_shapes_engine.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/realAlgebra.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/shapes.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/short_slopes.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/squareExtensions.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/verifyCanonical.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/verifyHyperbolicity.cpython-38.pyc +0 -0
- snappy/verify/__pycache__/volume.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/adjust_torsion.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/closed.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/compute_ptolemys.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/cusped.cpython-38.pyc +0 -0
- snappy/verify/complex_volume/__pycache__/extended_bloch.cpython-38.pyc +0 -0
- snappy/verify/cuspCrossSection.py +0 -1422
- snappy/verify/maximal_cusp_area_matrix/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/verify/maximal_cusp_area_matrix/__pycache__/cusp_tiling_engine.cpython-38.pyc +0 -0
- snappy/verify/maximal_cusp_area_matrix/__pycache__/cusp_translate_engine.cpython-38.pyc +0 -0
- snappy/verify/upper_halfspace/__pycache__/__init__.cpython-38.pyc +0 -0
- snappy/verify/upper_halfspace/__pycache__/extended_matrix.cpython-38.pyc +0 -0
- snappy/verify/upper_halfspace/__pycache__/finite_point.cpython-38.pyc +0 -0
- snappy/verify/upper_halfspace/__pycache__/ideal_point.cpython-38.pyc +0 -0
- snappy-3.1.1.dist-info/RECORD +0 -575
- {snappy-3.1.1.dist-info → snappy-3.2.dist-info}/entry_points.txt +0 -0
@@ -1,1422 +0,0 @@
|
|
1
|
-
# Original source:
|
2
|
-
# Asymmetric hyperbolic L-spaces, Heegaard genus, and Dehn filling
|
3
|
-
# Nathan M. Dunfield, Neil R. Hoffman, Joan E. Licata
|
4
|
-
# http://arxiv.org/abs/1407.7827
|
5
|
-
# This code is copyrighted by Nathan Dunfield, Neil Hoffman, and Joan Licata
|
6
|
-
# and released under the GNU GPL version 2 or (at your option) any later
|
7
|
-
# version.
|
8
|
-
#
|
9
|
-
# 02/22/15 Major rewrite and checked into SnapPy repository:
|
10
|
-
# handle any number of cusps,
|
11
|
-
# agnostic of type of numbers for shape,
|
12
|
-
# support non-orientable manifolds,
|
13
|
-
# refactoring and cleanup
|
14
|
-
# - Matthias Goerner
|
15
|
-
#
|
16
|
-
# 01/15/16 Split CuspCrossSectionClass into a base class and
|
17
|
-
# two subclasses for computing real and
|
18
|
-
# complex edge lengths. Added methods to ensure a cusp
|
19
|
-
# neighborhood is disjoint and methods to compute the
|
20
|
-
# complex edge length.
|
21
|
-
#
|
22
|
-
# 01/28/18 Fix an important bug: do not use built-in min for intervals.
|
23
|
-
|
24
|
-
from ..sage_helper import _within_sage
|
25
|
-
|
26
|
-
import math
|
27
|
-
|
28
|
-
if _within_sage:
|
29
|
-
# python's log and sqrt only work for floats
|
30
|
-
# They would fail or convert to float losing precision
|
31
|
-
from sage.functions.log import log
|
32
|
-
from sage.functions.other import sqrt
|
33
|
-
else:
|
34
|
-
# Otherwise, define our own log and sqrt which checks whether
|
35
|
-
# the given type defines a log/sqrt method and fallsback
|
36
|
-
# to python's log and sqrt which has the above drawback of
|
37
|
-
# potentially losing precision.
|
38
|
-
import cmath
|
39
|
-
|
40
|
-
def log(x):
|
41
|
-
if hasattr(x, 'log'):
|
42
|
-
return x.log()
|
43
|
-
return cmath.log(x)
|
44
|
-
|
45
|
-
def sqrt(x):
|
46
|
-
if hasattr(x, 'sqrt'):
|
47
|
-
return x.sqrt()
|
48
|
-
return math.sqrt(x)
|
49
|
-
|
50
|
-
from ..snap import t3mlite as t3m
|
51
|
-
from ..snap.kernel_structures import *
|
52
|
-
from ..snap.mcomplex_base import *
|
53
|
-
|
54
|
-
from ..math_basics import correct_min
|
55
|
-
from .exceptions import *
|
56
|
-
|
57
|
-
__all__ = [
|
58
|
-
'IncompleteCuspError',
|
59
|
-
'RealCuspCrossSection',
|
60
|
-
'ComplexCuspCrossSection']
|
61
|
-
|
62
|
-
|
63
|
-
class IncompleteCuspError(RuntimeError):
|
64
|
-
"""
|
65
|
-
Exception raised when trying to construct a CuspCrossSection
|
66
|
-
from a Manifold with Dehn-fillings.
|
67
|
-
"""
|
68
|
-
def __init__(self, manifold):
|
69
|
-
self.manifold = manifold
|
70
|
-
|
71
|
-
def __str__(self):
|
72
|
-
return (('Cannot construct CuspCrossSection from manifold with '
|
73
|
-
'Dehn-fillings: %s') % self.manifold)
|
74
|
-
|
75
|
-
|
76
|
-
class HoroTriangleBase:
|
77
|
-
@staticmethod
|
78
|
-
def _make_second(sides, x):
|
79
|
-
"""
|
80
|
-
Cyclically rotate sides = (a,b,c) so that x is the second entry"
|
81
|
-
"""
|
82
|
-
i = (sides.index(x) + 2) % len(sides)
|
83
|
-
return sides[i:]+sides[:i]
|
84
|
-
|
85
|
-
@staticmethod
|
86
|
-
def _sides_and_cross_ratios(tet, vertex, side):
|
87
|
-
sides = t3m.simplex.FacesAroundVertexCounterclockwise[vertex]
|
88
|
-
left_side, center_side, right_side = (
|
89
|
-
HoroTriangleBase._make_second(sides, side))
|
90
|
-
z_left = tet.ShapeParameters[left_side & center_side ]
|
91
|
-
z_right = tet.ShapeParameters[center_side & right_side ]
|
92
|
-
return left_side, center_side, right_side, z_left, z_right
|
93
|
-
|
94
|
-
|
95
|
-
class RealHoroTriangle:
|
96
|
-
"""
|
97
|
-
A horosphere cross section in the corner of an ideal tetrahedron.
|
98
|
-
The sides of the triangle correspond to faces of the tetrahedron.
|
99
|
-
The lengths stored for the triangle are real.
|
100
|
-
"""
|
101
|
-
def __init__(self, tet, vertex, known_side, length_of_side):
|
102
|
-
left_side, center_side, right_side, z_left, z_right = (
|
103
|
-
HoroTriangleBase._sides_and_cross_ratios(tet, vertex, known_side))
|
104
|
-
|
105
|
-
L = length_of_side
|
106
|
-
self.lengths = { center_side : L,
|
107
|
-
left_side : abs(z_left) * L,
|
108
|
-
right_side : L / abs(z_right) }
|
109
|
-
a, b, c = self.lengths.values()
|
110
|
-
self.area = L * L * z_left.imag() / 2
|
111
|
-
|
112
|
-
# Below is the usual formula for circumradius
|
113
|
-
self.circumradius = a * b * c / (4 * self.area)
|
114
|
-
|
115
|
-
def rescale(self, t):
|
116
|
-
"Rescales the triangle by a Euclidean dilation"
|
117
|
-
for face in self.lengths:
|
118
|
-
self.lengths[face] *= t
|
119
|
-
self.circumradius *= t
|
120
|
-
self.area *= t * t
|
121
|
-
|
122
|
-
@staticmethod
|
123
|
-
def direction_sign():
|
124
|
-
return +1
|
125
|
-
|
126
|
-
|
127
|
-
# Given a vertex, cyclically order the three adjacent faces in
|
128
|
-
# clockwise fashion. For each face, return the triple (face, edge, next face)
|
129
|
-
# where edge is adjacent to both faces.
|
130
|
-
_face_edge_face_triples_for_vertex_link = {
|
131
|
-
vertex : [ (faces[i], faces[i] & faces[(i+1) % 3], faces[(i+1) % 3])
|
132
|
-
for i in range(3) ]
|
133
|
-
for vertex, faces in t3m.simplex.FacesAroundVertexCounterclockwise.items()
|
134
|
-
}
|
135
|
-
|
136
|
-
# For each vertex, return an edge connected to it
|
137
|
-
_pick_an_edge_for_vertex = {
|
138
|
-
vertex : [ edge
|
139
|
-
for edge in t3m.simplex.OneSubsimplices
|
140
|
-
if t3m.simplex.is_subset(vertex, edge) ][0]
|
141
|
-
for vertex in t3m.simplex.ZeroSubsimplices
|
142
|
-
}
|
143
|
-
|
144
|
-
# For each (vertex, face) pair, pick one of the two edges adjacent
|
145
|
-
# to both the vertex and face
|
146
|
-
_pick_an_edge_for_vertex_and_face = {
|
147
|
-
(vertex, face): [ edge
|
148
|
-
for edge in t3m.simplex.OneSubsimplices
|
149
|
-
if (t3m.simplex.is_subset(vertex, edge) and
|
150
|
-
t3m.simplex.is_subset(edge, face)) ][0]
|
151
|
-
for vertex in t3m.simplex.ZeroSubsimplices
|
152
|
-
for face in t3m.simplex.TwoSubsimplices
|
153
|
-
if t3m.simplex.is_subset(vertex, face)
|
154
|
-
}
|
155
|
-
|
156
|
-
|
157
|
-
class ComplexHoroTriangle:
|
158
|
-
"""
|
159
|
-
A horosphere cross section in the corner of an ideal tetrahedron.
|
160
|
-
The sides of the triangle correspond to faces of the tetrahedron.
|
161
|
-
The lengths stored for the triangle are complex.
|
162
|
-
"""
|
163
|
-
def __init__(self, tet, vertex, known_side, length_of_side):
|
164
|
-
left_side, center_side, right_side, z_left, z_right = (
|
165
|
-
HoroTriangleBase._sides_and_cross_ratios(tet, vertex, known_side))
|
166
|
-
|
167
|
-
L = length_of_side
|
168
|
-
self.lengths = { center_side : L,
|
169
|
-
left_side : - z_left * L,
|
170
|
-
right_side : - L / z_right }
|
171
|
-
absL = abs(L)
|
172
|
-
self.area = absL * absL * z_left.imag() / 2
|
173
|
-
|
174
|
-
self._real_lengths_cache = None
|
175
|
-
|
176
|
-
def get_real_lengths(self):
|
177
|
-
if not self._real_lengths_cache:
|
178
|
-
self._real_lengths_cache = {
|
179
|
-
side : abs(length)
|
180
|
-
for side, length in self.lengths.items() }
|
181
|
-
return self._real_lengths_cache
|
182
|
-
|
183
|
-
def rescale(self, t):
|
184
|
-
"Rescales the triangle by a Euclidean dilation"
|
185
|
-
for face in self.lengths:
|
186
|
-
self.lengths[face] *= t
|
187
|
-
self.area *= t * t
|
188
|
-
|
189
|
-
@staticmethod
|
190
|
-
def direction_sign():
|
191
|
-
return -1
|
192
|
-
|
193
|
-
def add_vertex_positions(self, vertex, edge, position):
|
194
|
-
"""
|
195
|
-
Adds a dictionary vertex_positions mapping
|
196
|
-
an edge (such as t3m.simplex.E01) to complex position
|
197
|
-
for the vertex of the horotriangle obtained by
|
198
|
-
intersecting the edge with the horosphere.
|
199
|
-
|
200
|
-
Two of these positions are computed from the one given
|
201
|
-
using the complex edge lengths. The given vertex and
|
202
|
-
edge are t3m-style.
|
203
|
-
"""
|
204
|
-
|
205
|
-
self.vertex_positions = {}
|
206
|
-
|
207
|
-
# The three triples
|
208
|
-
# (face, edge adjacent to face and next face, next face)
|
209
|
-
# when going around the vertex counter clockwise
|
210
|
-
vertex_link = _face_edge_face_triples_for_vertex_link[vertex]
|
211
|
-
|
212
|
-
# Find for which of these triples the position is for
|
213
|
-
for i in range(3):
|
214
|
-
if edge == vertex_link[i][1]:
|
215
|
-
break
|
216
|
-
|
217
|
-
# Now go through the triples starting with the one for
|
218
|
-
# which we have given the vertex position
|
219
|
-
for j in range(3):
|
220
|
-
face0, edge, face1 = vertex_link[(i + j) % 3]
|
221
|
-
# Assign vertex position
|
222
|
-
self.vertex_positions[edge] = position
|
223
|
-
# Update vertex position to be for the next
|
224
|
-
# edge using complex edge length
|
225
|
-
position += self.lengths[face1]
|
226
|
-
|
227
|
-
def lift_vertex_positions(self, lifted_position):
|
228
|
-
"""
|
229
|
-
Lift the vertex positions of this triangle. lifted_position is
|
230
|
-
used as a guide what branch of the logarithm to use.
|
231
|
-
|
232
|
-
The lifted position is computed as the log of the vertex
|
233
|
-
position where it is assumed that the fixed point of the
|
234
|
-
holonomy is at the origin. The branch of the logarithm
|
235
|
-
closest to lifted_position is used.
|
236
|
-
"""
|
237
|
-
|
238
|
-
NumericalField = lifted_position.parent()
|
239
|
-
twoPi = 2 * NumericalField.pi()
|
240
|
-
I = NumericalField(1j)
|
241
|
-
|
242
|
-
def adjust_log(z):
|
243
|
-
# Compute log and adjust
|
244
|
-
logZ = log(z)
|
245
|
-
# Add multiplies of 2 * pi * I so that it is close
|
246
|
-
# to lifted_position
|
247
|
-
return logZ + ((lifted_position - logZ) / twoPi).imag().round() * twoPi * I
|
248
|
-
|
249
|
-
self.lifted_vertex_positions = {
|
250
|
-
# Take log of vertex position
|
251
|
-
# (assuming fixed point is at origin).
|
252
|
-
edge: adjust_log(position)
|
253
|
-
for edge, position in self.vertex_positions.items()
|
254
|
-
}
|
255
|
-
|
256
|
-
|
257
|
-
class CuspCrossSectionBase(McomplexEngine):
|
258
|
-
"""
|
259
|
-
Base class for RealCuspCrossSection and ComplexCuspCrossSection.
|
260
|
-
"""
|
261
|
-
|
262
|
-
def add_structures(self, one_cocycle=None):
|
263
|
-
self._add_edge_dict()
|
264
|
-
self._add_cusp_cross_sections(one_cocycle)
|
265
|
-
|
266
|
-
def _add_edge_dict(self):
|
267
|
-
"""
|
268
|
-
Adds a dictionary that maps a pair of vertices to all edges
|
269
|
-
of the triangulation connecting these vertices.
|
270
|
-
The key is a pair (v0, v1) of integers with v0 < v1 that are the
|
271
|
-
indices of the two vertices.
|
272
|
-
"""
|
273
|
-
|
274
|
-
self._edge_dict = {}
|
275
|
-
for edge in self.mcomplex.Edges:
|
276
|
-
vert0, vert1 = edge.Vertices
|
277
|
-
key = tuple(sorted([vert0.Index, vert1.Index]))
|
278
|
-
self._edge_dict.setdefault(key, []).append(edge)
|
279
|
-
|
280
|
-
def _add_cusp_cross_sections(self, one_cocycle):
|
281
|
-
for T in self.mcomplex.Tetrahedra:
|
282
|
-
T.horotriangles = {
|
283
|
-
t3m.simplex.V0 : None,
|
284
|
-
t3m.simplex.V1 : None,
|
285
|
-
t3m.simplex.V2 : None,
|
286
|
-
t3m.simplex.V3 : None
|
287
|
-
}
|
288
|
-
for cusp in self.mcomplex.Vertices:
|
289
|
-
self._add_one_cusp_cross_section(cusp, one_cocycle)
|
290
|
-
|
291
|
-
def _add_one_cusp_cross_section(self, cusp, one_cocycle):
|
292
|
-
"""
|
293
|
-
Build a cusp cross section as described in Section 3.6 of the paper
|
294
|
-
|
295
|
-
Asymmetric hyperbolic L-spaces, Heegaard genus, and Dehn filling
|
296
|
-
Nathan M. Dunfield, Neil R. Hoffman, Joan E. Licata
|
297
|
-
http://arxiv.org/abs/1407.7827
|
298
|
-
"""
|
299
|
-
corner0 = cusp.Corners[0]
|
300
|
-
tet0, vert0 = corner0.Tetrahedron, corner0.Subsimplex
|
301
|
-
face0 = t3m.simplex.FacesAroundVertexCounterclockwise[vert0][0]
|
302
|
-
tet0.horotriangles[vert0] = self.HoroTriangle(tet0, vert0, face0, 1)
|
303
|
-
active = [(tet0, vert0)]
|
304
|
-
while active:
|
305
|
-
tet0, vert0 = active.pop()
|
306
|
-
for face0 in t3m.simplex.FacesAroundVertexCounterclockwise[vert0]:
|
307
|
-
tet1, face1, vert1 = CuspCrossSectionBase._glued_to(
|
308
|
-
tet0, face0, vert0)
|
309
|
-
if tet1.horotriangles[vert1] is None:
|
310
|
-
known_side = (self.HoroTriangle.direction_sign() *
|
311
|
-
tet0.horotriangles[vert0].lengths[face0])
|
312
|
-
if one_cocycle:
|
313
|
-
known_side *= one_cocycle[tet0.Index, face0, vert0]
|
314
|
-
|
315
|
-
tet1.horotriangles[vert1] = self.HoroTriangle(
|
316
|
-
tet1, vert1, face1, known_side)
|
317
|
-
active.append((tet1, vert1))
|
318
|
-
|
319
|
-
@staticmethod
|
320
|
-
def _glued_to(tetrahedron, face, vertex):
|
321
|
-
"""
|
322
|
-
Returns (other tet, other face, other vertex).
|
323
|
-
"""
|
324
|
-
gluing = tetrahedron.Gluing[face]
|
325
|
-
return tetrahedron.Neighbor[face], gluing.image(face), gluing.image(vertex)
|
326
|
-
|
327
|
-
@staticmethod
|
328
|
-
def _cusp_area(cusp):
|
329
|
-
area = 0
|
330
|
-
for corner in cusp.Corners:
|
331
|
-
subsimplex = corner.Subsimplex
|
332
|
-
area += corner.Tetrahedron.horotriangles[subsimplex].area
|
333
|
-
return area
|
334
|
-
|
335
|
-
def cusp_areas(self):
|
336
|
-
"""
|
337
|
-
List of all cusp areas.
|
338
|
-
"""
|
339
|
-
return [ CuspCrossSectionBase._cusp_area(cusp) for cusp in self.mcomplex.Vertices ]
|
340
|
-
|
341
|
-
@staticmethod
|
342
|
-
def _scale_cusp(cusp, scale):
|
343
|
-
for corner in cusp.Corners:
|
344
|
-
subsimplex = corner.Subsimplex
|
345
|
-
corner.Tetrahedron.horotriangles[subsimplex].rescale(scale)
|
346
|
-
|
347
|
-
def scale_cusps(self, scales):
|
348
|
-
"""
|
349
|
-
Scale each cusp by Euclidean dilation by values in given array.
|
350
|
-
"""
|
351
|
-
for cusp, scale in zip(self.mcomplex.Vertices, scales):
|
352
|
-
CuspCrossSectionBase._scale_cusp(cusp, scale)
|
353
|
-
|
354
|
-
def normalize_cusps(self, areas=None):
|
355
|
-
"""
|
356
|
-
Scale cusp so that they have the given target area.
|
357
|
-
Without argument, each cusp is scaled to have area 1.
|
358
|
-
If the argument is a number, scale each cusp to have that area.
|
359
|
-
If the argument is an array, scale each cusp by the respective
|
360
|
-
entry in the array.
|
361
|
-
"""
|
362
|
-
current_areas = self.cusp_areas()
|
363
|
-
if not areas:
|
364
|
-
areas = [ 1 for area in current_areas ]
|
365
|
-
elif not isinstance(areas, list):
|
366
|
-
areas = [ areas for area in current_areas ]
|
367
|
-
scales = [ sqrt(area / current_area)
|
368
|
-
for area, current_area in zip(areas, current_areas) ]
|
369
|
-
self.scale_cusps(scales)
|
370
|
-
|
371
|
-
def check_cusp_development_exactly(self):
|
372
|
-
"""
|
373
|
-
Check that all side lengths of horo triangles are consistent.
|
374
|
-
If the logarithmic edge equations are fulfilled, this implices
|
375
|
-
that the all cusps are complete and thus the manifold is complete.
|
376
|
-
"""
|
377
|
-
|
378
|
-
for tet0 in self.mcomplex.Tetrahedra:
|
379
|
-
for vert0 in t3m.simplex.ZeroSubsimplices:
|
380
|
-
for face0 in t3m.simplex.FacesAroundVertexCounterclockwise[vert0]:
|
381
|
-
tet1, face1, vert1 = CuspCrossSectionBase._glued_to(
|
382
|
-
tet0, face0, vert0)
|
383
|
-
side0 = tet0.horotriangles[vert0].lengths[face0]
|
384
|
-
side1 = tet1.horotriangles[vert1].lengths[face1]
|
385
|
-
if not side0 == side1 * self.HoroTriangle.direction_sign():
|
386
|
-
raise CuspDevelopmentExactVerifyError(side0, side1)
|
387
|
-
|
388
|
-
@staticmethod
|
389
|
-
def _shape_for_edge_embedding(tet, perm):
|
390
|
-
"""
|
391
|
-
Given an edge embedding, find the shape assignment for it.
|
392
|
-
If the edge embedding flips orientation, apply conjugate inverse.
|
393
|
-
"""
|
394
|
-
|
395
|
-
# Get the shape for this edge embedding
|
396
|
-
subsimplex = perm.image(3)
|
397
|
-
|
398
|
-
# Figure out the orientation of this tetrahedron
|
399
|
-
# with respect to the edge, apply conjugate inverse
|
400
|
-
# if differ
|
401
|
-
if perm.sign():
|
402
|
-
return 1 / tet.ShapeParameters[subsimplex].conjugate()
|
403
|
-
else:
|
404
|
-
return tet.ShapeParameters[subsimplex]
|
405
|
-
|
406
|
-
def check_polynomial_edge_equations_exactly(self):
|
407
|
-
"""
|
408
|
-
Check that the polynomial edge equations are fulfilled exactly.
|
409
|
-
|
410
|
-
We use the conjugate inverse to support non-orientable manifolds.
|
411
|
-
"""
|
412
|
-
|
413
|
-
# For each edge
|
414
|
-
for edge in self.mcomplex.Edges:
|
415
|
-
# The exact value when evaluating the edge equation
|
416
|
-
val = 1
|
417
|
-
|
418
|
-
# Iterate through edge embeddings
|
419
|
-
for tet, perm in edge.embeddings():
|
420
|
-
# Accumulate shapes of the edge exactly
|
421
|
-
val *= CuspCrossSectionBase._shape_for_edge_embedding(
|
422
|
-
tet, perm)
|
423
|
-
|
424
|
-
if not val == 1:
|
425
|
-
raise EdgeEquationExactVerifyError(val)
|
426
|
-
|
427
|
-
def check_logarithmic_edge_equations_and_positivity(self, NumericalField):
|
428
|
-
"""
|
429
|
-
Check that the shapes have positive imaginary part and that the
|
430
|
-
logarithmic gluing equations have small error.
|
431
|
-
|
432
|
-
The shapes are coerced into the field given as argument before the
|
433
|
-
logarithm is computed. It can be, e.g., a ComplexIntervalField.
|
434
|
-
"""
|
435
|
-
|
436
|
-
# For each edge
|
437
|
-
for edge in self.mcomplex.Edges:
|
438
|
-
|
439
|
-
# The complex interval arithmetic value of the logarithmic
|
440
|
-
# version of the edge equation.
|
441
|
-
log_sum = 0
|
442
|
-
|
443
|
-
# Iterate through edge embeddings
|
444
|
-
for tet, perm in edge.embeddings():
|
445
|
-
|
446
|
-
shape = CuspCrossSectionBase._shape_for_edge_embedding(
|
447
|
-
tet, perm)
|
448
|
-
|
449
|
-
numerical_shape = NumericalField(shape)
|
450
|
-
|
451
|
-
log_shape = log(numerical_shape)
|
452
|
-
|
453
|
-
# Note that this is true for z in R, R < 0 as well,
|
454
|
-
# but then it would fail for 1 - 1/z or 1 / (1-z)
|
455
|
-
|
456
|
-
if not (log_shape.imag() > 0):
|
457
|
-
raise ShapePositiveImaginaryPartNumericalVerifyError(
|
458
|
-
numerical_shape)
|
459
|
-
|
460
|
-
# Take logarithm and accumulate
|
461
|
-
log_sum += log_shape
|
462
|
-
|
463
|
-
twoPiI = NumericalField.pi() * NumericalField(2j)
|
464
|
-
|
465
|
-
if not abs(log_sum - twoPiI) < NumericalField(1e-7):
|
466
|
-
raise EdgeEquationLogLiftNumericalVerifyError(log_sum)
|
467
|
-
|
468
|
-
def _testing_check_against_snappea(self, epsilon):
|
469
|
-
# Short-hand
|
470
|
-
ZeroSubs = t3m.simplex.ZeroSubsimplices
|
471
|
-
|
472
|
-
# SnapPea kernel results
|
473
|
-
snappea_tilts, snappea_edges = self.manifold._cusp_cross_section_info()
|
474
|
-
|
475
|
-
# Check edge lengths
|
476
|
-
# Iterate through tet
|
477
|
-
for tet, snappea_tet_edges in zip(self.mcomplex.Tetrahedra, snappea_edges):
|
478
|
-
# Iterate through vertices of tet
|
479
|
-
for v, snappea_triangle_edges in zip(ZeroSubs, snappea_tet_edges):
|
480
|
-
# Iterate through faces touching that vertex
|
481
|
-
for f, snappea_triangle_edge in zip(ZeroSubs,
|
482
|
-
snappea_triangle_edges):
|
483
|
-
if v != f:
|
484
|
-
F = t3m.simplex.comp(f)
|
485
|
-
length = abs(tet.horotriangles[v].lengths[F])
|
486
|
-
if not abs(length - snappea_triangle_edge) < epsilon:
|
487
|
-
raise ConsistencyWithSnapPeaNumericalVerifyError(
|
488
|
-
snappea_triangle_edge, length)
|
489
|
-
|
490
|
-
@staticmethod
|
491
|
-
def _lower_bound_max_area_triangle_for_std_form(z):
|
492
|
-
"""
|
493
|
-
Imagine an ideal tetrahedron in the upper half space model with
|
494
|
-
vertices at 0, 1, z, and infinity. Pick the lowest (horizontal)
|
495
|
-
horosphere about infinity that intersects the tetrahedron in a
|
496
|
-
triangle, i.e, just touches the face opposite to infinity.
|
497
|
-
This method will return the hyperbolic area of that triangle.
|
498
|
-
|
499
|
-
The result is the same for z, 1/(1-z), and 1 - 1/z.
|
500
|
-
"""
|
501
|
-
|
502
|
-
# First, we check whether the center of the circumcenter of the
|
503
|
-
# triangle containing 0, 1, and z is contained within the triangle.
|
504
|
-
|
505
|
-
# If the center is outside of the triangle, the Euclidean height of the
|
506
|
-
# horosphere is that of the highest point of the three arcs between
|
507
|
-
# 0, 1, and z.
|
508
|
-
# The height is half of the length e of the longest edge of the
|
509
|
-
# triangle.
|
510
|
-
# Given that the Euclidean area of the triangle is given by
|
511
|
-
# A = Im(z) / 2, its hyperbolic area is
|
512
|
-
# A / (e/2)^2 = Im(z) / 2 / (e^2/4) = 2 * Im(z) / e^2
|
513
|
-
#
|
514
|
-
# This is similar to fef_gen.py except that it had a bug in version 1.3
|
515
|
-
# and implemented the last inequality the other way around!
|
516
|
-
#
|
517
|
-
# The center is outside if one of the angles is > pi/2, cover each case
|
518
|
-
#
|
519
|
-
|
520
|
-
# Angle at 0 is > pi/2
|
521
|
-
if z.real() < 0:
|
522
|
-
# So longest edge of the triangle must be opposite of 0
|
523
|
-
return 2 * z.imag() / (abs(z - 1) ** 2)
|
524
|
-
# Angle at 1 is > pi/2
|
525
|
-
if z.real() > 1:
|
526
|
-
# So longest edge of the triangle must be opposite of 1
|
527
|
-
return 2 * z.imag() / (abs(z) ** 2)
|
528
|
-
# Angle at z is > pi/2
|
529
|
-
if abs(2 * z - 1) < 1:
|
530
|
-
# So longest edge of the triangle must be opposite of z
|
531
|
-
return 2 * z.imag()
|
532
|
-
|
533
|
-
# An interval note: the circumcenter might still be in the triangle,
|
534
|
-
# we just were not able to prove it. The area we compute is a lower
|
535
|
-
# bound in any case. Thus, the function is not guaranteed to compute
|
536
|
-
# the maximal area, just a lower bound for it.
|
537
|
-
|
538
|
-
# Now cover the case that the center of the triangle is within the
|
539
|
-
# triangle.
|
540
|
-
|
541
|
-
# The Euclidean area of the above triangle is given by
|
542
|
-
# A = Im(z) / 2
|
543
|
-
# and its Euclidean side lengths are given by
|
544
|
-
# a = 1, b = abs(z), and c = abs(z - 1).
|
545
|
-
#
|
546
|
-
# The Euclidean circumradius r of the triangle is given by the usual
|
547
|
-
# formula
|
548
|
-
# r = a * b * c / (4 * A)
|
549
|
-
#
|
550
|
-
# This is also the Euclidean radius of the circle containing 0, 1, and
|
551
|
-
# z and of the halfsphere above that circle that contains the face
|
552
|
-
# opposite to infinity.
|
553
|
-
# Therefore, r is also the Euclidean height of the above horosphere and
|
554
|
-
# hence, the hyperbolic metric at that height is 1/r.
|
555
|
-
# So the hyperbolic area of the triangle becomes
|
556
|
-
#
|
557
|
-
# A / r^2 = A / (a * b * c / (4 * A))^2 = 16 * A^3 / (a * b * c)^2
|
558
|
-
# = 2 * Im(z)^3 / (abs(z) * abs(z-1)) ^ 2
|
559
|
-
|
560
|
-
return 2 * z.imag() ** 3 / (abs(z) * abs(z - 1)) ** 2
|
561
|
-
|
562
|
-
def ensure_std_form(self, allow_scaling_up=False):
|
563
|
-
"""
|
564
|
-
Makes sure that the cusp neighborhoods intersect each tetrahedron
|
565
|
-
in standard form by scaling the cusp neighborhoods down if necessary.
|
566
|
-
"""
|
567
|
-
|
568
|
-
z = self.mcomplex.Tetrahedra[0].ShapeParameters[t3m.simplex.E01]
|
569
|
-
RF = z.real().parent()
|
570
|
-
|
571
|
-
# For each cusp, save the scaling factors for all triangles so that
|
572
|
-
# we can later take the minimum to scale each cusp.
|
573
|
-
if allow_scaling_up:
|
574
|
-
area_scales = [ [] for v in self.mcomplex.Vertices ]
|
575
|
-
else:
|
576
|
-
# Add 1 so that we never scale the cusp area up, just down.
|
577
|
-
area_scales = [ [RF(1)] for v in self.mcomplex.Vertices ]
|
578
|
-
|
579
|
-
for tet in self.mcomplex.Tetrahedra:
|
580
|
-
# Compute maximal area of a triangle for standard form
|
581
|
-
z = tet.ShapeParameters[t3m.simplex.E01]
|
582
|
-
max_area = ComplexCuspCrossSection._lower_bound_max_area_triangle_for_std_form(z)
|
583
|
-
|
584
|
-
# For all four triangles corresponding to the four vertices of the
|
585
|
-
# tetrahedron
|
586
|
-
for zeroSubsimplex, triangle in tet.horotriangles.items():
|
587
|
-
# Compute the area scaling factor
|
588
|
-
area_scale = max_area / triangle.area
|
589
|
-
# Get the cusp we need to scale
|
590
|
-
vertex = tet.Class[zeroSubsimplex]
|
591
|
-
# Remember it
|
592
|
-
area_scales[vertex.Index].append(area_scale)
|
593
|
-
|
594
|
-
# Compute scale per cusp as sqrt of the minimum of all area scales
|
595
|
-
# of all triangles in that cusp
|
596
|
-
scales = [ sqrt(correct_min(s)) for s in area_scales ]
|
597
|
-
|
598
|
-
self.scale_cusps(scales)
|
599
|
-
|
600
|
-
@staticmethod
|
601
|
-
def _exp_distance_edge(edge):
|
602
|
-
"""
|
603
|
-
Given an edge, returns the exp of the (hyperbolic) distance of the
|
604
|
-
two cusp neighborhoods at the ends of the edge measured along that
|
605
|
-
edge.
|
606
|
-
"""
|
607
|
-
|
608
|
-
# Get one embedding of the edge, tet is adjacent to that edge
|
609
|
-
tet, perm = next(edge.embeddings())
|
610
|
-
# Get a face of the tetrahedron adjacent to that edge
|
611
|
-
face = 15 - (1 << perm[3])
|
612
|
-
# At each end of the edge, this tetrahedron gives us one
|
613
|
-
# triangle of a cusp cross-section and the intersection of the
|
614
|
-
# face with the cusp cross-section gives us one edge of the
|
615
|
-
# triangle.
|
616
|
-
# Multiply the two edge lengths. If these are complex edge
|
617
|
-
# lengths, the result is actually the square of a Ptolemy
|
618
|
-
# coordinate (see C. Zickert, The volume and Chern-Simons
|
619
|
-
# invariant of a representation).
|
620
|
-
ptolemy_sqr = (tet.horotriangles[1 << perm[0]].lengths[face] *
|
621
|
-
tet.horotriangles[1 << perm[1]].lengths[face])
|
622
|
-
# Take abs value in case we have complex edge lengths.
|
623
|
-
return abs(1 / ptolemy_sqr)
|
624
|
-
|
625
|
-
@staticmethod
|
626
|
-
def _exp_distance_of_edges(edges):
|
627
|
-
"""
|
628
|
-
Given edges between two (not necessarily distinct) cusps,
|
629
|
-
compute the exp of the smallest (hyperbolic) distance of the
|
630
|
-
two cusp neighborhoods measured along all the given edges.
|
631
|
-
"""
|
632
|
-
return correct_min(
|
633
|
-
[ ComplexCuspCrossSection._exp_distance_edge(edge)
|
634
|
-
for edge in edges])
|
635
|
-
|
636
|
-
def ensure_disjoint_on_edges(self):
|
637
|
-
"""
|
638
|
-
Scales the cusp neighborhoods down until they are disjoint when
|
639
|
-
intersected with the edges of the triangulations.
|
640
|
-
|
641
|
-
Given an edge of a triangulation, we can easily compute the signed
|
642
|
-
distance between the two cusp neighborhoods at the ends of the edge
|
643
|
-
measured along that edge. Thus, we can easily check that all the
|
644
|
-
distances measured along all the edges are positive and scale the
|
645
|
-
cusps down if necessary.
|
646
|
-
|
647
|
-
Unfortunately, this is not sufficient to ensure that two cusp
|
648
|
-
neighborhoods are disjoint since there might be a geodesic between
|
649
|
-
the two cusps such that the distance between the two cusps measured
|
650
|
-
along the geodesic is shorter than measured along any edge of the
|
651
|
-
triangulation.
|
652
|
-
|
653
|
-
Thus, it is necessary to call ensure_std_form as well:
|
654
|
-
it will make sure that the cusp neighborhoods are small enough so
|
655
|
-
that they intersect the tetrahedra in "standard" form.
|
656
|
-
Here, "standard" form means that the corresponding horoball about a
|
657
|
-
vertex of a tetrahedron intersects the three faces of the tetrahedron
|
658
|
-
adjacent to the vertex but not the one opposite to the vertex.
|
659
|
-
|
660
|
-
For any geometric triangulation, standard form and positive distance
|
661
|
-
measured along all edges of the triangulation is sufficient for
|
662
|
-
disjoint neighborhoods.
|
663
|
-
|
664
|
-
The SnapPea kernel uses the proto-canonical triangulation associated
|
665
|
-
to the cusp neighborhood to get around this when computing the
|
666
|
-
"reach" and the "stoppers" for the cusps.
|
667
|
-
|
668
|
-
**Remark:** This means that the cusp neighborhoods might be scaled down
|
669
|
-
more than necessary. Related open questions are: given maximal disjoint
|
670
|
-
cusp neighborhoods (maximal in the sense that no neighborhood can be
|
671
|
-
expanded without bumping into another or itself), is there always a
|
672
|
-
geometric triangulation intersecting the cusp neighborhoods in standard
|
673
|
-
form? Is there an easy algorithm to find this triangulation, e.g., by
|
674
|
-
applying a 2-3 move whenever we see a non-standard intersection?
|
675
|
-
"""
|
676
|
-
|
677
|
-
num_cusps = len(self.mcomplex.Vertices)
|
678
|
-
|
679
|
-
# First check for every cusp that its cusp neighborhood does not bump
|
680
|
-
# into itself - at least when measured along the edges of the
|
681
|
-
# triangulation
|
682
|
-
for i in range(num_cusps):
|
683
|
-
# Get all edges
|
684
|
-
if (i,i) in self._edge_dict:
|
685
|
-
dist = ComplexCuspCrossSection._exp_distance_of_edges(
|
686
|
-
self._edge_dict[(i,i)])
|
687
|
-
# For verified computations, do not use the seemingly
|
688
|
-
# equivalent dist <= 1. We want to scale down every time
|
689
|
-
# we cannot ensure they are disjoint.
|
690
|
-
if not (dist > 1):
|
691
|
-
scale = sqrt(dist)
|
692
|
-
# Scale the one cusp
|
693
|
-
ComplexCuspCrossSection._scale_cusp(self.mcomplex.Vertices[i],
|
694
|
-
scale)
|
695
|
-
|
696
|
-
# Now check for the pairs of two distinct cusps that the corresponding
|
697
|
-
# neighborhoods do not bump into each other - at least when measured
|
698
|
-
# along the edges of the triangulation
|
699
|
-
for i in range(num_cusps):
|
700
|
-
for j in range(i):
|
701
|
-
# Get all edges
|
702
|
-
if (j,i) in self._edge_dict:
|
703
|
-
dist = ComplexCuspCrossSection._exp_distance_of_edges(
|
704
|
-
self._edge_dict[(j,i)])
|
705
|
-
# Above comment applies
|
706
|
-
if not (dist > 1):
|
707
|
-
# Scale the two cusps by the same amount
|
708
|
-
# We have choices here, for example, we could only
|
709
|
-
# scale one cusp by dist.
|
710
|
-
scale = sqrt(dist)
|
711
|
-
ComplexCuspCrossSection._scale_cusp(self.mcomplex.Vertices[i],
|
712
|
-
scale)
|
713
|
-
ComplexCuspCrossSection._scale_cusp(self.mcomplex.Vertices[j],
|
714
|
-
scale)
|
715
|
-
|
716
|
-
|
717
|
-
class RealCuspCrossSection(CuspCrossSectionBase):
|
718
|
-
"""
|
719
|
-
A t3m triangulation with real edge lengths of cusp cross sections built
|
720
|
-
from a cusped (possibly non-orientable) SnapPy manifold M with a hyperbolic
|
721
|
-
structure specified by shapes. It can scale the cusps to areas that can be
|
722
|
-
specified or scale them such that they are disjoint.
|
723
|
-
It can also compute the "tilts" used in the Tilt Theorem, see
|
724
|
-
``canonize_part_1.c``.
|
725
|
-
|
726
|
-
The computations are agnostic about the type of numbers provided as shapes
|
727
|
-
as long as they provide ``+``, ``-``, ``*``, ``/``, ``conjugate()``,
|
728
|
-
``im()``, ``abs()``, ``sqrt()``.
|
729
|
-
Shapes can be a numerical type such as ComplexIntervalField or an exact
|
730
|
-
type (supporting sqrt) such as QQbar.
|
731
|
-
|
732
|
-
The resulting edge lengths and tilts will be of the type returned by
|
733
|
-
applying the above operations to the shapes. For example, if the shapes
|
734
|
-
are in ComplexIntervalField, the edge lengths and tilts are elements in
|
735
|
-
RealIntervalField.
|
736
|
-
|
737
|
-
**Remark:** The real edge lengths could also be obtained from the complex
|
738
|
-
edge lengths computed by ``ComplexCuspCrossSection``, but this has two
|
739
|
-
drawbacks. The times at which we apply ``abs`` or ``sqrt`` during the
|
740
|
-
development and rescaling of the cusps would be different. Though this
|
741
|
-
gives the same values, the resulting representation of these values by an
|
742
|
-
exact number type (such as the ones in ``squareExtension.py``) might be
|
743
|
-
prohibitively more complicated. Furthermore, ``ComplexCuspCrossSection``
|
744
|
-
does not work for non-orientable manifolds (it does not implement working
|
745
|
-
in a cusp's double-cover like the SnapPea kernel does).
|
746
|
-
"""
|
747
|
-
|
748
|
-
HoroTriangle = RealHoroTriangle
|
749
|
-
|
750
|
-
@staticmethod
|
751
|
-
def fromManifoldAndShapes(manifold, shapes):
|
752
|
-
"""
|
753
|
-
**Examples:**
|
754
|
-
|
755
|
-
Initialize from shapes provided from the floats returned by
|
756
|
-
tetrahedra_shapes. The tilts appear to be negative but are not
|
757
|
-
verified by interval arithmetics::
|
758
|
-
|
759
|
-
>>> from snappy import Manifold
|
760
|
-
>>> M = Manifold("m004")
|
761
|
-
>>> M.canonize()
|
762
|
-
>>> shapes = M.tetrahedra_shapes('rect')
|
763
|
-
>>> e = RealCuspCrossSection.fromManifoldAndShapes(M, shapes)
|
764
|
-
>>> e.normalize_cusps()
|
765
|
-
>>> e.compute_tilts()
|
766
|
-
>>> tilts = e.read_tilts()
|
767
|
-
>>> for tilt in tilts:
|
768
|
-
... print('%.8f' % tilt)
|
769
|
-
-0.31020162
|
770
|
-
-0.31020162
|
771
|
-
-0.31020162
|
772
|
-
-0.31020162
|
773
|
-
-0.31020162
|
774
|
-
-0.31020162
|
775
|
-
-0.31020162
|
776
|
-
-0.31020162
|
777
|
-
|
778
|
-
Use verified intervals:
|
779
|
-
|
780
|
-
sage: from snappy.verify import *
|
781
|
-
sage: M = Manifold("m004")
|
782
|
-
sage: M.canonize()
|
783
|
-
sage: shapes = M.tetrahedra_shapes('rect', intervals=True)
|
784
|
-
|
785
|
-
Verify that the tetrahedra shapes form a complete manifold:
|
786
|
-
|
787
|
-
sage: check_logarithmic_gluing_equations_and_positively_oriented_tets(M,shapes)
|
788
|
-
sage: e = RealCuspCrossSection.fromManifoldAndShapes(M, shapes)
|
789
|
-
sage: e.normalize_cusps()
|
790
|
-
sage: e.compute_tilts()
|
791
|
-
|
792
|
-
|
793
|
-
Tilts are verified to be negative:
|
794
|
-
|
795
|
-
sage: [tilt < 0 for tilt in e.read_tilts()]
|
796
|
-
[True, True, True, True, True, True, True, True]
|
797
|
-
|
798
|
-
Setup necessary things in Sage:
|
799
|
-
|
800
|
-
sage: from sage.rings.qqbar import QQbar
|
801
|
-
sage: from sage.rings.rational_field import RationalField
|
802
|
-
sage: from sage.rings.polynomial.polynomial_ring import polygen
|
803
|
-
sage: from sage.rings.real_mpfi import RealIntervalField
|
804
|
-
sage: from sage.rings.complex_interval_field import ComplexIntervalField
|
805
|
-
sage: x = polygen(RationalField())
|
806
|
-
sage: RIF = RealIntervalField()
|
807
|
-
sage: CIF = ComplexIntervalField()
|
808
|
-
|
809
|
-
sage: M = Manifold("m412")
|
810
|
-
sage: M.canonize()
|
811
|
-
|
812
|
-
Make our own exact shapes using Sage. They are the root of the given
|
813
|
-
polynomial isolated by the given interval.
|
814
|
-
|
815
|
-
sage: r=QQbar.polynomial_root(x**2-x+1,CIF(RIF(0.49,0.51),RIF(0.86,0.87)))
|
816
|
-
sage: shapes = 5 * [r]
|
817
|
-
sage: e=RealCuspCrossSection.fromManifoldAndShapes(M, shapes)
|
818
|
-
sage: e.normalize_cusps()
|
819
|
-
|
820
|
-
The following three lines verify that we have shapes giving a complete
|
821
|
-
hyperbolic structure. The last one uses complex interval arithmetics.
|
822
|
-
|
823
|
-
sage: e.check_polynomial_edge_equations_exactly()
|
824
|
-
sage: e.check_cusp_development_exactly()
|
825
|
-
sage: e.check_logarithmic_edge_equations_and_positivity(CIF)
|
826
|
-
|
827
|
-
Because we use exact types, we can verify that each tilt is either
|
828
|
-
negative or exactly zero.
|
829
|
-
|
830
|
-
sage: e.compute_tilts()
|
831
|
-
sage: [(tilt < 0, tilt == 0) for tilt in e.read_tilts()]
|
832
|
-
[(True, False), (True, False), (False, True), (True, False), (True, False), (True, False), (True, False), (False, True), (True, False), (True, False), (True, False), (False, True), (False, True), (False, True), (False, True), (False, True), (True, False), (True, False), (False, True), (True, False)]
|
833
|
-
|
834
|
-
Some are exactly zero, so the canonical cell decomposition has
|
835
|
-
non-tetrahedral cells. In fact, the one cell is a cube. We can obtain
|
836
|
-
the retriangulation of the canonical cell decomposition as follows:
|
837
|
-
|
838
|
-
sage: e.compute_tilts()
|
839
|
-
sage: opacities = [tilt < 0 for tilt in e.read_tilts()]
|
840
|
-
sage: N = M._canonical_retriangulation()
|
841
|
-
sage: N.num_tetrahedra()
|
842
|
-
12
|
843
|
-
|
844
|
-
The manifold m412 has 8 isometries, the above code certified that using
|
845
|
-
exact arithmetic:
|
846
|
-
sage: len(N.isomorphisms_to(N))
|
847
|
-
8
|
848
|
-
"""
|
849
|
-
for cusp_info in manifold.cusp_info():
|
850
|
-
if not cusp_info['complete?']:
|
851
|
-
raise IncompleteCuspError(manifold)
|
852
|
-
|
853
|
-
m = t3m.Mcomplex(manifold)
|
854
|
-
|
855
|
-
t = TransferKernelStructuresEngine(m, manifold)
|
856
|
-
t.reindex_cusps_and_transfer_peripheral_curves()
|
857
|
-
t.add_shapes(shapes)
|
858
|
-
|
859
|
-
c = RealCuspCrossSection(m)
|
860
|
-
c.add_structures()
|
861
|
-
|
862
|
-
# For testing against SnapPea kernel data
|
863
|
-
c.manifold = manifold
|
864
|
-
|
865
|
-
return c
|
866
|
-
|
867
|
-
@staticmethod
|
868
|
-
def _tet_tilt(tet, face):
|
869
|
-
"The tilt of the face of the tetrahedron."
|
870
|
-
|
871
|
-
v = t3m.simplex.comp(face)
|
872
|
-
|
873
|
-
ans = 0
|
874
|
-
for w in t3m.simplex.ZeroSubsimplices:
|
875
|
-
if v == w:
|
876
|
-
c_w = 1
|
877
|
-
else:
|
878
|
-
z = tet.ShapeParameters[v | w]
|
879
|
-
c_w = -z.real() / abs(z)
|
880
|
-
R_w = tet.horotriangles[w].circumradius
|
881
|
-
ans += c_w * R_w
|
882
|
-
return ans
|
883
|
-
|
884
|
-
@staticmethod
|
885
|
-
def _face_tilt(face):
|
886
|
-
"""
|
887
|
-
Tilt of a face in the trinagulation: this is the sum of
|
888
|
-
the two tilts of the two faces of the two tetrahedra that are
|
889
|
-
glued. The argument is a t3m.simplex.Face.
|
890
|
-
"""
|
891
|
-
|
892
|
-
return sum([ RealCuspCrossSection._tet_tilt(corner.Tetrahedron,
|
893
|
-
corner.Subsimplex)
|
894
|
-
for corner in face.Corners ])
|
895
|
-
|
896
|
-
def compute_tilts(self):
|
897
|
-
"""
|
898
|
-
Computes all tilts. They are written to the instances of
|
899
|
-
t3m.simplex.Face and can be accessed as
|
900
|
-
[ face.Tilt for face in crossSection.Faces].
|
901
|
-
"""
|
902
|
-
|
903
|
-
for face in self.mcomplex.Faces:
|
904
|
-
face.Tilt = RealCuspCrossSection._face_tilt(face)
|
905
|
-
|
906
|
-
def read_tilts(self):
|
907
|
-
"""
|
908
|
-
After compute_tilts() has been called, put the tilt values into an
|
909
|
-
array containing the tilt of face 0, 1, 2, 3 of the first tetrahedron,
|
910
|
-
... of the second tetrahedron, ....
|
911
|
-
"""
|
912
|
-
|
913
|
-
def index_of_face_corner(corner):
|
914
|
-
face_index = t3m.simplex.comp(corner.Subsimplex).bit_length() - 1
|
915
|
-
return 4 * corner.Tetrahedron.Index + face_index
|
916
|
-
|
917
|
-
tilts = (4 * len(self.mcomplex.Tetrahedra)) * [ None ]
|
918
|
-
|
919
|
-
# For each face of the triangulation
|
920
|
-
for face in self.mcomplex.Faces:
|
921
|
-
for corner in face.Corners:
|
922
|
-
tilts[index_of_face_corner(corner)] = face.Tilt
|
923
|
-
|
924
|
-
return tilts
|
925
|
-
|
926
|
-
def _testing_check_against_snappea(self, epsilon):
|
927
|
-
"""
|
928
|
-
Compare the computed edge lengths and tilts against the one computed by
|
929
|
-
the SnapPea kernel.
|
930
|
-
|
931
|
-
>>> from snappy import Manifold
|
932
|
-
|
933
|
-
Convention of the kernel is to use (3/8) sqrt(3) as area (ensuring that
|
934
|
-
cusp neighborhoods are disjoint).
|
935
|
-
|
936
|
-
>>> cusp_area = 0.649519052838329
|
937
|
-
|
938
|
-
>>> for name in ['m009', 'm015', 't02333']:
|
939
|
-
... M = Manifold(name)
|
940
|
-
... e = RealCuspCrossSection.fromManifoldAndShapes(M, M.tetrahedra_shapes('rect'))
|
941
|
-
... e.normalize_cusps(cusp_area)
|
942
|
-
... e._testing_check_against_snappea(1e-10)
|
943
|
-
|
944
|
-
"""
|
945
|
-
|
946
|
-
CuspCrossSectionBase._testing_check_against_snappea(self, epsilon)
|
947
|
-
|
948
|
-
# Short-hand
|
949
|
-
TwoSubs = t3m.simplex.TwoSubsimplices
|
950
|
-
|
951
|
-
# SnapPea kernel results
|
952
|
-
snappea_tilts, snappea_edges = self.manifold._cusp_cross_section_info()
|
953
|
-
|
954
|
-
# Check tilts
|
955
|
-
# Iterate through tet
|
956
|
-
for tet, snappea_tet_tilts in zip(self.mcomplex.Tetrahedra, snappea_tilts):
|
957
|
-
# Iterate through vertices of tet
|
958
|
-
for f, snappea_tet_tilt in zip(TwoSubs, snappea_tet_tilts):
|
959
|
-
tilt = RealCuspCrossSection._tet_tilt(tet, f)
|
960
|
-
if not abs(snappea_tet_tilt - tilt) < epsilon:
|
961
|
-
raise ConsistencyWithSnapPeaNumericalVerifyError(
|
962
|
-
snappea_tet_tilt, tilt)
|
963
|
-
|
964
|
-
|
965
|
-
class ComplexCuspCrossSection(CuspCrossSectionBase):
|
966
|
-
"""
|
967
|
-
Similarly to RealCuspCrossSection with the following differences: it
|
968
|
-
computes the complex edge lengths and the cusp translations (instead
|
969
|
-
of the tilts) and it only works for orientable manifolds.
|
970
|
-
|
971
|
-
The same comment applies about the type of the shapes. The resulting
|
972
|
-
edge lengths and translations will be of the same type as the shapes.
|
973
|
-
|
974
|
-
For shapes corresponding to a non-boundary unipotent representation
|
975
|
-
(in other words, a manifold having an incomplete cusp), a cusp can
|
976
|
-
be developed if an appropriate 1-cocycle is given. The 1-cocycle
|
977
|
-
is a cellular cocycle in the dual of the cusp triangulations and
|
978
|
-
represents an element in H^1(boundary M; C^*) that must match the
|
979
|
-
PSL(2,C) boundary holonomy of the representation.
|
980
|
-
It is encoded as dictionary with key (tet index, t3m face, t3m vertex).
|
981
|
-
"""
|
982
|
-
|
983
|
-
HoroTriangle = ComplexHoroTriangle
|
984
|
-
|
985
|
-
@staticmethod
|
986
|
-
def fromManifoldAndShapes(manifold, shapes, one_cocycle=None):
|
987
|
-
if not one_cocycle:
|
988
|
-
for cusp_info in manifold.cusp_info():
|
989
|
-
if not cusp_info['complete?']:
|
990
|
-
raise IncompleteCuspError(manifold)
|
991
|
-
|
992
|
-
if not manifold.is_orientable():
|
993
|
-
raise ValueError("Non-orientable")
|
994
|
-
|
995
|
-
m = t3m.Mcomplex(manifold)
|
996
|
-
|
997
|
-
t = TransferKernelStructuresEngine(m, manifold)
|
998
|
-
t.reindex_cusps_and_transfer_peripheral_curves()
|
999
|
-
t.add_shapes(shapes)
|
1000
|
-
|
1001
|
-
if one_cocycle == 'develop':
|
1002
|
-
resolved_one_cocycle = None
|
1003
|
-
else:
|
1004
|
-
resolved_one_cocycle = one_cocycle
|
1005
|
-
|
1006
|
-
c = ComplexCuspCrossSection(m)
|
1007
|
-
c.add_structures(resolved_one_cocycle)
|
1008
|
-
|
1009
|
-
# For testing against SnapPea kernel data
|
1010
|
-
c.manifold = manifold
|
1011
|
-
|
1012
|
-
return c
|
1013
|
-
|
1014
|
-
def _dummy_for_testing(self):
|
1015
|
-
"""
|
1016
|
-
Compare the computed edge lengths and tilts against the one computed by
|
1017
|
-
the SnapPea kernel.
|
1018
|
-
|
1019
|
-
>>> from snappy import Manifold
|
1020
|
-
|
1021
|
-
Convention of the kernel is to use (3/8) sqrt(3) as area (ensuring that
|
1022
|
-
cusp neighborhoods are disjoint).
|
1023
|
-
|
1024
|
-
>>> cusp_area = 0.649519052838329
|
1025
|
-
|
1026
|
-
>>> for name in ['m009', 'm015', 't02333']:
|
1027
|
-
... M = Manifold(name)
|
1028
|
-
... e = ComplexCuspCrossSection.fromManifoldAndShapes(M, M.tetrahedra_shapes('rect'))
|
1029
|
-
... e.normalize_cusps(cusp_area)
|
1030
|
-
... e._testing_check_against_snappea(1e-10)
|
1031
|
-
|
1032
|
-
"""
|
1033
|
-
|
1034
|
-
@staticmethod
|
1035
|
-
def _get_translation(vertex, ml):
|
1036
|
-
"""
|
1037
|
-
Compute the translation corresponding to the meridian (ml = 0) or
|
1038
|
-
longitude (ml = 1) of the given cusp.
|
1039
|
-
"""
|
1040
|
-
|
1041
|
-
# Accumulate result
|
1042
|
-
result = 0
|
1043
|
-
|
1044
|
-
# For each triangle of this cusp's cross-section
|
1045
|
-
for corner in vertex.Corners:
|
1046
|
-
# Get the corresponding tetrahedron
|
1047
|
-
tet = corner.Tetrahedron
|
1048
|
-
# Get the corresponding vertex of this tetrahedron
|
1049
|
-
subsimplex = corner.Subsimplex
|
1050
|
-
# Get the three faces of the tetrahedron adjacent to that vertex
|
1051
|
-
# Each one intersects the cusp cross-section in an edge of
|
1052
|
-
# the triangle.
|
1053
|
-
faces = t3m.simplex.FacesAroundVertexCounterclockwise[subsimplex]
|
1054
|
-
# Get the data for this triangle
|
1055
|
-
triangle = tet.horotriangles[subsimplex]
|
1056
|
-
|
1057
|
-
# Restrict the peripheral curve data to this triangle.
|
1058
|
-
# The result consists of four integers, but the one at
|
1059
|
-
# subsimplex will always be zero, so effectively, it
|
1060
|
-
# is three integers corresponding to the three sides of the
|
1061
|
-
# triangle.
|
1062
|
-
# Each of these integers tells us how often the peripheral curve
|
1063
|
-
# "enters" the triangle from the corresponding side of the
|
1064
|
-
# triangle.
|
1065
|
-
# Each time the peripheral curve "enters" the triangle through a
|
1066
|
-
# side, its contribution to the translation is the vector from the
|
1067
|
-
# center of the side to the center of the triangle.
|
1068
|
-
curves = tet.PeripheralCurves[ml][0][subsimplex]
|
1069
|
-
|
1070
|
-
# We know need to compute this contribution to the translation.
|
1071
|
-
# Imagine a triangle with complex edge lengths e_0, e_1, e_2 and,
|
1072
|
-
# without loss of generality, move it such that its vertices are
|
1073
|
-
# at v_0 = 0, v_1 = e_0, v_2 = e_0 + e_1.
|
1074
|
-
# The center of the triangle is at
|
1075
|
-
# c = (v_0 + v_1 + v_2) / 3 = 2 * e_0 / 3 + e_1 / 3.
|
1076
|
-
# The vector from the center of the side corresponding to e_0
|
1077
|
-
# to the center of the triangle is given by
|
1078
|
-
# c - e_0 / 2 = e_0 / 6 + e_1 / 3
|
1079
|
-
#
|
1080
|
-
# If the peripheral curves enters the side of the triangle
|
1081
|
-
# corresponding to e_i n_i-times, then the total contribution
|
1082
|
-
# with respect to that triangle is given by
|
1083
|
-
# n_0 * (e_0 / 6 + e_1 / 3)
|
1084
|
-
# + n_1 * (e_1 / 6 + e_2 / 3)
|
1085
|
-
# + n_2 * (e_2 / 6 + e_0 / 3)
|
1086
|
-
# = ( (n_0 + 2 * n_2) * e_0
|
1087
|
-
# + (n_1 + 2 * n_0) * e_1
|
1088
|
-
# + (n_2 + 2 * n_1) * e_2) / 6
|
1089
|
-
#
|
1090
|
-
# = (sum_{i=0,1,2} (n_i + 2 * n_{i+2}) * e_i) / 6
|
1091
|
-
|
1092
|
-
# Implement this sum
|
1093
|
-
for i in range(3):
|
1094
|
-
# Find the t3m faces corresponding to two edges of this
|
1095
|
-
# triangle
|
1096
|
-
this_face = faces[ i ]
|
1097
|
-
prev_face = faces[(i+2) % 3]
|
1098
|
-
|
1099
|
-
# n_i + 2 * n_{i+2} in above notation
|
1100
|
-
f = curves[this_face] + 2 * curves[prev_face]
|
1101
|
-
|
1102
|
-
# (n_i + 2 * n_{i+2}) * e_i in above notation
|
1103
|
-
result += f * triangle.lengths[this_face]
|
1104
|
-
|
1105
|
-
return result / 6
|
1106
|
-
|
1107
|
-
@staticmethod
|
1108
|
-
def _compute_translations(vertex):
|
1109
|
-
vertex.Translations = [
|
1110
|
-
ComplexCuspCrossSection._get_translation(vertex, i)
|
1111
|
-
for i in range(2) ]
|
1112
|
-
|
1113
|
-
def compute_translations(self):
|
1114
|
-
for vertex in self.mcomplex.Vertices:
|
1115
|
-
ComplexCuspCrossSection._compute_translations(vertex)
|
1116
|
-
|
1117
|
-
@staticmethod
|
1118
|
-
def _get_normalized_translations(vertex):
|
1119
|
-
"""
|
1120
|
-
Compute the translations corresponding to the merdian and longitude of
|
1121
|
-
the given cusp.
|
1122
|
-
"""
|
1123
|
-
|
1124
|
-
m, l = vertex.Translations
|
1125
|
-
return m / l * abs(l), abs(l)
|
1126
|
-
|
1127
|
-
def all_normalized_translations(self):
|
1128
|
-
"""
|
1129
|
-
Compute the translations corresponding to the meridian and longitude
|
1130
|
-
for each cusp.
|
1131
|
-
"""
|
1132
|
-
|
1133
|
-
self.compute_translations()
|
1134
|
-
return [ ComplexCuspCrossSection._get_normalized_translations(vertex)
|
1135
|
-
for vertex in self.mcomplex.Vertices ]
|
1136
|
-
|
1137
|
-
@staticmethod
|
1138
|
-
def _compute_cusp_shape(vertex):
|
1139
|
-
m, l = vertex.Translations
|
1140
|
-
return (l / m).conjugate()
|
1141
|
-
|
1142
|
-
def cusp_shapes(self):
|
1143
|
-
"""
|
1144
|
-
Compute the cusp shapes as conjugate of the quotient of the translations
|
1145
|
-
corresponding to the longitude and meridian for each cusp (SnapPea
|
1146
|
-
kernel convention).
|
1147
|
-
"""
|
1148
|
-
self.compute_translations()
|
1149
|
-
return [ ComplexCuspCrossSection._compute_cusp_shape(vertex)
|
1150
|
-
for vertex in self.mcomplex.Vertices ]
|
1151
|
-
|
1152
|
-
def add_vertex_positions_to_horotriangles(self):
|
1153
|
-
"""
|
1154
|
-
Develops cusp to assign to each horotriangle the positions of its three
|
1155
|
-
vertices in the Euclidean plane.
|
1156
|
-
|
1157
|
-
Note: For a complete cusp, this is defined only up to translating the
|
1158
|
-
entire triangle by translations generated by meridian and longitude.
|
1159
|
-
|
1160
|
-
For an incomplete cusp, this is defined only up to
|
1161
|
-
similarities generated by the meridian and longitude. The
|
1162
|
-
positions can be moved such that the fixed point of these
|
1163
|
-
similarities is at the origin by calling
|
1164
|
-
move_fixed_point_to_zero after
|
1165
|
-
add_vertex_positions_to_horotriangles.
|
1166
|
-
|
1167
|
-
Note: This is not working when one_cocycle is passed during the
|
1168
|
-
construction of the cusp cross section.
|
1169
|
-
"""
|
1170
|
-
for cusp in self.mcomplex.Vertices:
|
1171
|
-
self._add_one_cusp_vertex_positions(cusp)
|
1172
|
-
|
1173
|
-
def _add_one_cusp_vertex_positions(self, cusp):
|
1174
|
-
"""
|
1175
|
-
Procedure is similar to _add_one_cusp_cross_section
|
1176
|
-
"""
|
1177
|
-
|
1178
|
-
corner0 = cusp.Corners[0]
|
1179
|
-
tet0, vert0 = corner0.Tetrahedron, corner0.Subsimplex
|
1180
|
-
zero = tet0.ShapeParameters[t3m.simplex.E01].parent()(0)
|
1181
|
-
tet0.horotriangles[vert0].add_vertex_positions(
|
1182
|
-
vert0, _pick_an_edge_for_vertex[vert0], zero)
|
1183
|
-
|
1184
|
-
active = [(tet0, vert0)]
|
1185
|
-
|
1186
|
-
# Pairs (tet index, vertex) indicating what has already been
|
1187
|
-
# visited
|
1188
|
-
visited = set()
|
1189
|
-
|
1190
|
-
while active:
|
1191
|
-
tet0, vert0 = active.pop()
|
1192
|
-
for face0 in t3m.simplex.FacesAroundVertexCounterclockwise[vert0]:
|
1193
|
-
tet1, face1, vert1 = CuspCrossSectionBase._glued_to(
|
1194
|
-
tet0, face0, vert0)
|
1195
|
-
if not (tet1.Index, vert1) in visited:
|
1196
|
-
edge0 = _pick_an_edge_for_vertex_and_face[vert0, face0]
|
1197
|
-
edge1 = tet0.Gluing[face0].image(edge0)
|
1198
|
-
|
1199
|
-
tet1.horotriangles[vert1].add_vertex_positions(
|
1200
|
-
vert1,
|
1201
|
-
edge1,
|
1202
|
-
tet0.horotriangles[vert0].vertex_positions[edge0])
|
1203
|
-
|
1204
|
-
active.append( (tet1, vert1) )
|
1205
|
-
visited.add((tet1.Index, vert1))
|
1206
|
-
|
1207
|
-
def _debug_show_horotriangles(self, cusp=0):
|
1208
|
-
from sage.all import line, real, imag
|
1209
|
-
|
1210
|
-
self.add_vertex_positions_to_horotriangles()
|
1211
|
-
|
1212
|
-
return sum(
|
1213
|
-
[ line( [ (real(z0), imag(z0)),
|
1214
|
-
(real(z1), imag(z1)) ] )
|
1215
|
-
for tet in self.mcomplex.Tetrahedra
|
1216
|
-
for V, h in tet.horotriangles.items()
|
1217
|
-
for z0 in h.vertex_positions.values()
|
1218
|
-
for z1 in h.vertex_positions.values()
|
1219
|
-
if tet.Class[V].Index == cusp ])
|
1220
|
-
|
1221
|
-
def _debug_show_lifted_horotriangles(self, cusp=0):
|
1222
|
-
from sage.all import line, real, imag
|
1223
|
-
|
1224
|
-
self.add_vertex_positions_to_horotriangles()
|
1225
|
-
|
1226
|
-
return sum(
|
1227
|
-
[ line( [ (real(z0), imag(z0)),
|
1228
|
-
(real(z1), imag(z1)) ] )
|
1229
|
-
for tet in self.mcomplex.Tetrahedra
|
1230
|
-
for V, h in tet.horotriangles.items()
|
1231
|
-
for z0 in h.lifted_vertex_positions.values()
|
1232
|
-
for z1 in h.lifted_vertex_positions.values()
|
1233
|
-
if tet.Class[V].Index == cusp ])
|
1234
|
-
|
1235
|
-
def move_fixed_point_to_zero(self):
|
1236
|
-
"""
|
1237
|
-
Determines the fixed point of the holonomies for all
|
1238
|
-
incomplete cusps. Then moves the vertex positions of the
|
1239
|
-
corresponding cusp triangles so that the fixed point is at the
|
1240
|
-
origin.
|
1241
|
-
|
1242
|
-
It also add the boolean v.is_complete to all vertices of the
|
1243
|
-
triangulation to mark whether the corresponding cusp is
|
1244
|
-
complete or not.
|
1245
|
-
"""
|
1246
|
-
|
1247
|
-
# For each cusp
|
1248
|
-
for cusp, cusp_info in zip(self.mcomplex.Vertices,
|
1249
|
-
self.manifold.cusp_info()):
|
1250
|
-
|
1251
|
-
cusp.is_complete = cusp_info['complete?']
|
1252
|
-
if not cusp.is_complete:
|
1253
|
-
# For an incomplete cusp, compute fixed point
|
1254
|
-
fixed_pt = self._compute_cusp_fixed_point(cusp)
|
1255
|
-
for corner in cusp.Corners:
|
1256
|
-
tet, vert = corner.Tetrahedron, corner.Subsimplex
|
1257
|
-
trig = tet.horotriangles[vert]
|
1258
|
-
# Move all vertex positions so that fixed point
|
1259
|
-
# is at origin
|
1260
|
-
trig.vertex_positions = {
|
1261
|
-
edge : position - fixed_pt
|
1262
|
-
for edge, position in trig.vertex_positions.items() }
|
1263
|
-
|
1264
|
-
def _compute_cusp_fixed_point(self, cusp):
|
1265
|
-
"""
|
1266
|
-
Compute fixed point for an incomplete cusp.
|
1267
|
-
"""
|
1268
|
-
|
1269
|
-
# Given a horotriangle trig0 with a vertex and edge, let
|
1270
|
-
# l0 be the complex position of the vertex and p0 the complex
|
1271
|
-
# edge length.
|
1272
|
-
# Let trig1 be the horotriangle glued to trig0 along the edge
|
1273
|
-
# and the l1 and p1 be the corresponding position and edge length
|
1274
|
-
# (traversed the opposite direction) in the other horotriangle.
|
1275
|
-
#
|
1276
|
-
# Then the similarity is described the complex number z = -l1 / l0
|
1277
|
-
# which is one or the holonomy of meridian or longitude (depending
|
1278
|
-
# on whether the common edge is inside or on the boundary of a
|
1279
|
-
# fundamental domain implicitly chosen when developing the cusp).
|
1280
|
-
#
|
1281
|
-
# Furthermore, we can compute the fixed point p of the similarity
|
1282
|
-
# using p1 - p = z * (p0 - p).
|
1283
|
-
|
1284
|
-
# Compute z, p0, p1 for each horotriangle, vertex and edge and pick
|
1285
|
-
# the one where z is furthest away from one.
|
1286
|
-
dummy, z, p0, p1 = max(self._compute_cusp_fixed_point_data(cusp),
|
1287
|
-
key=lambda d: d[0])
|
1288
|
-
|
1289
|
-
# Compute fixed point
|
1290
|
-
return (p1 - z * p0) / (1 - z)
|
1291
|
-
|
1292
|
-
def _compute_cusp_fixed_point_data(self, cusp):
|
1293
|
-
"""
|
1294
|
-
Compute abs(z-1), z, p0, p1 for each horotriangle, vertex and edge
|
1295
|
-
as described in _compute_cusp_fixed_point.
|
1296
|
-
"""
|
1297
|
-
|
1298
|
-
# For each horotriangle
|
1299
|
-
for corner in cusp.Corners:
|
1300
|
-
tet0, vert0 = corner.Tetrahedron, corner.Subsimplex
|
1301
|
-
vertex_link = _face_edge_face_triples_for_vertex_link[vert0]
|
1302
|
-
|
1303
|
-
# A flag of a horotriangle corresponds to a face and edge
|
1304
|
-
# of the tetrahedron.
|
1305
|
-
for face0, edge0, other_face in vertex_link:
|
1306
|
-
# How that horotriangle is glued to the neighboring one
|
1307
|
-
tet1, face1, vert1 = CuspCrossSectionBase._glued_to(
|
1308
|
-
tet0, face0, vert0)
|
1309
|
-
edge1 = tet0.Gluing[face0].image(edge0)
|
1310
|
-
|
1311
|
-
# Get horotriangle and the complex vertex position and
|
1312
|
-
# edge length
|
1313
|
-
trig0 = tet0.horotriangles[vert0]
|
1314
|
-
l0 = trig0.lengths[face0]
|
1315
|
-
p0 = trig0.vertex_positions[edge0]
|
1316
|
-
|
1317
|
-
# And for neighbor
|
1318
|
-
trig1 = tet1.horotriangles[vert1]
|
1319
|
-
l1 = trig1.lengths[face1]
|
1320
|
-
p1 = trig1.vertex_positions[edge1]
|
1321
|
-
|
1322
|
-
# Parameter for similarity
|
1323
|
-
z = - l1 / l0
|
1324
|
-
yield (abs(z - 1), z, p0, p1)
|
1325
|
-
|
1326
|
-
def lift_vertex_positions_of_horotriangles(self):
|
1327
|
-
"""
|
1328
|
-
After developing an incomplete cusp with
|
1329
|
-
add_vertex_positions_to_horotriangles, this function moves the
|
1330
|
-
vertex positions first to zero the fixed point (see
|
1331
|
-
move_ffixed_point_to_zero) and computes logarithms for all the
|
1332
|
-
vertex positions of the horotriangles in the Euclidean plane
|
1333
|
-
in a consistent manner. These logarithms are written to a
|
1334
|
-
dictionary lifted_vertex_positions on the HoroTriangle's.
|
1335
|
-
|
1336
|
-
For an incomplete cusp, the respective value in lifted_vertex_positions
|
1337
|
-
will be None.
|
1338
|
-
|
1339
|
-
The three logarithms of the vertex positions of a triangle are only
|
1340
|
-
defined up to adding mu Z + lambda Z where mu and lambda are the
|
1341
|
-
logarithmic holonomies of the meridian and longitude.
|
1342
|
-
"""
|
1343
|
-
|
1344
|
-
self.move_fixed_point_to_zero()
|
1345
|
-
|
1346
|
-
for cusp in self.mcomplex.Vertices:
|
1347
|
-
self._lift_one_cusp_vertex_positions(cusp)
|
1348
|
-
|
1349
|
-
def _lift_one_cusp_vertex_positions(self, cusp):
|
1350
|
-
# Pick first triangle to develop
|
1351
|
-
corner0 = cusp.Corners[0]
|
1352
|
-
tet0, vert0 = corner0.Tetrahedron, corner0.Subsimplex
|
1353
|
-
trig0 = tet0.horotriangles[vert0]
|
1354
|
-
edge0 = _pick_an_edge_for_vertex[vert0]
|
1355
|
-
|
1356
|
-
if cusp.is_complete:
|
1357
|
-
# If cusp is complete, we store None for the logarithms
|
1358
|
-
for corner in cusp.Corners:
|
1359
|
-
tet0, vert0 = corner.Tetrahedron, corner.Subsimplex
|
1360
|
-
tet0.horotriangles[vert0].lifted_vertex_positions = {
|
1361
|
-
vert0 | vert1 : None
|
1362
|
-
for vert1 in t3m.ZeroSubsimplices
|
1363
|
-
if vert0 != vert1 }
|
1364
|
-
return
|
1365
|
-
|
1366
|
-
# Lift first triangle, picking main branch of logarithm for
|
1367
|
-
# the first vertex
|
1368
|
-
trig0.lift_vertex_positions(log(trig0.vertex_positions[edge0]))
|
1369
|
-
|
1370
|
-
# Procedure similar to _add_one_cusp_cross_section
|
1371
|
-
active = [(tet0, vert0)]
|
1372
|
-
|
1373
|
-
# Pairs (tet index, vertex) indicating what has already been
|
1374
|
-
# visited
|
1375
|
-
visited = set()
|
1376
|
-
|
1377
|
-
while active:
|
1378
|
-
tet0, vert0 = active.pop()
|
1379
|
-
for face0 in t3m.simplex.FacesAroundVertexCounterclockwise[vert0]:
|
1380
|
-
tet1, face1, vert1 = CuspCrossSectionBase._glued_to(
|
1381
|
-
tet0, face0, vert0)
|
1382
|
-
if not (tet1.Index, vert1) in visited:
|
1383
|
-
edge0 = _pick_an_edge_for_vertex_and_face[vert0, face0]
|
1384
|
-
|
1385
|
-
# Lift triangle using lifted vertex position of
|
1386
|
-
# neighboring triangle as guide (when determining what
|
1387
|
-
# branch of logarithm to take).
|
1388
|
-
tet1.horotriangles[vert1].lift_vertex_positions(
|
1389
|
-
tet0.horotriangles[vert0].lifted_vertex_positions[edge0])
|
1390
|
-
|
1391
|
-
active.append( (tet1, vert1) )
|
1392
|
-
visited.add( (tet1.Index, vert1) )
|
1393
|
-
|
1394
|
-
def move_lifted_vertex_positions_to_zero_first(self):
|
1395
|
-
"""
|
1396
|
-
Shift the lifted vertex positions such that the one associated
|
1397
|
-
to the first vertex when developing the incomplete cusp is
|
1398
|
-
zero. This makes the values we obtain more stable when
|
1399
|
-
changing the Dehn-surgery parameters.
|
1400
|
-
"""
|
1401
|
-
|
1402
|
-
for cusp in self.mcomplex.Vertices:
|
1403
|
-
if not cusp.is_complete:
|
1404
|
-
ComplexCuspCrossSection._move_lifted_vertex_positions_cusp(cusp)
|
1405
|
-
|
1406
|
-
@staticmethod
|
1407
|
-
def _move_lifted_vertex_positions_cusp(cusp):
|
1408
|
-
corner0 = cusp.Corners[0]
|
1409
|
-
tet0, vert0 = corner0.Tetrahedron, corner0.Subsimplex
|
1410
|
-
trig0 = tet0.horotriangles[vert0]
|
1411
|
-
edge0 = _pick_an_edge_for_vertex[vert0]
|
1412
|
-
|
1413
|
-
log0 = trig0.lifted_vertex_positions[edge0]
|
1414
|
-
|
1415
|
-
for corner in cusp.Corners:
|
1416
|
-
tet, vert = corner.Tetrahedron, corner.Subsimplex
|
1417
|
-
trig = tet.horotriangles[vert]
|
1418
|
-
|
1419
|
-
trig.lifted_vertex_positions = {
|
1420
|
-
edge: position - log0
|
1421
|
-
for edge, position in trig.lifted_vertex_positions.items()
|
1422
|
-
}
|