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,1005 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The square_extensions module provides
|
|
3
|
+
two special classes to give exact representations of the values
|
|
4
|
+
involved when computing a cusp cross section.
|
|
5
|
+
|
|
6
|
+
The method find_shapes_as_complex_sqrt_lin_combinations returns a list of
|
|
7
|
+
shapes as ComplexSqrtLinCombination's. This can be used as input to
|
|
8
|
+
CuspCrossSection. The outputs of CuspCrossSection, including the tilts, will
|
|
9
|
+
then be of type SqrtLinCombination.
|
|
10
|
+
|
|
11
|
+
Consider the real number field N generated by the real and imaginary part of
|
|
12
|
+
the shapes. The edge lengths and the factors used to normalize the cusp areas
|
|
13
|
+
will be square roots in N and thus the tilts will be N-linear combinations of
|
|
14
|
+
square roots in N. To avoid computing in a massive tower of square extensions
|
|
15
|
+
of N, we implement SqrtLinCombination here that provides a special
|
|
16
|
+
implementation of the == operator.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import operator
|
|
20
|
+
from functools import reduce
|
|
21
|
+
from ..math_basics import prod
|
|
22
|
+
from ..sage_helper import _within_sage, sage_method, SageNotAvailable
|
|
23
|
+
|
|
24
|
+
__all__ = ['find_shapes_as_complex_sqrt_lin_combinations',
|
|
25
|
+
'SqrtLinCombination',
|
|
26
|
+
'ComplexSqrtLinCombination']
|
|
27
|
+
|
|
28
|
+
if _within_sage:
|
|
29
|
+
from sage.rings.complex_interval_field import ComplexIntervalField
|
|
30
|
+
from sage.rings.real_mpfi import RealIntervalField
|
|
31
|
+
from sage.rings.integer import Integer
|
|
32
|
+
from sage.rings.rational import Rational
|
|
33
|
+
from sage.rings.number_field.number_field_element import NumberFieldElement
|
|
34
|
+
from sage.functions.other import sqrt
|
|
35
|
+
from ..sage_helper import create_ComplexNumber
|
|
36
|
+
|
|
37
|
+
_Zero = Integer(0)
|
|
38
|
+
_One = Integer(1)
|
|
39
|
+
|
|
40
|
+
from ..snap import find_field
|
|
41
|
+
|
|
42
|
+
from .real_algebra import field_containing_real_and_imaginary_part_of_number_field
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def eval_number_field_elt(elt, root):
|
|
46
|
+
# SageMath 7.6 can no longer evaluate a rational polynomial on an
|
|
47
|
+
# arbitrary type that supports the basic arithmetic
|
|
48
|
+
# operations. Rather, one can only evaluate on inputs that have
|
|
49
|
+
# been registered into its coercion model. Thus we have to
|
|
50
|
+
# evaluate things manually.
|
|
51
|
+
if elt.is_zero():
|
|
52
|
+
return _Zero
|
|
53
|
+
poly = elt.lift()
|
|
54
|
+
R = poly.base_ring()
|
|
55
|
+
coeffs = poly.coefficients()
|
|
56
|
+
exps = poly.exponents()
|
|
57
|
+
powers = [R(1)]
|
|
58
|
+
for i in range(max(exps)):
|
|
59
|
+
powers.append(powers[-1]*root)
|
|
60
|
+
return sum(c*powers[e] for (c, e) in zip(coeffs, exps))
|
|
61
|
+
|
|
62
|
+
# One problem in verifying canonical cell decomposition is that we need to do
|
|
63
|
+
# computations in the real field which contains the real and imaginary part
|
|
64
|
+
# of the shape field.
|
|
65
|
+
#
|
|
66
|
+
# Our first try was using Sage's QQbar which was suggested here:
|
|
67
|
+
# http://ask.sagemath.org/question/25822/number-field-containing-realimaginary-part-of-algebraic-number/
|
|
68
|
+
# But turns out way too slow.
|
|
69
|
+
#
|
|
70
|
+
# Next, we tried to use the LLL-algorithm applied to the real and imaginary part
|
|
71
|
+
# of the generator of the shape field (effectively applying LLL twice, once to
|
|
72
|
+
# find the shape field and then the field containing the real and imaginary part
|
|
73
|
+
# of the shape field).
|
|
74
|
+
# This is implemented in
|
|
75
|
+
# _field_containing_real_and_imaginary_part_of_algebraic_number_LLL.
|
|
76
|
+
# This was still very slow and failed on t11669 and 9 manifolds with 9 tetrahedra.
|
|
77
|
+
#
|
|
78
|
+
# The fastest implementation so far is in real_algebra. The implementation there
|
|
79
|
+
# turns the one complex equation p(z) = 0 defining the number field into two
|
|
80
|
+
# real equations for the real and imaginary part of the complex equation and
|
|
81
|
+
# then uses the resultant to find exact solutions.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@sage_method
|
|
85
|
+
def find_shapes_as_complex_sqrt_lin_combinations(M, prec, degree):
|
|
86
|
+
"""
|
|
87
|
+
Given a manifold M, use snap (which uses LLL-algorithm) with the given
|
|
88
|
+
decimal precision and maximal degree to find exact values for the shapes'
|
|
89
|
+
real and imaginary part. Return the shapes as list of
|
|
90
|
+
ComplexSqrtLinCombination's. Return None on failure.
|
|
91
|
+
|
|
92
|
+
Example::
|
|
93
|
+
|
|
94
|
+
sage: from snappy import Manifold
|
|
95
|
+
sage: M=Manifold("m412")
|
|
96
|
+
sage: find_shapes_as_complex_sqrt_lin_combinations(M, 200, 10)
|
|
97
|
+
[ComplexSqrtLinCombination((1/2) * sqrt(1), (x - 1/2) * sqrt(1)), ComplexSqrtLinCombination((1/2) * sqrt(1), (x - 1/2) * sqrt(1)), ComplexSqrtLinCombination((1/2) * sqrt(1), (x - 1/2) * sqrt(1)), ComplexSqrtLinCombination((1/2) * sqrt(1), (x - 1/2) * sqrt(1)), ComplexSqrtLinCombination((1/2) * sqrt(1), (x - 1/2) * sqrt(1))]
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
# We need to find the NumberField that contains the real and imaginary
|
|
101
|
+
# parts of all shapes.
|
|
102
|
+
|
|
103
|
+
# First we try to find the field containing the complex shapes:
|
|
104
|
+
complex_data = M.tetrahedra_field_gens().find_field(prec, degree)
|
|
105
|
+
if not complex_data:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
# Split Snap's result
|
|
109
|
+
# complex_root is an ApproximateAlgebraicNumber, the root of the
|
|
110
|
+
# NumberField's defining polynomial
|
|
111
|
+
# exact_complex_shapes are elements in a Sage NumberField
|
|
112
|
+
complex_number_field, complex_root, exact_complex_shapes = complex_data
|
|
113
|
+
|
|
114
|
+
# We now have a generator (complex_root) for the NumberField
|
|
115
|
+
# containing the shapes.
|
|
116
|
+
# Next, we need to find the NumberField containing the real and imaginary
|
|
117
|
+
# part of this generator.
|
|
118
|
+
|
|
119
|
+
real_result = field_containing_real_and_imaginary_part_of_number_field(
|
|
120
|
+
complex_number_field)
|
|
121
|
+
|
|
122
|
+
if not real_result:
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
real_number_field, real_part, imag_part = real_result
|
|
126
|
+
|
|
127
|
+
# Caches the values of
|
|
128
|
+
# nf.gen_embedding()
|
|
129
|
+
# and RealIntervalField(prec)(nf.gen_embedding())
|
|
130
|
+
# for different precision prec where nf is the NumberField real_number_field
|
|
131
|
+
# in which real_part and imag_part live.
|
|
132
|
+
#
|
|
133
|
+
# This is for speed only. See _get_interval_embedding_from_cache for
|
|
134
|
+
# details.
|
|
135
|
+
embed_cache = {}
|
|
136
|
+
|
|
137
|
+
# The generator of the shape field as the desired return type
|
|
138
|
+
exact_complex_root = ComplexSqrtLinCombination(real_part, imag_part,
|
|
139
|
+
embed_cache=embed_cache)
|
|
140
|
+
|
|
141
|
+
# All shapes are given as polynomials in the generator,
|
|
142
|
+
# so translate them to be of the desired return type
|
|
143
|
+
return [ eval_number_field_elt(exact_complex_shape, exact_complex_root)
|
|
144
|
+
for exact_complex_shape in exact_complex_shapes ]
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SqrtLinCombination():
|
|
148
|
+
"""
|
|
149
|
+
A class representing a linear combination
|
|
150
|
+
|
|
151
|
+
c_1 * sqrt(r_1) + c_2 * sqrt(r_2) + ... + c_n * sqrt(r_n)
|
|
152
|
+
|
|
153
|
+
where c_i and r_i have to be of type Integer, Rational or elements
|
|
154
|
+
of the same Sage NumberField with a real embedding (Caution: this is
|
|
155
|
+
assumed but not checked!) such that all r_i are positive (Caution: this is
|
|
156
|
+
not checked during construction!).
|
|
157
|
+
|
|
158
|
+
It implements +, -, * where one of the operators is allowed to be an
|
|
159
|
+
integer or rational.
|
|
160
|
+
|
|
161
|
+
/ is only implemented when the denominator has only one term c_1 * sqrt(1).
|
|
162
|
+
sqrt is only implemented for c_1 * sqrt(1) and it is not checked that
|
|
163
|
+
c_1 is positive.
|
|
164
|
+
|
|
165
|
+
== is implemented, but the other comparison operators are not: casting to
|
|
166
|
+
a RealIntervalField is implemented instead and the user can compare the
|
|
167
|
+
intervals.
|
|
168
|
+
|
|
169
|
+
The == operator is implemented by first reducing A == B to D == 0 and then
|
|
170
|
+
converting to a different data type (_FactorizedSqrtLinCombination) that can
|
|
171
|
+
represent linear combinations::
|
|
172
|
+
|
|
173
|
+
D = c_1 * sqrt(r_{1,1}) * sqrt(r_{1,2}) * ... * sqrt(r_{1,k_1})
|
|
174
|
+
+ c_2 * sqrt(r_{2,1}) * sqrt(r_{2,2}) * ... * sqrt(r_{2,k_2})
|
|
175
|
+
+ ...
|
|
176
|
+
+ c_n * sqrt(r_{n,1}) * sqrt(r_{n,2}) * ... * sqrt(r_{n,k_n})
|
|
177
|
+
|
|
178
|
+
by just trivially setting
|
|
179
|
+
k_i = 0 when r_i = 1 and
|
|
180
|
+
r_{i,1} = r_i and k_1 = 1 otherwise.
|
|
181
|
+
|
|
182
|
+
For this data type, multiplying two sqrt(r_{i,j}) with equal r_{i,j} will
|
|
183
|
+
cancel the two sqrt's and apply the common r_{i,j} to the c_i of the result
|
|
184
|
+
instead. Thus, the following procedure for determining whether D == 0 will
|
|
185
|
+
eventually terminate:
|
|
186
|
+
|
|
187
|
+
- if the number of terms n is 0, return True
|
|
188
|
+
- if the number of terms n is 1, return c_1 == 0
|
|
189
|
+
- if there is a r_{i,j} common to each summand, factor it out
|
|
190
|
+
- pick one of the r_{i,j}, split the sum into two parts "left",
|
|
191
|
+
respectively, "right" of all the terms containing sqrt(r_{i,j}),
|
|
192
|
+
respectively, not containing sqrt(r_{i,j}).
|
|
193
|
+
- If left^2 - right^2 == 0 is False, return False.
|
|
194
|
+
(sqrt(r_{i,j})^2 simplifies to r_{i,j} and disappears, so the resulting
|
|
195
|
+
expression is easier and this recursion terminates eventually.)
|
|
196
|
+
- If left == 0 (some comment applies), return True
|
|
197
|
+
- Use interval arithmetic of increasing precision until it is high enough
|
|
198
|
+
to determine the signs of left and right.
|
|
199
|
+
Return True if and only if the signs differ, otherwise False.
|
|
200
|
+
|
|
201
|
+
Examples::
|
|
202
|
+
|
|
203
|
+
sage: from sage.rings.number_field.number_field import NumberField
|
|
204
|
+
sage: from sage.rings.integer import Integer
|
|
205
|
+
sage: from sage.rings.rational import Rational
|
|
206
|
+
sage: from sage.rings.real_mpfr import RealLiteral, RealField
|
|
207
|
+
sage: from sage.rings.real_mpfi import RealIntervalField
|
|
208
|
+
sage: from sage.calculus.var import var
|
|
209
|
+
sage: from sage.functions.other import sqrt
|
|
210
|
+
sage: x = var('x')
|
|
211
|
+
sage: poly = x ** 6 + Rational((3,2))*x**4 + Rational((9,16))*x**2 - Rational((23,64))
|
|
212
|
+
sage: nf = NumberField(poly, 'z', embedding = RealField()(0.56227951206))
|
|
213
|
+
sage: z = nf.gen()
|
|
214
|
+
|
|
215
|
+
sage: A = SqrtLinCombination(z)
|
|
216
|
+
sage: B = SqrtLinCombination(Rational((8,9))*z**4 + Rational((10,9))*z**2 + Rational((2,9)))
|
|
217
|
+
sage: C = SqrtLinCombination(3)
|
|
218
|
+
sage: D = SqrtLinCombination(Integer(5))
|
|
219
|
+
sage: E = SqrtLinCombination(Rational((6,7)))
|
|
220
|
+
|
|
221
|
+
sage: A + B
|
|
222
|
+
(8/9*z^4 + 10/9*z^2 + z + 2/9) * sqrt(1)
|
|
223
|
+
sage: B - E
|
|
224
|
+
(8/9*z^4 + 10/9*z^2 - 40/63) * sqrt(1)
|
|
225
|
+
sage: A + sqrt(B) * sqrt(B)
|
|
226
|
+
(8/9*z^4 + 10/9*z^2 + z + 2/9) * sqrt(1)
|
|
227
|
+
sage: A + sqrt(B) * sqrt(B) + C == A + B + C
|
|
228
|
+
True
|
|
229
|
+
sage: A / E
|
|
230
|
+
(7/6*z) * sqrt(1)
|
|
231
|
+
sage: B / A.sqrt()
|
|
232
|
+
(128/207*z^5 + 376/207*z^3 + 302/207*z) * sqrt(z)
|
|
233
|
+
sage: B / (D * A.sqrt())
|
|
234
|
+
(128/1035*z^5 + 376/1035*z^3 + 302/1035*z) * sqrt(z)
|
|
235
|
+
sage: RIF = RealIntervalField(100)
|
|
236
|
+
sage: RIF(B.sqrt() + E.sqrt())
|
|
237
|
+
1.73967449622339881238507307209?
|
|
238
|
+
sage: A - B == 0
|
|
239
|
+
False
|
|
240
|
+
sage: (A + B).sqrt()
|
|
241
|
+
(1) * sqrt(8/9*z^4 + 10/9*z^2 + z + 2/9)
|
|
242
|
+
sage: 3 * A.sqrt() + (4 * B).sqrt() + C + 8 == (9 * A).sqrt() + 2 * B.sqrt() + (C * C).sqrt() + 11 - 3
|
|
243
|
+
True
|
|
244
|
+
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
def __init__(self, value=None, d={}, embed_cache=None):
|
|
248
|
+
# Initialize from either a value or a dictionary
|
|
249
|
+
|
|
250
|
+
# c_1 * sqrt(r_1) + c_2 * sqrt(r_2) + ... + c_n * sqrt(r_n)
|
|
251
|
+
#
|
|
252
|
+
# is encoded as dictionary
|
|
253
|
+
#
|
|
254
|
+
# { r_1 : c_1, r_2 : c_2, ..., r_n : c_n }
|
|
255
|
+
|
|
256
|
+
if value is not None:
|
|
257
|
+
if d:
|
|
258
|
+
raise TypeError("SqrtLinCombination has both value and "
|
|
259
|
+
"dictionary.")
|
|
260
|
+
|
|
261
|
+
# Write value as
|
|
262
|
+
# value * sqrt(1)
|
|
263
|
+
#
|
|
264
|
+
# Use empty dictionary when value is zero.
|
|
265
|
+
|
|
266
|
+
self._dict = _filter_zero(
|
|
267
|
+
{ _One : _convert_to_allowed_type(value) })
|
|
268
|
+
else:
|
|
269
|
+
# Filter out zero elements
|
|
270
|
+
self._dict = _filter_zero(d)
|
|
271
|
+
|
|
272
|
+
# Set embed cache, see _get_interval_embedding_from_cache for details
|
|
273
|
+
self._embed_cache = embed_cache
|
|
274
|
+
|
|
275
|
+
def parent(self):
|
|
276
|
+
return SqrtLinCombination
|
|
277
|
+
|
|
278
|
+
def __add__(self, other):
|
|
279
|
+
# Try to convert other term to SqrtLinCombination if necessary
|
|
280
|
+
if not isinstance(other, SqrtLinCombination):
|
|
281
|
+
return self + SqrtLinCombination(
|
|
282
|
+
other, embed_cache=_get_embed_cache(self, other))
|
|
283
|
+
|
|
284
|
+
# Add
|
|
285
|
+
d = {}
|
|
286
|
+
for k, v in self._dict.items():
|
|
287
|
+
d[k] = d.get(k, 0) + v
|
|
288
|
+
for k, v in other._dict.items():
|
|
289
|
+
d[k] = d.get(k, 0) + v
|
|
290
|
+
return SqrtLinCombination(
|
|
291
|
+
d=d,
|
|
292
|
+
embed_cache=_get_embed_cache(self, other))
|
|
293
|
+
|
|
294
|
+
def __neg__(self):
|
|
295
|
+
# Negate
|
|
296
|
+
return SqrtLinCombination(
|
|
297
|
+
d={ k: -v for k, v in self._dict.items() },
|
|
298
|
+
embed_cache=self._embed_cache)
|
|
299
|
+
|
|
300
|
+
def __sub__(self, other):
|
|
301
|
+
# Subtract
|
|
302
|
+
return self + (-other)
|
|
303
|
+
|
|
304
|
+
def __mul__(self, other):
|
|
305
|
+
# Try to convert other term to SqrtLinCombination if necessary
|
|
306
|
+
if not isinstance(other, SqrtLinCombination):
|
|
307
|
+
return self * SqrtLinCombination(
|
|
308
|
+
other,
|
|
309
|
+
embed_cache=_get_embed_cache(self, other))
|
|
310
|
+
|
|
311
|
+
# Result
|
|
312
|
+
d = {}
|
|
313
|
+
|
|
314
|
+
# Multiply each term with each term
|
|
315
|
+
for k1, v1 in self._dict.items():
|
|
316
|
+
for k2, v2 in other._dict.items():
|
|
317
|
+
# multiply c_i * sqrt(r_i) * c_j * sqrt(r_j)
|
|
318
|
+
|
|
319
|
+
# c_i * c_j
|
|
320
|
+
p = v1 * v2
|
|
321
|
+
|
|
322
|
+
# Multiplying the two roots sqrt(r_i) sqrt(r_j)
|
|
323
|
+
if k1 == k2:
|
|
324
|
+
# Case r_i = r_j
|
|
325
|
+
# The term becomes (r_i * c_i * c_j) * sqrt(1)
|
|
326
|
+
d[_One] = d.get(_One, 0) + k1 * p
|
|
327
|
+
else:
|
|
328
|
+
# Case r_i != r_j
|
|
329
|
+
# The term becomes (c_i * c_j) * sqrt(r_i * r_j)
|
|
330
|
+
m = k1 * k2
|
|
331
|
+
d[m] = d.get(m, 0) + p
|
|
332
|
+
return SqrtLinCombination(
|
|
333
|
+
d=d, embed_cache=_get_embed_cache(self, other))
|
|
334
|
+
|
|
335
|
+
def inverse(self):
|
|
336
|
+
# The inverse element of c_1 * sqrt(r_1)
|
|
337
|
+
# is (1 / (c_1 * r_1)) * sqrt(r_1)
|
|
338
|
+
l = len(self._dict)
|
|
339
|
+
if l != 1:
|
|
340
|
+
# Do not implement other elements.
|
|
341
|
+
if l == 0:
|
|
342
|
+
# In particular, do not invert 0
|
|
343
|
+
raise ZeroDivisionError('SqrtLinCombination division by zero')
|
|
344
|
+
raise TypeError('SqrtLinCombination division not fully '
|
|
345
|
+
'implemented')
|
|
346
|
+
|
|
347
|
+
# Iteration over the only term
|
|
348
|
+
for k, v in self._dict.items():
|
|
349
|
+
return SqrtLinCombination(
|
|
350
|
+
d={ k : 1 / (v * k) },
|
|
351
|
+
embed_cache=self._embed_cache)
|
|
352
|
+
|
|
353
|
+
def __div__(self, other):
|
|
354
|
+
# Try to convert other term to SqrtLinCombination if necessary
|
|
355
|
+
if not isinstance(other, SqrtLinCombination):
|
|
356
|
+
return self / SqrtLinCombination(
|
|
357
|
+
other, embed_cache=_get_embed_cache(self, other))
|
|
358
|
+
return self * other.inverse()
|
|
359
|
+
|
|
360
|
+
def __truediv__(self, other):
|
|
361
|
+
return self.__div__(other)
|
|
362
|
+
|
|
363
|
+
def __radd__(self, other):
|
|
364
|
+
return self + other
|
|
365
|
+
|
|
366
|
+
def __rsub__(self, other):
|
|
367
|
+
return (-self) + other
|
|
368
|
+
|
|
369
|
+
def __rmul__(self, other):
|
|
370
|
+
return self * other
|
|
371
|
+
|
|
372
|
+
def __rdiv__(self, other):
|
|
373
|
+
return self.inverse() * other
|
|
374
|
+
|
|
375
|
+
def __rtruediv__(self, other):
|
|
376
|
+
return self.__rdiv__(other)
|
|
377
|
+
|
|
378
|
+
def sqrt(self):
|
|
379
|
+
# Implement sqrt of 0 and c_1 * sqrt(1)
|
|
380
|
+
l = len(self._dict)
|
|
381
|
+
if l == 0:
|
|
382
|
+
# sqrt of 0
|
|
383
|
+
return SqrtLinCombination(
|
|
384
|
+
embed_cache=self._embed_cache)
|
|
385
|
+
if l == 1:
|
|
386
|
+
# Iterate through only term
|
|
387
|
+
for k, v in self._dict.items():
|
|
388
|
+
# Make sure expression in sqrt is 1
|
|
389
|
+
if k != 1:
|
|
390
|
+
raise TypeError('SqrtLinCombination sqrt not fully '
|
|
391
|
+
'implemented')
|
|
392
|
+
return SqrtLinCombination(
|
|
393
|
+
d={ v: _One},
|
|
394
|
+
embed_cache=self._embed_cache)
|
|
395
|
+
raise TypeError('SqrtLinCombination sqrt not fully implemented')
|
|
396
|
+
|
|
397
|
+
def __repr__(self):
|
|
398
|
+
if self._dict:
|
|
399
|
+
return '+'.join(
|
|
400
|
+
['(%r) * sqrt(%r)' % (v, k) for k, v in self._dict.items()])
|
|
401
|
+
return '0'
|
|
402
|
+
|
|
403
|
+
def __eq__(self, other):
|
|
404
|
+
"""
|
|
405
|
+
Implements the == operator as described above.
|
|
406
|
+
"""
|
|
407
|
+
diff = self - other
|
|
408
|
+
|
|
409
|
+
# Convert to type holding linear combinations of factorized
|
|
410
|
+
# sqrts.
|
|
411
|
+
f = _FactorizedSqrtLinCombination.from_sqrt_lin_combination(diff)
|
|
412
|
+
return f.is_zero()
|
|
413
|
+
|
|
414
|
+
def __lt__(self, other):
|
|
415
|
+
raise Exception('Not implemented')
|
|
416
|
+
|
|
417
|
+
def __le__(self, other):
|
|
418
|
+
raise Exception('Not implemented')
|
|
419
|
+
|
|
420
|
+
def __gt__(self, other):
|
|
421
|
+
raise Exception('Not implemented')
|
|
422
|
+
|
|
423
|
+
def __ge__(self, other):
|
|
424
|
+
raise Exception('Not implemented')
|
|
425
|
+
|
|
426
|
+
def _real_mpfi_(self, RIF):
|
|
427
|
+
"""
|
|
428
|
+
Convert to interval in given RealIntervalField instance.
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
def eval_term(k, v):
|
|
432
|
+
# Evaluate one term c_i * sqrt(r_i)
|
|
433
|
+
# where c_i = k, r_i = v
|
|
434
|
+
s = _to_RIF(k, RIF, self._embed_cache)
|
|
435
|
+
if not s > 0:
|
|
436
|
+
raise _SqrtException()
|
|
437
|
+
return _to_RIF(v, RIF, self._embed_cache) * s.sqrt()
|
|
438
|
+
|
|
439
|
+
# Sum over all terms
|
|
440
|
+
return sum([eval_term(k, v) for k, v in self._dict.items()], RIF(0))
|
|
441
|
+
|
|
442
|
+
def _sign_numerical(self, prec):
|
|
443
|
+
"""
|
|
444
|
+
Use interval arithmetics with precision prec to try to determine the
|
|
445
|
+
sign. If we could not certify the sign, return None.
|
|
446
|
+
The result is a pair (sign, interval).
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
# Evaluate as interval
|
|
450
|
+
RIF = RealIntervalField(prec)
|
|
451
|
+
try:
|
|
452
|
+
interval_val = RIF(self)
|
|
453
|
+
except _SqrtException:
|
|
454
|
+
# This exception happens if we try to take the square root of an
|
|
455
|
+
# interval that contains negative numbers.
|
|
456
|
+
# This is not supposed to happen but if we take the square of a small
|
|
457
|
+
# number and the precision is low, it might happen.
|
|
458
|
+
# It just means we need to use higher precision.
|
|
459
|
+
# So just return "None" to indicate failed certification.
|
|
460
|
+
return None, None
|
|
461
|
+
|
|
462
|
+
# Interval certifies positive sign
|
|
463
|
+
if interval_val > 0:
|
|
464
|
+
return +1, interval_val
|
|
465
|
+
# Interval certified negative sign
|
|
466
|
+
if interval_val < 0:
|
|
467
|
+
return -1, interval_val
|
|
468
|
+
# Interval contains zero and we can't say.
|
|
469
|
+
return None, interval_val
|
|
470
|
+
|
|
471
|
+
def sign_with_interval(self):
|
|
472
|
+
"""
|
|
473
|
+
Similar to sign, but for the non-zero case, also return the interval
|
|
474
|
+
certifying the sign - useful for debugging.
|
|
475
|
+
"""
|
|
476
|
+
# First try to determine the sign using interval arithmetics in twice
|
|
477
|
+
# the double precision. This is for performance: the exact case can
|
|
478
|
+
# be slow so we try numerically first.
|
|
479
|
+
prec = 106
|
|
480
|
+
numerical_sign, interval_val = self._sign_numerical(prec)
|
|
481
|
+
if numerical_sign is not None:
|
|
482
|
+
# We could determine the sign using interval arithmetics
|
|
483
|
+
# Return the result.
|
|
484
|
+
return numerical_sign, interval_val
|
|
485
|
+
|
|
486
|
+
# Now try to determine whether it is zero using exact arithmetics.
|
|
487
|
+
if self == 0:
|
|
488
|
+
# It is zero
|
|
489
|
+
return 0, 0
|
|
490
|
+
|
|
491
|
+
# We know that the value is non-zero. Increase precision until we have
|
|
492
|
+
# determined the sign using interval arithmetics.
|
|
493
|
+
while True:
|
|
494
|
+
prec *= 2
|
|
495
|
+
numerical_sign, interval_val = self._sign_numerical(prec)
|
|
496
|
+
if numerical_sign is not None:
|
|
497
|
+
return numerical_sign, interval_val
|
|
498
|
+
|
|
499
|
+
def sign(self):
|
|
500
|
+
"""
|
|
501
|
+
Returns the +1, 0, -1 depending on whether the value is positive,
|
|
502
|
+
zero or negative. For the zero case, exact arithmetic is used to
|
|
503
|
+
certify. Otherwise, interval arithmetic is used.
|
|
504
|
+
"""
|
|
505
|
+
return self.sign_with_interval()[0]
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
class ComplexSqrtLinCombination():
|
|
509
|
+
"""
|
|
510
|
+
A pair (real, imag) of SqrtLinCombinations representing the complex number
|
|
511
|
+
real + imag * I. Supports ``real()``, ``imag()``, ``+``, ``-``, ``*``, ``/``,
|
|
512
|
+
``abs``, ``conjugate()`` and ``==``.
|
|
513
|
+
"""
|
|
514
|
+
|
|
515
|
+
def __init__(self, real, imag=0, embed_cache=None):
|
|
516
|
+
if isinstance(real, SqrtLinCombination):
|
|
517
|
+
self._real = real
|
|
518
|
+
else:
|
|
519
|
+
self._real = SqrtLinCombination(
|
|
520
|
+
real,
|
|
521
|
+
embed_cache=embed_cache)
|
|
522
|
+
|
|
523
|
+
if isinstance(imag, SqrtLinCombination):
|
|
524
|
+
self._imag = imag
|
|
525
|
+
else:
|
|
526
|
+
self._imag = SqrtLinCombination(
|
|
527
|
+
imag,
|
|
528
|
+
embed_cache=embed_cache)
|
|
529
|
+
|
|
530
|
+
def __repr__(self):
|
|
531
|
+
return "ComplexSqrtLinCombination(%r, %r)" % (self._real, self._imag)
|
|
532
|
+
|
|
533
|
+
def real(self):
|
|
534
|
+
"""
|
|
535
|
+
Real part.
|
|
536
|
+
"""
|
|
537
|
+
return self._real
|
|
538
|
+
|
|
539
|
+
def imag(self):
|
|
540
|
+
"""
|
|
541
|
+
Imaginary part.
|
|
542
|
+
"""
|
|
543
|
+
return self._imag
|
|
544
|
+
|
|
545
|
+
def __abs__(self):
|
|
546
|
+
"""
|
|
547
|
+
Absolute value.
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
return sqrt(self._real * self._real + self._imag * self._imag)
|
|
551
|
+
|
|
552
|
+
def __add__(self, other):
|
|
553
|
+
if not isinstance(other, ComplexSqrtLinCombination):
|
|
554
|
+
return self + ComplexSqrtLinCombination(other)
|
|
555
|
+
|
|
556
|
+
return ComplexSqrtLinCombination(self._real + other._real,
|
|
557
|
+
self._imag + other._imag)
|
|
558
|
+
|
|
559
|
+
def __neg__(self):
|
|
560
|
+
return ComplexSqrtLinCombination(-self._real, -self._imag)
|
|
561
|
+
|
|
562
|
+
def __sub__(self, other):
|
|
563
|
+
return self + (-other)
|
|
564
|
+
|
|
565
|
+
def __mul__(self, other):
|
|
566
|
+
if not isinstance(other, ComplexSqrtLinCombination):
|
|
567
|
+
return self * ComplexSqrtLinCombination(other)
|
|
568
|
+
|
|
569
|
+
return ComplexSqrtLinCombination(
|
|
570
|
+
self._real * other._real - self._imag * other._imag,
|
|
571
|
+
self._real * other._imag + self._imag * other._real)
|
|
572
|
+
|
|
573
|
+
def __div__(self, other):
|
|
574
|
+
if not isinstance(other, ComplexSqrtLinCombination):
|
|
575
|
+
return self / ComplexSqrtLinCombination(other)
|
|
576
|
+
|
|
577
|
+
num = 1 / (other._real * other._real + other._imag * other._imag)
|
|
578
|
+
|
|
579
|
+
return ComplexSqrtLinCombination(
|
|
580
|
+
(self._real * other._real + self._imag * other._imag) * num,
|
|
581
|
+
(other._real * self._imag - self._real * other._imag) * num)
|
|
582
|
+
|
|
583
|
+
def __truediv__(self, other):
|
|
584
|
+
return self.__div__(other)
|
|
585
|
+
|
|
586
|
+
def conjugate(self):
|
|
587
|
+
return ComplexSqrtLinCombination(self._real, -self._imag)
|
|
588
|
+
|
|
589
|
+
def __radd__(self, other):
|
|
590
|
+
return self + other
|
|
591
|
+
|
|
592
|
+
def __rsub__(self, other):
|
|
593
|
+
return (-self) + other
|
|
594
|
+
|
|
595
|
+
def __rmul__(self, other):
|
|
596
|
+
return self * other
|
|
597
|
+
|
|
598
|
+
def __rdiv__(self, other):
|
|
599
|
+
return ComplexSqrtLinCombination(other) / self
|
|
600
|
+
|
|
601
|
+
def __rtruediv__(self, other):
|
|
602
|
+
return self.__rdiv__(other)
|
|
603
|
+
|
|
604
|
+
def __eq__(self, other):
|
|
605
|
+
if not isinstance(other, ComplexSqrtLinCombination):
|
|
606
|
+
return self == ComplexSqrtLinCombination(other)
|
|
607
|
+
|
|
608
|
+
return (self._real == other._real) and (self._imag == other._imag)
|
|
609
|
+
|
|
610
|
+
def __ne__(self, other):
|
|
611
|
+
return not (self == other)
|
|
612
|
+
|
|
613
|
+
def __lt__(self, other):
|
|
614
|
+
raise TypeError('No order on complex numbers.')
|
|
615
|
+
|
|
616
|
+
def __le__(self, other):
|
|
617
|
+
raise TypeError('No order on complex numbers.')
|
|
618
|
+
|
|
619
|
+
def __gt__(self, other):
|
|
620
|
+
raise TypeError('No order on complex numbers.')
|
|
621
|
+
|
|
622
|
+
def __ge__(self, other):
|
|
623
|
+
raise TypeError('No order on complex numbers.')
|
|
624
|
+
|
|
625
|
+
def _complex_mpfi_(self, CIF):
|
|
626
|
+
"""
|
|
627
|
+
Convert to complex interval in given ComplexIntervalField instance.
|
|
628
|
+
"""
|
|
629
|
+
|
|
630
|
+
# Get corresponding RealIntervalField
|
|
631
|
+
RIF = CIF(0).real().parent()
|
|
632
|
+
# And just pair
|
|
633
|
+
return CIF(RIF(self._real), RIF(self._imag))
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class _SqrtException(Exception):
|
|
637
|
+
pass
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
class _FactorizedSqrtLinCombination():
|
|
641
|
+
def __init__(self, d={}, embed_cache=None):
|
|
642
|
+
# c_1 * sqrt(r_{1,1}) * sqrt(r_{1,2}) * ... * sqrt(r_{1,k_1})
|
|
643
|
+
# + c_2 * sqrt(r_{2,1}) * sqrt(r_{2,2}) * ... * sqrt(r_{2,k_2})
|
|
644
|
+
# + ...
|
|
645
|
+
# + c_n * sqrt(r_{n,1}) * sqrt(r_{n,2}) * ... * sqrt(r_{n,k_n})
|
|
646
|
+
#
|
|
647
|
+
# is encoded by a dictionary
|
|
648
|
+
#
|
|
649
|
+
# { frozenset([r_{1,1}, r_{1,2}, ..., r_{1,k_1}]) : c_1,
|
|
650
|
+
# frozenset([r_{2,1}, r_{2,2}, ..., r_{2,k_2}]) : c_2,
|
|
651
|
+
# ...,
|
|
652
|
+
# frozenset([r_{n,1}, r_{n,2}, ..., r_{n,k_n}]) : c_n }
|
|
653
|
+
|
|
654
|
+
self._dict = _filter_zero(d)
|
|
655
|
+
|
|
656
|
+
# Set embed cache, see _get_interval_embedding_from_cache for details
|
|
657
|
+
self._embed_cache = embed_cache
|
|
658
|
+
|
|
659
|
+
def _real_mpfi_(self, RIF):
|
|
660
|
+
|
|
661
|
+
def eval_term(k, v):
|
|
662
|
+
# Evaluate one term
|
|
663
|
+
# c_i * sqrt(r_{i,1}) * sqrt(r_{i,2}) * ... * sqrt(r_{i,k_2})
|
|
664
|
+
# where c_i is stored in v
|
|
665
|
+
# and k is the set of r_{i,j}
|
|
666
|
+
|
|
667
|
+
# Take the product of all r_{i,j} after converting to intervals
|
|
668
|
+
pr = prod([_to_RIF(t, RIF, self._embed_cache) for t in k],
|
|
669
|
+
RIF(1))
|
|
670
|
+
|
|
671
|
+
# Raise exception if interval isn't positive
|
|
672
|
+
if not pr > 0:
|
|
673
|
+
raise _SqrtException()
|
|
674
|
+
|
|
675
|
+
# Return interval for term
|
|
676
|
+
return pr.sqrt() * _to_RIF(v, RIF, self._embed_cache)
|
|
677
|
+
|
|
678
|
+
# Sum over all terms
|
|
679
|
+
return sum([eval_term(k, v) for k, v in self._dict.items()], RIF(0))
|
|
680
|
+
|
|
681
|
+
def __repr__(self):
|
|
682
|
+
if not self._dict:
|
|
683
|
+
return '0'
|
|
684
|
+
|
|
685
|
+
def term(item):
|
|
686
|
+
k, v = item
|
|
687
|
+
b = '(%r)' % v
|
|
688
|
+
for s in k:
|
|
689
|
+
b += ' * sqrt(%r)' % s
|
|
690
|
+
return b
|
|
691
|
+
|
|
692
|
+
return '+'.join([term(item) for item in self._dict.items()])
|
|
693
|
+
|
|
694
|
+
@staticmethod
|
|
695
|
+
def from_sqrt_lin_combination(l):
|
|
696
|
+
"""
|
|
697
|
+
Construct from a SqrtLinCombination.
|
|
698
|
+
"""
|
|
699
|
+
|
|
700
|
+
# Need to change encoding, see __init__
|
|
701
|
+
def to_set(k):
|
|
702
|
+
if k == _One:
|
|
703
|
+
return frozenset()
|
|
704
|
+
else:
|
|
705
|
+
return frozenset([k])
|
|
706
|
+
|
|
707
|
+
return _FactorizedSqrtLinCombination({
|
|
708
|
+
to_set(k): v for k, v in l._dict.items()},
|
|
709
|
+
embed_cache=l._embed_cache)
|
|
710
|
+
|
|
711
|
+
def __add__(self, other):
|
|
712
|
+
# Add
|
|
713
|
+
d = {}
|
|
714
|
+
for k, v in self._dict.items():
|
|
715
|
+
d[k] = d.get(k, 0) + v
|
|
716
|
+
for k, v in other._dict.items():
|
|
717
|
+
d[k] = d.get(k, 0) + v
|
|
718
|
+
return _FactorizedSqrtLinCombination(
|
|
719
|
+
d,
|
|
720
|
+
embed_cache=_get_embed_cache(self, other))
|
|
721
|
+
|
|
722
|
+
def __neg__(self):
|
|
723
|
+
return _FactorizedSqrtLinCombination(
|
|
724
|
+
{k: -v for k, v in self._dict.items()},
|
|
725
|
+
embed_cache=self._embed_cache)
|
|
726
|
+
|
|
727
|
+
def __sub__(self, other):
|
|
728
|
+
return self + (-other)
|
|
729
|
+
|
|
730
|
+
def __mul__(self, other):
|
|
731
|
+
d = {}
|
|
732
|
+
# Multiply each term with each
|
|
733
|
+
for k1, v1 in self._dict.items():
|
|
734
|
+
for k2, v2 in other._dict.items():
|
|
735
|
+
# Multiply
|
|
736
|
+
# c_i * sqrt(r_{i,1}) * ... * sqrt(r_{i,k_i})
|
|
737
|
+
# with
|
|
738
|
+
# c'_i' * sqrt(r'_{i',1}) * ... * sqrt(r'_{i',k'_i'})
|
|
739
|
+
#
|
|
740
|
+
# If sqrt(r) appears in both terms, it becomes
|
|
741
|
+
# sqrt(r) * sqrt(r) = r and is multiplied into the coefficient
|
|
742
|
+
# (this is done by prod(k1 & k2), _One).
|
|
743
|
+
# A sqrt(r) appearing only appearing in one term survives
|
|
744
|
+
# (k1^k2)
|
|
745
|
+
|
|
746
|
+
k = k1 ^ k2
|
|
747
|
+
v = v1 * v2 * prod(k1 & k2, _One)
|
|
748
|
+
d[k] = d.get(k, 0) + v
|
|
749
|
+
return _FactorizedSqrtLinCombination(
|
|
750
|
+
d, embed_cache=_get_embed_cache(self, other))
|
|
751
|
+
|
|
752
|
+
def is_zero(self):
|
|
753
|
+
"""
|
|
754
|
+
Returns True if it is zero, False otherwise.
|
|
755
|
+
"""
|
|
756
|
+
|
|
757
|
+
# Implements the algorithm for operator == described in
|
|
758
|
+
# SqrtLinCombination
|
|
759
|
+
|
|
760
|
+
# The case of no terms n = 0
|
|
761
|
+
if not self._dict:
|
|
762
|
+
return True
|
|
763
|
+
|
|
764
|
+
# Case of one term n = 1
|
|
765
|
+
if len(self._dict) == 1:
|
|
766
|
+
return _first(self._dict.values()) == 0
|
|
767
|
+
|
|
768
|
+
# Find all r_{i,j} common to all summands
|
|
769
|
+
common_terms = reduce(
|
|
770
|
+
operator.and_, self._dict.keys())
|
|
771
|
+
|
|
772
|
+
# Factor them out
|
|
773
|
+
d = {k - common_terms: v for k, v in self._dict.items()}
|
|
774
|
+
|
|
775
|
+
# Pick one r_{i,j}
|
|
776
|
+
term = _firstfirst(d.keys())
|
|
777
|
+
|
|
778
|
+
# Split the summands into "left" and "right"
|
|
779
|
+
left = _FactorizedSqrtLinCombination(
|
|
780
|
+
{ k: v for k, v in d.items() if term in k },
|
|
781
|
+
embed_cache=self._embed_cache)
|
|
782
|
+
right = _FactorizedSqrtLinCombination(
|
|
783
|
+
{ k: v for k, v in d.items() if term not in k},
|
|
784
|
+
embed_cache=self._embed_cache)
|
|
785
|
+
|
|
786
|
+
# Check left^2 - right^2 == 0
|
|
787
|
+
if not (left * left - right * right).is_zero():
|
|
788
|
+
return False
|
|
789
|
+
|
|
790
|
+
# Check left == 0
|
|
791
|
+
if left.is_zero():
|
|
792
|
+
return True
|
|
793
|
+
|
|
794
|
+
# Start with double precision and then increase until we could
|
|
795
|
+
# determine the signs of left and right
|
|
796
|
+
prec = 53
|
|
797
|
+
while True:
|
|
798
|
+
# Determine signs, None indicates the signs couldn't be certified
|
|
799
|
+
opposite_signs = _opposite_signs(left, right, prec)
|
|
800
|
+
if opposite_signs is not None:
|
|
801
|
+
# Done
|
|
802
|
+
return opposite_signs
|
|
803
|
+
|
|
804
|
+
# Otherwise, increase precision
|
|
805
|
+
prec *= 2
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _opposite_signs(left, right, prec):
|
|
809
|
+
"""
|
|
810
|
+
Given two objects left and right that can be coerced to real interval of
|
|
811
|
+
the given precision, try to certify their signs. If succeed, return True
|
|
812
|
+
if the signs are opposite and False otherwise. If failed, return None.
|
|
813
|
+
"""
|
|
814
|
+
|
|
815
|
+
# Try to cast the elements to real intervals
|
|
816
|
+
RIF = RealIntervalField(prec)
|
|
817
|
+
try:
|
|
818
|
+
left_interval = RIF(left)
|
|
819
|
+
right_interval = RIF(right)
|
|
820
|
+
except _SqrtException:
|
|
821
|
+
# This exception happens if we try to take the square root of an
|
|
822
|
+
# interval that contains negative numbers.
|
|
823
|
+
# This is not supposed to happen but if we take the square of a small
|
|
824
|
+
# number and the precision is low, it might happen.
|
|
825
|
+
# It just means we need to use higher precision.
|
|
826
|
+
# So just return "None" to indicate failed certification.
|
|
827
|
+
return None
|
|
828
|
+
|
|
829
|
+
# Try to determine sign of left expression.
|
|
830
|
+
left_negative = bool(left_interval < 0)
|
|
831
|
+
left_positive = bool(left_interval > 0)
|
|
832
|
+
left_determined = left_negative or left_positive
|
|
833
|
+
|
|
834
|
+
# Try to determine sign of right expression
|
|
835
|
+
right_negative = bool(right_interval < 0)
|
|
836
|
+
right_positive = bool(right_interval > 0)
|
|
837
|
+
right_determined = right_negative or right_positive
|
|
838
|
+
|
|
839
|
+
# If both signs could be determined
|
|
840
|
+
if left_determined and right_determined:
|
|
841
|
+
# Return true if and only if signs are opposite
|
|
842
|
+
return left_positive ^ right_positive
|
|
843
|
+
|
|
844
|
+
# At least one sign couldn't be determined.
|
|
845
|
+
return None
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _first(iterable):
|
|
849
|
+
"""
|
|
850
|
+
Return first element of iterable.
|
|
851
|
+
"""
|
|
852
|
+
for i in iterable:
|
|
853
|
+
return i
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def _firstfirst(iterable):
|
|
857
|
+
"""
|
|
858
|
+
Given a nested iterable, i.e., list of lists, return the first element
|
|
859
|
+
of the first non-empty element.
|
|
860
|
+
"""
|
|
861
|
+
for i in iterable:
|
|
862
|
+
for j in i:
|
|
863
|
+
return j
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _filter_zero(d):
|
|
867
|
+
"""
|
|
868
|
+
Given a dict, filter out all items where the value is 0.
|
|
869
|
+
"""
|
|
870
|
+
return {k: v for k, v in d.items() if v != 0}
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def _convert_to_allowed_type(number):
|
|
874
|
+
"""
|
|
875
|
+
When given a Python int, convert to Sage Integer (so that
|
|
876
|
+
division of two integers gives a Rational). Otherwise,
|
|
877
|
+
check that the type is allowed.
|
|
878
|
+
"""
|
|
879
|
+
|
|
880
|
+
if isinstance(number, int):
|
|
881
|
+
return Integer(number)
|
|
882
|
+
if isinstance(number, Integer):
|
|
883
|
+
return number
|
|
884
|
+
if isinstance(number, Rational):
|
|
885
|
+
return number
|
|
886
|
+
if isinstance(number, NumberFieldElement):
|
|
887
|
+
return number
|
|
888
|
+
|
|
889
|
+
raise Exception("Not an allowed type")
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def _get_embed_cache(l1, l2):
|
|
893
|
+
"""
|
|
894
|
+
Given objects of type SqrtLinCombination or _FactorizedSqrtLinCombination
|
|
895
|
+
return the first _embed_cache that is not None.
|
|
896
|
+
For example, one SqrtLinCombination might be instantiated from an
|
|
897
|
+
Integer and the other from an element in the number field that we are
|
|
898
|
+
currently working in. Then only the latter one has an _embed_cache. Thus,
|
|
899
|
+
the need for this function when adding, multiplying, ... those two
|
|
900
|
+
instances.
|
|
901
|
+
"""
|
|
902
|
+
for l in [l1, l2]:
|
|
903
|
+
if ((isinstance(l, SqrtLinCombination) or
|
|
904
|
+
isinstance(l, _FactorizedSqrtLinCombination)) and
|
|
905
|
+
l._embed_cache is not None):
|
|
906
|
+
return l._embed_cache
|
|
907
|
+
|
|
908
|
+
return None
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def _get_interval_embedding_from_cache(nf, RIF, cache):
|
|
912
|
+
"""
|
|
913
|
+
Evaluate RIF(nf.gen_embedding()) where RIF is a RealIntervalField with
|
|
914
|
+
some precision. This is a real interval that is guaranteed to contain the
|
|
915
|
+
preferred root of the defining polynomial of the number field.
|
|
916
|
+
|
|
917
|
+
To avoid re-evaluation, use cache which is (a reference) to a python
|
|
918
|
+
dictionary.
|
|
919
|
+
|
|
920
|
+
The idea is that while working over one number field, all instances of
|
|
921
|
+
(_Factorized)SqrtLinCombination have a reference to the same (shared) python
|
|
922
|
+
dictionary and fill it in as needed.
|
|
923
|
+
|
|
924
|
+
Unfortunately, the reference to the cache needs to passed down along a lot
|
|
925
|
+
of places. There might be a nicer mechanism for doing this.
|
|
926
|
+
"""
|
|
927
|
+
|
|
928
|
+
# Cache is None (vs an empty dictionary) means that we do not wish to use
|
|
929
|
+
# a cache.
|
|
930
|
+
|
|
931
|
+
# Uncomment to debug performance problems that are suspected to come
|
|
932
|
+
# from the reference to the cache not being passed along
|
|
933
|
+
# if cache is None:
|
|
934
|
+
# print("Warning: No cache used")
|
|
935
|
+
|
|
936
|
+
# The key 'gen_embedding' holds the value of nf.gen_embedding()
|
|
937
|
+
if cache is not None and 'gen_embedding' in cache:
|
|
938
|
+
# We can read it from cache
|
|
939
|
+
gen_embedding = cache['gen_embedding']
|
|
940
|
+
else:
|
|
941
|
+
# We need to evaluate it
|
|
942
|
+
gen_embedding = nf.gen_embedding()
|
|
943
|
+
if cache is not None:
|
|
944
|
+
# Save in cache for future use
|
|
945
|
+
cache['gen_embedding'] = gen_embedding
|
|
946
|
+
|
|
947
|
+
# Get the desired precision of the RealIntervalField
|
|
948
|
+
prec = RIF.prec()
|
|
949
|
+
# The precision (which is an int) is the key into the cache
|
|
950
|
+
if cache is not None and prec in cache:
|
|
951
|
+
# RIF(nf.gen_embedding()) is in the cache
|
|
952
|
+
# We can just return the result
|
|
953
|
+
return cache[prec]
|
|
954
|
+
|
|
955
|
+
# We need to actually compute it.
|
|
956
|
+
interval = RIF(gen_embedding)
|
|
957
|
+
if cache is not None:
|
|
958
|
+
# Save in cache for future use.
|
|
959
|
+
cache[prec] = interval
|
|
960
|
+
|
|
961
|
+
return interval
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
def _to_RIF(x, RIF, embed_cache=None):
|
|
965
|
+
"""
|
|
966
|
+
Given a Sage Integer, Rational or an element x in a
|
|
967
|
+
Sage NumberField with a real embedding and an instance
|
|
968
|
+
of a RealIntervalField to specify the desired precision,
|
|
969
|
+
return a real interval containing the true value of x.
|
|
970
|
+
|
|
971
|
+
Warning: one can actually call RIF(x) and get an interval, but I have
|
|
972
|
+
found examples where that interval does not contain the true value!
|
|
973
|
+
Seems a bug in Sage. CIF(x) doesn't work, so maybe there is just some
|
|
974
|
+
sequence of casts going on to convert x to an interval that wasn't
|
|
975
|
+
anticipated.
|
|
976
|
+
"""
|
|
977
|
+
# Handle Integer and Rational case
|
|
978
|
+
if isinstance(x, Integer) or isinstance(x, Rational):
|
|
979
|
+
return RIF(x)
|
|
980
|
+
|
|
981
|
+
# Get the number field
|
|
982
|
+
nf = x.parent()
|
|
983
|
+
|
|
984
|
+
# Get the generator of number field as interval
|
|
985
|
+
# The code is equivalent to root = RIF(nf.gen_embedding()) but
|
|
986
|
+
# caches the result.
|
|
987
|
+
root = _get_interval_embedding_from_cache(nf, RIF, embed_cache)
|
|
988
|
+
|
|
989
|
+
# Sanity check on the root. The polynomial should be
|
|
990
|
+
# zero at it, so the interval has to contain zero.
|
|
991
|
+
# This does not certify it. To certify, we would need
|
|
992
|
+
# to take each end point of the interval, evaluate
|
|
993
|
+
# it using interval arithmetics and check for opposite
|
|
994
|
+
# signs
|
|
995
|
+
if not nf.defining_polynomial()(root).contains_zero():
|
|
996
|
+
raise Exception("Root failed test.")
|
|
997
|
+
|
|
998
|
+
# Evaluate the polynomial representing the element in the number field
|
|
999
|
+
# at the root
|
|
1000
|
+
return x.lift()(root)
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
if __name__ == '__main__':
|
|
1004
|
+
import doctest
|
|
1005
|
+
doctest.testmod()
|