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.
- snappy/CyOpenGL.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/SnapPy.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/SnapPy.ico +0 -0
- snappy/SnapPy.png +0 -0
- snappy/SnapPyHP.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/__init__.py +534 -0
- snappy/app.py +604 -0
- snappy/app_menus.py +372 -0
- snappy/browser.py +998 -0
- snappy/cache.py +25 -0
- snappy/canonical.py +249 -0
- snappy/cusps/__init__.py +280 -0
- snappy/cusps/cusp_area_matrix.py +98 -0
- snappy/cusps/cusp_areas_from_matrix.py +96 -0
- snappy/cusps/maximal_cusp_area_matrix.py +136 -0
- snappy/cusps/short_slopes_for_cusp.py +217 -0
- snappy/cusps/test.py +22 -0
- snappy/cusps/trig_cusp_area_matrix.py +63 -0
- snappy/database.py +454 -0
- snappy/db_utilities.py +79 -0
- snappy/decorated_isosig.py +717 -0
- snappy/dev/__init__.py +0 -0
- snappy/dev/extended_ptolemy/__init__.py +8 -0
- snappy/dev/extended_ptolemy/closed.py +106 -0
- snappy/dev/extended_ptolemy/complexVolumesClosed.py +149 -0
- snappy/dev/extended_ptolemy/direct.py +42 -0
- snappy/dev/extended_ptolemy/extended.py +406 -0
- snappy/dev/extended_ptolemy/giac_helper.py +43 -0
- snappy/dev/extended_ptolemy/giac_rur.py +129 -0
- snappy/dev/extended_ptolemy/gluing.py +46 -0
- snappy/dev/extended_ptolemy/phc_wrapper.py +220 -0
- snappy/dev/extended_ptolemy/printMatrices.py +70 -0
- snappy/dev/vericlosed/__init__.py +1 -0
- snappy/dev/vericlosed/computeApproxHyperbolicStructureNew.py +159 -0
- snappy/dev/vericlosed/computeApproxHyperbolicStructureOrb.py +90 -0
- snappy/dev/vericlosed/computeVerifiedHyperbolicStructure.py +111 -0
- snappy/dev/vericlosed/gimbalLoopFinder.py +130 -0
- snappy/dev/vericlosed/hyperbolicStructure.py +313 -0
- snappy/dev/vericlosed/krawczykCertifiedEdgeLengthsEngine.py +165 -0
- snappy/dev/vericlosed/oneVertexTruncatedComplex.py +122 -0
- snappy/dev/vericlosed/orb/__init__.py +1 -0
- snappy/dev/vericlosed/orb/orb_solution_for_snappea_finite_triangulation_mac +0 -0
- snappy/dev/vericlosed/parseVertexGramMatrixFile.py +47 -0
- snappy/dev/vericlosed/polishApproxHyperbolicStructure.py +61 -0
- snappy/dev/vericlosed/test.py +54 -0
- snappy/dev/vericlosed/truncatedComplex.py +176 -0
- snappy/dev/vericlosed/verificationError.py +58 -0
- snappy/dev/vericlosed/verifyHyperbolicStructureEngine.py +177 -0
- snappy/doc/_images/SnapPy-196.png +0 -0
- snappy/doc/_images/m004_paper_plane_on_systole.jpg +0 -0
- snappy/doc/_images/m125_paper_plane.jpg +0 -0
- snappy/doc/_images/mac.png +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/_images/plink-action.png +0 -0
- snappy/doc/_images/ubuntu.png +0 -0
- snappy/doc/_images/win7.png +0 -0
- snappy/doc/_sources/additional_classes.rst.txt +40 -0
- snappy/doc/_sources/bugs.rst.txt +14 -0
- snappy/doc/_sources/censuses.rst.txt +52 -0
- snappy/doc/_sources/credits.rst.txt +81 -0
- snappy/doc/_sources/development.rst.txt +261 -0
- snappy/doc/_sources/index.rst.txt +215 -0
- snappy/doc/_sources/installing.rst.txt +249 -0
- snappy/doc/_sources/manifold.rst.txt +6 -0
- snappy/doc/_sources/manifoldhp.rst.txt +46 -0
- snappy/doc/_sources/news.rst.txt +425 -0
- snappy/doc/_sources/other.rst.txt +25 -0
- snappy/doc/_sources/platonic_census.rst.txt +20 -0
- snappy/doc/_sources/plink.rst.txt +102 -0
- snappy/doc/_sources/ptolemy.rst.txt +66 -0
- snappy/doc/_sources/ptolemy_classes.rst.txt +42 -0
- snappy/doc/_sources/ptolemy_examples1.rst.txt +298 -0
- snappy/doc/_sources/ptolemy_examples2.rst.txt +363 -0
- snappy/doc/_sources/ptolemy_examples3.rst.txt +301 -0
- snappy/doc/_sources/ptolemy_examples4.rst.txt +61 -0
- snappy/doc/_sources/ptolemy_prelim.rst.txt +105 -0
- snappy/doc/_sources/screenshots.rst.txt +21 -0
- snappy/doc/_sources/snap.rst.txt +87 -0
- snappy/doc/_sources/snappy.rst.txt +28 -0
- snappy/doc/_sources/spherogram.rst.txt +103 -0
- snappy/doc/_sources/todo.rst.txt +47 -0
- snappy/doc/_sources/triangulation.rst.txt +11 -0
- snappy/doc/_sources/tutorial.rst.txt +49 -0
- snappy/doc/_sources/verify.rst.txt +210 -0
- snappy/doc/_sources/verify_internals.rst.txt +79 -0
- snappy/doc/_static/SnapPy-horizontal-128.png +0 -0
- snappy/doc/_static/SnapPy.ico +0 -0
- snappy/doc/_static/_sphinx_javascript_frameworks_compat.js +123 -0
- snappy/doc/_static/basic.css +906 -0
- snappy/doc/_static/css/badge_only.css +1 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- snappy/doc/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.eot +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.svg +2671 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.woff +0 -0
- snappy/doc/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-bold-italic.woff +0 -0
- snappy/doc/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-bold.woff +0 -0
- snappy/doc/_static/css/fonts/lato-bold.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-normal-italic.woff +0 -0
- snappy/doc/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- snappy/doc/_static/css/fonts/lato-normal.woff +0 -0
- snappy/doc/_static/css/fonts/lato-normal.woff2 +0 -0
- snappy/doc/_static/css/theme.css +4 -0
- snappy/doc/_static/doctools.js +149 -0
- snappy/doc/_static/documentation_options.js +13 -0
- snappy/doc/_static/file.png +0 -0
- 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/jquery.js +2 -0
- snappy/doc/_static/js/badge_only.js +1 -0
- snappy/doc/_static/js/theme.js +1 -0
- snappy/doc/_static/js/versions.js +228 -0
- snappy/doc/_static/language_data.js +192 -0
- snappy/doc/_static/minus.png +0 -0
- snappy/doc/_static/plus.png +0 -0
- snappy/doc/_static/pygments.css +75 -0
- snappy/doc/_static/searchtools.js +635 -0
- snappy/doc/_static/snappy_furo.css +33 -0
- snappy/doc/_static/snappy_sphinx_rtd_theme.css +42 -0
- snappy/doc/_static/sphinx_highlight.js +154 -0
- snappy/doc/additional_classes.html +1500 -0
- snappy/doc/bugs.html +132 -0
- snappy/doc/censuses.html +453 -0
- snappy/doc/credits.html +184 -0
- snappy/doc/development.html +385 -0
- snappy/doc/doc-latest/additional_classes.html +1500 -0
- snappy/doc/doc-latest/bugs.html +132 -0
- snappy/doc/doc-latest/censuses.html +453 -0
- snappy/doc/doc-latest/credits.html +184 -0
- snappy/doc/doc-latest/development.html +385 -0
- snappy/doc/doc-latest/genindex.html +1349 -0
- snappy/doc/doc-latest/index.html +287 -0
- snappy/doc/doc-latest/installing.html +346 -0
- snappy/doc/doc-latest/manifold.html +3632 -0
- snappy/doc/doc-latest/manifoldhp.html +180 -0
- snappy/doc/doc-latest/news.html +438 -0
- snappy/doc/doc-latest/objects.inv +0 -0
- snappy/doc/doc-latest/other.html +160 -0
- snappy/doc/doc-latest/platonic_census.html +376 -0
- snappy/doc/doc-latest/plink.html +210 -0
- snappy/doc/doc-latest/ptolemy.html +253 -0
- snappy/doc/doc-latest/ptolemy_classes.html +1144 -0
- snappy/doc/doc-latest/ptolemy_examples1.html +409 -0
- snappy/doc/doc-latest/ptolemy_examples2.html +471 -0
- snappy/doc/doc-latest/ptolemy_examples3.html +414 -0
- snappy/doc/doc-latest/ptolemy_examples4.html +195 -0
- snappy/doc/doc-latest/ptolemy_prelim.html +248 -0
- snappy/doc/doc-latest/py-modindex.html +165 -0
- snappy/doc/doc-latest/screenshots.html +141 -0
- snappy/doc/doc-latest/search.html +135 -0
- snappy/doc/doc-latest/searchindex.js +1 -0
- snappy/doc/doc-latest/snap.html +202 -0
- snappy/doc/doc-latest/snappy.html +181 -0
- snappy/doc/doc-latest/spherogram.html +1346 -0
- snappy/doc/doc-latest/todo.html +166 -0
- snappy/doc/doc-latest/triangulation.html +1676 -0
- snappy/doc/doc-latest/tutorial.html +159 -0
- snappy/doc/doc-latest/verify.html +330 -0
- snappy/doc/doc-latest/verify_internals.html +1235 -0
- snappy/doc/genindex.html +1349 -0
- snappy/doc/index.html +287 -0
- snappy/doc/installing.html +346 -0
- snappy/doc/manifold.html +3632 -0
- snappy/doc/manifoldhp.html +180 -0
- snappy/doc/news.html +438 -0
- snappy/doc/objects.inv +0 -0
- snappy/doc/other.html +160 -0
- snappy/doc/platonic_census.html +376 -0
- snappy/doc/plink.html +210 -0
- snappy/doc/ptolemy.html +253 -0
- snappy/doc/ptolemy_classes.html +1144 -0
- snappy/doc/ptolemy_examples1.html +409 -0
- snappy/doc/ptolemy_examples2.html +471 -0
- snappy/doc/ptolemy_examples3.html +414 -0
- snappy/doc/ptolemy_examples4.html +195 -0
- snappy/doc/ptolemy_prelim.html +248 -0
- snappy/doc/py-modindex.html +165 -0
- snappy/doc/screenshots.html +141 -0
- snappy/doc/search.html +135 -0
- snappy/doc/searchindex.js +1 -0
- snappy/doc/snap.html +202 -0
- snappy/doc/snappy.html +181 -0
- snappy/doc/spherogram.html +1346 -0
- snappy/doc/todo.html +166 -0
- snappy/doc/triangulation.html +1676 -0
- snappy/doc/tutorial.html +159 -0
- snappy/doc/verify.html +330 -0
- snappy/doc/verify_internals.html +1235 -0
- snappy/drilling/__init__.py +456 -0
- snappy/drilling/barycentric.py +103 -0
- snappy/drilling/constants.py +5 -0
- snappy/drilling/crush.py +270 -0
- snappy/drilling/cusps.py +125 -0
- snappy/drilling/debug.py +242 -0
- snappy/drilling/epsilons.py +6 -0
- snappy/drilling/exceptions.py +55 -0
- snappy/drilling/moves.py +620 -0
- snappy/drilling/peripheral_curves.py +210 -0
- snappy/drilling/perturb.py +188 -0
- snappy/drilling/shorten.py +36 -0
- snappy/drilling/subdivide.py +274 -0
- snappy/drilling/test.py +23 -0
- snappy/drilling/test_cases.py +132 -0
- snappy/drilling/tracing.py +351 -0
- snappy/exceptions.py +26 -0
- snappy/export_stl.py +120 -0
- snappy/exterior_to_link/__init__.py +2 -0
- snappy/exterior_to_link/barycentric_geometry.py +463 -0
- snappy/exterior_to_link/exceptions.py +6 -0
- snappy/exterior_to_link/geodesic_map.json +14408 -0
- snappy/exterior_to_link/hyp_utils.py +112 -0
- snappy/exterior_to_link/link_projection.py +323 -0
- snappy/exterior_to_link/main.py +198 -0
- snappy/exterior_to_link/mcomplex_with_expansion.py +261 -0
- snappy/exterior_to_link/mcomplex_with_link.py +687 -0
- snappy/exterior_to_link/mcomplex_with_memory.py +162 -0
- snappy/exterior_to_link/pl_utils.py +491 -0
- snappy/exterior_to_link/put_in_S3.py +156 -0
- snappy/exterior_to_link/rational_linear_algebra.py +130 -0
- snappy/exterior_to_link/rational_linear_algebra_wrapped.py +135 -0
- snappy/exterior_to_link/simplify_to_base_tri.py +114 -0
- snappy/exterior_to_link/stored_moves.py +475 -0
- snappy/exterior_to_link/test.py +31 -0
- snappy/filedialog.py +28 -0
- 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 +691 -0
- snappy/geometric_structure/cusp_neighborhood/cusp_cross_section_base.py +480 -0
- snappy/geometric_structure/cusp_neighborhood/exceptions.py +41 -0
- snappy/geometric_structure/cusp_neighborhood/real_cusp_cross_section.py +294 -0
- snappy/geometric_structure/cusp_neighborhood/tiles_for_cusp_neighborhood.py +156 -0
- snappy/geometric_structure/cusp_neighborhood/vertices.py +35 -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_representatives.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/geometric_structure/geodesic/fixed_points.py +106 -0
- snappy/geometric_structure/geodesic/geodesic_start_point_info.py +435 -0
- 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 +128 -0
- snappy/geometric_structure/test.py +22 -0
- snappy/gui.py +121 -0
- snappy/horoviewer.py +443 -0
- snappy/hyperboloid/__init__.py +212 -0
- snappy/hyperboloid/distances.py +259 -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/info_icon.gif +0 -0
- snappy/infowindow.py +65 -0
- snappy/isometry_signature.py +389 -0
- snappy/len_spec/__init__.py +609 -0
- snappy/len_spec/geodesic_info.py +129 -0
- snappy/len_spec/geodesic_key_info_dict.py +116 -0
- snappy/len_spec/geodesic_piece.py +146 -0
- snappy/len_spec/geometric_structure.py +182 -0
- snappy/len_spec/geometry.py +136 -0
- snappy/len_spec/length_spectrum_geodesic_info.py +185 -0
- snappy/len_spec/spine.py +128 -0
- snappy/len_spec/test.py +24 -0
- snappy/len_spec/test_cases.py +69 -0
- snappy/len_spec/tile.py +276 -0
- snappy/len_spec/word.py +86 -0
- snappy/manifolds/HTWKnots/alternating.gz +0 -0
- snappy/manifolds/HTWKnots/nonalternating.gz +0 -0
- snappy/manifolds/__init__.py +3 -0
- snappy/margulis/__init__.py +332 -0
- snappy/margulis/cusp_neighborhood_neighborhood.py +66 -0
- snappy/margulis/geodesic_neighborhood.py +152 -0
- snappy/margulis/margulis_info.py +21 -0
- snappy/margulis/mu_from_neighborhood_pair.py +175 -0
- snappy/margulis/neighborhood.py +29 -0
- snappy/margulis/test.py +22 -0
- snappy/math_basics.py +187 -0
- snappy/matrix.py +525 -0
- snappy/number.py +657 -0
- snappy/numeric_output_checker.py +345 -0
- snappy/pari.py +41 -0
- snappy/phone_home.py +57 -0
- snappy/polyviewer.py +259 -0
- snappy/ptolemy/__init__.py +17 -0
- snappy/ptolemy/component.py +103 -0
- snappy/ptolemy/coordinates.py +2290 -0
- snappy/ptolemy/fieldExtensions.py +153 -0
- snappy/ptolemy/findLoops.py +473 -0
- snappy/ptolemy/geometricRep.py +59 -0
- snappy/ptolemy/homology.py +165 -0
- snappy/ptolemy/magma/default.magma_template +229 -0
- snappy/ptolemy/magma/radicalsOfPrimaryDecomposition.magma_template +79 -0
- snappy/ptolemy/manifoldMethods.py +395 -0
- snappy/ptolemy/matrix.py +350 -0
- snappy/ptolemy/numericalSolutionsToGroebnerBasis.py +113 -0
- snappy/ptolemy/polynomial.py +856 -0
- snappy/ptolemy/processComponents.py +173 -0
- snappy/ptolemy/processFileBase.py +247 -0
- snappy/ptolemy/processFileDispatch.py +46 -0
- snappy/ptolemy/processMagmaFile.py +392 -0
- snappy/ptolemy/processRurFile.py +150 -0
- snappy/ptolemy/ptolemyGeneralizedObstructionClass.py +102 -0
- snappy/ptolemy/ptolemyObstructionClass.py +64 -0
- snappy/ptolemy/ptolemyVariety.py +995 -0
- snappy/ptolemy/ptolemyVarietyPrimeIdealGroebnerBasis.py +140 -0
- snappy/ptolemy/reginaWrapper.py +698 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/regina_testing_files_generalized/m015__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/rur.py +545 -0
- snappy/ptolemy/solutionsToPrimeIdealGroebnerBasis.py +277 -0
- snappy/ptolemy/test.py +1126 -0
- snappy/ptolemy/testing_files/3_1__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/3_1__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl4_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/4_1__sl4_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/5_2__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/5_2__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/DT_mcbbiceaibjklmdfgh__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c0.magma_out +95 -0
- snappy/ptolemy/testing_files/data/pgl2/OrientableCuspedCensus/03_tetrahedra/m019__sl2_c1.magma_out +95 -0
- snappy/ptolemy/testing_files/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c4.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c5.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c6.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/m135__sl2_c7.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/s000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/s000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/t00000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/t00000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c2.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files/v0000__sl2_c3.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m003__sl3_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m004__sl2_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m004__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m015__sl2_c1.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_generalized/m015__sl3_c0.magma_out.bz2 +0 -0
- snappy/ptolemy/testing_files_rur/m052__sl3_c0.rur.bz2 +0 -0
- snappy/ptolemy/utilities.py +236 -0
- 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 +197 -0
- snappy/raytracing/eyeball.py +124 -0
- snappy/raytracing/finite_raytracing_data.py +237 -0
- snappy/raytracing/finite_viewer.py +590 -0
- snappy/raytracing/geodesic_tube_info.py +174 -0
- snappy/raytracing/geodesics.py +246 -0
- snappy/raytracing/geodesics_window.py +258 -0
- snappy/raytracing/gui_utilities.py +293 -0
- snappy/raytracing/hyperboloid_navigation.py +556 -0
- snappy/raytracing/hyperboloid_utilities.py +234 -0
- snappy/raytracing/ideal_raytracing_data.py +592 -0
- snappy/raytracing/inside_viewer.py +974 -0
- snappy/raytracing/pack.py +22 -0
- snappy/raytracing/raytracing_data.py +126 -0
- snappy/raytracing/raytracing_view.py +454 -0
- snappy/raytracing/shaders/Eye.png +0 -0
- snappy/raytracing/shaders/NonGeometric.png +0 -0
- snappy/raytracing/shaders/__init__.py +101 -0
- snappy/raytracing/shaders/fragment.glsl +1744 -0
- snappy/raytracing/test.py +29 -0
- snappy/raytracing/tooltip.py +146 -0
- snappy/raytracing/upper_halfspace_utilities.py +98 -0
- snappy/raytracing/view_scale_controller.py +98 -0
- snappy/raytracing/zoom_slider/__init__.py +263 -0
- snappy/raytracing/zoom_slider/inward.png +0 -0
- snappy/raytracing/zoom_slider/inward18.png +0 -0
- snappy/raytracing/zoom_slider/outward.png +0 -0
- snappy/raytracing/zoom_slider/outward18.png +0 -0
- snappy/raytracing/zoom_slider/test.py +20 -0
- snappy/sage_helper.py +119 -0
- snappy/settings.py +407 -0
- snappy/shell.py +53 -0
- snappy/snap/__init__.py +117 -0
- snappy/snap/character_varieties.py +375 -0
- snappy/snap/find_field.py +372 -0
- snappy/snap/fox_milnor.py +271 -0
- snappy/snap/fundamental_polyhedron.py +569 -0
- snappy/snap/generators.py +39 -0
- snappy/snap/interval_reps.py +81 -0
- snappy/snap/kernel_structures.py +128 -0
- snappy/snap/mcomplex_base.py +18 -0
- snappy/snap/nsagetools.py +716 -0
- snappy/snap/peripheral/__init__.py +1 -0
- snappy/snap/peripheral/dual_cellulation.py +219 -0
- snappy/snap/peripheral/link.py +127 -0
- snappy/snap/peripheral/peripheral.py +159 -0
- snappy/snap/peripheral/surface.py +522 -0
- snappy/snap/peripheral/test.py +35 -0
- snappy/snap/polished_reps.py +335 -0
- snappy/snap/shapes.py +152 -0
- snappy/snap/slice_obs_HKL/__init__.py +194 -0
- snappy/snap/slice_obs_HKL/basics.py +236 -0
- snappy/snap/slice_obs_HKL/direct.py +217 -0
- snappy/snap/slice_obs_HKL/poly_norm.py +212 -0
- snappy/snap/slice_obs_HKL/rep_theory.py +424 -0
- snappy/snap/t3mlite/__init__.py +2 -0
- snappy/snap/t3mlite/arrow.py +243 -0
- snappy/snap/t3mlite/corner.py +22 -0
- snappy/snap/t3mlite/edge.py +172 -0
- snappy/snap/t3mlite/face.py +37 -0
- snappy/snap/t3mlite/files.py +211 -0
- snappy/snap/t3mlite/homology.py +53 -0
- snappy/snap/t3mlite/linalg.py +419 -0
- snappy/snap/t3mlite/mcomplex.py +1499 -0
- snappy/snap/t3mlite/perm4.py +320 -0
- snappy/snap/t3mlite/setup.py +12 -0
- snappy/snap/t3mlite/simplex.py +199 -0
- snappy/snap/t3mlite/spun.py +297 -0
- snappy/snap/t3mlite/surface.py +519 -0
- snappy/snap/t3mlite/test.py +20 -0
- snappy/snap/t3mlite/test_vs_regina.py +86 -0
- snappy/snap/t3mlite/tetrahedron.py +109 -0
- snappy/snap/t3mlite/vertex.py +42 -0
- snappy/snap/test.py +139 -0
- snappy/snap/utilities.py +288 -0
- snappy/test.py +213 -0
- snappy/test_cases.py +263 -0
- snappy/testing.py +131 -0
- snappy/tiling/__init__.py +2 -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/quotient_dict.py +70 -0
- snappy/tiling/real_hash_dict.py +164 -0
- snappy/tiling/test.py +23 -0
- snappy/tiling/tile.py +224 -0
- snappy/tiling/triangle.py +33 -0
- snappy/tkterminal.py +920 -0
- snappy/twister/__init__.py +20 -0
- snappy/twister/main.py +646 -0
- snappy/twister/surfaces/S_0_1 +3 -0
- snappy/twister/surfaces/S_0_2 +3 -0
- snappy/twister/surfaces/S_0_4 +7 -0
- snappy/twister/surfaces/S_0_4_Lantern +8 -0
- snappy/twister/surfaces/S_1 +3 -0
- snappy/twister/surfaces/S_1_1 +4 -0
- snappy/twister/surfaces/S_1_2 +5 -0
- snappy/twister/surfaces/S_1_2_5 +6 -0
- snappy/twister/surfaces/S_2 +6 -0
- snappy/twister/surfaces/S_2_1 +8 -0
- snappy/twister/surfaces/S_2_heeg +10 -0
- snappy/twister/surfaces/S_3 +8 -0
- snappy/twister/surfaces/S_3_1 +10 -0
- snappy/twister/surfaces/S_4_1 +12 -0
- snappy/twister/surfaces/S_5_1 +14 -0
- snappy/twister/surfaces/heeg_fig8 +9 -0
- snappy/twister/twister_core.cpython-310-aarch64-linux-gnu.so +0 -0
- snappy/upper_halfspace/__init__.py +146 -0
- snappy/upper_halfspace/ideal_point.py +29 -0
- snappy/verify/__init__.py +13 -0
- snappy/verify/canonical.py +542 -0
- snappy/verify/complex_volume/__init__.py +18 -0
- snappy/verify/complex_volume/adjust_torsion.py +86 -0
- snappy/verify/complex_volume/closed.py +168 -0
- snappy/verify/complex_volume/compute_ptolemys.py +90 -0
- snappy/verify/complex_volume/cusped.py +56 -0
- snappy/verify/complex_volume/extended_bloch.py +201 -0
- snappy/verify/cusp_translations.py +85 -0
- snappy/verify/edge_equations.py +80 -0
- snappy/verify/exceptions.py +254 -0
- snappy/verify/hyperbolicity.py +224 -0
- snappy/verify/interval_newton_shapes_engine.py +523 -0
- snappy/verify/interval_tree.py +400 -0
- snappy/verify/krawczyk_shapes_engine.py +518 -0
- snappy/verify/real_algebra.py +286 -0
- snappy/verify/shapes.py +25 -0
- snappy/verify/square_extensions.py +1005 -0
- snappy/verify/test.py +72 -0
- snappy/verify/volume.py +128 -0
- snappy/version.py +2 -0
- snappy-3.3.dist-info/METADATA +58 -0
- snappy-3.3.dist-info/RECORD +541 -0
- snappy-3.3.dist-info/WHEEL +6 -0
- snappy-3.3.dist-info/entry_points.txt +2 -0
- snappy-3.3.dist-info/top_level.txt +28 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
There are two Python interfaces to PHCpack, but both require
|
|
3
|
+
compiling from the Ada source, which is often a pain.
|
|
4
|
+
|
|
5
|
+
This file implements a simple wrapper for the command-line version of
|
|
6
|
+
PHCpack.
|
|
7
|
+
|
|
8
|
+
>>> from sage.all import RealField, ComplexField, QQ, PolynomialRing
|
|
9
|
+
>>> R = PolynomialRing(QQ, ['x', 'y', 'e'])
|
|
10
|
+
>>> I = R.ideal([R('x^2 + y^2 - 1'), R('(x - 1/2)^2 + y^2 - 1'), R('x*y*e - 1')])
|
|
11
|
+
>>> I.dimension()
|
|
12
|
+
0
|
|
13
|
+
>>> abs(find_solutions(I, 2)[0]['coors']['y'])
|
|
14
|
+
0.9682458365518542212948163499456
|
|
15
|
+
|
|
16
|
+
However, the black-box solver applies more root-counts than just using
|
|
17
|
+
CyPHC, sometimes slowing things down by a factor of 10 or more.
|
|
18
|
+
Moreover, CyPHC uses root counts geared toward finding solutions with
|
|
19
|
+
all coordinates non-zero, which is what we often want anyway.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import re, sys, os, tempfile, json
|
|
23
|
+
replacements = [('i', 3*'xX'), ('I', 3*'Xx'), ('e', 3*'yY'), ('E', 3*'Yy'),
|
|
24
|
+
('t', 3*'Mm'), ('m', 3*'Mm')]
|
|
25
|
+
|
|
26
|
+
def remove_forbidden(poly_str):
|
|
27
|
+
"""
|
|
28
|
+
PHCpack doesn't allow variables with {i, I, e, E} in the name.
|
|
29
|
+
Also, with the phcpy interface, it uses "m" for the multiplicity
|
|
30
|
+
and "t" for the homotopy parameter.
|
|
31
|
+
"""
|
|
32
|
+
for bad, replacement in replacements:
|
|
33
|
+
poly_str = poly_str.replace(bad, replacement)
|
|
34
|
+
return poly_str
|
|
35
|
+
|
|
36
|
+
def restore_forbidden(var_str):
|
|
37
|
+
for bad, replacement in replacements:
|
|
38
|
+
var_str = var_str.replace(replacement, bad)
|
|
39
|
+
return var_str
|
|
40
|
+
|
|
41
|
+
def ideal_to_file(ideal, filename):
|
|
42
|
+
outfile = open(filename, 'w')
|
|
43
|
+
polys = ideal.gens()
|
|
44
|
+
outfile.write(r'%d\n' % len(polys))
|
|
45
|
+
for p in polys:
|
|
46
|
+
outfile.write(' ' + remove_forbidden(repr(p)) + r';\n')
|
|
47
|
+
outfile.close()
|
|
48
|
+
|
|
49
|
+
def parse_file(filename, prec=53):
|
|
50
|
+
from sage.all import RealField, ComplexField
|
|
51
|
+
RR = RealField(prec)
|
|
52
|
+
CC = ComplexField(prec)
|
|
53
|
+
data = open(filename).read()
|
|
54
|
+
open('polys.txt', 'w').write(data)
|
|
55
|
+
data = data.split('THE SOLUTIONS')[-1]
|
|
56
|
+
data = re.subn('[*]{3,}', '', data)[0]
|
|
57
|
+
ans = []
|
|
58
|
+
solutions = re.findall(r'(solution \d+ : \s* start residual .*?) ==', data, re.DOTALL)
|
|
59
|
+
for sol in solutions:
|
|
60
|
+
kind = sol.split('=')[-1].strip()
|
|
61
|
+
if kind == 'no solution':
|
|
62
|
+
continue
|
|
63
|
+
mult = int(re.search(r'^m : (\d+)', sol, re.MULTILINE).group(1))
|
|
64
|
+
err = float(re.search(r'== err :\s+(.*?)\s+= ', sol).group(1))
|
|
65
|
+
coors = re.findall(r'^ (.*) :\s+(\S*)\s+(\S*)', sol, re.MULTILINE)
|
|
66
|
+
if kind.startswith('real'):
|
|
67
|
+
coors = {restore_forbidden(var):RR(real) for var, real, imag in coors}
|
|
68
|
+
ans.append({'kind':'real', 'mult':mult, 'err':err, 'coors':coors})
|
|
69
|
+
elif kind.startswith('complex'):
|
|
70
|
+
coors = {restore_forbidden(var):CC(RR(real), RR(imag)) for var, real, imag in coors}
|
|
71
|
+
ans.append({'kind':'complex', 'mult':mult, 'err':err, 'coors':coors})
|
|
72
|
+
|
|
73
|
+
num_real = int(re.search(r'Number of real solutions\s+:\s(.*).', data).group(1))
|
|
74
|
+
num_complex = int(re.search(r'Number of complex solutions\s+:\s(.*).', data).group(1))
|
|
75
|
+
kinds = [sol['kind'] for sol in ans]
|
|
76
|
+
assert kinds.count('real') == num_real
|
|
77
|
+
assert kinds.count('complex') == num_complex
|
|
78
|
+
return ans
|
|
79
|
+
|
|
80
|
+
def find_solutions(ideal, doubles=1):
|
|
81
|
+
assert doubles in [1, 2, 4]
|
|
82
|
+
prec = 53*doubles
|
|
83
|
+
tmpdir = tempfile.mkdtemp()
|
|
84
|
+
infile = tmpdir + os.sep + 'polys.txt'
|
|
85
|
+
outfile = tmpdir + os.sep + 'out.txt'
|
|
86
|
+
ideal_to_file(ideal, tmpdir + os.sep + 'polys.txt')
|
|
87
|
+
flag = {1:'-b', 2:'-b2', 4:'-b4'}[doubles]
|
|
88
|
+
os.system('phc ' + flag + ' ' + infile + ' ' + outfile)
|
|
89
|
+
ans = parse_file(outfile, prec)
|
|
90
|
+
os.system('rm -rf tmpdir')
|
|
91
|
+
return ans
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def clean_complex(z, epsilon=1e-14):
|
|
95
|
+
r, i = abs(z.real), abs(z.imag)
|
|
96
|
+
if r < epsilon and i < epsilon:
|
|
97
|
+
ans = 0.0
|
|
98
|
+
elif r < epsilon:
|
|
99
|
+
ans = z.imag*1j
|
|
100
|
+
elif i < epsilon:
|
|
101
|
+
ans = z.real
|
|
102
|
+
else:
|
|
103
|
+
ans = z
|
|
104
|
+
assert abs(z - ans) < 1.5*epsilon
|
|
105
|
+
return ans
|
|
106
|
+
|
|
107
|
+
def sol_to_dict(sol, vars):
|
|
108
|
+
ans = {v:clean_complex(p) for v, p in zip(vars, sol.point)}
|
|
109
|
+
for attr in ['err', 'rco', 'res', 'mult']:
|
|
110
|
+
ans[attr] = getattr(sol, attr)
|
|
111
|
+
return ans
|
|
112
|
+
|
|
113
|
+
def serialize_sol_dict(sol):
|
|
114
|
+
sol = sol.copy()
|
|
115
|
+
for key, val in sol.items():
|
|
116
|
+
if isinstance(val, complex):
|
|
117
|
+
sol[key] = (val.real, val.imag)
|
|
118
|
+
return sol
|
|
119
|
+
|
|
120
|
+
def phc_direct_base(var_names, eqns_as_strings):
|
|
121
|
+
import cyphc
|
|
122
|
+
mangled_vars = [remove_forbidden(v) for v in var_names]
|
|
123
|
+
R = cyphc.PolyRing(mangled_vars)
|
|
124
|
+
polys = [cyphc.PHCPoly(R, remove_forbidden(eqn)) for eqn in eqns_as_strings]
|
|
125
|
+
system = cyphc.PHCSystem(R, polys)
|
|
126
|
+
sols = system.solution_list()
|
|
127
|
+
return [sol_to_dict(sol, var_names) for sol in sols]
|
|
128
|
+
|
|
129
|
+
def phc_direct(ideal):
|
|
130
|
+
vars = ideal.ring().variable_names()
|
|
131
|
+
eqns = [repr(p) for p in ideal.gens()]
|
|
132
|
+
return phc_direct_base(vars, eqns)
|
|
133
|
+
|
|
134
|
+
def phcpy_direct_base(var_names, eqns_as_strings,
|
|
135
|
+
tasks=0, precision='d', tolerance=1e-6):
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
Use the official PHCPack Python interface to find solutions to the
|
|
139
|
+
given equations. To duplicate the behavior of CyPHC, we filter out
|
|
140
|
+
any solutions with a coordinate too close to zero or infinity, as
|
|
141
|
+
determined by the tolerance parameter. It's not clear to me why
|
|
142
|
+
this is needed since I believe phcpy is calling the Laurent
|
|
143
|
+
polynomial version of it's solver, which presumably assumes this,
|
|
144
|
+
but...
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
import phcpy
|
|
148
|
+
polys = [remove_forbidden(eqn) + ';' for eqn in eqns_as_strings]
|
|
149
|
+
sols = phcpy.solver.solve(polys, verbose=False, tasks=tasks,
|
|
150
|
+
precision=precision, checkin=True)
|
|
151
|
+
ans = []
|
|
152
|
+
for sol in sols:
|
|
153
|
+
if sol.find('NaN******') > -1:
|
|
154
|
+
continue
|
|
155
|
+
sol = phcpy.solutions.strsol2dict(sol)
|
|
156
|
+
good_sol = True
|
|
157
|
+
sol['mult'] = sol['m']
|
|
158
|
+
sol.pop('m')
|
|
159
|
+
sol['t_hom_val'] = sol['t']
|
|
160
|
+
sol.pop('t')
|
|
161
|
+
for v in var_names:
|
|
162
|
+
w = remove_forbidden(v)
|
|
163
|
+
if v != w:
|
|
164
|
+
sol[v] = sol[w]
|
|
165
|
+
sol.pop(w)
|
|
166
|
+
sol[v] = clean_complex(sol[v])
|
|
167
|
+
size = abs(sol[v])
|
|
168
|
+
if size < tolerance or size > 1.0/tolerance:
|
|
169
|
+
good_sol = False
|
|
170
|
+
if good_sol:
|
|
171
|
+
ans.append(sol)
|
|
172
|
+
return ans
|
|
173
|
+
|
|
174
|
+
def phcpy_direct(ideal, tasks=0, precision='d'):
|
|
175
|
+
vars = ideal.ring().variable_names()
|
|
176
|
+
eqns = [repr(p) for p in ideal.gens()]
|
|
177
|
+
return phcpy_direct_base(vars, eqns, tasks=tasks, precision=precision)
|
|
178
|
+
|
|
179
|
+
def direct_hack(backend, ideal, **kwargs):
|
|
180
|
+
"""
|
|
181
|
+
To avoid memory leaks and random PARI crashes, runs CyPHC
|
|
182
|
+
in a separate subprocess.
|
|
183
|
+
"""
|
|
184
|
+
vars = ideal.ring().variable_names()
|
|
185
|
+
polys = [repr(eqn) for eqn in ideal.gens()]
|
|
186
|
+
|
|
187
|
+
data = {'backend':backend, 'vars':vars, 'polys':polys, 'kwargs':kwargs}
|
|
188
|
+
problem_data = json.dumps(data).encode('base64').replace(r'\n', '')
|
|
189
|
+
ans_data = os.popen('sage -python ' + __file__ + ' ' + problem_data).read()
|
|
190
|
+
ans_data = re.sub(r'PHCv.*? released .*? works!\n', '', ans_data)
|
|
191
|
+
if len(ans_data):
|
|
192
|
+
ans = json.loads(ans_data)
|
|
193
|
+
for sol in ans:
|
|
194
|
+
for key, val in sol.items():
|
|
195
|
+
if isinstance(val, list):
|
|
196
|
+
sol[key] = complex(*val)
|
|
197
|
+
else:
|
|
198
|
+
ans = []
|
|
199
|
+
return ans
|
|
200
|
+
|
|
201
|
+
def phc_direct_hack(ideal):
|
|
202
|
+
return direct_hack('cyphc', ideal)
|
|
203
|
+
|
|
204
|
+
def phcpy_direct_hack(ideal, **kwargs):
|
|
205
|
+
return direct_hack('phcpy', ideal, **kwargs)
|
|
206
|
+
|
|
207
|
+
def execute_hack():
|
|
208
|
+
data = json.loads(sys.argv[1].decode('base64'))
|
|
209
|
+
if data['backend'] == 'cyphc':
|
|
210
|
+
solver = phc_direct_base
|
|
211
|
+
elif data['backend'] == 'phcpy':
|
|
212
|
+
solver = phcpy_direct_base
|
|
213
|
+
else:
|
|
214
|
+
raise ValueError('nonexistent backend specified')
|
|
215
|
+
sols = [serialize_sol_dict(sol) for sol in
|
|
216
|
+
solver(data['vars'], data['polys'], **data['kwargs'])]
|
|
217
|
+
sys.stdout.write(json.dumps(sols))
|
|
218
|
+
|
|
219
|
+
if __name__ == '__main__':
|
|
220
|
+
execute_hack()
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from snappy.dev.extended_ptolemy import extended
|
|
2
|
+
from snappy.dev.extended_ptolemy import giac_rur
|
|
3
|
+
from snappy.dev.extended_ptolemy.complexVolumesClosed import evaluate_at_roots
|
|
4
|
+
from snappy.ptolemy.coordinates import PtolemyCoordinates, CrossRatios
|
|
5
|
+
|
|
6
|
+
def compute_ptolemy_from_solution(I, solution, dict_value):
|
|
7
|
+
sign, m_count, l_count, name = dict_value
|
|
8
|
+
|
|
9
|
+
R = I.ring()
|
|
10
|
+
|
|
11
|
+
m = solution[R('M')] if m_count >= 0 else solution[R('m')]
|
|
12
|
+
l = solution[R('L')] if l_count >= 0 else solution[R('l')]
|
|
13
|
+
|
|
14
|
+
return sign * m ** abs(m_count) * l ** abs(l_count) * solution[R(name)]
|
|
15
|
+
|
|
16
|
+
def extended_ptolemy_solutions(M):
|
|
17
|
+
I, full_var_dict = extended.ptolemy_ideal_for_filled(
|
|
18
|
+
M, return_full_var_dict = 'data', notation = 'full')
|
|
19
|
+
|
|
20
|
+
rur = giac_rur.rational_univariate_representation(I)
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
(nf, { key : compute_ptolemy_from_solution(I, solution, value)
|
|
24
|
+
for key, value in full_var_dict.items() },
|
|
25
|
+
multiplicity)
|
|
26
|
+
for nf, solution, multiplicity in rur ]
|
|
27
|
+
|
|
28
|
+
def ptolemy_coordinates(M):
|
|
29
|
+
return [
|
|
30
|
+
PtolemyCoordinates(d, is_numerical = False,
|
|
31
|
+
manifold_thunk = lambda : M)
|
|
32
|
+
for nf, d, multiplicity
|
|
33
|
+
in extended_ptolemy_solutions(M) ]
|
|
34
|
+
|
|
35
|
+
def cross_ratios(M):
|
|
36
|
+
return [
|
|
37
|
+
ptolemys.cross_ratios()
|
|
38
|
+
for ptolemys
|
|
39
|
+
in ptolemy_coordinates(M) ]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if __name__ == '__main__':
|
|
43
|
+
from snappy import Manifold
|
|
44
|
+
import sys
|
|
45
|
+
|
|
46
|
+
if len(sys.argv) != 2:
|
|
47
|
+
print("Usage: sage -python printMatrices.py CLOSED_MFD")
|
|
48
|
+
print()
|
|
49
|
+
print('Example: sage -python printMatrices.py "m004(2,3)"')
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
M = Manifold(sys.argv[1])
|
|
53
|
+
G = M.fundamental_group()
|
|
54
|
+
list_z = cross_ratios(M)
|
|
55
|
+
for i, z in enumerate(list_z):
|
|
56
|
+
print("Solution %d:" % i)
|
|
57
|
+
nf = z['z_0000_0'].parent()
|
|
58
|
+
print(" Number field:", nf.defining_polynomial())
|
|
59
|
+
print(" Exact values:")
|
|
60
|
+
for g in G.generators():
|
|
61
|
+
print(" Generator %s:" % g)
|
|
62
|
+
print(z.evaluate_word(g, G))
|
|
63
|
+
|
|
64
|
+
for z_numerical in evaluate_at_roots(nf, z, precision = 100):
|
|
65
|
+
print(" Numerical values:")
|
|
66
|
+
d = CrossRatios(z_numerical, manifold_thunk = lambda : M)
|
|
67
|
+
|
|
68
|
+
for g in G.generators():
|
|
69
|
+
print(" Generator %s:" % g)
|
|
70
|
+
print(d.evaluate_word(g, G))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .computeVerifiedHyperbolicStructure import *
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from .hyperbolicStructure import *
|
|
2
|
+
from .verificationError import *
|
|
3
|
+
|
|
4
|
+
from sage.all import RDF, pi, matrix, block_matrix, vector
|
|
5
|
+
|
|
6
|
+
__all__ = ['compute_approx_hyperbolic_structure_new']
|
|
7
|
+
|
|
8
|
+
# Constants
|
|
9
|
+
|
|
10
|
+
_singular_epsilon = RDF(1e-4)
|
|
11
|
+
_theta_thres = RDF(2.6)
|
|
12
|
+
_theta_max = RDF(2.5)
|
|
13
|
+
_two_pi = RDF(2*pi)
|
|
14
|
+
_start_edge_param = RDF(-2)
|
|
15
|
+
|
|
16
|
+
_iteration_stop = RDF(1e-12)
|
|
17
|
+
|
|
18
|
+
def _pseudo_inverse(m, verbose = False):
|
|
19
|
+
global _singular_epsilon
|
|
20
|
+
|
|
21
|
+
u, d, v = m.SVD()
|
|
22
|
+
|
|
23
|
+
dims = d.dimensions()
|
|
24
|
+
dQuasiInverse = matrix(RDF, dims[1], dims[0])
|
|
25
|
+
|
|
26
|
+
rank = 0
|
|
27
|
+
|
|
28
|
+
for i in range(min(dims)):
|
|
29
|
+
if abs(d[i,i]) > _singular_epsilon:
|
|
30
|
+
dQuasiInverse[i,i] = 1.0 / d[i,i]
|
|
31
|
+
rank += 1
|
|
32
|
+
|
|
33
|
+
if verbose:
|
|
34
|
+
print("Rank: %d" % rank)
|
|
35
|
+
|
|
36
|
+
return v * dQuasiInverse * u.transpose()
|
|
37
|
+
|
|
38
|
+
def _large_angle_penalties_and_derivatives(hyperbolicStructure,
|
|
39
|
+
verbose = False):
|
|
40
|
+
global _theta_thres
|
|
41
|
+
global _theta_max
|
|
42
|
+
|
|
43
|
+
penalties = []
|
|
44
|
+
penalty_derivatives = []
|
|
45
|
+
|
|
46
|
+
number_large_angles = 0
|
|
47
|
+
max_angle = 0
|
|
48
|
+
|
|
49
|
+
for tet, m in enumerate(hyperbolicStructure.dihedral_angles):
|
|
50
|
+
for j in range(1, 4):
|
|
51
|
+
for i in range(0, j):
|
|
52
|
+
if m[i][j] > _theta_thres:
|
|
53
|
+
penalties.append(m[i][j] - _theta_max)
|
|
54
|
+
penalty_derivatives.append(
|
|
55
|
+
hyperbolicStructure.derivative_of_single_dihedral_angle(
|
|
56
|
+
tet, i, j))
|
|
57
|
+
|
|
58
|
+
number_large_angles += 1
|
|
59
|
+
if m[i][j] > max_angle:
|
|
60
|
+
max_angle = m[i][j]
|
|
61
|
+
|
|
62
|
+
if verbose:
|
|
63
|
+
print("Number of large angles: %d, Maximum: %f" % (
|
|
64
|
+
number_large_angles, max_angle))
|
|
65
|
+
|
|
66
|
+
return penalties, penalty_derivatives
|
|
67
|
+
|
|
68
|
+
def _compute_errors_with_norm(hyperbolicStructure):
|
|
69
|
+
global _two_pi
|
|
70
|
+
errors = [ angle - _two_pi for angle in hyperbolicStructure.angle_sums ]
|
|
71
|
+
return errors, vector(errors).norm()
|
|
72
|
+
|
|
73
|
+
def _adaptive_newton_step(hyperbolicStructure, errors_with_norm, verbose = False):
|
|
74
|
+
|
|
75
|
+
errors, errors_norm = errors_with_norm
|
|
76
|
+
|
|
77
|
+
num_edges = len(hyperbolicStructure.mcomplex.Edges)
|
|
78
|
+
|
|
79
|
+
penalties, penalty_derivative = _large_angle_penalties_and_derivatives(
|
|
80
|
+
hyperbolicStructure, verbose = verbose)
|
|
81
|
+
|
|
82
|
+
all_errors = vector(errors + penalties)
|
|
83
|
+
|
|
84
|
+
jacobian = hyperbolicStructure.jacobian()
|
|
85
|
+
penalty_derivative_matrix = matrix(
|
|
86
|
+
RDF, penalty_derivative, ncols = num_edges)
|
|
87
|
+
|
|
88
|
+
m = block_matrix(
|
|
89
|
+
[[jacobian],
|
|
90
|
+
[penalty_derivative_matrix]])
|
|
91
|
+
|
|
92
|
+
mInv = _pseudo_inverse(m, verbose = verbose)
|
|
93
|
+
mInvErrs = mInv * all_errors
|
|
94
|
+
|
|
95
|
+
for i in range(14):
|
|
96
|
+
step_size = RDF(0.5) ** i
|
|
97
|
+
|
|
98
|
+
new_edge_params = list(
|
|
99
|
+
vector(hyperbolicStructure.edge_lengths) - step_size * mInvErrs)
|
|
100
|
+
try:
|
|
101
|
+
newHyperbolicStructure = HyperbolicStructure(
|
|
102
|
+
hyperbolicStructure.mcomplex, new_edge_params)
|
|
103
|
+
except BadDihedralAngleError:
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
new_errors_with_norm = _compute_errors_with_norm(
|
|
107
|
+
newHyperbolicStructure)
|
|
108
|
+
|
|
109
|
+
if new_errors_with_norm[1] < errors_norm:
|
|
110
|
+
return (newHyperbolicStructure,
|
|
111
|
+
new_errors_with_norm)
|
|
112
|
+
|
|
113
|
+
raise NewtonStepError("Error Newton step")
|
|
114
|
+
|
|
115
|
+
def compute_approx_hyperbolic_structure_new(mcomplex, verbose = False):
|
|
116
|
+
"""
|
|
117
|
+
Finds unverified hyperbolic structure for an Mcomplex.
|
|
118
|
+
|
|
119
|
+
>>> from snappy import Triangulation
|
|
120
|
+
>>> from snappy.snap.t3mlite import Mcomplex
|
|
121
|
+
>>> isosig = 'uLLvLALLQPAPAMcbehgilknmkonpoqrqrsttxxuvcaiauxawkkutxhqqw'
|
|
122
|
+
>>> m = Mcomplex(Triangulation(isosig, remove_finite_vertices = False))
|
|
123
|
+
>>> h = compute_approx_hyperbolic_structure_new(m)
|
|
124
|
+
>>> all([ abs(s - _two_pi) < 1e-11 for s in h.angle_sums ])
|
|
125
|
+
True
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
global _start_edge_param
|
|
130
|
+
global _iteration_stop
|
|
131
|
+
|
|
132
|
+
edge_params = [
|
|
133
|
+
_start_edge_param for edge in mcomplex.Edges ]
|
|
134
|
+
|
|
135
|
+
hyperbolicStructure = HyperbolicStructure(
|
|
136
|
+
mcomplex, edge_params)
|
|
137
|
+
|
|
138
|
+
errors_with_norm = _compute_errors_with_norm(hyperbolicStructure)
|
|
139
|
+
|
|
140
|
+
for i in range(100):
|
|
141
|
+
|
|
142
|
+
if verbose:
|
|
143
|
+
print("Iteration: %d" % i)
|
|
144
|
+
|
|
145
|
+
hyperbolicStructure, errors_with_norm = (
|
|
146
|
+
_adaptive_newton_step(
|
|
147
|
+
hyperbolicStructure, errors_with_norm, verbose = verbose))
|
|
148
|
+
|
|
149
|
+
if max([abs(x) for x in errors_with_norm[0]]) < _iteration_stop:
|
|
150
|
+
return hyperbolicStructure
|
|
151
|
+
|
|
152
|
+
raise NewtonMethodConvergenceError("Error Newton convergence")
|
|
153
|
+
|
|
154
|
+
def _doctest():
|
|
155
|
+
import doctest
|
|
156
|
+
doctest.testmod()
|
|
157
|
+
|
|
158
|
+
if __name__ == '__main__':
|
|
159
|
+
_doctest()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from .parseVertexGramMatrixFile import *
|
|
2
|
+
from .verificationError import *
|
|
3
|
+
|
|
4
|
+
from .orb import __path__ as orb_path
|
|
5
|
+
|
|
6
|
+
from snappy.snap.t3mlite import Mcomplex
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import tempfile
|
|
10
|
+
import shutil
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
__all__ = ['compute_approx_hyperbolic_structure_orb']
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TmpDir():
|
|
17
|
+
"""
|
|
18
|
+
To be used in a with statement, creating a temporary
|
|
19
|
+
directory deleted at the end of the with statement.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, delete = True):
|
|
23
|
+
self.delete = delete
|
|
24
|
+
|
|
25
|
+
def __enter__(self):
|
|
26
|
+
self.path = tempfile.mkdtemp()
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def __exit__(self, type, value, traceback):
|
|
30
|
+
if self.delete:
|
|
31
|
+
shutil.rmtree(self.path)
|
|
32
|
+
|
|
33
|
+
def _compute_vertex_gram_matrix_file_orb(triangulation, tmpDir, verbose = False):
|
|
34
|
+
"""
|
|
35
|
+
Calls Orb to compute an approximate solution to the edge equation
|
|
36
|
+
for the given snappy.Triangulation (using directory at path tmpDir).
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Path for triangulation
|
|
40
|
+
basename = os.path.join(tmpDir, 'manifold.tri')
|
|
41
|
+
|
|
42
|
+
# Save triangulation to disk
|
|
43
|
+
triangulation.save(basename)
|
|
44
|
+
|
|
45
|
+
if verbose:
|
|
46
|
+
print("Calling orb to find approximate solution on", basename)
|
|
47
|
+
|
|
48
|
+
# Find Orb wrapper
|
|
49
|
+
path = os.path.join(orb_path[0], 'orb_solution_for_snappea_finite_triangulation_mac')
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
if verbose:
|
|
53
|
+
stdout = None
|
|
54
|
+
else:
|
|
55
|
+
# Redirect output to nirvana
|
|
56
|
+
stdout = open(os.devnull, 'w')
|
|
57
|
+
|
|
58
|
+
# Call Orb
|
|
59
|
+
ret = subprocess.call([path, basename], stdout = stdout)
|
|
60
|
+
except OSError as e:
|
|
61
|
+
raise OrbMissingError(
|
|
62
|
+
"Seems orb binary %s is missing. Original exception: %s" % (
|
|
63
|
+
path, e))
|
|
64
|
+
|
|
65
|
+
if ret != 0:
|
|
66
|
+
if ret <= 7:
|
|
67
|
+
raise OrbSolutionTypeError("Solution type orb found was", ret)
|
|
68
|
+
raise UnknownOrbFailureError("Exit code", ret)
|
|
69
|
+
|
|
70
|
+
# Path of output that Orb produced
|
|
71
|
+
return basename + '.vgm'
|
|
72
|
+
|
|
73
|
+
def compute_approx_hyperbolic_structure_orb(triangulation, verbose = False):
|
|
74
|
+
"""
|
|
75
|
+
Calls Orb to compute an approximate solution to the edge equation
|
|
76
|
+
for the given snappy.Triangulation.
|
|
77
|
+
|
|
78
|
+
The result is a veriClosed.HyperbolicStructure where the edge lengths
|
|
79
|
+
are in SageMath's RealDoubleField.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
# Input and output to Orb are in a temporary directory deleted
|
|
83
|
+
# at the end
|
|
84
|
+
with TmpDir() as tmp_dir:
|
|
85
|
+
# Invoke Orb to compute file containing vertex gram matrices
|
|
86
|
+
vgm_file_path = _compute_vertex_gram_matrix_file_orb(
|
|
87
|
+
triangulation, tmp_dir.path, verbose = verbose)
|
|
88
|
+
# Parse the file to obtain hyperbolic structure
|
|
89
|
+
return compute_approx_hyperbolic_structure_from_vertex_gram_matrix_file(
|
|
90
|
+
Mcomplex(triangulation), vgm_file_path)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from .computeApproxHyperbolicStructureNew import *
|
|
2
|
+
from .computeApproxHyperbolicStructureOrb import *
|
|
3
|
+
from .polishApproxHyperbolicStructure import *
|
|
4
|
+
from .krawczykCertifiedEdgeLengthsEngine import *
|
|
5
|
+
from .verifyHyperbolicStructureEngine import *
|
|
6
|
+
from .parseVertexGramMatrixFile import (
|
|
7
|
+
compute_approx_hyperbolic_structure_from_vertex_gram_matrix_file)
|
|
8
|
+
|
|
9
|
+
from snappy.snap.t3mlite import Mcomplex
|
|
10
|
+
|
|
11
|
+
def compute_unverified_hyperbolic_structure(triangulation,
|
|
12
|
+
source = 'new',
|
|
13
|
+
verbose = False):
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Given a snappy.Triangulation, computes an unverified hyperbolic structure,
|
|
17
|
+
i.e., an instance of HyperbolicStructure where the edge lengths are in
|
|
18
|
+
SageMath's RealDoubleField.
|
|
19
|
+
|
|
20
|
+
The optional argument source can be:
|
|
21
|
+
- 'new' to use the new python only implementation
|
|
22
|
+
- 'orb' to use Orb
|
|
23
|
+
- the path to a file containing vertex gram matrices as produced by
|
|
24
|
+
orb_solution_for_snappea_finite_triangulation
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
if source == 'new':
|
|
28
|
+
return compute_approx_hyperbolic_structure_new(
|
|
29
|
+
Mcomplex(triangulation),
|
|
30
|
+
verbose = verbose)
|
|
31
|
+
elif source == 'orb':
|
|
32
|
+
return compute_approx_hyperbolic_structure_orb(
|
|
33
|
+
triangulation)
|
|
34
|
+
else:
|
|
35
|
+
return compute_approx_hyperbolic_structure_from_vertex_gram_matrix_file(
|
|
36
|
+
Mcomplex(triangulation), source)
|
|
37
|
+
|
|
38
|
+
def compute_verified_hyperbolic_structure_from_approx_structure(
|
|
39
|
+
approx_hyperbolic_structure, bits_prec = 53, verbose = False):
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
Computes a verified hyperbolic structure given an instance of
|
|
43
|
+
HyperbolicStructure where the (unverified) edge lengths are in
|
|
44
|
+
SageMath's RealDoubleField or RealField.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Step I of the algorithm:
|
|
48
|
+
approx_hyperbolic_structure.pick_exact_and_var_edges()
|
|
49
|
+
|
|
50
|
+
# Use ordinary Newton method before Step II to obtain a higher
|
|
51
|
+
# precision solution
|
|
52
|
+
polished_hyperbolic_structure = polish_approx_hyperbolic_structure(
|
|
53
|
+
approx_hyperbolic_structure, bits_prec, verbose = verbose)
|
|
54
|
+
|
|
55
|
+
# Step II of the algorithm
|
|
56
|
+
K = KrawczykCertifiedEdgeLengthsEngine(
|
|
57
|
+
polished_hyperbolic_structure, bits_prec)
|
|
58
|
+
result = K.partially_verified_hyperbolic_structure()
|
|
59
|
+
|
|
60
|
+
# Step III-V of the algorithm
|
|
61
|
+
verify_engine = VerifyHyperbolicStructureEngine(result)
|
|
62
|
+
|
|
63
|
+
verify_engine.assert_verified_hyperbolic()
|
|
64
|
+
|
|
65
|
+
return result
|
|
66
|
+
|
|
67
|
+
def compute_verified_hyperbolic_structure(
|
|
68
|
+
triangulation, source = 'orb', bits_prec = 53, verbose = False):
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
Computes a verified hyperbolic structure given a snappy.Triangulation.
|
|
72
|
+
If all verification tests pass, the result is an instance of
|
|
73
|
+
HyperbolicStructure with edge lengths being SageMath's
|
|
74
|
+
RealIntervalField. Otherwise, raises an exception subclassed from
|
|
75
|
+
VerificationError.
|
|
76
|
+
|
|
77
|
+
The argument source specifies whether Orb ('orb') or a python-only
|
|
78
|
+
implementation ('new') to find the initial unverified hyperbolic
|
|
79
|
+
structure is used. It can also be a path to a vgm file containing
|
|
80
|
+
the vertex gram matrices.
|
|
81
|
+
|
|
82
|
+
The precision can be specified by the argument bits_prec.
|
|
83
|
+
|
|
84
|
+
>>> from snappy import Triangulation
|
|
85
|
+
>>> T = Triangulation('kLLLLPQkbcghihjijhjtsmnnnegufa', remove_finite_vertices = False)
|
|
86
|
+
>>> bool(compute_verified_hyperbolic_structure(T, source = 'orb'))
|
|
87
|
+
True
|
|
88
|
+
>>> bool(compute_verified_hyperbolic_structure(T, source = 'new'))
|
|
89
|
+
True
|
|
90
|
+
|
|
91
|
+
>>> from sage.all import RealIntervalField, pi
|
|
92
|
+
>>> RIF = RealIntervalField(212)
|
|
93
|
+
>>> two_pi = RIF(2*pi)
|
|
94
|
+
>>> h = compute_verified_hyperbolic_structure(T, source = 'orb', bits_prec = 212)
|
|
95
|
+
>>> max([abs(d - two_pi) for d in h.angle_sums]) < RIF(1e-55)
|
|
96
|
+
True
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
approx = compute_unverified_hyperbolic_structure(triangulation, source, verbose)
|
|
101
|
+
|
|
102
|
+
return compute_verified_hyperbolic_structure_from_approx_structure(
|
|
103
|
+
approx, bits_prec, verbose)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _doctest():
|
|
107
|
+
import doctest
|
|
108
|
+
doctest.testmod()
|
|
109
|
+
|
|
110
|
+
if __name__ == '__main__':
|
|
111
|
+
_doctest()
|