diff --git a/CITATION.cff b/CITATION.cff index b4e3a375a3a..305ef37cbd5 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.6.beta5 +version: 10.6.beta6 doi: 10.5281/zenodo.8042260 -date-released: 2025-01-26 +date-released: 2025-02-10 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/Makefile b/Makefile index 6bc60acdca3..15dbbb411c0 100644 --- a/Makefile +++ b/Makefile @@ -253,9 +253,19 @@ TEST_TARGET = $@ TEST = ./sage -t --logfile=$(TEST_LOG) $(TEST_FLAGS) --optional=$(TEST_OPTIONAL) $(TEST_FILES) +test-git-no-uncommitted-changes: + @UNCOMMITTED=$$(git status --porcelain); \ + if [ -n "$$UNCOMMITTED" ]; then \ + echo "Error: the git repo has uncommitted changes:"; \ + echo "$$UNCOMMITTED"; \ + echo; \ + exit 1; \ + fi + test: all @echo '### make $(TEST_TARGET): Running $(TEST)' >> $(TEST_LOG) - $(TEST) + $(TEST); \ + $(MAKE) test-git-no-uncommitted-changes check: @$(MAKE) test @@ -302,7 +312,8 @@ ptestoptionallong: test-nodoc: TEST_OPTIONAL := $(TEST_OPTIONAL),!sagemath_doc_html,!sagemath_doc_pdf test-nodoc: build @echo '### make $(TEST_TARGET): Running $(TEST)' >> $(TEST_LOG) - $(TEST) + $(TEST); \ + $(MAKE) test-git-no-uncommitted-changes check-nodoc: @$(MAKE) test-nodoc @@ -387,5 +398,6 @@ list: misc-clean bdist-clean distclean bootstrap-clean maintainer-clean \ test check testoptional testall testlong testoptionallong testallong \ ptest ptestoptional ptestall ptestlong ptestoptionallong ptestallong \ + test-git-no-uncommitted-changes \ list \ doc-clean clean sagelib-clean build-clean diff --git a/VERSION.txt b/VERSION.txt index 5c3eb78337a..0dec762feaa 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.6.beta5, Release Date: 2025-01-26 +SageMath version 10.6.beta6, Release Date: 2025-02-10 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index ada82676b33..a0a7107a52d 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,3 +1,3 @@ tarball=configure-VERSION.tar.gz -sha1=86711d4cbef2cd4e7bb4afcde36965e5dea908e0 -sha256=9793cf92ebdceb09050a585294de93c242c033745a0012cae1bd301d307a45df +sha1=db7e875cd888f974f7de2c038b2ae75ea5a31b1b +sha256=c905a38a83d6f718258cab25cb643c574fdb600016605c5e252584271bf6ac53 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index d1c6cb2da71..62ea228719b 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -efc0914cd8d72a9bdfdcca4511b55e290b9b6674 +00c302e55c156a7dd678229ff976ed90ce3d5161 diff --git a/build/pkgs/fplll/checksums.ini b/build/pkgs/fplll/checksums.ini index 7ad5ac91a3b..952e43611c3 100644 --- a/build/pkgs/fplll/checksums.ini +++ b/build/pkgs/fplll/checksums.ini @@ -1,4 +1,4 @@ tarball=fplll-VERSION.tar.gz -sha1=607f5922109d93ddd5a05419682511e26579f9d6 -sha256=76d3778f0326597ed7505bab19493a9bf6b73a5c5ca614e8fb82f42105c57d00 +sha1=b40beea04697cccb26c9e6140935039ef87371df +sha256=f0af6bdd0ebd5871e87ff3ef7737cb5360b1e38181a4e5a8c1236f3476fec3b2 upstream_url=https://github.com/fplll/fplll/releases/download/VERSION/fplll-VERSION.tar.gz diff --git a/build/pkgs/fplll/package-version.txt b/build/pkgs/fplll/package-version.txt index 8ce222e90f7..d50359de185 100644 --- a/build/pkgs/fplll/package-version.txt +++ b/build/pkgs/fplll/package-version.txt @@ -1 +1 @@ -5.4.5 +5.5.0 diff --git a/build/pkgs/fplll/spkg-configure.m4 b/build/pkgs/fplll/spkg-configure.m4 index 053c9c74daf..468db6c3f29 100644 --- a/build/pkgs/fplll/spkg-configure.m4 +++ b/build/pkgs/fplll/spkg-configure.m4 @@ -8,7 +8,7 @@ SAGE_SPKG_CONFIGURE([fplll], [ dnl Issue #31025: FPLLL/FPyLLL make no guarantee regarding compatibility dnl other than "whatever versions were released at the same time should work together" PKG_CHECK_MODULES([FPLLL], - [fplll >= 5.4.5 fplll <= 5.4.5], + [fplll >= 5.5.0], [ AC_MSG_CHECKING([whether BKZ default strategy JSON is installed]) AC_LANG_PUSH([C++]) diff --git a/build/pkgs/fpylll/checksums.ini b/build/pkgs/fpylll/checksums.ini index 188e768b787..b02fd91bedc 100644 --- a/build/pkgs/fpylll/checksums.ini +++ b/build/pkgs/fpylll/checksums.ini @@ -1,4 +1,4 @@ tarball=fpylll-VERSION.tar.gz -sha1=c0bcf8c5583ebf614da9b26710a2c835d498bf34 -sha256=dfd9529a26c50993a2a716177debd7994312219070574cad31b35b4f0c040a19 +sha1=9399eea85c3f4cbd0dc33893532a324adc905d4e +sha256=a3f4049e1c27b52136f71f722312c4265e3a2dcb5722536ec8247d708dd4248a upstream_url=https://files.pythonhosted.org/packages/source/f/fpylll/fpylll-VERSION.tar.gz diff --git a/build/pkgs/fpylll/dependencies b/build/pkgs/fpylll/dependencies index 03eb318a449..8faa54d6781 100644 --- a/build/pkgs/fpylll/dependencies +++ b/build/pkgs/fpylll/dependencies @@ -1,4 +1,4 @@ - cython cysignals numpy fplll | $(PYTHON_TOOLCHAIN) $(PYTHON) +cython cysignals numpy fplll | $(PYTHON_TOOLCHAIN) $(PYTHON) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/fpylll/package-version.txt b/build/pkgs/fpylll/package-version.txt index ee6cdce3c29..844f6a91acb 100644 --- a/build/pkgs/fpylll/package-version.txt +++ b/build/pkgs/fpylll/package-version.txt @@ -1 +1 @@ -0.6.1 +0.6.3 diff --git a/build/pkgs/gap/checksums.ini b/build/pkgs/gap/checksums.ini index 3704072d609..7f8c0e24541 100644 --- a/build/pkgs/gap/checksums.ini +++ b/build/pkgs/gap/checksums.ini @@ -1,4 +1,4 @@ tarball=gap-VERSION.tar.gz -sha1=cf91834954849dbaeae17079a4c4565bc28d03a8 -sha256=9794dbdba6fb998e0a2d0aa8ce21fc8848ad3d3f9cc9993b0b8e20be7e1dbeba +sha1=403c261db32049cc124b8f43221f7c57f2616133 +sha256=845f5272c26feb1b8eb9ef294bf0545f264c1fe5a19b0601bbc65d79d9506487 upstream_url=https://github.com/gap-system/gap/releases/download/vVERSION/gap-VERSION.tar.gz diff --git a/build/pkgs/gap/package-version.txt b/build/pkgs/gap/package-version.txt index 56d2fb548a2..c412a4e2e1e 100644 --- a/build/pkgs/gap/package-version.txt +++ b/build/pkgs/gap/package-version.txt @@ -1 +1 @@ -4.13.1 +4.14.0 diff --git a/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch b/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch deleted file mode 100644 index 46f5e36f109..00000000000 --- a/build/pkgs/gap/patches/0001-Makefile.rules-Darwin-Remove-use-of-single_module-ob.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 89b95994807970d90671e1e02cc03ddca4cf0a10 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Sat, 8 Jun 2024 14:01:16 -0700 -Subject: [PATCH] Makefile.rules [Darwin]: Remove use of '-single_module' - (obsolete), activate '-install_name' - ---- - Makefile.rules | 5 +---- - 1 file changed, 1 insertion(+), 4 deletions(-) - -diff --git a/Makefile.rules b/Makefile.rules -index 8bfd3dba0..adfed731f 100644 ---- a/Makefile.rules -+++ b/Makefile.rules -@@ -446,10 +446,7 @@ else ifneq (,$(findstring darwin,$(host_os))) - LINK_SHLIB_FLAGS = -dynamiclib - LINK_SHLIB_FLAGS += -compatibility_version $(LIBGAP_COMPAT_VER) - LINK_SHLIB_FLAGS += -current_version $(LIBGAP_CURRENT_VER) -- LINK_SHLIB_FLAGS += -Wl,-single_module -- -- # TODO: set install_name, at least for installed version of the lib? -- #LINK_SHLIB_FLAGS += -install_name $(libdir)/$(LIBGAP_FULL) -+ LINK_SHLIB_FLAGS += -install_name $(libdir)/$(LIBGAP_FULL) - - GAP_CPPFLAGS += -DPIC - GAP_CFLAGS += -fno-common --- -2.42.0 - diff --git a/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch b/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch deleted file mode 100644 index c99b39ab91c..00000000000 --- a/build/pkgs/gap/patches/gap-4.13.1-hash-fixes.patch +++ /dev/null @@ -1,178 +0,0 @@ -diff --git a/lib/dicthf.gi b/lib/dicthf.gi -index 5ee7341..e4349ac 100644 ---- a/lib/dicthf.gi -+++ b/lib/dicthf.gi -@@ -152,16 +152,37 @@ end); - ## - InstallMethod(SparseIntKey,"for bounded tuples",true, - [ IsList,IsList and IsCyclotomicCollection ], 0, --function(m,v) --local c; -- if Length(m)<>3 or m[1]<>"BoundedTuples" then -+function(m, v) -+ if Length(m)<> 3 or m[1]<>"BoundedTuples" then - TryNextMethod(); - fi; -- c:=[1,Maximum(m[2])+1]; -- return function(a) -- return a*c; -+ # Due to the way BoundedTuples are presently implemented we expect the input -+ # to the hash function to always be a list of positive immediate integers. This means -+ # that using HashKeyWholeBag should be safe. -+ return function(x) -+ Assert(1, IsPositionsList(x)); -+ if not IsPlistRep(x) then -+ x := AsPlist(x); -+ fi; -+ return HashKeyWholeBag(x, 1); - end; - -+ # alternative code w/o HashKeyBag -+ ## build a weight vector to distinguish lists. Make entries large while staying clearly within -+ ## immediate int (2^55 replacing 2^60, since we take subsequent primes). -+ #step:=NextPrimeInt(QuoInt(2^55,Maximum(m[2])*m[3])); -+ #weights:=[1]; -+ #len:=Length(v); -+ ## up to 56 full, then increasingly reduce -+ #len:=Minimum(len,8*RootInt(len)); -+ #while Length(weights)0 and ForAll(pnt,IsPosInt) and -+ ForAll(acts,IsPerm) and - (act=OnSets or act=OnPoints or act=OnRight or act=\^)) then - TryNextMethod(); - fi; -diff --git a/lib/vecmat.gi b/lib/vecmat.gi -index 017c3c6..93ba828 100644 ---- a/lib/vecmat.gi -+++ b/lib/vecmat.gi -@@ -2142,7 +2142,8 @@ InstallMethod(DomainForAction,"matrix/matrix",IsElmsCollsX, - function(pnt,acts,act) - local l,f; - if (not ForAll(acts,IsMatrix)) or -- (act<>OnPoints and act<>OnSubspacesByCanonicalBasis and act<>OnRight) then -+ (act<>OnPoints and act<>OnSubspacesByCanonicalBasis and act<>OnRight and act<>OnSets and -+ act<>OnTuples) then - TryNextMethod(); # strange operation, might extend the domain - fi; - l:=NaturalActedSpace(acts,pnt); -diff --git a/tst/testbugfix/2024-09-14-actdomain.tst b/tst/testbugfix/2024-09-14-actdomain.tst -new file mode 100644 -index 0000000..84f8b50 ---- /dev/null -+++ b/tst/testbugfix/2024-09-14-actdomain.tst -@@ -0,0 +1,85 @@ -+# Fix #5786 and error reported by Len Soicher in support list -+gap> gg:=SpecialUnitaryGroup(4,2);; -+gap> hl:=Z(2)*[ -+> [0,0,1,0], -+> [1,1,0,0], -+> [0,1,0,1], -+> [0,1,1,0], -+> [1,1,0,1]];; -+gap> o:=Orbit(gg,Set(hl),OnSets);; -+gap> Length(o); -+216 -+gap> set:=[ 1,10,15,24,29,33,38,40,44,59, 60, 63, 69, 74, 77,79,85, 86, 90, -+> 95, 99, 103, 105, 110, 122, 125, 143, 148, 149, 153, 162, 165, 174, 182, -+> 185, 191, 197, 198, 202, 218, 223, 227, 228, 235, 236, 240, 243, 248, -+> 254, -+> 256, 259, 270, 275, 288, 291, 295, 298, 302, 305, 310, 312, 315, 325, -+> 329, -+> 333, 340, 341, 350, 356, 366, 369, 381, 385, 390, 397, 402, 412, 414, -+> 419, -+> 421, 425, 428, 433, 436, 445, 447, 451, 452, 453, 454, 461, 466, 474, -+> 479, -+> 481, 489, 490, 493, 497, 507, 509, 512, 513, 519, 521 ];; -+gap> gp:=Group( -+> ( 1,340,124,306,216,492,100, 25,108,270,220,332)( 2,138, 54,161,132,159,198, -+> 336,439,269, 89,419)( 3,467,177,404,505,437,379,312,481,271,223,135) -+> ( 4,510, 79,504,259,234,378,251,272,268,360,303)( 5,278,176,191,231,275,263, -+> 190,230,146,265,192)( 6,486,126,523,490,448,375,237,288,400,243,329) -+> ( 7,131,123,516, 48,392,350,333,418, 16,139,175)( 8,289,125,386,241, 29,376, -+> 334,242,417,442,331)( 9,430, 32, 59,446,367,377,335,411,416,515,330) -+> ( 10,391, 56,407,475,414,200,328,165,473, 86,119)( 11,368, 35,390,522,408,199, -+> 415,440,326, 87,503)( 12,412, 55,457,399,245,201, 33,438,431, 88,317) -+> ( 13,471, 40,348,452,292, 43,346,373, 77, 41,347)( 14,137,174,162, 60, 69,321, -+> 487, 61,158,322,370)( 15,101,114,109,130,160,488,489,352,351,420, 17) -+> ( 18,339,167,290,202,385, 99, 22, 90,323,217,129)( 19, 26, 93,304, 96,342) -+> ( 20,338,166,305,215,141, 97, 24, 51,150,219,507)( 21,337, 63,186,214,424, 98, -+> 23,107,382,218,349)( 27, 91,445,451,525, 67,519,239,144,203,155,353) -+> ( 28,324,444,128, 70,428,496,238,286,300,283, 64)( 30,236,287,441,387,354) -+> ( 31,345,366,517, 45,344,413,521, 46,248,244,121)( 34,314,394,402,222,447, 81, -+> 282,262,173,246,435)( 36,482,178,364,148,495,179,363,140,102,113,111) -+> ( 37,253,273,168,294,302,226,183, 72,480,154,233)( 38,483,520,393,403,465,362, -+> 298,143,356,153,369)( 39,157,320,472)( 42,228,277,264)( 44,343,147,501) -+> ( 47, 73,308,380,184,389,310,327,163,295,151,425)( 49,221,456, 80,474,260,405, -+> 325,164,524,152,449)( 50,479,365,477,461,459,497,169,296,247,134,117) -+> ( 52,361,299,285,355,188,423,464,434,453,133,118)( 53,257,509, 68,511,458,293, -+> 204,384,374, 75, 82)( 57,116,112,149,514,396,470,485,493,249,421,120) -+> ( 58,500,266,250,429,122)( 62,156,319,311)( 65,187,225,357,127, 71,388,235, -+> 460,252,274,371)( 66,106,462,291,205,383,372, 76, 92,410,280,498) -+> ( 74,401,381,476,409,281,171,104,297,307,426,182)( 78, 84,261,256,180,436,512, -+> 313,181,491,224,499)( 83,466,255,508,506,395,469,422,142,103,115,110) -+> ( 85,468,258,502,267,136)( 94,341)( 95,211)(105,478,195,432,518,316,197,484, -+> 494,455,196,170)(145,513,359,232,227,254)(172,209,398,207,279,206) -+> (185,194,309,443)(189,406,463,318,450,427,433,454,315,301,284,358) -+> (193,229,276,240)(208,397)(210,213,212) -+> ,( 1,379,148, 48,128,430,416)( 2, 34,338, 35,235,131,521)( 3,512,352, 47, -+> 318,289,237)( 4,272,506, 49,434,486,282)( 5,524,485, 10,483,340, 55) -+> ( 6,458, 36,487, 60,121, 16)( 7,313,140,336,127,435,270)( 8, 85,147,489, -+> 98,201,417)( 9,469, 94,488,129,329,400)( 11,291, 26, 54,234,473,169) -+> ( 12,207,339, 56,233,503,515)( 13,426,337, 40,232,295,500)( 14, 32,414, 27, -+> 167,130,472)( 15, 33,188, 38,382,109,501)( 17, 31,459, 37,496,132,517) -+> ( 18,263,294,446,451,134,497)( 19,198,525,241,441,244,470)( 20,100,199,490, -+> 242,429,413)( 21,378,403,216,523,421, 58)( 22,124,159, 77, 63,123,292) -+> ( 23,176,275,431,168, 86,293)( 24,177,492,326,104,151,290)( 25, 79,437,269, -+> 163,152,144)( 28,462,150,162, 62,120,415)( 29,239, 83,311, 61,117,260) -+> ( 30,452,149,370, 39,122,389)( 41,468, 73,254,277,432,371)( 42,210,319,502, -+> 373,205,283)( 43,387,194,212,320,508, 99)( 44,396,349,331,399,250,420) -+> ( 45,411,461,375,475,377,418)( 46,344,409,519,522,477,419)( 50,253,278,433, -+> 231, 88,422)( 51,482,138,358,229,463,381)( 52, 91,467,221,230,518,484) -+> ( 53,390,510,494,228,454, 92)( 57,310,460,118,259,367,363)( 59,281,227,274, -+> 505,402,215)( 64,125,245, 76, 93,160,471)( 65,126,408, 75,166, 69,264) -+> ( 66,302,186,116,257,424,327)( 67,187,297,479,217,425,171)( 68,303,296,345, -+> 280,226,273)( 70,240,359,366,364, 97,200)( 71,265,360,394,393,133,423) -+> ( 72,251,146,164,268, 87,312)( 74,252,276,406,513, 89,181)( 78,395,158,185, -+> 315,287,333)( 80,136,351,156,316,286,334)( 81,511,350,184,317,288,335) -+> ( 82,261,392,183,105,309,238)( 84,224,439,182,284,193,236)( 90,466,391,357, -+> 443,170,465)( 95,321,509,353,203,243,197)( 96,516,498,354,300,246,401) -+> (101,405,453,464,154,112,341)(102,139,450,208,388,365,111)(103,440,520,285, -+> 192,266,343)(106,308,218,119,262,448,298)(107,219,368,356,442,301,209) -+> (108,514,412,355,376,474,222)(110,495,137,478,361,444,380)(113,255,342,175, -+> 173,247,305)(114,256,141,328,153,307,304)(115,258,385,325,172,214,306) -+> (135,493,157,455,362,445,407)(142,346,189,398,323,161,499)(143,190,145,436, -+> 271,165,314)(155,195,211,174,404,374,204)(178,507,332,330,248,249,179) -+> (180,372,202,386,196,213,322)(191,267,491,384,438,279,225)(206,428,299,369, -+> 480,220,449)(223,481,383,427,397,324,504)(347,457,447,410,348,456,476));; -+gap> Length(Orbit(gp,set,OnSets)); -+241920 -diff --git a/tst/teststandard/hash2.tst b/tst/teststandard/hash2.tst -index 6bfa4d3..d47452e 100644 ---- a/tst/teststandard/hash2.tst -+++ b/tst/teststandard/hash2.tst -@@ -32,4 +32,8 @@ gap> Length(Orbit(h,h.1[1],OnRight)); - 2079 - gap> Length(Orbit(h,h.2^5,OnPoints)); - 693 -+gap> Length(Orbit(SymmetricGroup(14), [1 .. 7], OnSets)); -+3432 -+gap> Length(Orbit(SymmetricGroup(16), [1 .. 8], OnSets)); -+12870 - gap> STOP_TEST( "hash2.tst", 1); diff --git a/build/pkgs/gap/spkg-check.in b/build/pkgs/gap/spkg-check.in index 6dc12ca9266..917c8eb7209 100644 --- a/build/pkgs/gap/spkg-check.in +++ b/build/pkgs/gap/spkg-check.in @@ -1,29 +1,3 @@ cd src -# #28728: Fix test failure in tst/testinstall/strings.tst -export LC_CTYPE=en_US.UTF-8 - -# #34391: in GAP 4.12 some packages need GAP package io -# to let tests run, otherwise this hangs. Thus we install io here. -cd pkg/io -./configure --with-gaproot=../.. -make -cd ../.. - -# This is the same as 'dev/ci.sh testinstall' (but dev/ci.sh is not part of the GAP tarball) -./gap tst/testinstall.g -if [[ $? -ne 0 ]]; then - exit 1 -fi - -LOG=dev/log/testinstall2_* - -echo "================================================================" -echo "Test log:" -cat $LOG -echo "================================================================" - -ERRORS=`grep ^##### $LOG` -if [[ ! -z "$ERRORS" ]]; then - exit 1 -fi +$MAKE check diff --git a/build/pkgs/h11/spkg-configure.m4 b/build/pkgs/h11/spkg-configure.m4 new file mode 100644 index 00000000000..4e2f4839a5f --- /dev/null +++ b/build/pkgs/h11/spkg-configure.m4 @@ -0,0 +1 @@ +SAGE_SPKG_CONFIGURE([h11], [SAGE_PYTHON_PACKAGE_CHECK([h11])]) diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt index 040b5f2631a..77d6c0cb0c5 100644 --- a/build/pkgs/sage_conf/version_requirements.txt +++ b/build/pkgs/sage_conf/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.6b5 +sage-conf ~= 10.6b6 diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt index c0283b9405a..50eb65e2172 100644 --- a/build/pkgs/sage_docbuild/version_requirements.txt +++ b/build/pkgs/sage_docbuild/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.6b5 +sage-docbuild ~= 10.6b6 diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt index bd4234f31a6..06adbdc6df6 100644 --- a/build/pkgs/sage_setup/version_requirements.txt +++ b/build/pkgs/sage_setup/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.6b5 +sage-setup ~= 10.6b6 diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt index e856ed4cc63..dd05f21376e 100644 --- a/build/pkgs/sage_sws2rst/version_requirements.txt +++ b/build/pkgs/sage_sws2rst/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.6b5 +sage-sws2rst ~= 10.6b6 diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt index c6e36270655..e64a5d0c206 100644 --- a/build/pkgs/sagelib/version_requirements.txt +++ b/build/pkgs/sagelib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-standard ~= 10.6b5 +sagemath-standard ~= 10.6b6 diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt index bf5262fa6eb..d88731f06e9 100644 --- a/build/pkgs/sagemath_bliss/version_requirements.txt +++ b/build/pkgs/sagemath_bliss/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-bliss ~= 10.6b5 +sagemath-bliss ~= 10.6b6 diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt index 39c5c3b7890..198cb911336 100644 --- a/build/pkgs/sagemath_categories/version_requirements.txt +++ b/build/pkgs/sagemath_categories/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.6b5 +sagemath-categories ~= 10.6b6 diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt index d1ebc2d30a9..7415f70536b 100644 --- a/build/pkgs/sagemath_coxeter3/version_requirements.txt +++ b/build/pkgs/sagemath_coxeter3/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-coxeter3 ~= 10.6b5 +sagemath-coxeter3 ~= 10.6b6 diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt index c88968f714c..09106be9ba3 100644 --- a/build/pkgs/sagemath_environment/version_requirements.txt +++ b/build/pkgs/sagemath_environment/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.6b5 +sagemath-environment ~= 10.6b6 diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt index ef5a7f503b7..bff6125dcaf 100644 --- a/build/pkgs/sagemath_mcqd/version_requirements.txt +++ b/build/pkgs/sagemath_mcqd/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-mcqd ~= 10.6b5 +sagemath-mcqd ~= 10.6b6 diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt index 569e25992ab..662a7b7e7f6 100644 --- a/build/pkgs/sagemath_meataxe/version_requirements.txt +++ b/build/pkgs/sagemath_meataxe/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-meataxe ~= 10.6b5 +sagemath-meataxe ~= 10.6b6 diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt index 2e920aa86d6..df4dc506909 100644 --- a/build/pkgs/sagemath_objects/version_requirements.txt +++ b/build/pkgs/sagemath_objects/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.6b5 +sagemath-objects ~= 10.6b6 diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt index cfff7dea4c2..5f906d7000a 100644 --- a/build/pkgs/sagemath_repl/version_requirements.txt +++ b/build/pkgs/sagemath_repl/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.6b5 +sagemath-repl ~= 10.6b6 diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt index b0d92f0ad87..9077fb7fff0 100644 --- a/build/pkgs/sagemath_sirocco/version_requirements.txt +++ b/build/pkgs/sagemath_sirocco/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-sirocco ~= 10.6b5 +sagemath-sirocco ~= 10.6b6 diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt index 0d1d8e4eaaa..37d47b62c5f 100644 --- a/build/pkgs/sagemath_tdlib/version_requirements.txt +++ b/build/pkgs/sagemath_tdlib/version_requirements.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-tdlib ~= 10.6b5 +sagemath-tdlib ~= 10.6b6 diff --git a/build/pkgs/symengine_py/spkg-configure.m4 b/build/pkgs/symengine_py/spkg-configure.m4 index 9559bdb6da0..b065d24fbb9 100644 --- a/build/pkgs/symengine_py/spkg-configure.m4 +++ b/build/pkgs/symengine_py/spkg-configure.m4 @@ -1 +1 @@ -SAGE_SPKG_CONFIGURE([symengine_py], [SAGE_PYTHON_PACKAGE_CHECK([symengine_py])]) +SAGE_SPKG_CONFIGURE([symengine_py], [SAGE_PYTHON_PACKAGE_CHECK([symengine])]) diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-conf_conda/VERSION.txt +++ b/pkgs/sage-conf_conda/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-bliss/VERSION.txt +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-categories/MANIFEST.in.m4 b/pkgs/sagemath-categories/MANIFEST.in.m4 index b2e35673ec0..ae0c1085e3d 100644 --- a/pkgs/sagemath-categories/MANIFEST.in.m4 +++ b/pkgs/sagemath-categories/MANIFEST.in.m4 @@ -42,7 +42,6 @@ global-exclude *.c global-exclude *.cpp global-exclude all__sagemath_*.* -global-include all__sagemath_categories.py global-exclude __pycache__ global-exclude *.py[co] diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-coxeter3/VERSION.txt +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-mcqd/VERSION.txt +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-meataxe/VERSION.txt +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-sirocco/VERSION.txt +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/pkgs/sagemath-tdlib/VERSION.txt +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/src/VERSION.txt b/src/VERSION.txt index d201bc08e28..8b3698135db 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.6.beta5 +10.6.beta6 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index a4df2cecd2c..d1e2aa14731 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.6.beta5' -SAGE_RELEASE_DATE='2025-01-26' -SAGE_VERSION_BANNER='SageMath version 10.6.beta5, Release Date: 2025-01-26' +SAGE_VERSION='10.6.beta6' +SAGE_RELEASE_DATE='2025-02-10' +SAGE_VERSION_BANNER='SageMath version 10.6.beta6, Release Date: 2025-02-10' diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 02936d3e423..02ebb75c8a3 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -979,6 +979,14 @@ written. checked, as they are the most likely to be broken, now or in the future. This probably belongs to the TESTS block (see :ref:`section-docstring-function`). +- **Interruption:** if the function might take a very long time, use + :func:`~sage.doctest.util.ensure_interruptible_after` to check that the user + can interrupt it. For example, the following tests ``sleep(3)`` can be + interrupted after 1 second:: + + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(1) as data: sleep(3) + - **Systematic tests** of all small-sized inputs, or tests of **random** instances if possible. @@ -1106,12 +1114,10 @@ written. The :ref:`doctest fixer ` uses tab stops at columns 48, 56, 64, ... for these tags. -- **Split long lines:** You may want to split long lines of code with a - backslash. Note: this syntax is non-standard and may be removed in the - future:: +- **Split long lines:** Standard Python rules apply. For example:: - sage: n = 123456789123456789123456789\ - ....: 123456789123456789123456789 + sage: n = (123456789123456789123456789 + + ....: 123456789123456789123456789) sage: n.is_prime() False diff --git a/src/doc/en/installation/meson.rst b/src/doc/en/installation/meson.rst index e0051dbbf68..45ba64c4100 100644 --- a/src/doc/en/installation/meson.rst +++ b/src/doc/en/installation/meson.rst @@ -88,12 +88,11 @@ To configure the project, we need to run the following command: $ meson setup builddir --prefix=$PWD/build-install This will create a build directory ``builddir`` that will hold the build artifacts. -The ``--prefix`` option specifies the directory where the Sage will be installed. +The ``--prefix`` option specifies the directory where the Sage will be installed, +and can be omitted when ``pip`` is used to install as explained below. -If pip is used as above, ``builddir`` is set to be +If pip is used as above with ``--editable``, ``builddir`` is set to be ``build/cp[Python major version][Python minor version]``, such as ``build/cp311``. -``--prefix=`` can be left unspecified, when conda is used then meson will -install to the conda environment e.g. ``$HOME/miniforge3/envs/sage-dev/``. To compile the project, run the following command: @@ -117,7 +116,8 @@ Usually, this directory is not on your Python path, so you have to use: When editable install is used, it is not necessary to reinstall after each compilation. -Alternatively, we can still use pip to install: +Alternatively, we can still use pip to install (which does not require specifying +``--prefix`` in advance and automatically works with conda environment): .. CODE-BLOCK:: shell-session @@ -134,7 +134,11 @@ Alternatively, we can still use pip to install: $ meson setup builddir --prefix=/usr --libdir=... -Dcpp_args=... $ meson compile -C builddir $ DESTDIR=/path/to/staging/root meson install -C builddir - + + With the `default `_ prefix + being ``/usr/local``, it may then install to + ``$DESTDIR/usr/local/lib/python3.12/site-packages/sage``. + See `Meson's quick guide `_ and `Meson's install guide `_ for more information. @@ -144,6 +148,7 @@ Miscellaneous tips The environment variable ``MESONPY_EDITABLE_VERBOSE=1`` can be set while running ``./sage``, so that when Cython files are recompiled a message is printed out. +See ``_. If a new ``.pyx`` file is added, it need to be added to ``meson.build`` file in the containing directory. diff --git a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst index 958a378f945..5870df3d88a 100644 --- a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst +++ b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst @@ -25,7 +25,7 @@ the standard deviation:: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: np.mean([1, 2, 3, 5]) 2.75 diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 31cb3ec8074..4fef159bfd0 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -568,6 +568,10 @@ REFERENCES: Springer Verlag 2006; pre-print available at http://eprint.iacr.org/2005/200 +.. [BQ2024] Asilata Bapat and Hoel Queffelec. + *Some remarks about the faithfulness of the Burau representation + of Artin-Tits groups*. Preprint, 2024. :arxiv:`2409.00144`. + .. [BBS1982] \L. Blum, M. Blum, and M. Shub. Comparison of Two Pseudo-Random Number Generators. *Advances in Cryptology: Proceedings of Crypto '82*, pp.61--78, 1982. @@ -2099,6 +2103,10 @@ REFERENCES: Adv. Math. ***177** no. 1 (2002) pp. 115-179. :arxiv:`math/0203127`. +.. [Day1979] Alan Day, *Characterizations of Finite Lattices that are + Bounded-Homomorphic Images or Sublattices of Free Lattices*, + Canadian Journal of Mathematics 31 (1979), 69-78 + .. [DB1996] K. Duggal, A. Bejancu, *Lightlike Submanifolds of Semi-Riemannian Manifolds and Applications*, Mathematics and Its Applications, 1996. @@ -3689,6 +3697,9 @@ REFERENCES: .. [Ja1971] \N. Jacobson. *Exceptional Lie Algebras*. Marcel Dekker, Inc. New York. 1971. IBSN No. 0-8247-1326-5. +.. [Jer2006] Mark Jerrum. *Two remarks concerning balanced matroids*. + Combinatorica 26, no. 6 (2006): 733-742. + .. [Jet2008] \D. Jetchev. Global divisibility of Heegner points and Tamagawa numbers. Compos. Math. 144 (2008), no. 4, 811--826. @@ -4991,6 +5002,10 @@ REFERENCES: *Yangians and classical Lie algebras*. (1994) :arxiv:`hep-th/9409025` +.. [MNWW2011] Dillon Mayhew, Mike Newman, Dominic Welsh, and Geoff Whittle. + *On the asymptotic proportion of connected matroids*. + European Journal of Combinatorics. 2011 Aug 1;32(6):882-90. + .. [Mol2007] Alexander Ivanovich Molev. *Yangians and Classical Lie Algebras*. Mathematical Surveys and Monographs. diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 12a5cebe3aa..2a039fb920b 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -123,18 +123,15 @@ This base class provides a lot more methods than a general parent:: 'algebraic_closure', 'an_embedding', 'base_extend', - 'divides', 'epsilon', 'extension', 'fraction_field', 'gen', 'gens', - 'integral_closure', 'is_field', 'ngens', 'one', 'order', - 'prime_subfield', 'random_element', 'zero', 'zeta', diff --git a/src/doc/en/thematic_tutorials/lie/branching_rules.rst b/src/doc/en/thematic_tutorials/lie/branching_rules.rst index f2c24f0e8be..422c08edba2 100644 --- a/src/doc/en/thematic_tutorials/lie/branching_rules.rst +++ b/src/doc/en/thematic_tutorials/lie/branching_rules.rst @@ -344,7 +344,7 @@ we could accomplish the branching in two steps, thus:: sage: A1xA1 = WeylCharacterRing("A1xA1", style="coroots") sage: reps = [A3(fw) for fw in A3.fundamental_weights()] sage: [pi.branch(C2, rule="symmetric").branch(B2, rule="isomorphic"). \ - branch(D2, rule="extended").branch(A1xA1, rule="isomorphic") for pi in reps] + ....: branch(D2, rule="extended").branch(A1xA1, rule="isomorphic") for pi in reps] [A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)] As you can see, we've redone the branching rather circuitously this diff --git a/src/doc/en/thematic_tutorials/lie/crystals.rst b/src/doc/en/thematic_tutorials/lie/crystals.rst index fc3d867e532..ae64b3b5278 100644 --- a/src/doc/en/thematic_tutorials/lie/crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/crystals.rst @@ -741,7 +741,7 @@ irreducible crystal. We can make four such crystals:: sage: C = crystals.Letters("A2") sage: T = crystals.TensorProduct(C,C,C) sage: [T1,T2,T3,T4] = \ - [crystals.TensorProduct(C,C,C,generators=[v]) for v in T.highest_weight_vectors()] + ....: [crystals.TensorProduct(C,C,C,generators=[v]) for v in T.highest_weight_vectors()] sage: [B.cardinality() for B in [T1,T2,T3,T4]] [10, 8, 8, 1] sage: [B.character(A2) for B in [T1,T2,T3,T4]] @@ -754,7 +754,7 @@ We see that two of these crystals are isomorphic, with character sage: C = crystals.Letters("A2") sage: T = crystals.TensorProduct(C,C,C) sage: [T1,T2,T3,T4] = \ - [crystals.TensorProduct(C,C,C,generators=[v]) for v in T.highest_weight_vectors()] + ....: [crystals.TensorProduct(C,C,C,generators=[v]) for v in T.highest_weight_vectors()] sage: T1.plot() Graphics object consisting of 35 graphics primitives sage: T2.plot() diff --git a/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst b/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst index 925e5312882..ab774ca8fd1 100644 --- a/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst +++ b/src/doc/en/thematic_tutorials/numerical_sage/numpy.rst @@ -8,7 +8,7 @@ import it. sage: import numpy sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") # to ensure numpy 2.0 compatibility + ....: _ = numpy.set_printoptions(legacy="1.25") # to ensure numpy 2.0 compatibility The basic object of computation in NumPy is an array. It is simple to create an array. diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst index 417f10e4dbd..0c7b2b05a18 100644 --- a/src/doc/en/thematic_tutorials/sandpile.rst +++ b/src/doc/en/thematic_tutorials/sandpile.rst @@ -1174,7 +1174,7 @@ dict (configuration) EXAMPLES:: sage: g = {0:{},1:{0:1,3:1,4:1},2:{0:1,3:1,5:1}, \ - 3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}} + ....: 3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}} sage: S = Sandpile(g,0) sage: S.burning_config() {1: 2, 2: 0, 3: 1, 4: 1, 5: 0} @@ -1226,7 +1226,7 @@ OUTPUT: dict EXAMPLES:: sage: g = {0:{},1:{0:1,3:1,4:1},2:{0:1,3:1,5:1},\ - 3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}} + ....: 3:{2:1,5:1},4:{1:1,3:1},5:{2:1,3:1}} sage: S = Sandpile(g,0) sage: S.burning_config() {1: 2, 2: 0, 3: 1, 4: 1, 5: 0} diff --git a/src/sage/all__sagemath_categories.py b/src/sage/all__sagemath_categories.py deleted file mode 100644 index cb37b0baf2f..00000000000 --- a/src/sage/all__sagemath_categories.py +++ /dev/null @@ -1,6 +0,0 @@ -# sage_setup: distribution = sagemath-categories -from sage.all__sagemath_objects import * - -from sage.categories.all import * - -from sage.rings.all__sagemath_categories import * diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 5525fae666c..f4d8f87b78b 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -265,7 +265,7 @@ def norm(v): raise NotImplementedError("proof and height bound only implemented for real and complex numbers") else: - from sage.libs.pari.all import pari + from sage.libs.pari import pari y = pari(z) f = y.algdep(degree) @@ -378,7 +378,7 @@ def bernoulli(n, algorithm='default', num_threads=1): from sage.libs.flint.arith_sage import bernoulli_number as flint_bernoulli return flint_bernoulli(n) elif algorithm == 'pari' or algorithm == 'gp': - from sage.libs.pari.all import pari + from sage.libs.pari import pari x = pari(n).bernfrac() # Use the PARI C library return Rational(x) elif algorithm == 'gap': @@ -470,7 +470,7 @@ def factorial(n, algorithm='gmp'): if algorithm == 'gmp': return ZZ(n).factorial() elif algorithm == 'pari': - from sage.libs.pari.all import pari + from sage.libs.pari import pari return pari.factorial(n) else: raise ValueError('unknown algorithm') @@ -3164,7 +3164,7 @@ def __call__(self, n): return ZZ.zero() if n <= 2: return ZZ.one() - from sage.libs.pari.all import pari + from sage.libs.pari import pari return ZZ(pari(n).eulerphi()) def plot(self, xmin=1, xmax=50, pointsize=30, rgbcolor=(0, 0, 1), @@ -4425,7 +4425,7 @@ def primitive_root(n, check=True): sage: primitive_root(mpz(-46)) # needs sage.libs.pari 5 """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari if not check: return ZZ(pari(n).znprimroot()) n = ZZ(n).abs() @@ -4482,7 +4482,7 @@ def nth_prime(n): """ if n <= 0: raise ValueError("nth prime meaningless for nonpositive n (=%s)" % n) - from sage.libs.pari.all import pari + from sage.libs.pari import pari return ZZ(pari.prime(n)) @@ -4600,7 +4600,7 @@ def __call__(self, n): # Use fast PARI algorithm if n == 0: return ZZ.zero() - from sage.libs.pari.all import pari + from sage.libs.pari import pari return ZZ(pari(n).moebius()) def __repr__(self): @@ -4685,7 +4685,7 @@ def range(self, start, stop=None, step=None): return self.range(start, 0, step) + [ZZ.zero()] +\ self.range(step, stop, step) - from sage.libs.pari.all import pari + from sage.libs.pari import pari if step == 1: v = pari('vector(%s, i, moebius(i-1+%s))' % (stop - start, start)) @@ -4815,7 +4815,7 @@ def number_of_divisors(n): m = ZZ(n) if m.is_zero(): raise ValueError("input must be nonzero") - from sage.libs.pari.all import pari + from sage.libs.pari import pari return ZZ(pari(m).numdiv()) @@ -4888,7 +4888,7 @@ def hilbert_symbol(a, b, p, algorithm='pari'): if algorithm == "pari": if p == -1: p = 0 - from sage.libs.pari.all import pari + from sage.libs.pari import pari return ZZ(pari(a).hilbert(b, p)) elif algorithm == 'direct': diff --git a/src/sage/calculus/transforms/fft.pyx b/src/sage/calculus/transforms/fft.pyx index 1f190f00685..eb82b397766 100644 --- a/src/sage/calculus/transforms/fft.pyx +++ b/src/sage/calculus/transforms/fft.pyx @@ -163,6 +163,16 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): Traceback (most recent call last): ... TypeError: unable to convert 1.0*I to float; use abs() or real_part() as desired + + TESTS: + + Verify that :issue:`10758` is fixed. :: + + sage: F = FFT(1) + sage: F[0] = (1,1) + sage: F[0] = 1 + sage: F[0] + (1.0, 0.0) """ # just set real for now if i < 0 or i >= self.n: @@ -172,6 +182,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): self.data[2*i+1] = xy[1] else: self.data[2*i] = xy + self.data[2*i+1] = 0 def __getitem__(self, i): """ diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index feb1b2cb393..07383a84f68 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -943,6 +943,22 @@ def all_super_categories(self, proper=False): appropriate. Simply because lazy attributes are much faster than any method. + .. NOTE:: + + This is not the same as the concept of super category in mathematics. + In fact, this is not even the opposite relation of :meth:`is_subcategory`:: + + sage: A = VectorSpaces(QQ); A + Category of vector spaces over Rational Field + sage: B = VectorSpaces(QQ.category()); B + Category of vector spaces over (number fields and quotient fields and metric spaces) + sage: A.is_subcategory(B) + True + sage: B in A.all_super_categories() + False + + .. SEEALSO:: :meth:`_test_category_graph` + EXAMPLES:: sage: C = Rings(); C @@ -1379,7 +1395,16 @@ def _test_category_graph(self, **options): method resolution order of the parent and element classes. This method checks this. - .. TODO:: currently, this won't work for hom categories. + Note that if + :meth:`~sage.structure.category_object.CategoryObject._refine_category_` + is called at unexpected times, the invariant might be false. Most + commonly, this happens with rings like ``Zmod(n)`` or ``SR``, where + a check like ``Zmod(n) in Fields()`` is needed (which checks the primality + of `n`) to refine their category to be a subcategory of fields. + + .. SEEALSO:: + + :meth:`CategoryWithParameters._make_named_class_key` EXAMPLES:: @@ -1615,6 +1640,11 @@ def subcategory_class(self): sage: isinstance(AlgebrasWithBasis(QQ), cls) True + .. NOTE:: + + See the note about :meth:`_test_category_graph` regarding Python + class hierarchy. + TESTS:: sage: cls = Algebras(QQ).subcategory_class; cls @@ -1667,6 +1697,11 @@ def parent_class(self): :class:`~sage.categories.bimodules.Bimodules`, :class:`~sage.categories.category_types.Category_over_base` and :class:`sage.categories.category.JoinCategory`. + + .. NOTE:: + + See the note about :meth:`_test_category_graph` regarding Python + class hierarchy. """ return self._make_named_class('parent_class', 'ParentMethods') @@ -1713,6 +1748,11 @@ def element_class(self): 0 .. SEEALSO:: :meth:`parent_class` + + .. NOTE:: + + See the note about :meth:`_test_category_graph` regarding Python + class hierarchy. """ return self._make_named_class('element_class', 'ElementMethods') @@ -1757,7 +1797,7 @@ def required_methods(self): # Operations on the lattice of categories def is_subcategory(self, c): """ - Return ``True`` if ``self`` is naturally embedded as a subcategory of `c`. + Return ``True`` if there is a natural forgetful functor from ``self`` to `c`. EXAMPLES:: @@ -2046,13 +2086,18 @@ def _with_axiom(self, axiom): Return the subcategory of the objects of ``self`` satisfying the given ``axiom``. + Note that this is a private method thus should not be directly + used, see below. + INPUT: - ``axiom`` -- string, the name of an axiom EXAMPLES:: - sage: Sets()._with_axiom("Finite") + sage: Sets()._with_axiom("Finite") # not idiomatic + Category of finite sets + sage: Sets().Finite() # recommended Category of finite sets sage: type(Magmas().Finite().Commutative()) @@ -2068,7 +2113,7 @@ def _with_axiom(self, axiom): sage: Sets()._with_axiom("Associative") Category of sets - .. WARNING:: This may be changed in the future to raising an error. + .. WARNING:: This may be changed in the future to raise an error. """ return Category.join(self._with_axiom_as_tuple(axiom)) @@ -2718,6 +2763,10 @@ def _make_named_class(self, name, method_provider, cache=False, **options): It is assumed that this method is only called from a lazy attribute whose name coincides with the given ``name``. + Currently, this means :meth:`Category.subcategory_class`, + :meth:`Category.parent_class` or :meth:`element_class`. + + Subclasses need to implement :meth:`_make_named_class_key`. OUTPUT: @@ -2810,6 +2859,10 @@ def _make_named_class(self, name, method_provider, cache=False, **options): pass result = Category._make_named_class(self, name, method_provider, cache=cache, **options) + if key[2] != self._make_named_class_key(name): + # the object in the parameter may have had its category refined, which might modify the key + # throw result away and recompute + return self._make_named_class(name, method_provider, cache=cache, **options) self._make_named_class_cache[key] = result return result @@ -2818,6 +2871,50 @@ def _make_named_class_key(self, name): r""" Return what the element/parent/... class depend on. + This method starts as an optimization to allow different related + categories to share the Python types, see :issue:`11935`. + However, because of the guarantees stated in :meth:`Category._test_category_graph`, + the following rules must be followed. + + - If two categories have different lists of supercategories, they must return + different keys:: + + sage: Zmod(5) in Fields() + True + sage: Algebras(Zmod(5)).all_super_categories() + [..., Category of vector spaces over Ring of integers modulo 5, ...] + sage: Zmod(6) in Fields() + False + sage: Algebras(Zmod(6)).all_super_categories() # of course don't have category of vector spaces + [..., Category of modules over Ring of integers modulo 6, ...] + sage: # therefore: + sage: Algebras(Zmod(5))._make_named_class_key("parent_class") != Algebras(Zmod(6))._make_named_class_key("parent_class") + True + sage: Algebras(Zmod(5)).parent_class != Algebras(Zmod(6)).parent_class + True + + - If category ``A`` is a supercategory of category ``B``, + and category ``B`` uses the optimization, then so must ``A``. + + For example, ``Modules(ZZ)`` is a supercategory of ``Algebras(ZZ)``, + and ``Algebras(ZZ)`` implements the optimization:: + + sage: from sage.categories.category import CategoryWithParameters + sage: isinstance(Algebras(ZZ), CategoryWithParameters) + True + sage: Algebras(ZZ).parent_class is Algebras(ZZ.category()).parent_class + True + sage: Modules(ZZ) in Algebras(ZZ).all_super_categories() + True + + This forces ``Modules(ZZ)`` to also implement the optimization:: + + sage: Modules(ZZ).parent_class is Modules(ZZ.category()).parent_class + True + + As a complication, computing the exact category might require some potentially + expensive test. See :meth:`Category._test_category_graph` for more details. + INPUT: - ``name`` -- string; the name of the class as an attribute @@ -2826,6 +2923,9 @@ def _make_named_class_key(self, name): .. SEEALSO:: - :meth:`_make_named_class` + + The following can be read for typical implementations of this method. + - :meth:`sage.categories.category_types.Category_over_base._make_named_class_key` - :meth:`sage.categories.bimodules.Bimodules._make_named_class_key` - :meth:`JoinCategory._make_named_class_key` @@ -3064,6 +3164,9 @@ def _with_axiom(self, axiom): """ Return the category obtained by adding an axiom to ``self``. + As mentioned in :meth:`Category._with_axiom`, this method should not be used directly + except in internal code. + .. NOTE:: This is just an optimization of @@ -3073,7 +3176,9 @@ def _with_axiom(self, axiom): EXAMPLES:: sage: C = Category.join([Monoids(), Posets()]) - sage: C._with_axioms(["Finite"]) + sage: C._with_axioms(["Finite"]) # not idiomatic + Join of Category of finite monoids and Category of finite posets + sage: C.Finite() # recommended Join of Category of finite monoids and Category of finite posets TESTS: diff --git a/src/sage/categories/examples/graded_connected_hopf_algebras_with_basis.py b/src/sage/categories/examples/graded_connected_hopf_algebras_with_basis.py index ae91a374489..d5235909b5f 100644 --- a/src/sage/categories/examples/graded_connected_hopf_algebras_with_basis.py +++ b/src/sage/categories/examples/graded_connected_hopf_algebras_with_basis.py @@ -12,7 +12,7 @@ from sage.categories.graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis from sage.combinat.free_module import CombinatorialFreeModule -from sage.arith.misc import binomial +from sage.rings.integer import Integer from sage.misc.cachefunc import cached_method from sage.sets.non_negative_integers import NonNegativeIntegers @@ -144,8 +144,8 @@ def coproduct_on_basis(self, i): P0 # P3 + 3*P1 # P2 + 3*P2 # P1 + P3 # P0 """ return self.sum_of_terms( - ((i-j, j), binomial(i, j)) - for j in range(i+1) + ((i - j, j), Integer(i).binomial(j)) + for j in range(i + 1) ) diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 2868cd29d3e..9c7eca5c82c 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -240,6 +240,61 @@ def is_integrally_closed(self) -> bool: """ return True + def integral_closure(self): + """ + Return this field, since fields are integrally closed in their + fraction field. + + EXAMPLES:: + + sage: QQ.integral_closure() + Rational Field + sage: Frac(ZZ['x,y']).integral_closure() + Fraction Field of Multivariate Polynomial Ring in x, y + over Integer Ring + """ + return self + + def prime_subfield(self): + """ + Return the prime subfield of ``self``. + + EXAMPLES:: + + sage: k = GF(9, 'a') # needs sage.rings.finite_rings + sage: k.prime_subfield() # needs sage.rings.finite_rings + Finite Field of size 3 + """ + if self.characteristic() == 0: + import sage.rings.rational_field + return sage.rings.rational_field.RationalField() + + from sage.rings.finite_rings.finite_field_constructor import GF + return GF(self.characteristic()) + + def divides(self, x, y, coerce=True): + """ + Return ``True`` if ``x`` divides ``y`` in this field. + + This is usually ``True`` in a field!. + + If ``coerce`` is ``True`` (the default), first coerce + ``x`` and ``y`` into ``self``. + + EXAMPLES:: + + sage: QQ.divides(2, 3/4) + True + sage: QQ.divides(0, 5) + False + """ + if coerce: + x = self(x) + y = self(y) + if x.is_zero(): + return y.is_zero() + return True + def _gcd_univariate_polynomial(self, a, b): """ Return the gcd of ``a`` and ``b`` as a monic polynomial. diff --git a/src/sage/categories/filtered_modules.py b/src/sage/categories/filtered_modules.py index c4ca2db0028..17dcf1fbdca 100644 --- a/src/sage/categories/filtered_modules.py +++ b/src/sage/categories/filtered_modules.py @@ -67,6 +67,54 @@ def _repr_object_names(self): """ return "filtered {}".format(self.base_category()._repr_object_names()) + def _make_named_class_key(self, name): + r""" + Return what the element/parent/... classes depend on. + + .. SEEALSO:: + + - :meth:`.CategoryWithParameters._make_named_class_key` + + EXAMPLES:: + + sage: Modules(ZZ).Filtered()._make_named_class_key('element_class') + + + Note that we cannot simply return the base as in + :meth:`.Category_over_base._make_named_class_key` because of the following + (see :issue:`39154`):: + + sage: VectorSpacesQQ = VectorSpaces(QQ); VectorSpacesQQ + Category of vector spaces over Rational Field + sage: # ModulesQQ = Modules(QQ) # doesn't work because... + sage: Modules(QQ) is VectorSpacesQQ + True + sage: ModulesQQ = VectorSpacesQQ.super_categories()[0]; ModulesQQ + Category of modules over Rational Field + sage: VectorSpacesQQ.Filtered() + Category of filtered vector spaces over Rational Field + sage: ModulesQQ.Filtered() + Category of filtered modules over Rational Field + sage: VectorSpacesQQ.Filtered()._make_named_class_key('parent_class') + + sage: ModulesQQ.Filtered()._make_named_class_key('parent_class') + + sage: assert (VectorSpacesQQ.Filtered()._make_named_class_key('parent_class') != + ....: ModulesQQ.Filtered()._make_named_class_key('parent_class')) + sage: VectorSpacesQQ.Filtered().parent_class + + sage: ModulesQQ.Filtered().parent_class + + + Nevertheless, as explained in :meth:`.Category_over_base._make_named_class_key`, + ``Modules(QQ).Filtered()`` and ``Modules(QQ.category()).Filtered()`` must have + the same parent class:: + + sage: Modules(QQ).Filtered().parent_class == Modules(QQ.category()).Filtered().parent_class + True + """ + return getattr(self._base_category, name) + class FilteredModules(FilteredModulesCategory): r""" @@ -122,8 +170,9 @@ def extra_super_categories(self): """ from sage.categories.modules import Modules from sage.categories.fields import Fields + from sage.categories.category import Category base_ring = self.base_ring() - if base_ring in Fields(): + if base_ring in Fields() or (isinstance(base_ring, Category) and base_ring.is_subcategory(Fields())): return [Modules(base_ring)] else: return [] diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 4e5212885ce..686d748e8d8 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -17,14 +17,14 @@ # https://www.gnu.org/licenses/ # **************************************************************************** - +from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.lie_algebras import LieAlgebras +from sage.categories.subobjects import SubobjectsCategory from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import LazyImport -from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring -from sage.categories.lie_algebras import LieAlgebras -from sage.categories.subobjects import SubobjectsCategory +from sage.rings.integer import Integer from sage.sets.family import Family @@ -147,7 +147,7 @@ def names_map(x): F = FreeAlgebra(self.base_ring(), names) except ValueError: names = ['b{}'.format(i) for i in range(self.dimension())] - self._UEA_names_map = {g: names[i] for i,g in enumerate(I)} + self._UEA_names_map = {g: names[i] for i, g in enumerate(I)} names_map = self._UEA_names_map.__getitem__ F = FreeAlgebra(self.base_ring(), names) # ``F`` is the free algebra over the basis of ``self``. The @@ -207,7 +207,7 @@ def _basis_key_inverse(self): sage: [L._basis_key_inverse[k] for k in L._basis_ordering] [0, 1, 2, 3, 4, 5] """ - return {k: i for i,k in enumerate(self._basis_ordering)} + return {k: i for i, k in enumerate(self._basis_ordering)} def _basis_key(self, x): """ @@ -288,7 +288,7 @@ def from_vector(self, v, order=None): if order is None: order = self._basis_ordering B = self.basis() - return self.sum(v[i] * B[k] for i,k in enumerate(order) if v[i] != 0) + return self.sum(v[i] * B[k] for i, k in enumerate(order) if v[i] != 0) def killing_matrix(self, x, y): r""" @@ -424,9 +424,9 @@ def structure_coefficients(self, include_zeros=False): if not include_zeros and val == zero: continue if self._basis_key(x) > self._basis_key(y): - d[y,x] = -val + d[y, x] = -val else: - d[x,y] = val + d[x, y] = val return Family(d) def centralizer_basis(self, S): @@ -478,8 +478,8 @@ def centralizer_basis(self, S): """ from sage.matrix.constructor import matrix - #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra - #if isinstance(S, LieSubalgebra) or S is self: + # from sage.algebras.lie_algebras.subalgebra import LieSubalgebra + # if isinstance(S, LieSubalgebra) or S is self: if S is self: from sage.matrix.special import identity_matrix m = identity_matrix(self.base_ring(), self.dimension()) @@ -493,11 +493,11 @@ def centralizer_basis(self, S): for k in S.keys(): v = S[k].to_vector() sc[k] = v - sc[k[1],k[0]] = -v + sc[k[1], k[0]] = -v X = self.basis().keys() d = len(X) c_mat = matrix(self.base_ring(), - [[sum(m[i,j] * sc[x,xp][k] for j,xp in enumerate(X) + [[sum(m[i, j] * sc[x, xp][k] for j, xp in enumerate(X) if (x, xp) in sc) for x in X] for i in range(m.nrows()) for k in range(d)]) @@ -702,24 +702,24 @@ def derivations_basis(self): R = self.base_ring() B = self.basis() keys = list(B.keys()) - scoeffs = {(j,y,i): c for y in keys for i in keys - for j,c in self.bracket(B[y], B[i]) - } + scoeffs = {(j, y, i): c for y in keys for i in keys + for j, c in self.bracket(B[y], B[i]) + } zero = R.zero() data = {} N = len(keys) - for ii,i in enumerate(keys): - for ij,j in enumerate(keys[ii+1:]): + for ii, i in enumerate(keys): + for ij, j in enumerate(keys[ii+1:]): ijp = ij + ii + 1 - for il,l in enumerate(keys): + for il, l in enumerate(keys): row = ii + N * il + N**2 * ij - for ik,k in enumerate(keys): - data[row,ik+N*il] = (data.get((row,ik+N*il), zero) - + scoeffs.get((k, i, j), zero)) - data[row,ii+N*ik] = (data.get((row,ii+N*ik), zero) - - scoeffs.get((l, k, j), zero)) - data[row,ijp+N*ik] = (data.get((row,ijp+N*ik), zero) - - scoeffs.get((l, i, k), zero)) + for ik, k in enumerate(keys): + data[row, ik+N*il] = (data.get((row, ik+N*il), zero) + + scoeffs.get((k, i, j), zero)) + data[row, ii+N*ik] = (data.get((row, ii+N*ik), zero) + - scoeffs.get((l, k, j), zero)) + data[row, ijp+N*ik] = (data.get((row, ijp+N*ik), zero) + - scoeffs.get((l, i, k), zero)) mat = matrix(R, data, sparse=True) return tuple([matrix(R, N, N, list(b)) for b in mat.right_kernel().basis()]) @@ -1524,7 +1524,7 @@ def is_abelian(self): """ return len(self.structure_coefficients()) == 0 # TODO: boolean handling of empty family - #return not self.structure_coefficients() + # return not self.structure_coefficients() def is_solvable(self): r""" @@ -1699,9 +1699,7 @@ def chevalley_eilenberg_complex(self, M=None, dual=False, sparse=True, ncpus=Non sparse=sparse, ncpus=ncpus).dual() - import itertools from itertools import combinations, product - from sage.arith.misc import binomial from sage.matrix.matrix_space import MatrixSpace from sage.algebras.lie_algebras.representation import Representation_abstract R = self.base_ring() @@ -1776,8 +1774,8 @@ def compute_diff(k): Y.pop(i) # This is where we would do the action on # the coefficients module - #ret[indices[tuple(Y)]] += mone**i * zero - for j in range(i+1,k): + # ret[indices[tuple(Y)]] += mone**i * zero + for j in range(i + 1, k): # We shift j by 1 because we already removed # an earlier element from X. Z = tuple(Y[:j-1] + Y[j:]) @@ -1807,8 +1805,9 @@ def compute_diff(k): else: p2_data.append(ret) - nrows = binomial(len(LI), k) - ncols = binomial(len(LI), k-1) + lenLI = Integer(len(LI)) + nrows = lenLI.binomial(k) + ncols = lenLI.binomial(k - 1) MS = MatrixSpace(R, nrows, ncols, sparse=sparse) if M is None: p2 = MS(p2_data).transpose() @@ -1852,7 +1851,7 @@ def compute_diff(k): else: p1_data.append(ret) - nrows = len(MI) * binomial(len(LI), k) + nrows = len(MI) * Integer(len(LI)).binomial(k) ncols = len(ten_ind) MS = MatrixSpace(R, nrows, ncols, sparse=sparse) ret = MS(p1_data).transpose() + p2 @@ -1862,8 +1861,7 @@ def compute_diff(k): from sage.homology.chain_complex import ChainComplex ind = list(range(1, len(LI) + 1)) chain_data = {X[0][0]: M for X, M in compute_diff(ind)} - C = ChainComplex(chain_data, degree_of_differential=-1) - return C + return ChainComplex(chain_data, degree_of_differential=-1) def homology(self, deg=None, M=None, sparse=True, ncpus=None): r""" @@ -2019,11 +2017,11 @@ def as_finite_dimensional_algebra(self): M = [] for kp in K: if (k, kp) in S: - M.append( -S[k,kp].to_vector() ) + M.append(-S[k, kp].to_vector()) elif (kp, k) in S: - M.append( S[kp,k].to_vector() ) + M.append(S[kp, k].to_vector()) else: - M.append( zero_vec ) + M.append(zero_vec) mats.append(matrix(R, M)) from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra return FiniteDimensionalAlgebra(R, mats, names=self._names) @@ -2172,7 +2170,7 @@ def sc(i, j): vs = 'X{}_{}' else: vs = 'X{}{}' - R = PolynomialRing(self.base_ring(), ','.join(vs.format(i,j) + R = PolynomialRing(self.base_ring(), ','.join(vs.format(i, j) for i in range(n) for j in range(n))) X = [[R.gen(i+n*j) for i in range(n)] for j in range(n)] @@ -2184,10 +2182,10 @@ def sc(i, j): if i != j: s = sc(i, j) d[k] = (R.sum(s[I[u]] * X[a][u] for u in range(n)) - - R.sum(sc(s,t)[I[a]] * X[s][i] * X[t][j] + - R.sum(sc(s, t)[I[a]] * X[s][i] * X[t][j] for s in range(n) for t in range(n) if s != t)) else: - d[k] = -R.sum(sc(s,t)[I[a]] * X[s][i] * X[t][j] + d[k] = -R.sum(sc(s, t)[I[a]] * X[s][i] * X[t][j] for s in range(n) for t in range(n) if s != t) return Family(keys, d.__getitem__) @@ -2494,7 +2492,8 @@ def faithful_representation(self, algorithm=None): raise ValueError("invalid algorithm '{}'".format(algorithm)) class ElementMethods: - def adjoint_matrix(self, sparse=False): # In #11111 (more or less) by using matrix of a morphism + def adjoint_matrix(self, sparse=False): + # In #11111 (more or less) by using matrix of a morphism """ Return the matrix of the adjoint action of ``self``. @@ -2598,7 +2597,7 @@ def to_vector(self, sparse=False, order=None): from sage.modules.free_module import FreeModule M = FreeModule(self.parent().base_ring(), self.dimension(), sparse=True) if order is None: - order = {b: i for i,b in enumerate(self.parent()._basis_ordering)} + order = {b: i for i, b in enumerate(self.parent()._basis_ordering)} return M({order[k]: c for k, c in mc.items()}) else: M = self.parent().module() diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 5fd49d46ec9..a79d2d11017 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -396,7 +396,7 @@ def Hom(X, Y, category=None, check=True): """ # This should use cache_function instead # However some special handling is currently needed for - # domains/docomains that break the unique parent condition. Also, + # domains/codomains that break the unique parent condition. Also, # at some point, it somehow broke the coercion (see e.g. sage -t # sage.rings.real_mpfr). To be investigated. global _cache diff --git a/src/sage/categories/homsets.py b/src/sage/categories/homsets.py index d930e5a3fd2..73dc6be120c 100644 --- a/src/sage/categories/homsets.py +++ b/src/sage/categories/homsets.py @@ -10,13 +10,13 @@ # ***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.categories.category import Category, JoinCategory +from sage.categories.category import Category, JoinCategory, CategoryWithParameters from sage.categories.category_singleton import Category_singleton from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.covariant_functorial_construction import FunctorialConstructionCategory -class HomsetsCategory(FunctorialConstructionCategory): +class HomsetsCategory(FunctorialConstructionCategory, CategoryWithParameters): _functor_category = "Homsets" @@ -155,6 +155,17 @@ def base(self): return C.base() raise AttributeError("This hom category has no base") + def _make_named_class_key(self, name): + r""" + Return what the element/parent/... classes depend on. + + .. SEEALSO:: + + - :meth:`CategoryWithParameters` + - :meth:`CategoryWithParameters._make_named_class_key` + """ + return getattr(self.base_category(), name) + class HomsetsOf(HomsetsCategory): """ diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index a13925b8f6e..5948090c3a7 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -773,16 +773,17 @@ def classically_highest_weight_vectors(self): except StopIteration: it.pop() if path: - path.pop(0) + path.pop() continue - b = self.element_class(self, [x] + path) + path.append(x) + b = self.element_class(self, reversed(path)) if not b.is_highest_weight(index_set=I0): + path.pop() continue - path.insert(0, x) if len(path) == n: ret.append(b) - path.pop(0) + path.pop() else: it.append(iter(self.crystals[-len(path) - 1])) return tuple(ret) diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 83dbefdfaf3..1b8b1d17ae1 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -617,7 +617,7 @@ cdef codeword *expand_to_ortho_basis(BinaryCode B, int n) noexcept: i = k word = 1 << k k += 1 - else: # NOTE THIS WILL NEVER HAPPEN AS CURRENTLY SET UP! + else: # NOTE THIS WILL NEVER HAPPEN AS CURRENTLY SET UP! temp = (1 << k) - 1 i = k word = 1 << k @@ -813,7 +813,7 @@ cdef class BinaryCode: combination ^= (1 << j) word ^= self_basis[j] - else: # isinstance(arg1, BinaryCode) + else: # isinstance(arg1, BinaryCode) other_basis = other.basis for i from 0 <= i < nrows-1: self_basis[i] = other_basis[i] @@ -2423,9 +2423,12 @@ cdef class PartitionStack: cdef int *self_wd_lvls = self.wd_lvls cdef int *self_wd_ents = self.wd_ents while True: - if CG.is_one(self_wd_ents[wd_ptr], col): i += 1 - if self_wd_lvls[wd_ptr] > k: wd_ptr += 1 - else: break + if CG.is_one(self_wd_ents[wd_ptr], col): + i += 1 + if self_wd_lvls[wd_ptr] > k: + wd_ptr += 1 + else: + break return i def _wd_degree(self, C, wd, col_ptr, k): @@ -3347,15 +3350,17 @@ cdef class BinaryCodeClassifier: if qzb > 0: zb__Lambda_rho[k] = Lambda[k] state = 3 - elif state == 3: # attempt to rule out automorphisms while moving down the tree + elif state == 3: # attempt to rule out automorphisms while moving down the tree # if k > hzf, then we know that nu currently does not look like zeta, the first # terminal node encountered, thus there is no automorphism to discover. If qzb < 0, # i.e. Lambda[k] < zb[k], then the indicator is not maximal, and we can't reach a # canonical leaf. If neither of these is the case, then proceed to state 4. - if hzf__h_zeta <= k or qzb >= 0: state = 4 - else: state = 6 + if hzf__h_zeta <= k or qzb >= 0: + state = 4 + else: + state = 6 - elif state == 4: # at this point we have -not- ruled out the presence of automorphisms + elif state == 4: # at this point we have -not- ruled out the presence of automorphisms if nu.is_discrete(k): state = 7 continue # we have a terminal node, so process it @@ -3365,17 +3370,18 @@ cdef class BinaryCodeClassifier: # equal to its minimum element v[k] = nu.new_first_smallest_nontrivial(k, W, self.Phi_size * k) if not nu.sat_225(k): hh = k + 1 - e[k] = 0 # see state 12 and 17 - state = 2 # continue down the tree + e[k] = 0 # see state 12 and 17 + state = 2 # continue down the tree - elif state == 5: # same as state 3, but in the case where we haven't yet defined zeta - # i.e. this is our first time down the tree. Once we get to the bottom, - # we will have zeta = nu = rho, so we do: + elif state == 5: + # same as state 3, but in the case where we haven't yet defined zeta + # i.e. this is our first time down the tree. Once we get to the bottom, + # we will have zeta = nu = rho, so we do: zf__Lambda_zeta[k] = Lambda[k] zb__Lambda_rho[k] = Lambda[k] state = 4 - elif state == 6: # at this stage, there is no reason to continue downward, so backtrack + elif state == 6: # at this stage, there is no reason to continue downward, so backtrack j = k # return to the longest ancestor nu[i] of nu that could have a @@ -3396,9 +3402,10 @@ cdef class BinaryCodeClassifier: else: k = hh-1 # TODO: is the following line necessary? - if k == -1: k = 0 + if k == -1: + k = 0 - if hb > k:# update hb since we are backtracking + if hb > k: # update hb since we are backtracking hb = k # if j == hh, then all nodes lower than our current position are equivalent, so bail out if j == hh: @@ -3465,7 +3472,7 @@ cdef class BinaryCodeClassifier: state = 10 - elif state == 9: # nu is a better guess at the canonical label than rho + elif state == 9: # nu is a better guess at the canonical label than rho rho = PartitionStack(nu) k_rho = k qzb = 0 @@ -3475,7 +3482,7 @@ cdef class BinaryCodeClassifier: zb__Lambda_rho[k+1] = -1 state = 6 - elif state == 10: # we have an automorphism to process + elif state == 10: # we have an automorphism to process # increment l if l < self.L-1: l += 1 # store information about the automorphism to Omega and Phi @@ -3496,8 +3503,8 @@ cdef class BinaryCodeClassifier: Omega[ii] ^= (1< bool: """ return self == self.parent().one() or self.descents(side) == [i] - def index_set(self): + def index_set(self) -> tuple: r""" Index set of the affine permutation group. @@ -255,7 +254,7 @@ def index_set(self): sage: A.index_set() (0, 1, 2, 3, 4, 5, 6, 7) """ - return tuple(range(self.k+1)) + return tuple(range(self.k + 1)) def lower_covers(self, side='right'): r""" @@ -305,7 +304,7 @@ def reduced_word(self): sage: p.reduced_word() [0, 7, 4, 1, 0, 7, 5, 4, 2, 1] """ - #This is about 25% faster than the default algorithm. + # This is about 25% faster than the default algorithm. x = self i = 0 word = [] @@ -410,10 +409,10 @@ def grassmannian_quotient(self, i=0, side='right'): class AffinePermutationTypeA(AffinePermutation): - #---------------------- - #Type-specific methods. - #(Methods existing in all types, but with type-specific definition.) - #---------------------- + # ---------------------- + # Type-specific methods. + # (Methods existing in all types, but with type-specific definition.) + # ---------------------- def check(self): r""" Check that ``self`` is an affine permutation. @@ -440,13 +439,14 @@ def check(self): if not self: return k = self.parent().k - #Type A. + # Type A if len(self) != k + 1: - raise ValueError("length of list must be k+1="+str(k+1)) - if binomial(k+2,2) != sum(self): - raise ValueError("window does not sum to " + str(binomial((k+2),2))) - l = sorted([i % (k+1) for i in self]) - if l != list(range(k+1)): + raise ValueError(f"length of list must be k+1={k + 1}") + sigma = (k + 2).binomial(2) + if sigma != sum(self): + raise ValueError(f"window does not sum to {sigma}") + l = sorted(i % (k + 1) for i in self) + if any(i != j for i, j in enumerate(l)): raise ValueError("entries must have distinct residues") def value(self, i, base_window=False): @@ -510,11 +510,11 @@ def apply_simple_reflection_right(self, i): Type A affine permutation with window [3, -1, 6, 0, 5, 4, 10, 9] """ j = i % (self.k+1) - #Cloning is currently kinda broken, in that caches don't clear which - #leads to strangeness with the cloned object. - #The clone approach is quite a bit (2x) faster, though, so this should - #switch once the caching situation is fixed. - #with self.clone(check=False) as l: + # Cloning is currently kinda broken, in that caches don't clear which + # leads to strangeness with the cloned object. + # The clone approach is quite a bit (2x) faster, though, so this should + # switch once the caching situation is fixed. + # with self.clone(check=False) as l: l = self[:] if j == 0: a = l[0] @@ -524,7 +524,6 @@ def apply_simple_reflection_right(self, i): a = l[j-1] l[j-1] = l[j] l[j] = a - #return l return type(self)(self.parent(), l, check=False) def apply_simple_reflection_left(self, i): @@ -539,18 +538,18 @@ def apply_simple_reflection_left(self, i): sage: p.apply_simple_reflection_left(11) Type A affine permutation with window [4, -1, 0, 6, 5, 3, 10, 9] """ - #Here are a couple other methods we tried out, but turned out - #to be slower than the current implementation. - #1) This one was very bad: - # return self.inverse().apply_simple_reflection_right(i).inverse() - #2) Also bad, though not quite so bad: - # return (self.parent().simple_reflection(i))*self - i = i % (self.k+1) - #Cloning is currently kinda broken, in that caches don't clear which - #leads to strangeness with the cloned object. - #The clone approach is quite a bit faster, though, so this should switch - #once the caching situation is fixed. - #with self.clone(check=False) as l: + # Here are a couple other methods we tried out, but turned out + # to be slower than the current implementation. + # 1) This one was very bad: + # return self.inverse().apply_simple_reflection_right(i).inverse() + # 2) Also bad, though not quite so bad: + # return (self.parent().simple_reflection(i))*self + i = i % (self.k + 1) + # Cloning is currently kinda broken, in that caches don't clear which + # leads to strangeness with the cloned object. + # The clone approach is quite a bit faster, though, + # so this should switch once the caching situation is fixed. + # with self.clone(check=False) as l: l = [] if i != self.k: for m in range(self.k + 1): @@ -627,10 +626,10 @@ def to_type_a(self): """ return self - #---------------------- - #Type-A-specific methods. - #Only available in Type A. - #---------------------- + # ---------------------- + # Type-A-specific methods. + # Only available in Type A. + # ---------------------- def flip_automorphism(self): r""" @@ -699,7 +698,7 @@ def maximal_cyclic_factor(self, typ='decreasing', side='right', verbose=False): else: descents = self.descents(side='left') side = 'left' - #for now, assume side is 'right') + # for now, assume side is 'right') best_T = [] for i in descents: y = self.clone().apply_simple_reflection(i,side) @@ -834,8 +833,8 @@ def to_lehmer_code(self, typ='decreasing', side='right'): """ code = [0 for i in range(self.k+1)] if typ[0] == 'i' and side[0] == 'r': - #Find number of positions to the right of position i with smaller - #value than the number in position i. + # Find number of positions to the right of position i with smaller + # value than the number in position i. for i in range(self.k+1): a = self(i) for j in range(i+1, i+self.k+1): @@ -843,9 +842,9 @@ def to_lehmer_code(self, typ='decreasing', side='right'): if b < a: code[i] += (a-b) // (self.k+1) + 1 elif typ[0] == 'd' and side[0] == 'r': - #Find number of positions to the left of position i with larger - #value than the number in position i. Then cyclically shift - #the resulting vector. + # Find number of positions to the left of position i with larger + # value than the number in position i. Then cyclically shift + # the resulting vector. for i in range(self.k+1): a = self(i) for j in range(i-self.k, i): @@ -855,18 +854,18 @@ def to_lehmer_code(self, typ='decreasing', side='right'): if a < b: code[i-1] += ((b-a)//(self.k+1)+1) elif typ[0] == 'i' and side[0] == 'l': - #Find number of positions to the right of i smaller than i, then - #cyclically shift the resulting vector. + # Find number of positions to the right of i smaller than i, then + # cyclically shift the resulting vector. for i in range(self.k+1): pos = self.position(i) for j in range(pos+1, pos+self.k+1): b = self(j) - #A small rotation is necessary for the reduced word from - #the lehmer code to match the element. + # A small rotation is necessary for the reduced word from + # the lehmer code to match the element. if b < i: code[i-1] += (i-b) // (self.k+1) + 1 elif typ[0] == 'd' and side[0] == 'l': - #Find number of positions to the left of i larger than i. + # Find number of positions to the left of i larger than i. for i in range(self.k+1): pos = self.position(i) for j in range(pos-self.k, pos): @@ -1103,7 +1102,7 @@ def value(self, i): sage: C = AffinePermutationGroup(['C',4,1]) sage: x = C.one() - sage: [x.value(i) for i in range(-10,10)] == list(range(-10,10)) + sage: all(x.value(i) == i for i in range(-10,10)) True """ N = 2*self.k + 1 @@ -1124,7 +1123,7 @@ def position(self, i): sage: C = AffinePermutationGroup(['C',4,1]) sage: x = C.one() - sage: [x.position(i) for i in range(-10,10)] == list(range(-10,10)) + sage: all(x.position(i) == i for i in range(-10,10)) True """ N = 2*self.k + 1 @@ -2001,9 +2000,9 @@ class AffinePermutationGroupGeneric(UniqueRepresentation, Parent): methods for the specific affine permutation groups. """ - #---------------------- - #Type-free methods. - #---------------------- + # ---------------------- + # Type-free methods. + # ---------------------- def __init__(self, cartan_type): r""" @@ -2014,13 +2013,13 @@ def __init__(self, cartan_type): """ Parent.__init__(self, category=AffineWeylGroups()) ct = CartanType(cartan_type) - self.k = ct.n + self.k = Integer(ct.n) self.n = ct.rank() - #This N doesn't matter for type A, but comes up in all other types. + # This N doesn't matter for type A, but comes up in all other types. if ct.letter == 'A': self.N = self.k + 1 elif ct.letter == 'B' or ct.letter == 'C' or ct.letter == 'D': - self.N = 2*self.k + 1 + self.N = 2 * self.k + 1 elif ct.letter == 'G': self.N = 6 self._cartan_type = ct @@ -2034,14 +2033,14 @@ def _element_constructor_(self, *args, **keywords): """ return self.element_class(self, *args, **keywords) - def _repr_(self): + def _repr_(self) -> str: r""" TESTS:: sage: AffinePermutationGroup(['A',7,1]) The group of affine permutations of type ['A', 7, 1] """ - return "The group of affine permutations of type "+str(self.cartan_type()) + return "The group of affine permutations of type " + str(self.cartan_type()) def _test_enumeration(self, n=4, **options): r""" @@ -2242,12 +2241,12 @@ def one(self): True sage: TestSuite(A).run() """ - return self(list(range(1, self.k + 2))) + return self(range(1, self.k + 2)) - #------------------------ - #Type-unique methods. - #(Methods which do not exist in all types.) - #------------------------ + # ------------------------ + # Type-unique methods. + # (Methods which do not exist in all types.) + # ------------------------ def from_lehmer_code(self, C, typ='decreasing', side='right'): r""" Return the affine permutation with the supplied Lehmer code (a weak @@ -2353,7 +2352,7 @@ def one(self): True sage: TestSuite(C).run() """ - return self(list(range(1, self.k + 1))) + return self(range(1, self.k + 1)) Element = AffinePermutationTypeC diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 08b67b96540..6cddccb1940 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -1,13 +1,12 @@ """ Baxter permutations """ - -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent -from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.combinat.permutation import Permutations - +from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation class BaxterPermutations(UniqueRepresentation, Parent): @@ -79,11 +78,11 @@ def __init__(self, n): Baxter permutations of size 5 """ self.element_class = Permutations(n).element_class - self._n = ZZ(n) + self._n = Integer(n) from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets super().__init__(category=FiniteEnumeratedSets()) - def _repr_(self): + def _repr_(self) -> str: """ Return a string representation of ``self``. @@ -93,9 +92,9 @@ def _repr_(self): sage: BaxterPermutations_size(5) Baxter permutations of size 5 """ - return "Baxter permutations of size %s" % self._n + return f"Baxter permutations of size {self._n}" - def __contains__(self, x): + def __contains__(self, x) -> bool: r""" Return ``True`` if and only if ``x`` is a Baxter permutation of size ``self._n``. @@ -228,12 +227,11 @@ def cardinality(self): Integer Ring """ if self._n == 0: - return 1 - from sage.arith.misc import binomial - return sum((binomial(self._n + 1, k) * - binomial(self._n + 1, k + 1) * - binomial(self._n + 1, k + 2)) // - ((self._n + 1) * binomial(self._n + 1, 2)) + return ZZ.one() + n = self._n + 1 + return sum((n.binomial(k) * + n.binomial(k + 1) * + n.binomial(k + 2)) // (n * n.binomial(2)) for k in range(self._n)) diff --git a/src/sage/combinat/blob_algebra.py b/src/sage/combinat/blob_algebra.py index 595063a69fd..2ea8a223e40 100644 --- a/src/sage/combinat/blob_algebra.py +++ b/src/sage/combinat/blob_algebra.py @@ -17,22 +17,19 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element import Element, get_coercion_model -from sage.structure.richcmp import richcmp -#from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.misc.cachefunc import cached_method -from sage.combinat.subset import powerset -from sage.arith.misc import binomial -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.categories.algebras import Algebras +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.diagram_algebras import (TemperleyLiebDiagrams, diagram_latex, TL_diagram_ascii_art) -from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.dyck_word import DyckWords - -#@add_metaclass(InheritComparisonClasscallMetaclass) +from sage.combinat.free_module import CombinatorialFreeModule +from sage.combinat.subset import powerset +from sage.misc.cachefunc import cached_method +from sage.rings.integer import Integer +from sage.structure.element import Element, get_coercion_model +from sage.structure.parent import Parent +from sage.structure.richcmp import richcmp +from sage.structure.unique_representation import UniqueRepresentation class BlobDiagram(Element): @@ -167,7 +164,7 @@ def __init__(self, n): sage: BD4 = BlobDiagrams(4) sage: TestSuite(BD4).run() """ - self._n = n + self._n = Integer(n) self._TL_diagrams = TemperleyLiebDiagrams(n) Parent.__init__(self, category=FiniteEnumeratedSets()) @@ -181,7 +178,7 @@ def _repr_(self): sage: BlobDiagrams(4) Blob diagrams of order 4 """ - return "Blob diagrams of order {}".format(self._n) + return f"Blob diagrams of order {self._n}" def cardinality(self): r""" @@ -194,7 +191,7 @@ def cardinality(self): sage: BD4.cardinality() 70 """ - return binomial(2*self._n, self._n) + return (2 * self._n).binomial(self._n) def order(self): r""" @@ -221,7 +218,7 @@ def base_set(self): sage: sorted(BD4.base_set()) [-4, -3, -2, -1, 1, 2, 3, 4] """ - return frozenset(range(1,self._n+1)).union(range(-self._n,0)) + return frozenset(range(1, self._n + 1)).union(range(-self._n, 0)) def _element_constructor_(self, marked, unmarked=None): r""" diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 5ab85f84208..7860f4e0d87 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -332,7 +332,7 @@ def __init__(self, data, frozen=None, is_principal=False, user_labels=None, user else: labelset = set(user_labels) # Sanitizes our ``user_labels`` to use Integers instead of ints - user_labels = [ZZ(x) if x in ZZ else x for x in user_labels] + user_labels = [Integer(x) if x in ZZ else x for x in user_labels] if labelset != set(self._nlist + self._mlist) and labelset != set(range(self._n + self._m)): raise ValueError('user_labels conflict with both the given' ' vertex labels and the default labels') @@ -3593,7 +3593,7 @@ def mutation_class_iter(self, depth=infinity, show_depth=False, else: orbits = [index for index in range(n) if index > i or sd2._M[index, i] != 0] - clusters[cl2] = [sd2, orbits, clusters[key][2]+[i]] + clusters[cl2] = [sd2, orbits, clusters[key][2] + [i]] if return_paths: yield (sd2, clusters[cl2][2]) else: @@ -3602,10 +3602,10 @@ def mutation_class_iter(self, depth=infinity, show_depth=False, if show_depth and gets_bigger: timer2 = time.time() dc = str(depth_counter) - dc += ' ' * (5-len(dc)) + dc += ' ' * (5 - len(dc)) nr = str(len(clusters)) - nr += ' ' * (10-len(nr)) - print(f"Depth: {dc} found: {nr} Time: {timer2-timer:.2f} s") + nr += ' ' * (10 - len(nr)) + print(f"Depth: {dc} found: {nr} Time: {timer2 - timer:.2f} s") def mutation_class(self, depth=infinity, show_depth=False, return_paths=False, up_to_equivalence=True, only_sink_source=False): @@ -4718,7 +4718,7 @@ def _produce_upper_cluster_algebra_element(self, vd, cList): for l in range(num_cols): denominator = denominator * (R.gen(l))**vd[i][0][l] # Each copy of a vector in vd contributes a factor of the Laurent polynomial calculated from it. - final = (numerator/denominator)**vd[i][1] + final = (numerator / denominator)**vd[i][1] finalP.append(final) laurentP = 1 # The UCA element for the vector a is the product of the elements produced from the vectors in its decomposition. @@ -4740,10 +4740,8 @@ def _bino(n, k): 0 """ if n >= 0: - from sage.arith.misc import binomial - return binomial(n, k) - else: - return 0 + return Integer(n).binomial(k) + return 0 def coeff_recurs(p, q, a1, a2, b, c): @@ -5192,7 +5190,7 @@ def almost_positive_root(self): mt = self._mutation_type._repr_() # mt is a string of the shape "['A', 15]" # where A is a single letter and 15 is an integer - Phi = RootSystem([mt[2: 3], ZZ(mt[6: -1])]) + Phi = RootSystem([mt[2: 3], Integer(mt[6: -1])]) Phiplus = Phi.root_lattice().simple_roots() if self.denominator() == 1: diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 03c1797abfb..e0217176636 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -184,7 +184,7 @@ from sage.structure.element import Element lazy_import('sage.interfaces.maxima_lib', 'maxima') -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.misc.prandom', 'randint') diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 18b1882014d..de5f2c35ddb 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -519,7 +519,7 @@ def module_generator(self): r = self.r() s = self.s() weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() - return [b for b in self.module_generators if b.weight() == weight][0] + return next(b for b in self.module_generators if b.weight() == weight) def r(self): """ @@ -993,8 +993,8 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): sage: K.from_pm_diagram_to_highest_weight_vector(pm) [[2], [-2]] """ - u = [b for b in self.classical_decomposition().module_generators - if b.to_tableau().shape() == pm.outer_shape()][0] + u = next(b for b in self.classical_decomposition().module_generators + if b.to_tableau().shape() == pm.outer_shape()) ct = self.cartan_type() rank = ct.rank() - 1 ct_type = ct.classical().type() @@ -1031,7 +1031,7 @@ class KR_type_E6(KirillovReshetikhinCrystalFromPromotion): [(1,)] sage: b.e(0) [(-2, 1)] - sage: b = [t for t in K if t.epsilon(1) == 1 and t.phi(3) == 1 and t.phi(2) == 0 and t.epsilon(2) == 0][0] + sage: b = next(t for t in K if t.epsilon(1) == 1 and t.phi(3) == 1 and t.phi(2) == 0 and t.epsilon(2) == 0) sage: b [(-1, 3)] sage: b.e(0) @@ -1622,7 +1622,7 @@ def module_generator(self): weight = s*Lambda[r] - s*Lambda[0] if r == self.cartan_type().rank() - 1: weight += s*Lambda[r] # Special case for r == n - return [b for b in self.module_generators if b.weight() == weight][0] + return next(b for b in self.module_generators if b.weight() == weight) def classical_decomposition(self): r""" @@ -2489,7 +2489,8 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): sage: K.from_pm_diagram_to_highest_weight_vector(pm) [[2, 2], [3, 3], [-3, -1]] """ - u = [b for b in self.classical_decomposition().module_generators if b.to_tableau().shape() == pm.outer_shape()][0] + u = next(b for b in self.classical_decomposition().module_generators + if b.to_tableau().shape() == pm.outer_shape()) ct = self.cartan_type() rank = ct.rank()-1 ct_type = ct.classical().type() @@ -2537,7 +2538,7 @@ def e0(self): [[3, -3], [-3, -2], [-1, -1]] """ n = self.parent().cartan_type().n - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] if l1 == 0: @@ -2562,7 +2563,7 @@ def f0(self): sage: b.f(0) # indirect doctest """ n = self.parent().cartan_type().n - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] if l2 == 0: @@ -2585,7 +2586,7 @@ def epsilon0(self): 1 """ n = self.parent().cartan_type().n - b = self.lift().to_highest_weight(index_set=list(range(2, n + 1)))[0] + b = self.lift().to_highest_weight(index_set=range(2, n + 1))[0] pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] return l1 @@ -2602,7 +2603,7 @@ def phi0(self): 0 """ n = self.parent().cartan_type().n - b = self.lift().to_highest_weight(index_set=list(range(2, n + 1)))[0] + b = self.lift().to_highest_weight(index_set=range(2, n + 1))[0] pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] return l2 @@ -2825,7 +2826,7 @@ def e0(self): """ n = self.parent().cartan_type().rank()-1 s = self.parent().s() - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] l3 = pm.pm_diagram[n-2][0] @@ -2860,7 +2861,7 @@ def f0(self): """ n = self.parent().cartan_type().rank()-1 s = self.parent().s() - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1, l2 = pm.pm_diagram[n-1] l3 = pm.pm_diagram[n-2][0] @@ -2907,7 +2908,7 @@ def epsilon0(self): True """ n = self.parent().cartan_type().rank() - 1 - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1 = pm.pm_diagram[n-1][0] l4 = pm.pm_diagram[n][0] @@ -2940,7 +2941,7 @@ def phi0(self): True """ n = self.parent().cartan_type().rank() - 1 - b, l = self.lift().to_highest_weight(index_set=list(range(2, n + 1))) + b, l = self.lift().to_highest_weight(index_set=range(2, n + 1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l2 = pm.pm_diagram[n-1][1] l4 = pm.pm_diagram[n][0] @@ -3075,9 +3076,9 @@ def classical_decomposition(self): C = self.cartan_type().classical() s = QQ(self.s()) if self.r() == C.n: - c = [s/QQ(2)]*C.n + c = [s / QQ(2)]*C.n else: - c = [s/QQ(2)]*(C.n-1)+[-s/QQ(2)] + c = [s / QQ(2)]*(C.n-1) + [-s / QQ(2)] return CrystalOfTableaux(C, shape=c) def dynkin_diagram_automorphism(self, i): @@ -3155,6 +3156,7 @@ def neg(x): y = list(x) # map a (shallow) copy y[0] = -y[0] return tuple(y) + return {dic_weight[w]: dic_weight_dual[neg(w)] for w in dic_weight} @cached_method @@ -3779,7 +3781,7 @@ def __init__(self, pm_diagram, from_shapes=None): self._list = [i for a in reversed(pm_diagram) for i in a] self.width = sum(self._list) - def _repr_(self): + def _repr_(self) -> str: r""" Turning on pretty printing allows to display the `\pm` diagram as a tableau with the `+` and `-` displayed. @@ -3791,7 +3793,7 @@ def _repr_(self): """ return repr(self.pm_diagram) - def _repr_diagram(self): + def _repr_diagram(self) -> str: """ Return a string representation of ``self`` as a diagram. @@ -3910,7 +3912,7 @@ def intermediate_shape(self): p = [p[i] + ll[2*i+1] for i in range(self.n)] return Partition(p) - def heights_of_minus(self): + def heights_of_minus(self) -> list: r""" Return a list with the heights of all minus in the `\pm` diagram. @@ -3930,7 +3932,7 @@ def heights_of_minus(self): heights += [n-2*i]*((self.outer_shape()+[0]*n)[n-2*i-1]-(self.intermediate_shape()+[0]*n)[n-2*i-1]) return heights - def heights_of_addable_plus(self): + def heights_of_addable_plus(self) -> list: r""" Return a list with the heights of all addable plus in the `\pm` diagram. @@ -4176,7 +4178,7 @@ def _call_(self, x): self._cache[x] = y return y - def _repr_type(self): + def _repr_type(self) -> str: """ Return a string describing ``self``. @@ -4188,7 +4190,7 @@ def _repr_type(self): """ return "Diagram automorphism" - def is_isomorphism(self): + def is_isomorphism(self) -> bool: """ Return ``True`` as ``self`` is a crystal isomorphism. diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index f5912e83690..43eeecf4c0c 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -267,7 +267,7 @@ def generalised_quadrangle_hermitian_with_ovoid(const int q): TESTS:: sage: from sage.combinat.designs.gen_quadrangles_with_spread import \ - is_GQ_with_spread, dual_GQ_ovoid + ....: is_GQ_with_spread, dual_GQ_ovoid sage: t = designs.generalised_quadrangle_hermitian_with_ovoid(3) sage: t = dual_GQ_ovoid(*t) sage: is_GQ_with_spread(*t, s=3, t=9) diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index 08ea78b8ea3..b14f4e1f92a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -837,14 +837,14 @@ def thwart_lemma_4_1(k, n, m, explain_construction=False): # - (1+t,t,1+t), (1,1,1), (1+t,t,t), (1,1,2), (0,0,1), (1,0,1), (0,1,1+t), # (0,1,1), (1,0,-t) points = [(1+t,t,1+t), (1,1,1), (1+t,t,t), (1,1,2), (0,0,1), (1,0,1), (0,1,1+t), (0,1,1), (1,0,-t)] - points = [[K(_) for _ in t] for t in points] # triples of K^3 + points = [[K(c) for c in t] for t in points] # triples of K^3 AG_2_3 = [] for x,y,z in points: if z != 0: - x,y,z = x/z,y/z,z/z + x, y, z = x / z, y / z, K.one() AG_2_3.append(relabel[x]+n*relabel[y]) elif y != 0: - x,y,z = x/y,y/y,z + x, y = x / y, K.one() AG_2_3.append(q**2+relabel[x]) else: AG_2_3.append(q**2+q) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 841c9efe9d3..758a1f1432d 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -4802,8 +4802,7 @@ def insert_pairing(cur, intervals): else: level.append(cur) return # We have stopped - else: - intervals.append([cur]) + intervals.append([cur]) # Build a set of intervals that defines where to draw the diagram intervals = [[]] propogating = [] diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index df841984f31..22aa66d39f9 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -90,7 +90,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.posets import Posets -from sage.rings.integer_ring import ZZ +from sage.rings.integer import Integer from sage.rings.rational_field import QQ from sage.combinat.permutation import Permutation, Permutations from sage.combinat.words.word import Word @@ -544,13 +544,13 @@ def _repr_lattice(self, type=None, labelling=None, underpath=True) -> str: row = " " * (n - alst[-1] - 1) + final_fall + "\n" for i in range(n - 1): c = 0 - row = row + " "*(n-i-2-alst[-i-2]) + row = row + " " * (n-i-2-alst[-i-2]) c += n-i-2-alst[-i-2] if alst[-i-2]+1 != alst[-i-1]: row += " _" c += alst[-i-2] - alst[-i-1] if underpath: - row += "__"*(alst[-i-2]-alst[-i-1])+"|" + labels[-1] + "x "*(n-c-2-i) + " ."*i + "\n" + row += "__" * (alst[-i-2]-alst[-i-1]) + "|" + labels[-1] + "x "*(n-c-2-i) + " ." * i + "\n" else: row += "__"*(alst[-i-2]-alst[-i-1])+"| " + "x "*(n-c-2-i) + " ."*i + labels[-1] + "\n" labels.pop() @@ -3286,14 +3286,14 @@ def __classcall_private__(cls, k1=None, k2=None, complete=True): return CompleteDyckWords_all() return DyckWords_all() - k1 = ZZ(k1) + k1 = Integer(k1) if k1 < 0: raise ValueError("k1 (= %s) must be nonnegative" % k1) return CompleteDyckWords_size(k1) else: - k1 = ZZ(k1) + k1 = Integer(k1) - k2 = ZZ(k2) + k2 = Integer(k2) if k1 < 0 or (k2 is not None and k2 < 0): raise ValueError("k1 (= %s) and k2 (= %s) must be nonnegative, with k1 >= k2" % (k1, k2)) if k1 < k2: @@ -3638,8 +3638,8 @@ def __init__(self, k1, k2): # Dyck paths, not words; having k1 opening parens and k2 closing # parens corresponds to paths of length k1 + k2 ending at height # k1 - k2. - k1 = ZZ(k1) - k2 = ZZ(k2) + k1 = Integer(k1) + k2 = Integer(k2) self.n = k1 + k2 self.endht = k1 - k2 @@ -3697,8 +3697,8 @@ def __init__(self, k1, k2): Integer Ring sage: TestSuite(DyckWords(4,2)).run() """ - self.k1 = ZZ(k1) - self.k2 = ZZ(k2) + self.k1 = Integer(k1) + self.k2 = Integer(k2) DyckWords.__init__(self, category=FiniteEnumeratedSets()) def _repr_(self) -> str: @@ -3780,8 +3780,7 @@ def cardinality(self) -> int: ....: for p in range(7)) True """ - from sage.arith.misc import binomial - return (self.k1 - self.k2 + 1) * binomial(self.k1 + self.k2, self.k2) // (self.k1 + 1) + return (self.k1 - self.k2 + 1) * (self.k1 + self.k2).binomial(self.k2) // (self.k1 + 1) ################################################################ # Complete Dyck words diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 81e3a30508f..e6c89ec3c3c 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -68,7 +68,7 @@ def _make_color_list(n, colors=None, color_map=None, randomize=False): sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.combinat.fully_packed_loop import _make_color_list sage: _make_color_list(5) sage: _make_color_list(5, ['blue', 'red']) diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 6848609cf5d..4587cb5f46c 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -1043,8 +1043,19 @@ def __iter__(self): [2, 1], [1, 2], [0, 3]] + + TESTS: + + Check corner case:: + + sage: IV = IntegerVectors(k=0) + sage: list(IV) + [[]] """ n = 0 + if self.k == 0: # special case + yield self.element_class(self, [], check=False) + return while True: for iv in integer_vectors_nk_fast_iter(n, self.k): yield self.element_class(self, iv, check=False) diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index afdae758dfc..9fa34fd3986 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -47,6 +47,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.lazy_import import lazy_import from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.non_negative_integers import NonNegativeIntegers from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets @@ -3757,11 +3758,10 @@ def cardinality(self) -> Integer: sage: [TamariIntervalPosets(i).cardinality() for i in range(6)] [1, 1, 3, 13, 68, 399] """ - from sage.arith.misc import binomial n = self._size if n == 0: - return Integer(1) - return (2 * binomial(4 * n + 1, n - 1)) // (n * (n + 1)) + return ZZ.one() + return (2 * Integer(4 * n + 1).binomial(n - 1)) // (n * (n + 1)) # return Integer(2 * factorial(4*n+1)/(factorial(n+1)*factorial(3*n+2))) def __iter__(self) -> Iterator[TIP]: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index abc84654166..dd2ef6fe63e 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -317,7 +317,7 @@ lazy_import('sage.combinat.skew_partition', 'SkewPartition') lazy_import('sage.combinat.partition_tuple', 'PartitionTuple') lazy_import('sage.combinat.root_system.weyl_group', 'WeylGroup') -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.groups.perm_gps.permgroup', 'PermutationGroup') lazy_import("sage.symbolic.ring", "var") diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index fa12e689039..ebfd7479639 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -275,7 +275,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.structure.unique_representation import UniqueRepresentation lazy_import('sage.groups.perm_gps.permgroup', 'PermutationGroup') -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') # ------------------------------------------------- # Partition tuple - element class diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 149915f8fb5..542213f0f06 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1292,7 +1292,8 @@ def is_semidistributive(self): - Weaker properties: :meth:`is_join_semidistributive`, :meth:`is_meet_semidistributive` - - Stronger properties: :meth:`is_distributive` + - Stronger properties: :meth:`is_distributive`, + :meth:`is_congruence_uniform` TESTS:: @@ -3549,7 +3550,7 @@ def day_doubling(self, S): True """ # Rationale for naming of elements: a lattice can have - # elements 1, (1, 1), (1, (1, 1)) and so on. We can't just + # elements 1, (1, 1), (1, (1, 1)) and so on. We cannot just # make a copy of S with elements (s, 1). # The construction could be defined for any convex @@ -3954,7 +3955,7 @@ def is_sublattice_dismantlable(self): Add a certificate-option. """ - # Todo: This can be made much faster, if we don't regenerate meet- and + # Todo: This can be made much faster, if we do not regenerate meet- and # join-matrices every time, but instead remove some rows and columns # from them. @@ -4270,7 +4271,7 @@ def is_constructible_by_doublings(self, type) -> bool: The congruence lattice of this lattice has maximal chains satisfying the needed property, but also maximal chains not satisfying that; this shows that the code - can't be optimized to test just some maximal chain:: + cannot be optimized to test just some maximal chain:: sage: L = LatticePoset(DiGraph('QSO?I?_?_GBG??_??a???@?K??A??B???C??s??G??I??@??A??@???')) sage: L.is_constructible_by_doublings('convex') @@ -4343,6 +4344,29 @@ def splitting_depth_2(a, b): todo.append(e_up) return False + def is_congruence_uniform(self) -> bool: + """ + Return whether ``self`` is congruence uniform. + + This is equivalent to being constructible by doubling intervals. + + .. SEEALSO:: :meth:`is_constructible_by_doublings` + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: P.is_congruence_uniform() + True + sage: P = posets.DiamondPoset(5) + sage: P.is_congruence_uniform() + False + + REFERENCES: + + - [Day1979]_ + """ + return self.is_constructible_by_doublings(type="interval") + def is_isoform(self, certificate=False): """ Return ``True`` if the lattice is isoform and ``False`` otherwise. diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index a2039494ce4..173cbb872da 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -292,7 +292,6 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc_c import prod -from sage.arith.misc import binomial from sage.categories.category import Category from sage.categories.sets_cat import Sets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -1815,7 +1814,7 @@ def atkinson(self, a): for r in range(1, n + 1): new_a_spec.append(0) for i in range(max(1, r - n + k), min(r, k) + 1): - k_val = binomial(r - 1, i - 1) * binomial(n - r, k - i) + k_val = Integer(r - 1).binomial(i - 1) * Integer(n - r).binomial(k - i) new_a_spec[-1] += k_val * a_spec[i - 1] * n_lin_exts return new_a_spec @@ -5295,7 +5294,7 @@ def factor(self): dg = self._hasse_diagram if not dg.is_connected(): raise NotImplementedError('the poset is not connected') - if ZZ(dg.num_verts()).is_prime(): + if Integer(dg.num_verts()).is_prime(): return [self] G = dg.to_undirected() is_product, dic = G.is_cartesian_product(relabeling=True) @@ -5306,7 +5305,7 @@ def factor(self): prod_dg = dg.relabel(dic, inplace=False) v0 = next(iter(dic.values())) n = len(v0) - factors_range = list(range(n)) + factors_range = range(n) fusion = Graph(n) def edge_color(va, vb): @@ -6492,7 +6491,7 @@ def random_order_ideal(self, direction='down'): with seed(currseed): for _ in range(count): for element in range(n): - if random() % 2 == 1: + if random() % 2: # should use one random bit s = [state[i] for i in lower_covers[element]] if 1 not in s: if 2 not in s: @@ -7168,12 +7167,12 @@ def order_polytope(self): True """ from sage.geometry.polyhedron.constructor import Polyhedron - ineqs = [[0] + [ZZ(j == v) - ZZ(j == u) for j in self] + ineqs = [[0] + [Integer(j == v) - Integer(j == u) for j in self] for u, v in self.hasse_diagram().edges(sort=False, labels=False)] for i in self.maximal_elements(): - ineqs += [[1] + [-ZZ(j == i) for j in self]] + ineqs += [[1] + [-Integer(j == i) for j in self]] for i in self.minimal_elements(): - ineqs += [[0] + [ZZ(j == i) for j in self]] + ineqs += [[0] + [Integer(j == i) for j in self]] return Polyhedron(ieqs=ineqs, base_ring=ZZ) def chain_polytope(self): @@ -7207,10 +7206,10 @@ def chain_polytope(self): A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 8 vertices """ from sage.geometry.polyhedron.constructor import Polyhedron - ineqs = [[1] + [-ZZ(j in chain) for j in self] + ineqs = [[1] + [-Integer(j in chain) for j in self] for chain in self.maximal_chains_iterator()] for i in self: - ineqs += [[0] + [ZZ(j == i) for j in self]] + ineqs += [[0] + [Integer(j == i) for j in self]] return Polyhedron(ieqs=ineqs, base_ring=ZZ) def zeta_polynomial(self): @@ -8141,7 +8140,7 @@ def is_eulerian(self, k=None, certificate=False): n = self.cardinality() if n == 1: return True - if k is None and not certificate and n % 2 == 1: + if k is None and not certificate and n % 2: return False H = self._hasse_diagram diff --git a/src/sage/combinat/regular_sequence.py b/src/sage/combinat/regular_sequence.py index 6ad1c12c088..5ce785d6a42 100644 --- a/src/sage/combinat/regular_sequence.py +++ b/src/sage/combinat/regular_sequence.py @@ -1882,8 +1882,7 @@ def some_inverse_U_matrix(lines): return U.inverse(), m_indices except ZeroDivisionError: pass - else: - raise RuntimeError('no invertible submatrix found') + raise RuntimeError('no invertible submatrix found') def linear_combination_candidate(t_L, r_L, lines): r""" diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index fa97defb73e..c1f2ba8afd3 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -11,22 +11,21 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +import sage.combinat.ranker +from sage.combinat.root_system.root_system import RootSystem +from sage.combinat.root_system.weyl_group import WeylGroup from sage.misc.cachefunc import cached_method -from sage.misc.constant_function import ConstantFunction from sage.misc.call import attrcall +from sage.misc.constant_function import ConstantFunction from sage.misc.lazy_import import lazy_import from sage.misc.misc_c import prod -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.structure.parent import Parent -from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.infinity import infinity from sage.rings.integer import Integer from sage.rings.rational_field import QQ -from sage.rings.infinity import infinity -from sage.arith.misc import binomial -import sage.combinat.ranker from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet -from sage.combinat.root_system.root_system import RootSystem -from sage.combinat.root_system.weyl_group import WeylGroup +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation lazy_import('sage.graphs.digraph', 'DiGraph') lazy_import('sage.combinat.root_system.dynkin_diagram', 'DynkinDiagram') @@ -755,8 +754,7 @@ def cardinality(self): """ if self._min_length == len(self._min_support) and self._max_length == len(self._max_support) - 1: return Integer(2**(len(self._extra_support)) - 1) - else: - return self.generating_series(weight=ConstantFunction(1)) + return self.generating_series(weight=ConstantFunction(1)) def generating_series(self, weight=None): r""" @@ -774,7 +772,7 @@ def generating_series(self, weight=None): weight = self.default_weight() l_min = len(self._min_support) l_max = len(self._max_support) - return sum(binomial(l_max - l_min, l - l_min) * weight(l) + return sum(Integer(l_max - l_min).binomial(l - l_min) * weight(l) for l in range(self._min_length, self._max_length + 1)) def __iter__(self): diff --git a/src/sage/combinat/sf/elementary.py b/src/sage/combinat/sf/elementary.py index 675c02604fd..d24e3425116 100644 --- a/src/sage/combinat/sf/elementary.py +++ b/src/sage/combinat/sf/elementary.py @@ -98,6 +98,24 @@ def P(i): T = self.tensor_square() return T.sum_of_monomials( (P(j), P(i-j)) for j in range(i+1) ) + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: E = SymmetricFunctions(QQ).e() + sage: t = 4*E[3,2]+9 + sage: mt = magma(t); mt + 9 + 4*$.[3,2] + sage: mt.sage() + 9*e[] + 4*e[3, 2] + """ + B = magma(self.base_ring()) + Bref = B._ref() + return f"SymmetricFunctionAlgebraElementary({Bref})" + class Element(classical.SymmetricFunctionAlgebra_classical.Element): def omega(self): r""" diff --git a/src/sage/combinat/sf/homogeneous.py b/src/sage/combinat/sf/homogeneous.py index e6bc361f3b0..aa25b1a6ebb 100644 --- a/src/sage/combinat/sf/homogeneous.py +++ b/src/sage/combinat/sf/homogeneous.py @@ -125,6 +125,24 @@ def P(i): T = self.tensor_square() return T.sum_of_monomials( (P(j), P(i-j)) for j in range(i+1) ) + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: H = SymmetricFunctions(QQ).h() + sage: t = 4*H[3,2]+9 + sage: mt = magma(t); mt + 9 + 4*$.[3,2] + sage: mt.sage() + 9*h[] + 4*h[3, 2] + """ + B = magma(self.base_ring()) + Bref = B._ref() + return f"SymmetricFunctionAlgebraHomogeneous({Bref})" + class Element(classical.SymmetricFunctionAlgebra_classical.Element): def omega(self): r""" diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index bb7b54bae4e..7dc003e00ca 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -276,6 +276,24 @@ def antipode_by_coercion(self, element): s = self.realization_of().schur() return self(s.antipode(s(element))) + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: M = SymmetricFunctions(QQ).m() + sage: t = 4*M[3,2]+9 + sage: mt = magma(t); mt + 9 + 4*$.[3,2] + sage: mt.sage() + 9*m[] + 4*m[3, 2] + """ + B = magma(self.base_ring()) + Bref = B._ref() + return f"SymmetricFunctionAlgebraMonomial({Bref})" + class Element(classical.SymmetricFunctionAlgebra_classical.Element): def expand(self, n, alphabet='x'): """ diff --git a/src/sage/combinat/sf/powersum.py b/src/sage/combinat/sf/powersum.py index ce64edef000..9b4faa329cc 100644 --- a/src/sage/combinat/sf/powersum.py +++ b/src/sage/combinat/sf/powersum.py @@ -216,6 +216,24 @@ def eval_at_permutation_roots_on_generators(self, k, rho): """ return self.base_ring().sum(d*list(rho).count(d) for d in divisors(k)) + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: P = SymmetricFunctions(QQ).p() + sage: t = 4*P[3,2]+9 + sage: mt = magma(t); mt + 9 + 4*$.[3,2] + sage: mt.sage() + 9*p[] + 4*p[3, 2] + """ + B = magma(self.base_ring()) + Bref = B._ref() + return f"SymmetricFunctionAlgebraPower({Bref})" + class Element(classical.SymmetricFunctionAlgebra_classical.Element): def omega(self): r""" diff --git a/src/sage/combinat/sf/schur.py b/src/sage/combinat/sf/schur.py index 6d68365f270..9ff7072ea8f 100644 --- a/src/sage/combinat/sf/schur.py +++ b/src/sage/combinat/sf/schur.py @@ -224,6 +224,24 @@ def _repeated_bernstein_creation_operator_on_basis(self, la, nu): return (-1)**m * self([a+b for (a,b) in zip(ga, range(-r,0))]) return self.zero() + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: S = SymmetricFunctions(QQ).s() + sage: t = 4*S[3,2]+9 + sage: mt = magma(t); mt + 9 + 4*$.[3,2] + sage: mt.sage() + 9*s[] + 4*s[3, 2] + """ + B = magma(self.base_ring()) + Bref = B._ref() + return f"SymmetricFunctionAlgebraSchur({Bref})" + class Element(classical.SymmetricFunctionAlgebra_classical.Element): def __pow__(self, n): """ diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 44189013d5c..6997cd396f9 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1042,10 +1042,10 @@ def backward_slide(self, corner=None): # -1, which doesn't trigger the conditional if P_left > P_up: new_st[i][j] = P_left - i, j = (i, j - 1) + j = j - 1 else: # if they are equal, we slide up new_st[i][j] = P_up - i, j = (i - 1, j) + i = i - 1 # We don't need to reset the intermediate cells inside the loop # because the conditional above will continue to overwrite it until # the while loop terminates. We do need to reset it at the end. diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index b9651829109..12580cbb8ca 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -6796,7 +6796,7 @@ def _powerful_numbers_in_range(self, n, m): n = max(n, 4) # Use PARI directly -- much faster. - from sage.libs.pari.all import pari + from sage.libs.pari import pari L = pari('v=listcreate(); for(i=%s,%s,if(vecmin(factor(i)[,2])>1,listput(v,i))); v' % (n, m)) return [ZZ(x) for x in L] # not very many, so not much overhead diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index ba9bf3f2157..a47c8006767 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -2367,8 +2367,7 @@ def longest_common_suffix(self, other): for i, (b, c) in iter: if b != c: return self[-i:] - else: - return self[-i-1:] + return self[-i-1:] def is_palindrome(self, f=None): r""" @@ -3739,8 +3738,7 @@ def is_subword_of(self, other): for e in other: if s == e: s = next(its) - else: - return False + return False except StopIteration: return True @@ -3881,8 +3879,7 @@ def is_lyndon(self) -> bool: else: # we found the first word in the lyndon factorization; return False - else: - return i == 0 + return i == 0 def lyndon_factorization(self): r""" diff --git a/src/sage/combinat/words/morphic.py b/src/sage/combinat/words/morphic.py index 160f4e2243a..1d79d9dd1fc 100644 --- a/src/sage/combinat/words/morphic.py +++ b/src/sage/combinat/words/morphic.py @@ -344,8 +344,7 @@ def __iter__(self): try: for a in self._morphism.image(next(w)): yield self._coding[a] - else: - next_w = next(w) - w = chain([next_w], w, self._morphism.image(next_w)) + next_w = next(w) + w = chain([next_w], w, self._morphism.image(next_w)) except StopIteration: return diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 343fe8af9eb..3939e450b99 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -1550,8 +1550,7 @@ def _check_primitive(self): for image in self.images(): if not dom_alphabet <= set(image): return False - else: - return True + return True def is_primitive(self): r""" diff --git a/src/sage/combinat/words/shuffle_product.py b/src/sage/combinat/words/shuffle_product.py index 5aea554b6b6..9d5c43dd73b 100644 --- a/src/sage/combinat/words/shuffle_product.py +++ b/src/sage/combinat/words/shuffle_product.py @@ -21,11 +21,11 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.words.word import Word_class, Word -from sage.arith.misc import binomial from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.combinat.integer_vector import IntegerVectors from sage.combinat.composition import Composition +from sage.combinat.integer_vector import IntegerVectors +from sage.combinat.words.word import Word_class, Word +from sage.rings.integer import Integer from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -174,7 +174,7 @@ def cardinality(self): """ len_w1 = self._w1.length() len_w2 = self._w2.length() - return binomial(len_w1 + len_w2, len_w1) + return Integer(len_w1 + len_w2).binomial(len_w1) def __iter__(self): """ diff --git a/src/sage/combinat/words/word_generators.py b/src/sage/combinat/words/word_generators.py index 05ccb242508..a0150f8ed31 100644 --- a/src/sage/combinat/words/word_generators.py +++ b/src/sage/combinat/words/word_generators.py @@ -612,8 +612,7 @@ def _FibonacciWord_RecursiveConstructionIterator(self, alphabet=(0, 1)): for i in it: n += 1 yield alphabet[i] - else: - Fib1, Fib0 = Fib1 + Fib0, Fib1 + Fib1, Fib0 = Fib1 + Fib0, Fib1 def FixedPointOfMorphism(self, morphism, first_letter): r""" @@ -962,8 +961,7 @@ def _CharacteristicSturmianWord_LetterIterator(self, cf, alphabet=(0, 1)): for i in s1[n:]: n += 1 yield alphabet[i] - else: - s1, s0 = s1*next(cf) + s0, s1 + s1, s0 = s1*next(cf) + s0, s1 except StopIteration: return @@ -1306,8 +1304,7 @@ def _StandardEpisturmianWord_LetterIterator(self, directive_word): for x in w[n:]: n += 1 yield x - else: - w = W(w * W(next(d))).palindromic_closure() + w = W(w * W(next(d))).palindromic_closure() def MinimalSmoothPrefix(self, n): r""" diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index 96dd83b4f8e..a96a55e5b50 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -1372,10 +1372,8 @@ def _biseq_stresstest(): TESTS:: sage: from sage.data_structures.bounded_integer_sequences import _biseq_stresstest - sage: alarm(1); _biseq_stresstest() # long time - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(1): _biseq_stresstest() # long time """ cdef int branch cdef Py_ssize_t x, y, z diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py index 9b881ed70ea..498bfe491d2 100644 --- a/src/sage/databases/cremona.py +++ b/src/sage/databases/cremona.py @@ -177,7 +177,7 @@ def is_optimal_id(id): return id[-1] == '1' and not id[-2].isdigit() -def cremona_letter_code(n): +def cremona_letter_code(n) -> str: """ Return the Cremona letter code corresponding to an integer. @@ -252,7 +252,7 @@ def cremona_letter_code(n): return s -def old_cremona_letter_code(n): +def old_cremona_letter_code(n) -> str: r""" Return the *old* Cremona letter code corresponding to an integer. @@ -559,7 +559,7 @@ def cremona_to_lmfdb(cremona_label, CDB=None): sage: for label in ['438.c2','306.b','462.f3']: ....: assert(cremona_to_lmfdb(lmfdb_to_cremona(label)) == label) """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari m = cremona_label_regex.match(cremona_label) if m is None: raise ValueError("Invalid Cremona label") @@ -609,7 +609,7 @@ def lmfdb_to_cremona(lmfdb_label, CDB=None): sage: cremona_to_lmfdb('990j1') '990.h3' """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari m = lmfdb_label_regex.match(lmfdb_label) if m is None: raise ValueError("Invalid LMFDB label") @@ -1651,9 +1651,11 @@ def _init_allgens(self, ftpdata, largest_conductor=0): _db = None -def CremonaDatabase(name=None, mini=None, set_global=None): +def CremonaDatabase(name=None, mini=None): """ - Initialize the Cremona database with name ``name``. If ``name`` is + Initialize the Cremona database with name ``name``. + + If ``name`` is ``None`` it instead initializes large Cremona database (named 'cremona'), if available or default mini Cremona database (named 'cremona mini'). @@ -1690,10 +1692,6 @@ def CremonaDatabase(name=None, mini=None, set_global=None): ... ValueError: the full Cremona database is not available; consider using the mini Cremona database by setting mini=True """ - if set_global is not None: - from sage.misc.superseded import deprecation - deprecation(25825, "the set_global argument for CremonaDatabase is deprecated and ignored") - if name is None: if mini is None: if DatabaseCremona().is_present(): diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index dec5cabd8a4..77d3473a21f 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -1809,11 +1809,9 @@ def programs(self, language='all', preparsing=True, keep_comments=False): EXAMPLES:: - sage: ee = oeis('A001113') ; ee # optional -- internet - A001113: Decimal expansion of e. - + sage: ee = oeis.find_by_id('A00260') # optional -- internet sage: ee.programs('pari')[0] # optional -- internet - 0: default(realprecision, 50080); x=exp(1); for (n=1, 50000, d=floor(x); x=(x-d)*10; write("b001113.txt", n, " ", d)); \\ _Harry J. Smith_, Apr 15 2009 + 0: {a(n) = binomial(...)};... sage: G = oeis.find_by_id('A27642') # optional -- internet sage: G.programs('all') # optional -- internet diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 2f353a7dcba..df55ad9f6a0 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1821,8 +1821,8 @@ def parallel_dispatch(self): canceled:: sage: from tempfile import NamedTemporaryFile as NTF - sage: with NTF(suffix='.py', mode='w+t') as f1, \ - ....: NTF(suffix='.py', mode='w+t') as f2: + sage: with (NTF(suffix='.py', mode='w+t') as f1, + ....: NTF(suffix='.py', mode='w+t') as f2): ....: _ = f1.write("'''\nsage: import time; time.sleep(60)\n'''") ....: f1.flush() ....: _ = f2.write("'''\nsage: True\nFalse\n'''") diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 79a92835b26..05662691d16 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -880,26 +880,6 @@ def parse(self, string, *args): sage: ex.source 'for i in range(Integer(4)):\n print(i)\n' - Sage currently accepts backslashes as indicating that the end - of the current line should be joined to the next line. This - feature allows for breaking large integers over multiple lines - but is not standard for Python doctesting. It's not - guaranteed to persist:: - - sage: n = 1234\ - ....: 5678 - sage: print(n) - 12345678 - sage: type(n) - - - It also works without the line continuation:: - - sage: m = 8765\ - 4321 - sage: print(m) - 87654321 - Optional tags at the start of an example block persist to the end of the block (delimited by a blank line):: @@ -993,15 +973,15 @@ def parse(self, string, *args): sage: parse("::\n\n sage: # needs sage.combinat\n sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \\\n ....: import incidence_matrix_to_bit_rep_of_Vrep\n sage: P = polytopes.associahedron(['A',3])\n\n") ['::\n\n', - '', - (None, - 'from sage.geometry.polyhedron.combinatorial_polyhedron.conversions import incidence_matrix_to_bit_rep_of_Vrep\n', - 'from sage.geometry.polyhedron.combinatorial_polyhedron.conversions import incidence_matrix_to_bit_rep_of_Vrep\n'), - '', - (None, - "P = polytopes.associahedron(['A',3])\n", - "P = polytopes.associahedron(['A',Integer(3)])\n"), - '\n'] + '', + (None, + 'from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \\\n import incidence_matrix_to_bit_rep_of_Vrep\n', + 'from sage.geometry.polyhedron.combinatorial_polyhedron.conversions import incidence_matrix_to_bit_rep_of_Vrep\n'), + '', + (None, + "P = polytopes.associahedron(['A',3])\n", + "P = polytopes.associahedron(['A',Integer(3)])\n"), + '\n'] sage: example4 = '::\n\n sage: C.minimum_distance(algorithm="guava") # optional - guava\n ...\n 24\n\n' sage: parsed4 = DTP.parse(example4) @@ -1016,20 +996,10 @@ def parse(self, string, *args): find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) find_python_continuation = re.compile(r"^(\s*)\.\.\.([^\.])", re.M) python_prompt = re.compile(r"^(\s*)>>>", re.M) - backslash_replacer = re.compile(r"""(\s*)sage:(.*)\\\ * -\ *((\.){4}:)?\ *""") # The following are used to allow ... at the beginning of output ellipsis_tag = "" - # Hack for non-standard backslash line escapes accepted by the current - # doctest system. - m = backslash_replacer.search(string) - while m is not None: - g = m.groups() - string = string[:m.start()] + g[0] + "sage:" + g[1] + string[m.end():] - m = backslash_replacer.search(string, m.start()) - replace_ellipsis = not python_prompt.search(string) if replace_ellipsis: # There are no >>> prompts, so we can allow ... to begin the output diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index e17df277c1f..a62d48cfa19 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -25,6 +25,8 @@ from time import time as walltime from os import sysconf, times +from contextlib import contextmanager +from cysignals.alarm import alarm, cancel_alarm, AlarmInterrupt def count_noun(number, noun, plural=None, pad_number=False, pad_noun=False): @@ -749,3 +751,152 @@ def __ne__(self, other): True """ return not (self == other) + + +@contextmanager +def ensure_interruptible_after(seconds: float, max_wait_after_interrupt: float = 0.2, inaccuracy_tolerance: float = 0.1): + """ + Helper function for doctesting to ensure that the code is interruptible after a certain amount of time. + This should only be used for internal doctesting purposes. + + EXAMPLES:: + + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(1) as data: sleep(3) + + ``as data`` is optional, but if it is used, it will contain a few useful values:: + + sage: data # abs tol 1 + {'alarm_raised': True, 'elapsed': 1.0} + + ``max_wait_after_interrupt`` can be passed if the function may take longer than usual to be interrupted:: + + sage: # needs sage.misc.cython + sage: cython(r''' + ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t + ....: from cysignals.signals cimport sig_check + ....: + ....: cpdef void uninterruptible_sleep(double seconds): + ....: cdef timespec start_time, target_time + ....: clock_gettime(CLOCK_REALTIME, &start_time) + ....: + ....: cdef time_t floor_seconds = seconds + ....: target_time.tv_sec = start_time.tv_sec + floor_seconds + ....: target_time.tv_nsec = start_time.tv_nsec + ((seconds - floor_seconds) * 1e9) + ....: if target_time.tv_nsec >= 1000000000: + ....: target_time.tv_nsec -= 1000000000 + ....: target_time.tv_sec += 1 + ....: + ....: while True: + ....: clock_gettime(CLOCK_REALTIME, &start_time) + ....: if start_time.tv_sec > target_time.tv_sec or (start_time.tv_sec == target_time.tv_sec and start_time.tv_nsec >= target_time.tv_nsec): + ....: break + ....: + ....: cpdef void check_interrupt_only_occasionally(): + ....: for i in range(10): + ....: uninterruptible_sleep(0.8) + ....: sig_check() + ....: ''') + sage: with ensure_interruptible_after(1): # not passing max_wait_after_interrupt will raise an error + ....: check_interrupt_only_occasionally() + Traceback (most recent call last): + ... + RuntimeError: Function is not interruptible within 1.0000 seconds, only after 1.60... seconds + sage: with ensure_interruptible_after(1, max_wait_after_interrupt=0.9): + ....: check_interrupt_only_occasionally() + + TESTS:: + + sage: with ensure_interruptible_after(2) as data: sleep(1) + Traceback (most recent call last): + ... + RuntimeError: Function terminates early after 1... < 2.0000 seconds + sage: data # abs tol 1 + {'alarm_raised': False, 'elapsed': 1.0} + + The test above requires a large tolerance, because both ``time.sleep`` and + ``from posix.unistd cimport usleep`` may have slowdown on the order of 0.1s on Mac, + likely because the system is idle and GitHub CI switches the program out, + and context switch back takes time. Besides, there is an issue with ``Integer`` + destructor, see ``_ + So we use busy wait and Python integers:: + + sage: # needs sage.misc.cython + sage: cython(r''' + ....: from posix.time cimport clock_gettime, CLOCK_REALTIME, timespec, time_t + ....: from cysignals.signals cimport sig_check + ....: + ....: cpdef void interruptible_sleep(double seconds): + ....: cdef timespec start_time, target_time + ....: clock_gettime(CLOCK_REALTIME, &start_time) + ....: + ....: cdef time_t floor_seconds = seconds + ....: target_time.tv_sec = start_time.tv_sec + floor_seconds + ....: target_time.tv_nsec = start_time.tv_nsec + ((seconds - floor_seconds) * 1e9) + ....: if target_time.tv_nsec >= 1000000000: + ....: target_time.tv_nsec -= 1000000000 + ....: target_time.tv_sec += 1 + ....: + ....: while True: + ....: sig_check() + ....: clock_gettime(CLOCK_REALTIME, &start_time) + ....: if start_time.tv_sec > target_time.tv_sec or (start_time.tv_sec == target_time.tv_sec and start_time.tv_nsec >= target_time.tv_nsec): + ....: break + ....: ''') + sage: with ensure_interruptible_after(2) as data: interruptible_sleep(1r) + Traceback (most recent call last): + ... + RuntimeError: Function terminates early after 1.00... < 2.0000 seconds + sage: with ensure_interruptible_after(1) as data: uninterruptible_sleep(2r) + Traceback (most recent call last): + ... + RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00... seconds + sage: data # abs tol 0.01 + {'alarm_raised': True, 'elapsed': 2.0} + sage: with ensure_interruptible_after(1): uninterruptible_sleep(2r); raise RuntimeError + Traceback (most recent call last): + ... + RuntimeError: Function is not interruptible within 1.0000 seconds, only after 2.00... seconds + sage: data # abs tol 0.01 + {'alarm_raised': True, 'elapsed': 2.0} + + :: + + sage: with ensure_interruptible_after(1) as data: raise ValueError + Traceback (most recent call last): + ... + ValueError + sage: data # abs tol 0.01 + {'alarm_raised': False, 'elapsed': 0.0} + """ + seconds = float(seconds) + max_wait_after_interrupt = float(max_wait_after_interrupt) + inaccuracy_tolerance = float(inaccuracy_tolerance) + # use Python float to avoid slowdown with Sage Integer (see https://github.com/sagemath/cysignals/issues/215) + data = {} + start_time = walltime() + alarm(seconds) + alarm_raised = False + + try: + yield data + except AlarmInterrupt as e: + e.__traceback__ = None # workaround for https://github.com/python/cpython/pull/129276 + alarm_raised = True + finally: + before_cancel_alarm_elapsed = walltime() - start_time + cancel_alarm() + elapsed = walltime() - start_time + data["elapsed"] = elapsed + data["alarm_raised"] = alarm_raised + + if elapsed > seconds + max_wait_after_interrupt: + raise RuntimeError( + f"Function is not interruptible within {seconds:.4f} seconds, only after {elapsed:.4f} seconds" + + ("" if alarm_raised else " (__exit__ called before interrupt check)")) + + if alarm_raised: + if elapsed < seconds - inaccuracy_tolerance: + raise RuntimeError(f"Interrupted too early: {elapsed:.4f} < {seconds:.4f}, this should not happen") + else: + raise RuntimeError(f"Function terminates early after {elapsed:.4f} < {seconds:.4f} seconds") diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index c9d9a0e5403..619d4b54447 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -519,7 +519,7 @@ def dynatomic_polynomial(self, period): sage: R. = QQ[] sage: Pc. = ProjectiveSpace(R, 1) sage: G = DynamicalSystem_projective([(1/2*c + 1/2)*x^2 + (-2*c)*x*y + 2*c*y^2 , \ - (1/4*c + 1/2)*x^2 + (-c - 1)*x*y + (c + 1)*y^2]) + ....: (1/4*c + 1/2)*x^2 + (-c - 1)*x*y + (c + 1)*y^2]) sage: G.dehomogenize(1).dynatomic_polynomial(2) (1/4*c + 1/4)*x^2 + (-c - 1/2)*x + c + 1 """ diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 591693e5af8..8cc1d8358bc 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -116,7 +116,7 @@ class initialization directly. lazy_import('sage.rings.padics.factory', 'Qp') lazy_import('sage.rings.qqbar', 'number_field_elements_from_algebraics') -from sage.libs.pari.all import PariError +from cypari2.handle_error import PariError class DynamicalSystem_projective(SchemeMorphism_polynomial_projective_space, @@ -2259,10 +2259,10 @@ def canonical_height(self, P, **kwds): :: - sage: RSA768 = 123018668453011775513049495838496272077285356959533479219732245215\ - ....: 1726400507263657518745202199786469389956474942774063845925192557326303453731548\ - ....: 2685079170261221429134616704292143116022212404792747377940806653514195974598569\ - ....: 02143413 + sage: RSA768 = Integer('123018668453011775513049495838496272077285356959533479219732245215' + ....: '1726400507263657518745202199786469389956474942774063845925192557326303453731548' + ....: '2685079170261221429134616704292143116022212404792747377940806653514195974598569' + ....: '02143413') sage: P. = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([RSA768*x^2 + y^2, x*y]) sage: Q = P(RSA768,1) diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 0a1a5ba1526..fab391ececf 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -205,11 +205,11 @@ def _check_satisfies_equations(self, P): sage: P. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 \ - + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ + ....: 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0 * y0 + x1 * y1 + x2 * y2 sage: X = WehlerK3Surface([Z, Y]) sage: X._check_satisfies_equations([0, 0, 1, 1, 0, 0]) @@ -219,11 +219,11 @@ def _check_satisfies_equations(self, P): sage: P. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 \ - + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ + ....: 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: X._check_satisfies_equations([0, 1, 1, 1, 0, 0]) @@ -262,7 +262,7 @@ def _Lcoeff(self, component, i): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X._Lcoeff(0, 0) y0 @@ -272,7 +272,7 @@ def _Lcoeff(self, component, i): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z =x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X._Lcoeff(1, 0) x0 @@ -312,7 +312,7 @@ def _Qcoeff(self, component, i, j): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X._Qcoeff(0, 0, 0) y0*y1 + y2^2 @@ -322,7 +322,7 @@ def _Qcoeff(self, component, i, j): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X._Qcoeff(1, 1, 0) x0^2 @@ -360,7 +360,7 @@ def Gpoly(self, component, k): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X.Gpoly(1, 0) x0^2*x1^2 + x1^4 - x0*x1^2*x2 + x1^3*x2 + x1^2*x2^2 + x2^4 @@ -406,7 +406,7 @@ def Hpoly(self, component, i, j): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 \ - + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X.Hpoly(0, 1, 0) 2*y0*y1^3 + 2*y0*y1*y2^2 - y1*y2^3 @@ -449,10 +449,10 @@ def Lxa(self, a): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 \ - + 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - \ - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ - + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ - + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - \ + ....: x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ + ....: + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ + ....: + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(1, 1, 0, 1, 0, 0) @@ -489,9 +489,9 @@ def Qxa(self, a): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 \ - - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ - + 5*x0*x2*y0*y2 \ - - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ + ....: + 5*x0*x2*y0*y2 \ + ....: - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(1, 1, 0, 1, 0, 0) @@ -528,10 +528,10 @@ def Sxa(self, a): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 \ - + 3*x0*x1*y0*y1 \ - - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ - + 5*x0*x2*y0*y2 \ - - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 3*x0*x1*y0*y1 \ + ....: - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ + ....: + 5*x0*x2*y0*y2 \ + ....: - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) @@ -566,10 +566,10 @@ def Lyb(self, b): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z =x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 \ - + 3*x0*x1*y0*y1 \ - - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ - + 5*x0*x2*y0*y2 \ - - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 3*x0*x1*y0*y1 \ + ....: - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 \ + ....: + 5*x0*x2*y0*y2 \ + ....: - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) @@ -607,9 +607,9 @@ def Qyb(self, b): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 \ - + 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ - + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: + 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ + ....: + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(1, 1, 0, 1, 0, 0) @@ -645,9 +645,9 @@ def Syb(self, b): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ - + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 \ + ....: + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0 * y0 + x1 * y1 + x2 * y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(1, 1, 0, 1, 0, 0) @@ -721,7 +721,7 @@ def is_degenerate(self): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 + x2^2*y2^2 + \ - x2^2*y1^2 + x1^2*y2^2 + ....: x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X.is_degenerate() True @@ -730,8 +730,8 @@ def is_degenerate(self): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ + ....: 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: X.is_degenerate() @@ -741,8 +741,8 @@ def is_degenerate(self): sage: PP. = ProductProjectiveSpaces([2, 2], GF(3)) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ + ....: 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: X.is_degenerate() @@ -801,7 +801,7 @@ def degenerate_fibers(self): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 + x2^2*y2^2\ - + x2^2*y1^2 + x1^2*y2^2 + ....: + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X.degenerate_fibers() [[], [(1 : 0 : 0)]] @@ -824,7 +824,7 @@ def degenerate_fibers(self): sage: R = PP.coordinate_ring() sage: l = y0*x0 + y1*x1 + (y0 - y1)*x2 sage: q = (y1*y0 + y2^2)*x0^2 + ((y0^2 - y2*y1)*x1 + (y0^2 + (y1^2 - y2^2))*x2)*x0 \ - + (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 + ....: + (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 sage: X = WehlerK3Surface([l,q]) sage: X.degenerate_fibers() [[(-1 : 1 : 1), (0 : 0 : 1)], [(-1 : -1 : 1), (0 : 0 : 1)]] @@ -928,9 +928,9 @@ def degenerate_primes(self, check=True): sage: R. = PolynomialRing(QQ, 6) sage: L = y0*x0 + (y1*x1 + y2*x2) sage: Q = (2*y0^2 + y2*y0 + (2*y1^2 + y2^2))*x0^2 + ((y0^2 + y1*y0 + \ - (y1^2 + 2*y2*y1 + y2^2))*x1 + (2*y1^2 + y2*y1 + y2^2)*x2)*x0 + ((2*y0^2\ - + (y1 + 2*y2)*y0 + (2*y1^2 + y2*y1))*x1^2 + ((2*y1 + 2*y2)*y0 + (y1^2 + \ - y2*y1 + 2*y2^2))*x2*x1 + (2*y0^2 + y1*y0 + (2*y1^2 + y2^2))*x2^2) + ....: (y1^2 + 2*y2*y1 + y2^2))*x1 + (2*y1^2 + y2*y1 + y2^2)*x2)*x0 + ((2*y0^2\ + ....: + (y1 + 2*y2)*y0 + (2*y1^2 + y2*y1))*x1^2 + ((2*y1 + 2*y2)*y0 + (y1^2 + \ + ....: y2*y1 + 2*y2^2))*x2*x1 + (2*y0^2 + y1*y0 + (2*y1^2 + y2^2))*x2^2) sage: X = WehlerK3Surface([L, Q]) sage: X.degenerate_primes() [2, 3, 5, 11, 23, 47, 48747691, 111301831] @@ -1019,7 +1019,7 @@ def is_smooth(self): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = x0^2*y0*y1 + x0^2*y2^2 - x0*x1*y1*y2 + x1^2*y2*y1 +\ - x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 + ....: x2^2*y2^2 + x2^2*y1^2 + x1^2*y2^2 sage: X = WehlerK3Surface([Z, Y]) sage: X.is_smooth() False @@ -1028,9 +1028,9 @@ def is_smooth(self): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: X.is_smooth() @@ -1083,9 +1083,9 @@ def sigmaX(self, P, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 +\ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -\ - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 +\ - 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -\ + ....: 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 +\ + ....: 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(0, 0, 1, 1, 0, 0) @@ -1097,7 +1097,7 @@ def sigmaX(self, P, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: l = y0*x0 + y1*x1 + (y0 - y1)*x2 sage: q = (y1*y0)*x0^2 + ((y0^2)*x1 + (y0^2 + (y1^2 - y2^2))*x2)*x0\ - + (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 + ....: + (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 sage: X = WehlerK3Surface([l, q]) sage: X.sigmaX(X([1, 0, 0, 0, 1, -2])) (1 : 0 : 0 , 0 : 1/2 : 1) @@ -1330,9 +1330,9 @@ def sigmaY(self, P, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(0, 0, 1, 1, 0, 0) @@ -1344,7 +1344,7 @@ def sigmaY(self, P, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: l = y0*x0 + y1*x1 + (y0 - y1)*x2 sage: q = (y1*y0)*x0^2 + ((y0^2)*x1 + (y0^2 + (y1^2 - y2^2))*x2)*x0 +\ - (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 + ....: (y2*y0 + y1^2)*x1^2 + (y0^2 + (-y1^2 + y2^2))*x2*x1 sage: X = WehlerK3Surface([l, q]) sage: X.sigmaY(X([1, -1, 0 ,-1, -1, 1])) (1/10 : -1/10 : 1 , -1 : -1 : 1) @@ -1567,9 +1567,9 @@ def phi(self, a, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP([0, 0, 1, 1 ,0, 0]) @@ -1607,9 +1607,9 @@ def psi(self, a, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP([0, 0, 1, 1, 0, 0]) @@ -1652,8 +1652,8 @@ def lambda_plus(self, P, v, N, m, n, prec=100): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1\ - - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2\ - - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2\ + ....: - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: P = X([0, 0, 1, 1, 0, 0]) @@ -1752,8 +1752,8 @@ def lambda_minus(self, P, v, N, m, n, prec=100): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 \ - - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2\ - - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2\ + ....: - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: P = X([0, 0, 1, 1, 0, 0]) @@ -1845,8 +1845,8 @@ def canonical_height_plus(self, P, N, badprimes=None, prec=100): sage: R. = PolynomialRing(QQ, 6) sage: L = (-y0 - y1)*x0 + (-y0*x1 - y2*x2) sage: Q = (-y2*y0 - y1^2)*x0^2 + ((-y0^2 - y2*y0 + (-y2*y1 - y2^2))*x1 + \ - (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 \ - + (-y0^2 + (-y1 - y2)*y0)*x2^2) + ....: (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 \ + ....: + (-y0^2 + (-y1 - y2)*y0)*x2^2) sage: X = WehlerK3Surface([L, Q]) sage: P = X([1, 0, -1, 1, -1, 0]) #order 16 sage: X.canonical_height_plus(P, 5) # long time @@ -1857,9 +1857,9 @@ def canonical_height_plus(self, P, N, badprimes=None, prec=100): sage: set_verbose(None) sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: P = X([0, 1, 0, 0, 0, 1]) @@ -1909,8 +1909,8 @@ def canonical_height_minus(self, P, N, badprimes=None, prec=100): sage: R. = PolynomialRing(QQ, 6) sage: L = (-y0 - y1)*x0 + (-y0*x1 - y2*x2) sage: Q = (-y2*y0 - y1^2)*x0^2 + ((-y0^2 - y2*y0 + (-y2*y1 - y2^2))*x1\ - + (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1\ - + (-y0^2 + (-y1 - y2)*y0)*x2^2) + ....: + (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1\ + ....: + (-y0^2 + (-y1 - y2)*y0)*x2^2) sage: X = WehlerK3Surface([L, Q]) sage: P = X([1, 0, -1, 1, -1, 0]) #order 16 sage: X.canonical_height_minus(P, 5) # long time @@ -1921,9 +1921,9 @@ def canonical_height_minus(self, P, N, badprimes=None, prec=100): sage: set_verbose(None) sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 +\ - 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ - x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ + ....: 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ + ....: x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: P = X([0, 1, 0, 0, 0, 1]) @@ -1971,8 +1971,8 @@ def canonical_height(self, P, N, badprimes=None, prec=100): sage: R. = PolynomialRing(QQ, 6) sage: L = (-y0 - y1)*x0 + (-y0*x1 - y2*x2) sage: Q = (-y2*y0 - y1^2)*x0^2 + ((-y0^2 - y2*y0 + (-y2*y1 - y2^2))*x1 + \ - (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 \ - + (-y0^2 + (-y1 - y2)*y0)*x2^2) + ....: (-y0^2 - y2*y1)*x2)*x0 + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 \ + ....: + (-y0^2 + (-y1 - y2)*y0)*x2^2) sage: X = WehlerK3Surface([L, Q]) sage: P = X([1, 0, -1, 1,- 1, 0]) #order 16 sage: X.canonical_height(P, 5) # long time @@ -1983,8 +1983,8 @@ def canonical_height(self, P, N, badprimes=None, prec=100): sage: set_verbose(None) sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 \ - -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 \ + ....: -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: P = X(0, 1, 0, 0, 0, 1) @@ -2016,7 +2016,7 @@ def fiber(self, p, component): sage: R. = PolynomialRing(ZZ, 6) sage: Y = x0*y0 + x1*y1 - x2*y2 sage: Z = y0^2*x0*x1 + y0^2*x2^2 - y0*y1*x1*x2 + y1^2*x2*x1 + y2^2*x2^2 +\ - y2^2*x1^2 + y1^2*x2^2 + ....: y2^2*x1^2 + y1^2*x2^2 sage: X = WehlerK3Surface([Z, Y]) sage: Proj = ProjectiveSpace(QQ, 2) sage: P = Proj([1, 0, 0]) @@ -2029,8 +2029,8 @@ def fiber(self, p, component): sage: P. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 - \ - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - \ + ....: 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: Proj = P[0] @@ -2220,7 +2220,7 @@ def nth_iterate_phi(self, P, n, **kwds): sage: R.=PolynomialRing(QQ, 6) sage: L = (-y0 - y1)*x0 + (-y0*x1 - y2*x2) sage: Q = (-y2*y0 - y1^2)*x0^2 + ((-y0^2 - y2*y0 + (-y2*y1 - y2^2))*x1 + (-y0^2 - y2*y1)*x2)*x0 \ - + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 + (-y0^2 + (-y1 - y2)*y0)*x2^2) + ....: + ((-y0^2 - y2*y0 - y2^2)*x1^2 + (-y2*y0 - y1^2)*x2*x1 + (-y0^2 + (-y1 - y2)*y0)*x2^2) sage: X = WehlerK3Surface([L, Q]) sage: P = X([1, 0, -1, 1, -1, 0]) sage: X.nth_iterate_phi(P, 8) == X.nth_iterate_psi(P, 8) @@ -2320,9 +2320,9 @@ def orbit_phi(self, P, N, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ - x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ + ....: 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ + ....: x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP(0, 0, 1, 1, 0, 0) @@ -2379,9 +2379,9 @@ def orbit_psi(self, P, N, **kwds): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ - x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 - \ + ....: 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + \ + ....: x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = X(0, 0, 1, 1, 0, 0) @@ -2425,9 +2425,9 @@ def is_isomorphic(self, right): sage: PP. = ProductProjectiveSpaces([2, 2], QQ) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 - 4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: W = WehlerK3Surface([Z + Y^2, Y]) @@ -2464,8 +2464,8 @@ def is_symmetric_orbit(self, orbit): sage: PP. = ProductProjectiveSpaces([2, 2], GF(7)) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + 3*x0*x1*y0*y1 \ - -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 \ - -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: -2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 -4*x1*x2*y1^2 + 5*x0*x2*y0*y2 \ + ....: -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: T = PP([0, 0, 1, 1, 0, 0]) @@ -2518,9 +2518,9 @@ def cardinality( self): sage: PP. = ProductProjectiveSpaces([2, 2], GF(7)) sage: Z = x0^2*y0^2 + 3*x0*x1*y0^2 + x1^2*y0^2 + 4*x0^2*y0*y1 + \ - 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ - - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ - + x0*x1*y2^2 + 3*x2^2*y2^2 + ....: 3*x0*x1*y0*y1 - 2*x2^2*y0*y1 - x0^2*y1^2 + 2*x1^2*y1^2 - x0*x2*y1^2 \ + ....: - 4*x1*x2*y1^2 + 5*x0*x2*y0*y2 -4*x1*x2*y0*y2 + 7*x0^2*y1*y2 + 4*x1^2*y1*y2 \ + ....: + x0*x1*y2^2 + 3*x2^2*y2^2 sage: Y = x0*y0 + x1*y1 + x2*y2 sage: X = WehlerK3Surface([Z, Y]) sage: X.cardinality() diff --git a/src/sage/ext_data/magma/sage/basic.m b/src/sage/ext_data/magma/sage/basic.m index 678174dfd49..027b87e6287 100644 --- a/src/sage/ext_data/magma/sage/basic.m +++ b/src/sage/ext_data/magma/sage/basic.m @@ -8,7 +8,7 @@ function PreparseElts(R) end function; intrinsic Sage(X::.) -> MonStgElt, BoolElt -{Default way to convert a Magma object to Sage if we haven't +{Default way to convert a Magma object to Sage if we have not written anything better.} return Sprintf("%o", X), true; end intrinsic; @@ -153,16 +153,20 @@ intrinsic SageNamesHelper(X::.) -> MonStgElt {} /* XXX */ i := NumberOfNames(X); - if i ge 2 then - return (&* [ Sprintf("%o, ", X.j) : j in [ 1..i-1 ] ]) * Sprintf("%o", X.i); + if "$" in Sprint(X.i) then + /* unnamed variables */ + return "(" * (&* [ Sprintf("'x%o', ", j) : j in [ 1..i ] ]) * ")"; else - return Sprintf("%o", X.i); - end if; + /* named variables */ + return "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", X.j) : j in [ 1..i ] ]) * ")"; + +end if; end intrinsic; intrinsic Sage(X::RngUPol) -> MonStgElt, BoolElt {} - return Sprintf("%o['%o'.replace('$.', 'x').replace('.', '')]", Sage(BaseRing(X)), SageNamesHelper(X)), false; + txt := "PolynomialRing(%o, %o)"; + return Sprintf(txt, Sage(BaseRing(X)), SageNamesHelper(X)), false; end intrinsic; intrinsic Sage(X::RngUPolElt) -> MonStgElt, BoolElt @@ -173,7 +177,8 @@ intrinsic Sage(X::RngUPolElt) -> MonStgElt, BoolElt intrinsic Sage(X::RngMPol) -> MonStgElt, BoolElt {} - return Sprintf("%o['%o'.replace('$.', 'x').replace('.', '')]", Sage(BaseRing(X)), SageNamesHelper(X)), false; + txt := "PolynomialRing(%o, %o)"; + return Sprintf(txt, Sage(BaseRing(X)), SageNamesHelper(X)), false; end intrinsic; intrinsic Sage(X::RngMPolElt) -> MonStgElt, BoolElt @@ -187,9 +192,16 @@ intrinsic Sage(X::RngMPolElt) -> MonStgElt, BoolElt intrinsic Sage(K::FldNum) -> MonStgElt, BoolElt {} - names := [Sprintf("'%o'.replace('$.', 'a').replace('.', '')", a) : a in GeneratorsSequence(K)]; - polynomials := DefiningPolynomial(K); - return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false; + gens := GeneratorsSequence(K); + if "$" in Sprint(gens[1]) then + /* unnamed variables */ + names := "(" * (&* [ Sprintf("'a%o', ", j) : j in [ 1..#gens ] ]) * ")"; + else + /* named variables */ + names := "(" * (&* [ Sprintf("'%o'.replace('.', ''), ", a) : a in gens]) * ")"; + end if; + polynomials := DefiningPolynomial(K); + return Sprintf("NumberField(%o, %o)", Sage(polynomials), names), false; end intrinsic; intrinsic Sage(A::FldNumElt) -> MonStgElt, BoolElt @@ -233,6 +245,33 @@ intrinsic Sage(I::RngOrdIdl) -> MonStgElt, BoolElt return Sprintf("%o.ideal(%o)", Sage(K),Sage(seq)), true; end intrinsic; +/* Symmetric functions */ + +intrinsic Sage(X::AlgSym) -> MonStgElt, BoolElt +{} +if HasSchurBasis(X) then + return Sprintf("SymmetricFunctions(%o).s()", Sage(BaseRing(X))), false; +elif HasHomogeneousBasis(X) then + return Sprintf("SymmetricFunctions(%o).h()", Sage(BaseRing(X))), false; +elif HasElementaryBasis(X) then + return Sprintf("SymmetricFunctions(%o).e()", Sage(BaseRing(X))), false; +elif HasPowerSumBasis(X) then + return Sprintf("SymmetricFunctions(%o).p()", Sage(BaseRing(X))), false; +elif HasMonomialBasis(X) then + return Sprintf("SymmetricFunctions(%o).m()", Sage(BaseRing(X))), false; +end if; +end intrinsic; + +intrinsic Sage(X::AlgSymElt) -> MonStgElt, BoolElt +{} +PA := Parent(X); +SF := Sage(PA); +BR := Sage(BaseRing(PA)); +parts, coeffs := Support(X); +dict := (&* [ Sprintf("Partition(%o):%o(%o),", Sage(parts[i]), BR, Sage(coeffs[i])) : i in [1..#parts] ]); +return Sprintf("%o._from_dict({%o})", SF, dict), false; +end intrinsic; + /* Elliptic curves */ intrinsic Sage(X::CrvEll) -> MonStgElt, BoolElt @@ -264,3 +303,26 @@ intrinsic Sage(X::ModTupRngElt) -> MonStgElt, BoolElt {} return Sprintf("%o(%o)", Sage(Parent(X)), Sage(ElementToSequence(X))), true; end intrinsic; + +/* Power series rings */ + +intrinsic Sage(X::RngSerPow) -> MonStgElt, BoolElt +{} + txt := "PowerSeriesRing(%o, %o)"; + var := Sprintf("['%o']", X.1); + return Sprintf(txt, Sage(BaseRing(X)), var), false; +end intrinsic; + +intrinsic Sage(X::RngSerLaur) -> MonStgElt, BoolElt +{} + txt := "LaurentSeriesRing(%o, %o)"; + var := Sprintf("['%o']", X.1); + return Sprintf(txt, Sage(BaseRing(X)), var), false; +end intrinsic; + +intrinsic Sage(X::RngSerPuis) -> MonStgElt, BoolElt +{} + txt := "PuiseuxSeriesRing(%o, %o)"; + var := Sprintf("['%o']", X.1); + return Sprintf(txt, Sage(BaseRing(X)), var), false; +end intrinsic; diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index ac4a0bcd97f..8d3f67efae7 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -820,9 +820,9 @@ def absolute_filename(self) -> str: A :exc:`FeatureNotPresentError` is raised if the file cannot be found:: sage: from sage.features import StaticFile - sage: StaticFile(name='no_such_file', filename='KaT1aihu',\ - search_path=(), spkg='some_spkg',\ - url='http://rand.om').absolute_filename() # optional - sage_spkg + sage: StaticFile(name='no_such_file', filename='KaT1aihu', # optional - sage_spkg + ....: search_path=(), spkg='some_spkg', + ....: url='http://rand.om').absolute_filename() Traceback (most recent call last): ... FeatureNotPresentError: no_such_file is not available. diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index f511190aeff..19d677d3b0d 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -218,7 +218,7 @@ class SphericalHarmonic(BuiltinFunction): 0.345494149471335 sage: import numpy as np # needs scipy sage: if int(np.version.short_version[0]) > 1: # needs scipy - ....: np.set_printoptions(legacy="1.25") # needs scipy + ....: _ = np.set_printoptions(legacy="1.25") # needs scipy sage: import scipy.version sage: if scipy.version.version < '1.15.0': ....: from scipy.special import sph_harm # NB: arguments x and y are swapped # needs scipy @@ -458,7 +458,7 @@ def elliptic_j(z, prec=53): z = CC(z) except ValueError: raise ValueError("elliptic_j only defined for complex arguments.") - from sage.libs.pari.all import pari + from sage.libs.pari import pari return CC(pari(z).ellj()) diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index 2c731eba212..2019cf4d363 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -130,7 +130,8 @@ def bigraphical(self, G, A=None, K=QQ, names=None): sage: HA = hyperplane_arrangements.bigraphical(G, A) sage: HA.n_regions() 63 - sage: hyperplane_arrangements.bigraphical(G, 'generic').n_regions() + sage: hyperplane_arrangements.bigraphical(G, # random + ....: 'generic').n_regions() 65 sage: hyperplane_arrangements.bigraphical(G).n_regions() 59 @@ -138,6 +139,18 @@ def bigraphical(self, G, A=None, K=QQ, names=None): REFERENCES: - [HP2016]_ + + TESTS: + + One of the above examples was marked "# random" because the output is + not always the same. However, the answer is "65" more than 99.9% of the + time, so we can make a doctest by running it repeatedly + (see :issue:`39167`). :: + + sage: G = graphs.CycleGraph(4) + sage: any(hyperplane_arrangements.bigraphical(G, + ....: 'generic').n_regions() == 65 for _ in range(5)) + True """ n = G.num_verts() if A is None: # default to G-semiorder arrangement diff --git a/src/sage/geometry/integral_points.pxi b/src/sage/geometry/integral_points.pxi index a67535b450b..f351e63f5aa 100644 --- a/src/sage/geometry/integral_points.pxi +++ b/src/sage/geometry/integral_points.pxi @@ -531,10 +531,8 @@ cpdef rectangular_box_points(list box_min, list box_max, ....: (0, 0, 0, 0, 0, -1, 2, -1, 0), ....: (0, 0, 0, 0, 0, 0, -1, 2, -1)] sage: P = Polyhedron(ieqs=ieqs) - sage: alarm(0.5); P.integral_points() - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): P.integral_points() """ assert len(box_min) == len(box_max) assert not (count_only and return_saturated) diff --git a/src/sage/geometry/integral_points.py b/src/sage/geometry/integral_points.py index 48153591bcf..155c70480b9 100644 --- a/src/sage/geometry/integral_points.py +++ b/src/sage/geometry/integral_points.py @@ -1,3 +1,7 @@ +r""" +Cython helper methods to compute integral points in polyhedra +""" + try: from .integral_points_integer_dense import ( parallelotope_points, @@ -22,3 +26,9 @@ Inequality_int, InequalityCollection, ) + + +# __all__ is needed to generate Sphinx documentation +__all__ = ['InequalityCollection', 'Inequality_generic', 'Inequality_int', + 'loop_over_parallelotope_points', 'parallelotope_points', 'print_cache', + 'ray_matrix_normal_form', 'rectangular_box_points', 'simplex_points'] diff --git a/src/sage/geometry/polyhedron/base4.py b/src/sage/geometry/polyhedron/base4.py index ea0423c60fe..55a20537607 100644 --- a/src/sage/geometry/polyhedron/base4.py +++ b/src/sage/geometry/polyhedron/base4.py @@ -1126,10 +1126,10 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): Two polytopes with the same `f`-vector, but different combinatorial types:: sage: P = Polyhedron([[-605520/1525633, -605520/1525633, -1261500/1525633, -52200/1525633, 11833/1525633],\ - [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \ - [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\ - [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\ - [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]]) + ....: [-720/1769, -600/1769, 1500/1769, 0, -31/1769], [-216/749, 240/749, -240/749, -432/749, 461/749], \ + ....: [-50/181, 50/181, 60/181, -100/181, -119/181], [-32/51, -16/51, -4/51, 12/17, 1/17],\ + ....: [1, 0, 0, 0, 0], [16/129, 128/129, 0, 0, 1/129], [64/267, -128/267, 24/89, -128/267, 57/89],\ + ....: [1200/3953, -1200/3953, -1440/3953, -360/3953, -3247/3953], [1512/5597, 1512/5597, 588/5597, 4704/5597, 2069/5597]]) sage: C = polytopes.cyclic_polytope(5,10) sage: C.f_vector() == P.f_vector(); C.f_vector() True diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx index 4db4eb54171..cc150ba8e8d 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx @@ -1943,15 +1943,14 @@ cdef class CombinatorialPolyhedron(SageObject): """ if self.is_simplex(): return self.dim() + 1 - else: - from sage.arith.misc import binomial - k = 1 - while self.f_vector()[k+1] == binomial(self.n_vertices(), k + 1): - k += 1 - return k + cdef int k = 2 + f = self.f_vector() + while f[k] == self.n_vertices().binomial(k): + k += 1 + return k - 1 @cached_method - def is_neighborly(self, k=None): + def is_neighborly(self, k=None) -> bool: r""" Return whether the polyhedron is neighborly. @@ -2000,7 +1999,7 @@ cdef class CombinatorialPolyhedron(SageObject): return all(self.f_vector()[i+1] == binomial(self.n_vertices(), i + 1) for i in range(1, k)) - def is_simplex(self): + def is_simplex(self) -> bool: r""" Return whether the polyhedron is a simplex. @@ -2014,10 +2013,10 @@ cdef class CombinatorialPolyhedron(SageObject): sage: CombinatorialPolyhedron([[0,1],[0,2],[1,2]]).is_simplex() True """ - return self.is_bounded() and (self.dim()+1 == self.n_vertices()) + return self.is_bounded() and (self.dim() + 1 == self.n_vertices()) @cached_method - def is_simplicial(self): + def is_simplicial(self) -> bool: r""" Test whether the polytope is simplicial. diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 7a0ad2a2153..a93690e345f 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -390,7 +390,7 @@ def __init__(self, polyhedron, proj=projection_func_identity): self(proj) - def _repr_(self): + def _repr_(self) -> str: """ Return a string describing the projection. @@ -1234,7 +1234,7 @@ def render_3d(self, point_opts=None, line_opts=None, polygon_opts=None): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', opacity=0.8, vertex_color='green', axis=False, - output_type=None): + output_type='TikzPicture'): r""" Return a tikz picture of ``self`` as a string or as a :class:`~sage.misc.latex_standalone.TikzPicture` @@ -1256,8 +1256,8 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` -- real number (default: 0.8) between 0 and 1 giving the opacity of the front facets - ``axis`` -- boolean (default: ``False``); draw the axes at the origin or not - - ``output_type`` -- string (default: ``None``); valid values - are ``None`` (deprecated), ``'LatexExpr'`` and ``'TikzPicture'``, + - ``output_type`` -- string (default: ``'TikzPicture'``); valid values + are ``'LatexExpr'`` and ``'TikzPicture'``, whether to return a :class:`LatexExpr` object (which inherits from Python :class:`str`) or a :class:`TikzPicture` object from module :mod:`sage.misc.latex_standalone` @@ -1372,8 +1372,7 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: Image3 = P3.projection().tikz([0.5, -1, -0.1], 55, scale=3, ....: edge_color='blue!95!black', ....: facet_color='orange!95!black', opacity=0.7, - ....: vertex_color='yellow', axis=True, - ....: output_type='TikzPicture') + ....: vertex_color='yellow', axis=True) sage: Image3 \documentclass[tikz]{standalone} \begin{document} @@ -1406,6 +1405,15 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. + TESTS:: + + sage: P = Polyhedron(vertices=[[0,0,0], [1,0,0], + ....: [0,0,1], [0,1,0]]) + sage: P.projection().tikz(output_type='kawai') + Traceback (most recent call last): + ... + ValueError: output_type (='kawai') must be 'LatexExpr' or 'TikzPicture' + .. TODO:: Make it possible to draw Schlegel diagram for 4-polytopes. :: @@ -1425,39 +1433,30 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, elif self.polyhedron_dim < 2 or self.polyhedron_dim > 3: raise NotImplementedError("The polytope has to be 2 or 3-dimensional.") elif self.polyhedron_ambient_dim == 2: # self is a polygon in 2-space - tikz_string = self._tikz_2d(scale, edge_color, facet_color, opacity, - vertex_color, axis) + tikz_string = self._tikz_2d(scale, edge_color, + facet_color, opacity, + vertex_color, axis) elif self.polyhedron_dim == 2: # self is a polygon in 3-space tikz_string = self._tikz_2d_in_3d(view, angle, scale, edge_color, - facet_color, opacity, vertex_color, axis) + facet_color, opacity, + vertex_color, axis) else: # self is a 3-polytope in 3-space tikz_string = self._tikz_3d_in_3d(view, angle, scale, edge_color, - facet_color, opacity, vertex_color, axis) - - # set default value - if output_type is None: - from sage.misc.superseded import deprecation - msg = ("The default type of the returned object will soon be " - "changed from `sage.misc.latex.LatexExpr` to " - "`sage.misc.latex_standalone.TikzPicture`. Please " - "update your code to specify the desired output type as " - "`.tikz(output_type='LatexExpr')` to keep the old " - "behavior or `.tikz(output_type='TikzPicture')` to use " - "the future default behavior.") - deprecation(33002, msg) - output_type = 'LatexExpr' + facet_color, opacity, + vertex_color, axis) # return if output_type == 'LatexExpr': return tikz_string - elif output_type == 'TikzPicture': + + if output_type == 'TikzPicture': from sage.misc.latex_standalone import TikzPicture return TikzPicture(tikz_string, standalone_config=None, - usepackage=None, usetikzlibrary=None, macros=None, - use_sage_preamble=False) - else: - raise ValueError("output_type (='{}') must be 'LatexExpr' or" - " 'TikzPicture'".format(output_type)) + usepackage=None, usetikzlibrary=None, + macros=None, use_sage_preamble=False) + + raise ValueError("output_type (='{}') must be 'LatexExpr' or" + " 'TikzPicture'".format(output_type)) def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): r""" diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 62bce67f5c0..53189984bee 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -674,7 +674,7 @@ cdef class StaticSparseBackend(CGraphBackend): """ raise ValueError("graph is immutable; please change a copy instead (use function copy())") - def del_edge(self, object u, object v, object l, bint directed): + cpdef del_edge(self, object u, object v, object l, bint directed): r""" Delete an edge of the graph. No way. diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 821068763b3..76bd7bc50c4 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -2320,21 +2320,16 @@ def RandomTriangulation(n, set_position=False, k=3, seed=None): pattern = ['in', 'in', 'in', 'lf', 'in'] # 'partial closures' - def rotate_word_to_next_occurrence(word): - """ - Rotate ``word`` so that the given pattern occurs at the beginning. - - If the given pattern is not found, return the empty list. - """ + # We greedily perform the replacements 'in1,in2,in3,lf,in3'->'in1,in3'. + while True: + # first we rotate the word to it starts with pattern + word2 = [] N = len(word) for i in range(N): if all(word[(i + j) % N][0] == pattern[j] for j in range(5)): - return word[i:] + word[:i] - return [] + word2 = word[i:] + word[:i] + break - # We greedily perform the replacements 'in1,in2,in3,lf,in3'->'in1,in3'. - while True: - word2 = rotate_word_to_next_occurrence(word) if len(word2) >= 5: word = [word2[0]] + word2[4:] in1, in2, in3 = (u[1] for u in word2[:3]) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index acbf386833f..5b36d0da867 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -5488,6 +5488,17 @@ def cycle_basis(self, output='vertex'): [[(2, 3, 'c'), (3, 2, 'b')], [(3, 4, 'd'), (4, 1, 'f'), (1, 2, 'a'), (2, 3, 'b')], [(3, 4, 'e'), (4, 1, 'f'), (1, 2, 'a'), (2, 3, 'b')]] + + Check that the method is valid for immutable graphs:: + + sage: G = Graph(graphs.CycleGraph(3), immutable=True) + sage: G.cycle_basis() + [[0, 1, 2]] + sage: G = Graph([(1, 2, 'a'), (2, 3, 'b'), (2, 3, 'c'), + ....: (3, 4, 'd'), (3, 4, 'e'), (4, 1, 'f')], + ....: multiedges=True, immutable=True) + sage: G.cycle_basis() + [[3, 2], [4, 1, 2, 3], [4, 1, 2, 3]] """ if output not in ['vertex', 'edge']: raise ValueError('output must be either vertex or edge') @@ -5503,7 +5514,7 @@ def cycle_basis(self, output='vertex'): from sage.graphs.graph import Graph T = Graph(self.min_spanning_tree(), multiedges=True, format='list_of_edges') - H = self.copy() + H = self.copy(immutable=False) H.delete_edges(T.edge_iterator()) root = next(T.vertex_iterator()) rank = dict(T.breadth_first_search(root, report_distance=True)) @@ -7044,7 +7055,7 @@ def planar_dual(self, embedding=None): if e: e = e.pop() # just one edge since self and its dual are simple edges.append([v1, v2, self.edge_label(e[0], e[1])]) - return Graph([verts, edges]) + return Graph([verts, edges], format='vertices_and_edges') # Connectivity @@ -8176,6 +8187,7 @@ def good_edge(e): return val def longest_cycle(self, induced=False, use_edge_labels=False, + immutable=None, solver=None, verbose=0, *, integrality_tolerance=0.001): r""" Return the longest (induced) cycle of ``self``. @@ -8207,6 +8219,10 @@ def longest_cycle(self, induced=False, use_edge_labels=False, considered as a weight of `1`), or to compute a cycle with the largest possible number of edges (i.e., edge weights are set to 1) + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable (di)graph. ``immutable=None`` (default) means that + the (di)graph and its longest cycle will behave the same way. + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to ``None``, the default one is used. For more information on MILP solvers and which @@ -8317,6 +8333,32 @@ def longest_cycle(self, induced=False, use_edge_labels=False, longest cycle from Subgraph of (Circuit disjoint_union Circuit): Digraph on 5 vertices sage: D.longest_cycle(induced=True) longest induced cycle from Subgraph of (Circuit disjoint_union Circuit): Digraph on 5 vertices + + Check the behavior of parameter ``immutable``:: + + sage: G = Graph([(0, 1), (1, 2), (0, 2)], immutable=True) + sage: G.longest_cycle().is_immutable() + True + sage: G = graphs.Grid2dGraph(3, 4) + sage: G.longest_cycle(induced=False, immutable=True).is_immutable() + True + sage: G.longest_cycle(induced=True, immutable=True).is_immutable() + True + sage: D = digraphs.Circuit(15) + sage: for u, v in D.edges(labels=False): + ....: D.set_edge_label(u, v, 1) + sage: D.add_edge(0, 10, 50) + sage: D.add_edge(11, 1, 1) + sage: D.add_edge(13, 0, 1) + sage: D.longest_cycle(induced=False, use_edge_labels=False, immutable=True).is_immutable() + True + sage: D.longest_cycle(induced=False, use_edge_labels=True, immutable=True)[1].is_immutable() + True + sage: D = DiGraph(D, immutable=True) + sage: D.longest_cycle(induced=False, use_edge_labels=True)[1].is_immutable() + True + sage: D.longest_cycle(induced=False, use_edge_labels=True, immutable=False)[1].is_immutable() + False """ self._scream_if_not_simple() G = self @@ -8338,7 +8380,8 @@ def total_weight(gg): return gg.order() directed = G.is_directed() - immutable = G.is_immutable() + if immutable is None: + immutable = G.is_immutable() if directed: from sage.graphs.digraph import DiGraph as MyGraph blocks = G.strongly_connected_components() @@ -8356,6 +8399,7 @@ def total_weight(gg): h = G.subgraph(vertices=block) C = h.longest_cycle(induced=induced, use_edge_labels=use_edge_labels, + immutable=immutable, solver=solver, verbose=verbose, integrality_tolerance=integrality_tolerance) if total_weight(C) > best_w: @@ -8374,8 +8418,7 @@ def total_weight(gg): return MyGraph(name=name, immutable=immutable) if (not induced and ((directed and G.order() == 2) or (not directed and G.order() == 3))): - answer = G.copy() - answer.name(name) + answer = MyGraph(G, immutable=immutable, name=name) if use_edge_labels: return total_weight(answer), answer return answer @@ -8494,7 +8537,8 @@ def F(e): best.set_pos({u: pp for u, pp in G.get_pos().items() if u in best}) return (best_w, best) if use_edge_labels else best - def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP', + def longest_path(self, s=None, t=None, use_edge_labels=False, + algorithm='MILP', immutable=None, solver=None, verbose=0, *, integrality_tolerance=1e-3): r""" Return a longest path of ``self``. @@ -8535,6 +8579,10 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP', parameters ``s``, ``t`` and ``use_edge_labels``. An error is raised if these parameters are set. + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable (di)graph. ``immutable=None`` (default) means that + the (di)graph and its longest path will behave the same way. + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to ``None``, the default one is used. For more information on MILP solvers and which @@ -8702,6 +8750,24 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP', ... ValueError: parameters s, t, and use_edge_labels can not be used in combination with algorithm 'heuristic' + + Check the behavior of parameter ``immutable``:: + + sage: # needs sage.numerical.mip + sage: g1 = digraphs.RandomDirectedGNP(15, 0.2) + sage: for u,v in g.edge_iterator(labels=False): + ....: g.set_edge_label(u, v, random()) + sage: g2 = DiGraph(2 * g1, immutable=True) + sage: lp1 = g1.longest_path(use_edge_labels=True) + sage: lp2 = g2.longest_path(use_edge_labels=True) + sage: lp1[0] == lp2[0] + True + sage: not lp1[1].is_immutable() and lp2[1].is_immutable() + True + sage: lp1 = g1.longest_path(use_edge_labels=True, immutable=True) + sage: lp2 = g2.longest_path(use_edge_labels=True, immutable=False) + sage: lp1[1].is_immutable() and not lp2[1].is_immutable() + True """ self._scream_if_not_simple() @@ -8717,16 +8783,19 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP', raise ValueError("parameters s, t, and use_edge_labels can not " "be used in combination with algorithm 'heuristic'") + if immutable is None: + immutable = self.is_immutable() + # Quick improvement if not self.is_connected(): if use_edge_labels: - return max((g.longest_path(s=s, t=t, + return max((g.longest_path(s=s, t=t, immutable=immutable, use_edge_labels=use_edge_labels, algorithm=algorithm) for g in self.connected_components_subgraphs()), key=lambda x: x[0]) - return max((g.longest_path(s=s, t=t, + return max((g.longest_path(s=s, t=t, immutable=immutable, use_edge_labels=use_edge_labels, algorithm=algorithm) for g in self.connected_components_subgraphs()), @@ -8755,16 +8824,18 @@ def longest_path(self, s=None, t=None, use_edge_labels=False, algorithm='MILP', (self._directed and (s is not None) and (t is not None) and not self.shortest_path(s, t))): if self._directed: - from sage.graphs.digraph import DiGraph - return [0, DiGraph()] if use_edge_labels else DiGraph() - from sage.graphs.graph import Graph - return [0, Graph()] if use_edge_labels else Graph() + from sage.graphs.digraph import DiGraph as MyGraph + else: + from sage.graphs.graph import Graph as MyGraph + GG = MyGraph(immutable=immutable) + return [0, GG] if use_edge_labels else GG # Calling the heuristic if asked if algorithm == "heuristic": from sage.graphs.generic_graph_pyx import find_hamiltonian as fh x = fh(self, find_path=True)[1] - return self.subgraph(vertices=x, edges=list(zip(x[:-1], x[1:]))) + return self.subgraph(vertices=x, edges=list(zip(x[:-1], x[1:])), + immutable=immutable) ################## # LP Formulation # @@ -8894,21 +8965,20 @@ def weight(x): edge_used = p.get_values(edge_used, convert=bool, tolerance=integrality_tolerance) vertex_used = p.get_values(vertex_used, convert=bool, tolerance=integrality_tolerance) if self._directed: - g = self.subgraph(vertices=(v for v in self if vertex_used[v]), - edges=((u, v, l) for u, v, l in self.edge_iterator() - if edge_used[u, v])) + edges = ((u, v, l) for u, v, l in self.edge_iterator() + if edge_used[u, v]) else: - g = self.subgraph( - vertices=(v for v in self if vertex_used[v]), - edges=((u, v, l) for u, v, l in self.edge_iterator() - if edge_used[frozenset((u, v))])) + edges = ((u, v, l) for u, v, l in self.edge_iterator() + if edge_used[frozenset((u, v))]) + g = self.subgraph(vertices=(v for v in self if vertex_used[v]), + edges=edges, immutable=immutable) if use_edge_labels: return sum(map(weight, g.edge_labels())), g return g def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, - maximize=False, algorithm='MILP', solver=None, verbose=0, - *, integrality_tolerance=1e-3): + maximize=False, algorithm='MILP', immutable=None, + solver=None, verbose=0, *, integrality_tolerance=1e-3): r""" Return a Hamiltonian path of the current graph/digraph. @@ -8954,6 +9024,10 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, * The backtrack algorithm does not support edge weighting, so setting ``use_edge_labels=True`` will force the use of the MILP algorithm. + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable (di)graph. ``immutable=None`` (default) means that + the (di)graph and its hamiltonian path will behave the same way. + - ``solver`` -- string (default: ``None``); specifies a Mixed Integer Linear Programming (MILP) solver to be used. If set to ``None``, the default one is used. For more information on MILP solvers and which @@ -9039,6 +9113,20 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, Traceback (most recent call last): ... ValueError: algorithm must be either 'backtrack' or 'MILP' + + Check the behavior of parameter ``immutable``:: + + sage: # needs sage.numerical.mip + sage: g = graphs.Grid2dGraph(3, 3) + sage: g.hamiltonian_path().is_immutable() + False + sage: g.hamiltonian_path(immutable=True).is_immutable() + True + sage: g = Graph(g, immutable=True) + sage: g.hamiltonian_path().is_immutable() + True + sage: g.hamiltonian_path(use_edge_labels=True)[1].is_immutable() + True """ if use_edge_labels or algorithm is None: # We force the algorithm to 'MILP' @@ -9053,6 +9141,8 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, if not self.is_connected(): return (0, None) if use_edge_labels else None + if immutable is None: + immutable = self.is_immutable() # # Deal with loops and multiple edges # @@ -9116,7 +9206,8 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, new_t = ones.pop() if not use_edge_labels and algorithm == "backtrack": - path = g.longest_path(s=new_s, t=new_t, algorithm='backtrack') + path = g.longest_path(s=new_s, t=new_t, algorithm='backtrack', + immutable=immutable) return path if path.order() == g.order() else None # @@ -9163,6 +9254,8 @@ def hamiltonian_path(self, s=None, t=None, use_edge_labels=False, tsp.delete_vertices(extra_vertices) tsp.name("Hamiltonian path from {}".format(self.name())) + if immutable: + tsp = tsp.copy(immutable=True) def weight(label): return 1 if label is None else label @@ -10799,7 +10892,7 @@ def multicommodity_flow(self, terminals, integer=True, use_edge_labels=False, solvers over an inexact base ring; see :meth:`MixedIntegerLinearProgram.get_values`. - Only useful when parameter ``ìnteger`` is ``True``. + Only useful when parameter ``integer`` is ``True``. ALGORITHM: @@ -19358,7 +19451,16 @@ def add_clique(self, vertices, loops=False): sage: D.add_clique(range(4), loops=True) sage: D.is_clique(directed_clique=True, loops=True) True + + Immutable graph:: + + sage: Graph(immutable=True).add_clique([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ + if self.is_immutable(): + raise ValueError("graph is immutable; please change a copy instead (use function copy())") import itertools if loops: if self.is_directed(): @@ -19424,6 +19526,13 @@ def add_cycle(self, vertices): sage: G.add_cycle(['a', 'b', 'c']) sage: G.order(), G.size() (3, 3) + + Immutable graph:: + + sage: Graph(immutable=True).add_cycle([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ if vertices: self.add_path(vertices) @@ -19460,13 +19569,23 @@ def add_path(self, vertices): sage: D.add_path(list(range(4))) sage: D.edges(sort=True) [(0, 1, None), (1, 2, None), (2, 3, None)] + + TESTS: + + Immutable graph:: + + sage: Graph(immutable=True).add_path([]) + sage: Graph(immutable=True).add_path([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ if not vertices: return self.add_vertices(vertices) self.add_edges(zip(vertices[:-1], vertices[1:])) - def complement(self): + def complement(self, immutable=None): """ Return the complement of the (di)graph. @@ -19474,6 +19593,12 @@ def complement(self): that are not in the original graph. This is not well defined for graphs with multiple edges. + INPUT: + + - ``immutable`` -- boolean (default: ``None``); whether to return a + mutable or an immutable version of ``self``. By default (``None``), + the graph and its complement behave the same. + EXAMPLES:: sage: P = graphs.PetersenGraph() @@ -19515,6 +19640,17 @@ def complement(self): Graph on 10 vertices sage: g.complement() Graph on 10 vertices + + Check the behavior of parameter ``immutable``:: + + sage: type(Graph().complement()._backend) + + sage: type(Graph().complement(immutable=True)._backend) + + sage: type(Graph(immutable=True).complement()._backend) + + sage: type(Graph(immutable=True).complement(immutable=False)._backend) + """ self._scream_if_not_simple() @@ -19523,8 +19659,9 @@ def complement(self): if self.name(): G.name("complement({})".format(self.name())) - - if self.is_immutable(): + if immutable is None: + immutable = self.is_immutable() + if immutable: return G.copy(immutable=True) return G @@ -19578,12 +19715,31 @@ def to_simple(self, to_undirected=True, keep_label='any', immutable=None): [(2, 3, 1), (3, 2, None)] sage: G.to_simple(to_undirected=False, keep_label='max').edges(sort=True) [(2, 3, 2), (3, 2, None)] + + TESTS: + + Check the behavior of parameter ``immutable``:: + + sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=True) + sage: H = G.to_simple() + sage: H.is_immutable() + True + sage: H.edges(labels=False) + [(0, 1)] + sage: H = G.to_simple(immutable=False) + sage: H.is_immutable() + False + sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=False) + sage: G.to_simple().is_immutable() + False + sage: G.to_simple(immutable=True).is_immutable() + True """ if to_undirected: from sage.graphs.graph import Graph - g = Graph(self) + g = Graph(self, immutable=False) else: - g = copy(self) + g = self.copy(immutable=False) g.allow_loops(False) g.allow_multiple_edges(False, keep_label=keep_label) if immutable is None: @@ -19640,8 +19796,27 @@ def disjoint_union(self, other, labels='pairs', immutable=None): Custom path disjoint_union Cycle graph: Graph on 5 vertices sage: J.vertices(sort=True) [(0, 'a'), (0, 'b'), (1, 0), (1, 1), (1, 2)] + + TESTS: + + Check the behavior of parameter ``immutable``:: + + sage: G = Graph([(0, 1)]) + sage: G.disjoint_union(G).is_immutable() + False + sage: G.disjoint_union(G, immutable=True).is_immutable() + True + sage: H = G.copy(immutable=True) + sage: H.disjoint_union(H).is_immutable() + True + sage: G.disjoint_union(G, immutable=False).is_immutable() + False + sage: H.disjoint_union(G).is_immutable() + False + sage: G.disjoint_union(G, immutable=True).is_immutable() + True """ - if (self._directed and not other._directed) or (not self._directed and other._directed): + if self._directed != other._directed: raise TypeError('both arguments must be of the same class') if labels not in ['pairs', 'integers']: @@ -19653,7 +19828,11 @@ def disjoint_union(self, other, labels='pairs', immutable=None): else: r_self = {v: (0, v) for v in self} r_other = {v: (1, v) for v in other} - G = self.relabel(r_self, inplace=False).union(other.relabel(r_other, inplace=False), immutable=immutable) + + from itertools import chain + vertices = chain(r_self.values(), r_other.values()) + edges = chain(((r_self[u], r_self[v], w) for u, v, w in self.edge_iterator()), + ((r_other[u], r_other[v], w) for u, v, w in other.edge_iterator())) a = self.name() if not a: @@ -19661,8 +19840,22 @@ def disjoint_union(self, other, labels='pairs', immutable=None): b = other.name() if not b: b = other._repr_() - G._name = '{} disjoint_union {}'.format(a, b) - return G + name = f"{a} disjoint_union {b}" + + multiedges = self.allows_multiple_edges() or other.allows_multiple_edges() + loops = self.allows_loops() or other.allows_loops() + weighted = self.weighted() and other.weighted() + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + + if self._directed: + from sage.graphs.digraph import DiGraph as GT + else: + from sage.graphs.graph import Graph as GT + + return GT([vertices, edges], format='vertices_and_edges', + weighted=weighted, loops=loops, multiedges=multiedges, + name=name, immutable=immutable) def union(self, other, immutable=None): """ @@ -19739,32 +19932,27 @@ def union(self, other, immutable=None): sage: D1.union(D2).weighted() or D2.union(D1).weighted() False """ - if (self._directed and not other._directed) or (not self._directed and other._directed): + if self._directed != other._directed: raise TypeError('both arguments must be of the same class') multiedges = self.allows_multiple_edges() or other.allows_multiple_edges() loops = self.allows_loops() or other.allows_loops() weighted = self.weighted() and other.weighted() + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() if self._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(multiedges=multiedges, loops=loops, weighted=weighted) + from sage.graphs.digraph import DiGraph as GT else: - from sage.graphs.graph import Graph - G = Graph(multiedges=multiedges, loops=loops, weighted=weighted) - G.add_vertices(self) - G.add_vertices(other) - G.add_edges(self.edge_iterator()) - G.add_edges(other.edge_iterator()) + from sage.graphs.graph import Graph as GT - if immutable is None: - immutable = self.is_immutable() and other.is_immutable() - if immutable: - G = G.copy(immutable=True) - - return G + from itertools import chain + return GT([chain(self, other), + chain(self.edge_iterator(), other.edge_iterator())], + format='vertices_and_edges', weighted=weighted, loops=loops, + multiedges=multiedges, immutable=immutable) - def cartesian_product(self, other): + def cartesian_product(self, other, immutable=None): r""" Return the Cartesian product of ``self`` and ``other``. @@ -19773,6 +19961,15 @@ def cartesian_product(self, other): and `((u,v), (w,x))` is an edge iff either - `(u, w)` is an edge of self and `v = x`, or - `(v, x)` is an edge of other and `u = w`. + INPUT: + + - ``other`` -- a graph or a digraph + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable product. ``immutable=None`` (default) means that the + graphs and their product will behave the same way. If only one of them + is immutable, the product will be mutable. + .. SEEALSO:: - :meth:`~sage.graphs.graph_decompositions.graph_products.is_cartesian_product` @@ -19825,27 +20022,48 @@ def cartesian_product(self, other): sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # needs sage.combinat sage: B.is_isomorphic(Q.subgraph(V)) # needs sage.combinat True + + Check the behavior of parameter ``immutable``:: + + sage: A = Graph([(0, 1)]) + sage: B = Graph([('a', 'b')], immutable=True) + sage: A.cartesian_product(A).is_immutable() + False + sage: A.cartesian_product(A, immutable=True).is_immutable() + True + sage: A.cartesian_product(B).is_immutable() + False + sage: A.cartesian_product(B, immutable=True).is_immutable() + True + sage: B.cartesian_product(B).is_immutable() + True + sage: B.cartesian_product(B, immutable=False).is_immutable() + False """ self._scream_if_not_simple(allow_loops=True) if self._directed and other._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.digraph import DiGraph as GT elif (not self._directed) and (not other._directed): - from sage.graphs.graph import Graph - G = Graph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.graph import Graph as GT else: raise TypeError('the graphs should be both directed or both undirected') - G.add_vertices((u, v) for u in self for v in other) - for u, w in self.edge_iterator(labels=None): - for v in other: - G.add_edge((u, v), (w, v)) - for v, x in other.edge_iterator(labels=None): - for u in self: - G.add_edge((u, v), (u, x)) - return G + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + loops = self.has_loops() or other.has_loops() + vertices = ((u, v) for u in self for v in other) + from itertools import chain + edges = chain((((u, v), (w, v)) + for u, w in self.edge_iterator(labels=False) + for v in other), + (((u, v), (u, x)) + for v, x in other.edge_iterator(labels=False) + for u in self)) + + return GT([vertices, edges], format='vertices_and_edges', + loops=loops, immutable=immutable) - def tensor_product(self, other): + def tensor_product(self, other, immutable=None): r""" Return the tensor product of ``self`` and ``other``. @@ -19858,6 +20076,15 @@ def tensor_product(self, other): Kronecker product (referring to the Kronecker matrix product). See the :wikipedia:`Kronecker_product`. + INPUT: + + - ``other`` -- a graph or a digraph + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable product. ``immutable=None`` (default) means that the + graphs and their product will behave the same way. If only one of them + is immutable, the product will be mutable. + EXAMPLES:: sage: Z = graphs.CompleteGraph(2) @@ -19910,28 +20137,53 @@ def tensor_product(self, other): sage: T = B1.tensor_product(B2) sage: T.is_isomorphic(digraphs.DeBruijn(2 * 3, 3)) True + + Check the behavior of parameter ``immutable``:: + + sage: A = Graph([(0, 1)]) + sage: B = Graph([('a', 'b')], immutable=True) + sage: A.tensor_product(A).is_immutable() + False + sage: A.tensor_product(A, immutable=True).is_immutable() + True + sage: A.tensor_product(B).is_immutable() + False + sage: A.tensor_product(B, immutable=True).is_immutable() + True + sage: B.tensor_product(B).is_immutable() + True + sage: B.tensor_product(B, immutable=False).is_immutable() + False """ self._scream_if_not_simple(allow_loops=True) if self._directed and other._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.digraph import DiGraph as GT + edges = (((u, v), (w, x)) + for u, w in self.edge_iterator(labels=False) + for v, x in other.edge_iterator(labels=False)) elif (not self._directed) and (not other._directed): - from sage.graphs.graph import Graph - G = Graph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.graph import Graph as GT + from itertools import chain + edges = chain((((u, v), (w, x)) + for u, w in self.edge_iterator(labels=False) + for v, x in other.edge_iterator(labels=False)), + (((u, x), (w, v)) + for u, w in self.edge_iterator(labels=False) + for v, x in other.edge_iterator(labels=False))) else: raise TypeError('the graphs should be both directed or both undirected') - G.add_vertices((u, v) for u in self for v in other) - for u, w in self.edge_iterator(labels=None): - for v, x in other.edge_iterator(labels=None): - G.add_edge((u, v), (w, x)) - if not G._directed: - G.add_edge((u, x), (w, v)) - return G + + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + loops = self.has_loops() or other.has_loops() + vertices = ((u, v) for u in self for v in other) + return GT([vertices, edges], format='vertices_and_edges', + loops=loops, immutable=immutable) categorical_product = tensor_product kronecker_product = tensor_product - def lexicographic_product(self, other): + def lexicographic_product(self, other, immutable=None): r""" Return the lexicographic product of ``self`` and ``other``. @@ -19941,6 +20193,15 @@ def lexicographic_product(self, other): * `(u, w)` is an edge of `G`, or * `u = w` and `(v, x)` is an edge of `H`. + INPUT: + + - ``other`` -- a graph or a digraph + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable product. ``immutable=None`` (default) means that the + graphs and their product will behave the same way. If only one of them + is immutable, the product will be mutable. + EXAMPLES:: sage: Z = graphs.CompleteGraph(2) @@ -19986,27 +20247,48 @@ def lexicographic_product(self, other): ((1, 'b'), (2, 'b')), ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(J.lexicographic_product(I)) False + + Check the behavior of parameter ``immutable``:: + + sage: A = Graph([(0, 1)]) + sage: B = Graph([('a', 'b')], immutable=True) + sage: A.lexicographic_product(A).is_immutable() + False + sage: A.lexicographic_product(A, immutable=True).is_immutable() + True + sage: A.lexicographic_product(B).is_immutable() + False + sage: A.lexicographic_product(B, immutable=True).is_immutable() + True + sage: B.lexicographic_product(B).is_immutable() + True + sage: B.lexicographic_product(B, immutable=False).is_immutable() + False """ self._scream_if_not_simple(allow_loops=True) if self._directed and other._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.digraph import DiGraph as GT elif (not self._directed) and (not other._directed): - from sage.graphs.graph import Graph - G = Graph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.graph import Graph as GT else: raise TypeError('the graphs should be both directed or both undirected') - G.add_vertices((u, v) for u in self for v in other) - for u, w in self.edge_iterator(labels=None): - for v in other: - for x in other: - G.add_edge((u, v), (w, x)) - for u in self: - for v, x in other.edge_iterator(labels=None): - G.add_edge((u, v), (u, x)) - return G - def strong_product(self, other): + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + loops = self.has_loops() or other.has_loops() + vertices = ((u, v) for u in self for v in other) + from itertools import chain + edges = chain((((u, v), (w, x)) + for u, w in self.edge_iterator(labels=False) + for v in other + for x in other), + (((u, v), (u, x)) + for u in self + for v, x in other.edge_iterator(labels=False))) + return GT([vertices, edges], format='vertices_and_edges', + loops=loops, immutable=immutable) + + def strong_product(self, other, immutable=None): r""" Return the strong product of ``self`` and ``other``. @@ -20021,6 +20303,15 @@ def strong_product(self, other): In other words, the edges of the strong product is the union of the edges of the tensor and Cartesian products. + INPUT: + + - ``other`` -- a graph or a digraph + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable product. ``immutable=None`` (default) means that the + graphs and their product will behave the same way. If only one of them + is immutable, the product will be mutable. + EXAMPLES:: sage: Z = graphs.CompleteGraph(2) @@ -20068,31 +20359,57 @@ def strong_product(self, other): sage: expected = gm * hn + hm * gn + 2 * gm * hm sage: product_size == expected True + + Check the behavior of parameter ``immutable``:: + + sage: A = Graph([(0, 1)]) + sage: B = Graph([('a', 'b')], immutable=True) + sage: A.strong_product(A).is_immutable() + False + sage: A.strong_product(A, immutable=True).is_immutable() + True + sage: A.strong_product(B).is_immutable() + False + sage: A.strong_product(B, immutable=True).is_immutable() + True + sage: B.strong_product(B).is_immutable() + True + sage: B.strong_product(B, immutable=False).is_immutable() + False """ self._scream_if_not_simple(allow_loops=True) if self._directed and other._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.digraph import DiGraph as GT elif (not self._directed) and (not other._directed): - from sage.graphs.graph import Graph - G = Graph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.graph import Graph as GT else: raise TypeError('the graphs should be both directed or both undirected') - G.add_vertices((u, v) for u in self for v in other) - for u, w in self.edge_iterator(labels=None): - for v in other: - G.add_edge((u, v), (w, v)) - for v, x in other.edge_iterator(labels=None): - G.add_edge((u, v), (w, x)) - if not self._directed: - G.add_edge((w, v), (u, x)) - for v, x in other.edge_iterator(labels=None): - for u in self: - G.add_edge((u, v), (u, x)) - return G + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + loops = self.has_loops() or other.has_loops() + vertices = ((u, v) for u in self for v in other) + + edges_1 = (((u, v), (w, v)) + for u, w in self.edge_iterator(labels=False) for v in other) + edges_2 = (((u, v), (u, x)) + for v, x in other.edge_iterator(labels=False) for u in self) + edges_3 = (((u, v), (w, x)) + for u, w in self.edge_iterator(labels=False) + for v, x in other.edge_iterator(labels=False)) + if self._directed: + edges_4 = () + else: + edges_4 = (((w, v), (u, x)) + for u, w in self.edge_iterator(labels=False) + for v, x in other.edge_iterator(labels=False)) - def disjunctive_product(self, other): + from itertools import chain + edges = chain(edges_1, edges_2, edges_3, edges_4) + return GT([vertices, edges], format='vertices_and_edges', + loops=loops, immutable=immutable) + + def disjunctive_product(self, other, immutable=None): r""" Return the disjunctive product of ``self`` and ``other``. @@ -20102,6 +20419,15 @@ def disjunctive_product(self, other): * `(u, w)` is an edge of `G`, or * `(v, x)` is an edge of `H`. + INPUT: + + - ``other`` -- a graph or a digraph + + - ``immutable`` -- boolean (default: ``None``); whether to create a + mutable/immutable product. ``immutable=None`` (default) means that the + graphs and their product will behave the same way. If only one of them + is immutable, the product will be mutable. + EXAMPLES:: sage: Z = graphs.CompleteGraph(2) @@ -20147,27 +20473,48 @@ def disjunctive_product(self, other): ((2, 'a'), (0, 'b')), ((2, 'a'), (1, 'b')), ((2, 'a'), (2, 'b'))] sage: T.is_isomorphic(J.disjunctive_product(I)) True + + Check the behavior of parameter ``immutable``:: + + sage: A = Graph([(0, 1)]) + sage: B = Graph([('a', 'b')], immutable=True) + sage: A.disjunctive_product(A).is_immutable() + False + sage: A.disjunctive_product(A, immutable=True).is_immutable() + True + sage: A.disjunctive_product(B).is_immutable() + False + sage: A.disjunctive_product(B, immutable=True).is_immutable() + True + sage: B.disjunctive_product(B).is_immutable() + True + sage: B.disjunctive_product(B, immutable=False).is_immutable() + False """ self._scream_if_not_simple(allow_loops=True) if self._directed and other._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.digraph import DiGraph as GT elif (not self._directed) and (not other._directed): - from sage.graphs.graph import Graph - G = Graph(loops=(self.has_loops() or other.has_loops())) + from sage.graphs.graph import Graph as GT else: raise TypeError('the graphs should be both directed or both undirected') - G.add_vertices((u, v) for u in self for v in other) - for u, w in self.edge_iterator(labels=None): - for v in other: - for x in other: - G.add_edge((u, v), (w, x)) - for v, x in other.edge_iterator(labels=None): - for u in self: - for w in self: - G.add_edge((u, v), (w, x)) - return G + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + loops = self.has_loops() or other.has_loops() + vertices = ((u, v) for u in self for v in other) + edges_1 = (((u, v), (w, x)) + for u, w in self.edge_iterator(labels=False) + for v in other + for x in other) + edges_2 = (((u, v), (w, x)) + for v, x in other.edge_iterator(labels=False) + for u in self + for w in self) + from itertools import chain + edges = chain(edges_1, edges_2) + return GT([vertices, edges], format='vertices_and_edges', + loops=loops, immutable=immutable) def transitive_closure(self, loops=True): r""" diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index e60f444a089..3e8354d925d 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -104,7 +104,7 @@ :: sage: d = {0: [1,4,5], 1: [2,6], 2: [3,7], 3: [4,8], 4: [9], \ - 5: [7, 8], 6: [8,9], 7: [9]} + ....: 5: [7, 8], 6: [8,9], 7: [9]} sage: G = Graph(d); G Graph on 10 vertices sage: G.plot().show() # or G.show() # needs sage.plot @@ -308,7 +308,7 @@ objects for entries, one can make that association:: sage: d = {0 : graphs.DodecahedralGraph(), 1 : graphs.FlowerSnark(), \ - 2 : graphs.MoebiusKantorGraph(), 3 : graphs.PetersenGraph() } + ....: 2 : graphs.MoebiusKantorGraph(), 3 : graphs.PetersenGraph() } sage: d[2] Moebius-Kantor Graph: Graph on 16 vertices sage: T = graphs.TetrahedralGraph() diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 74f0ec0b0b5..948ebefaad9 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -852,13 +852,8 @@ def update_db(self): This method downloads the ISGCI database from the website `GraphClasses.org `_. It then extracts the - zip file and parses its XML content. - - Depending on the credentials of the user running Sage when this command - is run, one attempt is made at saving the result in Sage's directory so - that all users can benefit from it. If the credentials are not - sufficient, the XML file are saved instead in the user's directory (in - the SAGE_DB folder). + zip file and parses its XML content. The XML file is saved in the directory + controlled by the :class:`DatabaseGraphs` class (usually, ``$HOME/.sage/db``). EXAMPLES:: diff --git a/src/sage/graphs/matching.py b/src/sage/graphs/matching.py index c8eea2bb005..66e87683be8 100644 --- a/src/sage/graphs/matching.py +++ b/src/sage/graphs/matching.py @@ -877,7 +877,7 @@ def is_matching_covered(G, matching=None, algorithm='Edmonds', coNP_certificate= sage: H = graphs.PathGraph(20) sage: M = H.matching() sage: H.is_matching_covered(matching=M, coNP_certificate=True) - (False, (1, 2, None)) + (False, (2, 1, None)) TESTS: @@ -1019,44 +1019,33 @@ def is_matching_covered(G, matching=None, algorithm='Edmonds', coNP_certificate= if color[u]: u, v = v, u - if M.has_edge(u, v): - H.add_edge(u, v) - else: - H.add_edge(v, u) + H.add_edge((u, v)) + if next(M.neighbor_iterator(u)) == v: + H.add_edge((v, u)) # Check if H is strongly connected using Kosaraju's algorithm - def dfs(J, v, visited, orientation): + def dfs(v, visited, neighbor_iterator): stack = [v] # a stack of vertices while stack: v = stack.pop() + visited.add(v) - if v not in visited: - visited[v] = True - - if orientation == 'in': - for u in J.neighbors_out(v): - if u not in visited: - stack.append(u) - - elif orientation == 'out': - for u in J.neighbors_in(v): - if u not in visited: - stack.append(u) - else: - raise ValueError('Unknown orientation') + for u in neighbor_iterator(v): + if u not in visited: + stack.append(u) root = next(H.vertex_iterator()) - visited_in = {} - dfs(H, root, visited_in, 'in') + visited_in = set() + dfs(root, visited_in, H.neighbor_in_iterator) - visited_out = {} - dfs(H, root, visited_out, 'out') + visited_out = set() + dfs(root, visited_out, H.neighbor_out_iterator) for edge in H.edge_iterator(): u, v, _ = edge - if (u not in visited_in) or (v not in visited_out): + if (u not in visited_out) or (v not in visited_in): if not M.has_edge(edge): return (False, edge) if coNP_certificate else False diff --git a/src/sage/graphs/matching_covered_graph.py b/src/sage/graphs/matching_covered_graph.py index 370fc1f9cad..b52333443a9 100644 --- a/src/sage/graphs/matching_covered_graph.py +++ b/src/sage/graphs/matching_covered_graph.py @@ -69,6 +69,7 @@ ``lexicographic_product()`` | Return the lexicographic product of ``self`` and ``other``. ``load_afile()`` | Load the matching covered graph specified in the given file into the current object. ``merge_vertices()`` | Merge vertices. + ``minor()`` | Return the vertices of a minor isomorphic to `H` in the current graph. ``random_subgraph()`` | Return a random matching covered subgraph containing each vertex with probability ``p``. ``save_afile()`` | Save the graph to file in alist format. ``strong_product()`` | Return the strong product of ``self`` and ``other``. @@ -80,6 +81,7 @@ ``subgraph_search_iterator()`` | Return an iterator over the labelled copies of (matching covered) ``G`` in ``self``. ``tensor_product()`` | Return the tensor product of ``self`` and ``other``. ``to_undirected()`` | Return an undirected Graph instance of the matching covered graph. + ``topological_minor()`` | Return a topological `H`-minor from ``self`` if one exists. ``transitive_closure()`` | Return the transitive closure of the matching covered graph. ``transitive_reduction()`` | Return a transitive reduction of the matching covered graph. ``union()`` | Return the union of ``self`` and ``other``. @@ -92,12 +94,10 @@ :delim: | ``bricks_and_braces()`` | Return the list of (underlying simple graph of) the bricks and braces of the (matching covered) graph. - ``is_brace()`` | Check if the (matching covered) graph is a brace. - ``is_brick()`` | Check if the (matching covered) graph is a brick. ``number_of_braces()`` | Return the number of braces. ``number_of_bricks()`` | Return the number of bricks. ``number_of_petersen_bricks()`` | Return the number of Petersen bricks. - ``tight_cut_decomposition()`` | Return a tight cut decomposition. + ``tight_cut_decomposition()`` | Return a maximal set of laminar nontrivial tight cuts and a corresponding vertex set partition. **Removability and ear decomposition** @@ -1616,13 +1616,13 @@ def allow_loops(self, new, check=True): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ if new: raise ValueError('loops are not allowed in ' @@ -1657,13 +1657,13 @@ def allows_loops(self): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ return False @@ -2057,8 +2057,7 @@ def maximal_barrier(self, vertex): sage: J = G.copy() sage: J.delete_vertices(B) - sage: all(len(K)%2 != 0 for K in J.connected_components()) - ... + sage: all(len(K)%2 != 0 for K in J.connected_components(sort=True)) True Let `B` be a maximal barrier in a matching covered graph `G` and let @@ -2067,7 +2066,7 @@ def maximal_barrier(self, vertex): `v` is the end of that edge in `V(K)`, then `M \cap E(K)` is a perfect matching of `K - v`:: - sage: K = J.subgraph(vertices=(J.connected_components())[0]) + sage: K = J.subgraph(vertices=(J.connected_components(sort=True))[0]) sage: # Let F := \partial_G(K) and T := M \cap F sage: F = [edge for edge in G.edge_iterator() ....: if (edge[0] in K and edge[1] not in K) @@ -2091,7 +2090,7 @@ def maximal_barrier(self, vertex): `G - B` is factor critical:: sage: all((K.subgraph(vertices=connected_component)).is_factor_critical() - ....: for connected_component in K.connected_components() + ....: for connected_component in K.connected_components(sort=True) ....: ) True @@ -2251,13 +2250,13 @@ def has_loops(self): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ return False @@ -2346,6 +2345,820 @@ def has_perfect_matching(G, algorithm='Edmonds', solver=None, verbose=0, raise ValueError('algorithm must be set to \'Edmonds\', ' '\'LP_matching\' or \'LP\'') + @doc_index('Bricks, braces and tight cut decomposition') + def is_brace(self, coNP_certificate=False): + r""" + Check if the (matching covered) graph is a brace. + + A matching covered graph which is free of nontrivial tight cuts is + called a *brace* if it is bipartite. Let `G := (A \cup B, E)` be a + bipartite matching covered graph on six or more vertices. The + following statements are equivalent [LM2024]_: + + 1. `G` is a brace (aka free of nontrivial tight cuts). + 2. `G - a_1 - a_2 - b_1 - b_2` has a perfect matching for any two + distinct vertices `a_1` and `a_2` in `A` and any two distinct + vertices `b_1` and `b_2` in `B`. + 3. `G` is two extendable (any two nonadjacent distinct edges can be + extended to some perfect matching of `G`). + 4. `|N(X)| \geq |X| + 2`, for all `X ⊂ A` such that `0 < |X| < + |A| - 1`, where `N(S) := \{b \mid (a, b) \in E ∧ a \in S\}` is called + the neighboring set of `S`. + 5. `G - a - b` is matching covered, for some perfect matching `M` of + `G` and for each edge `ab` in `M`. + + We shall be using the 5th characterization mentioned above in order + to determine whether the provided bipartite matching covered graph + is a brace or not using *M*-alternating tree search [LZ2001]_. + + INPUT: + + - ``coNP_certificate`` -- boolean (default: ``False``) + + OUTPUT: + + - If the input matching covered graph is not bipartite, a + :exc:`ValueError` is returned. + + - If the input bipartite matching covered graph is a brace, a boolean + ``True`` is returned if ``coNP_certificate`` is set to ``False`` + otherwise a 5-tuple ``(True, None, None, None, None)`` is returned. + + - If the input bipartite matching covered graph is not a brace, a + boolean ``False`` is returned if ``coNP_certificate`` is set to + ``False`` otherwise a 5-tuple of + + 1. a boolean ``False``, + + 2. a list of edges constituting a nontrivial tight cut (which is a + nontrivial barrier cut) + + 3. a set of vertices of one of the shores of the nontrivial tight cut + + 4. a string 'nontrivial tight cut' + + 5. a set of vertices showing the respective barrier + + is returned. + + EXAMPLES: + + The complete graph on two vertices `K_2` is the smallest brace:: + + sage: K = graphs.CompleteGraph(2) + sage: G = MatchingCoveredGraph(K) + sage: G.is_brace() + True + + The cycle graph on four vertices `C_4` is a brace:: + + sage: C = graphs.CycleGraph(4) + sage: G = MatchingCoveredGraph(C) + sage: G.is_brace() + True + + Each graph that is isomorphic to a biwheel is a brace:: + + sage: B = graphs.BiwheelGraph(15) + sage: G = MatchingCoveredGraph(B) + sage: G.is_brace() + True + + A circular ladder graph of order eight or more on `2n` vertices for + an even `n` is a brace:: + + sage: n = 10 + sage: CL = graphs.CircularLadderGraph(n) + sage: G = MatchingCoveredGraph(CL) + sage: G.is_brace() + True + + A moebius ladder graph of order six or more on `2n` vertices for an odd + `n` is a brace:: + + sage: n = 11 + sage: ML = graphs.MoebiusLadderGraph(n) + sage: G = MatchingCoveredGraph(ML) + sage: G.is_brace() + True + + Note that the union of the above mentioned four families of braces, + that are: + + 1. the biwheel graph ``BiwheelGraph(n)``, + 2. the circular ladder graph ``CircularLadderGraph(n)`` for even ``n``, + 3. the moebius ladder graph ``MoebiusLadderGraph(n)`` for odd ``n``, + + is referred to as the *McCuaig* *family* *of* *braces.* + + The only simple brace of order six is the complete graph of the same + order, that is `K_{3, 3}`:: + + sage: L = list(graphs(6, + ....: lambda G: G.size() <= 15 and + ....: G.is_bipartite()) + ....: ) + sage: L = list(G for G in L if G.is_connected() and + ....: G.is_matching_covered() + ....: ) + sage: M = list(MatchingCoveredGraph(G) for G in L) + sage: B = list(G for G in M if G.is_brace()) + sage: K = graphs.CompleteBipartiteGraph(3, 3) + sage: G = MatchingCoveredGraph(K) + sage: next(iter(B)).is_isomorphic(G) + True + + The nonplanar `K_{3, 3}`-free brace Heawood graph is the unique cubic + graph of girth six with the fewest number of vertices (that is 14). + Note that by `K_{3, 3}`-free, it shows that the Heawood graph does not + contain a subgraph that is isomophic to a graph obtained by + bisubdivision of `K_{3, 3}`:: + + sage: K = graphs.CompleteBipartiteGraph(3, 3) + sage: J = graphs.HeawoodGraph() + sage: H = MatchingCoveredGraph(J) + sage: H.is_brace() and not H.is_planar() and \ + ....: H.is_regular(k=3) and H.girth() == 6 + True + + Braces of order six or more are 3-connected:: + + sage: H = graphs.HexahedralGraph() + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace() and G.is_triconnected() + True + + Braces of order four or more are 2-extendable:: + + sage: H = graphs.EllinghamHorton54Graph() + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace() + True + sage: e = next(G.edge_iterator(labels=False)); f = None + sage: for f in G.edge_iterator(labels=False): + ....: if not (set(e) & set(f)): + ....: break + sage: S = [u for x in [e, f] for u in set(x)] + sage: J = H.copy(); J.delete_vertices(S) + sage: M = Graph(J.matching()) + sage: M.add_edges([e, f]) + sage: if all(d == 1 for d in M.degree()) and \ + ....: G.order() == M.order() and \ + ....: G.order() == 2*M.size(): + ....: print(f'graph {G} is 2-extendable') + graph Ellingham-Horton 54-graph is 2-extendable + + Every edge in a brace of order at least six is removable:: + + sage: H = graphs.CircularLadderGraph(8) + sage: G = MatchingCoveredGraph(H) + sage: # len(G.removble_edges()) == G.size() + # True + + Every brace of order eight has the hexahedral graph as a spanning + subgraph:: + + sage: H = graphs.HexahedralGraph() + sage: L = list(graphs(8, + ....: lambda G: G.size() <= 28 and + ....: G.is_bipartite()) + ....: ) + sage: L = list(G for G in L if G.is_connected() and + ....: G.is_matching_covered() + ....: ) + sage: M = list(MatchingCoveredGraph(G) for G in L) + sage: B = list(G for G in M if G.is_brace()) + sage: C = list(G for G in M if Graph(G).subgraph_search(H) is not None) + sage: B == C + True + + For every brace `G[A, B]` of order at least six, the graph + `G - a_1 - a_2 - b_1 - b_2` has a perfect matching for any two distinct + vertices `a_1` and `a_2` in `A` and any two distinct vertices `b_1` and + `b_2` in `B`:: + + sage: H = graphs.CompleteBipartiteGraph(10, 10) + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace() + True + sage: S = [0, 1, 10, 12] + sage: G.delete_vertices(S) + sage: G.has_perfect_matching() + True + + For a brace `G[A, B]` of order six or more, `|N(X)| \geq |X| + 2`, for + all `X \subset A` such that `0 < |X| <|A| - 1`, where + `N(S) := \{b | (a, b) \in E \^ a \in S\}` is called the neighboring set + of `S`:: + + sage: H = graphs.MoebiusLadderGraph(15) + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace() + True + sage: A, _ = G.bipartite_sets() + sage: # needs random + sage: X = random.sample(list(A), random.randint(1, len(A) - 1)) + sage: N = {v for u in X for v in G.neighbor_iterator(u)} + sage: len(N) >= len(X) + 2 + True + + For a brace `G` of order four or more with a perfect matching `M`, the + graph `G - a - b` is matching covered for each edge `(a, b)` in `M`:: + + sage: H = graphs.HeawoodGraph() + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace() + True + sage: M = G.get_matching() + sage: L = [] + sage: for a, b, *_ in M: + ....: J = G.copy(); J.delete_vertices([a, b]) + ....: if J.is_matching_covered(): + ....: L.append(J) + sage: len(L) == len(M) + True + + A cycle graph of order six or more is a bipartite matching covered + graph, but is not a brace:: + + sage: C = graphs.CycleGraph(10) + sage: G = MatchingCoveredGraph(C) + sage: G.is_brace() + False + + A ladder graph of order six or more is a bipartite matching covered + graph, that is not a brace. The tight cut decomposition of a ladder + graph produces a list graphs the underlying graph of each of which + is isomorphic to a 4-cycle:: + + sage: L = graphs.LadderGraph(10) + sage: G = MatchingCoveredGraph(L) + sage: G.is_brace() + False + + One may set the ``coNP_certificate`` to be ``True``:: + + sage: H = graphs.HexahedralGraph() + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace(coNP_certificate=True) + (True, None, None, None, None) + sage: C = graphs.CycleGraph(6) + sage: D = MatchingCoveredGraph(C) + sage: is_brace, nontrivial_tight_cut, nontrivial_odd_component, \ + ....: nontrivial_tight_cut_variant, cut_identifier = \ + ....: D.is_brace(coNP_certificate=True) + sage: is_brace is False + True + sage: J = C.subgraph(vertices=nontrivial_odd_component) + sage: J.is_isomorphic(graphs.PathGraph(3)) + True + sage: len(nontrivial_tight_cut) == 2 + True + sage: nontrivial_tight_cut_variant + 'nontrivial barrier cut' + sage: # Corresponding barrier + sage: cut_identifier == {a for u, v, *_ in nontrivial_tight_cut for a in [u, v] \ + ....: if a not in nontrivial_odd_component} + True + sage: for u, v, *_ in nontrivial_tight_cut: + ....: assert (u in nontrivial_odd_component and v not in nontrivial_odd_component) + sage: L = graphs.LadderGraph(3) # A ladder graph with two constituent braces + sage: G = MatchingCoveredGraph(L) + sage: is_brace, nontrivial_tight_cut, nontrivial_odd_component, cut_variant, cut_identifier = \ + ....: G.is_brace(coNP_certificate=True) + sage: is_brace is False + True + sage: G1 = L.copy() + sage: G1.merge_vertices(list(nontrivial_odd_component)) + sage: G1.to_simple().is_isomorphic(graphs.CycleGraph(4)) + True + sage: G2 = L.copy() + sage: G2.merge_vertices([v for v in G if v not in nontrivial_odd_component]) + sage: G2.to_simple().is_isomorphic(graphs.CycleGraph(4)) + True + sage: cut_variant + 'nontrivial barrier cut' + sage: cut_identifier == {a for u, v, *_ in nontrivial_tight_cut for a in [u, v] \ + ....: if a not in nontrivial_odd_component} + True + sage: H = graphs.CompleteBipartiteGraph(3, 3) + sage: H.delete_edge(0, 3) + sage: G = MatchingCoveredGraph(H) + sage: G.is_brace(coNP_certificate=True) + (False, + [(4, 1, None), (5, 1, None), (4, 2, None), (5, 2, None)], + {0, 4, 5}, + 'nontrivial barrier cut', + {1, 2}) + + If the input matching covered graph is nonbipartite, a + :exc:`ValueError` is thrown:: + + sage: K4 = graphs.CompleteGraph(4) + sage: G = MatchingCoveredGraph(K4) + sage: G.is_brace() + Traceback (most recent call last): + ... + ValueError: the input graph is not bipartite + sage: P = graphs.PetersenGraph() + sage: H = MatchingCoveredGraph(P) + sage: H.is_brace(coNP_certificate=True) + Traceback (most recent call last): + ... + ValueError: the input graph is not bipartite + + .. SEEALSO:: + + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.is_brick` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.bricks_and_braces` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_braces` + """ + if not self.is_bipartite(): + raise ValueError('the input graph is not bipartite') + + if self.order() < 6: + return (True, None, None, None, None) if coNP_certificate else True + + A, B = self.bipartite_sets() + matching = set(self.get_matching()) + matching_neighbor = {x: y for u, v, *_ in matching for x, y in [(u, v), (v, u)]} + + for e in matching: + u, v, *_ = e + + # Let G denote the undirected graph self, and + # let the graph H(e) := G — u — v + H = Graph(self, multiedges=False) + H.delete_vertices([u, v]) + + if not H.is_connected() or not H.is_matching_covered(list(matching - set([e]))): + if not coNP_certificate: + return False + + # Construct the digraph D(e)(A ∪ B, F) defined as follows: + from sage.graphs.digraph import DiGraph + D = DiGraph() + + # For each edge (a, b) in E(H(e)) ∩ M with a in A, b —> a in D(e). + # For each edge (a, b) in E(H(e)) with a in A, a —> b in D(e). + for a, b in H.edge_iterator(labels=False, sort_vertices=True): + + if a in B: + a, b = b, a + + D.add_edge((a, b)) + if matching_neighbor[a] == b: + D.add_edge((b, a)) + + # H(e) is matching covered iff D(e) is strongly connected. + # Check if D(e) is strongly connected using Kosaraju's algorithm + def dfs(x, visited, neighbor_iterator): + stack = [x] # a stack of xertices + + while stack: + x = stack.pop() + visited.add(x) + + for y in neighbor_iterator(x): + if y not in visited: + stack.append(y) + + root = next(D.vertex_iterator()) + + visited_in = set() + dfs(root, visited_in, D.neighbor_in_iterator) + + # Since D(e) is not strongly connected, it has a directed cut T(e). + # Note that by definition of D(e), it follows that T(e) ⊆ E(H(e)) — M. + # Thus, T(e) is a cut of H(e), which has a shore X such that every edge of T(e) is + # incident with a vertex in X ∩ B. + + # Moreover, M — e is a perfect matching of H(e), and thus, |X ∩ A| = |X ∩ B| + # Consequently, Y := X + v is a shore of a nontrivial tight cut T of G + + if len(visited_in) != D.order(): + X = visited_in + else: + X = set() + dfs(root, X, D.neighbor_out_iterator) + + color_class = None + + for a, b in H.edge_iterator(labels=False, sort_vertices=True): + if (a in X) ^ (b in X): + x = a if a in A else b + color_class = x not in X + break + + # Obtain the color class Z ∈ {A, B} such that X ∩ Z is a vertex cover for T(e) + # Thus, obtain Y := X + v + X.add(u if (not color_class and u in A) or (color_class and u in B) or (color_class is None) else v) + + # Compute the nontrivial tight cut C := ∂(Y) + C = [(x, y, w) if x in X else (y, x, w) + for x, y, w in self.edge_iterator(sort_vertices=True) + if (x in X) ^ (y in X)] + + # Obtain the barrier Z + Z = None + + if (u in X and u in A) or (v in X and v in A): + Z = {b for b in B if b not in X} + else: + Z = {a for a in A if a not in X} + + return (False, C, set(X), 'nontrivial barrier cut', Z) + + return (True, None, None, None, None) if coNP_certificate else True + + @doc_index('Bricks, braces and tight cut decomposition') + def is_brick(self, coNP_certificate=False): + r""" + Check if the (matching covered) graph is a brick. + + A matching covered graph which is free of nontrivial tight cuts is + called a *brick* if it is nonbipartite. A nonbipartite matching covered + graph is a brick if and only if it is 3-connected and bicritical + [LM2024]_. + + INPUT: + + - ``coNP_certificate`` -- boolean (default: ``False``) + + OUTPUT: + + - If the input matching covered graph is bipartite, a :exc:`ValueError` + is returned. + + - If the input nonbipartite matching covered graph is a brick, a + boolean ``True`` is returned if ``coNP_certificate`` is set to + ``False``, otherwise a 5-tuple ``(True, None, None, None, None)`` is + returned. + + - If the input nonbipartite matching covered graph is not a brick, a + boolean ``False`` is returned if ``coNP_certificate`` is set to + ``False``. + + - If ``coNP_certificate`` is set to ``True`` and the input nonbipartite + graph is not a brick, a 5-tuple of + + 1. a boolean ``False``, + + 2. a list of lists of edges, each list constituting a nontrivial + tight cut collectively representing a laminar tight cut, + + 3. a list of set of vertices of one of the shores of those respective + nontrivial tight cuts: + + #. In case of nontrivial barrier cuts, each of the shores is a + nontrivial odd component with respect to a nontrivial barrier, + thus the returned list forms mutually exclusive collection of + (odd) sets. + + #. Otherwise each of the nontrivial tight cuts is a 2-separation + cut, each of the shores form a subset sequence, with the + `i` th shore being a proper subset of the `i + 1` th shore. + + 4. a string showing whether the nontrivial tight cuts are barrier + cuts (if the string is 'nontrivial barrier cut'), or 2-separation + cuts (if the string is 'nontrivial 2-separation cut') + + 5. a set of vertices showing the respective barrier if the + nontrivial tight cuts are barrier cuts, or otherwise + a set of two vertices constituting the corresponding + two vertex cut (in this case the nontrivial tight cuts are + 2-separation cuts) + + is returned. + + EXAMPLES: + + The complete graph on four vertices `K_4` is the smallest brick:: + + sage: K = graphs.CompleteGraph(4) + sage: G = MatchingCoveredGraph(K) + sage: G.is_brick() + True + + The triangular cicular ladder (a graph on six vertices), aka + `\overline{C_6}` is a brick:: + + sage: C6Bar = graphs.CircularLadderGraph(3) + sage: G = MatchingCoveredGraph(C6Bar) + sage: G.is_brick() + True + + Each of Petersen graph, Bicorn graph, Tricorn graph, Cubeplex graph, + Twinplex graph, Wagner graph is a brick:: + + sage: MatchingCoveredGraph(graphs.PetersenGraph()).is_brick() and \ + ....: MatchingCoveredGraph(graphs.StaircaseGraph(4)).is_brick() and \ + ....: MatchingCoveredGraph(graphs.TricornGraph()).is_brick() and \ + ....: MatchingCoveredGraph(graphs.CubeplexGraph()).is_brick() and \ + ....: MatchingCoveredGraph(graphs.TwinplexGraph()).is_brick() and \ + ....: MatchingCoveredGraph(graphs.WagnerGraph()).is_brick() + True + + The Murty graph is the smallest simple brick that is not odd-intercyclic:: + + sage: M = graphs.MurtyGraph() + sage: G = MatchingCoveredGraph(M) + sage: G.is_brick() + True + + A circular ladder graph of order six or more on `2n` vertices for an + odd `n` is a brick:: + + sage: n = 11 + sage: CL = graphs.CircularLadderGraph(n) + sage: G = MatchingCoveredGraph(CL) + sage: G.is_brick() + True + + A moebius ladder graph of order eight or more on `2n` vertices for an + even `n` is a brick:: + + sage: n = 10 + sage: ML = graphs.MoebiusLadderGraph(n) + sage: G = MatchingCoveredGraph(ML) + sage: G.is_brick() + True + + A wheel graph of an even order is a brick:: + + sage: W = graphs.WheelGraph(10) + sage: G = MatchingCoveredGraph(W) + sage: G.is_brick() + True + + A graph that is isomorphic to a truncated biwheel graph is a brick:: + + sage: TB = graphs.TruncatedBiwheelGraph(15) + sage: G = MatchingCoveredGraph(TB) + sage: G.is_brick() + True + + Each of the graphs in the staircase graph family with order eight or + more is a brick:: + + sage: ST = graphs.StaircaseGraph(9) + sage: G = MatchingCoveredGraph(ST) + sage: G.is_brick() + True + + Bricks are 3-connected:: + + sage: P = graphs.PetersenGraph() + sage: G = MatchingCoveredGraph(P) + sage: G.is_brick() + True + sage: G.is_triconnected() + True + + Bricks are bicritical:: + + sage: P = graphs.PetersenGraph() + sage: G = MatchingCoveredGraph(P) + sage: G.is_brick() + True + sage: G.is_bicritical() + True + + Examples of nonbipartite matching covered graphs that are not + bricks:: + + sage: H = Graph([ + ....: (0, 3), (0, 4), (0, 7), + ....: (1, 3), (1, 5), (1, 7), + ....: (2, 3), (2, 6), (2, 7), + ....: (4, 5), (4, 6), (5, 6) + ....: ]) + sage: G = MatchingCoveredGraph(H) + sage: G.is_bipartite() + False + sage: G.is_bicritical() + False + sage: G.is_triconnected() + True + sage: G.is_brick() + False + sage: H = Graph([ + ....: (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), + ....: (1, 5), (2, 5), (3, 4), (3, 5), (4, 5) + ....: ]) + sage: G = MatchingCoveredGraph(H) + sage: G.is_bipartite() + False + sage: G.is_bicritical() + True + sage: G.is_triconnected() + False + sage: G.is_brick() + False + + One may set the ``coNP_certificate`` to be ``True``:: + + sage: K4 = graphs.CompleteGraph(4) + sage: G = MatchingCoveredGraph(K4) + sage: G.is_brick(coNP_certificate=True) + (True, None, None, None, None) + sage: # K(4) ⊙ K(3, 3) is nonbipartite but not a brick + sage: H = graphs.MurtyGraph(); H.delete_edge(0, 1) + sage: G = MatchingCoveredGraph(H) + sage: G.is_brick(coNP_certificate=True) + (False, [[(5, 2, None), (6, 3, None), (7, 4, None)]], [{5, 6, 7}], + 'nontrivial barrier cut', {2, 3, 4}) + sage: H = Graph([ + ....: (0, 12), (0, 13), (0, 15), (1, 4), (1, 13), (1, 14), + ....: (1, 19), (2, 4), (2, 13), (2, 14), (2, 17), (3, 9), + ....: (3, 13), (3, 16), (3, 21), (4, 6), (4, 7), (5, 7), + ....: (5, 8), (5, 12), (6, 8), (6, 11), (7, 10), (8, 9), + ....: (9, 10), (10, 11), (11, 12), (14, 15), (14, 16), (15, 16), + ....: (17, 18), (17, 21), (18, 19), (18, 20), (19, 20), (20, 21) + ....: ]) + sage: G = MatchingCoveredGraph(H) + sage: G.is_brick(coNP_certificate=True) + (False, + [[(12, 0, None), (4, 1, None), (4, 2, None), (9, 3, None)], + [(19, 1, None), (17, 2, None), (21, 3, None)], + [(15, 0, None), (14, 1, None), (14, 2, None), (16, 3, None)]], + [{4, 5, 6, 7, 8, 9, 10, 11, 12}, {17, 18, 19, 20, 21}, {14, 15, 16}], + 'nontrivial barrier cut', {0, 1, 2, 3}) + sage: J = Graph([ + ....: (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), + ....: (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), + ....: (1, 2), (1, 11), (2, 11), (3, 4), (3, 11), + ....: (4, 11), (5, 6), (5, 11), (6, 11), (7, 8), + ....: (7, 11), (8, 11), (9, 10), (9, 11), (10, 11) + ....: ]) + sage: G = MatchingCoveredGraph(J) + sage: G.is_brick(coNP_certificate=True) + (False, + [[(0, 3, None), + (0, 4, None), + (0, 5, None), + (0, 6, None), + (0, 7, None), + (0, 8, None), + (0, 9, None), + (0, 10, None), + (1, 11, None), + (2, 11, None)], + [(0, 5, None), + (0, 6, None), + (0, 7, None), + (0, 8, None), + (0, 9, None), + (0, 10, None), + (1, 11, None), + (2, 11, None), + (3, 11, None), + (4, 11, None)], + [(0, 7, None), + (0, 8, None), + (0, 9, None), + (0, 10, None), + (1, 11, None), + (2, 11, None), + (3, 11, None), + (4, 11, None), + (5, 11, None), + (6, 11, None)], + [(0, 9, None), + (0, 10, None), + (1, 11, None), + (2, 11, None), + (3, 11, None), + (4, 11, None), + (5, 11, None), + (6, 11, None), + (7, 11, None), + (8, 11, None)]], + [{0, 1, 2}, + {0, 1, 2, 3, 4}, + {0, 1, 2, 3, 4, 5, 6}, + {0, 1, 2, 3, 4, 5, 6, 7, 8}], + 'nontrivial 2-separation cut', + {0, 11}) + + If the input matching covered graph is bipartite, a + :exc:`ValueError` is thrown:: + + sage: H = graphs.HexahedralGraph() + sage: G = MatchingCoveredGraph(H) + sage: G.is_brick() + Traceback (most recent call last): + ... + ValueError: the input graph is bipartite + sage: J = graphs.HeawoodGraph() + sage: G = MatchingCoveredGraph(J) + sage: G.is_brick(coNP_certificate=True) + Traceback (most recent call last): + ... + ValueError: the input graph is bipartite + + .. SEEALSO:: + + - :meth:`~sage.graphs.graph.Graph.is_bicritical` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.is_brace` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.bricks_and_braces` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_bricks` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_petersen_bricks` + """ + if self.is_bipartite(): + raise ValueError('the input graph is bipartite') + + # Check if G is bicritical + bicritical, certificate = self.is_bicritical(coNP_certificate=True) + + if not bicritical: + if not coNP_certificate: + return False + + # G has a pair of vertices u, v such that G - u - v is not matching + # covered, thus has a nontrivial barrier B containing both u and v. + u, _ = certificate + B = self.maximal_barrier(u) + + H = Graph(self) + H.delete_vertices(B) + + # Let K be a nontrivial odd component of H := G - B. Note that + # there exists at least one such K since G is nonbipartite + nontrivial_odd_components = [ + set(component) for component in H.connected_components(sort=True) + if len(component) % 2 and len(component) > 1 + ] + + # Find a laminar set of nontrivial barrier cuts + C = [[(u, v, w) if u in nontrivial_odd_component else (v, u, w) + for u, v, w in self.edge_iterator() + if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)] + for nontrivial_odd_component in nontrivial_odd_components] + + return (False, C, nontrivial_odd_components, 'nontrivial barrier cut', B) + + # Check if G is 3-connected + if self.is_triconnected(): + return (True, None, None, None, None) if coNP_certificate else True + + # G has a 2-vertex cut + # Compute the SPQR-tree decomposition + spqr_tree = self.spqr_tree() + two_vertex_cut = [] + + # Check for 2-vertex cuts in a P node + # Since the graph is matching covered, it is free of cut vertices + # It can be shown using counting arguments that the spqr tree + # decomposition for a bicritical graph, that is 2-connected but not + # 3-connected, is free of 'S' nodes + for u in spqr_tree: + if u[0] == 'P': + two_vertex_cut.extend(u[1]) + break + + # If no 2-vertex cut found, look for R nodes + if not two_vertex_cut: + from collections import Counter + R_frequency = Counter() + + for t, g in spqr_tree: + if t == 'R': + R_frequency.update(g) + + # R frequency must be at least 2, + # since the graph is 2-connected but not 3-connected + two_vertex_cut = [u for u, f in R_frequency.items() if f >= 2][:2] + + # We obtain a 2-vertex cut (u, v) + H = Graph(self) + H.delete_vertices(two_vertex_cut) + + # Check if all components of H are odd + components = H.connected_components(sort=True) + + # Find a nontrivial odd component + nontrivial_tight_cut_variation = 'nontrivial 2-separation cut' + nontrivial_odd_components = [] + + for index, component in enumerate(components): + if index == len(components) - 1: + continue + elif not index: + nontrivial_odd_components.append(set(components[0] + [two_vertex_cut[0]])) + else: + nontrivial_odd_component = nontrivial_odd_components[-1].copy() + nontrivial_odd_component.update(component) + nontrivial_odd_components.append(nontrivial_odd_component) + + C = [[(u, v, w) if u in nontrivial_odd_component else (v, u, w) + for u, v, w in self.edge_iterator() + if (u in nontrivial_odd_component) ^ (v in nontrivial_odd_component)] + for nontrivial_odd_component in nontrivial_odd_components] + + # Edge (u, v, w) in C are formatted so that u is in a nontrivial odd component + return (False, C, nontrivial_odd_components, nontrivial_tight_cut_variation, set(two_vertex_cut)) if coNP_certificate else False + @doc_index('Overwritten methods') def loop_edges(self, labels=True): r""" @@ -2416,13 +3229,13 @@ def loop_edges(self, labels=True): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ return [] @@ -2483,13 +3296,13 @@ def loop_vertices(self): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ return [] @@ -2555,13 +3368,13 @@ def number_of_loops(self): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.remove_loops` """ return 0 @@ -2647,13 +3460,13 @@ def remove_loops(self, vertices=None): .. SEEALSO:: - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops`, - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allow_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.allows_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.has_loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_edges` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loop_vertices` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.loops` + - :meth:`~sage.graphs.matching_covered_graph.MatchingCoveredGraph.number_of_loops` """ from collections.abc import Iterable diff --git a/src/sage/groups/artin.py b/src/sage/groups/artin.py index 2339394224e..6697a55bbb8 100644 --- a/src/sage/groups/artin.py +++ b/src/sage/groups/artin.py @@ -9,10 +9,17 @@ AUTHORS: - Travis Scrimshaw (2018-02-05): Initial version +- Travis Scrimshaw (2025-01-30): Allowed general construction; implemented + general Burau representation + +.. TODO:: + + Implement affine type Artin groups with their well-known embeddings into + the classical braid group. """ # **************************************************************************** -# Copyright (C) 2018 Travis Scrimshaw +# Copyright (C) 2018-2025 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -26,6 +33,7 @@ from sage.groups.free_group import FreeGroup from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.rings.infinity import Infinity from sage.structure.richcmp import richcmp, rich_to_bool from sage.structure.unique_representation import UniqueRepresentation @@ -156,6 +164,155 @@ def coxeter_group_element(self, W=None): In = W.index_set() return W.prod(s[In[abs(i)-1]] for i in self.Tietze()) + def burau_matrix(self, var='t'): + r""" + Return the Burau matrix of the Artin group element. + + Following [BQ2024]_, the (generalized) Burau representation + of an Artin group is defined by deforming the reflection + representation of the corresponding Coxeter group. However, + we substitute `q \mapsto -t` from [BQ2024]_ to match one of + the unitary (reduced) Burau representations of the braid group + (see :meth:`sage.groups.braid.Braid.burau_matrix()` for details.) + + More precisely, let `(m_{ij})_{i,j \in I}` be the + :meth:`Coxeter matrix`. Then the action is + given on the basis `(\alpha_1, \ldots \alpha_n)` (corresponding + to the reflection representation of the corresponding + :meth:`Coxeter group`) by + + .. MATH:: + + \sigma_i(\alpha_j) = \alpha_j + - \langle \alpha_i, \alpha_j \rangle_q \alpha_i, + \qquad \text{ where } + \langle \alpha_i, \alpha_j \rangle_q := \begin{cases} + 1 + t^2 & \text{if } i = j, \\ + -2 t \cos(\pi/m_{ij}) & \text{if } i \neq j. + \end{cases}. + + By convention `\cos(\pi/\infty) = 1`. Note that the inverse of the + generators act by `\sigma_i^{-1}(\alpha_j) = \alpha_j - q^{-2} + \langle \alpha_j, \alpha_i \rangle_q \alpha_i`. + + INPUT: + + - ``var`` -- string (default: ``'t'``); the name of the + variable in the entries of the matrix + + OUTPUT: + + The Burau matrix of the Artin group element over the Laurent + polynomial ring in the variable ``var``. + + EXAMPLES:: + + sage: A. = ArtinGroup(['B',3]) + sage: B1 = s1.burau_matrix() + sage: B2 = s2.burau_matrix() + sage: B3 = s3.burau_matrix() + sage: [B1, B2, B3] + [ + [-t^2 t 0] [ 1 0 0] [ 1 0 0] + [ 0 1 0] [ t -t^2 a*t] [ 0 1 0] + [ 0 0 1], [ 0 0 1], [ 0 a*t -t^2] + ] + sage: B1 * B2 * B1 == B2 * B1 * B2 + True + sage: B2 * B3 * B2 * B3 == B3 * B2 * B3 * B2 + True + sage: B1 * B3 == B3 * B1 + True + sage: (~s1).burau_matrix() * B1 == 1 + True + + We verify the example in Theorem 4.1 of [BQ2024]_:: + + sage: A. = ArtinGroup(['A', 3, 1]) + sage: [g.burau_matrix() for g in A.gens()] + [ + [-t^2 t 0 t] [ 1 0 0 0] [ 1 0 0 0] + [ 0 1 0 0] [ t -t^2 t 0] [ 0 1 0 0] + [ 0 0 1 0] [ 0 0 1 0] [ 0 t -t^2 t] + [ 0 0 0 1], [ 0 0 0 1], [ 0 0 0 1], + + [ 1 0 0 0] + [ 0 1 0 0] + [ 0 0 1 0] + [ t 0 t -t^2] + ] + sage: a = s3^2 * s4 * s3 * s2 *s1 * ~s3 * s4 * s3 * s2 * s1^-2 * s4 + sage: b = s1^2 * ~s2 * s4 * s1 * ~s3 * s2 * ~s4 * s3 * s1 * s4 * s1 * ~s2 * s4^-2 * s3 + sage: alpha = a * s3 * ~a + sage: beta = b * s2 * ~b + sage: elm = alpha * beta * ~alpha * ~beta + sage: print(elm.Tietze()) + (3, 3, 4, 3, 2, 1, -3, 4, 3, 2, -1, -1, 4, 3, -4, 1, 1, -2, -3, + -4, 3, -1, -2, -3, -4, -3, -3, 1, 1, -2, 4, 1, -3, 2, -4, 3, 1, + 4, 1, -2, -4, -4, 3, 2, -3, 4, 4, 2, -1, -4, -1, -3, 4, -2, 3, + -1, -4, 2, -1, -1, 3, 3, 4, 3, 2, 1, -3, 4, 3, 2, -1, -1, 4, + -3, -4, 1, 1, -2, -3, -4, 3, -1, -2, -3, -4, -3, -3, 1, 1, -2, + 4, 1, -3, 2, -4, 3, 1, 4, 1, -2, -4, -4, 3, -2, -3, 4, 4, 2, + -1, -4, -1, -3, 4, -2, 3, -1, -4, 2, -1, -1) + sage: elm.burau_matrix() + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + + Next, we show ``elm`` is not the identity by using the embedding of + the affine braid group `\widetilde{B}_n \to B_{n+1}`:: + + sage: B. = BraidGroup(5) + sage: D = t1 * t2 * t3 * t4^2 + sage: t0 = D * t3 * ~D + sage: t0*t1*t0 == t1*t0*t1 + True + sage: t0*t2 == t2*t0 + True + sage: t0*t3*t0 == t3*t0*t3 + True + sage: T = [t0, t1, t2, t3] + sage: emb = B.prod(T[i-1] if i > 0 else ~T[-i-1] for i in elm.Tietze()) + sage: emb.is_one() + False + + Since the Burau representation does not respect the group embedding, + the corresponding `B_5` element's Burau matrix is not the identity + (Bigelow gave an example of the representation not being faithful for + `B_5`, but it is still open for `B_4`):: + + sage: emb.burau_matrix() != 1 + True + + We also verify the result using the elements in [BQ2024]_ Remark 4.2:: + + sage: ap = s3 * s1 * s2 * s1 * ~s3 * s4 * s2 * s3 * s2 * ~s3 * s1^-2 * s4 + sage: bp = s1 * ~s4 * s1^2 * s3^-2 * ~s2 * s4 * s1 * ~s3 * s2 * ~s4 * s3 * s1 * s4 * s1 * ~s2 * s4^-2 * s3 + sage: alpha = ap * s3 * ~ap + sage: beta = bp * s2 * ~bp + sage: elm = alpha * beta * ~alpha * ~beta + sage: elm.burau_matrix() + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + + REFERENCES: + + - [BQ2024]_ + """ + gens, invs = self.parent()._burau_generators + MS = gens[0].parent() + ret = MS.prod(gens[i-1] if i > 0 else invs[-i-1] for i in self.Tietze()) + + if var == 't': + return ret + + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + poly_ring = LaurentPolynomialRing(ret.base_ring().base_ring(), var) + return ret.change_ring(poly_ring) + class FiniteTypeArtinGroupElement(ArtinGroupElement): """ @@ -453,7 +610,7 @@ def __classcall_private__(cls, coxeter_data, names=None): from sage.groups.raag import RightAngledArtinGroup return RightAngledArtinGroup(coxeter_data.coxeter_graph(), names) if not coxeter_data.is_finite(): - raise NotImplementedError + return super().__classcall__(cls, coxeter_data, names) if coxeter_data.coxeter_type().cartan_type().type() == 'A': from sage.groups.braid import BraidGroup return BraidGroup(coxeter_data.rank()+1, names) @@ -476,15 +633,17 @@ def __init__(self, coxeter_matrix, names): rels = [] # Generate the relations based on the Coxeter graph I = coxeter_matrix.index_set() + gens = free_group.gens() for ii, i in enumerate(I): - for j in I[ii + 1:]: + for jj, j in enumerate(I[ii + 1:], start=ii+1): m = coxeter_matrix[i, j] if m == Infinity: # no relation continue - elt = [i, j] * m - for ind in range(m, 2*m): - elt[ind] = -elt[ind] - rels.append(free_group(elt)) + elt = (gens[ii] * gens[jj]) ** (m // 2) + if m % 2 == 1: + elt = elt * gens[ii] * ~gens[jj] + elt = elt * (~gens[ii] * ~gens[jj]) ** (m // 2) + rels.append(elt) FinitelyPresentedGroup.__init__(self, free_group, tuple(rels)) def _repr_(self): @@ -688,6 +847,153 @@ def _standard_lift(self, w): """ return self(self._standard_lift_Tietze(w)) + @lazy_attribute + def _burau_generators(self): + """ + The Burau matrices for the generators of ``self`` and their inverses. + + EXAMPLES:: + + sage: A = ArtinGroup(['G',2]) + sage: A._burau_generators + [[ + [-t^2 a*t] [ 1 0] + [ 0 1], [ a*t -t^2] + ], + [ + [ -t^-2 a*t^-1] [ 1 0] + [ 0 1], [a*t^-1 -t^-2] + ]] + + sage: A = ArtinGroup(['H',3]) + sage: A._burau_generators + [[ + [-t^2 t 0] [ 1 0 0] + [ 0 1 0] [ t -t^2 (1/2*a + 1/2)*t] + [ 0 0 1], [ 0 0 1], + + [ 1 0 0] + [ 0 1 0] + [ 0 (1/2*a + 1/2)*t -t^2] + ], + [ + [-t^-2 t^-1 0] + [ 0 1 0] + [ 0 0 1], + + [ 1 0 0] + [ t^-1 -t^-2 (1/2*a + 1/2)*t^-1] + [ 0 0 1], + + [ 1 0 0] + [ 0 1 0] + [ 0 (1/2*a + 1/2)*t^-1 -t^-2] + ]] + + sage: CM = matrix([[1,4,7], [4,1,10], [7,10,1]]) + sage: A = ArtinGroup(CM) + sage: gens = A._burau_generators[0]; gens + [ + [ -t^2 (E(8) - E(8)^3)*t (-E(7)^3 - E(7)^4)*t] + [ 0 1 0] + [ 0 0 1], + + [ 1 0 0] + [ (E(8) - E(8)^3)*t -t^2 (E(20) - E(20)^9)*t] + [ 0 0 1], + + [ 1 0 0] + [ 0 1 0] + [(-E(7)^3 - E(7)^4)*t (E(20) - E(20)^9)*t -t^2] + ] + sage: all(gens[i] * A._burau_generators[1][i] == 1 for i in range(3)) + True + sage: B1, B2, B3 = gens + sage: (B1 * B2)^2 == (B2 * B1)^2 + True + sage: (B2 * B3)^5 == (B3 * B2)^5 + True + sage: B1 * B3 * B1 * B3 * B1 * B3 * B1 == B3 * B1 * B3 * B1 * B3 * B1 * B3 + True + """ + data = self.coxeter_type() + coxeter_matrix = self.coxeter_matrix() + n = coxeter_matrix.rank() + + # Determine the base field + if data.is_simply_laced(): + from sage.rings.integer_ring import ZZ + base_ring = ZZ + elif data.is_finite(): + from sage.rings.number_field.number_field import QuadraticField + letter = data.cartan_type().type() + if letter in ['B', 'C', 'F']: + base_ring = QuadraticField(2) + elif letter == 'G': + base_ring = QuadraticField(3) + elif letter == 'H': + base_ring = QuadraticField(5) + else: + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField + base_ring = UniversalCyclotomicField() + else: + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField + base_ring = UniversalCyclotomicField() + + # Construct the matrices + from sage.matrix.args import SparseEntry + from sage.matrix.matrix_space import MatrixSpace + from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + import sage.rings.abc + poly_ring = LaurentPolynomialRing(base_ring, 't') + q = -poly_ring.gen() + MS = MatrixSpace(poly_ring, n, sparse=True) + one = MS.one() + # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty + if isinstance(base_ring, sage.rings.abc.UniversalCyclotomicField): + E = base_ring.gen + + def val(x): + if x == -1: + return 2 * q + elif x == 1: + return 1 + q**2 + else: + return q * (E(2*x) + ~E(2*x)) + elif isinstance(base_ring, sage.rings.abc.NumberField_quadratic): + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField + E = UniversalCyclotomicField().gen + + def val(x): + if x == -1: + return 2 * q + elif x == 1: + return 1 + q**2 + else: + return q * base_ring((E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) + else: + def val(x): + if x == -1: + return 2 * q + elif x == 1: + return 1 + q**2 + elif x == 2: + return 0 + elif x == 3: + return q + else: + from sage.functions.trig import cos + from sage.symbolic.constants import pi + return q * base_ring(2 * cos(pi / x)) + index_set = data.index_set() + gens = [one - MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]])) + for j in range(n)]) + for i in range(n)] + invs = [one - q**-2 * MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]])) + for j in range(n)]) + for i in range(n)] + return [gens, invs] + Element = ArtinGroupElement diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index ce0375b121f..f302118904b 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -226,7 +226,7 @@ def burau_matrix(self, var='t', reduced=False): sage: B = BraidGroup(4) sage: B.inject_variables() Defining s0, s1, s2 - sage: b = s0*s1/s2/s1 + sage: b = s0 * s1 / s2 / s1 sage: b.burau_matrix() [ 1 - t 0 t - t^2 t^2] [ 1 0 0 0] @@ -265,6 +265,40 @@ def burau_matrix(self, var='t', reduced=False): sage: M * H * Madj == H True + The adjoined matrix (``Madj`` in the above example) matches the + output of :meth:`sage.groups.artin.ArtinGroupElement.burau_matrix`:: + + sage: from sage.groups.artin import ArtinGroupElement + sage: Madj == ArtinGroupElement.burau_matrix(b) + True + + sage: a = s0^2 * s1 * s0 * s2 *s1 * ~s0 * s1^3 * s0 * s2 * s1^-2 * s0 + sage: a.burau_matrix(reduced='unitary')[1] == ArtinGroupElement.burau_matrix(a) + True + + We verify Bigelow's example that in `B_5` the Burau representation + is not faithful:: + + sage: B. = BraidGroup(5) + sage: psi1 = ~s3 * s2 * s1^2 * s2 * s4^3 * s3 * s2 + sage: psi2 = ~s4 * s3 * s2 * s1^-2 * s2 * s1^2 * s2^2 * s1 * s4^5 + sage: alpha = ~psi1 * s4 * psi1 + sage: beta = ~psi2 * s4 * s3 * s2 * s1^2 * s2 * s3 * s4 * psi2 + sage: elm = alpha * beta * ~alpha * ~beta + sage: elm.burau_matrix() + [1 0 0 0 0] + [0 1 0 0 0] + [0 0 1 0 0] + [0 0 0 1 0] + [0 0 0 0 1] + sage: elm.burau_matrix(reduced=True) + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + sage: elm.is_one() + False + REFERENCES: - :wikipedia:`Burau_representation` diff --git a/src/sage/groups/conjugacy_classes.py b/src/sage/groups/conjugacy_classes.py index 2208389e17d..2bb655c926d 100644 --- a/src/sage/groups/conjugacy_classes.py +++ b/src/sage/groups/conjugacy_classes.py @@ -249,12 +249,12 @@ def set(self): sage: h = H(matrix(F,2,[1,2, -1, 1])) sage: C = ConjugacyClass(H,h) sage: S = [[[3, 2], [2, 4]], [[0, 1], [2, 2]], [[3, 4], [1, 4]],\ - [[0, 3], [4, 2]], [[1, 2], [4, 1]], [[2, 1], [2, 0]],\ - [[4, 1], [4, 3]], [[4, 4], [1, 3]], [[2, 4], [3, 0]],\ - [[1, 4], [2, 1]], [[3, 3], [3, 4]], [[2, 3], [4, 0]],\ - [[0, 2], [1, 2]], [[1, 3], [1, 1]], [[4, 3], [3, 3]],\ - [[4, 2], [2, 3]], [[0, 4], [3, 2]], [[1, 1], [3, 1]],\ - [[2, 2], [1, 0]], [[3, 1], [4, 4]]] + ....: [[0, 3], [4, 2]], [[1, 2], [4, 1]], [[2, 1], [2, 0]],\ + ....: [[4, 1], [4, 3]], [[4, 4], [1, 3]], [[2, 4], [3, 0]],\ + ....: [[1, 4], [2, 1]], [[3, 3], [3, 4]], [[2, 3], [4, 0]],\ + ....: [[0, 2], [1, 2]], [[1, 3], [1, 1]], [[4, 3], [3, 3]],\ + ....: [[4, 2], [2, 3]], [[0, 4], [3, 2]], [[1, 1], [3, 1]],\ + ....: [[2, 2], [1, 0]], [[3, 1], [4, 4]]] sage: C.set() == Set(H(x) for x in S) True diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index d004cdf98e2..64e9d27ea27 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -55,17 +55,17 @@ cdef class Group(Parent): sage: from sage.groups.group import Group sage: G = Group() sage: TestSuite(G).run(skip = ["_test_an_element",\ - "_test_associativity",\ - "_test_elements",\ - "_test_elements_eq_reflexive",\ - "_test_elements_eq_symmetric",\ - "_test_elements_eq_transitive",\ - "_test_elements_neq",\ - "_test_inverse",\ - "_test_one",\ - "_test_pickling",\ - "_test_prod",\ - "_test_some_elements"]) + ....: "_test_associativity",\ + ....: "_test_elements",\ + ....: "_test_elements_eq_reflexive",\ + ....: "_test_elements_eq_symmetric",\ + ....: "_test_elements_eq_transitive",\ + ....: "_test_elements_neq",\ + ....: "_test_inverse",\ + ....: "_test_one",\ + ....: "_test_pickling",\ + ....: "_test_prod",\ + ....: "_test_some_elements"]) Generic groups have very little functionality:: diff --git a/src/sage/groups/perm_gps/constructor.py b/src/sage/groups/perm_gps/constructor.py index 63c9aecd5d3..fabc7a6c9d3 100644 --- a/src/sage/groups/perm_gps/constructor.py +++ b/src/sage/groups/perm_gps/constructor.py @@ -28,7 +28,7 @@ lazy_import('sage.combinat.permutation', ['Permutation', 'from_cycles']) try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index d7dd1bdb2c4..bd57a0f32c6 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -3514,7 +3514,7 @@ class SmallPermutationGroup(PermutationGroup_generic): [ 2 0 -1 2 0 -1] sage: def numgps(n): return ZZ(libgap.NumberSmallGroups(n)) sage: all(SmallPermutationGroup(n,k).id() == [n,k] - ....: for n in [1..64] for k in [1..numgps(n)]) + ....: for n in [1..64] for k in [1..numgps(n)]) # long time (180s) True sage: H = SmallPermutationGroup(6,1) sage: H.is_abelian() diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index a58a0748477..3ce16952a74 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -925,7 +925,7 @@ def bisection_method(title, f, interval, d, maxn): maxn: IntSlider(value=10, description='max iterations', max=15) """ def _bisection_method(f, a, b, maxn, eps): - intervals = [(a,b)] + intervals = [(a, b)] round = 1 two = float(2) while True: @@ -938,12 +938,12 @@ def _bisection_method(f, a, b, maxn, eps): if abs(fc) < eps: return c, intervals if fa*fc < 0: - a, b = a, c + b = c elif fc*fb < 0: - a, b = c, b + a = c else: raise ValueError("f must have a sign change in the interval (%s,%s)" % (a,b)) - intervals.append((a,b)) + intervals.append((a, b)) round += 1 return c, intervals diff --git a/src/sage/interfaces/genus2reduction.py b/src/sage/interfaces/genus2reduction.py index 913188f9182..487287386e6 100644 --- a/src/sage/interfaces/genus2reduction.py +++ b/src/sage/interfaces/genus2reduction.py @@ -36,7 +36,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.libs.pari.all import pari +from sage.libs.pari import pari roman_numeral = ["", "I", "II", "III", "IV", "V", "VI", "VII"] diff --git a/src/sage/interfaces/gp.py b/src/sage/interfaces/gp.py index b98c050d889..eeefad9cd44 100644 --- a/src/sage/interfaces/gp.py +++ b/src/sage/interfaces/gp.py @@ -145,7 +145,7 @@ from sage.env import DOT_SAGE from sage.interfaces.tab_completion import ExtraTabCompletion -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.misc.instancedoc import instancedoc from sage.misc.lazy_import import lazy_import from sage.misc.verbose import verbose diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 3ec584931cc..563a9865272 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -10,7 +10,7 @@ You must have Magma installed on your computer for this interface to work. Magma is not free, so it is not included with Sage, but you can obtain it from - http://magma.maths.usyd.edu.au/. + https://magma.maths.usyd.edu.au/. The Magma interface offers three pieces of functionality: @@ -214,18 +214,13 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import annotations +from pathlib import Path import re import sys import os from sage.structure.parent import Parent from .expect import Expect, ExpectElement, ExpectFunction, FunctionElement -PROMPT = ">>>" - -SAGE_REF = "_sage_ref" -SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF) - from sage.env import SAGE_EXTCODE, DOT_SAGE import sage.misc.misc import sage.misc.sage_eval @@ -233,15 +228,21 @@ from sage.interfaces.tab_completion import ExtraTabCompletion from sage.misc.instancedoc import instancedoc +PROMPT = ">>>" + +SAGE_REF = "_sage_ref" +SAGE_REF_RE = re.compile(r'%s\d+' % SAGE_REF) + INTRINSIC_CACHE = '%s/magma_intrinsic_cache.sobj' % DOT_SAGE EXTCODE_DIR = None -def extcode_dir(iface=None): +def extcode_dir(iface=None) -> str: """ - Return directory that contains all the Magma extcode. This is put - in a writable directory owned by the user, since when attached, - Magma has to write sig and lck files. + Return directory that contains all the Magma extcode. + + This is put in a writable directory owned by the user, since when + attached, Magma has to write sig and lck files. EXAMPLES:: @@ -413,7 +414,7 @@ def __reduce__(self): """ return reduce_load_Magma, tuple([]) - def _read_in_file_command(self, filename): + def _read_in_file_command(self, filename) -> str: """ Return the command in Magma that reads in the contents of the given file. @@ -433,7 +434,7 @@ def _read_in_file_command(self, filename): """ return 'load "%s";' % filename - def _post_process_from_file(self, s): + def _post_process_from_file(self, s) -> str: r""" Used internally in the Magma interface to post-process the result of evaluating a string using a file. For Magma what this does is @@ -494,7 +495,7 @@ def __getattr__(self, attrname): raise AttributeError return MagmaFunction(self, attrname) - def eval(self, x, strip=True, **kwds): + def eval(self, x, strip=True, **kwds) -> str: """ Evaluate the given block x of code in Magma and return the output as a string. @@ -553,7 +554,7 @@ def eval(self, x, strip=True, **kwds): raise RuntimeError("Error evaluating Magma code.\nIN:%s\nOUT:%s" % (x, ans)) return ans - def _preparse(self, s): + def _preparse(self, s) -> str: """ All input gets preparsed by calling this function before it gets evaluated. @@ -578,7 +579,7 @@ def _preparse(self, s): pass return s - def _start(self): + def _start(self) -> None: """ Initialize a Magma interface instance. This involves (1) setting up an obfuscated prompt, and (2) attaching the MAGMA_SPEC file (see @@ -619,7 +620,7 @@ def set(self, var, value): if out.lower().find("error") != -1: raise TypeError("Error executing Magma code:\n%s" % out) - def get(self, var): + def get(self, var) -> str: """ Get the value of the variable var. @@ -655,7 +656,7 @@ def objgens(self, value, gens): sage: R = magma.objgens('PolynomialRing(Rationals(),2)', 'alpha,beta') # optional - magma sage: R.gens() # optional - magma - [alpha, beta] + (alpha, beta) Because of how Magma works you can use this to change the variable names of the generators of an object:: @@ -911,16 +912,15 @@ def cputime(self, t=None): sage: # optional - magma sage: type(magma.cputime()) <... 'float'> - sage: magma.cputime() + sage: magma.cputime() # random 1.9399999999999999 sage: t = magma.cputime() - sage: magma.cputime(t) + sage: magma.cputime(t) # random 0.02 """ if t: return float(self.eval('Cputime(%s)' % t)) - else: - return float(self.eval('Cputime()')) + return float(self.eval('Cputime()')) def chdir(self, dir): """ @@ -1003,7 +1003,7 @@ def load(self, filename): Loading a file in Magma makes all the functions and procedures in the file available. The file should not contain any intrinsics (or - you'll get errors). It also runs code in the file, which can + you will get errors). It also runs code in the file, which can produce output. INPUT: @@ -1018,14 +1018,15 @@ def load(self, filename): sage: with NTF(mode='w+t', suffix='.m') as f: # optional - magma ....: _ = f.write('function f(n) return n^2; end function;\nprint "hi";') ....: print(magma.load(f.name)) - Loading ".../a.m" + Loading "....m" hi sage: magma('f(12)') # optional - magma 144 """ - return self.eval('load "%s"' % filename) + p = Path(filename) + return self.eval('load "%s"' % p.absolute()) - def _next_var_name(self): + def _next_var_name(self) -> str: """ Return the next available variable name in Magma. @@ -1234,7 +1235,7 @@ def bar_call(self, left, name, gens, nvals=1): magma = self # coerce each arg to be a Magma element if isinstance(gens, (list, tuple)): - gens = (magma(z) for z in gens) + gens = [magma(z) for z in gens] # make comma separated list of names (in Magma) of each of the gens v = ', '.join(w.name() for w in gens) else: @@ -1880,10 +1881,11 @@ def __getattr__(self, attrname): def _sage_(self): """ - Return Sage version of this object. Use self.sage() to get the Sage - version. + Return Sage version of this object. + + Use self.sage() to get the Sage version. - Edit src/ext/magma/sage/basic.m to add functionality. + Edit ``src/sage/ext_data/magma/sage/basic.m`` to add functionality. EXAMPLES: Enumerated Sets:: @@ -2070,14 +2072,14 @@ def AssignNames(self, names): def gen(self, n): """ - Return the `n`-th generator of this Magma element. Note that - generators are 1-based in Magma rather than 0-based! + Return the `n`-th generator of this Magma element. + + Note that generators are 1-based in Magma rather than 0-based! INPUT: - ``n`` -- *positive* integer - OUTPUT: :class:`MagmaElement` EXAMPLES:: @@ -2100,7 +2102,7 @@ def gen(self, n): sage: m.gen(4) # optional -- magma Traceback (most recent call last): ... - IndexError: list index out of range + IndexError: tuple index out of range """ if n <= 0: raise IndexError("index must be positive since Magma indexes are 1-based") @@ -2112,7 +2114,7 @@ def gens(self) -> tuple: If ``self`` is named X in Magma, this function evaluates X.1, X.2, etc., in Magma until an error occurs. It then returns a Sage tuple - of the resulting X.i. Note - I don't think there is a Magma command + of the resulting X.i. Note - I do not think there is a Magma command that returns the list of valid X.i. There are numerous ad hoc functions for various classes but nothing systematic. This function gets around that problem. Again, this is something that should @@ -2294,7 +2296,7 @@ def _polynomial_(self, R): sage: R. = QQ[] sage: f = magma(x^2 + 2/3*x + 5) # optional - magma sage: f # optional - magma - t^2 + 2/3*t + 5 + x^2 + 2/3*x + 5 sage: f.Type() # optional - magma RngUPolElt sage: f._polynomial_(R) # optional - magma @@ -2302,7 +2304,7 @@ def _polynomial_(self, R): """ return R(list(self.Eltseq())) - def _latex_(self): + def _latex_(self) -> str: r""" Return latex representation of ``self``. @@ -2854,6 +2856,7 @@ def write(self, s): sage: P. = GF(32003)[] sage: I = sage.rings.ideal.Katsura(P) sage: _ = I.groebner_basis('magma',prot=True) # indirect doctest, optional - magma + ... ******************** FAUGERE F4 ALGORITHM ******************** diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index df9f1583c86..2afac1b01c5 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -830,9 +830,9 @@ def __init__(self, formula, sage: (x, y, z) = var('x, y, z') sage: conds = [-z < 0, -y + z < 0, x^2 + x*y + 2*x*z + 2*y*z - x < 0, \ - x^2 + x*y + 3*x*z + 2*y*z + 2*z^2 - x - z < 0, \ - -2*x + 1 < 0, -x*y - x*z - 2*y*z - 2*z^2 + z < 0, \ - x + 3*y + 3*z - 1 < 0] + ....: x^2 + x*y + 3*x*z + 2*y*z + 2*z^2 - x - z < 0, \ + ....: -2*x + 1 < 0, -x*y - x*z - 2*y*z - 2*z^2 + z < 0, \ + ....: x + 3*y + 3*z - 1 < 0] sage: qepcad(conds, memcells=3000000) # optional - qepcad 2 x - 1 > 0 /\ z > 0 /\ z - y < 0 /\ 3 z + 3 y + x - 1 < 0 """ diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index c57777bb2c2..d2ccaa0468a 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -1427,7 +1427,7 @@ def homfly_polynomial(self, var1='v', var2='z', original=False): sage: H = KnotInfo.L11n459_1_1_1.homfly_polynomial() # optional - database_knotinfo sage: all(L.homfly_polynomial() == L.link().homfly_polynomial(normalization='vz')\ - for L in KnotInfo if L.crossing_number() < 7) + ....: for L in KnotInfo if L.crossing_number() < 7) True REFERENCES: diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 2c5f59ef19f..7e597a49d87 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -515,9 +515,9 @@ def fundamental_group(self, presentation='wirtinger'): F = FreeGroup(len(arcs)) rels = [] for crossing, orientation in zip(self.pd_code(), self.orientation()): - a = arcs.index([i for i in arcs if crossing[0] in i][0]) - b = arcs.index([i for i in arcs if crossing[3] in i][0]) - c = arcs.index([i for i in arcs if crossing[2] in i][0]) + a = next(idx for idx, i in enumerate(arcs) if crossing[0] in i) + b = next(idx for idx, i in enumerate(arcs) if crossing[3] in i) + c = next(idx for idx, i in enumerate(arcs) if crossing[2] in i) ela = F.gen(a) elb = F.gen(b) if orientation < 0: @@ -526,7 +526,7 @@ def fundamental_group(self, presentation='wirtinger'): rels.append(ela * elb / elc / elb) return F.quotient(rels) - def _repr_(self): + def _repr_(self) -> str: r""" Return a string representation. @@ -705,9 +705,8 @@ def idx(cross, edge): """ i = cross.index(edge) if cross.count(edge) > 1: - return cross.index(edge, i+1) - else: - return i + return cross.index(edge, i + 1) + return i seifert_circles = self.seifert_circles() newedge = max(flatten(pd_code)) + 1 @@ -740,8 +739,8 @@ def idx(cross, edge): C1[idx(C1, a)] = newedge + 1 C2 = newPD[newPD.index(tails[b])] C2[idx(C2, b)] = newedge + 2 - newPD.append([newedge + 3, newedge, b, a]) # D - newPD.append([newedge + 2, newedge, newedge + 3, newedge + 1]) # E + newPD.append([newedge + 3, newedge, b, a]) # D + newPD.append([newedge + 2, newedge, newedge + 3, newedge + 1]) # E self._braid = Link(newPD).braid(remove_loops=remove_loops) return self._braid else: @@ -761,21 +760,21 @@ def idx(cross, edge): C1[idx(C1, -a)] = newedge + 1 C2 = newPD[newPD.index(tails[-b])] C2[idx(C2, -b)] = newedge + 2 - newPD.append([newedge + 2, newedge + 1, newedge + 3, newedge]) # D - newPD.append([newedge + 3, -a, -b, newedge]) # E + newPD.append([newedge + 2, newedge + 1, newedge + 3, newedge]) # D + newPD.append([newedge + 3, -a, -b, newedge]) # E self._braid = Link(newPD).braid(remove_loops=remove_loops) return self._braid # We are in the case where no Vogel moves are necessary. G = DiGraph() G.add_vertices([tuple(c) for c in seifert_circles]) - for i,c in enumerate(pd_code): + for i, c in enumerate(pd_code): if self.orientation()[i] == 1: - a = [x for x in seifert_circles if c[3] in x][0] - b = [x for x in seifert_circles if c[0] in x][0] + a = next(x for x in seifert_circles if c[3] in x) + b = next(x for x in seifert_circles if c[0] in x) else: - a = [x for x in seifert_circles if c[0] in x][0] - b = [x for x in seifert_circles if c[1] in x][0] + a = next(x for x in seifert_circles if c[0] in x) + b = next(x for x in seifert_circles if c[1] in x) G.add_edge(tuple(a), tuple(b)) # Get a simple path from a source to a sink in the digraph @@ -785,7 +784,7 @@ def idx(cross, edge): B = BraidGroup(len(ordered_cycles)) available_crossings = copy(pd_code) oc_set = set(ordered_cycles[0]) - for i,x in enumerate(pd_code): + for i, x in enumerate(pd_code): if any(elt in oc_set for elt in x): crossing = x crossing_index = i @@ -903,7 +902,7 @@ def _directions_of_edges(self): heads[a] = next_crossing[0] tails[a] = D D = next_crossing[0] - a = D[(D.index(a)+2) % 4] + a = D[(D.index(a) + 2) % 4] unassigned = set(flatten(pd_code)).difference(set(tails)) while unassigned: @@ -920,7 +919,7 @@ def _directions_of_edges(self): break heads[a] = next_crossing D = next_crossing - a = D[(D.index(a)+2) % 4] + a = D[(D.index(a) + 2) % 4] if a in unassigned: unassigned.remove(a) return tails, heads @@ -1110,7 +1109,7 @@ def _enhanced_states(self): nmax = max(flatten(crossings)) + 1 for i in range(2 ** ncross): v = Integer(i).bits() - v = v + (ncross - len(v))*[0] + v = v + (ncross - len(v)) * [0] G = Graph() for j, cr in enumerate(crossings): n = nmax + j @@ -1134,7 +1133,7 @@ def _enhanced_states(self): smoothings.append((tuple(v), sm, iindex, jmin, jmax)) states = [] # we got all the smoothings, now find all the states for sm in smoothings: - for k in range(len(sm[1])+1): + for k in range(len(sm[1]) + 1): for circpos in combinations(sorted(sm[1]), k): # Add each state circneg = sm[1].difference(circpos) j = writhe + sm[2] + len(circpos) - len(circneg) @@ -1197,13 +1196,13 @@ def _khovanov_homology_cached(self, height, ring=ZZ): difs = [index for index, value in enumerate(V1[0]) if value != V20[index]] if len(difs) == 1 and not (V2[2].intersection(V1[1]) or V2[1].intersection(V1[2])): - m[ii, jj] = (-1)**sum(V2[0][x] for x in range(difs[0]+1, ncross)) + m[ii, jj] = (-1)**sum(V2[0][x] for x in range(difs[0] + 1, ncross)) # Here we have the matrix constructed, now we have to put it in the dictionary of complexes else: m = matrix(ring, len(bij), 0) complexes[i] = m.transpose() - if (i-1, j) not in bases: - complexes[i-1] = matrix(ring, len(bases[(i,j)]), 0) + if (i - 1, j) not in bases: + complexes[i - 1] = matrix(ring, len(bases[(i, j)]), 0) homologies = ChainComplex(complexes).homology() return tuple(sorted(homologies.items())) @@ -1457,7 +1456,7 @@ def pd_code(self): for i, j in zip(last_component, first_component): d_dic[i][1] = d_dic[j][0] crossing_dic = {} - for i,x in enumerate(oriented_gauss_code[1]): + for i, x in enumerate(oriented_gauss_code[1]): if x == -1: crossing_dic[i + 1] = [d_dic[-(i + 1)][0], d_dic[i + 1][0], d_dic[-(i + 1)][1], d_dic[i + 1][1]] @@ -1611,7 +1610,7 @@ def _braid_word_components(self): missing = sorted(missing1) x = [[] for i in range(len(missing) + 1)] - for i,a in enumerate(missing): + for i, a in enumerate(missing): for j, mlj in enumerate(ml): if mlj != 0 and abs(mlj) < a: x[i].append(mlj) @@ -2108,7 +2107,7 @@ def khovanov_polynomial(self, var1='q', var2='t', base_ring=ZZ): gens = [g for g in H.gens() if g.order() == infinity or ch.divides(g.order())] l = len(gens) if l: - coeff[(h,d)] = l + coeff[(h, d)] = l return L(coeff) def determinant(self): @@ -2826,7 +2825,7 @@ def jones_polynomial(self, variab=None, skein_normalization=False, algorithm='jo if variab is None: variab = 't' # We force the result to be in the symbolic ring because of the expand - return jones(SR(variab)**(ZZ(1)/ZZ(4))).expand() + return jones(SR(variab)**(ZZ.one() / ZZ(4))).expand() elif algorithm == 'jonesrep': braid = self.braid() # Special case for the trivial knot with no crossings @@ -3084,14 +3083,14 @@ def homfly_polynomial(self, var1=None, var2=None, normalization='lm'): L = LaurentPolynomialRing(ZZ, [var1, var2]) if len(self._isolated_components()) > 1: if normalization == 'lm': - fact = L({(1, -1):-1, (-1, -1):-1}) + fact = L({(1, -1): -1, (-1, -1): -1}) elif normalization == 'az': - fact = L({(1, -1):1, (-1, -1):-1}) + fact = L({(1, -1): 1, (-1, -1): -1}) elif normalization == 'vz': - fact = L({(1, -1):-1, (-1, -1):1}) + fact = L({(1, -1): -1, (-1, -1): 1}) else: raise ValueError('normalization must be either `lm`, `az` or `vz`') - fact = fact ** (len(self._isolated_components())-1) + fact = fact ** (len(self._isolated_components()) - 1) for i in self._isolated_components(): fact = fact * Link(i).homfly_polynomial(var1, var2, normalization) return fact @@ -3102,7 +3101,7 @@ def homfly_polynomial(self, var1=None, var2=None, normalization='lm'): for comp in ogc[0]: s += ' {}'.format(len(comp)) for cr in comp: - s += ' {} {}'.format(abs(cr)-1, sign(cr)) + s += ' {} {}'.format(abs(cr) - 1, sign(cr)) for i, cr in enumerate(ogc[1]): s += ' {} {}'.format(i, cr) from sage.libs.homfly import homfly_polynomial_dict @@ -3124,7 +3123,7 @@ def homfly_polynomial(self, var1=None, var2=None, normalization='lm'): h_az = self.homfly_polynomial(var1=var1, var2=var2, normalization='az') a, z = h_az.parent().gens() v = ~a - return h_az.subs({a:v}) + return h_az.subs({a: v}) else: raise ValueError('normalization must be either `lm`, `az` or `vz`') @@ -3313,8 +3312,8 @@ def colorings(self, n=None): M = self._coloring_matrix(n=n) KM = M.right_kernel_matrix() F = FreeModule(M.base_ring(), KM.dimensions()[0]) - K = [v*KM for v in F] - res = set([]) + K = [v * KM for v in F] + res = set() arcs = self.arcs('pd') for coloring in K: colors = sorted(set(coloring)) @@ -3408,7 +3407,7 @@ def coloring_maps(self, n=None, finitely_presented=False): maps = [] for c in cols: t = list(c.values()) - ims = [b*a**i for i in t] + ims = [b * a**i for i in t] maps.append(gr.hom(ims)) return maps @@ -3627,7 +3626,7 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None, # Special case for the unknot if not pd_code: - return circle((0,0), ZZ(1)/ZZ(2), color=color, **kwargs) + return circle((0, 0), ZZ.one() / ZZ(2), color=color, **kwargs) # The idea is the same followed in spherogram, but using MLP instead of # network flows. @@ -3653,9 +3652,9 @@ def flow_from_source(e): Return the flow variable from the source. """ if e > 0: - return v[2*edges.index(e)] + return v[2 * edges.index(e)] else: - return v[2*edges.index(-e)+1] + return v[2 * edges.index(-e) + 1] def flow_to_sink(e): r""" @@ -3680,9 +3679,10 @@ def flow_to_sink(e): MLP.solve() # we store the result in a vector s packing right bends as negative left ones values = MLP.get_values(v, convert=ZZ, tolerance=1e-3) - s = [values[2*i] - values[2*i + 1] for i in range(len(edges))] + s = [values[2 * i] - values[2 * i + 1] for i in range(len(edges))] # segments represents the different parts of the previous edges after bending - segments = {e: [(e,i) for i in range(abs(s[edges.index(e)])+1)] for e in edges} + segments = {e: [(e, i) for i in range(abs(s[edges.index(e)]) + 1)] + for e in edges} pieces = {tuple(i): [i] for j in segments.values() for i in j} nregions = [] for r in regions[:-1]: # interior regions @@ -3711,7 +3711,7 @@ def flow_to_sink(e): c = -1 b = a while c != 2: - if b == len(badregion)-1: + if b == len(badregion) - 1: b = 0 else: b += 1 @@ -3739,15 +3739,15 @@ def flow_to_sink(e): if a < b: r1 = badregion[:a] + [[badregion[a][0], 0], [N1, 1]] + badregion[b:] - r2 = badregion[a + 1:b] + [[N2, 1],[N1, 1]] + r2 = badregion[a + 1:b] + [[N2, 1], [N1, 1]] else: r1 = badregion[b:a] + [[badregion[a][0], 0], [N1, 1]] - r2 = badregion[:b] + [[N2, 1],[N1, 1]] + badregion[a + 1:] + r2 = badregion[:b] + [[N2, 1], [N1, 1]] + badregion[a + 1:] if otherregion: c = [x for x in otherregion if badregion[b][0] == x[0]] c = otherregion.index(c[0]) - otherregion.insert(c + 1, [N2,otherregion[c][1]]) + otherregion.insert(c + 1, [N2, otherregion[c][1]]) otherregion[c][1] = 0 nregions.remove(badregion) nregions.append(r1) @@ -3805,7 +3805,7 @@ def flow_to_sink(e): turn = -1 else: turn = 1 - lengthse = [lengths[(e,k)] for k in range(abs(s[edges.index(e)])+1)] + lengthse = [lengths[(e, k)] for k in range(abs(s[edges.index(e)]) + 1)] if c.index(e) == 0 or (c.index(e) == 3 and orien == 1) or (c.index(e) == 1 and orien == -1): turn = -turn lengthse.reverse() diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi index b1be0216a2e..2bd2ae1205f 100644 --- a/src/sage/libs/flint/nmod_poly_linkage.pxi +++ b/src/sage/libs/flint/nmod_poly_linkage.pxi @@ -536,10 +536,8 @@ cdef inline int celement_pow(nmod_poly_t res, nmod_poly_t x, long e, nmod_poly_t Make sure that exponentiation can be interrupted, see :issue:`17470`:: sage: n = 2^23 - sage: alarm(0.2); x^n; cancel_alarm() - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.2): (x^n).degree() """ if modulus != NULL: sig_on() diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index b35626b29e1..3faa39c5329 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1135,10 +1135,8 @@ cdef class GapElement(RingElement): Check that this can be interrupted gracefully:: sage: a, b = libgap.GL(1000, 3).GeneratorsOfGroup(); g = a * b - sage: alarm(0.5); g ^ (2 ^ 10000) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): g ^ (2 ^ 10000) sage: libgap.CyclicGroup(2) ^ 2 Traceback (most recent call last): diff --git a/src/sage/libs/libecm.pyx b/src/sage/libs/libecm.pyx index 6e0fc8668a5..86492a5e232 100644 --- a/src/sage/libs/libecm.pyx +++ b/src/sage/libs/libecm.pyx @@ -143,10 +143,8 @@ def ecmfactor(number, double B1, verbose=False, sigma=0): Check that ``ecmfactor`` can be interrupted (factoring a large prime number):: - sage: alarm(0.5); ecmfactor(2^521-1, 1e7) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): ecmfactor(2^521-1, 1e7) Some special cases:: diff --git a/src/sage/libs/mpmath/ext_impl.pyx b/src/sage/libs/mpmath/ext_impl.pyx index 8996d6296cf..477b6a09b09 100644 --- a/src/sage/libs/mpmath/ext_impl.pyx +++ b/src/sage/libs/mpmath/ext_impl.pyx @@ -1183,10 +1183,14 @@ cdef MPF_exp(MPF *y, MPF *x, MPopts opts): cdef mpz_t t, u cdef tuple w if x.special: - if x.special == S_ZERO: MPF_set_si(y, 1) - elif x.special == S_NINF: MPF_set_zero(y) - elif x.special == S_INF: MPF_set_inf(y) - else: MPF_set_nan(y) + if x.special == S_ZERO: + MPF_set_si(y, 1) + elif x.special == S_NINF: + MPF_set_zero(y) + elif x.special == S_INF: + MPF_set_inf(y) + else: + MPF_set_nan(y) return wp = opts.prec + 14 sign = mpz_sgn(x.man) < 0 diff --git a/src/sage/libs/mpmath/utils.pyx b/src/sage/libs/mpmath/utils.pyx index 5f681852c68..47061a6bfa4 100644 --- a/src/sage/libs/mpmath/utils.pyx +++ b/src/sage/libs/mpmath/utils.pyx @@ -134,11 +134,15 @@ cpdef normalize(long sign, Integer man, exp, long bc, long prec, str rnd): elif rnd == 'd': mpz_fdiv_q_2exp(res.value, man.value, shift) elif rnd == 'f': - if sign: mpz_cdiv_q_2exp(res.value, man.value, shift) - else: mpz_fdiv_q_2exp(res.value, man.value, shift) + if sign: + mpz_cdiv_q_2exp(res.value, man.value, shift) + else: + mpz_fdiv_q_2exp(res.value, man.value, shift) elif rnd == 'c': - if sign: mpz_fdiv_q_2exp(res.value, man.value, shift) - else: mpz_cdiv_q_2exp(res.value, man.value, shift) + if sign: + mpz_fdiv_q_2exp(res.value, man.value, shift) + else: + mpz_cdiv_q_2exp(res.value, man.value, shift) elif rnd == 'u': mpz_cdiv_q_2exp(res.value, man.value, shift) exp += shift diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index dd910a567c0..57f5300845c 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -506,9 +506,9 @@ cdef class ntl_mat_GF2(): EXAMPLES:: sage: l = [0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, \ - 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, \ - 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, \ - 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0] + ....: 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, \ + ....: 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, \ + ....: 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0] sage: A = ntl.mat_GF2(8,8,l) sage: E = ~A*A sage: E.IsIdent() diff --git a/src/sage/libs/pari/__init__.py b/src/sage/libs/pari/__init__.py index b5bc281db4d..7d01109e276 100644 --- a/src/sage/libs/pari/__init__.py +++ b/src/sage/libs/pari/__init__.py @@ -173,6 +173,10 @@ 3.60546360143265208591582056420772677481026899659802474544 # 32-bit """ +from cypari2 import Pari + +from sage.ext.memory import init_memory_functions + def _get_pari_instance(): """ @@ -181,14 +185,12 @@ def _get_pari_instance(): sage: pari # indirect doctest Interface to the PARI C library """ - from cypari2 import Pari stack_initial = 1024 * 1024 stack_max = 1024 * stack_initial P = Pari(stack_initial, stack_max) # pari_init_opts() overrides MPIR's memory allocation functions, # so we need to reset them. - from sage.ext.memory import init_memory_functions init_memory_functions() # PARI sets debugmem=1 by default but we do not want those warning diff --git a/src/sage/libs/pari/all.py b/src/sage/libs/pari/all.py index 6cef5978cc1..e896e5b09ea 100644 --- a/src/sage/libs/pari/all.py +++ b/src/sage/libs/pari/all.py @@ -1,3 +1,4 @@ -from cypari2.gen import Gen as pari_gen from cypari2 import PariError +from cypari2.gen import Gen as pari_gen + from sage.libs.pari import pari diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index d4ed0e3b064..5acef85c3e6 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -96,16 +96,18 @@ cdef Rational si2sa_QQ(number *n, number **nn, ring *_ring): mpq_init(_z) - ## Immediate integers handles carry the tag 'SR_INT', i.e. the last bit is 1. - ## This distinguishes immediate integers from other handles which point to - ## structures aligned on 4 byte boundaries and therefore have last bit zero. - ## (The second bit is reserved as tag to allow extensions of this scheme.) - ## Using immediates as pointers and dereferencing them gives address errors. + # Immediate integers handles carry the tag 'SR_INT', i.e. the last bit is 1. + # This distinguishes immediate integers from other handles which point to + # structures aligned on 4 byte boundaries and therefore have last bit zero. + # (The second bit is reserved as tag to allow extensions of this scheme.) + # Using immediates as pointers and dereferencing them gives address errors. nom = nlGetNumerator(n, _ring.cf) mpz_init(nom_z) - if (SR_HDL(nom) & SR_INT): mpz_set_si(nom_z, SR_TO_INT(nom)) - else: mpz_set(nom_z,nom.z) + if SR_HDL(nom) & SR_INT: + mpz_set_si(nom_z, SR_TO_INT(nom)) + else: + mpz_set(nom_z,nom.z) mpq_set_num(_z,nom_z) nlDelete(&nom,_ring.cf) @@ -114,8 +116,10 @@ cdef Rational si2sa_QQ(number *n, number **nn, ring *_ring): denom = nlGetDenom(n, _ring.cf) mpz_init(denom_z) - if (SR_HDL(denom) & SR_INT): mpz_set_si(denom_z, SR_TO_INT(denom)) - else: mpz_set(denom_z,denom.z) + if SR_HDL(denom) & SR_INT: + mpz_set_si(denom_z, SR_TO_INT(denom)) + else: + mpz_set(denom_z,denom.z) mpq_set_den(_z, denom_z) nlDelete(&denom,_ring.cf) diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py index a52c82ee1cc..f2944602f40 100644 --- a/src/sage/manifolds/differentiable/affine_connection.py +++ b/src/sage/manifolds/differentiable/affine_connection.py @@ -372,7 +372,7 @@ def __init__(self, domain, name, latex_name=None): sage: M = Manifold(3, 'M') sage: from sage.manifolds.differentiable.affine_connection import \ - AffineConnection + ....: AffineConnection sage: nab = AffineConnection(M, 'nabla', latex_name=r'\nabla') sage: nab Affine connection nabla on the 3-dimensional differentiable diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 96a175825b3..41b1786bd52 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -87,7 +87,7 @@ cdef class Matrix(Matrix0): [1.000000000, 2.000000000; 3.000000000, 1.000000000] # 32-bit [1.00000000000000, 2.00000000000000; 3.00000000000000, 1.00000000000000] # 64-bit """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari return pari.matrix(self._nrows, self._ncols, self._list()) def _gap_init_(self): diff --git a/src/sage/matrix/matrix_dense.pyx b/src/sage/matrix/matrix_dense.pyx index f26078bde7e..5280d1e2778 100644 --- a/src/sage/matrix/matrix_dense.pyx +++ b/src/sage/matrix/matrix_dense.pyx @@ -11,6 +11,7 @@ TESTS:: cimport sage.matrix.matrix as matrix from sage.structure.richcmp cimport richcmp_item, rich_to_bool +from sage.calculus.functional import derivative import sage.matrix.matrix_space import sage.structure.sequence @@ -275,11 +276,19 @@ cdef class Matrix_dense(matrix.Matrix): sage: m._derivative(x) # needs sage.symbolic [ 0 1] [ 2*x 3*x^2] + + TESTS: + + Verify that :issue:`15067` is fixed:: + + sage: u = matrix(1, 2, [-1, 1]) + sage: derivative(u, x) + [0 0] """ # We could just use apply_map if self._nrows==0 or self._ncols==0: return self.__copy__() - v = [z.derivative(var) for z in self.list()] + v = [sage.calculus.functional.derivative(z, var) for z in self.list()] if R is None: v = sage.structure.sequence.Sequence(v) R = v.universe() diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 215af3ecd65..4738aa2685d 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -4385,14 +4385,8 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 2000, 2000) sage: B = random_matrix(ZZ, 2000, 2000) - sage: t0 = walltime() - sage: alarm(2); A._solve_iml(B) # long time - Traceback (most recent call last): - ... - AlarmInterrupt - sage: t = walltime(t0) - sage: t < 10 or t - True + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(2, max_wait_after_interrupt=8): A._solve_iml(B) ALGORITHM: Uses IML. @@ -4549,14 +4543,8 @@ cdef class Matrix_integer_dense(Matrix_dense): sage: A = random_matrix(ZZ, 2000, 2000) sage: B = random_matrix(ZZ, 2000, 2000) - sage: t0 = walltime() - sage: alarm(2); A._solve_flint(B) # long time - Traceback (most recent call last): - ... - AlarmInterrupt - sage: t = walltime(t0) - sage: t < 10 or t - True + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(2, max_wait_after_interrupt=8): A._solve_flint(B) AUTHORS: diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index a1e79eda019..1987a12332c 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -1393,14 +1393,15 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse def _magma_init_(self, magma): """ - Return a string of ``self`` in ``Magma`` form. Does not return - ``Magma`` object but string. + Return a string of ``self`` in ``Magma`` form. + + This does not return a ``Magma`` object but a string. EXAMPLES:: sage: A = random_matrix(GF(2),3,3) sage: A._magma_init_(magma) # optional - magma - 'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))' + 'Matrix(GF(2),3,3,StringToIntegerSequence("..."))' sage: A = random_matrix(GF(2),100,100) sage: B = random_matrix(GF(2),100,100) sage: magma(A*B) == magma(A) * magma(B) # optional - magma @@ -2004,10 +2005,8 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A = random_matrix(GF(2), n, m) sage: x = random_vector(GF(2), m) sage: B = A*x - sage: alarm(0.5); sol = A.solve_right(B) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): sol = A.solve_right(B) """ cdef mzd_t *B_entries = (B)._entries diff --git a/src/sage/matrix/matrix_polynomial_dense.pyx b/src/sage/matrix/matrix_polynomial_dense.pyx index 99979a37ed8..81cfe8163ce 100644 --- a/src/sage/matrix/matrix_polynomial_dense.pyx +++ b/src/sage/matrix/matrix_polynomial_dense.pyx @@ -4116,8 +4116,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): completed row-wise into a `3 \times 3` unimodular matrix:: sage: mat = matrix(ring, 2, 3, \ - [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ - [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) + ....: [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ + ....: [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) sage: rcomp = mat._basis_completion_via_reversed_approx(); rcomp [ 2*x^2 + 1 4*x^2 + 3*x 2*x^2 + 3*x] sage: mat.stack(rcomp).determinant() @@ -4128,9 +4128,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): completion has two columns; in both cases, the Smith form is preserved:: sage: mat = matrix(ring, 3, 2, \ - [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ - [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ - [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) + ....: [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ + ....: [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ + ....: [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) sage: mat.smith_form(transformation=False) [x + 3 0] [ 0 0] @@ -4327,8 +4327,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): there is nothing to complete):: sage: mat = matrix(ring, 2, 3, \ - [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ - [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) + ....: [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ + ....: [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) sage: rcomp = mat.basis_completion(); rcomp [ 2*x^2 + 1 4*x^2 + 3*x 2*x^2 + 3*x] sage: mat.stack(rcomp).determinant() @@ -4341,9 +4341,9 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): completion has two columns; in both cases, the Smith form is preserved:: sage: mat = matrix(ring, 3, 2, \ - [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ - [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ - [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) + ....: [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ + ....: [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ + ....: [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) sage: mat.smith_form(transformation=False) [x + 3 0] [ 0 0] @@ -4378,8 +4378,8 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): 1 sage: mat = matrix([[x*(x-1), x*(x-2)], \ - [x*(x-2), x*(x-3)], \ - [(x-1)*(x-2), (x-1)*(x-3)]]) + ....: [x*(x-2), x*(x-3)], \ + ....: [(x-1)*(x-2), (x-1)*(x-3)]]) sage: mat.smith_form(transformation=False) [1 0] [0 x] @@ -4496,14 +4496,14 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): sage: ring. = GF(7)[] sage: mat1 = matrix([[x*(x-1)*(x-2), (x-2)*(x-3)*(x-4), (x-4)*(x-5)*(x-6)]]) sage: rcomp1 = matrix(ring, 2, 3, \ - [[5*x^2 + 4*x + 1, 5*x^2 + 2*x, 5*x^2], \ - [2*x^3 + 4*x^2, 2*x^3 + 6*x^2 + 2*x + 1, 2*x^3 + x^2 + 3*x]]) + ....: [[5*x^2 + 4*x + 1, 5*x^2 + 2*x, 5*x^2], \ + ....: [2*x^3 + 4*x^2, 2*x^3 + 6*x^2 + 2*x + 1, 2*x^3 + x^2 + 3*x]]) sage: rcomp1._is_basis_completion(mat1) True sage: mat2 = matrix(ring, 2, 3, \ - [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ - [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) + ....: [[x^2 + 5*x + 5, 3*x^2 + x + 3, 4*x^2 + 5*x + 4], \ + ....: [5*x^2 + 4*x, 3*x^2 + 4*x + 5, 5*x^2 + 5*x + 3]]) sage: rcomp2 = matrix(ring, 1, 3, [[2*x^2 + 1, 4*x^2 + 3*x, 2*x^2 + 3*x]]) sage: rcomp2._is_basis_completion(mat2) True @@ -4512,16 +4512,16 @@ cdef class Matrix_polynomial_dense(Matrix_generic_dense): True sage: mat3 = matrix(ring, 3, 2, \ - [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ - [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ - [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) + ....: [[ x^3 + x^2 + 5*x + 5, 2*x^3 + 2*x + 4], \ + ....: [ 3*x^3 + 2*x^2 + x + 3, 6*x^3 + 5*x^2 + x + 1], \ + ....: [2*x^3 + 5*x^2 + 3*x + 4, 4*x^3 + 6*x^2 + 5*x + 6]]) sage: rcomp3 = matrix(ring, 1, 2, [[x + 1, 2*x]]) sage: rcomp3._is_basis_completion(mat3) True sage: ccomp3 = matrix(ring, 3, 2, \ - [[3*x + 1, 4*x + 4], \ - [ 2*x, 5*x + 1], \ - [ 6*x, x]]) + ....: [[3*x + 1, 4*x + 4], \ + ....: [ 2*x, 5*x + 1], \ + ....: [ 6*x, x]]) sage: ccomp3._is_basis_completion(mat3, row_wise=False) True diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index b0c58016c9b..59fff6a1693 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -119,7 +119,7 @@ from sage.misc.verbose import verbose # ######################################################## # PARI C library -from sage.libs.pari.all import PariError +from cypari2.handle_error import PariError from sage.libs.pari.convert_gmp cimport INTFRAC_to_mpq from sage.libs.pari.convert_flint cimport rational_matrix, _new_GEN_from_fmpq_mat_t from cypari2.stack cimport clear_stack diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 7f5cb645134..647efed20ca 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -955,6 +955,24 @@ def characteristic(self): """ return self.base_ring().characteristic() + def is_exact(self): + """ + Test whether elements of this matrix space are represented exactly. + + OUTPUT: + + Return ``True`` if elements of this matrix space are represented exactly, i.e., + there is no precision loss when doing arithmetic. + + EXAMPLES:: + + sage: MatrixSpace(ZZ, 3).is_exact() + True + sage: MatrixSpace(RR, 3).is_exact() + False + """ + return self._base.is_exact() + def _has_default_implementation(self): r""" EXAMPLES:: diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index 92a2d18d846..b8e5d76d8ea 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -16,6 +16,7 @@ from cysignals.signals cimport sig_check cimport sage.matrix.matrix as matrix cimport sage.matrix.matrix0 as matrix0 from sage.categories.rings import Rings +from sage.calculus.functional import derivative from sage.structure.element cimport Element, Vector from sage.structure.richcmp cimport richcmp_item, rich_to_bool @@ -810,13 +811,23 @@ cdef class Matrix_sparse(matrix.Matrix): sage: m._derivative(x) # needs sage.symbolic [ 0 1] [ 2*x 3*x^2] + + TESTS: + + Verify that :issue:`15067` is fixed:: + + sage: m = matrix(3, 3, {(1, 1): 2, (0,2): 5}) + sage: derivative(m, x) + [0 0 0] + [0 0 0] + [0 0 0] """ # We would just use apply_map, except that Cython does not # allow lambda functions if self._nrows==0 or self._ncols==0: return self.__copy__() - v = [(ij, z.derivative(var)) for ij, z in self.dict().iteritems()] + v = [(ij, sage.calculus.functional.derivative(z, var)) for ij, z in self.dict().iteritems()] if R is None: w = [x for _, x in v] w = sage.structure.sequence.Sequence(w) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index dbb0214994e..851fb623ddd 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -241,8 +241,11 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', implementation - ``'unimodular'`` -- creates a matrix of determinant 1 - - ``'diagonalizable'`` -- creates a diagonalizable matrix whose - eigenvectors, if computed by hand, will have only integer entries + - ``'diagonalizable'`` -- creates a diagonalizable matrix. if the + base ring is ``QQ`` creates a diagonalizable matrix whose eigenvectors, + if computed by hand, will have only integer entries. See the + documentation of :meth:`~sage.matrix.special.random_diagonalizable_matrix` + for more information - ``implementation`` -- (``None`` or string or a matrix class) a possible implementation. See the documentation of the constructor of @@ -3054,15 +3057,16 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): """ Create a random matrix that diagonalizes nicely. - To be used as a teaching tool. Return matrices have only real - eigenvalues. + To be used as a teaching tool. The eigenvalues will be elements of the + base ring. If the base ring used is ``QQ`` then the returned matrix will + have integer eigenvalues. INPUT: If eigenvalues and dimensions are not specified in a list, they will be assigned randomly. - - ``parent`` -- the desired size of the square matrix + - ``parent`` -- the desired parent of the square matrix (a matrix space) - ``eigenvalues`` -- the list of desired eigenvalues (default=None) @@ -3071,8 +3075,9 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): OUTPUT: - A square, diagonalizable, matrix with only integer entries. The - eigenspaces of this matrix, if computed by hand, give basis + A square, diagonalizable, matrix. Elements of the matrix are elements + of the base ring. If the ring used is ``QQ`` then we have integer entries, + and the eigenspaces of this matrix, if computed by hand, gives basis vectors with only integer entries. .. NOTE:: @@ -3118,15 +3123,28 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): sage: all(e in eigenvalues for e in eigenvalues2) True + Matrices over finite fields are also supported:: + + sage: K = GF(3) + sage: M = random_matrix(K, 3, 3, algorithm="diagonalizable") + sage: M.parent() + Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 3 + sage: M.is_diagonalizable() + True + sage: M # random + [0 0 1] + [2 1 1] + [1 0 0] + TESTS: - Eigenvalues must all be integers. :: + Eigenvalues must all be elements of the ring. :: sage: random_matrix(QQ, 3, algorithm='diagonalizable', # needs sage.symbolic ....: eigenvalues=[2+I, 2-I, 2], dimensions=[1,1,1]) Traceback (most recent call last): ... - TypeError: eigenvalues must be integers. + TypeError: eigenvalues must be elements of the corresponding ring. Diagonal matrices must be square. :: @@ -3189,6 +3207,7 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): from sage.misc.prandom import randint size = parent.nrows() + ring = parent.base_ring() if parent.nrows() != parent.ncols(): raise TypeError("a diagonalizable matrix must be square.") if eigenvalues is not None and dimensions is None: @@ -3199,7 +3218,7 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): values = [] # create a list with "size" number of entries for eigen_index in range(size): - eigenvalue = randint(-10, 10) + eigenvalue = ring(randint(-10, 10)) values.append(eigenvalue) values.sort() dimensions = [] @@ -3214,8 +3233,8 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): size_check = 0 for check in range(len(dimensions)): size_check = size_check + dimensions[check] - if not all(x in ZZ for x in eigenvalues): - raise TypeError("eigenvalues must be integers.") + if not all(x in ring for x in eigenvalues): + raise TypeError("eigenvalues must be elements of the corresponding ring.") if size != size_check: raise ValueError("the size of the matrix must equal the sum of the dimensions.") if min(dimensions) < 1: @@ -3227,7 +3246,7 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): dimensions = [x[0] for x in dimensions_sort] eigenvalues = [x[1] for x in dimensions_sort] # Create the matrix of eigenvalues on the diagonal. Use a lower limit and upper limit determined by the eigenvalue dimensions. - diagonal_matrix = matrix(QQ, size) + diagonal_matrix = matrix(ring, size) up_bound = 0 low_bound = 0 for row_index in range(len(dimensions)): @@ -3237,7 +3256,7 @@ def random_diagonalizable_matrix(parent, eigenvalues=None, dimensions=None): low_bound = low_bound+dimensions[row_index] # Create a matrix to hold each of the eigenvectors as its columns, begin with an identity matrix so that after row and column # operations the resulting matrix will be unimodular. - eigenvector_matrix = matrix(QQ, size, size, 1) + eigenvector_matrix = matrix.identity(ring, size) upper_limit = 0 lower_limit = 0 # run the routine over the necessary number of columns corresponding eigenvalue dimension. diff --git a/src/sage/matroids/database_collections.py b/src/sage/matroids/database_collections.py index b5175e0b3d7..3485e305cc1 100644 --- a/src/sage/matroids/database_collections.py +++ b/src/sage/matroids/database_collections.py @@ -85,7 +85,7 @@ def AllMatroids(n, r=None, type='all'): Traceback (most recent call last): ... ValueError: (n=10, r=4, type='all') is not available in the database - sage: for M in matroids.AllMatroids(12, 3, "unorientable"): + sage: for M in matroids.AllMatroids(12, 3, 'unorientable'): ....: M Traceback (most recent call last): ... @@ -94,12 +94,12 @@ def AllMatroids(n, r=None, type='all'): ....: M Traceback (most recent call last): ... - ValueError: The rank needs to be specified for type "unorientable" + ValueError: The rank needs to be specified for type 'unorientable' sage: for M in matroids.AllMatroids(6, type='nice'): ....: M Traceback (most recent call last): ... - AttributeError: The type "nice" is not available. There needs to be an "is_nice()" + AttributeError: The type 'nice' is not available. There needs to be an 'is_nice()' attribute for the type to be supported. REFERENCES: @@ -130,7 +130,7 @@ def AllMatroids(n, r=None, type='all'): ....: [ None, None, None, None, None, None, None, None, None, None, None, 1, 12], ....: [ None, None, None, None, None, None, None, None, None, None, None, None, 1] ....: ] - sage: for r in range(0, 12 + 1): # long time + sage: for r in range(0, 12 + 1): # long time ....: for n in range(r, 12 + 1): ....: if all[r][n] and all[r][n] < 1000: ....: assert len(list(matroids.AllMatroids(n, r))) == all[r][n] @@ -143,21 +143,21 @@ def AllMatroids(n, r=None, type='all'): ....: [ None, None, None, 1, 2, 4, 9, 23, 68, 383, 5249, 232928, None], ....: [ None, None, None, None, 1, 3, 11, 49, 617, 185981, None, None, None] ....: ] - sage: for r in range(0, 4 + 1): # long time + sage: for r in range(0, 4 + 1): # long time ....: for n in range(r, 12 + 1): ....: if simple[r][n] and simple[r][n] < 1000: - ....: assert len(list(matroids.AllMatroids(n, r, "simple"))) == simple[r][n] - ....: for M in matroids.AllMatroids(n, r, "simple"): + ....: assert len(list(matroids.AllMatroids(n, r, 'simple'))) == simple[r][n] + ....: for M in matroids.AllMatroids(n, r, 'simple'): ....: assert M.is_valid() and M.is_simple() sage: unorientable = [ ....: [1, 3, 18, 201, 9413], ....: [1, 34, 12284, None, None] ....: ] - sage: for r in range(0, 1 + 1): # long time + sage: for r in range(0, 1 + 1): # long time ....: for n in range(0, 4 + 1): ....: if unorientable[r][n] and unorientable[r][n] < 1000: - ....: assert len(list(matroids.AllMatroids(n+7, r+3, "unorientable"))) == unorientable[r][n] - ....: for M in matroids.AllMatroids(n+7, r+3, "unorientable"): + ....: assert len(list(matroids.AllMatroids(n+7, r+3, 'unorientable'))) == unorientable[r][n] + ....: for M in matroids.AllMatroids(n+7, r+3, 'unorientable'): ....: assert M.is_valid() """ from sage.matroids.constructor import Matroid @@ -165,44 +165,44 @@ def AllMatroids(n, r=None, type='all'): DatabaseMatroids().require() import matroid_database - if type != "all" and type != "unorientable": + if type != 'all' and type != 'unorientable': try: - getattr(Matroid(bases=[[1, 2], [1, 3]]), "is_" + type) + getattr(Matroid(bases=[[1, 2], [1, 3]]), 'is_' + type) except AttributeError: raise AttributeError( - "The type \"%s\" is not available. " % type + - "There needs to be an \"is_%s()\" attribute for the " % type + + "The type '%s' is not available. " % type + + "There needs to be an 'is_%s()' attribute for the " % type + "type to be supported." ) - if r is None and type == "unorientable": - raise ValueError("The rank needs to be specified for type \"%s\"" % type) + if r is None and type == 'unorientable': + raise ValueError("The rank needs to be specified for type '%s'" % type) if r is None: - rng = range(0, n+1) + rng = range(0, n + 1) else: - rng = range(r, r+1) + rng = range(r, r + 1) for r in rng: - if (r == 0 or r == n) and type != "unorientable": + if (r == 0 or r == n) and type != 'unorientable': M = Matroid(groundset=range(n), bases=[range(r)]) - M.rename(type + "_n" + str(n).zfill(2) + "_r" + str(r).zfill(2) + "_#" + "0" + ": " + repr(M)) - if type == "all": + M.rename(type + '_n' + str(n).zfill(2) + '_r' + str(r).zfill(2) + '_#' + '0' + ': ' + repr(M)) + if type == 'all': yield M else: - f = getattr(M, "is_" + type) + f = getattr(M, 'is_' + type) if f(): yield M else: - rp = min(r, n - r) if (type != "unorientable") else r - type_db = "all" if (type != "unorientable") else "unorientable" + rp = min(r, n - r) if (type != 'unorientable') else r + type_db = 'all' if (type != 'unorientable') else 'unorientable' - matroids_bases = getattr(matroid_database, type_db + "_matroids_bases") + matroids_bases = getattr(matroid_database, type_db + '_matroids_bases') try: matroids_bases(n, rp).__next__() except ValueError: raise ValueError( - "(n=%s, r=%s, type=\"%s\")" % (n, r, type) + "(n=%s, r=%s, type='%s')" % (n, r, type) + " is not available in the database" ) @@ -210,14 +210,14 @@ def AllMatroids(n, r=None, type='all'): for B in matroids_bases(n, rp): M = Matroid(groundset=range(n), bases=B) - if type != "unorientable" and n - r < r: + if type != 'unorientable' and n - r < r: M = M.dual() - M.rename(type + "_n" + str(n).zfill(2) + "_r" + str(r).zfill(2) + "_#" + str(cnt) + ": " + repr(M)) - if type == "all" or type == "unorientable": + M.rename(type + '_n' + str(n).zfill(2) + '_r' + str(r).zfill(2) + '_#' + str(cnt) + ': ' + repr(M)) + if type == 'all' or type == 'unorientable': yield M cnt += 1 else: - f = getattr(M, "is_" + type) + f = getattr(M, 'is_' + type) if f(): yield M cnt += 1 diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index cee2b831e5b..d4a71e8a38e 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6210,35 +6210,56 @@ cdef class Matroid(SageObject): sage: M = matroids.Theta(4) sage: M.is_paving() False + + REFERENCES: + + [Oxl2011]_, p. 24. """ if self.rank() >= 2: - for X in combinations(self.groundset(), self.rank() - 1): - if not self._is_independent(frozenset(X)): - return False + for _ in self.dependent_sets_iterator(self.rank() - 1): + return False return True cpdef bint is_sparse_paving(self) noexcept: """ Return if ``self`` is sparse-paving. - A matroid is sparse-paving if the symmetric difference of every pair - of circuits is greater than 2. + A matroid is sparse-paving if it is paving and its dual is paving. OUTPUT: boolean + ALGORITHM: + + First, check that the matroid is paving. Then, verify that the + symmetric difference of every pair of distinct `r`-circuits is greater + than 2. + EXAMPLES:: sage: M = matroids.catalog.Vamos() sage: M.is_sparse_paving() + True + sage: M = matroids.catalog.N1() + sage: M.is_sparse_paving() False - sage: M = matroids.catalog.Fano() + + REFERENCES: + + The definition of sparse-paving matroids can be found in [MNWW2011]_. + The algorithm uses an alternative characterization from [Jer2006]_. + + TESTS:: + + sage: M = matroids.Uniform(4, 50) # fast because we don't check M.dual().is_paving() sage: M.is_sparse_paving() True + sage: for M in matroids.AllMatroids(8): # optional - matroid_database + ....: assert M.is_sparse_paving() == (M.is_paving() and M.dual().is_paving()) """ if not self.is_paving(): return False from itertools import combinations - for (C1, C2) in combinations(self.circuits_iterator(), 2): + for (C1, C2) in combinations(self.nonbases_iterator(), 2): if len(C1 ^ C2) <= 2: return False return True diff --git a/src/sage/matroids/transversal_matroid.pyx b/src/sage/matroids/transversal_matroid.pyx index 7532fac62b0..a11c59f570f 100644 --- a/src/sage/matroids/transversal_matroid.pyx +++ b/src/sage/matroids/transversal_matroid.pyx @@ -668,7 +668,7 @@ cdef class TransversalMatroid(BasisExchangeMatroid): labels.append(l) return TransversalMatroid(sets, groundset=self.groundset(), set_labels=labels) - cpdef transversal_extension(self, element=None, newset=False, sets=[]): + cpdef transversal_extension(self, element=None, newset=False, sets=None): r""" Return a :class:`TransversalMatroid` extended by an element. @@ -751,6 +751,8 @@ cdef class TransversalMatroid(BasisExchangeMatroid): Transversal matroid of rank 3 on 5 elements, with 3 sets sage: Ne = N.transversal_extension(element='f', sets=['s2']) """ + if sets is None: + sets = [] cdef set parsed_sets = set(sets) if element is None: element = newlabel(self._groundset) @@ -781,17 +783,17 @@ cdef class TransversalMatroid(BasisExchangeMatroid): else: new_sets.append(s) + groundset = self._groundset.union([element]) if newset: if newset is True: - newset = newlabel(self._groundset.union(labels)) + newset = newlabel(groundset.union(labels)) new_sets.append([element]) labels = list(labels) # Make a shallow copy since we mutate it labels.append(newset) - groundset = self._groundset.union([element]) return TransversalMatroid(new_sets, groundset, labels) - def transversal_extensions(self, element=None, sets=[]): + def transversal_extensions(self, element=None, sets=None): r""" Return an iterator of extensions based on the transversal presentation. @@ -829,7 +831,7 @@ cdef class TransversalMatroid(BasisExchangeMatroid): raise ValueError("cannot extend by element already in groundset") labels = self._set_labels_input - if not sets: + if sets is None: sets = labels elif not set(sets).issubset(labels): raise ValueError("sets do not match presentation") diff --git a/src/sage/meson.build b/src/sage/meson.build index d0cf55161b9..c90df69663f 100644 --- a/src/sage/meson.build +++ b/src/sage/meson.build @@ -89,7 +89,6 @@ py.install_sources( '__init__.py', 'all.py', 'all__sagemath_bliss.py', - 'all__sagemath_categories.py', 'all__sagemath_coxeter3.py', 'all__sagemath_environment.py', 'all__sagemath_mcqd.py', diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index fa14ec98696..41112d9203e 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -9,8 +9,7 @@ exists, forall, is_iterator, random_sublist, pad_zeros, - SAGE_DB, - newton_method_sizes, compose, + newton_method_sizes, compose, nest) from sage.misc.banner import version diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 29c013eab19..c85be325c3d 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -60,7 +60,7 @@ def _standard_libs_libdirs_incdirs_aliases(): if SAGE_LOCAL: standard_libdirs.append(os.path.join(SAGE_LOCAL, "lib")) standard_libdirs.extend(aliases["CBLAS_LIBDIR"] + aliases["NTL_LIBDIR"]) - standard_incdirs = sage_include_directories() + aliases["CBLAS_INCDIR"] + aliases["NTL_INCDIR"] + standard_incdirs = sage_include_directories(use_sources=True) + aliases["CBLAS_INCDIR"] + aliases["NTL_INCDIR"] return standard_libs, standard_libdirs, standard_incdirs, aliases ################################################################ diff --git a/src/sage/misc/fpickle.pyx b/src/sage/misc/fpickle.pyx index 2c6a336c328..a17a92f0636 100644 --- a/src/sage/misc/fpickle.pyx +++ b/src/sage/misc/fpickle.pyx @@ -101,10 +101,7 @@ def unpickle_function(pickled): def call_pickled_function(fpargs): - try: - import sage.all as toplevel - except ImportError: - import sage.all__sagemath_categories as toplevel + import sage.all as toplevel (fp, (args, kwds)) = fpargs f = eval("unpickle_function(fp)", toplevel.__dict__, {'fp': fp}) res = eval("f(*args, **kwds)", toplevel.__dict__, diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index 40fc5630d40..15f665b5df1 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -777,6 +777,13 @@ def integral(x, *args, **kwds): ... sage: result # needs sage.symbolic -1/4 + + Verify that :issue:`33034` is fixed:: + + sage: f(x) = (x + sin(3*x)) * exp(-3*x*I) + sage: h(x) = f(x) - f(x).expand() + sage: integral(h(x), (x, 0, 2*pi)) + 0 """ if hasattr(x, 'integral'): return x.integral(*args, **kwds) diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index 9d8fcd4a341..9863d8b7789 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -477,16 +477,22 @@ def has_latex_attr(x) -> bool: def default_engine(): """ Return the default latex engine and the official name of the engine. - This is determined by availability of the popular engines on the user's system. It is assumed that at least latex is available. + This function is deprecated as part of the public API. There is + instead an internal counterpart :func:`_default_engine`, but no + stability promises are made with regards to its interface. + EXAMPLES:: sage: from sage.misc.latex import default_engine sage: default_engine() # random ('lualatex', 'LuaLaTeX') """ + from sage.misc.superseded import deprecation + deprecation(39351, "default_engine is being removed from the public API and replaced with the internal function _default_engine") + from sage.features.latex import pdflatex, xelatex, lualatex if lualatex().is_present(): return 'lualatex', 'LuaLaTeX' @@ -497,6 +503,48 @@ def default_engine(): return 'latex', 'LaTeX' +@cached_function +def _default_engine(): + r""" + Return the name of the default latex engine. + + This is determined by availability of the popular engines on the + user's system. It is assumed that at least "latex" is available. + + EXAMPLES:: + + sage: from sage.misc.latex import _default_engine + sage: _default_engine() # random + 'lualatex' + + TESTS: + + Ensure that this (expensive) function is not necessary to obtain + the latex representation of a matrix (doing so probes the latex + options dict for the delimiters):: + + sage: import sage.misc.latex + sage: real_de = sage.misc.latex._default_engine + sage: def crash(): + ....: raise ValueError + sage: sage.misc.latex._default_engine = crash + sage: latex(matrix.identity(QQ, 2)) + \left(\begin{array}{rr} + 1 & 0 \\ + 0 & 1 + \end{array}\right) + sage: sage.misc.latex._default_engine = real_de + """ + from sage.features.latex import pdflatex, xelatex, lualatex + if lualatex().is_present(): + return 'lualatex' + if xelatex().is_present(): + return 'xelatex' + if pdflatex().is_present(): + return 'pdflatex' + return 'latex' + + class _Latex_prefs_object(SageObject): """ An object that holds LaTeX global preferences. @@ -520,6 +568,9 @@ def __init__(self, bb=False, delimiters=["(", ")"], self.__option["macros"] = "" self.__option["preamble"] = "" + # If None, the _default_engine() will be used. + self.__option["engine"] = None + @lazy_attribute def _option(self): """ @@ -528,18 +579,16 @@ def _option(self): EXAMPLES:: sage: from sage.misc.latex import _Latex_prefs_object - sage: _Latex_prefs_object()._option # random - {'blackboard_bold': False, - 'matrix_delimiters': ['(', ')'], - 'vector_delimiters': ['(', ')'], - 'matrix_column_alignment': 'r', - 'macros': '', - 'preamble': '', - 'engine': 'lualatex', - 'engine_name': 'LuaLaTeX'} + sage: sorted(_Latex_prefs_object()._option.items()) + [('blackboard_bold', False), + ('engine', None), + ('macros', ''), + ('matrix_column_alignment', 'r'), + ('matrix_delimiters', ['(', ')']), + ('preamble', ''), + ('vector_delimiters', ['(', ')'])] + """ - self.__option["engine"] = default_engine()[0] - self.__option["engine_name"] = default_engine()[1] return self.__option @@ -649,6 +698,8 @@ def _run_latex_(filename, debug=False, density=150, engine=None, png=False, do_i """ if engine is None: engine = _Latex_prefs._option["engine"] + if engine is None: + engine = _default_engine() if not engine or engine == "latex": from sage.features.latex import latex @@ -1062,10 +1113,12 @@ def eval(self, x, globals, strip=False, filename=None, debug=None, O.close() if engine is None: - if self.__engine is None: + engine = self.__engine + if engine is None: engine = _Latex_prefs._option["engine"] - else: - engine = self.__engine + if engine is None: + engine = _default_engine() + e = _run_latex_(os.path.join(base, filename + ".tex"), debug=debug, density=density, @@ -1531,23 +1584,17 @@ def engine(self, e=None): 'pdflatex' """ if e is None: - return _Latex_prefs._option["engine"] - - if e == "latex": - _Latex_prefs._option["engine"] = "latex" - _Latex_prefs._option["engine_name"] = "LaTeX" - elif e == "pdflatex": - _Latex_prefs._option["engine"] = "pdflatex" - _Latex_prefs._option["engine_name"] = "PDFLaTeX" - elif e == "xelatex": - _Latex_prefs._option["engine"] = e - _Latex_prefs._option["engine_name"] = "XeLaTeX" - elif e == "lualatex": - _Latex_prefs._option["engine"] = e - _Latex_prefs._option["engine_name"] = "LuaLaTeX" - else: + e = _Latex_prefs._option["engine"] + if e is None: + return _default_engine() + else: + return e + + if e not in ["latex", "pdflatex", "xelatex", "luatex"]: raise ValueError("%s is not a supported LaTeX engine. Use latex, pdflatex, xelatex, or lualatex" % e) + _Latex_prefs._option["engine"] = e + # Note: latex used to be a separate function, which by default was # only loaded in command-line mode: in the old notebook, @@ -1846,6 +1893,9 @@ def view(objects, title='Sage', debug=False, sep='', tiny=False, s = _latex_file_(objects, title=title, sep=sep, tiny=tiny, debug=debug, **latex_options) if engine is None: engine = _Latex_prefs._option["engine"] + if engine is None: + engine = _default_engine() + if viewer == "pdf" and engine == "latex": engine = "pdflatex" # command line or notebook with viewer @@ -1947,6 +1997,9 @@ def pdf(x, filename, tiny=False, tightpage=True, margin=None, engine=None, debug s = _latex_file_([x], title='', tiny=tiny, debug=debug, **latex_options) if engine is None: engine = _Latex_prefs._option["engine"] + if engine is None: + engine = _default_engine() + # path name for permanent pdf output abs_path_to_pdf = os.path.abspath(filename) # temporary directory to store stuff @@ -2007,6 +2060,9 @@ def png(x, filename, density=150, debug=False, extra_preamble='\\textheight=2\\textheight') if engine is None: engine = _Latex_prefs._option["engine"] + if engine is None: + engine = _default_engine() + # path name for permanent png output abs_path_to_png = os.path.abspath(filename) # temporary directory to store stuff diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index e5b3aed1ace..8005a14ee5a 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -50,11 +50,11 @@ from sage.env import DOT_SAGE, HOSTNAME from sage.misc.lazy_import import lazy_import -lazy_import("sage.combinat.subset", ["powerset", "subsets", "uniq"], - deprecation=35564) +lazy_import("sage.combinat.subset", ["powerset", "subsets", "uniq"], deprecation=35564) -lazy_import("sage.misc.timing", ["cputime", "GlobalCputime", "walltime"], - deprecation=35816) +lazy_import( + "sage.misc.timing", ["cputime", "GlobalCputime", "walltime"], deprecation=35816 +) LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) @@ -169,9 +169,6 @@ def try_read(obj, splitlines=False): return data -SAGE_DB = os.path.join(DOT_SAGE, 'db') -os.makedirs(SAGE_DB, exist_ok=True) - try: # Create the matplotlib config directory. os.makedirs(os.environ["MPLCONFIGDIR"], exist_ok=True) @@ -351,6 +348,7 @@ def nest(f, n, x): x """ from sage.rings.integer import Integer + n = Integer(n) if n < 0: @@ -365,6 +363,7 @@ def nest(f, n, x): # The A \ b operator ################################################################# + class BackslashOperator: r""" Implement Matlab-style backslash operator for solving systems:: @@ -389,6 +388,7 @@ class BackslashOperator: sage: preparse("A^3 \\ b") 'A**Integer(3) * BackslashOperator() * b' """ + def __rmul__(self, left): """ EXAMPLES:: @@ -412,6 +412,7 @@ def __rmul__(self, left): True """ from sage.misc.superseded import deprecation + deprecation(36394, 'the backslash operator has been deprecated') self.left = left return self @@ -443,6 +444,7 @@ def __mul__(self, right): (0.0, 0.5, 1.0, 1.5, 2.0) """ from sage.misc.superseded import deprecation + deprecation(36394, 'the backslash operator has been deprecated') return self.left._backslash_(right) @@ -531,6 +533,7 @@ def random_sublist(X, s): True """ import sage.misc.prandom as random + return [a for a in X if random.random() <= s] @@ -603,6 +606,7 @@ def some_tuples(elements, repeat, bound, max_samples=None): """ if max_samples is None: from itertools import islice, product + P = elements if repeat is None else product(elements, repeat=repeat) return islice(P, int(bound)) else: @@ -612,6 +616,7 @@ def some_tuples(elements, repeat, bound, max_samples=None): N = n if repeat is None else n**repeat if N <= max_samples: from itertools import product + return elements if repeat is None else product(elements, repeat=repeat) return _some_tuples_sampling(elements, repeat, max_samples, n) @@ -638,6 +643,7 @@ def _some_tuples_sampling(elements, repeat, max_samples, n): """ from sage.rings.integer import Integer import sage.misc.prandom as random + N = n if repeat is None else n**repeat # We sample on range(N) and create tuples manually since we don't want to create the list of all possible tuples in memory for a in random.sample(range(N), max_samples): @@ -651,6 +657,7 @@ def _some_tuples_sampling(elements, repeat, max_samples, n): # Misc. ################################################################# + def exists(S, P): """ If S contains an element x such that P(x) is ``True``, this function @@ -836,8 +843,8 @@ def in_quote(): # which is the case if the previous character isn't # a backslash, or it is but both previous characters # are backslashes. - if line[i - 1: i] != '\\' or line[i - 2: i] == '\\\\': - if line[i: i + 3] in ['"""', "'''"]: + if line[i - 1 : i] != '\\' or line[i - 2 : i] == '\\\\': + if line[i : i + 3] in ['"""', "'''"]: if not in_quote(): in_triple_quote = True elif in_triple_quote: @@ -899,6 +906,7 @@ def get_main_globals(): module. """ import sys + depth = 0 while True: G = sys._getframe(depth).f_globals @@ -957,8 +965,9 @@ def inject_variable(name, value, warn=True): # also from functions in various modules. G = get_main_globals() if name in G and warn: - warnings.warn("redefining global value `%s`" % name, - RuntimeWarning, stacklevel=2) + warnings.warn( + "redefining global value `%s`" % name, RuntimeWarning, stacklevel=2 + ) G[name] = value @@ -1013,12 +1022,14 @@ def run_once(func): sage: foo(False) sage: foo(True) """ + @functools.wraps(func) def wrapper(*args, **kwargs): if not wrapper.has_run: result = func(*args, **kwargs) wrapper.has_run = True return result + wrapper.has_run = False return wrapper diff --git a/src/sage/misc/package_dir.py b/src/sage/misc/package_dir.py index 3ef3adf73be..6b6ca12803f 100644 --- a/src/sage/misc/package_dir.py +++ b/src/sage/misc/package_dir.py @@ -246,8 +246,7 @@ def is_package_or_sage_namespace_package_dir(path, *, distribution_filter=None): Implicit namespace packages (PEP 420) are only recognized if they follow the conventions of the Sage library, i.e., the directory contains - a file ``all.py`` or a file matching the pattern ``all__*.py`` - such as ``all__sagemath_categories.py``. + a file ``all.py`` or a file matching the pattern ``all__*.py``. INPUT: diff --git a/src/sage/misc/persist.pyx b/src/sage/misc/persist.pyx index f7f83a696b8..dd08f6ffbbc 100644 --- a/src/sage/misc/persist.pyx +++ b/src/sage/misc/persist.pyx @@ -44,7 +44,7 @@ comp = zlib comp_other = bz2 from sage.misc.sage_unittest import TestSuite - +from sage.misc.superseded import deprecation # We define two global dictionaries `already_pickled` and # `already_unpickled`, which are intended to help you to implement @@ -1230,6 +1230,8 @@ def db(name): The database directory is ``$HOME/.sage/db``. """ + deprecation(39012, "Directly use pickle/unpickle instead of db/db_save.") + from sage.misc.misc import SAGE_DB return load('%s/%s' % (SAGE_DB, name)) @@ -1240,6 +1242,8 @@ def db_save(x, name=None): The database directory is ``$HOME/.sage/db``. """ + deprecation(39012, "Directly use pickle/unpickle instead of db/db_save.") + try: x.db(name) except AttributeError: diff --git a/src/sage/misc/randstate.pyx b/src/sage/misc/randstate.pyx index fe3351d126b..db700897568 100644 --- a/src/sage/misc/randstate.pyx +++ b/src/sage/misc/randstate.pyx @@ -790,7 +790,7 @@ cdef class randstate: """ global _pari_seed_randstate if _pari_seed_randstate is not self: - from sage.libs.pari.all import pari + from sage.libs.pari import pari if self._pari_saved_seed is not None: seed = self._pari_saved_seed diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 12f319fc8e3..4f7aed2820f 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -276,8 +276,10 @@ def _extract_embedded_position(docstring): from sage.misc.temporary_file import spyx_tmp if raw_filename.startswith('sage/'): import sage - try_filenames = [os.path.join(directory, raw_filename[5:]) + from sage.env import SAGE_SRC + try_filenames = [os.path.join(directory, raw_filename.removeprefix('sage/')) for directory in sage.__path__] + try_filenames.append(os.path.join(SAGE_SRC, raw_filename)) # meson editable install else: try_filenames = [] try_filenames.append( @@ -1286,6 +1288,15 @@ def sage_getfile(obj): sage: sage_getfile(P) # needs sage.libs.singular '...sage/rings/polynomial/multi_polynomial_libsingular...' + Another bug with editable meson install:: + + sage: P. = QQ[] + sage: I = P * [x,y] + sage: path = sage_getfile(I.groebner_basis); path + '.../sage/rings/qqbar_decorators.py' + sage: path == sage_getfile(sage.rings.qqbar_decorators) + True + A problem fixed in :issue:`16309`:: sage: cython( # needs sage.misc.cython @@ -1325,7 +1336,7 @@ def sage_getfile(obj): return '' for suffix in import_machinery.EXTENSION_SUFFIXES: if sourcefile.endswith(suffix): - return sourcefile[:-len(suffix)]+os.path.extsep+'pyx' + return sourcefile.removesuffix(suffix)+os.path.extsep+'pyx' return sourcefile diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index 280acec8874..ca814bc4373 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -42,6 +42,7 @@ from collections import deque from sage.arith.misc import gcd, xgcd, kronecker_symbol, fundamental_discriminant +from sage.interfaces.magma import magma from sage.matrix.constructor import Matrix from sage.matrix.matrix_space import MatrixSpace from sage.misc.cachefunc import cached_method @@ -66,7 +67,7 @@ lazy_import('sage.algebras.quatalg.quaternion_algebra', 'QuaternionAlgebra') lazy_import('sage.graphs.graph', 'Graph') -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.plot.colors', 'rainbow') lazy_import('sage.rings.number_field.number_field', 'NumberField') lazy_import('sage.rings.padics.factory', ['Qp', 'Zp']) @@ -2307,7 +2308,7 @@ def get_extra_embedding_matrices(self): [1 0 2 0] [0 0 2 0] [0 0 0 0] - [1 0 2 2] + [1 2 2 0] ] """ if not self._use_magma or len(self._extra_level) == 0: @@ -2700,7 +2701,7 @@ def get_splitting_field(self): sage: X = BruhatTitsQuotient(5,11,use_magma=True) # optional - magma sage: X.get_splitting_field() # optional - magma - Number Field in a with defining polynomial X1^2 + 11 + Number Field in a with defining polynomial x^2 + 11 """ if not self._use_magma: raise NotImplementedError('Sage does not know yet how to work with the kind of orders that you are trying to use. Try installing Magma first and set it up so that Sage can use it.') diff --git a/src/sage/modular/cusps.py b/src/sage/modular/cusps.py index 1dc3a2aaa22..6472ddd30f2 100644 --- a/src/sage/modular/cusps.py +++ b/src/sage/modular/cusps.py @@ -41,7 +41,8 @@ from sage.structure.richcmp import richcmp try: - from sage.libs.pari.all import pari, pari_gen + from sage.libs.pari import pari + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index f93984335bd..3d2ee75a7e7 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -62,7 +62,7 @@ import sage.rings.abc from sage.arith.functions import lcm -from sage.arith.misc import bernoulli, binomial, factorial, kronecker, factor, gcd, fundamental_discriminant, euler_phi, valuation +from sage.arith.misc import bernoulli, factorial, kronecker, factor, gcd, fundamental_discriminant, euler_phi, valuation from sage.categories.map import Map from sage.categories.objects import Objects from sage.categories.rings import Rings @@ -724,7 +724,7 @@ def bernoulli(self, k, algorithm='recurrence', cache=True, **opts): def S(n): return sum(v[r] * r**n for r in range(1, N)) - ber = sum(binomial(k, j) * bernoulli(j, **opts) * + ber = sum(ZZ(k).binomial(j) * bernoulli(j, **opts) * N**(j - 1) * S(k - j) for j in range(k + 1)) elif algorithm == "definition": # This is better since it computes the same thing, but requires diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 3fdabbfed7b..e94d38c9379 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -510,13 +510,18 @@ def _compute(self, X): return [q_exp[i] for i in X] def coefficients(self, X): - """ - The coefficients a_n of self, for integers n>=0 in the list - X. If X is an Integer, return coefficients for indices from 1 - to X. + r""" + Return the coefficients `a_n` of the `q`-expansion of this modular form. This function caches the results of the compute function. + INPUT: + + - ``X`` -- an iterator or an integer. If ``X`` is an iterator, a list + containing all `a_{X_i}` is returned. If ``X`` is an integer, it must + be positive, in which case the coefficients `a_1` to `a_X` are + returned in a list. + TESTS:: sage: e = DirichletGroup(11).gen() @@ -545,8 +550,8 @@ def coefficients(self, X): self.__coefficients except AttributeError: self.__coefficients = {} - if isinstance(X, Integer): - X = list(range(1, X + 1)) + if isinstance(X, (int, Integer)): + X = list(range(1, ZZ(X) + 1)) Y = [n for n in X if n not in self.__coefficients] v = self._compute(Y) for i in range(len(v)): @@ -2502,9 +2507,8 @@ def minimal_twist(self, p=None): h, tau = g.minimal_twist(p=None) M = chi.modulus().lcm(tau.modulus()) return (h, chi.extend(M)*tau.extend(M)) - else: - # f locally minimal at all p, hence globally minimal - return (self, DirichletGroup(1, self.base_ring())(1)) + # f locally minimal at all p, hence globally minimal + return (self, DirichletGroup(1, self.base_ring())(1)) p = ZZ(p) N = self.level() @@ -2739,6 +2743,60 @@ def __mul__(self, other): return newparent.base_extend(newqexp.base_ring())(newqexp) + def _pow_int(self, n): + """ + Raises ``self`` to integer powers. + + TESTS:: + + sage: F = ModularForms(1, 12).0 + sage: (F^5).qexp(20) + q^5 - 120*q^6 + 7020*q^7 - 266560*q^8 + 7379190*q^9 - 158562144*q^10 + 2748847640*q^11 - + 39443189760*q^12 + 476711357265*q^13 - 4910778324400*q^14 + 43440479153652*q^15 - + 331129448133120*q^16 + 2173189785854230*q^17 - 12199334429782080*q^18 + + 57636170473930920*q^19 + O(q^20) + sage: _ == (F.qexp(20)**5) + True + + Testing modular forms of nontrivial character:: + + sage: F = ModularForms(DirichletGroup(17).0^2, 2).2 + sage: F3 = F^3; F3 + q^3 + (-3*zeta8^2 + 6)*q^4 + (-12*zeta8^2 + 3*zeta8 + 18)*q^5 + O(q^6) + sage: F3.qexp(6) == F.qexp(6)^3 + True + sage: F3.character() == F.character()^3 + True + + Testing modular forms of level greater than 1:: + + sage: for F in ModularForms(Gamma0(4), 2).gens(): + ....: assert (F**5).qexp(10) == F.qexp(10)**5 + """ + # shamelessly copied from above + try: + eps = self.character() + verbose(f"character of self is {eps}") + newchar = eps ** n + verbose(f"character of product is {newchar}") + except (NotImplementedError, ValueError): + newchar = None + verbose("character of product not determined") + + from .constructor import ModularForms + if newchar is not None: + verbose("creating a parent with char") + newparent = ModularForms(newchar, self.weight() * n, + base_ring=newchar.base_ring()) + verbose(f"parent is {newparent}") + else: + newparent = ModularForms(self.group(), self.weight() * n, + base_ring=ZZ) + m = newparent.sturm_bound() + newqexp = self.qexp(m) ** n + + return newparent.base_extend(newqexp.base_ring())(newqexp) + def atkin_lehner_eigenvalue(self, d=None, embedding=None): """ Return the result of the Atkin-Lehner operator `W_d` on @@ -2953,7 +3011,7 @@ def _compute_element(self): """ M = self.parent() S = M.cuspidal_subspace() -# return S.find_in_space( self.__E.q_expansion( S.q_expansion_basis()[0].prec() ) ) + [0] * ( M.dimension() - S.dimension() ) + # return S.find_in_space( self.__E.q_expansion( S.q_expansion_basis()[0].prec() ) ) + [0] * ( M.dimension() - S.dimension() ) return vector(S.find_in_space(self.__E.q_expansion(S.sturm_bound())) + [0] * (M.dimension() - S.dimension())) def _compute_q_expansion(self, prec): @@ -3544,6 +3602,40 @@ def q_expansion(self, prec=None): qexp = q_expansion # alias + def coefficients(self, X): + r""" + Return the coefficients `a_n` of the `q`-expansion of this modular form. + + INPUT: + + - ``X`` -- an iterable or an integer. If ``X`` is iterable, a list + containing all `a_{X_i}` is returned. If ``X`` is an integer, it must + be positive, in which case the coefficients `a_1` to `a_X` are + returned in a list. + + EXAMPLES:: + + sage: M = ModularFormsRing(1) + sage: E4 = M.0; E6 = M.1 + sage: F = E4 + E6 + sage: F.coefficients([0,1,3,6]) + [2, -264, -116256, -3997728] + sage: F.coefficients(10) + [-264, -14472, -116256, -515208, -1545264, -3997728, -8388672, -16907400, -29701992, -51719472] + sage: assert _ == F.coefficients(range(1, 11)) == list(F.qexp(11))[1:] + + :: + + sage: F = ModularFormsRing(13).0 + sage: (F^3).coefficients(range(10, 20)) + [22812, 36552, 57680, 85686, 126744, 177408, 249246, 332172, 448926, 575736] + """ + if isinstance(X, (int, Integer)): + return list(self.q_expansion(X + 1))[1:X + 1] + prec = max(X) + v = self.q_expansion(prec + 1) + return [v[x] for x in X] + def _repr_(self): r""" The string representation of ``self``. @@ -3700,6 +3792,10 @@ def _mul_(self, other): sage: F4*F6 # indirect doctest 1 - 264*q - 135432*q^2 - 5196576*q^3 - 69341448*q^4 - 515625264*q^5 + O(q^6) + sage: E4 = EisensteinForms(1, 4).0 + sage: E4^2 + 1 + 480*q + 61920*q^2 + 1050240*q^3 + 7926240*q^4 + 37500480*q^5 + O(q^6) + This shows that the issue at :issue:`35932` is fixed:: sage: (F4 + M(1))^2 @@ -3896,7 +3992,7 @@ def _homogeneous_to_polynomial(self, names, gens): return poly_parent(self[k]) # create the set of "weighted exponents" and compute sturm bound - weights_of_generators = [gens[i].weight() for i in range(0, len(gens))] + weights_of_generators = [gen.weight() for gen in gens] W = WeightedIntegerVectors(k, weights_of_generators).list() sturm_bound = self.group().sturm_bound(k) @@ -3904,18 +4000,9 @@ def _homogeneous_to_polynomial(self, names, gens): matrix_datum = [] # form the matrix of coefficients and list the monomials of weight k - list_of_monomials = [] - for exponents in W: - monomial_form = M.one() - monomial_poly = poly_parent.one() - iter = 0 - for e, g in zip(exponents, gens): - monomial_form *= M(g) ** e - monomial_poly *= poly_parent.gen(iter) ** e - iter += 1 - matrix_datum.append(monomial_form[k].coefficients(range(0, sturm_bound + 1))) - list_of_monomials.append(monomial_poly) - + monomial_forms = [prod(M(gen) ** exp for exp, gen in zip(exps, gens)) for exps in W] + monomial_polys = [prod(poly_gen ** exp for exp, poly_gen in zip(exps, poly_parent.gens())) for exps in W] + matrix_datum = M._to_matrix(monomial_forms, prec=sturm_bound) mat = Matrix(matrix_datum).transpose() # initialize the column vector of the coefficients of self @@ -3926,8 +4013,8 @@ def _homogeneous_to_polynomial(self, names, gens): # initialize the polynomial associated to self poly = poly_parent.zero() - for iter, p in enumerate(list_of_monomials): - poly += soln[iter, 0] * p + for i, p in enumerate(monomial_polys): + poly += soln[i, 0] * p return poly def to_polynomial(self, names='x', gens=None): @@ -3954,8 +4041,9 @@ def to_polynomial(self, names='x', gens=None): sage: (M.0^10 + M.0 * M.1).to_polynomial() x0^10 + x0*x1 - This method is not necessarily the inverse of :meth:`~sage.modular.modform.find_generator.ModularFormsRing.from_polynomial` - since there may be some relations between the generators of the modular forms ring:: + This method is not necessarily the inverse of + :meth:`~sage.modular.modform.ring.ModularFormsRing.from_polynomial` since there may be some + relations between the generators of the modular forms ring:: sage: M = ModularFormsRing(Gamma0(6)) sage: P. = M.polynomial_ring() diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index dca6c86f125..d20088873c2 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -24,6 +24,7 @@ from random import shuffle from sage.categories.graded_algebras import GradedAlgebras +from sage.matrix.constructor import Matrix from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.misc.superseded import deprecated_function_alias @@ -1211,6 +1212,45 @@ def cuspidal_submodule_q_expansion_basis(self, weight, prec=None): R = G[0][1].parent() return [R(list(x), prec=prec) for x in W.gens()] + def _to_matrix(self, gens=None, prec=None): + r""" + Return a matrix corresponding to the `q`-expansion of the generators to the given precision. + + INPUT: + + - ``gens`` -- (default: ``None``) a list of generators. If not provided, + the list returned by :meth:`ModularFormsRing.gen_forms` + is used instead. + - ``prec`` -- (default: ``None``) precision to compute up to, or the Sturm + bound if not provided. + + OUTPUT: A matrix. + + TESTS:: + + sage: M = ModularFormsRing(1) + sage: E4 = M.0; E6 = M.1 + sage: gens = [E4^3, E6^2]; gens + [1 + 720*q + 179280*q^2 + 16954560*q^3 + 396974160*q^4 + 4632858720*q^5 + O(q^6), + 1 - 1008*q + 220752*q^2 + 16519104*q^3 + 399517776*q^4 + 4624512480*q^5 + O(q^6)] + sage: M._to_matrix(gens) + [ 1 720] + [ 1 -1008] + sage: M._to_matrix(gens, 6) + [ 1 720 179280 16954560 396974160 4632858720 34413301440] + [ 1 -1008 220752 16519104 399517776 4624512480 34423752384] + """ + if gens is None: + gens = self.gen_forms() + + if prec is None: + # we don't default to prec=6 because this is an internal function + # and is usually used to write other forms as a linear combination + # of generators, in which case using the Sturm bound is more reasonable + prec = max(gen.group().sturm_bound(gen.weight()) for gen in gens) + + return Matrix(gen.coefficients(range(prec + 1)) for gen in gens) + # Deprecated functions find_generators = deprecated_function_alias(31559, ModularFormsRing.generators) diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index f7d5f4b209b..8831d084640 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -828,7 +828,7 @@ cdef class P1List(): """ return len(self.__list) - def __repr__(self): + def __repr__(self) -> str: """ Return the string representation of this P1List. @@ -838,7 +838,7 @@ cdef class P1List(): sage: str(L) # indirect doctest 'The projective line over the integers modulo 8' """ - return "The projective line over the integers modulo %s" % self.__N + return f"The projective line over the integers modulo {self.__N}" def lift_to_sl2z(self, int i): r""" @@ -1369,22 +1369,3 @@ def lift_to_sl2z(c, d, N): if N <= 2147483647: return lift_to_sl2z_llong(c, d, N) raise NotImplementedError("N too large") - - -def _make_p1list(n): - """ - Helper function used in pickling. - - Not intended for end-users. - - EXAMPLES:: - - sage: from sage.modular.modsym.p1list import _make_p1list - sage: _make_p1list(3) - doctest:...: DeprecationWarning: _make_p1list() is deprecated - See https://github.com/sagemath/sage/issues/25848 for details. - The projective line over the integers modulo 3 - """ - from sage.misc.superseded import deprecation_cython as deprecation - deprecation(25848, '_make_p1list() is deprecated') - return P1List(n) diff --git a/src/sage/modular/multiple_zeta.py b/src/sage/modular/multiple_zeta.py index 239bc0b1ead..1e1bfb44ec6 100644 --- a/src/sage/modular/multiple_zeta.py +++ b/src/sage/modular/multiple_zeta.py @@ -195,7 +195,7 @@ from sage.rings.rational_field import QQ from sage.sets.positive_integers import PositiveIntegers -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') # multiplicative generators for weight <= 17 diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 9ed53a955c9..893b1dbf602 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -27,7 +27,7 @@ REFERENCES: # **************************************************************************** import operator -from sage.arith.misc import binomial, bernoulli +from sage.arith.misc import bernoulli from sage.categories.fields import Fields from sage.matrix.constructor import matrix from sage.matrix.matrix cimport Matrix @@ -1129,7 +1129,7 @@ cdef class Dist_vector(Dist): """ # assert self._moments[0][0]==0, "not total measure zero" # print("result accurate modulo p^",self.moment(0).valuation(self.p) ) - # v=[0 for j in range(0,i)]+[binomial(j,i)*bernoulli(j-i) for j in range(i,M)] + # v=[0 for j in range(i)]+[binomial(j,i)*bernoulli(j-i) for j in range(i,M)] M = self.precision_relative() R = self.parent().base_ring() K = R.fraction_field() @@ -1142,7 +1142,7 @@ cdef class Dist_vector(Dist): # bernoulli(1) = -1/2; the only nonzero odd Bernoulli number v[m] += m * minhalf * scalar for j in range(m - 1, M, 2): - v[j] += binomial(j, m - 1) * bern[(j - m + 1) // 2] * scalar + v[j] += ZZ(j).binomial(m - 1) * bern[(j - m + 1) // 2] * scalar p = self.parent().prime() cdef Dist_vector ans if p == 0: diff --git a/src/sage/modular/pollack_stevens/padic_lseries.py b/src/sage/modular/pollack_stevens/padic_lseries.py index 613ceef65b8..defacb1b357 100644 --- a/src/sage/modular/pollack_stevens/padic_lseries.py +++ b/src/sage/modular/pollack_stevens/padic_lseries.py @@ -121,7 +121,7 @@ def __init__(self, symb, gamma=None, quadratic_twist=1, precision=None): self._gamma = gamma self._quadratic_twist = quadratic_twist self._precision = precision - self._cinf = ZZ(1) # is set when called for an elliptic curve + self._cinf = ZZ.one() # is set when called for an elliptic curve def __getitem__(self, n): r""" @@ -373,7 +373,7 @@ def _basic_integral(self, a, j): ap = ap * kronecker(D, p) K = pAdicField(p, M) symb_twisted = symb.evaluate_twisted(a, D) - return sum(binomial(j, r) * + return sum(ZZ(j).binomial(r) * ((a - ZZ(K.teichmuller(a))) ** (j - r)) * (p ** r) * symb_twisted.moment(r) for r in range(j + 1)) / ap diff --git a/src/sage/modular/ssmod/ssmod.py b/src/sage/modular/ssmod/ssmod.py index 9422f33ca2e..37bcd9ea694 100644 --- a/src/sage/modular/ssmod/ssmod.py +++ b/src/sage/modular/ssmod/ssmod.py @@ -80,7 +80,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.richcmp import richcmp_method, richcmp -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') ZZy = PolynomialRing(ZZ, 'y') diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 471db14112b..ba8ac43f52c 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -998,6 +998,24 @@ def is_sparse(self): """ return self.__is_sparse + def is_exact(self): + """ + Test whether elements of this module are represented exactly. + + OUTPUT: + + Return ``True`` if elements of this module are represented exactly, i.e., + there is no precision loss when doing arithmetic. + + EXAMPLES:: + + sage: (ZZ^2).is_exact() + True + sage: (RR^2).is_exact() + False + """ + return self._base.is_exact() + def _an_element_(self): """ Return an arbitrary element of a free module. diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 617c53f8781..bd2f4eb7816 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -1055,7 +1055,7 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: pari(L) # needs sage.libs.pari [[0, 1, 2, 3], [0, 1, 8, 27], [0, 1, 32, 243]] """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari return pari(self.list()) def _pari_init_(self): diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx index 73917208342..168ade9c361 100644 --- a/src/sage/modules/with_basis/indexed_element.pyx +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -1020,6 +1020,16 @@ cdef class IndexedFreeModuleElement(ModuleElement): x_inv = B(x) ** -1 return type(self)(F, scal(x_inv, D)) + def _magma_init_(self, magma): + r""" + Convert ``self`` to Magma. + """ + # Get a reference to Magma version of parent. + R = magma(self.parent()).name() + # use dict {key: coefficient}. + return '+'.join(f"({c._magma_init_(magma)})*{R}.{m._magma_init_(magma)}" + for m, c in self.monomial_coefficients().items()) + def _unpickle_element(C, d): """ diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index c70ac0bb0e0..55c63a16353 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -2134,18 +2134,23 @@ cdef class GLPKBackend(GenericBackend): value = solver_parameter_values[value] if name == timelimit_intopt: - if value is None: return self.iocp.tm_lim - else: self.iocp.tm_lim = value + if value is None: + return self.iocp.tm_lim + else: + self.iocp.tm_lim = value if name == timelimit_seconds: - if value is None: return self.iocp.tm_lim / 1000.0 + if value is None: + return self.iocp.tm_lim / 1000.0 else: self.iocp.tm_lim = value * 1000.0 self.smcp.tm_lim = value * 1000.0 elif name == timelimit_simplex: - if value is None: return self.smcp.tm_lim - else: self.smcp.tm_lim = value + if value is None: + return self.smcp.tm_lim + else: + self.smcp.tm_lim = value elif name == simplex_or_intopt: if value is None: @@ -2155,155 +2160,227 @@ cdef class GLPKBackend(GenericBackend): self.simplex_or_intopt = value elif name == msg_lev_simplex: - if value is None: return self.smcp.msg_lev - else: self.smcp.msg_lev = value + if value is None: + return self.smcp.msg_lev + else: + self.smcp.msg_lev = value elif name == msg_lev_intopt: - if value is None: return self.iocp.msg_lev - else: self.iocp.msg_lev = value + if value is None: + return self.iocp.msg_lev + else: + self.iocp.msg_lev = value elif name == br_tech: - if value is None: return self.iocp.br_tech - else: self.iocp.br_tech = value + if value is None: + return self.iocp.br_tech + else: + self.iocp.br_tech = value elif name == bt_tech: - if value is None: return self.iocp.bt_tech - else: self.iocp.bt_tech = value + if value is None: + return self.iocp.bt_tech + else: + self.iocp.bt_tech = value elif name == pp_tech: - if value is None: return self.iocp.pp_tech - else: self.iocp.pp_tech = value + if value is None: + return self.iocp.pp_tech + else: + self.iocp.pp_tech = value elif name == fp_heur: - if value is None: return self.iocp.fp_heur + if value is None: + return self.iocp.fp_heur else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.fp_heur = value elif name == gmi_cuts: - if value is None: return self.iocp.gmi_cuts + if value is None: + return self.iocp.gmi_cuts else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.gmi_cuts = value elif name == mir_cuts: - if value is None: return self.iocp.mir_cuts + if value is None: + return self.iocp.mir_cuts else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.mir_cuts = value elif name == cov_cuts: - if value is None: return self.iocp.cov_cuts + if value is None: + return self.iocp.cov_cuts else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.cov_cuts = value elif name == clq_cuts: - if value is None: return self.iocp.clq_cuts + if value is None: + return self.iocp.clq_cuts else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.clq_cuts = value elif name == tol_int: - if value is None: return self.iocp.tol_int - else: self.iocp.tol_int = value + if value is None: + return self.iocp.tol_int + else: + self.iocp.tol_int = value elif name == tol_obj: - if value is None: return self.iocp.tol_obj - else: self.iocp.tol_obj = value + if value is None: + return self.iocp.tol_obj + else: + self.iocp.tol_obj = value elif name == mip_gap: - if value is None: return self.iocp.mip_gap - else: self.iocp.mip_gap = value + if value is None: + return self.iocp.mip_gap + else: + self.iocp.mip_gap = value elif name == tm_lim_intopt: - if value is None: return self.iocp.tm_lim - else: self.iocp.tm_lim = value + if value is None: + return self.iocp.tm_lim + else: + self.iocp.tm_lim = value elif name == out_frq_intopt: - if value is None: return self.iocp.out_frq - else: self.iocp.out_frq = value + if value is None: + return self.iocp.out_frq + else: + self.iocp.out_frq = value elif name == out_dly_intopt: - if value is None: return self.iocp.out_dly - else: self.iocp.out_dly = value + if value is None: + return self.iocp.out_dly + else: + self.iocp.out_dly = value elif name == presolve_intopt: - if value is None: return self.iocp.presolve + if value is None: + return self.iocp.presolve else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.presolve = value elif name == binarize: - if value is None: return self.iocp.binarize + if value is None: + return self.iocp.binarize else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.iocp.binarize = value elif name == msg_lev_simplex: - if value is None: return self.smcp.msg_lev - else: self.smcp.msg_lev = value + if value is None: + return self.smcp.msg_lev + else: + self.smcp.msg_lev = value elif name == meth: - if value is None: return self.smcp.meth - else: self.smcp.meth = value + if value is None: + return self.smcp.meth + else: + self.smcp.meth = value elif name == pricing: - if value is None: return self.smcp.pricing - else: self.smcp.pricing = value + if value is None: + return self.smcp.pricing + else: + self.smcp.pricing = value elif name == r_test: - if value is None: return self.smcp.r_test - else: self.smcp.r_test = value + if value is None: + return self.smcp.r_test + else: + self.smcp.r_test = value elif name == tol_bnd: - if value is None: return self.smcp.tol_bnd - else: self.smcp.tol_bnd = value + if value is None: + return self.smcp.tol_bnd + else: + self.smcp.tol_bnd = value elif name == tol_dj: - if value is None: return self.smcp.tol_dj - else: self.smcp.tol_dj = value + if value is None: + return self.smcp.tol_dj + else: + self.smcp.tol_dj = value elif name == tol_piv: - if value is None: return self.smcp.tol_piv - else: self.smcp.tol_piv = value + if value is None: + return self.smcp.tol_piv + else: + self.smcp.tol_piv = value elif name == obj_ll: - if value is None: return self.smcp.obj_ll - else: self.smcp.obj_ll = value + if value is None: + return self.smcp.obj_ll + else: + self.smcp.obj_ll = value elif name == obj_ul: - if value is None: return self.smcp.obj_ul - else: self.smcp.obj_ul = value + if value is None: + return self.smcp.obj_ul + else: + self.smcp.obj_ul = value elif name == it_lim: - if value is None: return self.smcp.it_lim - else: self.smcp.it_lim = value + if value is None: + return self.smcp.it_lim + else: + self.smcp.it_lim = value elif name == tm_lim_simplex: - if value is None: return self.smcp.tm_lim - else: self.smcp.tm_lim = value + if value is None: + return self.smcp.tm_lim + else: + self.smcp.tm_lim = value elif name == out_frq_simplex: - if value is None: return self.smcp.out_frq - else: self.smcp.out_frq = value + if value is None: + return self.smcp.out_frq + else: + self.smcp.out_frq = value elif name == out_dly_simplex: - if value is None: return self.smcp.out_dly - else: self.smcp.out_dly = value + if value is None: + return self.smcp.out_dly + else: + self.smcp.out_dly = value elif name == presolve_simplex: - if value is None: return self.smcp.presolve + if value is None: + return self.smcp.presolve else: - if value: value = GLP_ON - else: value = GLP_OFF + if value: + value = GLP_ON + else: + value = GLP_OFF self.smcp.presolve = value else: raise ValueError("This parameter is not available.") @@ -2322,7 +2399,7 @@ cdef class GLPKBackend(GenericBackend): EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='GLPK') + ....: solver='GLPK') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -2352,7 +2429,7 @@ cdef class GLPKBackend(GenericBackend): EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='GLPK') + ....: solver='GLPK') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -2383,7 +2460,7 @@ cdef class GLPKBackend(GenericBackend): EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='GLPK') + ....: solver='GLPK') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -2414,7 +2491,7 @@ cdef class GLPKBackend(GenericBackend): EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='GLPK') + ....: solver='GLPK') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 3519303a817..231e04a3e2c 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -1070,7 +1070,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -1100,7 +1100,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -1130,7 +1130,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -1160,7 +1160,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -1185,7 +1185,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) @@ -1218,7 +1218,7 @@ cdef class InteractiveLPBackend: EXAMPLES:: sage: p = MixedIntegerLinearProgram(maximization=True,\ - solver='InteractiveLP') + ....: solver='InteractiveLP') sage: x = p.new_variable(nonnegative=True) sage: p.add_constraint(-x[0] + x[1] <= 2) sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index cdcd3a524b3..38ff1316629 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -381,7 +381,7 @@ def minimize(func, x0, gradient=None, hessian=None, algorithm='default', ....: return sum(100.0r*(x[1r:]-x[:-1r]**2.0r)**2.0r + (1r-x[:-1r])**2.0r) sage: import numpy sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: from numpy import zeros sage: def rosen_der(x): ....: xm = x[1r:-1r] diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 02442f90ba4..c8a252cfcca 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -55,7 +55,7 @@ def get_minmax_data(self): sage: import numpy # to ensure numpy 2.0 compatibility sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: from sage.plot.arrow import CurveArrow sage: b = CurveArrow(path=[[(0,0),(.5,.5),(1,0)],[(.5,1),(0,0)]], ....: options={}) diff --git a/src/sage/plot/multigraphics.py b/src/sage/plot/multigraphics.py index 6ed26974c49..38ce09e9846 100644 --- a/src/sage/plot/multigraphics.py +++ b/src/sage/plot/multigraphics.py @@ -766,7 +766,7 @@ def _add_subplot(self, figure, index, **options): (0.2, 0.3, 0.4, 0.1) sage: import numpy # to ensure numpy 2.0 compatibility sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: ax1.get_position().bounds # tol 1.0e-13 (0.2, 0.3, 0.4000000000000001, 0.10000000000000003) """ @@ -1269,7 +1269,7 @@ def position(self, index): sage: G = graphics_array([g1, g2]) sage: import numpy # to ensure numpy 2.0 compatibility sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: G.position(0) # tol 5.0e-3 (0.025045451349937315, 0.03415488992713045, diff --git a/src/sage/plot/streamline_plot.py b/src/sage/plot/streamline_plot.py index 2801446433a..f995cd71f93 100644 --- a/src/sage/plot/streamline_plot.py +++ b/src/sage/plot/streamline_plot.py @@ -72,7 +72,7 @@ def get_minmax_data(self): sage: x, y = var('x y') sage: import numpy # to ensure numpy 2.0 compatibility sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: d = streamline_plot((.01*x, x+y), (x,10,20), (y,10,20))[0].get_minmax_data() sage: d['xmin'] 10.0 diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index 202da0652ff..a6707c242f5 100644 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -60,7 +60,8 @@ try: - from sage.libs.pari.all import pari_gen, pari + from sage.libs.pari import pari + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/quadratic_forms/quadratic_form__automorphisms.py b/src/sage/quadratic_forms/quadratic_form__automorphisms.py index 4fa4d4fe1ff..b19c7939252 100644 --- a/src/sage/quadratic_forms/quadratic_form__automorphisms.py +++ b/src/sage/quadratic_forms/quadratic_form__automorphisms.py @@ -181,7 +181,7 @@ def short_vector_list_up_to_length(self, len_bound, up_to_sign_flag=False): raise ValueError("Quadratic form must be positive definite " "in order to enumerate short vectors") - from sage.libs.pari.all import pari + from sage.libs.pari import pari if len_bound <= 0: return [] diff --git a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py index a8265a5bbe1..efa0143d970 100644 --- a/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py +++ b/src/sage/quadratic_forms/quadratic_form__ternary_Tornaria.py @@ -536,7 +536,7 @@ def representation_number_list(self, B): sage: Q.representation_number_list(10) # needs sage.libs.pari [1, 16, 112, 448, 1136, 2016, 3136, 5504, 9328, 12112] """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari ans = pari(1).concat(self.__pari__().qfrep(B - 1, 1) * 2) return ans.sage() diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index fb9d821a413..704ea939d1c 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -168,6 +168,7 @@ from sage.libs.flint.acb_hypgeom cimport * from sage.libs.flint.acb_elliptic cimport * from sage.libs.flint.acb_modular cimport * from sage.libs.flint.acb_poly cimport * +from sage.libs.flint.acb_dirichlet cimport * from sage.libs.flint.arf cimport arf_init, arf_get_d, arf_get_mpfr, arf_clear, arf_set, arf_is_nan from sage.libs.flint.mag cimport (mag_init, mag_clear, mag_set_d, MAG_BITS, mag_zero, mag_set_ui_2exp_si, @@ -1184,13 +1185,10 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): sage: ComplexBallField(100).integral(lambda x, _: sin(x), RBF(0), RBF(1)) [0.4596976941318602825990633926 +/- ...e-29] - sage: from cysignals.alarm import alarm - sage: alarm(0.1r) - sage: C = ComplexBallField(1000000) - sage: C.integral(lambda x, _: x.cos() * x.sin(), 0, 1) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.1): + ....: C = ComplexBallField(1000000) + ....: C.integral(lambda x, _: x.cos() * x.sin(), 0, 1) """ cdef IntegrationContext ctx = IntegrationContext() cdef acb_calc_integrate_opt_t arb_opts @@ -1261,6 +1259,70 @@ class ComplexBallField(UniqueRepresentation, sage.rings.abc.ComplexBallField): return res + def zeta_zeros(self, count, start=1): + r""" + Compute consecutive zeros of the Riemann zeta function. + + INPUT: + + - ``count`` -- positive integer; number of zeros to be computed, must fit in a machine integer + + - ``start`` -- positive integer (default: 1); index of the first zero to be computed + + OUTPUT: + + A list of ``count`` consecutive zeros of the Riemann zeta function, starting from the ``start``-th zero. + Indexing starts at one, following usual mathematical notations. + + EXAMPLES:: + + sage: CBF.zeta_zeros(10) + [0.5000000000000000 + [14.134725141734...]*I, + 0.5000000000000000 + [21.0220396387715...]*I, + 0.5000000000000000 + [25.010857580145...]*I, + 0.5000000000000000 + [30.4248761258595...]*I, + 0.5000000000000000 + [32.935061587739...]*I, + 0.5000000000000000 + [37.586178158825...]*I, + 0.5000000000000000 + [40.918719012147...]*I, + 0.5000000000000000 + [43.32707328091...]*I, + 0.5000000000000000 + [48.005150881167...]*I, + 0.5000000000000000 + [49.773832477672...]*I] + + sage: CBF.zeta_zeros(6, start=5) + [0.5000000000000000 + [32.935061587739...]*I, + 0.5000000000000000 + [37.586178158825...]*I, + 0.5000000000000000 + [40.918719012147...]*I, + 0.5000000000000000 + [43.32707328091...]*I, + 0.5000000000000000 + [48.005150881167...]*I, + 0.5000000000000000 + [49.773832477672...]*I] + """ + cdef fmpz_t _start + fmpz_init(_start) + fmpz_set_mpz(_start, ( Integer(start)).value) + + cdef long _count = count + if _count < 1: + raise ValueError("count must be positive") + + cdef acb_ptr ar = _acb_vec_init(_count) + + sig_on() + acb_dirichlet_zeta_zeros(ar, _start, _count, self._prec) + sig_off() + + res = [] + cdef ComplexBall b + for i in range(_count): + b = ComplexBall.__new__(ComplexBall) + b._parent = self + acb_swap(b.value, &ar[i]) + res.append(b) + + _acb_vec_clear(ar, _count) + fmpz_clear(_start) + + return res + cdef inline bint _do_sig(long prec) noexcept: """ diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 98efcb0b27b..64295a773ad 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -74,7 +74,9 @@ from sage.structure.richcmp cimport rich_to_bool from sage.categories.map cimport Map try: - from sage.libs.pari.all import pari, pari_gen, PariError + from sage.libs.pari import pari + from cypari2.handle_error import PariError + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = PariError = () diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index d24e61d2823..720ec37b969 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -56,9 +56,10 @@ cimport gmpy2 gmpy2.import_gmpy2() try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen + from cypari2.handle_error import PariError except ImportError: - pari_gen = () + pari_gen = PariError = () # Some objects that are not imported at startup in order to break # circular imports @@ -1434,7 +1435,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ if self.is_real(): return self.real().__pari__() - return sage.libs.pari.all.pari.complex(self.real() or 0, self.imag()) + return sage.libs.pari.pari.complex(self.real() or 0, self.imag()) def __mpc__(self): """ @@ -2401,7 +2402,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ try: return self._parent(self.__pari__().eta(not omit_frac)) - except sage.libs.pari.all.PariError: + except PariError: raise ValueError("value must be in the upper half plane") def sin(self): @@ -2878,7 +2879,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ try: return self._parent(self.__pari__().gamma()) - except sage.libs.pari.all.PariError: + except PariError: from sage.rings.infinity import UnsignedInfinityRing return UnsignedInfinityRing.gen() diff --git a/src/sage/rings/factorint_pari.pyx b/src/sage/rings/factorint_pari.pyx index a8d748c5845..1774cbf0886 100644 --- a/src/sage/rings/factorint_pari.pyx +++ b/src/sage/rings/factorint_pari.pyx @@ -16,7 +16,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.rings.integer cimport Integer @@ -50,10 +50,11 @@ def factor_using_pari(n, int_=False, debug_level=0, proof=None): Check that PARI's debug level is properly reset (:issue:`18792`):: - sage: alarm(0.5); factor(2^1000 - 1, verbose=5) - Traceback (most recent call last): + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): factor(2^1000 - 1, verbose=5) ... - AlarmInterrupt + doctest:warning... + RuntimeWarning: cypari2 leaked ... bytes on the PARI stack sage: pari.get_debug_level() 0 """ diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 6ac8470d4c9..5da0dfded3f 100644 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -87,14 +87,13 @@ cdef class FiniteRingElement(CommutativeRingElement): gcd = n.gcd(q-1) if self.is_one(): if gcd == 1: - if all: return [self] - else: return self - else: - nthroot = K.zeta(gcd) - return [nthroot**a for a in range(gcd)] if all else nthroot + return [self] if all else self + nthroot = K.zeta(gcd) + return [nthroot**a for a in range(gcd)] if all else nthroot if gcd == q-1: - if all: return [] - else: raise ValueError("no nth root") + if all: + return [] + raise ValueError("no nth root") gcd, alpha, _ = n.xgcd(q-1) # gcd = alpha*n + beta*(q-1), so 1/n = alpha/gcd (mod q-1) if gcd == 1: return [self**alpha] if all else self**alpha @@ -102,8 +101,9 @@ cdef class FiniteRingElement(CommutativeRingElement): n = gcd q1overn = (q-1)//n if self**q1overn != 1: - if all: return [] - else: raise ValueError("no nth root") + if all: + return [] + raise ValueError("no nth root") self = self**alpha if cunningham: from sage.rings.factorint import factor_cunningham @@ -133,12 +133,11 @@ cdef class FiniteRingElement(CommutativeRingElement): if all: nthroot = K.zeta(n) L = [self] - for i in range(1,n): + for i in range(1, n): self *= nthroot L.append(self) return L - else: - return self + return self else: raise ValueError("unknown algorithm") @@ -240,10 +239,11 @@ cdef class FinitePolyExtElement(FiniteRingElement): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self.parent().prime_subfield(), var) return R(self.__pari__().minpoly('x').lift()) - elif algorithm == 'matrix': + + if algorithm == 'matrix': return self.matrix().minpoly(var) - else: - raise ValueError("unknown algorithm '%s'" % algorithm) + + raise ValueError("unknown algorithm '%s'" % algorithm) # We have two names for the same method # for compatibility with sage.matrix @@ -502,8 +502,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): """ if self.parent().degree()>1: return self.polynomial()._latex_() - else: - return str(self) + return str(self) def __pari__(self, var=None): r""" @@ -623,10 +622,11 @@ cdef class FinitePolyExtElement(FiniteRingElement): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(self.parent().prime_subfield(), var) return R(self.__pari__().charpoly('x').lift()) - elif algorithm == 'matrix': + + if algorithm == 'matrix': return self.matrix().charpoly(var) - else: - raise ValueError("unknown algorithm '%s'" % algorithm) + + raise ValueError("unknown algorithm '%s'" % algorithm) def norm(self): """ @@ -656,10 +656,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): """ f = self.charpoly('x') n = f[0] - if f.degree() % 2: - return -n - else: - return n + return -n if f.degree() % 2 else n def trace(self): """ @@ -921,20 +918,22 @@ cdef class FinitePolyExtElement(FiniteRingElement): """ if self.is_zero(): if n <= 0: - if all: return [] - else: raise ValueError - if all: return [self] - else: return self + if all: + return [] + raise ValueError + return [self] if all else self if n < 0: self = ~self n = -n elif n == 0: if self == 1: - if all: return [a for a in self.parent().list() if a != 0] - else: return self + if all: + return [a for a in self.parent().list() if a != 0] + return self else: - if all: return [] - else: raise ValueError + if all: + return [] + raise ValueError if extend: raise NotImplementedError n = Integer(n) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 2c1c25254c4..7d7abe463e1 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -40,7 +40,7 @@ from sage.structure.element cimport Vector from sage.rings.finite_rings.finite_field_base cimport FiniteField -from sage.libs.pari.all import pari +from sage.libs.pari import pari from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack diff --git a/src/sage/rings/finite_rings/finite_field_givaro.py b/src/sage/rings/finite_rings/finite_field_givaro.py index ea40970b276..b1dfe8be331 100644 --- a/src/sage/rings/finite_rings/finite_field_givaro.py +++ b/src/sage/rings/finite_rings/finite_field_givaro.py @@ -22,7 +22,7 @@ from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.integer import Integer from sage.rings.finite_rings.element_givaro import Cache_givaro -from sage.libs.pari.all import pari +from sage.libs.pari import pari class FiniteField_givaro(FiniteField): diff --git a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py index 92404849b4e..7e051bc8b74 100644 --- a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py +++ b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py @@ -16,7 +16,7 @@ #***************************************************************************** from sage.rings.finite_rings.finite_field_base import FiniteField -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.rings.integer import Integer diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 19525b40937..b963fed101e 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -87,7 +87,8 @@ from sage.arith.long cimport ( import sage.rings.rational as rational try: - from sage.libs.pari.all import pari, PariError + from sage.libs.pari import pari + from cypari2.handle_error import PariError except ImportError: class PariError(Exception): pass @@ -1505,17 +1506,18 @@ cdef class IntegerMod_abstract(FiniteRingElement): n = Integer(n) if n == 0: if self == 1: - if all: return [K(a) for a in range(1,K.order())] - else: return self + if all: + return [K(a) for a in range(1, K.order())] + return self else: - if all: return [] - else: raise ValueError + if all: + return [] + raise ValueError F = K.factored_order() if len(F) == 0: if all: return [self] - else: - return self + return self if len(F) != 1: if all: # we should probably do a first pass to see if there are any solutions so that we don't get giant intermediate lists and waste time... @@ -1533,33 +1535,31 @@ cdef class IntegerMod_abstract(FiniteRingElement): p, k = F[0] if self.is_zero(): if n < 0: - if all: return [] - else: raise ValueError + if all: + return [] + raise ValueError if all: if k == 1: return [self] - else: - minval = max(1, (k/n).ceil()) - return [K(a*p**minval) for a in range(p**(k-minval))] - else: - return self + minval = max(1, (k/n).ceil()) + return [K(a*p**minval) for a in range(p**(k-minval))] + return self if n < 0: try: self = ~self except ZeroDivisionError: - if all: return [] - else: raise ValueError + if all: + return [] + raise ValueError n = -n if p == 2 and k == 1: - if all: return [self] - else: return self + return [self] if all else self if k > 1: pval, upart = self.lift().val_unit(p) if not n.divides(pval): if all: return [] - else: - raise ValueError("no nth root") + raise ValueError("no nth root") if pval > 0: if all: return [K(a.lift()*p**(pval // n) + p**(k - (pval - pval//n)) * b) for a in mod(upart, p**(k-pval)).nth_root(n, all=True, algorithm=algorithm) for b in range(p**(pval - pval//n))] @@ -1571,27 +1571,30 @@ cdef class IntegerMod_abstract(FiniteRingElement): sign = [1] if self % 4 == 3: if n % 2 == 0: - if all: return [] - else: raise ValueError("no nth root") + if all: + return [] + raise ValueError("no nth root") else: sign = [-1] self = -self elif n % 2 == 0: if k > 2 and self % 8 == 5: - if all: return [] - else: raise ValueError("no nth root") + if all: + return [] + raise ValueError("no nth root") sign = [1, -1] if k == 2: - if all: return [K(s) for s in sign[:2]] - else: return K(sign[0]) - if all: modp = [mod(self,8)] - else: modp = mod(self,8) + if all: + return [K(s) for s in sign[:2]] + return K(sign[0]) + modp = [mod(self, 8)] if all else mod(self, 8) else: sign = [1] modp = self % p self = self / K(R.teichmuller(modp)) modp = modp.nth_root(n, all=all, algorithm=algorithm) - # now self is congruent to 1 mod 4 or 1 mod p (for odd p), so the power series for p-adic log converges. + # now self is congruent to 1 mod 4 or 1 mod p (for odd p), + # so the power series for p-adic log converges. # Hensel lifting is probably better, but this is easier at the moment. plog = R(self).log() nval = n.valuation(p) @@ -1599,11 +1602,11 @@ cdef class IntegerMod_abstract(FiniteRingElement): if self == 1: if all: return [s*K(p*a+m.lift()) for a in range(p**(k-(2 if p==2 else 1))) for m in modp for s in sign] - else: - return K(modp.lift()) + return K(modp.lift()) else: - if all: return [] - else: raise ValueError("no nth root") + if all: + return [] + raise ValueError("no nth root") if all: ans = [plog // n + p**(k - nval) * i for i in range(p**nval)] ans = [s*K(R.teichmuller(m) * a.exp()) for a in ans for m in modp for s in sign] @@ -3193,10 +3196,12 @@ cdef int_fast32_t mod_pow_int(int_fast32_t base, int_fast32_t exp, int_fast32_t if exp == 4: return (prod * prod) % n pow2 = base - if exp % 2: prod = base - else: prod = 1 + if exp % 2: + prod = base + else: + prod = 1 exp = exp >> 1 - while(exp != 0): + while exp != 0: pow2 = pow2 * pow2 if pow2 >= INTEGER_MOD_INT32_LIMIT: pow2 = pow2 % n if exp % 2: @@ -3855,10 +3860,12 @@ cdef int_fast64_t mod_pow_int64(int_fast64_t base, int_fast64_t exp, int_fast64_ if exp == 4: return (prod * prod) % n pow2 = base - if exp % 2: prod = base - else: prod = 1 + if exp % 2: + prod = base + else: + prod = 1 exp = exp >> 1 - while(exp != 0): + while exp != 0: pow2 = pow2 * pow2 if pow2 >= INTEGER_MOD_INT64_LIMIT: pow2 = pow2 % n if exp % 2: diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 6d5b7c6b36c..70ab41447b4 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -76,7 +76,8 @@ import sage.rings.quotient_ring as quotient_ring try: - from sage.libs.pari.all import pari, PariError + from sage.libs.pari import pari + from cypari2.handle_error import PariError except ImportError: class PariError(Exception): pass diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 4e3e923c263..60ba0a5763f 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -692,7 +692,7 @@ def _element_constructor_(self, x, y=None, coerce=True): y = py_scalar_to_element(y) try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/rings/infinity.py b/src/sage/rings/infinity.py index 986b32cb5d5..70208389fa4 100644 --- a/src/sage/rings/infinity.py +++ b/src/sage/rings/infinity.py @@ -334,7 +334,8 @@ def __pari__(self): sage: pari(oo) # needs sage.libs.pari +oo """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari + if self._sign >= 0: return pari('oo') else: diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index da004072ccd..4304f2d25d2 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -595,7 +595,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: # needs numpy sage: import numpy sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: numpy.int8('12') == 12 True sage: 12 == numpy.int8('12') @@ -7108,21 +7108,16 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): Check that it can be interrupted (:issue:`17852`):: - sage: alarm(0.5); (2^100).binomial(2^22, algorithm='mpir') - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): (2^100).binomial(2^22, algorithm='mpir') For PARI, we try 10 interrupts with increasing intervals to check for reliable interrupting, see :issue:`18919`:: sage: from cysignals import AlarmInterrupt sage: for i in [1..10]: # long time (5s) # needs sage.libs.pari - ....: try: - ....: alarm(i/11) + ....: with ensure_interruptible_after(i/11): ....: (2^100).binomial(2^22, algorithm='pari') - ....: except AlarmInterrupt: - ....: pass doctest:...: RuntimeWarning: cypari2 leaked ... bytes on the PARI stack... """ cdef Integer x diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index f40ec694493..953c9ed5a9a 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -46,7 +46,7 @@ from sage.structure.unique_representation import UniqueRepresentation try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () @@ -407,6 +407,33 @@ def _repr_(self): s = 'Sparse ' + s return s + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: R = LaurentSeriesRing(QQ, 'y') + sage: R._magma_init_(magma) + 'SageCreateWithNames(LaurentSeriesRing(_sage_ref...),["y"])' + sage: S = magma(R) + sage: S + Laurent series field in y over Rational Field + sage: S.1 + y + sage: S.sage() == R + True + + sage: # optional - magma + sage: magma(LaurentSeriesRing(GF(7), 'x')) # needs sage.rings.finite_rings + Laurent series field in x over GF(7) + """ + B = magma(self.base_ring()) + Bref = B._ref() + s = 'LaurentSeriesRing(%s)' % (Bref) + return magma._with_names(s, self.variable_names()) + def _element_constructor_(self, x, n=0, prec=infinity): r""" Construct a Laurent series from `x`. diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 43f446a7e09..bd5cbf70a7e 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -59,11 +59,11 @@ sage: z = H.base_ring().gens() sage: f = H.gens() sage: h = 4*z[1]^2 + 2*z[0]*z[2] + z[1]*z[2] + z[2]^2 \ - + (-z[2]^2 - 2*z[0] + z[2])*f[0]*f[2] \ - + (-22*z[0]^2 + 2*z[1]^2 - z[0]*z[2] + z[2]^2 - 1955*z[2])*f[1]*f[2] \ - + (-z[0]*z[1] - 2*z[1]^2)*f[2]*f[3] \ - + (2*z[0]*z[1] + z[1]*z[2] - z[2]^2 - z[1] + 3*z[2])*f[3]^2 \ - + H.O(3) + ....: + (-z[2]^2 - 2*z[0] + z[2])*f[0]*f[2] \ + ....: + (-22*z[0]^2 + 2*z[1]^2 - z[0]*z[2] + z[2]^2 - 1955*z[2])*f[1]*f[2] \ + ....: + (-z[0]*z[1] - 2*z[1]^2)*f[2]*f[3] \ + ....: + (2*z[0]*z[1] + z[1]*z[2] - z[2]^2 - z[1] + 3*z[2])*f[3]^2 \ + ....: + H.O(3) sage: h in H True sage: h diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index 5f8bba84c49..00924fa3eae 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -1150,7 +1150,7 @@ def polynomial(self): Field sage: t = M.gens() sage: f = 1/2*t[0]^3*t[1]^3*t[2]^2 + 2/3*t[0]*t[2]^6*t[3] \ - - t[0]^3*t[1]^3*t[3]^3 - 1/4*t[0]*t[1]*t[2]^7 + M.O(10) + ....: - t[0]^3*t[1]^3*t[3]^3 - 1/4*t[0]*t[1]*t[2]^7 + M.O(10) sage: f 1/2*t0^3*t1^3*t2^2 + 2/3*t0*t2^6*t3 - t0^3*t1^3*t3^3 - 1/4*t0*t1*t2^7 + O(t0, t1, t2, t3)^10 @@ -1299,7 +1299,7 @@ def V(self, n): sage: H = QQ[['x,y,z']] sage: (x,y,z) = H.gens() sage: h = -x*y^4*z^7 - 1/4*y*z^12 + 1/2*x^7*y^5*z^2 \ - + 2/3*y^6*z^8 + H.O(15) + ....: + 2/3*y^6*z^8 + H.O(15) sage: h.V(3) -x^3*y^12*z^21 - 1/4*y^3*z^36 + 1/2*x^21*y^15*z^6 + 2/3*y^18*z^24 + O(x, y, z)^45 """ @@ -1382,7 +1382,7 @@ def truncate(self, prec=infinity): Multivariate Power Series Ring in t0, t1, t2, t3 over Rational Field sage: t = M.gens() sage: f = 1/2*t[0]^3*t[1]^3*t[2]^2 + 2/3*t[0]*t[2]^6*t[3] \ - - t[0]^3*t[1]^3*t[3]^3 - 1/4*t[0]*t[1]*t[2]^7 + M.O(10) + ....: - t[0]^3*t[1]^3*t[3]^3 - 1/4*t[0]*t[1]*t[2]^7 + M.O(10) sage: f 1/2*t0^3*t1^3*t2^2 + 2/3*t0*t2^6*t3 - t0^3*t1^3*t3^3 - 1/4*t0*t1*t2^7 + O(t0, t1, t2, t3)^10 diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index bb4e453c650..d539d60d25b 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -16,7 +16,7 @@ from sage.misc.superseded import deprecation from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.rings.infinity import infinity from sage.rings.number_field.number_field import refine_embedding from sage.rings.number_field.morphism import NumberFieldHomomorphism_im_gens diff --git a/src/sage/rings/number_field/maps.py b/src/sage/rings/number_field/maps.py index 8d8eb1b69ef..00422d022f3 100644 --- a/src/sage/rings/number_field/maps.py +++ b/src/sage/rings/number_field/maps.py @@ -45,7 +45,7 @@ import sage.rings.rational_field as rational_field -from sage.libs.pari.all import pari +from sage.libs.pari import pari QQ = rational_field.RationalField() diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 05371850c83..68031c3d642 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -211,7 +211,8 @@ def proof_flag(t): from . import number_field_element from . import number_field_element_quadratic from .number_field_ideal import NumberFieldIdeal, NumberFieldFractionalIdeal -from sage.libs.pari.all import pari, pari_gen +from sage.libs.pari import pari +from cypari2.gen import Gen as pari_gen from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 30fc8326917..e0d16090cc7 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -119,7 +119,7 @@ def __init__(self, field, gens, coerce=True): if len(gens) == 1 and isinstance(gens[0], (list, tuple)): gens = gens[0] - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen if len(gens) == 1 and isinstance(gens[0], pari_gen): # Init from PARI gens = gens[0] @@ -2646,7 +2646,7 @@ def _pari_bid_(self, flag=1): sage: bid.getattr('clgp') [2, [2]] """ - from sage.libs.pari.all import PariError + from cypari2.handle_error import PariError try: bid = self._bid if flag == 2: diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 970707f2457..dba7f67257d 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -101,7 +101,7 @@ from sage.rings.number_field.order import (RelativeOrder, relative_order_from_ring_generators) from sage.rings.number_field.morphism import RelativeNumberFieldHomomorphism_from_abs -from sage.libs.pari.all import pari_gen +from cypari2.gen import Gen as pari_gen from sage.categories.homset import Hom from sage.categories.sets_cat import Sets @@ -1654,7 +1654,7 @@ def _pari_relative_structure(self): elif f.poldegree() == 1: # PARI's rnfpolredbest() does not always return a # polynomial with integral coefficients in this case. - from sage.libs.pari.all import pari + from sage.libs.pari import pari g = f.variable() alpha = -f[0]/f[1] beta = pari(0).Mod(f) diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index fd5662048df..fe33c456ead 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -91,7 +91,7 @@ from sage.rings.monomials import monomials -from sage.libs.pari.all import pari +from sage.libs.pari import pari def quadratic_order_class_number(disc): diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index ff30a006bba..541130e8a71 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -134,7 +134,7 @@ def __init__(self, field, num_integer_primes=10000, max_iterations=100): self._lc = self._poly.leading_coefficient() # this uses that [ O_K : Z[a] ]^2 = | disc(f(x)) / disc(O_K) | - from sage.libs.pari.all import pari + from sage.libs.pari import pari self._prod_of_small_primes = ZZ(pari('TEMPn = %s; TEMPps = primes(TEMPn); prod(X = 1, TEMPn, TEMPps[X])' % num_integer_primes)) self._prod_of_small_primes //= self._prod_of_small_primes.gcd(self._poly.discriminant() * self._lc) diff --git a/src/sage/rings/number_field/splitting_field.py b/src/sage/rings/number_field/splitting_field.py index 28c5486f171..aa8e5cb1ed4 100644 --- a/src/sage/rings/number_field/splitting_field.py +++ b/src/sage/rings/number_field/splitting_field.py @@ -22,7 +22,8 @@ from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.rational_field import RationalField -from sage.libs.pari.all import pari, PariError +from sage.libs.pari import pari +from cypari2.handle_error import PariError class SplittingFieldAbort(Exception): diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 14e55237dd1..928f65f1e1c 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -89,7 +89,7 @@ import math import sys from sage.libs.gmp.mpz cimport * -from sage.libs.pari.all import pari +from sage.libs.pari import pari from cypari2.gen cimport Gen as pari_gen from sage.libs.pari.misc cimport new_t_POL_from_int_star diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index ac3f52a544c..e7b2829e7a4 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -100,7 +100,7 @@ from sage.rings.number_field.number_field import NumberField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.number_field.totallyreal import weed_fields, odlyzko_bound_totallyreal, enumerate_totallyreal_fields_prim -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ diff --git a/src/sage/rings/number_field/unit_group.py b/src/sage/rings/number_field/unit_group.py index eb6b46ed82e..d6ee8be1e94 100644 --- a/src/sage/rings/number_field/unit_group.py +++ b/src/sage/rings/number_field/unit_group.py @@ -167,7 +167,7 @@ from sage.groups.abelian_gps.values import AbelianGroupWithValues_class from sage.structure.proof.proof import get_flag -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 0e826c40db8..3373413adda 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -183,7 +183,7 @@ from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ from sage.libs.ntl.ntl_ZZ_p cimport ntl_ZZ_p from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.libs.pari.all import pari_gen +from cypari2.gen cimport Gen as pari_gen from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import IntegerMod_abstract from sage.rings.finite_rings.integer_mod_ring import IntegerModRing diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 5db54020fb2..e8c8e870e0c 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -201,7 +201,7 @@ from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ from sage.libs.ntl.ntl_ZZ_p cimport ntl_ZZ_p from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.libs.pari.all import pari_gen +from cypari2.gen cimport Gen as pari_gen from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import IntegerMod_abstract from sage.rings.padics.padic_ext_element cimport pAdicExtElement diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index de3df469423..9ed7b653450 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -143,7 +143,7 @@ from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ from sage.libs.ntl.ntl_ZZ_p cimport ntl_ZZ_p from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.rings.rational cimport Rational -from sage.libs.pari.all import pari_gen +from cypari2.gen cimport Gen as pari_gen from sage.interfaces.abc import GpElement from sage.rings.finite_rings.integer_mod import IntegerMod_abstract from sage.rings.finite_rings.integer_mod_ring import IntegerModRing diff --git a/src/sage/rings/padics/padic_floating_point_element.pyx b/src/sage/rings/padics/padic_floating_point_element.pyx index ceae683a62c..e7c0976c6f9 100644 --- a/src/sage/rings/padics/padic_floating_point_element.pyx +++ b/src/sage/rings/padics/padic_floating_point_element.pyx @@ -21,7 +21,7 @@ AUTHORS: include "sage/libs/linkages/padics/mpz.pxi" include "FP_template.pxi" -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.libs.pari.convert_gmp cimport new_gen_from_padic from sage.rings.finite_rings.integer_mod import Mod diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 7a103be85b3..42cb62c3d39 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -3514,7 +3514,7 @@ cdef class pAdicGenericElement(LocalGenericElement): ans = None if algorithm == "pari": - from sage.libs.pari.all import PariError + from cypari2.handle_error import PariError try: # use pari ans = parent(self.__pari__().sqrt()) diff --git a/src/sage/rings/pari_ring.py b/src/sage/rings/pari_ring.py index b8f2b62d5a7..b27e22e06e6 100644 --- a/src/sage/rings/pari_ring.py +++ b/src/sage/rings/pari_ring.py @@ -16,12 +16,12 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** -import sage.libs.pari.all as pari from sage.categories.rings import Rings -from sage.structure.parent import Parent +from sage.libs.pari import pari +from sage.misc.fast_methods import Singleton from sage.structure.element import RingElement +from sage.structure.parent import Parent from sage.structure.richcmp import richcmp -from sage.misc.fast_methods import Singleton class Pari(RingElement): @@ -44,7 +44,7 @@ def __init__(self, x, parent=None) -> None: if parent is None: parent = _inst RingElement.__init__(self, parent) - self.__x = pari.pari(x) + self.__x = pari(x) def __repr__(self) -> str: """ diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py index 8d4c770072d..f56dfe459ac 100644 --- a/src/sage/rings/polynomial/binary_form_reduce.py +++ b/src/sage/rings/polynomial/binary_form_reduce.py @@ -232,10 +232,9 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): z = v0[1].constant_coefficient() + v0[0].constant_coefficient()*CF.gen(0) err = z.diameter() # precision zz = (w - z).abs().lower() # difference in w and z - else: - # despite there is no break, this happens - if err > error_limit or err.is_NaN(): - raise ValueError("accuracy of Newton's root not within tolerance(%s > %s), increase precision" % (err, error_limit)) + # despite there is no break, this happens + if err > error_limit or err.is_NaN(): + raise ValueError("accuracy of Newton's root not within tolerance(%s > %s), increase precision" % (err, error_limit)) if z.imag().upper() <= z.diameter(): raise ArithmeticError("Newton's method converged to z not in the upper half plane") z = z.center() diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index e9eb5675b90..f9e09ce4fd3 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -36,7 +36,7 @@ from sage.rings.integer_ring import ZZ from sage.structure.element cimport parent try: - from sage.libs.pari.all import pari + from sage.libs.pari import pari except ImportError: pass diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 87b4e2d081c..33ec08133bf 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -2422,7 +2422,7 @@ cdef class MPolynomial(CommutativePolynomial): sage: R. = PolynomialRing(QQ) sage: f = 19*x^8 - 262*x^7*h + 1507*x^6*h^2 - 4784*x^5*h^3 + 9202*x^4*h^4\ - -10962*x^3*h^5 + 7844*x^2*h^6 - 3040*x*h^7 + 475*h^8 + ....: -10962*x^3*h^5 + 7844*x^2*h^6 - 3040*x*h^7 + 475*h^8 sage: f.reduced_form(prec=200, smallest_coeffs=False) # needs sage.modules sage.rings.complex_interval_field ( -x^8 - 2*x^7*h + 7*x^6*h^2 + 16*x^5*h^3 + 2*x^4*h^4 - 2*x^3*h^5 + 4*x^2*h^6 - 5*h^8, diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index d8546e31b33..d5415a8bea4 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -408,7 +408,7 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) sage: I = sage.rings.ideal.Cyclic(R, 6) sage: gb = I.groebner_basis('magma:GroebnerBasis', deg_bound=4) sage: len(gb) - 5 + 7 """ R = self.ring() if not deg_bound: @@ -2733,15 +2733,15 @@ def _variety_triangular_decomposition(self, ring): sage: R.inject_variables() Defining... sage: I = Ideal([x1 + 1, x2, x3 + 1, x5*x10 + x10 + x18, x5*x11 + x11, \ - x5*x18, x6, x7 + 1, x9, x10*x11 + x10 + x18, x10*x18 + x18, \ - x11*x18, x12, x13, x14, x15, x16 + 1, x17 + x18 + 1, x19, x20, \ - x21 + 1, x22, x23, x24, x25 + 1, x28 + 1, x29 + 1, x30, x8, \ - x26, x1^2 + x1, x2^2 + x2, x3^2 + x3, x4^2 + x4, x5^2 + x5, \ - x6^2 + x6, x7^2 + x7, x8^2 + x8, x9^2 + x9, x10^2 + x10, \ - x11^2 + x11, x12^2 + x12, x13^2 + x13, x14^2 + x14, x15^2 + x15, \ - x16^2 + x16, x17^2 + x17, x18^2 + x18, x19^2 + x19, x20^2 + x20, \ - x21^2 + x21, x22^2 + x22, x23^2 + x23, x24^2 + x24, x25^2 + x25, \ - x26^2 + x26, x27^2 + x27, x28^2 + x28, x29^2 + x29, x30^2 + x30]) + ....: x5*x18, x6, x7 + 1, x9, x10*x11 + x10 + x18, x10*x18 + x18, \ + ....: x11*x18, x12, x13, x14, x15, x16 + 1, x17 + x18 + 1, x19, x20, \ + ....: x21 + 1, x22, x23, x24, x25 + 1, x28 + 1, x29 + 1, x30, x8, \ + ....: x26, x1^2 + x1, x2^2 + x2, x3^2 + x3, x4^2 + x4, x5^2 + x5, \ + ....: x6^2 + x6, x7^2 + x7, x8^2 + x8, x9^2 + x9, x10^2 + x10, \ + ....: x11^2 + x11, x12^2 + x12, x13^2 + x13, x14^2 + x14, x15^2 + x15, \ + ....: x16^2 + x16, x17^2 + x17, x18^2 + x18, x19^2 + x19, x20^2 + x20, \ + ....: x21^2 + x21, x22^2 + x22, x23^2 + x23, x24^2 + x24, x25^2 + x25, \ + ....: x26^2 + x26, x27^2 + x27, x28^2 + x28, x29^2 + x29, x30^2 + x30]) sage: I.basis_is_groebner() True sage: sorted("".join(str(V[g]) for g in R.gens()) for V in I.variety()) # long time (6s on sage.math, 2011) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 26c32fe036c..ddf3d3f614a 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -69,7 +69,7 @@ import sage.interfaces.abc try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 8baa8b40b24..cdd4c859af0 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -77,6 +77,20 @@ cdef class MPolynomialRing_base(CommutativeRing): ....: pass sage: Foo(QQ, 2, ['x','y'], 'degrevlex') # needs sage.libs.singular Multivariate Polynomial Ring in x, y over Rational Field + + Check that :meth:`basis` works correctly:: + + sage: R = PolynomialRing(QQ, []) + sage: R.basis() + Lazy family (...monomial...(i))_{i in Integer vectors of length 0} + sage: [*R.basis()] + [1] + sage: R. = QQ[] + sage: R.basis() + Lazy family (...monomial...(i))_{i in Integer vectors of length 2} + sage: import itertools + sage: list(itertools.islice(R.basis(), 16)) + [1, x, y, x^2, x*y, y^2, x^3, x^2*y, x*y^2, y^3, x^4, x^3*y, x^2*y^2, x*y^3, y^4, x^5] """ if base_ring not in _CommutativeRings: raise TypeError("The base ring %s is not a commutative ring" % base_ring) @@ -97,7 +111,7 @@ cdef class MPolynomialRing_base(CommutativeRing): # Ring.__init__ assigns the names. Ring.__init__(self, base_ring, names, category=category) from sage.combinat.integer_vector import IntegerVectors - self._indices = IntegerVectors(self._ngens) + self._indices = IntegerVectors(length=self._ngens) def is_integral_domain(self, proof=True): """ @@ -1380,13 +1394,32 @@ cdef class MPolynomialRing_base(CommutativeRing): TESTS: - Check that ETuples also work:: + Check that :class:`.ETuple`s and :class:`.IntegerVector` also work + (:class:`.IntegerVector` is used for :meth:`basis`):: + sage: from sage.combinat.integer_vector import IntegerVector, IntegerVectors sage: from sage.rings.polynomial.polydict import ETuple sage: R.monomial(ETuple(e)) x*y^2*z^3 + sage: R.monomial(IntegerVector(IntegerVectors(), e)) + x*y^2*z^3 + + Corner case:: + + sage: R = PolynomialRing(QQ, []) + sage: R + Multivariate Polynomial Ring in no variables over Rational Field + sage: R.monomial(()) + 1 + sage: R.monomial() + 1 + sage: R.monomial(ETuple([])) + 1 + sage: R.monomial(IntegerVector(IntegerVectors(), [])) + 1 """ - if len(exponents) == 1 and isinstance((e := exponents[0]), (tuple, ETuple)): + from sage.combinat.integer_vector import IntegerVector + if len(exponents) == 1 and isinstance((e := exponents[0]), (tuple, IntegerVector, ETuple)): return self({e: self.base_ring().one()}) return self({exponents: self.base_ring().one()}) diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index 668f66fe574..51239d3d9db 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -21,7 +21,8 @@ from sage.rings.fraction_field_element import FractionFieldElement import copy -from sage.libs.pari.all import pari, pari_gen +from sage.libs.pari import pari +from cypari2.gen import Gen as pari_gen from sage.misc.lazy_import import lazy_import from sage.rings.infinity import infinity diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py index 4db824cd0cc..d26f7e10e71 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_flat.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_flat.py @@ -11,7 +11,7 @@ from sage.rings.polynomial.polynomial_element import Polynomial_generic_dense, Polynomial from sage.rings.polynomial.padics.polynomial_padic import Polynomial_padic from sage.rings.infinity import infinity -from sage.libs.pari.all import pari_gen +from cypari2.gen import Gen as pari_gen import sage.rings.padics.misc diff --git a/src/sage/rings/polynomial/pbori/gbcore.py b/src/sage/rings/polynomial/pbori/gbcore.py index 8371577157d..cdf5a0bd324 100644 --- a/src/sage/rings/polynomial/pbori/gbcore.py +++ b/src/sage/rings/polynomial/pbori/gbcore.py @@ -319,7 +319,7 @@ def variety_size_from_gb(I): sage: variety_size_from_gb([x(1)*x(2), x(2)*x(3)]) 5.0 sage: mons = [Monomial([r.variable(i) for i in range(100) if i!=j])\ - for j in range(100)] + ....: for j in range(100)] sage: variety_size_from_gb(mons) 1.2676506002282294e+30 """ diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 404771a2c85..1d9eaa42b15 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -4927,14 +4927,9 @@ class BooleanPolynomialIdeal(MPolynomialIdeal): sage: I = F.ideal() sage: I.groebner_basis(algorithm='magma', prot='sage') # optional - magma Leading term degree: 1. Critical pairs: 148. - Leading term degree: 2. Critical pairs: 144. - Leading term degree: 3. Critical pairs: 462. - Leading term degree: 1. Critical pairs: 167. - Leading term degree: 2. Critical pairs: 147. - Leading term degree: 3. Critical pairs: 101 (all pairs of current degree eliminated by criteria). - + ... Highest degree reached during computation: 3. - Polynomial Sequence with 35 Polynomials in 36 Variables + Polynomial Sequence with ... Polynomials in 36 Variables TESTS: diff --git a/src/sage/rings/polynomial/polydict.pyx b/src/sage/rings/polynomial/polydict.pyx index fe2438c8e4f..43cb77e823e 100644 --- a/src/sage/rings/polynomial/polydict.pyx +++ b/src/sage/rings/polynomial/polydict.pyx @@ -1481,6 +1481,7 @@ cdef class ETuple: return cdef size_t ind cdef int v + from sage.combinat.integer_vector import IntegerVector if isinstance(data, ETuple): self._length = (data)._length self._nonzero = (data)._nonzero @@ -1496,7 +1497,7 @@ cdef class ETuple: self._data[2*ind] = index self._data[2*ind+1] = exp ind += 1 - elif isinstance(data, (list, tuple)): + elif isinstance(data, (list, tuple, IntegerVector)): self._length = len(data) self._nonzero = 0 for v in data: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 4d2e607b37d..9181fb9ae3e 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -81,7 +81,9 @@ from sage.structure.richcmp cimport (richcmp, richcmp_item, rich_to_bool, rich_to_bool_sgn) try: - from sage.libs.pari.all import pari, pari_gen, PariError + from sage.libs.pari import pari + from cypari2.handle_error import PariError + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () pari = None @@ -1175,8 +1177,7 @@ cdef class Polynomial(CommutativePolynomial): cdef Py_ssize_t d = self.degree() if 0 <= i <= d: return self.get_unsafe(i) - else: - return self._parent._base.zero() + return self._parent._base.zero() cdef get_unsafe(self, Py_ssize_t i): """ @@ -1608,6 +1609,43 @@ cdef class Polynomial(CommutativePolynomial): AUTHORS: - Robert Bradshaw (2007-05-31) + + TESTS: + + At the time of writing :meth:`inverse_mod` is not implemented in general for the following ring, + but it should fallback to :meth:`inverse_of_unit` when possible:: + + sage: R. = ZZ[] + sage: S = R.localization(u) + sage: T. = S[] + sage: T + Univariate Polynomial Ring in x over Multivariate Polynomial Ring in u, v over Integer Ring localized at (u,) + sage: T(u).is_unit() + True + sage: T(u).inverse_of_unit() + 1/u + sage: T(u).inverse_mod(T(v)) + 1/u + sage: T(v).inverse_mod(T(v^2-1)) + Traceback (most recent call last): + ... + NotImplementedError: Multivariate Polynomial Ring in u, v over Integer Ring localized at (u,) does not provide... + + The behavior of ``xgcd`` over rings like ``ZZ`` are nonstandard, we check the behavior:: + + sage: R. = ZZ[] + sage: a = 2*x^2+1 + sage: b = -2*x^2+1 + sage: a.inverse_mod(b) + Traceback (most recent call last): + ... + NotImplementedError: The base ring (=Integer Ring) is not a field + sage: a.xgcd(b) + (16, 8, 8) + sage: a*x^2+b*(x^2+1) + 1 + sage: a*x^2%b # shows the result exists, thus we cannot raise ValueError, only NotImplementedError + 1 """ from sage.rings.ideal import Ideal_generic if isinstance(m, Ideal_generic): @@ -1625,13 +1663,25 @@ cdef class Polynomial(CommutativePolynomial): return a.parent()(~u) if a.parent().is_exact(): # use xgcd - g, s, _ = a.xgcd(m) - if g == 1: - return s - elif g.is_unit(): - return g.inverse_of_unit() * s - else: - raise ValueError("Impossible inverse modulo") + try: + g, s, _ = a.xgcd(m) + if g == 1: + return s + elif g.is_unit(): + return g.inverse_of_unit() * s + else: + R = m.base_ring() + if R.is_field(): + raise ValueError("Impossible inverse modulo") + else: + raise NotImplementedError(f"The base ring (={R}) is not a field") + except NotImplementedError: + # attempt fallback, return inverse_of_unit() if this is already an unit + try: + return a.inverse_of_unit() + except (ArithmeticError, ValueError, NotImplementedError): + pass + raise else: # xgcd may not converge for inexact rings. # Instead solve for the coefficients of @@ -1822,13 +1872,14 @@ cdef class Polynomial(CommutativePolynomial): cdef Polynomial _right = right if self.is_term(): return _right._mul_term(self, term_on_right=False) - elif _right.is_term(): + + if _right.is_term(): return self._mul_term(_right, term_on_right=True) - elif self._parent.is_exact(): + if self._parent.is_exact(): return self._mul_karatsuba(right) - else: - return self._mul_generic(right) + + return self._mul_generic(right) cpdef Polynomial _mul_trunc_(self, Polynomial right, long n): r""" @@ -2533,10 +2584,8 @@ cdef class Polynomial(CommutativePolynomial): sage: K. = GF(2^8) sage: x = polygen(K) sage: pol = x^1000000 + x + a - sage: alarm(0.5); pol.any_root() - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): pol.any_root() Check root computation over large finite fields:: @@ -6809,13 +6858,17 @@ cdef class Polynomial(CommutativePolynomial): e = self.exponents() c = self.coefficients() - if len(e) == 0: return [] + if len(e) == 0: + return [] if len(e) == 1: - if e[0] == 0: return [] - else: return [(infinity.infinity, e[0])] + if e[0] == 0: + return [] + return [(infinity.infinity, e[0])] - if e[0] == 0: slopes = [] - else: slopes = [(infinity.infinity, e[0])] + if e[0] == 0: + slopes = [] + else: + slopes = [(infinity.infinity, e[0])] points = [(e[0], c[0].valuation(p)), (e[1], c[1].valuation(p))] slopes.append((-(c[1].valuation(p)-c[0].valuation(p))/(e[1] - e[0]), e[1]-e[0])) @@ -6826,8 +6879,8 @@ cdef class Polynomial(CommutativePolynomial): slopes = slopes[:-1] points = points[:-1] s = -(v-points[-1][1])/(e[i]-points[-1][0]) - slopes.append((s,e[i]-points[-1][0])) - points.append((e[i],v)) + slopes.append((s, e[i]-points[-1][0])) + points.append((e[i], v)) return slopes diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index db8139bddad..677c542ed20 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -35,7 +35,7 @@ from sage.rings.polynomial.polynomial_singular_interface import Polynomial_singular_repr try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/rings/polynomial/polynomial_gf2x.pyx b/src/sage/rings/polynomial/polynomial_gf2x.pyx index cf4a224dda9..7a5eeb7ffae 100644 --- a/src/sage/rings/polynomial/polynomial_gf2x.pyx +++ b/src/sage/rings/polynomial/polynomial_gf2x.pyx @@ -26,7 +26,7 @@ include "sage/libs/ntl/ntl_GF2X_linkage.pxi" # and then the interface include "polynomial_template.pxi" -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.libs.m4ri cimport mzd_write_bit, mzd_read_bit from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index fc2ab9b584e..8070c07b9d1 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -55,7 +55,8 @@ from sage.libs.ntl.ntl_ZZX cimport ntl_ZZX from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.libs.pari.all import pari, pari_gen +from sage.libs.pari import pari +from cypari2.gen cimport Gen as pari_gen from sage.structure.factorization import Factorization from sage.rings.fraction_field_element import FractionFieldElement diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 142c7b324ad..1aa517e6e1d 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -58,7 +58,8 @@ from sage.rings.integer cimport Integer from sage.rings.real_mpfr cimport RealNumber from sage.rings.real_mpfi cimport RealIntervalFieldElement -from sage.libs.pari.all import pari, pari_gen +from sage.libs.pari import pari +from cypari2.gen cimport Gen as pari_gen from sage.structure.factorization import Factorization from sage.structure.element import coerce_binop @@ -618,7 +619,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): since they need not exist. Instead, over the integers, we first multiply `g` by a divisor of the resultant of `a/g` and `b/g`, up to sign, and return ``g, u, v`` such that - ``g = s*self + s*right``. But note that this `g` may be a + ``g = u*self + v*right``. But note that this `g` may be a multiple of the gcd. If ``self`` and ``right`` are coprime as polynomials over the diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index c1a5474adf2..7fc0f3bf14f 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -36,7 +36,8 @@ from cysignals.signals cimport sig_on, sig_off from sage.rings.polynomial.polynomial_element cimport Polynomial, _dict_to_list -from sage.libs.pari.all import pari, pari_gen +from sage.libs.pari import pari +from cypari2.gen cimport Gen as pari_gen from sage.rings.integer cimport smallInteger diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index c167caeb352..999fca6e8fd 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -386,10 +386,6 @@ def __invert__(self): """ Return the inverse of this element. - .. WARNING:: - - Only implemented when the base ring is a field. - EXAMPLES:: sage: R. = QQ[] @@ -414,35 +410,27 @@ def __invert__(self): sage: (2*y)^(-1) Traceback (most recent call last): ... - NotImplementedError: The base ring (=Ring of integers modulo 16) is not a field + NotImplementedError + sage: (2*y+1)^(-1) # this cannot raise ValueError because... + Traceback (most recent call last): + ... + NotImplementedError + sage: (2*y+1) * (10*y+5) # the element is in fact invertible + 1 Check that :issue:`29469` is fixed:: sage: ~S(3) 11 """ - if self._polynomial.is_zero(): - raise ZeroDivisionError("element %s of quotient polynomial ring not invertible" % self) - if self._polynomial.is_one(): - return self - - parent = self.parent() - + P = self.parent() try: - if self._polynomial.is_unit(): - inv_pol = self._polynomial.inverse_of_unit() - return parent(inv_pol) - except (TypeError, NotImplementedError): - pass - - base = parent.base_ring() - if not base.is_field(): - raise NotImplementedError("The base ring (=%s) is not a field" % base) - g, _, a = parent.modulus().xgcd(self._polynomial) - if g.degree() != 0: - raise ZeroDivisionError("element %s of quotient polynomial ring not invertible" % self) - c = g[0] - return self.__class__(self.parent(), (~c)*a, check=False) + return type(self)(P, self._polynomial.inverse_mod(P.modulus()), check=False) + except ValueError as e: + if e.args[0] == "Impossible inverse modulo": + raise ZeroDivisionError(f"element {self} of quotient polynomial ring not invertible") + else: + raise NotImplementedError def field_extension(self, names): r""" diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 979877606a7..4a8d72fd590 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -38,7 +38,7 @@ from sage.libs.flint.fmpq_poly_sage cimport * from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * -from cypari2.gen import Gen as pari_gen +from cypari2.gen cimport Gen as pari_gen from sage.rings.complex_arb cimport ComplexBall from sage.rings.integer cimport Integer, smallInteger @@ -2162,7 +2162,7 @@ cdef class Polynomial_rational_flint(Polynomial): Transitive group number 183 of degree 12 sage: f.galois_group(algorithm='magma') # optional - magma - Transitive group number 5 of degree 4 + Transitive group number 183 of degree 12 TESTS: diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index cfcdcabdcb5..44faac21762 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -41,7 +41,7 @@ from sage.structure.element import coerce_binop from sage.libs.mpfr cimport * try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 82d8eae3361..963fa8de570 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -159,7 +159,7 @@ from sage.rings.number_field.number_field_base import NumberField try: - from sage.libs.pari.all import pari_gen + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () @@ -3607,7 +3607,7 @@ def irreducible_element(self, n, algorithm=None): - Jeroen Demeyer (September 2014): add "ffprimroot" algorithm, see :issue:`8373`. """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari from sage.rings.finite_rings.conway_polynomials import (conway_polynomial, exists_conway_polynomial) diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 35c260e67ea..8752d519e6c 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -20,7 +20,7 @@ from sage.structure.element import coerce_binop from sage.structure.richcmp cimport rich_to_bool from sage.rings.fraction_field_element import FractionFieldElement from sage.rings.integer cimport Integer -from sage.libs.pari.all import pari_gen +from cypari2.gen cimport Gen as pari_gen import operator diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index 5b3539d6b70..2bde0865f72 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -811,10 +811,8 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sage: R. = PolynomialRing(GF(65537), implementation="FLINT") sage: f = R.random_element(9973) * R.random_element(10007) - sage: alarm(0.5); f.factor() - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): f.factor() Test zero polynomial:: diff --git a/src/sage/rings/power_series_mpoly.pyx b/src/sage/rings/power_series_mpoly.pyx index 741b0ad94e7..255c9d0ee03 100644 --- a/src/sage/rings/power_series_mpoly.pyx +++ b/src/sage/rings/power_series_mpoly.pyx @@ -8,7 +8,7 @@ from sage.rings import power_series_poly try: - from sage.libs.pari.all import PariError + from cypari2.handle_error import PariError except ImportError: PariError = () diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index 350362cadb7..bd76579346e 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -71,7 +71,7 @@ AUTHORS: from cypari2.gen cimport Gen as pari_gen from cypari2.pari_instance cimport get_var from cypari2.paridecl cimport gel, typ, lg, valp, varn, t_POL, t_SER, t_RFRAC, t_VEC -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.rings.polynomial.polynomial_element cimport Polynomial from sage.rings.power_series_ring_element cimport PowerSeries diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index c284b06a5e4..9acde829d4a 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -8,7 +8,8 @@ from sage.structure.element cimport Element from sage.rings.infinity import infinity try: - from sage.libs.pari.all import pari_gen, PariError + from cypari2.handle_error import PariError + from cypari2.gen import Gen as pari_gen except ImportError: pari_gen = () PariError = () diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 42ca735e671..6d290188760 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -722,6 +722,33 @@ def _coerce_map_from_(self, S): and self.variable_names() == S.variable_names()): return True + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: R = QQ[['y']] + sage: R._magma_init_(magma) + 'SageCreateWithNames(PowerSeriesRing(_sage_ref...),["y"])' + sage: S = magma(R) + sage: S + Power series ring in y over Rational Field + sage: S.1 + y + sage: S.sage() == R + True + + sage: # optional - magma + sage: magma(PowerSeriesRing(GF(7), 'x')) # needs sage.rings.finite_rings + Power series ring in x over GF(7) + """ + B = magma(self.base_ring()) + Bref = B._ref() + s = 'PowerSeriesRing(%s)' % (Bref) + return magma._with_names(s, self.variable_names()) + def _element_constructor_(self, f, prec=infinity, check=True): """ Coerce object to this power series ring. diff --git a/src/sage/rings/puiseux_series_ring.py b/src/sage/rings/puiseux_series_ring.py index 06b752f92df..52b2279c1c7 100644 --- a/src/sage/rings/puiseux_series_ring.py +++ b/src/sage/rings/puiseux_series_ring.py @@ -277,6 +277,33 @@ def uniformizer(self): Element = PuiseuxSeries + def _magma_init_(self, magma): + """ + Used in converting this ring to the corresponding ring in MAGMA. + + EXAMPLES:: + + sage: # optional - magma + sage: R = PuiseuxSeriesRing(QQ, 'y') + sage: R._magma_init_(magma) + 'SageCreateWithNames(PuiseuxSeriesRing(_sage_ref...),["y"])' + sage: S = magma(R) + sage: S + Puiseux series field in y over Rational Field + sage: S.1 + y + sage: S.sage() == R + True + + sage: # optional - magma + sage: magma(PuiseuxSeriesRing(GF(7), 'x')) # needs sage.rings.finite_rings + Puiseux series field in x over GF(7) + """ + B = magma(self.base_ring()) + Bref = B._ref() + s = 'PuiseuxSeriesRing(%s)' % (Bref) + return magma._with_names(s, self.variable_names()) + def _element_constructor_(self, x, e=1, prec=infinity): r""" Construct a Puiseux series from ``x``. diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 050ab750117..56e83da0d5a 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -7083,7 +7083,7 @@ def complex_roots(self, prec, multiplicity): return roots def exactify(self): - """ + r""" Compute a common field that holds all of the algebraic coefficients of this polynomial, then factor the polynomial over that field. Store the factors for later use (ignoring multiplicity). @@ -7106,14 +7106,14 @@ def exactify(self): sage: x = polygen(AA) sage: p = AA(2)^(1/100) * x + AA(3)^(1/100) sage: cp = AA.common_polynomial(p) - sage: alarm(0.5); cp.generator() - Traceback (most recent call last): - ... - AlarmInterrupt - sage: alarm(0.5); cp.generator() - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: from warnings import catch_warnings, filterwarnings + sage: with ensure_interruptible_after(0.5), catch_warnings(): + ....: filterwarnings("ignore", r"cypari2 leaked \d+ bytes on the PARI stack") + ....: cp.generator() + sage: with ensure_interruptible_after(0.5), catch_warnings(): + ....: filterwarnings("ignore", r"cypari2 leaked \d+ bytes on the PARI stack") + ....: cp.generator() """ if self._exact: return diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index ce9958ce7e7..bbe72c26ba2 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -252,7 +252,7 @@ TESTS:: sage: import numpy # needs numpy sage: if int(numpy.version.short_version[0]) > 1: # needs numpy - ....: numpy.set_printoptions(legacy="1.25") # needs numpy + ....: _ = numpy.set_printoptions(legacy="1.25") # needs numpy sage: RIF(2) == numpy.int8('2') # needs numpy True sage: numpy.int8('2') == RIF(2) # needs numpy diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index db62e4844e2..6f41d0cbb7a 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -518,29 +518,6 @@ cdef class Ring(ParentWithGens): else: return False - cpdef bint is_exact(self) except -2: - """ - Return ``True`` if elements of this ring are represented exactly, i.e., - there is no precision loss when doing arithmetic. - - .. NOTE:: - - This defaults to ``True``, so even if it does return ``True`` you - have no guarantee (unless the ring has properly overloaded this). - - EXAMPLES:: - - sage: QQ.is_exact() # indirect doctest - True - sage: ZZ.is_exact() - True - sage: Qp(7).is_exact() # needs sage.rings.padics - False - sage: Zp(7, type='capped-abs').is_exact() # needs sage.rings.padics - False - """ - return True - def order(self): """ The number of elements of ``self``. @@ -1002,40 +979,6 @@ cdef class Field(CommutativeRing): """ return self - def divides(self, x, y, coerce=True): - """ - Return ``True`` if ``x`` divides ``y`` in this field (usually ``True`` - in a field!). If ``coerce`` is ``True`` (the default), first coerce - ``x`` and ``y`` into ``self``. - - EXAMPLES:: - - sage: QQ.divides(2, 3/4) - True - sage: QQ.divides(0, 5) - False - """ - if coerce: - x = self(x) - y = self(y) - if x.is_zero(): - return y.is_zero() - return True - - def integral_closure(self): - """ - Return this field, since fields are integrally closed in their - fraction field. - - EXAMPLES:: - - sage: QQ.integral_closure() - Rational Field - sage: Frac(ZZ['x,y']).integral_closure() - Fraction Field of Multivariate Polynomial Ring in x, y over Integer Ring - """ - return self - def is_field(self, proof=True): """ Return ``True`` since this is a field. @@ -1047,23 +990,6 @@ cdef class Field(CommutativeRing): """ return True - def prime_subfield(self): - """ - Return the prime subfield of ``self``. - - EXAMPLES:: - - sage: k = GF(9, 'a') # needs sage.rings.finite_rings - sage: k.prime_subfield() # needs sage.rings.finite_rings - Finite Field of size 3 - """ - if self.characteristic() == 0: - import sage.rings.rational_field - return sage.rings.rational_field.RationalField() - else: - from sage.rings.finite_rings.finite_field_constructor import GF - return GF(self.characteristic()) - def algebraic_closure(self): """ Return the algebraic closure of ``self``. diff --git a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx index 16bad60ba56..b3bf87a6a55 100644 --- a/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx +++ b/src/sage/schemes/elliptic_curves/descent_two_isogeny.pyx @@ -1208,10 +1208,8 @@ def two_descent_by_two_isogeny(E, Elliptic Curve defined by y^2 = x^3 - x^2 - 900*x - 10098 over Rational Field sage: E.sha().an() 4 - sage: alarm(0.5); two_descent_by_two_isogeny(E, global_limit_large=10^8) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.5): two_descent_by_two_isogeny(E, global_limit_large=10^8) """ cdef Integer a1, a2, a3, a4, a6, s2, s4, s6 cdef Integer c, d, x0 @@ -1318,7 +1316,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, p_list_len += 1 else: # Factor more slowly using Pari via Python. - from sage.libs.pari.all import pari + from sage.libs.pari import pari d = Integer(0) mpz_set(d.value, d_mpz) primes = list(pari(d).factor()[0]) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index bfecebf1f86..32e2fac3696 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -1076,6 +1076,19 @@ def is_on_curve(self, x, y): a = self.ainvs() return y**2 + a[0]*x*y + a[2]*y == x**3 + a[1]*x**2 + a[3]*x + a[4] + def is_exact(self): + """ + Test whether elements of this elliptic curve are represented exactly. + + EXAMPLES:: + + sage: EllipticCurve(QQ, [1, 2]).is_exact() + True + sage: EllipticCurve(RR, [1, 2]).is_exact() + False + """ + return self.__base_ring.is_exact() + def a_invariants(self): r""" The `a`-invariants of this elliptic curve, as a tuple. diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 1d980d60666..a202dcb0fca 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -158,7 +158,8 @@ lazy_import('sage.schemes.generic.morphism', 'SchemeMorphism') try: - from sage.libs.pari.all import pari, PariError + from sage.libs.pari import pari + from cypari2.handle_error import PariError except ImportError: PariError = () diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 134531ad1a5..96a9cca752a 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -80,7 +80,7 @@ from sage.structure.element import Element, RingElement lazy_import("sage.functions.log", "log") -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import("sage.functions.gamma", "gamma_inc") lazy_import('sage.interfaces.gp', 'gp') diff --git a/src/sage/schemes/elliptic_curves/gal_reps.py b/src/sage/schemes/elliptic_curves/gal_reps.py index 8f0e0ed9a60..428bf4bebf6 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps.py +++ b/src/sage/schemes/elliptic_curves/gal_reps.py @@ -125,7 +125,7 @@ from sage.rings.real_mpfr import RealField from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') def _ex_set(p): diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index 96700fa254d..aef794ed8f9 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -1199,8 +1199,7 @@ def compare_via_evaluation(left, right): if P._has_order_at_least(4*d + 1, attempts=50): # if P.height(precision=250) == 0: # slow sometimes return left._eval(P) == right._eval(P) - else: - assert False, "couldn't find a point of large enough order" + assert False, "couldn't find a point of large enough order" else: raise NotImplementedError('not implemented for this base field') diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index d8cf1971f45..31968d8578f 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -581,8 +581,7 @@ def _point_outside_subgroup(P): Q = E.random_point() if n*Q or not P.weil_pairing(Q,n).is_one(): return Q - else: - raise NotImplementedError('could not find a point outside the kernel') + raise NotImplementedError('could not find a point outside the kernel') class EllipticCurveHom_velusqrt(EllipticCurveHom): diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index 13edc68a022..46ff983c54c 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -434,60 +434,71 @@ def graph(self): elif n == 2: # one edge, two vertices. We align horizontally and put # the lower number on the left vertex. - G.set_pos(pos={0:[-0.5,0],1:[0.5,0]}) + G.set_pos(pos={0: [-0.5, 0], 1: [0.5, 0]}) else: maxdegree = max(max(N)) if n == 3: # o--o--o - centervert = [i for i in range(3) if max(N.row(i)) < maxdegree][0] + centervert = next(i for i in range(3) if max(N.row(i)) < maxdegree) other = [i for i in range(3) if i != centervert] - G.set_pos(pos={centervert:[0,0],other[0]:[-1,0],other[1]:[1,0]}) + G.set_pos(pos={centervert: [0, 0], other[0]: [-1, 0], other[1]: [1, 0]}) elif maxdegree == 4: # o--o<8 - centervert = [i for i in range(4) if max(N.row(i)) < maxdegree][0] + centervert = next(i for i in range(4) if max(N.row(i)) < maxdegree) other = [i for i in range(4) if i != centervert] - G.set_pos(pos={centervert:[0,0],other[0]:[0,1],other[1]:[-0.8660254,-0.5],other[2]:[0.8660254,-0.5]}) + G.set_pos(pos={centervert: [0, 0], other[0]: [0, 1], + other[1]: [-0.8660254, -0.5], other[2]: [0.8660254, -0.5]}) elif maxdegree == 27: # o--o--o--o centers = [i for i in range(4) if list(N.row(i)).count(3) == 2] - left = [j for j in range(4) if N[centers[0],j] == 3 and j not in centers][0] - right = [j for j in range(4) if N[centers[1],j] == 3 and j not in centers][0] - G.set_pos(pos={left:[-1.5,0],centers[0]:[-0.5,0],centers[1]:[0.5,0],right:[1.5,0]}) + left = next(j for j in range(4) if N[centers[0], j] == 3 and j not in centers) + right = next(j for j in range(4) if N[centers[1], j] == 3 and j not in centers) + G.set_pos(pos={left: [-1.5, 0], centers[0]: [-0.5, 0], + centers[1]: [0.5, 0], right: [1.5, 0]}) elif n == 4: # square - opp = [i for i in range(1,4) if not N[0,i].is_prime()][0] - other = [i for i in range(1,4) if i != opp] - G.set_pos(pos={0:[1,1],other[0]:[-1,1],opp:[-1,-1],other[1]:[1,-1]}) + opp = next(i for i in range(1, 4) if not N[0, i].is_prime()) + other = [i for i in range(1, 4) if i != opp] + G.set_pos(pos={0: [1, 1], other[0]: [-1, 1], + opp: [-1, -1], other[1]: [1, -1]}) elif maxdegree == 8: # 8>o--o<8 centers = [i for i in range(6) if list(N.row(i)).count(2) == 3] - left = [j for j in range(6) if N[centers[0],j] == 2 and j not in centers] - right = [j for j in range(6) if N[centers[1],j] == 2 and j not in centers] - G.set_pos(pos={centers[0]:[-0.5,0],left[0]:[-1,0.8660254],left[1]:[-1,-0.8660254],centers[1]:[0.5,0],right[0]:[1,0.8660254],right[1]:[1,-0.8660254]}) + left = [j for j in range(6) if N[centers[0], j] == 2 and j not in centers] + right = [j for j in range(6) if N[centers[1], j] == 2 and j not in centers] + G.set_pos(pos={centers[0]: [-0.5, 0], left[0]: [-1, 0.8660254], + left[1]: [-1, -0.8660254], centers[1]: [0.5, 0], + right[0]: [1, 0.8660254], right[1]: [1, -0.8660254]}) elif maxdegree == 18: # two squares joined on an edge centers = [i for i in range(6) if list(N.row(i)).count(3) == 2] - top = [j for j in range(6) if N[centers[0],j] == 3] - bl = [j for j in range(6) if N[top[0],j] == 2][0] - br = [j for j in range(6) if N[top[1],j] == 2][0] - G.set_pos(pos={centers[0]:[0,0.5],centers[1]:[0,-0.5],top[0]:[-1,0.5],top[1]:[1,0.5],bl:[-1,-0.5],br:[1,-0.5]}) + top = [j for j in range(6) if N[centers[0], j] == 3] + bl = next(j for j in range(6) if N[top[0], j] == 2) + br = next(j for j in range(6) if N[top[1], j] == 2) + G.set_pos(pos={centers[0]: [0, 0.5], centers[1]: [0, -0.5], + top[0]: [-1, 0.5], top[1]: [1, 0.5], + bl: [-1, -0.5], br: [1, -0.5]}) elif maxdegree == 16: # tree from bottom, 3 regular except for the leaves. centers = [i for i in range(8) if list(N.row(i)).count(2) == 3] - center = [i for i in centers if len([j for j in centers if N[i,j] == 2]) == 2][0] + center = next(i for i in centers if len([j for j in centers if N[i, j] == 2]) == 2) centers.remove(center) - bottom = [j for j in range(8) if N[center,j] == 2 and j not in centers][0] - left = [j for j in range(8) if N[centers[0],j] == 2 and j != center] - right = [j for j in range(8) if N[centers[1],j] == 2 and j != center] - G.set_pos(pos={center:[0,0],bottom:[0,-1],centers[0]:[-0.8660254,0.5],centers[1]:[0.8660254,0.5],left[0]:[-0.8660254,1.5],right[0]:[0.8660254,1.5],left[1]:[-1.7320508,0],right[1]:[1.7320508,0]}) + bottom = next(j for j in range(8) if N[center, j] == 2 and j not in centers) + left = [j for j in range(8) if N[centers[0], j] == 2 and j != center] + right = [j for j in range(8) if N[centers[1], j] == 2 and j != center] + G.set_pos(pos={center: [0, 0], bottom: [0, -1], centers[0]: [-0.8660254, 0.5], + centers[1]: [0.8660254, 0.5], left[0]: [-0.8660254, 1.5], + right[0]: [0.8660254, 1.5], left[1]: [-1.7320508, 0], right[1]: [1.7320508, 0]}) elif maxdegree == 12: # tent centers = [i for i in range(8) if list(N.row(i)).count(2) == 3] - left = [j for j in range(8) if N[centers[0],j] == 2] + left = [j for j in range(8) if N[centers[0], j] == 2] right = [] for i in range(3): - right.append([j for j in range(8) if N[centers[1],j] == 2 and N[left[i],j] == 3][0]) - G.set_pos(pos={centers[0]:[-0.75,0],centers[1]:[0.75,0],left[0]:[-0.75,1],right[0]:[0.75,1],left[1]:[-1.25,-0.75],right[1]:[0.25,-0.75],left[2]:[-0.25,-0.25],right[2]:[1.25,-0.25]}) + right.append(next(j for j in range(8) if N[centers[1], j] == 2 and N[left[i], j] == 3)) + G.set_pos(pos={centers[0]: [-0.75, 0], centers[1]: [0.75, 0], left[0]: [-0.75, 1], + right[0]: [0.75, 1], left[1]: [-1.25, -0.75], right[1]: [0.25, -0.75], + left[2]: [-0.25, -0.25], right[2]: [1.25, -0.25]}) G.set_vertices(D) G.relabel(list(range(1, n + 1))) return G @@ -1207,7 +1218,7 @@ def isogeny_degrees_cm(E, verbose=False): if verbose: print("CM case, discriminant = %s" % d) - from sage.libs.pari.all import pari + from sage.libs.pari import pari from sage.sets.set import Set from sage.arith.misc import kronecker as kronecker_symbol diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 54cd135dea9..a4a44405671 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -120,7 +120,7 @@ from sage.schemes.elliptic_curves.constructor import EllipticCurve from sage.structure.richcmp import richcmp_method, richcmp, richcmp_not_equal -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.rings.number_field.number_field', 'refine_embedding') diff --git a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx index e436e0f756d..0640bf5585a 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx +++ b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx @@ -78,7 +78,7 @@ cdef class PeriodicRegion: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.schemes.elliptic_curves.period_lattice_region import PeriodicRegion sage: data = np.zeros((4, 4)) sage: PeriodicRegion(CDF(2), CDF(2*I), data).is_empty() @@ -296,7 +296,7 @@ cdef class PeriodicRegion: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.schemes.elliptic_curves.period_lattice_region import PeriodicRegion sage: data = np.zeros((10, 10)) sage: data[1:4,1:4] = True @@ -320,7 +320,7 @@ cdef class PeriodicRegion: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.schemes.elliptic_curves.period_lattice_region import PeriodicRegion sage: data = np.zeros((4, 4)) sage: data[1,1] = True @@ -375,7 +375,7 @@ cdef class PeriodicRegion: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.schemes.elliptic_curves.period_lattice_region import PeriodicRegion sage: data = np.zeros((20, 20)) @@ -528,7 +528,7 @@ cdef class PeriodicRegion: sage: import numpy as np sage: if int(np.version.short_version[0]) > 1: - ....: np.set_printoptions(legacy="1.25") + ....: _ = np.set_printoptions(legacy="1.25") sage: from sage.schemes.elliptic_curves.period_lattice_region import PeriodicRegion sage: data = np.zeros((4, 4)) sage: data[1, 1] = True diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 469e3eaa2f8..d2981b4fbd8 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -61,7 +61,7 @@ from sage.misc.functional import rank from sage.misc.lazy_import import lazy_import -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.schemes.hyperelliptic_curves.hypellfrob', 'hypellfrob') from sage.schemes.curves.projective_curve import ProjectivePlaneCurve_finite_field diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 61cd934c0a0..c8279e47391 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -669,7 +669,7 @@ def _magma_init_(self, magma): Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + x^10*y = x^3 + x + 2 sage: D = magma(C); D # needs sage.rings.finite_rings - Hyperelliptic Curve defined by y^2 + (x^10)*y = x^3 + x + 2 over GF(3^2) + Hyperelliptic Curve defined by y^2 + x^10*y = x^3 + x + 2 over GF(3^2) sage: D.sage() # needs sage.rings.finite_rings Hyperelliptic Curve over Finite Field in a of size 3^2 defined by y^2 + x^10*y = x^3 + x + 2 diff --git a/src/sage/schemes/jacobians/abstract_jacobian.py b/src/sage/schemes/jacobians/abstract_jacobian.py index 6ec2880a627..f1e060f77b1 100644 --- a/src/sage/schemes/jacobians/abstract_jacobian.py +++ b/src/sage/schemes/jacobians/abstract_jacobian.py @@ -101,14 +101,14 @@ def __init__(self, C, category=None): Note: this is an abstract parent, so we skip element tests:: sage: TestSuite(J).run(skip =["_test_an_element", \ - "_test_zero", \ - "_test_elements", \ - "_test_elements_eq_reflexive", \ - "_test_elements_eq_symmetric", \ - "_test_elements_eq_transitive", \ - "_test_additive_associativity", \ - "_test_elements_neq", \ - "_test_some_elements"]) + ....: "_test_zero", \ + ....: "_test_elements", \ + ....: "_test_elements_eq_reflexive", \ + ....: "_test_elements_eq_symmetric", \ + ....: "_test_elements_eq_transitive", \ + ....: "_test_additive_associativity", \ + ....: "_test_elements_neq", \ + ....: "_test_some_elements"]) :: diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index 1f29206dfd1..0173fdee72c 100644 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -16,7 +16,7 @@ sage: PP. = ProductProjectiveSpaces([1,0], QQ) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_rational_field + ....: enum_product_projective_rational_field sage: enum_product_projective_rational_field(PP, 3) [(-3 : 1 , 1), (-2 : 1 , 1), (-3/2 : 1 , 1), (-1 : 1 , 1), (-2/3 : 1 , 1), (-1/2 : 1 , 1), @@ -30,7 +30,7 @@ sage: P1. = ProductProjectiveSpaces([1, 1], GF(7)) sage: X = P1.subscheme([2*x + 3*y]) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_finite_field + ....: enum_product_projective_finite_field sage: enum_product_projective_finite_field(X) [(2 : 1 , 0 : 1), (2 : 1 , 1 : 0), (2 : 1 , 1 : 1), (2 : 1 , 2 : 1), (2 : 1 , 3 : 1), (2 : 1 , 4 : 1), @@ -89,7 +89,7 @@ def enum_product_projective_rational_field(X, B): sage: PP. = ProductProjectiveSpaces([1, 2], QQ) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_rational_field + ....: enum_product_projective_rational_field sage: enum_product_projective_rational_field(PP, 1) [(-1 : 1 , -1 : -1 : 1), (-1 : 1 , -1 : 0 : 1), (-1 : 1 , -1 : 1 : 0), (-1 : 1 , -1 : 1 : 1), (-1 : 1 , 0 : -1 : 1), (-1 : 1 , 0 : 0 : 1), @@ -115,7 +115,7 @@ def enum_product_projective_rational_field(X, B): sage: PP. = ProductProjectiveSpaces([2, 1], QQ) sage: X = PP.subscheme([x^2 + x*y + y*z, u*u - v*u]) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_rational_field + ....: enum_product_projective_rational_field sage: enum_product_projective_rational_field(X, 4) [(-2 : 4 : 1 , 0 : 1), (-2 : 4 : 1 , 1 : 1), (-1 : 1 : 0 , 0 : 1), (-1 : 1 : 0 , 1 : 1), (-2/3 : -4/3 : 1 , 0 : 1), (-2/3 : -4/3 : 1 , 1 : 1), @@ -215,7 +215,7 @@ def enum_product_projective_number_field(X, **kwds): sage: PP. = ProductProjectiveSpaces([1, 1], K) sage: X = PP.subscheme([x^2 + 2*y^2]) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_number_field + ....: enum_product_projective_number_field sage: enum_product_projective_number_field(X, bound=1.5) [(-v : 1 , -1 : 1), (-v : 1 , -v : 1), (-v : 1 , -1/2*v : 1), (-v : 1 , 0 : 1), (-v : 1 , 1/2*v : 1), (-v : 1 , v : 1), @@ -267,7 +267,7 @@ def enum_product_projective_finite_field(X): sage: PP. = ProductProjectiveSpaces([1, 1], GF(3)) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_finite_field + ....: enum_product_projective_finite_field sage: enum_product_projective_finite_field(PP) [(0 : 1 , 0 : 1), (0 : 1 , 1 : 0), (0 : 1 , 1 : 1), (0 : 1 , 2 : 1), (1 : 0 , 0 : 1), (1 : 0 , 1 : 0), @@ -281,7 +281,7 @@ def enum_product_projective_finite_field(X): sage: PP. = ProductProjectiveSpaces([1, 1], GF(17)) sage: X = PP.subscheme([x0^2 + 2*x1^2]) sage: from sage.schemes.product_projective.rational_point import \ - enum_product_projective_finite_field + ....: enum_product_projective_finite_field sage: len(enum_product_projective_finite_field(X)) 36 """ diff --git a/src/sage/schemes/product_projective/subscheme.py b/src/sage/schemes/product_projective/subscheme.py index 511f10a973e..3ee7f770551 100644 --- a/src/sage/schemes/product_projective/subscheme.py +++ b/src/sage/schemes/product_projective/subscheme.py @@ -55,7 +55,7 @@ class AlgebraicScheme_subscheme_product_projective(AlgebraicScheme_subscheme_pro TESTS:: sage: from sage.schemes.product_projective.subscheme \ - import AlgebraicScheme_subscheme_product_projective + ....: import AlgebraicScheme_subscheme_product_projective sage: AlgebraicScheme_subscheme_product_projective(P, [u*x^2 - v*y*x]) Closed subscheme of Product of projective spaces P^1 x P^1 over Rational Field defined by: diff --git a/src/sage/schemes/projective/proj_bdd_height.py b/src/sage/schemes/projective/proj_bdd_height.py index 232f51188b4..b8fbb265b8d 100644 --- a/src/sage/schemes/projective/proj_bdd_height.py +++ b/src/sage/schemes/projective/proj_bdd_height.py @@ -28,7 +28,7 @@ from sage.schemes.projective.projective_space import ProjectiveSpace lazy_import('sage.geometry.polyhedron.constructor', 'Polyhedron') -lazy_import('sage.libs.pari.all', 'pari') +lazy_import('sage.libs.pari', 'pari') lazy_import('sage.matrix.constructor', ['matrix', 'column_matrix']) lazy_import('sage.modules.free_module_element', 'vector') lazy_import('sage.rings.number_field.unit_group', 'UnitGroup') diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 9073faa9706..1ff1773f07b 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -982,9 +982,9 @@ def subscheme(self, X): TESTS:: sage: TestSuite(X).run(skip=["_test_an_element", "_test_elements",\ - "_test_elements_eq", "_test_some_elements", "_test_elements_eq_reflexive",\ - "_test_elements_eq_symmetric", "_test_elements_eq_transitive",\ - "_test_elements_neq"]) + ....: "_test_elements_eq", "_test_some_elements", "_test_elements_eq_reflexive",\ + ....: "_test_elements_eq_symmetric", "_test_elements_eq_transitive",\ + ....: "_test_elements_neq"]) """ R = self.base_ring() if R.is_field() and R.is_exact(): diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx index b20fa8cbe8a..098074b1649 100644 --- a/src/sage/sets/recursively_enumerated_set.pyx +++ b/src/sage/sets/recursively_enumerated_set.pyx @@ -1122,11 +1122,8 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic): {0} sage: next(it) {-1, 1} - sage: from cysignals.alarm import alarm - sage: alarm(0.02); next(it) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.02): next(it) sage: next(it) Traceback (most recent call last): ... @@ -1175,11 +1172,8 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic): ....: sleep(0.1r) ....: return [a - 1, a + 1] sage: C = RecursivelyEnumeratedSet([0], f, structure='symmetric') - sage: from cysignals.alarm import alarm - sage: alarm(0.45); C.graded_component(10) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.45): C.graded_component(10) sage: C.graded_component(1) {-1, 1} sage: C.graded_component(2) @@ -1394,11 +1388,8 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic): ....: sleep(0.1r) ....: return [a + 1, a + I] sage: C = RecursivelyEnumeratedSet([0], f, structure='graded') - sage: from cysignals.alarm import alarm - sage: alarm(0.45); C.graded_component(10) - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.45): C.graded_component(10) sage: C.graded_component(2) {2*I, I + 1, 2} sage: C.graded_component(3) diff --git a/src/sage/stats/basic_stats.py b/src/sage/stats/basic_stats.py index 47d890cf0ab..887e4dfd89f 100644 --- a/src/sage/stats/basic_stats.py +++ b/src/sage/stats/basic_stats.py @@ -222,7 +222,7 @@ def std(v, bias=False): sage: # needs numpy sage: import numpy sage: if int(numpy.version.short_version[0]) > 1: - ....: numpy.set_printoptions(legacy="1.25") + ....: _ = numpy.set_printoptions(legacy="1.25") sage: x = numpy.array([1,2,3,4,5]) sage: std(x, bias=False) 1.5811388300841898 @@ -299,7 +299,7 @@ def variance(v, bias=False): 0.4897530450000000? sage: import numpy # needs numpy sage: if int(numpy.version.short_version[0]) > 1: # needs numpy - ....: numpy.set_printoptions(legacy="1.25") # needs numpy + ....: _ = numpy.set_printoptions(legacy="1.25") # needs numpy sage: x = numpy.array([1,2,3,4,5]) # needs numpy sage: variance(x, bias=False) # needs numpy 2.5 diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 6861cfb5be3..35c76cebc67 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -521,7 +521,7 @@ cdef class CoercionModel: sage: import numpy # needs numpy sage: if int(numpy.version.short_version[0]) > 1: # needs numpy - ....: numpy.set_printoptions(legacy="1.25") # needs numpy + ....: __ = numpy.set_printoptions(legacy="1.25") # needs numpy sage: # needs sage.rings.real_mpfr sage: x = polygen(RR) diff --git a/src/sage/structure/coerce_actions.pyx b/src/sage/structure/coerce_actions.pyx index 17bbd397c98..f146a332368 100644 --- a/src/sage/structure/coerce_actions.pyx +++ b/src/sage/structure/coerce_actions.pyx @@ -801,10 +801,8 @@ cdef class IntegerMulAction(IntegerAction): sage: # needs sage.schemes sage: P = E([2,1,1]) - sage: alarm(0.001); 2^(10^8) * P - Traceback (most recent call last): - ... - AlarmInterrupt + sage: from sage.doctest.util import ensure_interruptible_after + sage: with ensure_interruptible_after(0.001): 2^(10^8) * P Verify that cysignals correctly detects that the above exception has been handled:: diff --git a/src/sage/structure/factorization.py b/src/sage/structure/factorization.py index ab3fa717031..e1279fec42f 100644 --- a/src/sage/structure/factorization.py +++ b/src/sage/structure/factorization.py @@ -918,7 +918,7 @@ def __pari__(self): sage: pari(g) # needs sage.libs.pari [x - 1, 1; x + 1, 1; x^4 - x^3 + x^2 - x + 1, 1; x^4 + x^3 + x^2 + x + 1, 1] """ - from sage.libs.pari.all import pari + from sage.libs.pari import pari from itertools import chain n = len(self) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 1dd2ae070ca..e9aa7d0ec3e 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -2863,17 +2863,17 @@ cdef class Parent(sage.structure.category_object.CategoryObject): cpdef bint is_exact(self) except -2: """ - Test whether the ring is exact. + Test whether elements of this parent are represented exactly. .. NOTE:: This defaults to true, so even if it does return ``True`` - you have no guarantee (unless the ring has properly + you have no guarantee (unless the parent has properly overloaded this). OUTPUT: - Return ``True`` if elements of this ring are represented exactly, i.e., + Return ``True`` if elements of this parent are represented exactly, i.e., there is no precision loss when doing arithmetic. EXAMPLES:: diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 7102316be70..8605cc2c729 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -972,7 +972,7 @@ cdef class SageObject: return self.__pari except AttributeError: pass - from sage.libs.pari.all import pari + from sage.libs.pari import pari x = pari(self._pari_init_()) if self._interface_is_cached_(): try: diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index da4d5db908f..ee31dae4e74 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -191,10 +191,10 @@ Check that :issue:`9880` is fixed:: sage: b = [var('b_%s'%i) for i in range(4)] sage: precomp = (2^b_2 + 2)*(2^b_1 + 2^(-b_1) + 2^b_1*2^b_0 - \ - 2^b_1*2^(-b_0) - 2^(-b_1)*2^b_0 - 2^(-b_1)*2^(-b_0) + \ - 2^b_0 + 2^(-b_0) - 9) + (2^b_1 + 2^(-b_1) + \ - 2^b_1*2^b_0 - 2^b_1*2^(-b_0) - 2^(-b_1)*2^b_0 - \ - 2^(-b_1)*2^(-b_0) + 2^b_0 + 2^(-b_0) - 9)/2^b_2 + ....: 2^b_1*2^(-b_0) - 2^(-b_1)*2^b_0 - 2^(-b_1)*2^(-b_0) + \ + ....: 2^b_0 + 2^(-b_0) - 9) + (2^b_1 + 2^(-b_1) + \ + ....: 2^b_1*2^b_0 - 2^b_1*2^(-b_0) - 2^(-b_1)*2^b_0 - \ + ....: 2^(-b_1)*2^(-b_0) + 2^b_0 + 2^(-b_0) - 9)/2^b_2 sage: repl_dict = {b_0: b_0, b_3: b_1, b_2: b_3, b_1: b_2} sage: P = precomp.substitute(repl_dict) sage: P.expand() @@ -209,35 +209,35 @@ Check that :issue:`9880` is fixed:: sage: _0,b_1,b_2=var('b_0,b_1,b_2') sage: f = 1/27*b_2^2/(2^b_2)^2 + 1/27*b_1^2/(2^b_1)^2 + \ - 1/27*b_0^2/(2^b_0)^2 + 1/27*b_2/(2^b_2)^2 - 2/81/(2^b_2)^2 + \ - 1/27*b_1/(2^b_1)^2 + 8/243/(2^b_2)^2 - 1/81*b_0/(2^b_0)^2 - \ - 1/27*b_1^2/((2^b_2)^2*(2^b_1)^2) - \ - 1/27*b_0^2/((2^b_2)^2*(2^b_0)^2) - 20/243/(2^b_1)^2 + 1/9/2^b_0 \ - + 4/81*b_0/(2^b_0)^2 - 8/243/(2^b_2)^2 - 2/9/(2^b_2*2^b_1) - \ - 2/9/(2^b_2*2^b_0) + 8/243/(2^b_1)^2 - 1/9/2^b_0 + \ - 2/9/(2^b_2*2^b_1) + 2/9/(2^b_2*2^b_0) - \ - 2/27*b_1*b_2/((2^b_2)^2*(2^b_1)^2) - \ - 1/27*b_2^2/((2^b_2)^2*(2^b_1)^2) - \ - 2/27*b_0*b_2/((2^b_2)^2*(2^b_0)^2) - \ - 1/27*b_2^2/((2^b_2)^2*(2^b_0)^2) + 2/81/(2^b_1)^2 - \ - 1/27*b_0^2/((2^b_1)^2*(2^b_0)^2) - \ - 2/27*b_0*b_1/((2^b_1)^2*(2^b_0)^2) - \ - 1/27*b_1^2/((2^b_1)^2*(2^b_0)^2) - 2/81/(2^b_0)^2 + \ - 5/27*b_1/((2^b_2)^2*(2^b_1)^2) + 5/27*b_2/((2^b_2)^2*(2^b_1)^2) \ - + 5/27*b_0/((2^b_2)^2*(2^b_0)^2) + \ - 5/27*b_2/((2^b_2)^2*(2^b_0)^2) + 5/27*b_0/((2^b_1)^2*(2^b_0)^2) \ - + 5/27*b_1/((2^b_1)^2*(2^b_0)^2) - 4/81/((2^b_2)^2*(2^b_1)^2) + \ - 1/27*b_0^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 2/27*b_0*b_1/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 2/27*b_0*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 1/27*b_1^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 2/27*b_1*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 1/27*b_2^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ - 4/81/((2^b_2)^2*(2^b_0)^2) - 4/81/((2^b_1)^2*(2^b_0)^2) - \ - 11/27*b_0/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ - 11/27*b_1/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ - 11/27*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ - 64/81/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + 35/81 + ....: 1/27*b_0^2/(2^b_0)^2 + 1/27*b_2/(2^b_2)^2 - 2/81/(2^b_2)^2 + \ + ....: 1/27*b_1/(2^b_1)^2 + 8/243/(2^b_2)^2 - 1/81*b_0/(2^b_0)^2 - \ + ....: 1/27*b_1^2/((2^b_2)^2*(2^b_1)^2) - \ + ....: 1/27*b_0^2/((2^b_2)^2*(2^b_0)^2) - 20/243/(2^b_1)^2 + 1/9/2^b_0 \ + ....: + 4/81*b_0/(2^b_0)^2 - 8/243/(2^b_2)^2 - 2/9/(2^b_2*2^b_1) - \ + ....: 2/9/(2^b_2*2^b_0) + 8/243/(2^b_1)^2 - 1/9/2^b_0 + \ + ....: 2/9/(2^b_2*2^b_1) + 2/9/(2^b_2*2^b_0) - \ + ....: 2/27*b_1*b_2/((2^b_2)^2*(2^b_1)^2) - \ + ....: 1/27*b_2^2/((2^b_2)^2*(2^b_1)^2) - \ + ....: 2/27*b_0*b_2/((2^b_2)^2*(2^b_0)^2) - \ + ....: 1/27*b_2^2/((2^b_2)^2*(2^b_0)^2) + 2/81/(2^b_1)^2 - \ + ....: 1/27*b_0^2/((2^b_1)^2*(2^b_0)^2) - \ + ....: 2/27*b_0*b_1/((2^b_1)^2*(2^b_0)^2) - \ + ....: 1/27*b_1^2/((2^b_1)^2*(2^b_0)^2) - 2/81/(2^b_0)^2 + \ + ....: 5/27*b_1/((2^b_2)^2*(2^b_1)^2) + 5/27*b_2/((2^b_2)^2*(2^b_1)^2) \ + ....: + 5/27*b_0/((2^b_2)^2*(2^b_0)^2) + \ + ....: 5/27*b_2/((2^b_2)^2*(2^b_0)^2) + 5/27*b_0/((2^b_1)^2*(2^b_0)^2) \ + ....: + 5/27*b_1/((2^b_1)^2*(2^b_0)^2) - 4/81/((2^b_2)^2*(2^b_1)^2) + \ + ....: 1/27*b_0^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 2/27*b_0*b_1/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 2/27*b_0*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 1/27*b_1^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 2/27*b_1*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 1/27*b_2^2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ + ....: 4/81/((2^b_2)^2*(2^b_0)^2) - 4/81/((2^b_1)^2*(2^b_0)^2) - \ + ....: 11/27*b_0/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ + ....: 11/27*b_1/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) - \ + ....: 11/27*b_2/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + \ + ....: 64/81/((2^b_2)^2*(2^b_1)^2*(2^b_0)^2) + 35/81 sage: f.nops() 38 @@ -3596,10 +3596,9 @@ cdef class Expression(Expression_abc): sage: k = 26 sage: bool(2/(2*pi)^(2*k) <= abs(bernoulli(2*k)/factorial(2*k))) True - sage: t = log(17179815199/17179869184) + 727717503781781876485802\ - ....: 752874818120860129694543334299450155913077668355/2315841784\ - ....: 74632390847141970017375815706539969331281128078915168015826\ - ....: 259279872 + sage: t = (log(17179815199/17179869184) + + ....: 727717503781781876485802752874818120860129694543334299450155913077668355 / + ....: 231584178474632390847141970017375815706539969331281128078915168015826259279872) sage: v = -53985/17179869184 sage: bool(abs(t) < 1.213*2^-56*v^4) True @@ -7828,18 +7827,18 @@ cdef class Expression(Expression_abc): sage: y = var('y') sage: z = var('z') sage: e = 792*z^8*w^4*x^3*y^4*u^7 + 24*z^4*w^4*x^2*y^3*u^4 + \ - 264*z^8*w^3*x^2*y^7*u^5 + 198*z^4*w^5*x^5*y*u^6 + 110*z^2*w^3*x^5*y^4*u^6 \ - - 120*z^8*w*x^4*u^6 - 480*z^5*w*x^4*y^6*u^8 - 720*z^7*x^3*y^3*u^7 + \ - 165*z^4*w^2*x^4*y*u^5 + 450*z^8*w^6*x^2*y*u^8 + 40*z^2*w^3*x^3*y^3*u^6 - \ - 288*z^7*w^2*x^3*y^6*u^6 + 250*z^6*w^4*x^2*y^4*u^8 + \ - 576*z^7*w^7*x^2*y^4*u^8 - 80*z^6*w^2*x^5*y^3*u^7 - 144*z^8*w^4*x^5*u^7 + \ - 120*z^4*w*x^2*y^6*u^6 + 320*z^5*w^5*x^2*y^7*u^8 + 192*z^7*w^6*x*y^7*u^6 - \ - 12*z^4*w^3*x^3*y^5*u^6 - 36*z^4*w^4*x^4*y^2*u^8 + 72*z^4*w^5*x^3*u^6 - \ - 20*z^2*w^2*x^4*y^5*u^8 + 660*z^8*w*x^2*y^4*u^6 + 66*z^4*w^4*x^4*y^4*u^4 + \ - 440*z^6*w^2*x^3*y^7*u^7 - 30*z^4*w*x^3*y^2*u^7 - 48*z^8*w^3*x^4*y^3*u^5 + \ - 72*z^6*w^2*x*y^6*u^4 - 864*z^7*w^3*x^4*y^3*u^8 + 480*z^7*w^4*x*y^4*u^7 + \ - 60*z^4*w^2*x^2*u^5 + 375*z^8*w^3*x*y*u^7 + 150*z^8*w^5*x*y^4*u^6 + \ - 180*z^6*x*y^3*u^5 + 216*z^6*w^3*x^2*y^3*u^6; + ....: 264*z^8*w^3*x^2*y^7*u^5 + 198*z^4*w^5*x^5*y*u^6 + 110*z^2*w^3*x^5*y^4*u^6 \ + ....: - 120*z^8*w*x^4*u^6 - 480*z^5*w*x^4*y^6*u^8 - 720*z^7*x^3*y^3*u^7 + \ + ....: 165*z^4*w^2*x^4*y*u^5 + 450*z^8*w^6*x^2*y*u^8 + 40*z^2*w^3*x^3*y^3*u^6 - \ + ....: 288*z^7*w^2*x^3*y^6*u^6 + 250*z^6*w^4*x^2*y^4*u^8 + \ + ....: 576*z^7*w^7*x^2*y^4*u^8 - 80*z^6*w^2*x^5*y^3*u^7 - 144*z^8*w^4*x^5*u^7 + \ + ....: 120*z^4*w*x^2*y^6*u^6 + 320*z^5*w^5*x^2*y^7*u^8 + 192*z^7*w^6*x*y^7*u^6 - \ + ....: 12*z^4*w^3*x^3*y^5*u^6 - 36*z^4*w^4*x^4*y^2*u^8 + 72*z^4*w^5*x^3*u^6 - \ + ....: 20*z^2*w^2*x^4*y^5*u^8 + 660*z^8*w*x^2*y^4*u^6 + 66*z^4*w^4*x^4*y^4*u^4 + \ + ....: 440*z^6*w^2*x^3*y^7*u^7 - 30*z^4*w*x^3*y^2*u^7 - 48*z^8*w^3*x^4*y^3*u^5 + \ + ....: 72*z^6*w^2*x*y^6*u^4 - 864*z^7*w^3*x^4*y^3*u^8 + 480*z^7*w^4*x*y^4*u^7 + \ + ....: 60*z^4*w^2*x^2*u^5 + 375*z^8*w^3*x*y*u^7 + 150*z^8*w^5*x*y^4*u^6 + \ + ....: 180*z^6*x*y^3*u^5 + 216*z^6*w^3*x^2*y^3*u^6; sage: d = e.diff(x) sage: gcd(d,e) / (u^4*z^2) in QQ True diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 86860237d5b..132bed222a9 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -962,7 +962,7 @@ cdef class BuiltinFunction(Function): sage: import numpy # needs numpy sage: if int(numpy.version.short_version[0]) > 1: # needs numpy - ....: numpy.set_printoptions(legacy="1.25") # needs numpy + ....: __ = numpy.set_printoptions(legacy="1.25") # needs numpy sage: sin(numpy.int32(0)) # needs numpy 0.0 diff --git a/src/sage/symbolic/pynac_impl.pxi b/src/sage/symbolic/pynac_impl.pxi index fcd084cea81..6b2d45499df 100644 --- a/src/sage/symbolic/pynac_impl.pxi +++ b/src/sage/symbolic/pynac_impl.pxi @@ -43,7 +43,7 @@ from sage.libs.gmp.all cimport * from sage.libs.gsl.complex cimport * from sage.libs.gsl.gamma cimport gsl_sf_lngamma_complex_e from sage.libs.mpmath import utils as mpmath_utils -from sage.libs.pari.all import pari +from sage.libs.pari import pari from sage.misc.persist import loads, dumps from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer, smallInteger diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index d5d435e6757..5b7bef7c442 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -1155,7 +1155,7 @@ cdef class NumpyToSRMorphism(Morphism): sage: import numpy # needs numpy sage: if int(numpy.version.short_version[0]) > 1: # needs numpy - ....: numpy.set_printoptions(legacy="1.25") # needs numpy + ....: _ = numpy.set_printoptions(legacy="1.25") # needs numpy sage: f(x) = x^2 sage: f(numpy.int8('2')) # needs numpy 4 diff --git a/src/sage/tensor/modules/tensor_with_indices.py b/src/sage/tensor/modules/tensor_with_indices.py index be7c88a859c..bd3edbb81d0 100644 --- a/src/sage/tensor/modules/tensor_with_indices.py +++ b/src/sage/tensor/modules/tensor_with_indices.py @@ -692,7 +692,7 @@ def __add__(self, other): sage: b[:] = [[-1,2,-3], [-4,5,6], [7,-8,9]] sage: T = a*a*b*b sage: 1/4*(T["ijkl_abcd"] + T["jikl_abcd"] + T["ijkl_abdc"]\ - + T["jikl_abdc"]) == T["(..).._..(..)"]["ijkl_abcd"] + ....: + T["jikl_abdc"]) == T["(..).._..(..)"]["ijkl_abcd"] True """ # Check tensor types are compatible @@ -770,7 +770,7 @@ def __sub__(self, other): sage: b[:] = [[-1,2,-3], [-4,5,6], [7,-8,9]] sage: T = a*a*b*b sage: 1/4*(T["ijkl_abcd"]-T["jikl_abcd"] - T["ijkl_abdc"]\ - + T["jikl_abdc"] ) == T["[..].._..[..]"]["ijkl_abcd"] + ....: + T["jikl_abdc"] ) == T["[..].._..[..]"]["ijkl_abcd"] True """ return self + (-other) diff --git a/src/sage/tests/book_stein_ent.py b/src/sage/tests/book_stein_ent.py index 970b377e962..2e942934b9f 100644 --- a/src/sage/tests/book_stein_ent.py +++ b/src/sage/tests/book_stein_ent.py @@ -20,11 +20,11 @@ 3^2 * 223 sage: factor(31415926535898) 2 * 3 * 53 * 73 * 2531 * 534697 -sage: n = 7403756347956171282804679609742957314259318888\ -....: 9231289084936232638972765034028266276891996419625117\ -....: 8439958943305021275853701189680982867331732731089309\ -....: 0055250511687706329907239638078671008609696253793465\ -....: 0563796359 +sage: n = Integer('7403756347956171282804679609742957314259318888' +....: '9231289084936232638972765034028266276891996419625117' +....: '8439958943305021275853701189680982867331732731089309' +....: '0055250511687706329907239638078671008609696253793465' +....: '0563796359') sage: len(n.str(2)) 704 sage: len(n.str(10)) diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py index e283c56c74b..524783b5bf3 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/float_doctest.py @@ -222,7 +222,7 @@ sage: c = sumcomp(y0,delta0,RDF(0.0),n,a) sage: print("exact - compensated summation: %.5f" \ - % RDF(s-RR(c).exact_rational())) + ....: % RDF(s-RR(c).exact_rational())) exact - compensated summation: -0.00042 Sage example in ./float.tex, line 1242:: diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py index 5bcd8aa9de6..f8b5ca511b2 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/integration_doctest.py @@ -181,7 +181,7 @@ Sage example in ./integration.tex, line 990:: sage: f = lambda y: numerical_integral(lambda x: exp(y*sin(x)), \ - 0, sqrt(y))[0] + ....: 0, sqrt(y))[0] sage: f(0.0), f(0.5), f(1.0) # abs tol 2e-15 (0.0, 0.8414895067661431, 1.6318696084180513) @@ -193,14 +193,14 @@ Sage example in ./integration.tex, line 1008:: sage: f = lambda y: sage.calculus.calculus.nintegral(exp(y*sin(x)), \ - x, 0, sqrt(y))[0] + ....: x, 0, sqrt(y))[0] sage: numerical_integral(f, 0, 1) # abs tol 2e-16 (0.8606791942204567, 6.301207560882096e-07) Sage example in ./integration.tex, line 1016:: sage: f = lambda y: RDF(mpmath.quad(lambda x: mpmath.exp(y*mpmath.sin(x)), \ - [0, sqrt(y)])) + ....: [0, sqrt(y)])) sage: numerical_integral(f, 0, 1) # abs tol 2e-16 (0.8606791942204567, 6.301207561187562e-07) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index ac0af5dcd7b..3204373e271 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -802,7 +802,7 @@ def __hash__(self): """ return hash(self.__set) - def _repr_(self): + def _repr_(self) -> str: """ Print representation. @@ -2003,7 +2003,7 @@ def suspension(self, n=1, is_mutable=True): rename_vertices=True) return self.suspension(1, is_mutable).suspension(int(n-1), is_mutable) - def disjoint_union(self, right, rename_vertices=None, is_mutable=True): + def disjoint_union(self, right, is_mutable=True): """ The disjoint union of this simplicial complex with another one. @@ -2011,14 +2011,6 @@ def disjoint_union(self, right, rename_vertices=None, is_mutable=True): - ``right`` -- the other simplicial complex (the right-hand factor) - - ``rename_vertices`` -- boolean (default: ``True``); if this is - ``True``, the vertices in the disjoint union will be renamed by the - formula: vertex "v" in the left-hand factor --> vertex "Lv" in the - disjoint union, vertex "w" in the right-hand factor --> vertex "Rw" - in the disjoint union. If this is false, this tries to construct the - disjoint union without renaming the vertices; this will cause - problems if the two factors have any vertices with names in common. - EXAMPLES:: sage: S1 = simplicial_complexes.Sphere(1) @@ -2026,15 +2018,8 @@ def disjoint_union(self, right, rename_vertices=None, is_mutable=True): sage: S1.disjoint_union(S2).homology() # needs sage.modules {0: Z, 1: Z, 2: Z} """ - if rename_vertices is not None: - from sage.misc.superseded import deprecation - deprecation(35907, 'the "rename_vertices" argument is deprecated') - - facets = [] - for f in self._facets: - facets.append(tuple(["L" + str(v) for v in f])) - for f in right._facets: - facets.append(tuple(["R" + str(v) for v in f])) + facets = [tuple(f"L{v}" for v in f) for f in self._facets] + facets.extend(tuple(f"R{v}" for v in f) for f in right._facets) return SimplicialComplex(facets, is_mutable=is_mutable) def wedge(self, right, rename_vertices=True, is_mutable=True): @@ -3465,7 +3450,7 @@ def minimal_nonfaces(self): TESTS:: sage: SC = SimplicialComplex([(0,1,2),(0,2,3),(2,3,4),(1,2,4), \ - (1,4,5),(0,3,6),(3,6,7),(4,5,7)]) + ....: (1,4,5),(0,3,6),(3,6,7),(4,5,7)]) This was taking a long time before :issue:`20078`:: @@ -4516,7 +4501,7 @@ def _translation_from_numeric(self): # this function overrides the standard one for GenericCellComplex, # because it lists the maximal faces, not the total number of faces. - def _repr_(self): + def _repr_(self) -> str: """ Print representation. diff --git a/src/sage/version.py b/src/sage/version.py index a8b68889f48..6291f1b503f 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.6.beta5' -date = '2025-01-26' -banner = 'SageMath version 10.6.beta5, Release Date: 2025-01-26' +version = '10.6.beta6' +date = '2025-02-10' +banner = 'SageMath version 10.6.beta6, Release Date: 2025-02-10' diff --git a/src/tox.ini b/src/tox.ini index 81822de97cf..4ca4f5bd283 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -311,7 +311,7 @@ passenv = RUFF_OUTPUT_FORMAT # 1 F402 [ ] Import `factor` from line 259 shadowed by loop variable # 1 PLC0208 [*] Use a sequence type instead of a `set` when iterating over values # -commands = ruff check --ignore E402,E721,E731,E741,E742,E743,F401,F402,F403,F405,F821,F841,I001,PLC0206,PLC0208,PLC2401,PLC3002,PLE0302,PLR0124,PLR0402,PLR0911,PLR0912,PLR0913,PLR0915,PLR1704,PLR1711,PLR1714,PLR1716,PLR1736,PLR2004,PLR5501,PLW0120,PLW0127,PLW0211,PLW0602,PLW0603,PLW0642,PLW1508,PLW1510,PLW2901,PLW3301 {posargs:{toxinidir}/sage/} +commands = ruff check --ignore E402,E721,E731,E741,E742,E743,F401,F402,F403,F405,F821,F841,I001,PLC0206,PLC0208,PLC2401,PLC3002,PLE0302,PLR0124,PLR0402,PLR0911,PLR0912,PLR0913,PLR0915,PLR1704,PLR1711,PLR1714,PLR1716,PLR1736,PLR2004,PLR5501,PLW0120,PLW0211,PLW0602,PLW0603,PLW0642,PLW1508,PLW1510,PLW2901,PLW3301 {posargs:{toxinidir}/sage/} [flake8] rst-roles =