diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index 40322ca86b9..2dc3ca61a41 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -51,7 +51,7 @@ diffParagraphs.forEach(paragraph => { EOF echo '' >> CHANGES.html echo '
' >> CHANGES.html -(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- *.html) > diff.txt +(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- "*.html") > diff.txt python3 - << EOF import os, re, html with open('diff.txt', 'r') as f: diff --git a/.ci/docker-exec-script.sh b/.ci/docker-exec-script.sh new file mode 100755 index 00000000000..60bbd70aee7 --- /dev/null +++ b/.ci/docker-exec-script.sh @@ -0,0 +1,14 @@ +#!/bin/sh -x +if [ $# -lt 3 ]; then + echo >&2 "usage: docker-exec-script.sh CONTAINER WORKDIR [VAR=VALUE...] SCRIPT" + exit 1 +fi +CONTAINER=$1 +WORKDIR=$2 +shift 2 +(echo "cd \"$WORKDIR\""; + while [ $# -gt 1 ]; do + echo "export \"$1\"" + shift + done; + cat "$1") | docker exec -i $CONTAINER bash -ex diff --git a/.ci/merge-fixes.sh b/.ci/merge-fixes.sh index 13350018221..9d36e40d72b 100755 --- a/.ci/merge-fixes.sh +++ b/.ci/merge-fixes.sh @@ -1,5 +1,8 @@ #!/bin/sh -# Apply open PRs labeled "blocker" from sagemath/sage as patches. +# Apply open PRs labeled "p: CI Fix" from sagemath/sage as patches. +# (policy set by vote in 2024-03, +# https://groups.google.com/g/sage-devel/c/OKwwUGyKveo/m/vpyCXYBqAAAJ) +# # This script is invoked by various workflows in .github/workflows # # The repository variable SAGE_CI_FIXES_FROM_REPOS can be set @@ -20,15 +23,15 @@ for REPO in ${SAGE_CI_FIXES_FROM_REPOSITORIES:-sagemath/sage}; do echo "Nothing to do for 'none' in SAGE_CI_FIXES_FROM_REPOSITORIES" ;; */*) - echo "Getting open PRs with 'blocker' status from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+blocker+%2F+1%22" + echo "Getting open PRs with 'p: CI Fix' label from https://github.com/$REPO/pulls?q=is%3Aopen+label%3A%22p%3A+CI+Fix%22" GH="gh -R $REPO" REPO_FILE="upstream/ci-fixes-${REPO%%/*}-${REPO##*/}" - PRs="$($GH pr list --label "p: blocker / 1" --json number --jq '.[].number' | tee $REPO_FILE)" + PRs="$($GH pr list --label "p: CI Fix" --json number --jq '.[].number' | tee $REPO_FILE)" date -u +"%Y-%m-%dT%H:%M:%SZ" > $REPO_FILE.date # Record the date, for future reference if [ -z "$PRs" ]; then - echo "Nothing to do: Found no open PRs with 'blocker' status in $REPO." + echo "Nothing to do: Found no open PRs with 'p: CI Fix' label in $REPO." else - echo "Found open PRs with 'blocker' status in $REPO: $(echo $PRs)" + echo "Found open PRs with 'p: CI Fix' label in $REPO: $(echo $PRs)" git tag -f test_base git commit -q -m "Uncommitted changes" --no-allow-empty -a for a in $PRs; do diff --git a/build/bin/write-dockerfile.sh b/.ci/write-dockerfile.sh similarity index 79% rename from build/bin/write-dockerfile.sh rename to .ci/write-dockerfile.sh index 3c62d6082e4..0aa53414553 100755 --- a/build/bin/write-dockerfile.sh +++ b/.ci/write-dockerfile.sh @@ -27,13 +27,13 @@ for SPKG in $(sage-package list --has-file=spkg-configure.m4 $SAGE_PACKAGE_LIST_ CONFIGURE_ARGS+="--with-system-${SPKG}=${WITH_SYSTEM_SPKG} " fi done -echo "# Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh" +echo "# Automatically generated by SAGE_ROOT/.ci/write-dockerfile.sh" echo "# the :comments: separate the generated file into sections" echo "# to simplify writing scripts that customize this file" ADD="ADD $__CHOWN" RUN=RUN cat <
+Date: Thu, 25 Jan 2024 08:29:17 -0500
+Subject: [PATCH] Temporary GCC 14 workaround
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Fixes https://github.com/linbox-team/givaro/issues/226 “GCC 14: No match
+for operator= for Givaro::ZRing”
+
+Recommended in
+https://github.com/linbox-team/givaro/issues/226#issuecomment-1908853755
+---
+ src/kernel/integer/random-integer.h | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/src/kernel/integer/random-integer.h b/src/kernel/integer/random-integer.h
+index f9361d33..ea189a36 100644
+--- a/src/kernel/integer/random-integer.h
++++ b/src/kernel/integer/random-integer.h
+@@ -87,7 +87,6 @@ namespace Givaro
+ if (this != &R) {
+ _bits = R._bits;
+ _integer = R._integer;
+- const_cast(_ring)=R._ring;
+ }
+ return *this;
+ }
+--
diff --git a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch b/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch
deleted file mode 100644
index 28fd95088c8..00000000000
--- a/build/pkgs/givaro/patches/fix-ksh-pkgconfig.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-From 91dcba743e15288abe69966a5f71704d9adcc57c Mon Sep 17 00:00:00 2001
-From: Dima Pasechnik
-Date: Fri, 8 May 2020 10:22:57 +0100
-Subject: [PATCH 1/1] remove 1st and last lines in givaro.pc.in
-
----
- givaro.pc.in | 2 --
- 1 file changed, 2 deletions(-)
-
-diff --git a/givaro.pc.in b/givaro.pc.in
-index 285b854..af38bf3 100644
---- a/givaro.pc.in
-+++ b/givaro.pc.in
-@@ -1,4 +1,3 @@
--/------------------ givaro.pc ------------------------
- prefix=@prefix@
- exec_prefix=@prefix@
- libdir=@prefix@/lib
-@@ -11,4 +10,3 @@ Version: @VERSION@
- Requires:
- Libs: -L@libdir@ -lgivaro @LIBS@
- Cflags: -I@includedir@ @REQUIRED_FLAGS@
--\-------------------------------------------------------
-\ No newline at end of file
---
-2.26.2
-
diff --git a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch b/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch
deleted file mode 100644
index 3d7e100ad09..00000000000
--- a/build/pkgs/givaro/patches/givaro-26932_recintvsflint_longlong.patch
+++ /dev/null
@@ -1,2501 +0,0 @@
-diff --git a/src/kernel/recint/reclonglong.h b/src/kernel/recint/reclonglong.h
-index cab889b..1b1e07e 100644
---- a/src/kernel/recint/reclonglong.h
-+++ b/src/kernel/recint/reclonglong.h
-@@ -41,7 +41,6 @@
- */
-
- /* longlong.h may already be included from another library (e.g. flint) */
--#ifndef add_ssaaaa
-
- #define __BITS4 (W_TYPE_SIZE / 4)
- #define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
-@@ -56,14 +55,14 @@
-
- /* Define auxiliary asm macros.
-
-- 1) umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two
-+ 1) recint_umul_ppmm(high_prod, low_prod, multiplier, multiplicand) multiplies two
- UWtype integers MULTIPLIER and MULTIPLICAND, and generates a two UWtype
- word product in HIGH_PROD and LOW_PROD.
-
- 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
-- UDWtype product. This is just a variant of umul_ppmm.
-+ UDWtype product. This is just a variant of recint_umul_ppmm.
-
-- 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
-+ 3) recint_udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
- denominator) divides a UDWtype, composed by the UWtype integers
- HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
- in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
-@@ -72,24 +71,24 @@
- UDIV_NEEDS_NORMALIZATION is defined to 1.
-
- 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
-- denominator). Like udiv_qrnnd but the numbers are signed. The quotient
-+ denominator). Like recint_udiv_qrnnd but the numbers are signed. The quotient
- is rounded towards 0.
-
-- 5) count_leading_zeros(count, x) counts the number of zero-bits from the
-+ 5) recint_count_leading_zeros(count, x) counts the number of zero-bits from the
- msb to the first non-zero bit in the UWtype X. This is the number of
- steps X needs to be shifted left to set the msb. Undefined for X == 0,
-- unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
-+ unless the symbol RECINT_COUNT_LEADING_ZEROS_0 is defined to some value.
-
-- 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
-+ 6) recint_count_trailing_zeros(count, x) like recint_count_leading_zeros, but counts
- from the least significant end.
-
-- 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
-+ 7) recint_add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
- high_addend_2, low_addend_2) adds two UWtype integers, composed by
- HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
- respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
- (i.e. carry out) is not stored anywhere, and is lost.
-
-- 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
-+ 8) recint_sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
- high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
- composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
- LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE
-@@ -102,7 +101,7 @@
-
- Notes:
-
-- For add_ssaaaa the two high and two low addends can both commute, but
-+ For recint_add_ssaaaa the two high and two low addends can both commute, but
- unfortunately gcc only supports one "%" commutative in each asm block.
- This has always been so but is only documented in recent versions
- (eg. pre-release 3.3). Having two or more "%"s can cause an internal
-@@ -123,9 +122,9 @@
- for the CPUs below! */
-
-
--/* count_leading_zeros_gcc_clz is count_leading_zeros implemented with gcc
-+/* recint_count_leading_zeros_gcc_clz is recint_count_leading_zeros implemented with gcc
- 3.4 __builtin_clzl or __builtin_clzll, according to our limb size.
-- Similarly count_trailing_zeros_gcc_ctz using __builtin_ctzl or
-+ Similarly recint_count_trailing_zeros_gcc_ctz using __builtin_ctzl or
- __builtin_ctzll.
-
- These builtins are only used when we check what code comes out, on some
-@@ -137,1697 +136,39 @@
- usage. We keep an asm block for use on prior versions of gcc though.
-
- For reference, __builtin_ffs existed in gcc prior to __builtin_clz, but
-- it's not used (for count_leading_zeros) because it generally gives extra
-+ it's not used (for recint_count_leading_zeros) because it generally gives extra
- code to ensure the result is 0 when the input is 0, which we don't need
- or want. */
-
- #ifdef _LONG_LONG_LIMB
--#define count_leading_zeros_gcc_clz(count,x) \
-+#define recint_count_leading_zeros_gcc_clz(count,x) \
- do { \
- (count) = __builtin_clzll (x); \
- } while (0)
- #else
--#define count_leading_zeros_gcc_clz(count,x) \
-+#define recint_count_leading_zeros_gcc_clz(count,x) \
- do { \
- (count) = __builtin_clzl (x); \
- } while (0)
- #endif
-
- #ifdef _LONG_LONG_LIMB
--#define count_trailing_zeros_gcc_ctz(count,x) \
-+#define recint_count_trailing_zeros_gcc_ctz(count,x) \
- do { \
- (count) = __builtin_ctzll (x); \
- } while (0)
- #else
--#define count_trailing_zeros_gcc_ctz(count,x) \
-+#define recint_count_trailing_zeros_gcc_ctz(count,x) \
- do { \
- (count) = __builtin_ctzl (x); \
- } while (0)
- #endif
-
-
--/* FIXME: The macros using external routines like __MPN(count_leading_zeros)
-- don't need to be under !NO_ASM */
--#if ! defined (NO_ASM)
--
--#if defined (__alpha) && W_TYPE_SIZE == 64
--/* Most alpha-based machines, except Cray systems. */
--#if defined (__GNUC__)
--#if __GMP_GNUC_PREREQ (3,3)
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- (ph) = __builtin_alpha_umulh (__m0, __m1); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#else
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("umulh %r1,%2,%0" \
-- : "=r" (ph) \
-- : "%rJ" (m0), "rI" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#endif
--#define UMUL_TIME 18
--#else /* ! __GNUC__ */
--#include
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- (ph) = __UMULH (m0, m1); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#endif
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __di; \
-- __di = __MPN(invert_limb) (d); \
-- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
-- } while (0)
--#define UDIV_PREINV_ALWAYS 1
--#define UDIV_NEEDS_NORMALIZATION 1
--#define UDIV_TIME 220
--#endif /* LONGLONG_STANDALONE */
--
--/* clz_tab is required in all configurations, since mpn/alpha/cntlz.asm
-- always goes into libgmp.so, even when not actually used. */
--#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
--
--#if defined (__GNUC__) && HAVE_HOST_CPU_alpha_CIX
--#define count_leading_zeros(COUNT,X) \
-- __asm__("ctlz %1,%0" : "=r"(COUNT) : "r"(X))
--#define count_trailing_zeros(COUNT,X) \
-- __asm__("cttz %1,%0" : "=r"(COUNT) : "r"(X))
--#endif /* clz/ctz using cix */
--
--#if ! defined (count_leading_zeros) \
-- && defined (__GNUC__) && ! defined (LONGLONG_STANDALONE)
-- /* ALPHA_CMPBGE_0 gives "cmpbge $31,src,dst", ie. test src bytes == 0.
-- "$31" is written explicitly in the asm, since an "r" constraint won't
-- select reg 31. There seems no need to worry about "r31" syntax for cray,
-- since gcc itself (pre-release 3.4) emits just $31 in various places. */
--#define ALPHA_CMPBGE_0(dst, src) \
-- do { asm ("cmpbge $31, %1, %0" : "=r" (dst) : "r" (src)); } while (0)
-- /* Zero bytes are turned into bits with cmpbge, a __clz_tab lookup counts
-- them, locating the highest non-zero byte. A second __clz_tab lookup
-- counts the leading zero bits in that byte, giving the result. */
--#define count_leading_zeros(count, x) \
-- do { \
-- UWtype __clz__b, __clz__c, __clz__x = (x); \
-- ALPHA_CMPBGE_0 (__clz__b, __clz__x); /* zero bytes */ \
-- __clz__b = __clz_tab [(__clz__b >> 1) ^ 0x7F]; /* 8 to 1 byte */ \
-- __clz__b = __clz__b * 8 - 7; /* 57 to 1 shift */ \
-- __clz__x >>= __clz__b; \
-- __clz__c = __clz_tab [__clz__x]; /* 8 to 1 bit */ \
-- __clz__b = 65 - __clz__b; \
-- (count) = __clz__b - __clz__c; \
-- } while (0)
--#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
--#endif /* clz using cmpbge */
--
--#if ! defined (count_leading_zeros) && ! defined (LONGLONG_STANDALONE)
--#if HAVE_ATTRIBUTE_CONST
--long __MPN(count_leading_zeros) (UDItype) __attribute__ ((const));
--#else
--long __MPN(count_leading_zeros) (UDItype);
--#endif
--#define count_leading_zeros(count, x) \
-- ((count) = __MPN(count_leading_zeros) (x))
--#endif /* clz using mpn */
--#endif /* __alpha */
--
--#if defined (__AVR) && W_TYPE_SIZE == 8
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- unsigned short __p = (unsigned short) (m0) * (m1); \
-- (ph) = __p >> 8; \
-- (pl) = __p; \
-- } while (0)
--#endif /* AVR */
--
--#if defined (_CRAY) && W_TYPE_SIZE == 64
--#include
--#define UDIV_PREINV_ALWAYS 1
--#define UDIV_NEEDS_NORMALIZATION 1
--#define UDIV_TIME 220
--long __MPN(count_leading_zeros) (UDItype);
--#define count_leading_zeros(count, x) \
-- ((count) = _leadz ((UWtype) (x)))
--#if defined (_CRAYIEEE) /* I.e., Cray T90/ieee, T3D, and T3E */
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- (ph) = _int_mult_upper (m0, m1); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __di; \
-- __di = __MPN(invert_limb) (d); \
-- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
-- } while (0)
--#endif /* LONGLONG_STANDALONE */
--#endif /* _CRAYIEEE */
--#endif /* _CRAY */
--
--#if defined (__ia64) && W_TYPE_SIZE == 64
-- /* This form encourages gcc (pre-release 3.4 at least) to emit predicated
-- "sub r=r,r" and "sub r=r,r,1", giving a 2 cycle latency. The generic
-- code using "al>= _c; \
-- if (_x >= 1 << 4) \
-- _x >>= 4, _c += 4; \
-- if (_x >= 1 << 2) \
-- _x >>= 2, _c += 2; \
-- _c += _x >> 1; \
-- (count) = W_TYPE_SIZE - 1 - _c; \
-- } while (0)
-- /* similar to what gcc does for __builtin_ffs, but 0 based rather than 1
-- based, and we don't need a special case for x==0 here */
--#define count_trailing_zeros(count, x) \
-- do { \
-- UWtype __ctz_x = (x); \
-- __asm__ ("popcnt %0 = %1" \
-- : "=r" (count) \
-- : "r" ((__ctz_x-1) & ~__ctz_x)); \
-- } while (0)
--#endif
--#if defined (__INTEL_COMPILER)
--#include
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UWtype _m0 = (m0), _m1 = (m1); \
-- ph = _m64_xmahu (_m0, _m1, 0); \
-- pl = _m0 * _m1; \
-- } while (0)
--#endif
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __di; \
-- __di = __MPN(invert_limb) (d); \
-- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
-- } while (0)
--#define UDIV_PREINV_ALWAYS 1
--#define UDIV_NEEDS_NORMALIZATION 1
--#endif
--#define UDIV_TIME 220
--#endif
--
--
--#if defined (__GNUC__)
--
-- /* We sometimes need to clobber "cc" with gcc2, but that would not be
-- understood by gcc1. Use cpp to avoid major code duplication. */
--#if __GNUC__ < 2
--#define __CLOBBER_CC
--#define __AND_CLOBBER_CC
--#else /* __GNUC__ >= 2 */
--#define __CLOBBER_CC : "cc"
--#define __AND_CLOBBER_CC , "cc"
--#endif /* __GNUC__ < 2 */
--
--#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add %1,%4,%5\n\taddc %0,%2,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub %1,%4,%5\n\tsubc %0,%2,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl))
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- USItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("multiplu %0,%1,%2" \
-- : "=r" (xl) \
-- : "r" (__m0), "r" (__m1)); \
-- __asm__ ("multmu %0,%1,%2" \
-- : "=r" (xh) \
-- : "r" (__m0), "r" (__m1)); \
-- } while (0)
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- __asm__ ("dividu %0,%3,%4" \
-- : "=r" (q), "=q" (r) \
-- : "1" (n1), "r" (n0), "r" (d))
--#define count_leading_zeros(count, x) \
-- __asm__ ("clz %0,%1" \
-- : "=r" (count) \
-- : "r" (x))
--#define COUNT_LEADING_ZEROS_0 32
--#endif /* __a29k__ */
--
--#if defined (__arc__)
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add.f\t%1, %4, %5\n\tadc\t%0, %2, %3" \
-- : "=r" (sh), \
-- "=&r" (sl) \
-- : "r" ((USItype) (ah)), \
-- "rIJ" ((USItype) (bh)), \
-- "%r" ((USItype) (al)), \
-- "rIJ" ((USItype) (bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub.f\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
-- : "=r" (sh), \
-- "=&r" (sl) \
-- : "r" ((USItype) (ah)), \
-- "rIJ" ((USItype) (bh)), \
-- "r" ((USItype) (al)), \
-- "rIJ" ((USItype) (bl)))
--#endif
--
--#if defined (__arm__) && !defined (__thumb__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("adds\t%1, %4, %5\n\tadc\t%0, %2, %3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- do { \
-- if (__builtin_constant_p (al)) \
-- { \
-- if (__builtin_constant_p (ah)) \
-- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
-- else \
-- __asm__ ("rsbs\t%1, %5, %4\n\tsbc\t%0, %2, %3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
-- } \
-- else if (__builtin_constant_p (ah)) \
-- { \
-- if (__builtin_constant_p (bl)) \
-- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
-- else \
-- __asm__ ("rsbs\t%1, %5, %4\n\trsc\t%0, %3, %2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rI" (ah), "r" (bh), "rI" (al), "r" (bl) __CLOBBER_CC); \
-- } \
-- else if (__builtin_constant_p (bl)) \
-- { \
-- if (__builtin_constant_p (bh)) \
-- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
-- else \
-- __asm__ ("subs\t%1, %4, %5\n\trsc\t%0, %3, %2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rI" (ah), "r" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
-- } \
-- else /* only bh might be a constant */ \
-- __asm__ ("subs\t%1, %4, %5\n\tsbc\t%0, %2, %3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "rI" (bh), "r" (al), "rI" (bl) __CLOBBER_CC); \
-- } while (0)
--#if 1 || defined (__arm_m__) /* `M' series has widening multiply support */
--#define umul_ppmm(xh, xl, a, b) \
-- __asm__ ("umull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b))
--#define UMUL_TIME 5
--#define smul_ppmm(xh, xl, a, b) \
-- __asm__ ("smull %0,%1,%2,%3" : "=&r" (xl), "=&r" (xh) : "r" (a), "r" (b))
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __di; \
-- __di = __MPN(invert_limb) (d); \
-- udiv_qrnnd_preinv (q, r, n1, n0, d, __di); \
-- } while (0)
--#define UDIV_PREINV_ALWAYS 1
--#define UDIV_NEEDS_NORMALIZATION 1
--#define UDIV_TIME 70
--#endif /* LONGLONG_STANDALONE */
--#else
--#define umul_ppmm(xh, xl, a, b) \
-- __asm__ ("%@ Inlined umul_ppmm\n" \
-- " mov %|r0, %2, lsr #16\n" \
-- " mov %|r2, %3, lsr #16\n" \
-- " bic %|r1, %2, %|r0, lsl #16\n" \
-- " bic %|r2, %3, %|r2, lsl #16\n" \
-- " mul %1, %|r1, %|r2\n" \
-- " mul %|r2, %|r0, %|r2\n" \
-- " mul %|r1, %0, %|r1\n" \
-- " mul %0, %|r0, %0\n" \
-- " adds %|r1, %|r2, %|r1\n" \
-- " addcs %0, %0, #65536\n" \
-- " adds %1, %1, %|r1, lsl #16\n" \
-- " adc %0, %0, %|r1, lsr #16" \
-- : "=&r" (xh), "=r" (xl) \
-- : "r" (a), "r" (b) \
-- : "r0", "r1", "r2")
--#define UMUL_TIME 20
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __r; \
-- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \
-- (r) = __r; \
-- } while (0)
--extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype);
--#define UDIV_TIME 200
--#endif /* LONGLONG_STANDALONE */
--#endif
-- /* This is a bizarre test, but GCC doesn't define useful common symbol. */
--#if defined (__ARM_ARCH_5__) || defined (__ARM_ARCH_5T__) || \
-- defined (__ARM_ARCH_5E__) || defined (__ARM_ARCH_5TE__)|| \
-- defined (__ARM_ARCH_6__) || defined (__ARM_ARCH_6J__) || \
-- defined (__ARM_ARCH_6K__) || defined (__ARM_ARCH_6Z__) || \
-- defined (__ARM_ARCH_6ZK__)|| defined (__ARM_ARCH_6T2__)|| \
-- defined (__ARM_ARCH_6M__) || defined (__ARM_ARCH_7__) || \
-- defined (__ARM_ARCH_7A__) || defined (__ARM_ARCH_7R__) || \
-- defined (__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
--#define count_leading_zeros(count, x) \
-- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x))
--#define COUNT_LEADING_ZEROS_0 32
--#endif
--#endif /* __arm__ */
--
--#if defined (__aarch64__) && W_TYPE_SIZE == 64
-- /* FIXME: Extend the immediate range for the low word by using both
-- ADDS and SUBS, since they set carry in the same way. */
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("adds\t%1, %x4, %5\n\tadc\t%0, %x2, %x3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rZ" (ah), "rZ" (bh), "%r" (al), "rI" (bl) __CLOBBER_CC)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subs\t%1, %x4, %5\n\tsbc\t%0, %x2, %x3" \
-- : "=r,r" (sh), "=&r,&r" (sl) \
-- : "rZ,rZ" (ah), "rZ,rZ" (bh), "r,Z" (al), "rI,r" (bl) __CLOBBER_CC)
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("umulh\t%0, %1, %2" : "=r" (ph) : "r" (m0), "r" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#define count_leading_zeros(count, x) \
-- __asm__ ("clz\t%0, %1" : "=r" (count) : "r" (x))
--#define COUNT_LEADING_ZEROS_0 64
--#endif /* __aarch64__ */
--
--#if defined (__clipper__) && W_TYPE_SIZE == 32
--#define umul_ppmm(w1, w0, u, v) \
-- ({union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __x; \
-- __asm__ ("mulwux %2,%0" \
-- : "=r" (__x.__ll) \
-- : "%0" ((USItype)(u)), "r" ((USItype)(v))); \
-- (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
--#define smul_ppmm(w1, w0, u, v) \
-- ({union {DItype __ll; \
-- struct {SItype __l, __h;} __i; \
-- } __x; \
-- __asm__ ("mulwx %2,%0" \
-- : "=r" (__x.__ll) \
-- : "%0" ((SItype)(u)), "r" ((SItype)(v))); \
-- (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
--#define __umulsidi3(u, v) \
-- ({UDItype __w; \
-- __asm__ ("mulwux %2,%0" \
-- : "=r" (__w) : "%0" ((USItype)(u)), "r" ((USItype)(v))); \
-- __w; })
--#endif /* __clipper__ */
--
--/* Fujitsu vector computers. */
--#if defined (__uxp__) && W_TYPE_SIZE == 32
--#define umul_ppmm(ph, pl, u, v) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mult.lu %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \
-- (ph) = __x.__i.__h; \
-- (pl) = __x.__i.__l; \
-- } while (0)
--#define smul_ppmm(ph, pl, u, v) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mult.l %1,%2,%0" : "=r" (__x.__ll) : "%r" (u), "rK" (v)); \
-- (ph) = __x.__i.__h; \
-- (pl) = __x.__i.__l; \
-- } while (0)
--#endif
--
--#if defined (__gmicro__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add.w %5,%1\n\taddx %3,%0" \
-- : "=g" (sh), "=&g" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub.w %5,%1\n\tsubx %3,%0" \
-- : "=g" (sh), "=&g" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define umul_ppmm(ph, pl, m0, m1) \
-- __asm__ ("mulx %3,%0,%1" \
-- : "=g" (ph), "=r" (pl) \
-- : "%0" ((USItype)(m0)), "g" ((USItype)(m1)))
--#define udiv_qrnnd(q, r, nh, nl, d) \
-- __asm__ ("divx %4,%0,%1" \
-- : "=g" (q), "=r" (r) \
-- : "1" ((USItype)(nh)), "0" ((USItype)(nl)), "g" ((USItype)(d)))
--#define count_leading_zeros(count, x) \
-- __asm__ ("bsch/1 %1,%0" \
-- : "=g" (count) : "g" ((USItype)(x)), "0" ((USItype)0))
--#endif
--
--#if defined (__hppa) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add%I5 %5,%r4,%1\n\taddc %r2,%r3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub%I4 %4,%r5,%1\n\tsubb %r2,%r3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl))
--#if defined (_PA_RISC1_1)
--#define umul_ppmm(wh, wl, u, v) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("xmpyu %1,%2,%0" : "=*f" (__x.__ll) : "*f" (u), "*f" (v)); \
-- (wh) = __x.__i.__h; \
-- (wl) = __x.__i.__l; \
-- } while (0)
--#define UMUL_TIME 8
--#define UDIV_TIME 60
--#else
--#define UMUL_TIME 40
--#define UDIV_TIME 80
--#endif
--#define count_leading_zeros(count, x) \
-- do { \
-- USItype __tmp; \
-- __asm__ ( \
-- "ldi 1,%0\n" \
-- " extru,= %1,15,16,%%r0 ; Bits 31..16 zero?\n" \
-- " extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
-- " ldo 16(%0),%0 ; Yes. Perform add.\n" \
-- " extru,= %1,23,8,%%r0 ; Bits 15..8 zero?\n" \
-- " extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
-- " ldo 8(%0),%0 ; Yes. Perform add.\n" \
-- " extru,= %1,27,4,%%r0 ; Bits 7..4 zero?\n" \
-- " extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
-- " ldo 4(%0),%0 ; Yes. Perform add.\n" \
-- " extru,= %1,29,2,%%r0 ; Bits 3..2 zero?\n" \
-- " extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
-- " ldo 2(%0),%0 ; Yes. Perform add.\n" \
-- " extru %1,30,1,%1 ; Extract bit 1.\n" \
-- " sub %0,%1,%0 ; Subtract it.\n" \
-- : "=r" (count), "=r" (__tmp) : "1" (x)); \
-- } while (0)
--#endif /* hppa */
--
-- /* These macros are for ABI=2.0w. In ABI=2.0n they can't be used, since GCC
-- (3.2) puts longlong into two adjacent 32-bit registers. Presumably this
-- is just a case of no direct support for 2.0n but treating it like 1.0. */
--#if defined (__hppa) && W_TYPE_SIZE == 64 && ! defined (_LONG_LONG_LIMB)
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add%I5 %5,%r4,%1\n\tadd,dc %r2,%r3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rM" (ah), "rM" (bh), "%rM" (al), "rI" (bl))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub%I4 %4,%r5,%1\n\tsub,db %r2,%r3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rM" (ah), "rM" (bh), "rI" (al), "rM" (bl))
--#endif /* hppa */
--
--#if (defined (__i370__) || defined (__s390__) || defined (__mvs__)) && W_TYPE_SIZE == 32
--#if defined (__zarch__) || defined (HAVE_HOST_CPU_s390_zarch)
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- do { \
-- /* if (__builtin_constant_p (bl)) \
-- __asm__ ("alfi\t%1,%o5\n\talcr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" (ah), "r" (bh), "%1" (al), "n" (bl) __CLOBBER_CC); \
-- else \
-- */ __asm__ ("alr\t%1,%5\n\talcr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" (ah), "r" (bh), "%1" (al), "r" (bl)__CLOBBER_CC); \
-- } while (0)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- do { \
-- /* if (__builtin_constant_p (bl)) \
-- __asm__ ("slfi\t%1,%o5\n\tslbr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" (ah), "r" (bh), "1" (al), "n" (bl) __CLOBBER_CC); \
-- else \
-- */ __asm__ ("slr\t%1,%5\n\tslbr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" (ah), "r" (bh), "1" (al), "r" (bl) __CLOBBER_CC); \
-- } while (0)
--#if __GMP_GNUC_PREREQ (4,5)
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __x.__ll = (UDItype) (m0) * (UDItype) (m1); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- } while (0)
--#else
--#if 0
-- /* FIXME: this fails if gcc knows about the 64-bit registers. Use only
-- with a new enough processor pretending we have 32-bit registers. */
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mlr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "%0" (m0), "r" (m1)); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- } while (0)
--#else
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- /* When we have 64-bit regs and gcc is aware of that, we cannot simply use
-- DImode for the product, since that would be allocated to a single 64-bit
-- register, whereas mlr uses the low 32-bits of an even-odd register pair.
-- */ \
--register USItype __r0 __asm__ ("0"); \
--register USItype __r1 __asm__ ("1") = (m0); \
--__asm__ ("mlr\t%0,%3" \
--: "=r" (__r0), "=r" (__r1) \
-- : "r" (__r1), "r" (m1)); \
--(xh) = __r0; (xl) = __r1; \
--} while (0)
--#endif /* if 0 */
--#endif
--#if 0
--/* FIXME: this fails if gcc knows about the 64-bit registers. Use only
-- with a new enough processor pretending we have 32-bit registers. */
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __x.__i.__h = n1; __x.__i.__l = n0; \
-- __asm__ ("dlr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "0" (__x.__ll), "r" (d)); \
-- (q) = __x.__i.__l; (r) = __x.__i.__h; \
-- } while (0)
--#else
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- register USItype __r0 __asm__ ("0") = (n1); \
-- register USItype __r1 __asm__ ("1") = (n0); \
-- __asm__ ("dlr\t%0,%4" \
-- : "=r" (__r0), "=r" (__r1) \
-- : "r" (__r0), "r" (__r1), "r" (d)); \
-- (q) = __r1; (r) = __r0; \
-- } while (0)
--#endif /* if 0 */
--#else /* if __zarch__ */
--/* FIXME: this fails if gcc knows about the 64-bit registers. */
--#define smul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {DItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "%0" (m0), "r" (m1)); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- } while (0)
--/* FIXME: this fails if gcc knows about the 64-bit registers. */
--#define sdiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- union {DItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __x.__i.__h = n1; __x.__i.__l = n0; \
-- __asm__ ("dr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "0" (__x.__ll), "r" (d)); \
-- (q) = __x.__i.__l; (r) = __x.__i.__h; \
-- } while (0)
--#endif /* if __zarch__ */
--#endif
--
--#if defined (__s390x__) && W_TYPE_SIZE == 64
--/* We need to cast operands with register constraints, otherwise their types
-- will be assumed to be SImode by gcc. For these machines, such operations
-- will insert a value into the low 32 bits, and leave the high 32 bits with
-- garbage. */
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- do { \
-- __asm__ ("algr\t%1,%5\n\talcgr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
-- "%1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \
-- } while (0)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- do { \
-- __asm__ ("slgr\t%1,%5\n\tslbgr\t%0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((UDItype)(ah)), "r" ((UDItype)(bh)), \
-- "1" ((UDItype)(al)), "r" ((UDItype)(bl)) __CLOBBER_CC); \
-- } while (0)
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {unsigned int __attribute__ ((mode(TI))) __ll; \
-- struct {UDItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mlgr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "%0" ((UDItype)(m0)), "r" ((UDItype)(m1))); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- } while (0)
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- union {unsigned int __attribute__ ((mode(TI))) __ll; \
-- struct {UDItype __h, __l;} __i; \
-- } __x; \
-- __x.__i.__h = n1; __x.__i.__l = n0; \
-- __asm__ ("dlgr\t%0,%2" \
-- : "=r" (__x.__ll) \
-- : "0" (__x.__ll), "r" ((UDItype)(d))); \
-- (q) = __x.__i.__l; (r) = __x.__i.__h; \
-- } while (0)
--#if 0 /* FIXME: Enable for z10 (?) */
--#define count_leading_zeros(cnt, x) \
-- do { \
-- union {unsigned int __attribute__ ((mode(TI))) __ll; \
-- struct {UDItype __h, __l;} __i; \
-- } __clr_cnt; \
-- __asm__ ("flogr\t%0,%1" \
-- : "=r" (__clr_cnt.__ll) \
-- : "r" (x) __CLOBBER_CC); \
-- (cnt) = __clr_cnt.__i.__h; \
-- } while (0)
--#endif
--#endif
--
--#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addl %5,%k1\n\tadcl %3,%k0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subl %5,%k1\n\tsbbl %3,%k0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("mull %3" \
-- : "=a" (w0), "=d" (w1) \
-- : "%0" ((USItype)(u)), "rm" ((USItype)(v)))
--#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \
-- __asm__ ("divl %4" /* stringification in K&R C */ \
-- : "=a" (q), "=d" (r) \
-- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "rm" ((USItype)(dx)))
--
--#if HAVE_HOST_CPU_i586 || HAVE_HOST_CPU_pentium || HAVE_HOST_CPU_pentiummmx
--/* Pentium bsrl takes between 10 and 72 cycles depending where the most
-- significant 1 bit is, hence the use of the following alternatives. bsfl
-- is slow too, between 18 and 42 depending where the least significant 1
-- bit is, so let the generic count_trailing_zeros below make use of the
-- count_leading_zeros here too. */
--
--#if HAVE_HOST_CPU_pentiummmx && ! defined (LONGLONG_STANDALONE)
--/* The following should be a fixed 14 or 15 cycles, but possibly plus an L1
-- cache miss reading from __clz_tab. For P55 it's favoured over the float
-- below so as to avoid mixing MMX and x87, since the penalty for switching
-- between the two is about 100 cycles.
--
-- The asm block sets __shift to -3 if the high 24 bits are clear, -2 for
-- 16, -1 for 8, or 0 otherwise. This could be written equivalently as
-- follows, but as of gcc 2.95.2 it results in conditional jumps.
--
-- __shift = -(__n < 0x1000000);
-- __shift -= (__n < 0x10000);
-- __shift -= (__n < 0x100);
--
-- The middle two sbbl and cmpl's pair, and with luck something gcc
-- generates might pair with the first cmpl and the last sbbl. The "32+1"
-- constant could be folded into __clz_tab[], but it doesn't seem worth
-- making a different table just for that. */
--
--#define count_leading_zeros(c,n) \
-- do { \
-- USItype __n = (n); \
-- USItype __shift; \
-- __asm__ ("cmpl $0x1000000, %1\n" \
-- "sbbl %0, %0\n" \
-- "cmpl $0x10000, %1\n" \
-- "sbbl $0, %0\n" \
-- "cmpl $0x100, %1\n" \
-- "sbbl $0, %0\n" \
-- : "=&r" (__shift) : "r" (__n)); \
-- __shift = __shift*8 + 24 + 1; \
-- (c) = 32 + 1 - __shift - __clz_tab[__n >> __shift]; \
-- } while (0)
--#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
--#define COUNT_LEADING_ZEROS_0 31 /* n==0 indistinguishable from n==1 */
--
--#else /* ! pentiummmx || LONGLONG_STANDALONE */
--/* The following should be a fixed 14 cycles or so. Some scheduling
-- opportunities should be available between the float load/store too. This
-- sort of code is used in gcc 3 for __builtin_ffs (with "n&-n") and is
-- apparently suggested by the Intel optimizing manual (don't know exactly
-- where). gcc 2.95 or up will be best for this, so the "double" is
-- correctly aligned on the stack. */
--#define count_leading_zeros(c,n) \
-- do { \
-- union { \
-- double d; \
-- unsigned a[2]; \
-- } __u; \
-- __u.d = (UWtype) (n); \
-- (c) = 0x3FF + 31 - (__u.a[1] >> 20); \
-- } while (0)
--#define COUNT_LEADING_ZEROS_0 (0x3FF + 31)
--#endif /* pentiummx */
--
--#else /* ! pentium */
--
--#if __GMP_GNUC_PREREQ (3,4) /* using bsrl */
--#define count_leading_zeros(count,x) count_leading_zeros_gcc_clz(count,x)
--#endif /* gcc clz */
--
--/* On P6, gcc prior to 3.0 generates a partial register stall for
-- __cbtmp^31, due to using "xorb $31" instead of "xorl $31", the former
-- being 1 code byte smaller. "31-__cbtmp" is a workaround, probably at the
-- cost of one extra instruction. Do this for "i386" too, since that means
-- generic x86. */
--#if ! defined (count_leading_zeros) && __GNUC__ < 3 \
-- && (HAVE_HOST_CPU_i386 \
-- || HAVE_HOST_CPU_i686 \
-- || HAVE_HOST_CPU_pentiumpro \
-- || HAVE_HOST_CPU_pentium2 \
-- || HAVE_HOST_CPU_pentium3)
--#define count_leading_zeros(count, x) \
-- do { \
-- USItype __cbtmp; \
-- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
-- (count) = 31 - __cbtmp; \
-- } while (0)
--#endif /* gcc<3 asm bsrl */
--
--#ifndef count_leading_zeros
--#define count_leading_zeros(count, x) \
-- do { \
-- USItype __cbtmp; \
-- __asm__ ("bsrl %1,%0" : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
-- (count) = __cbtmp ^ 31; \
-- } while (0)
--#endif /* asm bsrl */
--
--#if __GMP_GNUC_PREREQ (3,4) /* using bsfl */
--#define count_trailing_zeros(count,x) count_trailing_zeros_gcc_ctz(count,x)
--#endif /* gcc ctz */
--
--#ifndef count_trailing_zeros
--#define count_trailing_zeros(count, x) \
-- do { \
-- __asm__ ("bsfl %1,%k0" : "=r" (count) : "rm" ((USItype)(x))); \
-- } while (0)
--#endif /* asm bsfl */
--
--#endif /* ! pentium */
--
--#ifndef UMUL_TIME
--#define UMUL_TIME 10
--#endif
--#ifndef UDIV_TIME
--#define UDIV_TIME 40
--#endif
--#endif /* 80x86 */
--
--#if defined (__amd64__) && W_TYPE_SIZE == 64
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addq %5,%q1\n\tadcq %3,%q0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \
-- "%1" ((UDItype)(al)), "rme" ((UDItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subq %5,%q1\n\tsbbq %3,%q0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((UDItype)(ah)), "rme" ((UDItype)(bh)), \
-- "1" ((UDItype)(al)), "rme" ((UDItype)(bl)))
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("mulq %3" \
-- : "=a" (w0), "=d" (w1) \
-- : "%0" ((UDItype)(u)), "rm" ((UDItype)(v)))
--#define udiv_qrnnd(q, r, n1, n0, dx) /* d renamed to dx avoiding "=d" */ \
-- __asm__ ("divq %4" /* stringification in K&R C */ \
-- : "=a" (q), "=d" (r) \
-- : "0" ((UDItype)(n0)), "1" ((UDItype)(n1)), "rm" ((UDItype)(dx)))
--/* bsrq destination must be a 64-bit register, hence UDItype for __cbtmp. */
--#define count_leading_zeros(count, x) \
-- do { \
-- UDItype __cbtmp; \
-- __asm__ ("bsrq %1,%0" : "=r" (__cbtmp) : "rm" ((UDItype)(x))); \
-- (count) = __cbtmp ^ 63; \
-- } while (0)
--/* bsfq destination must be a 64-bit register, "%q0" forces this in case
-- count is only an int. */
--#define count_trailing_zeros(count, x) \
-- do { \
-- __asm__ ("bsfq %1,%q0" : "=r" (count) : "rm" ((UDItype)(x))); \
-- } while (0)
--#endif /* x86_64 */
--
--#if defined (__i860__) && W_TYPE_SIZE == 32
--#define rshift_rhlc(r,h,l,c) \
-- __asm__ ("shr %3,r0,r0\;shrd %1,%2,%0" \
-- "=r" (r) : "r" (h), "r" (l), "rn" (c))
--#endif /* i860 */
--
--#if defined (__i960__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("cmpo 1,0\;addc %5,%4,%1\;addc %3,%2,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "dI" (ah), "dI" (bh), "%dI" (al), "dI" (bl))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("cmpo 0,0\;subc %5,%4,%1\;subc %3,%2,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "dI" (ah), "dI" (bh), "dI" (al), "dI" (bl))
--#define umul_ppmm(w1, w0, u, v) \
-- ({union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __x; \
-- __asm__ ("emul %2,%1,%0" \
-- : "=d" (__x.__ll) : "%dI" (u), "dI" (v)); \
-- (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
--#define __umulsidi3(u, v) \
-- ({UDItype __w; \
-- __asm__ ("emul %2,%1,%0" : "=d" (__w) : "%dI" (u), "dI" (v)); \
-- __w; })
--#define udiv_qrnnd(q, r, nh, nl, d) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __nn; \
-- __nn.__i.__h = (nh); __nn.__i.__l = (nl); \
-- __asm__ ("ediv %d,%n,%0" \
-- : "=d" (__rq.__ll) : "dI" (__nn.__ll), "dI" (d)); \
-- (r) = __rq.__i.__l; (q) = __rq.__i.__h; \
-- } while (0)
--#define count_leading_zeros(count, x) \
-- do { \
-- USItype __cbtmp; \
-- __asm__ ("scanbit %1,%0" : "=r" (__cbtmp) : "r" (x)); \
-- (count) = __cbtmp ^ 31; \
-- } while (0)
--#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
--#if defined (__i960mx) /* what is the proper symbol to test??? */
--#define rshift_rhlc(r,h,l,c) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __nn; \
-- __nn.__i.__h = (h); __nn.__i.__l = (l); \
-- __asm__ ("shre %2,%1,%0" : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \
-- }
--#endif /* i960mx */
--#endif /* i960 */
--
--#if (defined (__mc68000__) || defined (__mc68020__) || defined(mc68020) \
-- || defined (__m68k__) || defined (__mc5200__) || defined (__mc5206e__) \
-- || defined (__mc5307__)) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add%.l %5,%1\n\taddx%.l %3,%0" \
-- : "=d" (sh), "=&d" (sl) \
-- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub%.l %5,%1\n\tsubx%.l %3,%0" \
-- : "=d" (sh), "=&d" (sl) \
-- : "0" ((USItype)(ah)), "d" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "g" ((USItype)(bl)))
-- /* The '020, '030, '040 and CPU32 have 32x32->64 and 64/32->32q-32r. */
--#if defined (__mc68020__) || defined(mc68020) \
-- || defined (__mc68030__) || defined (mc68030) \
-- || defined (__mc68040__) || defined (mc68040) \
-- || defined (__mcpu32__) || defined (mcpu32) \
-- || defined (__NeXT__)
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("mulu%.l %3,%1:%0" \
-- : "=d" (w0), "=d" (w1) \
-- : "%0" ((USItype)(u)), "dmi" ((USItype)(v)))
--#define UMUL_TIME 45
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- __asm__ ("divu%.l %4,%1:%0" \
-- : "=d" (q), "=d" (r) \
-- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d)))
--#define UDIV_TIME 90
--#define sdiv_qrnnd(q, r, n1, n0, d) \
-- __asm__ ("divs%.l %4,%1:%0" \
-- : "=d" (q), "=d" (r) \
-- : "0" ((USItype)(n0)), "1" ((USItype)(n1)), "dmi" ((USItype)(d)))
--#else /* for other 68k family members use 16x16->32 multiplication */
--#define umul_ppmm(xh, xl, a, b) \
-- do { USItype __umul_tmp1, __umul_tmp2; \
-- __asm__ ("| Inlined umul_ppmm\n" \
-- " move%.l %5,%3\n" \
-- " move%.l %2,%0\n" \
-- " move%.w %3,%1\n" \
-- " swap %3\n" \
-- " swap %0\n" \
-- " mulu%.w %2,%1\n" \
-- " mulu%.w %3,%0\n" \
-- " mulu%.w %2,%3\n" \
-- " swap %2\n" \
-- " mulu%.w %5,%2\n" \
-- " add%.l %3,%2\n" \
-- " jcc 1f\n" \
-- " add%.l %#0x10000,%0\n" \
-- "1: move%.l %2,%3\n" \
-- " clr%.w %2\n" \
-- " swap %2\n" \
-- " swap %3\n" \
-- " clr%.w %3\n" \
-- " add%.l %3,%1\n" \
-- " addx%.l %2,%0\n" \
-- " | End inlined umul_ppmm" \
-- : "=&d" (xh), "=&d" (xl), \
-- "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \
-- : "%2" ((USItype)(a)), "d" ((USItype)(b))); \
-- } while (0)
--#define UMUL_TIME 100
--#define UDIV_TIME 400
--#endif /* not mc68020 */
-- /* The '020, '030, '040 and '060 have bitfield insns.
-- GCC 3.4 defines __mc68020__ when in CPU32 mode, check for __mcpu32__ to
-- exclude bfffo on that chip (bitfield insns not available). */
--#if (defined (__mc68020__) || defined (mc68020) \
-- || defined (__mc68030__) || defined (mc68030) \
-- || defined (__mc68040__) || defined (mc68040) \
-- || defined (__mc68060__) || defined (mc68060) \
-- || defined (__NeXT__)) \
-- && ! defined (__mcpu32__)
--#define count_leading_zeros(count, x) \
-- __asm__ ("bfffo %1{%b2:%b2},%0" \
-- : "=d" (count) \
-- : "od" ((USItype) (x)), "n" (0))
--#define COUNT_LEADING_ZEROS_0 32
--#endif
--#endif /* mc68000 */
--
--#if defined (__m88000__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addu.co %1,%r4,%r5\n\taddu.ci %0,%r2,%r3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rJ" (bh), "%rJ" (al), "rJ" (bl))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subu.co %1,%r4,%r5\n\tsubu.ci %0,%r2,%r3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rJ" (bh), "rJ" (al), "rJ" (bl))
--#define count_leading_zeros(count, x) \
-- do { \
-- USItype __cbtmp; \
-- __asm__ ("ff1 %0,%1" : "=r" (__cbtmp) : "r" (x)); \
-- (count) = __cbtmp ^ 31; \
-- } while (0)
--#define COUNT_LEADING_ZEROS_0 63 /* sic */
--#if defined (__m88110__)
--#define umul_ppmm(wh, wl, u, v) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \
-- (wh) = __x.__i.__h; \
-- (wl) = __x.__i.__l; \
-- } while (0)
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- ({union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x, __q; \
-- __x.__i.__h = (n1); __x.__i.__l = (n0); \
-- __asm__ ("divu.d %0,%1,%2" \
-- : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \
-- (r) = (n0) - __q.__l * (d); (q) = __q.__l; })
--#define UMUL_TIME 5
--#define UDIV_TIME 25
--#else
--#define UMUL_TIME 17
--#define UDIV_TIME 150
--#endif /* __m88110__ */
--#endif /* __m88000__ */
--
--#if defined (__mips) && W_TYPE_SIZE == 32
--#if __GMP_GNUC_PREREQ (4,4)
--#define umul_ppmm(w1, w0, u, v) \
-- do { \
-- UDItype __ll = (UDItype)(u) * (v); \
-- w1 = __ll >> 32; \
-- w0 = __ll; \
-- } while (0)
--#endif
--#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7)
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("multu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v))
--#endif
--#if !defined (umul_ppmm)
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("multu %2,%3\n\tmflo %0\n\tmfhi %1" \
-- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v))
--#endif
--#define UMUL_TIME 10
--#define UDIV_TIME 100
--#endif /* __mips */
--
--#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64
--#if __GMP_GNUC_PREREQ (4,4)
--#define umul_ppmm(w1, w0, u, v) \
-- do { \
-- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
-- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \
-- w1 = __ll >> 64; \
-- w0 = __ll; \
-- } while (0)
--#endif
--#if !defined (umul_ppmm) && __GMP_GNUC_PREREQ (2,7)
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("dmultu %2,%3" : "=l" (w0), "=h" (w1) : "d" (u), "d" (v))
--#endif
--#if !defined (umul_ppmm)
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("dmultu %2,%3\n\tmflo %0\n\tmfhi %1" \
-- : "=d" (w0), "=d" (w1) : "d" (u), "d" (v))
--#endif
--#define UMUL_TIME 20
--#define UDIV_TIME 140
--#endif /* __mips */
--
--#if defined (__mmix__) && W_TYPE_SIZE == 64
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("MULU %0,%2,%3" : "=r" (w0), "=z" (w1) : "r" (u), "r" (v))
--#endif
--
--#if defined (__ns32000__) && W_TYPE_SIZE == 32
--#define umul_ppmm(w1, w0, u, v) \
-- ({union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __x; \
-- __asm__ ("meid %2,%0" \
-- : "=g" (__x.__ll) \
-- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \
-- (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
--#define __umulsidi3(u, v) \
-- ({UDItype __w; \
-- __asm__ ("meid %2,%0" \
-- : "=g" (__w) \
-- : "%0" ((USItype)(u)), "g" ((USItype)(v))); \
-- __w; })
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- ({union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __x; \
-- __x.__i.__h = (n1); __x.__i.__l = (n0); \
-- __asm__ ("deid %2,%0" \
-- : "=g" (__x.__ll) \
-- : "0" (__x.__ll), "g" ((USItype)(d))); \
-- (r) = __x.__i.__l; (q) = __x.__i.__h; })
--#define count_trailing_zeros(count,x) \
-- do { \
-- __asm__ ("ffsd %2,%0" \
-- : "=r" (count) \
-- : "0" ((USItype) 0), "r" ((USItype) (x))); \
-- } while (0)
--#endif /* __ns32000__ */
--
-- /* In the past we had a block of various #defines tested
-- _ARCH_PPC - AIX
-- _ARCH_PWR - AIX
-- __powerpc__ - gcc
-- __POWERPC__ - BEOS
-- __ppc__ - Darwin
-- PPC - old gcc, GNU/Linux, SysV
-- The plain PPC test was not good for vxWorks, since PPC is defined on all
-- CPUs there (eg. m68k too), as a constant one is expected to compare
-- CPU_FAMILY against.
--
-- At any rate, this was pretty unattractive and a bit fragile. The use of
-- HAVE_HOST_CPU_FAMILY is designed to cut through it all and be sure of
-- getting the desired effect.
--
-- ENHANCE-ME: We should test _IBMR2 here when we add assembly support for
-- the system vendor compilers. (Is that vendor compilers with inline asm,
-- or what?) */
--
--#if (HAVE_HOST_CPU_FAMILY_power || HAVE_HOST_CPU_FAMILY_powerpc) \
-- && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- do { \
-- if (__builtin_constant_p (bh) && (bh) == 0) \
-- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
-- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
-- else \
-- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \
-- } while (0)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- do { \
-- if (__builtin_constant_p (ah) && (ah) == 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (ah) && (ah) == ~(USItype) 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == ~(USItype) 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \
-- else \
-- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \
-- } while (0)
--#define count_leading_zeros(count, x) \
-- __asm__ ("cntlzw %0,%1" : "=r" (count) : "r" (x))
--#define COUNT_LEADING_ZEROS_0 32
--#if HAVE_HOST_CPU_FAMILY_powerpc
--#if __GMP_GNUC_PREREQ (4,4)
--#define umul_ppmm(w1, w0, u, v) \
-- do { \
-- UDItype __ll = (UDItype)(u) * (v); \
-- w1 = __ll >> 32; \
-- w0 = __ll; \
-- } while (0)
--#endif
--#if !defined (umul_ppmm)
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- USItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("mulhwu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#endif
--#define UMUL_TIME 15
--#define smul_ppmm(ph, pl, m0, m1) \
-- do { \
-- SItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("mulhw %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#define SMUL_TIME 14
--#define UDIV_TIME 120
--#else
--#define UMUL_TIME 8
--#define smul_ppmm(xh, xl, m0, m1) \
-- __asm__ ("mul %0,%2,%3" : "=r" (xh), "=q" (xl) : "r" (m0), "r" (m1))
--#define SMUL_TIME 4
--#define sdiv_qrnnd(q, r, nh, nl, d) \
-- __asm__ ("div %0,%2,%4" : "=r" (q), "=q" (r) : "r" (nh), "1" (nl), "r" (d))
--#define UDIV_TIME 100
--#endif
--#endif /* 32-bit POWER architecture variants. */
--
-- /* We should test _IBMR2 here when we add assembly support for the system
-- vendor compilers. */
--#if HAVE_HOST_CPU_FAMILY_powerpc && W_TYPE_SIZE == 64
--#if !defined (_LONG_LONG_LIMB)
-- /* _LONG_LONG_LIMB is ABI=mode32 where adde operates on 32-bit values. So
-- use adde etc only when not _LONG_LONG_LIMB. */
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- do { \
-- if (__builtin_constant_p (bh) && (bh) == 0) \
-- __asm__ ("add%I4c %1,%3,%4\n\taddze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
-- __asm__ ("add%I4c %1,%3,%4\n\taddme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "%r" (al), "rI" (bl)); \
-- else \
-- __asm__ ("add%I5c %1,%4,%5\n\tadde %0,%2,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "r" (bh), "%r" (al), "rI" (bl)); \
-- } while (0)
-- /* We use "*rI" for the constant operand here, since with just "I", gcc barfs.
-- This might seem strange, but gcc folds away the dead code late. */
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- do { \
-- if (__builtin_constant_p (bl) && bl > -0x8000 && bl <= 0x8000) { \
-- if (__builtin_constant_p (ah) && (ah) == 0) \
-- __asm__ ("addic %1,%3,%4\n\tsubfze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \
-- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \
-- __asm__ ("addic %1,%3,%4\n\tsubfme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "*rI" (-bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == 0) \
-- __asm__ ("addic %1,%3,%4\n\taddme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
-- __asm__ ("addic %1,%3,%4\n\taddze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "*rI" (-bl)); \
-- else \
-- __asm__ ("addic %1,%4,%5\n\tsubfe %0,%3,%2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "r" (bh), "rI" (al), "*rI" (-bl)); \
-- } else { \
-- if (__builtin_constant_p (ah) && (ah) == 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\tsubfze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (ah) && (ah) == ~(UDItype) 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\tsubfme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (bh), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\taddme %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \
-- else if (__builtin_constant_p (bh) && (bh) == ~(UDItype) 0) \
-- __asm__ ("subf%I3c %1,%4,%3\n\taddze %0,%2" \
-- : "=r" (sh), "=&r" (sl) : "r" (ah), "rI" (al), "r" (bl)); \
-- else \
-- __asm__ ("subf%I4c %1,%5,%4\n\tsubfe %0,%3,%2" \
-- : "=r" (sh), "=&r" (sl) \
-- : "r" (ah), "r" (bh), "rI" (al), "r" (bl)); \
-- } \
-- } while (0)
--#endif /* ! _LONG_LONG_LIMB */
--#define count_leading_zeros(count, x) \
-- __asm__ ("cntlzd %0,%1" : "=r" (count) : "r" (x))
--#define COUNT_LEADING_ZEROS_0 64
--#if 0 && __GMP_GNUC_PREREQ (4,4) /* Disable, this results in libcalls! */
--#define umul_ppmm(w1, w0, u, v) \
-- do { \
-- typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \
-- __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \
-- w1 = __ll >> 64; \
-- w0 = __ll; \
-- } while (0)
--#endif
--#if !defined (umul_ppmm)
--#define umul_ppmm(ph, pl, m0, m1) \
-- do { \
-- UDItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("mulhdu %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#endif
--#define UMUL_TIME 15
--#define smul_ppmm(ph, pl, m0, m1) \
-- do { \
-- DItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("mulhd %0,%1,%2" : "=r" (ph) : "%r" (m0), "r" (m1)); \
-- (pl) = __m0 * __m1; \
-- } while (0)
--#define SMUL_TIME 14 /* ??? */
--#define UDIV_TIME 120 /* ??? */
--#endif /* 64-bit PowerPC. */
--
--#if defined (__pyr__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addw %5,%1\n\taddwc %3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subw %5,%1\n\tsubwb %3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "g" ((USItype)(bl)))
-- /* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */
--#define umul_ppmm(w1, w0, u, v) \
-- ({union {UDItype __ll; \
-- struct {USItype __h, __l;} __i; \
-- } __x; \
-- __asm__ ("movw %1,%R0\n\tuemul %2,%0" \
-- : "=&r" (__x.__ll) \
-- : "g" ((USItype) (u)), "g" ((USItype)(v))); \
-- (w1) = __x.__i.__h; (w0) = __x.__i.__l;})
--#endif /* __pyr__ */
--
--#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("a %1,%5\n\tae %0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "r" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("s %1,%5\n\tse %0,%3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((USItype)(ah)), "r" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "r" ((USItype)(bl)))
--#define smul_ppmm(ph, pl, m0, m1) \
-- __asm__ ( \
-- "s r2,r2\n" \
-- " mts r10,%2\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " m r2,%3\n" \
-- " cas %0,r2,r0\n" \
-- " mfs r10,%1" \
-- : "=r" (ph), "=r" (pl) \
-- : "%r" ((USItype)(m0)), "r" ((USItype)(m1)) \
-- : "r2")
--#define UMUL_TIME 20
--#define UDIV_TIME 200
--#define count_leading_zeros(count, x) \
-- do { \
-- if ((x) >= 0x10000) \
-- __asm__ ("clz %0,%1" \
-- : "=r" (count) : "r" ((USItype)(x) >> 16)); \
-- else \
-- { \
-- __asm__ ("clz %0,%1" \
-- : "=r" (count) : "r" ((USItype)(x))); \
-- (count) += 16; \
-- } \
-- } while (0)
--#endif /* RT/ROMP */
--
--#if (defined (__SH2__) || defined (__SH3__) || defined (__SH4__)) && W_TYPE_SIZE == 32
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("dmulu.l %2,%3\n\tsts macl,%1\n\tsts mach,%0" \
-- : "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "macl", "mach")
--#define UMUL_TIME 5
--#endif
--
--#if defined (__sparc__) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addcc %r4,%5,%1\n\taddx %r2,%3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rI" (bh),"%rJ" (al), "rI" (bl) \
-- __CLOBBER_CC)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subcc %r4,%5,%1\n\tsubx %r2,%3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl) \
-- __CLOBBER_CC)
-- /* FIXME: When gcc -mcpu=v9 is used on solaris, gcc/config/sol2-sld-64.h
-- doesn't define anything to indicate that to us, it only sets __sparcv8. */
--#if defined (__sparc_v9__) || defined (__sparcv9)
-- /* Perhaps we should use floating-point operations here? */
--#if 0
-- /* Triggers a bug making mpz/tests/t-gcd.c fail.
-- Perhaps we simply need explicitly zero-extend the inputs? */
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("mulx %2,%3,%%g1; srl %%g1,0,%1; srlx %%g1,32,%0" : \
-- "=r" (w1), "=r" (w0) : "r" (u), "r" (v) : "g1")
--#else
-- /* Use v8 umul until above bug is fixed. */
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
--#endif
-- /* Use a plain v8 divide for v9. */
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- USItype __q; \
-- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
-- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \
-- (r) = (n0) - __q * (d); \
-- (q) = __q; \
-- } while (0)
--#else
--#if defined (__sparc_v8__) /* gcc normal */ \
-- || defined (__sparcv8) /* gcc solaris */ \
-- || HAVE_HOST_CPU_supersparc
-- /* Don't match immediate range because, 1) it is not often useful,
-- 2) the 'I' flag thinks of the range as a 13 bit signed interval,
-- while we want to match a 13 bit interval, sign extended to 32 bits,
-- but INTERPRETED AS UNSIGNED. */
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
--#define UMUL_TIME 5
--
--#if HAVE_HOST_CPU_supersparc
--#define UDIV_TIME 60 /* SuperSPARC timing */
--#else
-- /* Don't use this on SuperSPARC because its udiv only handles 53 bit
-- dividends and will trap to the kernel for the rest. */
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- USItype __q; \
-- __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
-- : "=r" (__q) : "r" (n1), "r" (n0), "r" (d)); \
-- (r) = (n0) - __q * (d); \
-- (q) = __q; \
-- } while (0)
--#define UDIV_TIME 25
--#endif /* HAVE_HOST_CPU_supersparc */
--
--#else /* ! __sparc_v8__ */
--#if defined (__sparclite__)
-- /* This has hardware multiply but not divide. It also has two additional
-- instructions scan (ffs from high bit) and divscc. */
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("umul %2,%3,%1;rd %%y,%0" : "=r" (w1), "=r" (w0) : "r" (u), "r" (v))
--#define UMUL_TIME 5
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- __asm__ ("! Inlined udiv_qrnnd\n" \
-- " wr %%g0,%2,%%y ! Not a delayed write for sparclite\n" \
-- " tst %%g0\n" \
-- " divscc %3,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%%g1\n" \
-- " divscc %%g1,%4,%0\n" \
-- " rd %%y,%1\n" \
-- " bl,a 1f\n" \
-- " add %1,%4,%1\n" \
-- "1: ! End of inline udiv_qrnnd" \
-- : "=r" (q), "=r" (r) : "r" (n1), "r" (n0), "rI" (d) \
-- : "%g1" __AND_CLOBBER_CC)
--#define UDIV_TIME 37
--#define count_leading_zeros(count, x) \
-- __asm__ ("scan %1,1,%0" : "=r" (count) : "r" (x))
-- /* Early sparclites return 63 for an argument of 0, but they warn that future
-- implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
-- undefined. */
--#endif /* __sparclite__ */
--#endif /* __sparc_v8__ */
--#endif /* __sparc_v9__ */
-- /* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
--#ifndef umul_ppmm
--#define umul_ppmm(w1, w0, u, v) \
-- __asm__ ("! Inlined umul_ppmm\n" \
-- " wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr\n" \
-- " sra %3,31,%%g2 ! Don't move this insn\n" \
-- " and %2,%%g2,%%g2 ! Don't move this insn\n" \
-- " andcc %%g0,0,%%g1 ! Don't move this insn\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,%3,%%g1\n" \
-- " mulscc %%g1,0,%%g1\n" \
-- " add %%g1,%%g2,%0\n" \
-- " rd %%y,%1" \
-- : "=r" (w1), "=r" (w0) : "%rI" (u), "r" (v) \
-- : "%g1", "%g2" __AND_CLOBBER_CC)
--#define UMUL_TIME 39 /* 39 instructions */
--#endif
--#ifndef udiv_qrnnd
--#ifndef LONGLONG_STANDALONE
--#define udiv_qrnnd(q, r, n1, n0, d) \
-- do { UWtype __r; \
-- (q) = __MPN(udiv_qrnnd) (&__r, (n1), (n0), (d)); \
-- (r) = __r; \
-- } while (0)
-- extern UWtype __MPN(udiv_qrnnd) (UWtype *, UWtype, UWtype, UWtype);
--#ifndef UDIV_TIME
--#define UDIV_TIME 140
--#endif
--#endif /* LONGLONG_STANDALONE */
--#endif /* udiv_qrnnd */
--#endif /* __sparc__ */
--
--#if defined (__sparc__) && W_TYPE_SIZE == 64
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ( \
-- "addcc %r4,%5,%1\n" \
-- " addccc %r6,%7,%%g0\n" \
-- " addc %r2,%3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rI" (bh), "%rJ" (al), "rI" (bl), \
-- "%rJ" ((al) >> 32), "rI" ((bl) >> 32) \
-- __CLOBBER_CC)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ( \
-- "subcc %r4,%5,%1\n" \
-- " subccc %r6,%7,%%g0\n" \
-- " subc %r2,%3,%0" \
-- : "=r" (sh), "=&r" (sl) \
-- : "rJ" (ah), "rI" (bh), "rJ" (al), "rI" (bl), \
-- "rJ" ((al) >> 32), "rI" ((bl) >> 32) \
-- __CLOBBER_CC)
--#endif
--
--#if (defined (__vax) || defined (__vax__)) && W_TYPE_SIZE == 32
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("addl2 %5,%1\n\tadwc %3,%0" \
-- : "=g" (sh), "=&g" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "%1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("subl2 %5,%1\n\tsbwc %3,%0" \
-- : "=g" (sh), "=&g" (sl) \
-- : "0" ((USItype)(ah)), "g" ((USItype)(bh)), \
-- "1" ((USItype)(al)), "g" ((USItype)(bl)))
--#define smul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {UDItype __ll; \
-- struct {USItype __l, __h;} __i; \
-- } __x; \
-- USItype __m0 = (m0), __m1 = (m1); \
-- __asm__ ("emul %1,%2,$0,%0" \
-- : "=g" (__x.__ll) : "g" (__m0), "g" (__m1)); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- } while (0)
--#define sdiv_qrnnd(q, r, n1, n0, d) \
-- do { \
-- union {DItype __ll; \
-- struct {SItype __l, __h;} __i; \
-- } __x; \
-- __x.__i.__h = n1; __x.__i.__l = n0; \
-- __asm__ ("ediv %3,%2,%0,%1" \
-- : "=g" (q), "=g" (r) : "g" (__x.__ll), "g" (d)); \
-- } while (0)
--#if 0
-- /* FIXME: This instruction appears to be unimplemented on some systems (vax
-- 8800 maybe). */
--#define count_trailing_zeros(count,x) \
-- do { \
-- __asm__ ("ffs 0, 31, %1, %0" \
-- : "=g" (count) \
-- : "g" ((USItype) (x))); \
-- } while (0)
--#endif
--#endif /* vax */
--
--#if defined (__z8000__) && W_TYPE_SIZE == 16
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-- __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \
-- "%1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl)))
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-- __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \
-- : "=r" (sh), "=&r" (sl) \
-- : "0" ((unsigned int)(ah)), "r" ((unsigned int)(bh)), \
-- "1" ((unsigned int)(al)), "rQR" ((unsigned int)(bl)))
--#define umul_ppmm(xh, xl, m0, m1) \
-- do { \
-- union {long int __ll; \
-- struct {unsigned int __h, __l;} __i; \
-- } __x; \
-- unsigned int __m0 = (m0), __m1 = (m1); \
-- __asm__ ("mult %S0,%H3" \
-- : "=r" (__x.__i.__h), "=r" (__x.__i.__l) \
-- : "%1" (m0), "rQR" (m1)); \
-- (xh) = __x.__i.__h; (xl) = __x.__i.__l; \
-- (xh) += ((((signed int) __m0 >> 15) & __m1) \
-- + (((signed int) __m1 >> 15) & __m0)); \
-- } while (0)
--#endif /* __z8000__ */
--
--#endif /* __GNUC__ */
--
--#endif /* NO_ASM */
--
-
- /* FIXME: "sidi" here is highly doubtful, should sometimes be "diti". */
--#if !defined (umul_ppmm) && defined (__umulsidi3)
--#define umul_ppmm(ph, pl, m0, m1) \
-+#if !defined (recint_umul_ppmm) && defined (__umulsidi3)
-+#define recint_umul_ppmm(ph, pl, m0, m1) \
- { \
- UDWtype __ll = __umulsidi3 (m0, m1); \
- ph = (UWtype) (__ll >> W_TYPE_SIZE); \
-@@ -1838,75 +179,75 @@ __asm__ ("mlr\t%0,%3" \
- #if !defined (__umulsidi3)
- #define __umulsidi3(u, v) \
- ({UWtype __hi, __lo; \
-- umul_ppmm (__hi, __lo, u, v); \
-+ recint_umul_ppmm (__hi, __lo, u, v); \
- ((UDWtype) __hi << W_TYPE_SIZE) | __lo; })
- #endif
-
-
-- /* Use mpn_umul_ppmm or mpn_udiv_qrnnd functions, if they exist. The "_r"
-+ /* Use mpn_recint_umul_ppmm or mpn_recint_udiv_qrnnd functions, if they exist. The "_r"
- forms have "reversed" arguments, meaning the pointer is last, which
- sometimes allows better parameter passing, in particular on 64-bit
- hppa. */
-
--#define mpn_umul_ppmm __MPN(umul_ppmm)
--extern UWtype mpn_umul_ppmm (UWtype *, UWtype, UWtype);
-+#define mpn_recint_umul_ppmm __MPN(recint_umul_ppmm)
-+extern UWtype mpn_recint_umul_ppmm (UWtype *, UWtype, UWtype);
-
--#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm \
-+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm \
- && ! defined (LONGLONG_STANDALONE)
--#define umul_ppmm(wh, wl, u, v) \
-+#define recint_umul_ppmm(wh, wl, u, v) \
- do { \
-- UWtype __umul_ppmm__p0; \
-- (wh) = mpn_umul_ppmm (&__umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \
-- (wl) = __umul_ppmm__p0; \
-+ UWtype __recint_umul_ppmm__p0; \
-+ (wh) = mpn_recint_umul_ppmm (&__recint_umul_ppmm__p0, (UWtype) (u), (UWtype) (v)); \
-+ (wl) = __recint_umul_ppmm__p0; \
- } while (0)
- #endif
-
--#define mpn_umul_ppmm_r __MPN(umul_ppmm_r)
--extern UWtype mpn_umul_ppmm_r (UWtype, UWtype, UWtype *);
-+#define mpn_recint_umul_ppmm_r __MPN(recint_umul_ppmm_r)
-+extern UWtype mpn_recint_umul_ppmm_r (UWtype, UWtype, UWtype *);
-
--#if ! defined (umul_ppmm) && HAVE_NATIVE_mpn_umul_ppmm_r \
-+#if ! defined (recint_umul_ppmm) && HAVE_NATIVE_mpn_recint_umul_ppmm_r \
- && ! defined (LONGLONG_STANDALONE)
--#define umul_ppmm(wh, wl, u, v) \
-+#define recint_umul_ppmm(wh, wl, u, v) \
- do { \
-- UWtype __umul_ppmm__p0; \
-- (wh) = mpn_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__umul_ppmm__p0); \
-- (wl) = __umul_ppmm__p0; \
-+ UWtype __recint_umul_ppmm__p0; \
-+ (wh) = mpn_recint_umul_ppmm_r ((UWtype) (u), (UWtype) (v), &__recint_umul_ppmm__p0); \
-+ (wl) = __recint_umul_ppmm__p0; \
- } while (0)
- #endif
-
--#define mpn_udiv_qrnnd __MPN(udiv_qrnnd)
--extern UWtype mpn_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype);
-+#define mpn_recint_udiv_qrnnd __MPN(recint_udiv_qrnnd)
-+extern UWtype mpn_recint_udiv_qrnnd (UWtype *, UWtype, UWtype, UWtype);
-
--#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd \
-+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd \
- && ! defined (LONGLONG_STANDALONE)
--#define udiv_qrnnd(q, r, n1, n0, d) \
-+#define recint_udiv_qrnnd(q, r, n1, n0, d) \
- do { \
-- UWtype __udiv_qrnnd__r; \
-- (q) = mpn_udiv_qrnnd (&__udiv_qrnnd__r, \
-+ UWtype __recint_udiv_qrnnd__r; \
-+ (q) = mpn_recint_udiv_qrnnd (&__recint_udiv_qrnnd__r, \
- (UWtype) (n1), (UWtype) (n0), (UWtype) d); \
-- (r) = __udiv_qrnnd__r; \
-+ (r) = __recint_udiv_qrnnd__r; \
- } while (0)
- #endif
-
--#define mpn_udiv_qrnnd_r __MPN(udiv_qrnnd_r)
--extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
-+#define mpn_recint_udiv_qrnnd_r __MPN(recint_udiv_qrnnd_r)
-+extern UWtype mpn_recint_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
-
--#if ! defined (udiv_qrnnd) && HAVE_NATIVE_mpn_udiv_qrnnd_r \
-+#if ! defined (recint_udiv_qrnnd) && HAVE_NATIVE_mpn_recint_udiv_qrnnd_r \
- && ! defined (LONGLONG_STANDALONE)
--#define udiv_qrnnd(q, r, n1, n0, d) \
-+#define recint_udiv_qrnnd(q, r, n1, n0, d) \
- do { \
-- UWtype __udiv_qrnnd__r; \
-- (q) = mpn_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \
-- &__udiv_qrnnd__r); \
-- (r) = __udiv_qrnnd__r; \
-+ UWtype __recint_udiv_qrnnd__r; \
-+ (q) = mpn_recint_udiv_qrnnd_r ((UWtype) (n1), (UWtype) (n0), (UWtype) d, \
-+ &__recint_udiv_qrnnd__r); \
-+ (r) = __recint_udiv_qrnnd__r; \
- } while (0)
- #endif
-
-
- /* If this machine has no inline assembler, use C macros. */
-
--#if !defined (add_ssaaaa)
--#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
-+#if !defined (recint_add_ssaaaa)
-+#define recint_add_ssaaaa(sh, sl, ah, al, bh, bl) \
- do { \
- UWtype __x; \
- __x = (al) + (bl); \
-@@ -1915,8 +256,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
- } while (0)
- #endif
-
--#if !defined (sub_ddmmss)
--#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
-+#if !defined (recint_sub_ddmmss)
-+#define recint_sub_ddmmss(sh, sl, ah, al, bh, bl) \
- do { \
- UWtype __x; \
- __x = (al) - (bl); \
-@@ -1925,20 +266,20 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
- } while (0)
- #endif
-
-- /* If we lack umul_ppmm but have smul_ppmm, define umul_ppmm in terms of
-- smul_ppmm. */
--#if !defined (umul_ppmm) && defined (smul_ppmm)
--#define umul_ppmm(w1, w0, u, v) \
-+ /* If we lack recint_umul_ppmm but have recint_smul_ppmm, define recint_umul_ppmm in terms of
-+ recint_smul_ppmm. */
-+#if !defined (recint_umul_ppmm) && defined (recint_smul_ppmm)
-+#define recint_umul_ppmm(w1, w0, u, v) \
- do { \
- UWtype __w1; \
- UWtype __xm0 = (u), __xm1 = (v); \
-- smul_ppmm (__w1, w0, __xm0, __xm1); \
-+ recint_smul_ppmm (__w1, w0, __xm0, __xm1); \
- (w1) = __w1 + (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \
- + (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \
- } while (0)
- #endif
-
-- /* If we still don't have umul_ppmm, define it using plain C.
-+ /* If we still don't have recint_umul_ppmm, define it using plain C.
-
- For reference, when this code is used for squaring (ie. u and v identical
- expressions), gcc recognises __x1 and __x2 are the same and generates 3
-@@ -1948,8 +289,8 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
- performance problems than a couple of extra instructions on the diagonal
- of sqr_basecase. */
-
--#if !defined (umul_ppmm)
--#define umul_ppmm(w1, w0, u, v) \
-+#if !defined (recint_umul_ppmm)
-+#define recint_umul_ppmm(w1, w0, u, v) \
- do { \
- UWtype __x0, __x1, __x2, __x3; \
- UHWtype __ul, __vl, __uh, __vh; \
-@@ -1975,21 +316,21 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
- } while (0)
- #endif
-
-- /* If we don't have smul_ppmm, define it using umul_ppmm (which surely will
-+ /* If we don't have recint_smul_ppmm, define it using recint_umul_ppmm (which surely will
- exist in one form or another. */
--#if !defined (smul_ppmm)
--#define smul_ppmm(w1, w0, u, v) \
-+#if !defined (recint_smul_ppmm)
-+#define recint_smul_ppmm(w1, w0, u, v) \
- do { \
- UWtype __w1; \
- UWtype __xm0 = (u), __xm1 = (v); \
-- umul_ppmm (__w1, w0, __xm0, __xm1); \
-+ recint_umul_ppmm (__w1, w0, __xm0, __xm1); \
- (w1) = __w1 - (-(__xm0 >> (W_TYPE_SIZE - 1)) & __xm1) \
- - (-(__xm1 >> (W_TYPE_SIZE - 1)) & __xm0); \
- } while (0)
- #endif
-
- /* Define this unconditionally, so it can be used for debugging. */
--#define __udiv_qrnnd_c(q, r, n1, n0, d) \
-+#define __recint_udiv_qrnnd_c(q, r, n1, n0, d) \
- do { \
- UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
- \
-@@ -2026,26 +367,27 @@ extern UWtype mpn_udiv_qrnnd_r (UWtype, UWtype, UWtype, UWtype *);
- (r) = __r0; \
- } while (0)
-
-- /* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
-+ /* If the processor has no recint_udiv_qrnnd but sdiv_qrnnd, go through
- __udiv_w_sdiv (defined in libgcc or elsewhere). */
--#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
--#define udiv_qrnnd(q, r, nh, nl, d) \
-- do { \
-- UWtype __r; \
-- (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \
-- (r) = __r; \
-- } while (0)
--UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype);
--#endif
--
-- /* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
--#if !defined (udiv_qrnnd)
-+/*
-+#if !defined (recint_udiv_qrnnd) && defined (sdiv_qrnnd)
-+ #define recint_udiv_qrnnd(q, r, nh, nl, d) \
-+ do { \
-+ UWtype __r; \
-+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \
-+ (r) = __r; \
-+ } while (0)
-+ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype);
-+ #endif
-+*/
-+ /* If recint_udiv_qrnnd was not defined for this processor, use __recint_udiv_qrnnd_c. */
-+#if !defined (recint_udiv_qrnnd)
- #define UDIV_NEEDS_NORMALIZATION 1
--#define udiv_qrnnd __udiv_qrnnd_c
-+#define recint_udiv_qrnnd __recint_udiv_qrnnd_c
- #endif
-
--#if !defined (count_leading_zeros)
--#define count_leading_zeros(count, x) \
-+#if !defined (recint_count_leading_zeros)
-+#define recint_count_leading_zeros(count, x) \
- do { \
- UWtype __xr = (x); \
- UWtype __a; \
-@@ -2068,35 +410,35 @@ UWtype __MPN(udiv_w_sdiv) (UWtype *, UWtype, UWtype, UWtype);
- (count) = W_TYPE_SIZE + 1 - __a - __clz_tab[__xr >> __a]; \
- } while (0)
- /* This version gives a well-defined value for zero. */
--#define COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1)
--#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
--#define COUNT_LEADING_ZEROS_SLOW
-+#define RECINT_COUNT_LEADING_ZEROS_0 (W_TYPE_SIZE - 1)
-+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB
-+#define RECINT_COUNT_LEADING_ZEROS_SLOW
- #endif
-
- /* clz_tab needed by mpn/x86/pentium/mod_1.asm in a fat binary */
- #if HAVE_HOST_CPU_FAMILY_x86 && WANT_FAT_BINARY
--#define COUNT_LEADING_ZEROS_NEED_CLZ_TAB
-+#define RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB
- #endif
-
--#ifdef COUNT_LEADING_ZEROS_NEED_CLZ_TAB
-+#ifdef RECINT_COUNT_LEADING_ZEROS_NEED_CLZ_TAB
- extern const unsigned char __clz_tab[129];
- #endif
-
--#if !defined (count_trailing_zeros)
--#if !defined (COUNT_LEADING_ZEROS_SLOW)
-- /* Define count_trailing_zeros using an asm count_leading_zeros. */
--#define count_trailing_zeros(count, x) \
-+#if !defined (recint_count_trailing_zeros)
-+#if !defined (RECINT_COUNT_LEADING_ZEROS_SLOW)
-+ /* Define recint_count_trailing_zeros using an asm recint_count_leading_zeros. */
-+#define recint_count_trailing_zeros(count, x) \
- do { \
- UWtype __ctz_x = (x); \
- UWtype __ctz_c; \
-- count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \
-+ recint_count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \
- (count) = W_TYPE_SIZE - 1 - __ctz_c; \
- } while (0)
- #else
-- /* Define count_trailing_zeros in plain C, assuming small counts are common.
-- We use clz_tab without ado, since the C count_leading_zeros above will have
-+ /* Define recint_count_trailing_zeros in plain C, assuming small counts are common.
-+ We use clz_tab without ado, since the C recint_count_leading_zeros above will have
- pulled it in. */
--#define count_trailing_zeros(count, x) \
-+#define recint_count_trailing_zeros(count, x) \
- do { \
- UWtype __ctz_x = (x); \
- int __ctz_c; \
-@@ -2122,7 +464,7 @@ extern const unsigned char __clz_tab[129];
- #define UDIV_NEEDS_NORMALIZATION 0
- #endif
-
-- /* Whether udiv_qrnnd is actually implemented with udiv_qrnnd_preinv, and
-+ /* Whether recint_udiv_qrnnd is actually implemented with recint_udiv_qrnnd_preinv, and
- that hence the latter should always be used. */
- #ifndef UDIV_PREINV_ALWAYS
- #define UDIV_PREINV_ALWAYS 0
-@@ -2138,4 +480,3 @@ extern const unsigned char __clz_tab[129];
- #endif
- /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
--#endif
-diff --git a/src/kernel/recint/ruadd.h b/src/kernel/recint/ruadd.h
-index 01d4073..deed001 100644
---- a/src/kernel/recint/ruadd.h
-+++ b/src/kernel/recint/ruadd.h
-@@ -190,7 +190,7 @@ namespace RecInt
- template<>
- inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) {
- auto bp(b);
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
- r = (a < bp);
- }
- #endif
-@@ -219,7 +219,7 @@ namespace RecInt
- template<>
- inline void add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
- auto bp(b);
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- r = (a < bp);
- }
- #endif
-@@ -245,7 +245,7 @@ namespace RecInt
- #else
- template<>
- inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) {
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
- }
- #endif
- template<>
-@@ -268,7 +268,7 @@ namespace RecInt
- #else
- template<>
- inline void add(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- }
- #endif
- template<>
-@@ -288,7 +288,7 @@ namespace RecInt
- // TODO Use __RECINT_USE_FAST_128 here too
- template
- inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) {
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c));
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, (UWtype)(c));
- r = (a < c);
- }
- template
-@@ -306,7 +306,7 @@ namespace RecInt
- }
- template
- inline __RECINT_IS_ARITH(T, void) add(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b));
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, (UWtype)(b));
- r = (a < b);
- }
- template
-@@ -345,7 +345,7 @@ namespace RecInt
- #else
- template<>
- inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
- r = (a == 0);
- }
- #endif
-@@ -371,7 +371,7 @@ namespace RecInt
- #else
- template<>
- inline void add_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- r = (a == 0);
- }
- #endif
-@@ -396,7 +396,7 @@ namespace RecInt
- #else
- template<>
- inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -419,7 +419,7 @@ namespace RecInt
- #else
- template<>
- inline void add_1(ruint<__RECINT_LIMB_SIZE+1>& a) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -452,9 +452,9 @@ namespace RecInt
- template<>
- inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) {
- auto bp(b);
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
- if (cy) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- r = (a <= bp);
- } else {
- r = (a < bp);
-@@ -496,9 +496,9 @@ namespace RecInt
- template<>
- inline void add_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) {
- auto bp(b);
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- if (cy) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- r = (a <= bp);
- } else {
- r = (a < bp);
-@@ -533,8 +533,8 @@ namespace RecInt
- #else
- template<>
- inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) {
-- add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-- if (cy) add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ if (cy) recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -557,8 +557,8 @@ namespace RecInt
- #else
- template<>
- inline void add_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) {
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-- add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_add_ssaaaa(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
- }
- #endif
- template<>
-diff --git a/src/kernel/recint/ruaddmul.h b/src/kernel/recint/ruaddmul.h
-index e3c8154..365340e 100644
---- a/src/kernel/recint/ruaddmul.h
-+++ b/src/kernel/recint/ruaddmul.h
-@@ -115,8 +115,8 @@ namespace RecInt
- template <>
- inline void laddmul(bool& r, ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) {
- auto dp(d.Value);
-- umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp);
-+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp);
- r = ((ah.Value == 0) && (al.Value < dp));
- }
-
-@@ -156,8 +156,8 @@ namespace RecInt
- template <>
- inline void laddmul(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c, const ruint<__RECINT_LIMB_SIZE>& d) {
- auto dp(d.Value);
-- umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp);
-+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, 0, dp);
- }
-
- // a = b*c + d (r stores the carry)
-@@ -202,8 +202,8 @@ namespace RecInt
- auto dph(d.High.Value);
- auto dpl(d.Low.Value);
-
-- umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-- add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl);
-+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-+ recint_add_ssaaaa(ah.Value, al.Value, ah.Value, al.Value, dph, dpl);
- r = ((ah.Value < dph) || ((ah.Value == dph) && (al.Value < dpl)));
- }
-
-diff --git a/src/kernel/recint/rudiv.h b/src/kernel/recint/rudiv.h
-index 83d2129..6151e22 100644
---- a/src/kernel/recint/rudiv.h
-+++ b/src/kernel/recint/rudiv.h
-@@ -211,7 +211,7 @@ namespace RecInt
- bool ret = false;
-
- if (a2.Value < b1.Value) {
-- udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value);
-+ recint_udiv_qrnnd(q.Value, c, a2.Value, a1.Value, b1.Value);
- } else {
- q.Value = __RECINT_MINUSONE;
- c = a1.Value + b1.Value;
-@@ -219,8 +219,8 @@ namespace RecInt
- ret = true;
- }
-
-- umul_ppmm(d1, d0, q.Value, b0.Value);
-- sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0);
-+ recint_umul_ppmm(d1, d0, q.Value, b0.Value);
-+ recint_sub_ddmmss(r1.Value, r0.Value, c, a0.Value, d1, d0);
-
- if (!ret && ((d1 > c) || ((d1 == c) && (d0 > a0.Value)))) {
- q.Value--;
-@@ -253,7 +253,7 @@ namespace RecInt
- inline void div_2_1(ruint<__RECINT_LIMB_SIZE>& q, ruint<__RECINT_LIMB_SIZE>& r,
- const ruint<__RECINT_LIMB_SIZE>& ah, const ruint<__RECINT_LIMB_SIZE>& al,
- const ruint<__RECINT_LIMB_SIZE>& b) {
-- udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value);
-+ recint_udiv_qrnnd(q.Value, r.Value, ah.Value, al.Value, b.Value);
- }
-
- // computes (q, r) such that a = q*b + r (0 <= r < b)
-diff --git a/src/kernel/recint/ruint.h b/src/kernel/recint/ruint.h
-index 1fc930b..e95d254 100644
---- a/src/kernel/recint/ruint.h
-+++ b/src/kernel/recint/ruint.h
-@@ -80,19 +80,7 @@ knowledge of the CeCILL-B license and that you accept its terms.
- /* GMP conversion system */
- #include "ruconvert.h"
-
--// Cleaning up MACROS defined in reclonglong.h (to avoid collision with FLINT's longlong.h)
--#undef add_ssaaaa
--#undef count_leading_zeros
--#undef count_trailing_zeros
--#undef smul_ppmm
--#undef sub_ddmmss
--#undef umul_ppmm
--#undef udiv_qrnnd
--#undef __BITS4
--#undef __ll_B
--#undef __ll_lowpart
--#undef __ll_highpart
--#endif
-+#endif // RUINT_H
-
- /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- // vim:sts=4:sw=4:ts=4:et:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s
-diff --git a/src/kernel/recint/rumul.h b/src/kernel/recint/rumul.h
-index 10edcf8..d7d33db 100644
---- a/src/kernel/recint/rumul.h
-+++ b/src/kernel/recint/rumul.h
-@@ -174,7 +174,7 @@ namespace RecInt
-
- template<>
- inline void lmul_naive(ruint<__RECINT_LIMB_SIZE>& ah, ruint<__RECINT_LIMB_SIZE>& al, const ruint<__RECINT_LIMB_SIZE>& b, const ruint<__RECINT_LIMB_SIZE>& c) {
-- umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
-+ recint_umul_ppmm(ah.Value, al.Value, b.Value, c.Value);
- }
- template
- inline void lmul_naive(ruint& a, const ruint& b, const ruint& c) {
-@@ -243,7 +243,7 @@ namespace RecInt
- }
- template
- inline __RECINT_IS_ARITH(T, void) lmul(limb& ret, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) {
-- umul_ppmm(ret, a.Value, b.Value, limb(c));
-+ recint_umul_ppmm(ret, a.Value, b.Value, limb(c));
- }
-
- // a = b*c
-@@ -352,7 +352,7 @@ namespace RecInt
- }
- template<>
- inline ruint<__RECINT_LIMB_SIZE+1>& lsquare(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE>& b) {
-- umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value);
-+ recint_umul_ppmm(a.High.Value, a.Low.Value, b.Value, b.Value);
- return a;
- }
-
-diff --git a/src/kernel/recint/rusub.h b/src/kernel/recint/rusub.h
-index 74a2e39..0b91088 100644
---- a/src/kernel/recint/rusub.h
-+++ b/src/kernel/recint/rusub.h
-@@ -187,7 +187,7 @@ namespace RecInt
- template<>
- inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) {
- r = (b < c);
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
- }
- #endif
- template<>
-@@ -213,7 +213,7 @@ namespace RecInt
- template<>
- inline void sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
- r = (a < b);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- }
- #endif
- template<>
-@@ -239,7 +239,7 @@ namespace RecInt
- #else
- template<>
- inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c) {
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
- return a;
- }
- #endif
-@@ -266,7 +266,7 @@ namespace RecInt
- #else
- template<>
- inline ruint<__RECINT_LIMB_SIZE+1>& sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- return a;
-
- }
-@@ -290,7 +290,7 @@ namespace RecInt
- template
- inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) {
- r = (b < c);
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c));
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c));
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) {
-@@ -308,7 +308,7 @@ namespace RecInt
- template
- inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) {
- r = (a < b);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b));
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b));
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(bool& r, ruint<__RECINT_LIMB_SIZE>& a, const T& b) {
-@@ -325,7 +325,7 @@ namespace RecInt
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const T& c) {
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c));
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, limb(c));
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const ruint<__RECINT_LIMB_SIZE>& b, const T& c) {
-@@ -341,7 +341,7 @@ namespace RecInt
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE+1>& a, const T& b) {
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b));
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, limb(b));
- }
- template
- inline __RECINT_IS_ARITH(T, void) sub(ruint<__RECINT_LIMB_SIZE>& a, const T& b) {
-@@ -367,7 +367,7 @@ namespace RecInt
- template<>
- inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
- r = (b == 0);
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -393,7 +393,7 @@ namespace RecInt
- template<>
- inline void sub_1(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a) {
- r = (a == 0);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -417,7 +417,7 @@ namespace RecInt
- #else
- template<>
- inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b) {
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -440,7 +440,7 @@ namespace RecInt
- #else
- template<>
- inline void sub_1(ruint<__RECINT_LIMB_SIZE+1>& a) {
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -469,8 +469,8 @@ namespace RecInt
- inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) {
- if (cy) r = (b <= c);
- else r = (b < c);
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -499,8 +499,8 @@ namespace RecInt
- inline void sub_wc(bool& r, ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) {
- if (cy) r = (a <= b);
- else r = (a < b);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-- if (cy) sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ if (cy) recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, 1);
- }
- #endif
- template<>
-@@ -525,8 +525,8 @@ namespace RecInt
- #else
- template<>
- inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const ruint<__RECINT_LIMB_SIZE+1>& c, const bool& cy) {
-- sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, b.High.Value, b.Low.Value, c.High.Value, c.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
- }
- #endif
- template<>
-@@ -549,8 +549,8 @@ namespace RecInt
- #else
- template<>
- inline void sub_wc(ruint<__RECINT_LIMB_SIZE+1>& a, const ruint<__RECINT_LIMB_SIZE+1>& b, const bool& cy) {
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
-- sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, 0, cy);
-+ recint_sub_ddmmss(a.High.Value, a.Low.Value, a.High.Value, a.Low.Value, b.High.Value, b.Low.Value);
- }
- #endif
- template<>
diff --git a/build/pkgs/givaro/spkg-configure.m4 b/build/pkgs/givaro/spkg-configure.m4
index 36cfab37081..18eb1d30195 100644
--- a/build/pkgs/givaro/spkg-configure.m4
+++ b/build/pkgs/givaro/spkg-configure.m4
@@ -1,6 +1,6 @@
SAGE_SPKG_CONFIGURE([givaro], [
PKG_CHECK_MODULES([GIVARO],
- [givaro >= 4.1.1],dnl The version test is refined in linbox/spkg-configure.m4
+ [givaro >= 4.2.0],dnl The version test is refined in linbox/spkg-configure.m4
[sage_spkg_install_givaro=no],
[sage_spkg_install_givaro=yes])
])
diff --git a/build/pkgs/gmpy2/version_requirements.txt b/build/pkgs/gmpy2/version_requirements.txt
deleted file mode 100644
index 11116398187..00000000000
--- a/build/pkgs/gmpy2/version_requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-gmpy2 ~=2.1.b999
diff --git a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
index 6da100dad5a..067609dd4fc 100644
--- a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
+++ b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini
@@ -1,5 +1,5 @@
tarball=hatch_fancy_pypi_readme-VERSION-py3-none-any.whl
-sha1=4076ea14577b3c711a8345498d8f91b1c8a13d09
-md5=d7acd13333f6c71dcbfa62420c7f257b
-cksum=1527082323
+sha1=25cd6749c20a6803cbf1b6c4d29338c344a8f09c
+md5=a38ee7191a80ebdbbf0f126f7dff7e46
+cksum=1509914432
upstream_url=https://pypi.io/packages/py3/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION-py3-none-any.whl
diff --git a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt
index f8aed3e0b7a..7c974b0f495 100644
--- a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt
+++ b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt
@@ -1 +1 @@
-23.1.0
+24.1.0
diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini
index 5d8b02696c5..1d5f9b819ce 100644
--- a/build/pkgs/hatchling/checksums.ini
+++ b/build/pkgs/hatchling/checksums.ini
@@ -1,5 +1,5 @@
tarball=hatchling-VERSION-py3-none-any.whl
-sha1=aa9d69b9dd820716440252d737a4aeaf9b4e541f
-md5=20e5ea4deea21f91759fb2269b71f0dd
-cksum=446304413
+sha1=2212af13a26dbaea72c7a4ecbdb950c05f6e7c00
+md5=0301aa5bc8739e9f4d58e29d285fb2f7
+cksum=1232194081
upstream_url=https://pypi.io/packages/py3/h/hatchling/hatchling-VERSION-py3-none-any.whl
diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt
index 39893559155..da9594fd66f 100644
--- a/build/pkgs/hatchling/package-version.txt
+++ b/build/pkgs/hatchling/package-version.txt
@@ -1 +1 @@
-1.20.0
+1.22.5
diff --git a/build/pkgs/importlib_metadata/dependencies b/build/pkgs/importlib_metadata/dependencies
index 3c8d3aeb24e..5f8dc152989 100644
--- a/build/pkgs/importlib_metadata/dependencies
+++ b/build/pkgs/importlib_metadata/dependencies
@@ -1,4 +1,4 @@
- zipp typing_extensions | $(PYTHON_TOOLCHAIN) tomli $(PYTHON)
+ zipp typing_extensions | pip tomli $(PYTHON)
----------
All lines of this file are ignored except the first.
diff --git a/build/pkgs/jupyter_core/version_requirements.txt b/build/pkgs/jupyter_core/version_requirements.txt
deleted file mode 100644
index a0e9af65ff5..00000000000
--- a/build/pkgs/jupyter_core/version_requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-jupyter_core >=4.6.3
diff --git a/build/pkgs/linbox/checksums.ini b/build/pkgs/linbox/checksums.ini
index 23f1a33a68f..397bf97190f 100644
--- a/build/pkgs/linbox/checksums.ini
+++ b/build/pkgs/linbox/checksums.ini
@@ -1,4 +1,5 @@
tarball=linbox-VERSION.tar.gz
-sha1=9268e21b5aecbbfc45204b25195b786f80b769bc
-md5=1e90e300c7a324a7b6cece7c605b7a4e
-cksum=1921179523
+sha1=24e8bdbd16fe3dedce0dd343398999a4aed7c02c
+md5=1e1b95f12f015815a0194eac0cb611d0
+cksum=253115750
+upstream_url=https://github.com/linbox-team/linbox/releases/download/vVERSION/linbox-VERSION.tar.gz
\ No newline at end of file
diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt
index 8ecda4f1393..bd8bf882d06 100644
--- a/build/pkgs/linbox/package-version.txt
+++ b/build/pkgs/linbox/package-version.txt
@@ -1 +1 @@
-1.6.3.p1
+1.7.0
diff --git a/build/pkgs/linbox/patches/292.patch b/build/pkgs/linbox/patches/292.patch
new file mode 100644
index 00000000000..d3333cc23d0
--- /dev/null
+++ b/build/pkgs/linbox/patches/292.patch
@@ -0,0 +1,24 @@
+From 49b9cccd0286980c1c1811c3b03df883ef0164df Mon Sep 17 00:00:00 2001
+From: Doug Torrance
+Date: Tue, 14 Dec 2021 16:22:33 -0500
+Subject: [PATCH] Only register uint128_t as a TypeName when it's available.
+
+Otherwise, test-fft will fail when it isn't.
+---
+ tests/test-fft.C | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/tests/test-fft.C b/tests/test-fft.C
+index d19184c2d..5811ebc5d 100644
+--- a/tests/test-fft.C
++++ b/tests/test-fft.C
+@@ -55,7 +55,9 @@ REGISTER_TYPE_NAME(double);
+ REGISTER_TYPE_NAME(uint16_t);
+ REGISTER_TYPE_NAME(uint32_t);
+ REGISTER_TYPE_NAME(uint64_t);
++#ifdef __FFLASFFPACK_HAVE_INT128
+ REGISTER_TYPE_NAME(uint128_t);
++#endif
+ REGISTER_TYPE_NAME(Modular);
+ REGISTER_TYPE_NAME(ModularExtended);
+
diff --git a/build/pkgs/linbox/patches/294.patch b/build/pkgs/linbox/patches/294.patch
new file mode 100644
index 00000000000..7c645f165d3
--- /dev/null
+++ b/build/pkgs/linbox/patches/294.patch
@@ -0,0 +1,38 @@
+From f81a1f4a5e0835b7a0f3bb88a0fcbbaa32174cfa Mon Sep 17 00:00:00 2001
+From: Cyril Bouvier
+Date: Wed, 15 Dec 2021 16:00:39 +0100
+Subject: [PATCH] Only register uint128_t as a TypeName when it's available
+
+---
+ benchmarks/benchmark-fft.C | 2 ++
+ benchmarks/benchmark-polynomial-matrix-mul-fft.C | 2 ++
+ 2 files changed, 4 insertions(+)
+
+diff --git a/benchmarks/benchmark-fft.C b/benchmarks/benchmark-fft.C
+index 39b86c9d9..59a8be57c 100644
+--- a/benchmarks/benchmark-fft.C
++++ b/benchmarks/benchmark-fft.C
+@@ -54,7 +54,9 @@ REGISTER_TYPE_NAME(double);
+ REGISTER_TYPE_NAME(uint16_t);
+ REGISTER_TYPE_NAME(uint32_t);
+ REGISTER_TYPE_NAME(uint64_t);
++#ifdef __FFLASFFPACK_HAVE_INT128
+ REGISTER_TYPE_NAME(uint128_t);
++#endif
+ REGISTER_TYPE_NAME(Modular);
+ REGISTER_TYPE_NAME(ModularExtended);
+
+diff --git a/benchmarks/benchmark-polynomial-matrix-mul-fft.C b/benchmarks/benchmark-polynomial-matrix-mul-fft.C
+index e9b184bcf..7bf17f33e 100644
+--- a/benchmarks/benchmark-polynomial-matrix-mul-fft.C
++++ b/benchmarks/benchmark-polynomial-matrix-mul-fft.C
+@@ -65,7 +65,9 @@ REGISTER_TYPE_NAME(double);
+ REGISTER_TYPE_NAME(uint16_t);
+ REGISTER_TYPE_NAME(uint32_t);
+ REGISTER_TYPE_NAME(uint64_t);
++#ifdef __FFLASFFPACK_HAVE_INT128
+ REGISTER_TYPE_NAME(uint128_t);
++#endif
+ REGISTER_TYPE_NAME(Modular);
+ REGISTER_TYPE_NAME(ModularExtended);
+
diff --git a/build/pkgs/linbox/patches/310-backport.patch b/build/pkgs/linbox/patches/310-backport.patch
new file mode 100644
index 00000000000..c0c44bc1ec7
--- /dev/null
+++ b/build/pkgs/linbox/patches/310-backport.patch
@@ -0,0 +1,69 @@
+From b8f2d4ccdc0af4418d14f72caf6c4d01969092a3 Mon Sep 17 00:00:00 2001
+From: Jean-Guillaume Dumas
+Date: Fri, 26 Jan 2024 16:31:56 +0100
+Subject: [PATCH] const_cast missing faster empty init
+
+---
+ linbox/algorithms/gauss/gauss-nullspace.inl | 10 +-
+ .../matrix/sparsematrix/sparse-ell-matrix.h | 8 +-
+ .../matrix/sparsematrix/sparse-ellr-matrix.h | 18 +--
+ linbox/ring/ntl/ntl-lzz_p.h | 11 +-
+ linbox/ring/ntl/ntl-lzz_pe.h | 143 +++++++++---------
+ linbox/ring/ntl/ntl-zz_px.h | 6 +
+ 6 files changed, 104 insertions(+), 92 deletions(-)
+
+diff --git a/linbox/matrix/sparsematrix/sparse-ell-matrix.h b/linbox/matrix/sparsematrix/sparse-ell-matrix.h
+index 59006d6c5f..2604f47b81 100644
+--- a/linbox/matrix/sparsematrix/sparse-ell-matrix.h
++++ b/linbox/matrix/sparsematrix/sparse-ell-matrix.h
+@@ -1210,10 +1210,10 @@ namespace LinBox
+ _colid_beg = iter._colid_beg ;
+ _colid_it = iter._colid_it ;
+ _data_it = iter._data_it ;
+- _data_beg = iter._data_beg ;
+- _data_end = iter._data_end ;
+- _field = iter._field ;
+- _ld = iter._ld ;
++ const_cast(_data_beg) = iter._data_beg ;
++ const_cast(_data_end) = iter._data_end ;
++ const_cast(_field) = iter._field ;
++ const_cast(ld) = iter._ld ;
+ _row = iter._row ;
+
+ return *this;
+diff --git a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h
+index 498a5525db..a60943868b 100644
+--- a/linbox/matrix/sparsematrix/sparse-ellr-matrix.h
++++ b/linbox/matrix/sparsematrix/sparse-ellr-matrix.h
+@@ -1102,11 +1102,11 @@ namespace LinBox
+ _Iterator &operator = (const _Iterator &iter)
+ {
+ _data_it = iter._data_it ;
+- _data_beg = iter._data_beg ;
+- _data_end = iter._data_end ;
+- _field = iter._field ;
+- _rowid = iter._rowid;
+- _ld = iter._ld ;
++ const_cast(_data_beg) = iter._data_beg ;
++ const_cast(_data_end)= iter._data_end ;
++ const_cast(_field) = iter._field ;
++ const_cast&>(_rowid) = iter._rowid;
++ const_cast(ld) = iter._ld ;
+ _row = iter._row ;
+
+ return *this;
+@@ -1252,10 +1252,10 @@ namespace LinBox
+ _colid_beg = iter._colid_beg ;
+ _colid_it = iter._colid_it ;
+ _data_it = iter._data_it ;
+- _data_beg = iter._data_beg ;
+- _data_end = iter._data_end ;
+- _field = iter._field ;
+- _ld = iter._ld ;
++ const_cast(_data_beg) = iter._data_beg ;
++ const_cast(_data_end) = iter._data_end ;
++ const_cast(_field) = iter._field ;
++ const_cast(ld)= iter._ld ;
+ _row = iter._row ;
+
+ return *this;
diff --git a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch b/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch
deleted file mode 100644
index e0cb575b1a1..00000000000
--- a/build/pkgs/linbox/patches/fix-ksh-pkgconfig.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From 52c78df67a08de074991a93b57946b7bd5ea7196 Mon Sep 17 00:00:00 2001
-From: Dima Pasechnik
-Date: Fri, 8 May 2020 15:53:25 +0100
-Subject: [PATCH 1/1] remove redundant 1st and last lines
-
-they remain if the script is run under ksh, leading to broken .pc file
----
- linbox.pc.in | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/linbox.pc.in b/linbox.pc.in
-index f54285e..eb6835b 100644
---- a/linbox.pc.in
-+++ b/linbox.pc.in
-@@ -1,4 +1,3 @@
--/------------------ linbox.pc ------------------------
- prefix=@prefix@
- exec_prefix=@prefix@
- libdir=@libdir@
-@@ -11,4 +10,4 @@ Version: @VERSION@
- Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0
- Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@
- Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@
--\-------------------------------------------------------
-+
---
-2.26.2
-
diff --git a/build/pkgs/linbox/patches/linbox-pr-256.patch b/build/pkgs/linbox/patches/linbox-pr-256.patch
deleted file mode 100644
index 9ca03791d30..00000000000
--- a/build/pkgs/linbox/patches/linbox-pr-256.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-Extracted from the following patch:
-
-From 27811e0021c73de26bf2cff5105c763e163ca8b7 Mon Sep 17 00:00:00 2001
-From: Jean-Guillaume Dumas
-Date: Fri, 26 Jun 2020 09:54:38 +0200
-Subject: [PATCH 1/3] iterators may have different types
-
----
- linbox/vector/blas-subvector.h | 3 ++-
- 1 file changed, 2 insertions(+), 1 deletion(-)
-
-From 4ff828e20053ab2ef9adc4ce6931d159fd513cef Mon Sep 17 00:00:00 2001
-From: Jean-Guillaume Dumas
-Date: Fri, 26 Jun 2020 09:54:57 +0200
-Subject: [PATCH 2/3] incompatible const & with modifiers
-
----
- examples/Makefile.am | 3 +-
- examples/ratdet.C | 96 ++++++++++++++++++++++++++++++++
- linbox/algorithms/det-rational.h | 4 +-
- 3 files changed, 100 insertions(+), 3 deletions(-)
- create mode 100644 examples/ratdet.C
-
-diff --git a/linbox/algorithms/det-rational.h b/linbox/algorithms/det-rational.h
-index 327b4710f..1943876c1 100644
---- a/linbox/algorithms/det-rational.h
-+++ b/linbox/algorithms/det-rational.h
-@@ -79,8 +79,8 @@ namespace LinBox
- struct MyRationalModularDet {
- const Blackbox &A;
- const MyMethod &M;
-- const Integer &mul;//multiplicative prec;
-- const Integer ÷
-+ Integer mul;//multiplicative prec;
-+ Integer div;
-
- MyRationalModularDet(const Blackbox& b, const MyMethod& n,
- const Integer & p1, const Integer & p2) :
-
-From 567f727aa42de6678433591d731b275ea55fa8ad Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Cl=C3=A9ment=20Pernet?=
-Date: Fri, 26 Jun 2020 17:11:37 +0200
-Subject: [PATCH 3/3] untabify + auto-indent
-
----
- examples/ratdet.C | 66 +++++++++++++++++++++++------------------------
- 1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch b/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch
deleted file mode 100644
index c93915fb1b0..00000000000
--- a/build/pkgs/linbox/patches/remove-linboxsage-libs-from-pc.patch
+++ /dev/null
@@ -1,23 +0,0 @@
-Backported from:
-
-From 426eb97ba762c7663884f57ead0909f2aa3cd6a5 Mon Sep 17 00:00:00 2001
-From: Cyril Bouvier
-Date: Thu, 17 Jan 2019 16:32:19 +0100
-Subject: [PATCH] Remove @LINBOXSAGE_LIBS@ from linbox.pc.in
-
----
- linbox.pc.in | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/linbox.pc.in b/linbox.pc.in
-index 278f127e4..c6b8091eb 100644
---- a/linbox.pc.in
-+++ b/linbox.pc.in
-@@ -9,6 +9,6 @@ Description: Exact Linear Algebra library
- URL: http://github.com/linbox-team/linbox
- Version: @VERSION@
- Requires: fflas-ffpack >= 2.4.0, givaro >= 4.1.0
--Libs: -L${libdir} -llinbox @LINBOXSAGE_LIBS@ @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@
-+Libs: -L${libdir} -llinbox @NTL_LIBS@ @MPFR_LIBS@ @FPLLL_LIBS@ @IML_LIBS@ @FLINT_LIBS@ @OCL_LIBS@
- Cflags: @DEFAULT_CFLAGS@ -DDISABLE_COMMENTATOR -I${includedir} @NTL_CFLAGS@ @MPFR_CFLAGS@ @FPLLL_CFLAGS@ @IML_CFLAGS@ @FLINT_CFLAGS@
- \-------------------------------------------------------
diff --git a/build/pkgs/linbox/spkg-configure.m4 b/build/pkgs/linbox/spkg-configure.m4
index 8c5b85ef7fd..49cc871a85c 100644
--- a/build/pkgs/linbox/spkg-configure.m4
+++ b/build/pkgs/linbox/spkg-configure.m4
@@ -1,10 +1,10 @@
SAGE_SPKG_CONFIGURE([linbox], [
SAGE_SPKG_DEPCHECK([fflas_ffpack flint fplll givaro gmp iml m4ri m4rie mpfr ntl], [
PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching old versions
- [linbox >= 1.6.3 linbox <= 1.6.4 fflas-ffpack >= 2.4.0 fflas-ffpack < 2.5.0 givaro >= 4.1.1 givaro < 4.2.0],
+ [linbox >= 1.7.0 linbox < 1.8.0 fflas-ffpack >= 2.5.0 fflas-ffpack < 2.6.0 givaro >= 4.2.0 givaro < 4.3.0],
[sage_spkg_install_linbox=no],
[PKG_CHECK_MODULES([LINBOX],dnl Check for a set of matching new versions
- [linbox >= 1.7.0 linbox <= 1.7.0 fflas-ffpack >= 2.5.0 givaro >= 4.2.0 givaro < 4.3.0],
+ [linbox >= 1.8.0 linbox <= 1.9.0 fflas-ffpack >= 2.6.0 givaro >= 4.3.0 givaro < 4.4.0],
[sage_spkg_install_linbox=no],
[sage_spkg_install_linbox=yes])])
])
diff --git a/build/pkgs/linbox/spkg-install.in b/build/pkgs/linbox/spkg-install.in
index 8d415e81fd8..7acc5547e9f 100644
--- a/build/pkgs/linbox/spkg-install.in
+++ b/build/pkgs/linbox/spkg-install.in
@@ -15,7 +15,7 @@ export CPPFLAGS="$CPPFLAGS -DDISABLE_COMMENTATOR"
# If SAGE_FAT_BINARY is set, disable dependency that be discovered on the building system.
if [ "$SAGE_FAT_BINARY" = yes ]; then
- LINBOX_CONFIGURE="--disable-sse --disable-sse2 --disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE"
+ LINBOX_CONFIGURE="--disable-sse3 --disable-ssse3 --disable-sse41 --disable-sse42 --disable-fma --disable-fma4 --disable-avx --disable-avx2 --without-ocl $LINBOX_CONFIGURE"
fi
# Disable fplll as version 5.x is not supported by linbox <= 1.5.0.
diff --git a/build/pkgs/memory_allocator/version_requirements.txt b/build/pkgs/memory_allocator/version_requirements.txt
deleted file mode 100644
index fad07b22ebe..00000000000
--- a/build/pkgs/memory_allocator/version_requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-memory_allocator
diff --git a/build/pkgs/networkx/version_requirements.txt b/build/pkgs/networkx/version_requirements.txt
index 5a7e22a60db..2ff86e7c737 100644
--- a/build/pkgs/networkx/version_requirements.txt
+++ b/build/pkgs/networkx/version_requirements.txt
@@ -1 +1 @@
-networkx >=2.4, <3.3
+networkx >=2.4
diff --git a/build/pkgs/numpy/version_requirements.txt b/build/pkgs/numpy/version_requirements.txt
deleted file mode 100644
index 45127ae0a86..00000000000
--- a/build/pkgs/numpy/version_requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-numpy >=1.19
diff --git a/build/pkgs/packaging/checksums.ini b/build/pkgs/packaging/checksums.ini
index 3ee369817f0..69a230aa11c 100644
--- a/build/pkgs/packaging/checksums.ini
+++ b/build/pkgs/packaging/checksums.ini
@@ -1,5 +1,5 @@
tarball=packaging-VERSION-py3-none-any.whl
-sha1=d3fb436d835b252ea884a5d172d7265220127f95
-md5=f6e9c6af858bd34eff07b407d3f650a1
-cksum=3531019080
+sha1=21573cef174a05ac2794b34f3841d6f9ea9fa507
+md5=8b7ed65f4b1a2175ccab25317f2efccc
+cksum=4283692602
upstream_url=https://pypi.io/packages/py3/p/packaging/packaging-VERSION-py3-none-any.whl
diff --git a/build/pkgs/packaging/package-version.txt b/build/pkgs/packaging/package-version.txt
index 3c8ce91a469..d9133a54b63 100644
--- a/build/pkgs/packaging/package-version.txt
+++ b/build/pkgs/packaging/package-version.txt
@@ -1 +1 @@
-23.2
+24.0
diff --git a/build/pkgs/pcre/distros/alpine.txt b/build/pkgs/pcre/distros/alpine.txt
deleted file mode 100644
index a16e4119734..00000000000
--- a/build/pkgs/pcre/distros/alpine.txt
+++ /dev/null
@@ -1 +0,0 @@
-pcre-dev
diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini
index 0558c5caa45..ebfbc1a26a1 100644
--- a/build/pkgs/pip/checksums.ini
+++ b/build/pkgs/pip/checksums.ini
@@ -1,5 +1,5 @@
tarball=pip-VERSION-py3-none-any.whl
-sha1=4b2baddc0673f73017e531648a9ee27e47925e7a
-md5=5d2d058044a3ae2800d18e358ddc72ca
-cksum=1470281176
+sha1=e44313ae1e6af3c2bd3b60ab2fa8c34308d00555
+md5=74e3c5e4082113b1239ca0e9abfd1e82
+cksum=88131429
upstream_url=https://pypi.io/packages/py3/p/pip/pip-VERSION-py3-none-any.whl
diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt
index a9a57c82265..d9133a54b63 100644
--- a/build/pkgs/pip/package-version.txt
+++ b/build/pkgs/pip/package-version.txt
@@ -1 +1 @@
-23.3.1
+24.0
diff --git a/build/pkgs/pkgconfig/version_requirements.txt b/build/pkgs/pkgconfig/version_requirements.txt
deleted file mode 100644
index 549fd1bf164..00000000000
--- a/build/pkgs/pkgconfig/version_requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-pkgconfig
diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini
index 3757f701faf..f8770e91289 100644
--- a/build/pkgs/platformdirs/checksums.ini
+++ b/build/pkgs/platformdirs/checksums.ini
@@ -1,5 +1,5 @@
tarball=platformdirs-VERSION-py3-none-any.whl
-sha1=cafa761738da959f2df0a8a92da4c72fd8eaf93e
-md5=487007776ff343efc509b68d08cd7fd7
-cksum=162426958
+sha1=487a4610a037c90b242aafbe1e3f8b6ebb3ba1c8
+md5=99200c4e22d44a64a9c3ad0c72a317af
+cksum=1011122610
upstream_url=https://pypi.io/packages/py3/p/platformdirs/platformdirs-VERSION-py3-none-any.whl
diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt
index ee74734aa22..6aba2b245a8 100644
--- a/build/pkgs/platformdirs/package-version.txt
+++ b/build/pkgs/platformdirs/package-version.txt
@@ -1 +1 @@
-4.1.0
+4.2.0
diff --git a/build/pkgs/pplpy/version_requirements.txt b/build/pkgs/pplpy/version_requirements.txt
deleted file mode 100644
index bd048ecc980..00000000000
--- a/build/pkgs/pplpy/version_requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Issue #30922: pplpy 0.8.4 and earlier do not declare dependencies correctly
-pplpy >=0.8.6
diff --git a/build/pkgs/pycygwin/spkg-configure.m4 b/build/pkgs/pycygwin/spkg-configure.m4
deleted file mode 100644
index 7876a693d7b..00000000000
--- a/build/pkgs/pycygwin/spkg-configure.m4
+++ /dev/null
@@ -1 +0,0 @@
-SAGE_SPKG_CONFIGURE([pycygwin], [SAGE_PYTHON_PACKAGE_CHECK([pycygwin])])
diff --git a/build/pkgs/pyproject_hooks/dependencies b/build/pkgs/pyproject_hooks/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/pyproject_hooks/dependencies
+++ b/build/pkgs/pyproject_hooks/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
----------
All lines of this file are ignored except the first.
diff --git a/build/pkgs/python3/distros/fedora.txt b/build/pkgs/python3/distros/fedora.txt
index ba7b5ea9b21..ab50f2d9b07 100644
--- a/build/pkgs/python3/distros/fedora.txt
+++ b/build/pkgs/python3/distros/fedora.txt
@@ -1,2 +1,2 @@
python3-devel
-python-setuptools
+python3-setuptools
diff --git a/build/pkgs/python_build/dependencies b/build/pkgs/python_build/dependencies
index af994ec1059..4d5609413be 100644
--- a/build/pkgs/python_build/dependencies
+++ b/build/pkgs/python_build/dependencies
@@ -1,4 +1,4 @@
-tomli packaging pyproject_hooks importlib_metadata | $(PYTHON_TOOLCHAIN) $(PYTHON)
+tomli packaging pyproject_hooks importlib_metadata | pip $(PYTHON)
----------
All lines of this file are ignored except the first.
diff --git a/build/pkgs/sage_conf/version_requirements.txt b/build/pkgs/sage_conf/version_requirements.txt
index 1e512338617..858e8da5968 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.4b4
+sage-conf ~= 10.4b6
diff --git a/build/pkgs/sage_docbuild/version_requirements.txt b/build/pkgs/sage_docbuild/version_requirements.txt
index 005cc4306ac..0005ac13ace 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.4b4
+sage-docbuild ~= 10.4b6
diff --git a/build/pkgs/sage_setup/version_requirements.txt b/build/pkgs/sage_setup/version_requirements.txt
index fc721f1c1da..4efa9ed072c 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.4b4
+sage-setup ~= 10.4b6
diff --git a/build/pkgs/sage_sws2rst/version_requirements.txt b/build/pkgs/sage_sws2rst/version_requirements.txt
index 9ec4b186ac9..a44b4f816f4 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.4b4
+sage-sws2rst ~= 10.4b6
diff --git a/build/pkgs/sagelib/version_requirements.txt b/build/pkgs/sagelib/version_requirements.txt
index 9c1daa7372c..43ee6e618ef 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.4b4
+sagemath-standard ~= 10.4b6
diff --git a/build/pkgs/sagemath_bliss/version_requirements.txt b/build/pkgs/sagemath_bliss/version_requirements.txt
index ba9e1b2081d..11ff86c39e6 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.4b4
+sagemath-bliss ~= 10.4b6
diff --git a/build/pkgs/sagemath_categories/version_requirements.txt b/build/pkgs/sagemath_categories/version_requirements.txt
index 6155490b8ef..4bf63679c82 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.4b4
+sagemath-categories ~= 10.4b6
diff --git a/build/pkgs/sagemath_coxeter3/version_requirements.txt b/build/pkgs/sagemath_coxeter3/version_requirements.txt
index 932bad03329..4d8933d8f37 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.4b4
+sagemath-coxeter3 ~= 10.4b6
diff --git a/build/pkgs/sagemath_environment/version_requirements.txt b/build/pkgs/sagemath_environment/version_requirements.txt
index 475da537a2b..c97a53a9437 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.4b4
+sagemath-environment ~= 10.4b6
diff --git a/build/pkgs/sagemath_mcqd/version_requirements.txt b/build/pkgs/sagemath_mcqd/version_requirements.txt
index 0631db2f93a..697eed8cf48 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.4b4
+sagemath-mcqd ~= 10.4b6
diff --git a/build/pkgs/sagemath_meataxe/version_requirements.txt b/build/pkgs/sagemath_meataxe/version_requirements.txt
index fa695fc4e4a..8e7db0b81a8 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.4b4
+sagemath-meataxe ~= 10.4b6
diff --git a/build/pkgs/sagemath_objects/spkg-check b/build/pkgs/sagemath_objects/spkg-check
index 87cea0fc38f..99a8528e0da 100755
--- a/build/pkgs/sagemath_objects/spkg-check
+++ b/build/pkgs/sagemath_objects/spkg-check
@@ -1,15 +1,45 @@
#!/usr/bin/env bash
cd src
+if [ ! -r tox.ini ]; then
+ echo "Not testing the package because there is no tox.ini"
+ exit 0
+fi
+
+for lib in "$SAGE_SRC/bin/sage-src-env-config" "$SAGE_SRC/bin/sage-env-config" "$SAGE_SRC/bin/sage-env" "$SAGE_ROOT/build/bin/sage-build-env-config" "$SAGE_ROOT/build/bin/sage-build-env"; do
+ source "$lib"
+ if [ $? -ne 0 ]; then
+ echo >&2 "Error: failed to source $lib"
+ echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?"
+ exit 1
+ fi
+done
+
export PIP_NO_INDEX=true
export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS"
-export TOX_PARALLEL_NO_SPINNER=1
+unset tox_args
+
wheel="$(sed -n '1s,.*@ file://,,p' $SAGE_SPKG_SCRIPTS/$PKG_BASE/spkg-requirements.txt)"
-echo Running "tox -r -p auto -v --installpkg $wheel"
-tox -r -p auto -v --installpkg "$wheel"
+if [ -n "$wheel" ]; then
+ tox_envs=$(tox -l)
+ tox_args="-r -p auto -v --installpkg $wheel"
+elif [ "$SAGE_EDITABLE" = yes ]; then
+ tox_envs=$(tox -l | sed s/norequirements/editable/)
+ # FIXME: Should use -r if sage_setup or another build requirement changes
+ tox_args="-r -v -v -v -v -e $(echo $tox_envs | sed 's/ /,/g')"
+else
+ echo "Not testing the package because SAGE_WHEELS=$SAGE_WHEELS and SAGE_EDITABLE=$SAGE_EDITABLE"
+ exit 0
+fi
+
+export TOX_PARALLEL_NO_SPINNER=1
+
+echo Running "tox $tox_args"
+tox $tox_args
status=$?
case $status:$SAGE_CHECK:$([ -r known-test-failures.json ]; echo $?) in
+ 0:no:*) echo "Not testing the package because SAGE_CHECK=no";;
0:*:0) echo "Passed the test suite (modulo baseline known-test-failures*.json)";;
0:*:*) echo "Passed the test suite";;
*:warn:0) echo "Warning: New failures (not in baseline known-test-failures*.json (ignored)"; status=0;;
@@ -18,10 +48,12 @@ case $status:$SAGE_CHECK:$([ -r known-test-failures.json ]; echo $?) in
*:yes:*) echo "Failures testing the package";;
esac
# Show summaries of failures (suppress lines ending with '[failed in baseline]')
-for f in $(pwd)/.tox/sagepython-sagewheels-nopypi-norequirements*/log/*-command*.log; do
- if [ -r "$f" ]; then
- echo "$f"
- grep '^sage -t.*#[^]]*$' "$f"
- fi
+for e in $tox_envs; do
+ for f in $(pwd)/.tox/$e/log/*-command*.log; do
+ if [ -r "$f" ]; then
+ echo "$f"
+ grep '^sage -t.*#[^]]*$' "$f"
+ fi
+ done
done
exit $status
diff --git a/build/pkgs/sagemath_objects/version_requirements.txt b/build/pkgs/sagemath_objects/version_requirements.txt
index 38fc3c4dec3..92bb9707da1 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.4b4
+sagemath-objects ~= 10.4b6
diff --git a/build/pkgs/sagemath_repl/version_requirements.txt b/build/pkgs/sagemath_repl/version_requirements.txt
index 9b039bdd460..2b2a5e66ef5 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.4b4
+sagemath-repl ~= 10.4b6
diff --git a/build/pkgs/sagemath_sirocco/version_requirements.txt b/build/pkgs/sagemath_sirocco/version_requirements.txt
index 09d50bb08f7..106893032ab 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.4b4
+sagemath-sirocco ~= 10.4b6
diff --git a/build/pkgs/sagemath_tdlib/version_requirements.txt b/build/pkgs/sagemath_tdlib/version_requirements.txt
index 5f55b35c539..71dc621822a 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.4b4
+sagemath-tdlib ~= 10.4b6
diff --git a/build/pkgs/sbcl/SPKG.rst b/build/pkgs/sbcl/SPKG.rst
new file mode 100644
index 00000000000..e3a383239c7
--- /dev/null
+++ b/build/pkgs/sbcl/SPKG.rst
@@ -0,0 +1,24 @@
+sbcl: a lisp compiler and runtime system
+=====================================================================
+
+Description
+-----------
+
+Steel Bank Common Lisp (SBCL) is a high performance Common Lisp compiler. It is
+open source / free software, with a permissive license (see https://www.sbcl.org/history.html).
+In addition to the compiler and runtime system for ANSI Common Lisp, it provides an interactive
+environment including a debugger, a statistical profiler, a code coverage tool,
+and many other extensions.
+
+(taken from https://www.sbcl.org)
+
+License
+-------
+
+- a mix of BSD-style and public domain
+
+
+Upstream Contact
+----------------
+
+- https://www.sbcl.org
diff --git a/build/pkgs/sbcl/distros/alpine.txt b/build/pkgs/sbcl/distros/alpine.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/alpine.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/arch.txt b/build/pkgs/sbcl/distros/arch.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/arch.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/conda.txt b/build/pkgs/sbcl/distros/conda.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/conda.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/cygwin.txt b/build/pkgs/sbcl/distros/cygwin.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/cygwin.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/debian.txt b/build/pkgs/sbcl/distros/debian.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/debian.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/fedora.txt b/build/pkgs/sbcl/distros/fedora.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/fedora.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/freebsd.txt b/build/pkgs/sbcl/distros/freebsd.txt
new file mode 100644
index 00000000000..51399eefa59
--- /dev/null
+++ b/build/pkgs/sbcl/distros/freebsd.txt
@@ -0,0 +1 @@
+lang/sbcl
diff --git a/build/pkgs/sbcl/distros/gentoo.txt b/build/pkgs/sbcl/distros/gentoo.txt
new file mode 100644
index 00000000000..e02e0542b58
--- /dev/null
+++ b/build/pkgs/sbcl/distros/gentoo.txt
@@ -0,0 +1 @@
+dev-lisp/sbcl
diff --git a/build/pkgs/sbcl/distros/homebrew.txt b/build/pkgs/sbcl/distros/homebrew.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/homebrew.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/macports.txt b/build/pkgs/sbcl/distros/macports.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/macports.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/nix.txt b/build/pkgs/sbcl/distros/nix.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/nix.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/openbsd.txt b/build/pkgs/sbcl/distros/openbsd.txt
new file mode 100644
index 00000000000..51399eefa59
--- /dev/null
+++ b/build/pkgs/sbcl/distros/openbsd.txt
@@ -0,0 +1 @@
+lang/sbcl
diff --git a/build/pkgs/sbcl/distros/opensuse.txt b/build/pkgs/sbcl/distros/opensuse.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/opensuse.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/repology.txt b/build/pkgs/sbcl/distros/repology.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/repology.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/sbcl/distros/void.txt b/build/pkgs/sbcl/distros/void.txt
new file mode 100644
index 00000000000..0e94cb07231
--- /dev/null
+++ b/build/pkgs/sbcl/distros/void.txt
@@ -0,0 +1 @@
+sbcl
diff --git a/build/pkgs/gambit/math b/build/pkgs/sbcl/math
similarity index 100%
rename from build/pkgs/gambit/math
rename to build/pkgs/sbcl/math
diff --git a/build/pkgs/sbcl/spkg-configure.m4 b/build/pkgs/sbcl/spkg-configure.m4
new file mode 100644
index 00000000000..aef49ce770b
--- /dev/null
+++ b/build/pkgs/sbcl/spkg-configure.m4
@@ -0,0 +1,20 @@
+SAGE_SPKG_CONFIGURE([sbcl], [dnl
+ m4_pushdef([SAGE_SBCL_MINVER], ["1.4.16"])
+ AC_CACHE_CHECK([for sbcl >= SAGE_SBCL_MINVER], [ac_cv_path_SBCL], [
+ AC_PATH_PROGS_FEATURE_CHECK([SBCL], [sbcl], [
+ sbcl_version=`$ac_path_SBCL --version 2>&1 \
+ | $SED -n -e 's/SBCL *\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\)/\1/p'`
+ AS_IF([test -n "$sbcl_version"], [
+ AX_COMPARE_VERSION([$sbcl_version], [ge], [SAGE_SBCL_MINVER], [
+ ac_cv_path_SBCL="$ac_path_SBCL"
+ ac_path_SBCL_found=:
+ ])
+ ])
+ ])
+ ])
+ AS_IF([test -z "$ac_cv_path_SBCL" ], [
+ sage_spkg_install_sbcl=yes
+ AC_SUBST(SAGE_FRICAS_LISP, ecl)], [
+ AC_SUBST(SAGE_FRICAS_LISP, "sbcl --dynamic-space-size 4Gb")])
+ m4_popdef([SAGE_SBCL_MINVER])
+])
diff --git a/build/pkgs/sbcl/type b/build/pkgs/sbcl/type
new file mode 100644
index 00000000000..134d9bc32d5
--- /dev/null
+++ b/build/pkgs/sbcl/type
@@ -0,0 +1 @@
+optional
diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini
index 1a88d2a7464..bcd55128882 100644
--- a/build/pkgs/setuptools/checksums.ini
+++ b/build/pkgs/setuptools/checksums.ini
@@ -1,5 +1,5 @@
tarball=setuptools-VERSION-py3-none-any.whl
-sha1=4227225bb193e3a45542f45966caf777d4c913e8
-md5=f096ed836f4036a11aa277fa16dc09ff
-cksum=263664173
+sha1=49841be6743b2d129d01d02d5fd339dd693c99dc
+md5=1555b24e28b53f3342e557500dedf8f3
+cksum=3445997019
upstream_url=https://pypi.io/packages/py3/s/setuptools/setuptools-VERSION-py3-none-any.whl
diff --git a/build/pkgs/setuptools/distros/fedora.txt b/build/pkgs/setuptools/distros/fedora.txt
index e1ad17860cd..1c0901c0374 100644
--- a/build/pkgs/setuptools/distros/fedora.txt
+++ b/build/pkgs/setuptools/distros/fedora.txt
@@ -1 +1 @@
-python-setuptools
+python3-setuptools
diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt
index 2c021f541a8..2a93f495d25 100644
--- a/build/pkgs/setuptools/package-version.txt
+++ b/build/pkgs/setuptools/package-version.txt
@@ -1 +1 @@
-69.0.2
+69.5.1
diff --git a/build/pkgs/setuptools/version_requirements.txt b/build/pkgs/setuptools/version_requirements.txt
deleted file mode 100644
index c12b5900873..00000000000
--- a/build/pkgs/setuptools/version_requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-# 68.1.0 Promote pyproject.toml's [tool.setuptools] out of beta.
-# 68.1.1 Fix editable install finder handling of nested packages
-setuptools >= 68.1.1
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt b/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt
deleted file mode 100644
index c08bc88e16c..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/alpine.txt
+++ /dev/null
@@ -1 +0,0 @@
-py3-setuptools-scm-git-archive
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt b/build/pkgs/setuptools_scm_git_archive/distros/arch.txt
deleted file mode 100644
index bc2a39f97a3..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/arch.txt
+++ /dev/null
@@ -1 +0,0 @@
-python-setuptools-scm-git-archive
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt b/build/pkgs/setuptools_scm_git_archive/distros/debian.txt
deleted file mode 100644
index 538474ff946..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/debian.txt
+++ /dev/null
@@ -1 +0,0 @@
-setuptools-scm-git-archive
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt b/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt
deleted file mode 100644
index ada37357769..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/fedora.txt
+++ /dev/null
@@ -1 +0,0 @@
-python-setuptools_scm_git_archive
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt b/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt
deleted file mode 100644
index 2ace76a2af0..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/freebsd.txt
+++ /dev/null
@@ -1 +0,0 @@
-devel/py-setuptools_scm_git_archive
diff --git a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt b/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt
deleted file mode 100644
index fb7388e3dd7..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/distros/gentoo.txt
+++ /dev/null
@@ -1 +0,0 @@
-dev-python/setuptools_scm_git_archive
diff --git a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4 b/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4
deleted file mode 100644
index 0da3db40d22..00000000000
--- a/build/pkgs/setuptools_scm_git_archive/spkg-configure.m4
+++ /dev/null
@@ -1 +0,0 @@
-SAGE_SPKG_CONFIGURE([setuptools_scm_git_archive], [SAGE_PYTHON_PACKAGE_CHECK([setuptools_scm_git_archive])])
diff --git a/build/pkgs/singular/checksums.ini b/build/pkgs/singular/checksums.ini
index 40ba7b3153f..3273f5f7055 100644
--- a/build/pkgs/singular/checksums.ini
+++ b/build/pkgs/singular/checksums.ini
@@ -1,5 +1,5 @@
tarball=singular-VERSION.tar.gz
-sha1=0dd736f26935ed72999bb9a4bbb98c6df18ab9ea
-md5=0f9368193bad9a0c3dc84545b2404761
-cksum=1698641648
-upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/4-3-2/singular-VERSION.tar.gz
+sha1=1f678e1cc756fd8dc29dcdef5ae67441b6bcc779
+md5=09382cdacbfe67b4099056b65c2ec016
+cksum=244065751
+upstream_url=ftp://jim.mathematik.uni-kl.de/pub/Math/Singular/SOURCES/${VERSION_MAJOR}-${VERSION_MINOR}-${VERSION_MICRO}/singular-VERSION.tar.gz
diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt
index 7801ff9a882..fdc6698807a 100644
--- a/build/pkgs/singular/package-version.txt
+++ b/build/pkgs/singular/package-version.txt
@@ -1 +1 @@
-4.3.2p8
+4.4.0
diff --git a/build/pkgs/trove_classifiers/checksums.ini b/build/pkgs/trove_classifiers/checksums.ini
index 7350f444667..8bd0eca4933 100644
--- a/build/pkgs/trove_classifiers/checksums.ini
+++ b/build/pkgs/trove_classifiers/checksums.ini
@@ -1,5 +1,5 @@
tarball=trove_classifiers-VERSION-py3-none-any.whl
-sha1=c341abee77b5c87d913b86dc587e544553f0658c
-md5=78e67f128f53b8417134429192810701
-cksum=3034057088
+sha1=36240d053d16400380aee01f0879785693008a96
+md5=02b3e7b2eb81c3656fa859a87482f120
+cksum=1500381935
upstream_url=https://pypi.io/packages/py3/t/trove_classifiers/trove_classifiers-VERSION-py3-none-any.whl
diff --git a/build/pkgs/trove_classifiers/package-version.txt b/build/pkgs/trove_classifiers/package-version.txt
index a33bd2f9968..c296ac66b65 100644
--- a/build/pkgs/trove_classifiers/package-version.txt
+++ b/build/pkgs/trove_classifiers/package-version.txt
@@ -1 +1 @@
-2023.11.29
+2024.4.10
diff --git a/build/pkgs/uri_template/dependencies b/build/pkgs/uri_template/dependencies
index 47296a7bace..644ad35f773 100644
--- a/build/pkgs/uri_template/dependencies
+++ b/build/pkgs/uri_template/dependencies
@@ -1,4 +1,4 @@
- | $(PYTHON_TOOLCHAIN) $(PYTHON)
+ | pip $(PYTHON)
----------
All lines of this file are ignored except the first.
diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini
index 4f2b8c5c534..6e28451ce44 100644
--- a/build/pkgs/wheel/checksums.ini
+++ b/build/pkgs/wheel/checksums.ini
@@ -1,5 +1,5 @@
tarball=wheel-VERSION-py3-none-any.whl
-sha1=fcf4ad8d5d8216d661bc98eede0d9210cbc5b697
-md5=779d91395ceb12e15e3a585b30b53f9f
-cksum=1421399426
+sha1=71a83a2237cb57ab45bdafed364564e36ca5dc95
+md5=e65b1197e1dfc6bbc8df362935f5943d
+cksum=1664872683
upstream_url=https://pypi.io/packages/py3/w/wheel/wheel-VERSION-py3-none-any.whl
diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt
index 787ffc30a81..8298bb08b2d 100644
--- a/build/pkgs/wheel/package-version.txt
+++ b/build/pkgs/wheel/package-version.txt
@@ -1 +1 @@
-0.42.0
+0.43.0
diff --git a/build/pkgs/wheel/version_requirements.txt b/build/pkgs/wheel/version_requirements.txt
deleted file mode 100644
index 43f74ab0144..00000000000
--- a/build/pkgs/wheel/version_requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# https://github.com/sagemath/sage/issues/31050 - version constraint for macOS Big Sur support
-wheel >=0.36.2
diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py
index 96bca3a6d8c..cf11e3c4da7 100644
--- a/build/sage_bootstrap/app.py
+++ b/build/sage_bootstrap/app.py
@@ -10,7 +10,10 @@
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2016 Volker Braun
+# 2020-2024 Matthias Koeppe
+# 2022 Thierry Monteil
+# 2024 Marc Culler
#
# 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
@@ -21,6 +24,7 @@
import os
+import re
import logging
log = logging.getLogger()
@@ -36,6 +40,10 @@
from sage_bootstrap.env import SAGE_DISTFILES
+# Approximation of https://peps.python.org/pep-0508/#names dependency specification
+dep_re = re.compile('^ *([-A-Z0-9._]+)', re.IGNORECASE)
+
+
class Application(object):
def config(self):
@@ -88,7 +96,7 @@ def properties(self, *package_classes, **kwds):
source_maxima='normal'
trees_maxima='SAGE_LOCAL'
"""
- props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees'])
+ props = kwds.pop('props', ['path', 'version_with_patchlevel', 'type', 'source', 'trees', 'purl'])
format = kwds.pop('format', 'plain')
log.debug('Looking up properties')
pc = PackageClass(*package_classes)
@@ -256,6 +264,9 @@ def update_latest(self, package_name, commit=False):
Update a package to the latest version. This modifies the Sage sources.
"""
pkg = Package(package_name)
+ if pkg.source not in ['normal', 'wheel']:
+ log.debug('update_latest can only update normal and wheel packages; %s is a %s package' % (pkg, pkg.source))
+ return
dist_name = pkg.distribution_name
if dist_name is None:
log.debug('%s does not have Python distribution info in version_requirements.txt' % pkg)
@@ -304,11 +315,14 @@ def download(self, package_name, allow_upstream=False):
package.tarball.download(allow_upstream=allow_upstream)
print(package.tarball.upstream_fqn)
- def download_cls(self, package_name_or_class, allow_upstream=False, on_error='stop'):
+ def download_cls(self, *package_classes, **kwds):
"""
Download a package or a class of packages
"""
- pc = PackageClass(package_name_or_class, has_files=['checksums.ini'])
+ allow_upstream = kwds.pop('allow_upstream', False)
+ on_error = kwds.pop('on_error', 'stop')
+ has_files = list(kwds.pop('has_files', []))
+ pc = PackageClass(*package_classes, has_files=has_files + ['checksums.ini'], **kwds)
def download_with_args(package):
try:
@@ -380,7 +394,8 @@ def fix_checksum(self, package_name):
update.fix_checksum()
def create(self, package_name, version=None, tarball=None, pkg_type=None, upstream_url=None,
- description=None, license=None, upstream_contact=None, pypi=False, source=None):
+ description=None, license=None, upstream_contact=None, pypi=False, source=None,
+ dependencies=None):
"""
Create a package
@@ -392,7 +407,12 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre
$ sage --package create jupyterlab_markup --pypi --source wheel --type optional
"""
- if '-' in package_name:
+ if package_name.startswith('pypi/'):
+ package_name = 'pkg:' + package_name
+ if package_name.startswith('pkg:pypi/'):
+ pypi = True
+ package_name = package_name[len('pkg:pypi/'):].lower().replace('-', '_').replace('.', '_')
+ elif '-' in package_name:
raise ValueError('package names must not contain dashes, use underscore instead')
if pypi:
if source is None:
@@ -420,6 +440,24 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre
raise ValueError('Only platform-independent wheels can be used for wheel packages, got {0}'.format(tarball))
if not version:
version = pypi_version.version
+ if dependencies is None:
+ requires_dist = pypi_version.requires_dist
+ if requires_dist:
+ dependencies = []
+ for item in requires_dist:
+ if "extra ==" in item:
+ continue
+ try:
+ dep = dep_re.match(item).groups()[0].strip()
+ except Exception:
+ continue
+ dep = 'pkg:pypi/' + dep
+ try:
+ dep = Package(dep).name
+ except ValueError:
+ self.create(dep, pkg_type=pkg_type)
+ dep = Package(dep).name
+ dependencies.append(dep)
upstream_url = 'https://pypi.io/packages/{2}/{0:1.1}/{0}/{1}'.format(package_name, tarball, pypi_version.python_version)
if not description:
description = pypi_version.summary
@@ -444,7 +482,8 @@ def create(self, package_name, version=None, tarball=None, pkg_type=None, upstre
if description or license or upstream_contact:
creator.set_description(description, license, upstream_contact)
if pypi or source == 'pip':
- creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source)
+ creator.set_python_data_and_scripts(pypi_package_name=pypi_version.name, source=source,
+ dependencies=dependencies)
if tarball:
creator.set_tarball(tarball, upstream_url)
if upstream_url and version:
diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py
index 3ed185a9185..ff11315aa51 100644
--- a/build/sage_bootstrap/cmdline.py
+++ b/build/sage_bootstrap/cmdline.py
@@ -12,7 +12,9 @@
"""
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2015-2016 Volker Braun
+# 2020-2024 Matthias Koeppe
+# 2022 Thierry Monteil
#
# 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
@@ -270,18 +272,19 @@ def make_parser():
parser_config = subparsers.add_parser(
'config', epilog=epilog_config,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Print the configuration')
+ help='print the configuration')
parser_list = subparsers.add_parser(
'list', epilog=epilog_list,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Print a list of packages known to Sage')
+ help='print a list of packages known to Sage')
parser_list.add_argument(
- 'package_class', metavar='[package_name|:package_type:]',
+ 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]',
type=str, default=[':all-or-nothing:'], nargs='*',
- help=('package name or designator for all packages of a given type '
+ help=('package name, pkg:pypi/ followed by a distribution name, '
+ 'or designator for all packages of a given type '
'(one of :all:, :standard:, :optional:, and :experimental:); '
- 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given'))
+ 'default: :all: (or nothing when --include-dependencies or --exclude-dependencies is given)'))
parser_list.add_argument(
'--has-file', action='append', default=[], metavar='FILENAME', dest='has_files',
help=('only include packages that have this file in their metadata directory '
@@ -303,11 +306,12 @@ def make_parser():
parser_properties = subparsers.add_parser(
'properties', epilog=epilog_properties,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Print properties of given packages')
+ help='print properties of given packages')
parser_properties.add_argument(
- 'package_class', metavar='[package_name|:package_type:]',
+ 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]',
type=str, nargs='+',
- help=('package name or designator for all packages of a given type '
+ help=('package name, pkg:pypi/ followed by a distribution name, '
+ 'or designator for all packages of a given type '
'(one of :all:, :standard:, :optional:, and :experimental:)'))
parser_properties.add_argument(
'--format', type=str, default='plain',
@@ -316,7 +320,7 @@ def make_parser():
parser_dependencies = subparsers.add_parser(
'dependencies', epilog=epilog_dependencies,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Print the list of packages that are dependencies of given packages')
+ help='print the list of packages that are dependencies of given packages')
parser_dependencies.add_argument(
'package_class', metavar='[package_name|:package_type:]',
type=str, nargs='+',
@@ -341,121 +345,136 @@ def make_parser():
parser_name = subparsers.add_parser(
'name', epilog=epilog_name,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Find the package name given a tarball filename')
- parser_name.add_argument('tarball_filename', type=str, help='Tarball filename')
+ help='find the package name given a tarball filename')
+ parser_name.add_argument('tarball_filename', type=str, help='tarball filename')
parser_tarball = subparsers.add_parser(
'tarball', epilog=epilog_tarball,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Find the tarball filename given a package name')
- parser_tarball.add_argument('package_name', type=str, help='Package name')
+ help='find the tarball filename given a package name')
+ parser_tarball.add_argument('package_name', type=str, help='package name')
parser_apropos = subparsers.add_parser(
'apropos', epilog=epilog_apropos,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Find up to 5 package names that are close to the given name')
+ help='find up to 5 package names that are close to the given name')
parser_apropos.add_argument(
'incorrect_name', type=str,
- help='Fuzzy name to search for')
+ help='fuzzy name to search for')
parser_update = subparsers.add_parser(
'update', epilog=epilog_update,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Update a package. This modifies the Sage sources.')
+ help='update a package, modifying the Sage sources')
parser_update.add_argument(
- 'package_name', type=str, help='Package name')
+ 'package_name', type=str, help='package name')
parser_update.add_argument(
- 'new_version', type=str, help='New version')
+ 'new_version', type=str, help='new version')
parser_update.add_argument(
- '--url', type=str, default=None, help='Download URL')
+ '--url', type=str, default=None, help='download URL')
parser_update.add_argument(
'--commit', action="store_true",
- help='Whether to run "git commit"')
+ help='whether to run "git commit"')
parser_update_latest = subparsers.add_parser(
'update-latest', epilog=epilog_update_latest,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Update a package to the latest version. This modifies the Sage sources.')
+ help='update a package to the latest version, modifying the Sage sources')
parser_update_latest.add_argument(
- 'package_name', type=str, help='Package name (:all: for all packages)')
+ 'package_name', type=str, help='package name (:all: for all packages)')
parser_update_latest.add_argument(
'--commit', action="store_true",
- help='Whether to run "git commit"')
+ help='whether to run "git commit"')
parser_download = subparsers.add_parser(
'download', epilog=epilog_download,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Download tarball')
+ help='download tarball')
parser_download.add_argument(
- 'package_name', type=str, help='Package name or :type:')
+ 'package_class', metavar='[package_name|:package_type:]',
+ type=str, nargs='+',
+ help=('package name or designator for all packages of a given type '
+ '(one of :all:, :standard:, :optional:, and :experimental:)'))
+ parser_download.add_argument(
+ '--has-file', action='append', default=[], metavar='FILENAME', dest='has_files',
+ help=('only include packages that have this file in their metadata directory '
+ '(examples: SPKG.rst, spkg-configure.m4, distros/debian.txt, spkg-install|spkg-install.in)'))
+ parser_download.add_argument(
+ '--no-file', action='append', default=[], metavar='FILENAME', dest='no_files',
+ help=('only include packages that do not have this file in their metadata directory '
+ '(examples: huge, patches, huge|has_nonfree_dependencies)'))
+ parser_download.add_argument(
+ '--exclude', nargs='*', action='append', default=[], metavar='PACKAGE_NAME',
+ help='exclude package from list')
parser_download.add_argument(
'--allow-upstream', action="store_true",
- help='Whether to fall back to downloading from the upstream URL')
+ help='whether to fall back to downloading from the upstream URL')
parser_download.add_argument(
'--on-error', choices=['stop', 'warn'], default='stop',
- help='What to do if the tarball cannot be downloaded')
+ help='what to do if the tarball cannot be downloaded')
parser_download.add_argument(
'--no-check-certificate', action='store_true',
- help='Do not check SSL certificates for https connections')
+ help='do not check SSL certificates for https connections')
parser_upload = subparsers.add_parser(
'upload', epilog=epilog_upload,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Upload tarball to Sage mirrors')
+ help='upload tarball to Sage mirrors')
parser_upload.add_argument(
- 'package_name', type=str, help='Package name or :type:')
+ 'package_name', type=str, help='package name or :type:')
parser_fix_checksum = subparsers.add_parser(
'fix-checksum', epilog=epilog_fix_checksum,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Fix the checksum of normal packages.')
+ help='fix the checksum of normal packages')
parser_fix_checksum.add_argument(
- 'package_class', metavar='[package_name|:package_type:]',
+ 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]',
type=str, default=[':all:'], nargs='*',
- help=('package name or designator for all packages of a given type '
- '(one of :all:, :standard:, :optional:, and :experimental:); '
- 'default: :all:'))
+ help=('package name, pkg:pypi/ followed by a distribution name, '
+ 'or designator for all packages of a given type '
+ '(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)'))
parser_create = subparsers.add_parser(
'create', epilog=epilog_create,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Create or overwrite package.')
+ help='create or overwrite a package')
parser_create.add_argument(
'package_name', default=None, type=str,
- help='Package name.')
+ help='package name')
parser_create.add_argument(
- '--source', type=str, default=None, help='Package source (one of normal, wheel, script, pip); default depends on provided arguments')
+ '--source', type=str, default=None, help='package source (one of normal, wheel, script, pip); default depends on provided arguments')
parser_create.add_argument(
- '--version', type=str, default=None, help='Package version')
+ '--version', type=str, default=None, help='package version')
parser_create.add_argument(
- '--tarball', type=str, default=None, help='Tarball filename pattern, e.g. Foo-VERSION.tar.bz2')
+ '--tarball', type=str, default=None, help='tarball filename pattern, e.g. Foo-VERSION.tar.bz2')
parser_create.add_argument(
- '--type', type=str, default=None, help='Package type')
+ '--type', type=str, default=None, help='package type')
parser_create.add_argument(
- '--url', type=str, default=None, help='Download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2')
+ '--url', type=str, default=None, help='download URL pattern, e.g. http://example.org/Foo-VERSION.tar.bz2')
parser_create.add_argument(
- '--description', type=str, default=None, help='Short description of the package (for SPKG.rst)')
+ '--description', type=str, default=None, help='short description of the package (for SPKG.rst)')
parser_create.add_argument(
- '--license', type=str, default=None, help='License of the package (for SPKG.rst)')
+ '--license', type=str, default=None, help='license of the package (for SPKG.rst)')
parser_create.add_argument(
- '--upstream-contact', type=str, default=None, help='Upstream contact (for SPKG.rst)')
+ '--upstream-contact', type=str, default=None, help='upstream contact (for SPKG.rst)')
parser_create.add_argument(
'--pypi', action="store_true",
- help='Create a package for a Python package available on PyPI')
+ help='create a package for a Python package available on PyPI')
parser_clean = subparsers.add_parser(
'clean', epilog=epilog_clean,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Remove outdated source tarballs from the upstream/ directory')
+ help='remove outdated source tarballs from the upstream/ directory')
parser_metrics = subparsers.add_parser(
'metrics', epilog=epilog_metrics,
formatter_class=argparse.RawDescriptionHelpFormatter,
- help='Print metrics of given packages')
+ help='print metrics of given packages')
parser_metrics.add_argument(
- 'package_class', metavar='[package_name|:package_type:]',
+ 'package_class', metavar='[PACKAGE_NAME|pkg:pypi/DISTRIBUTION-NAME|:PACKAGE_TYPE:]',
type=str, nargs='*', default=[':all:'],
- help=('package name or designator for all packages of a given type '
+ help=('package name, pkg:pypi/ followed by a distribution name, '
+ 'or designator for all packages of a given type '
'(one of :all:, :standard:, :optional:, and :experimental:; default: :all:)'))
return parser
@@ -517,7 +536,9 @@ def run():
ssl._create_default_https_context = ssl._create_unverified_context
except ImportError:
pass
- app.download_cls(args.package_name,
+ app.download_cls(*args.package_class,
+ has_files=args.has_files, no_files=args.no_files,
+ exclude=args.exclude,
allow_upstream=args.allow_upstream,
on_error=args.on_error)
elif args.subcommand == 'create':
diff --git a/build/sage_bootstrap/creator.py b/build/sage_bootstrap/creator.py
index a738d772215..e16002f12f9 100644
--- a/build/sage_bootstrap/creator.py
+++ b/build/sage_bootstrap/creator.py
@@ -4,7 +4,8 @@
"""
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2015-2016 Volker Braun
+# 2020-2024 Matthias Koeppe
#
# 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
@@ -105,7 +106,7 @@ def _remove_files(self, files):
except OSError:
pass
- def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'):
+ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal', dependencies=None):
"""
Write the file ``dependencies`` and other files for Python packages.
@@ -121,7 +122,15 @@ def set_python_data_and_scripts(self, pypi_package_name=None, source='normal'):
if pypi_package_name is None:
pypi_package_name = self.package_name
with open(os.path.join(self.path, 'dependencies'), 'w+') as f:
- f.write(' | $(PYTHON_TOOLCHAIN) $(PYTHON)\n\n')
+ if dependencies:
+ dependencies = ' '.join(dependencies)
+ else:
+ dependencies = ''
+ if source == 'wheel':
+ dependencies_order_only = 'pip $(PYTHON)'
+ else:
+ dependencies_order_only = '$(PYTHON_TOOLCHAIN) $(PYTHON)'
+ f.write(dependencies + ' | ' + dependencies_order_only + '\n\n')
f.write('----------\nAll lines of this file are ignored except the first.\n')
if source == 'normal':
with open(os.path.join(self.path, 'spkg-install.in'), 'w+') as f:
diff --git a/build/sage_bootstrap/download/cmdline.py b/build/sage_bootstrap/download/cmdline.py
index a6afb4f92be..594cc773b38 100644
--- a/build/sage_bootstrap/download/cmdline.py
+++ b/build/sage_bootstrap/download/cmdline.py
@@ -6,7 +6,9 @@
"""
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2015-2016 Volker Braun
+# 2015 Jeroen Demeyer
+# 2020 Matthias Koeppe
#
# 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
diff --git a/build/sage_bootstrap/download/mirror_list.py b/build/sage_bootstrap/download/mirror_list.py
index 4cab19f5d64..f464d87ffbd 100644
--- a/build/sage_bootstrap/download/mirror_list.py
+++ b/build/sage_bootstrap/download/mirror_list.py
@@ -4,7 +4,9 @@
"""
#*****************************************************************************
-# Copyright (C) 2015 Volker Braun
+# Copyright (C) 2014-2016 Volker Braun
+# 2015 Jeroen Demeyer
+# 2023 Matthias Koeppe
#
# 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
diff --git a/build/sage_bootstrap/expand_class.py b/build/sage_bootstrap/expand_class.py
index c7fdb308bd3..c5bab8a313e 100644
--- a/build/sage_bootstrap/expand_class.py
+++ b/build/sage_bootstrap/expand_class.py
@@ -4,7 +4,8 @@
"""
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2016 Volker Braun
+# 2020-2024 Matthias Koeppe
#
# 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
@@ -52,12 +53,21 @@ def included_in_filter(pkg):
self._init_optional(predicate=included_in_filter)
elif package_name_or_class == ':experimental:':
self._init_experimental(predicate=included_in_filter)
+ elif any(package_name_or_class.startswith(prefix)
+ for prefix in ["pkg:", "pypi/", "generic"]):
+ self.__names.add(Package(package_name_or_class).name)
else:
if ':' in package_name_or_class:
- raise ValueError('a colon may only appear in designators of package types, '
+ raise ValueError('a colon may only appear in a PURL such as '
+ 'pkg:pypi/DISTRIBUTION-NAME '
+ 'and in designators of package types, '
'which must be one of '
':all:, :standard:, :optional:, or :experimental:'
'got {}'.format(package_name_or_class))
+ if '-' in package_name_or_class:
+ raise ValueError('dashes may only appear in a PURL such as '
+ 'pkg:pypi/DISTRIBUTION-NAME; '
+ 'SPKG names use underscores')
self.__names.add(package_name_or_class)
def include_recursive_dependencies(names, package_name):
diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py
index aea31e4b6a4..176e842e43b 100644
--- a/build/sage_bootstrap/package.py
+++ b/build/sage_bootstrap/package.py
@@ -4,7 +4,9 @@
"""
# ****************************************************************************
-# Copyright (C) 2015 Volker Braun
+# Copyright (C) 2015-2016 Volker Braun
+# 2018 Jeroen Demeyer
+# 2020-2024 Matthias Koeppe
#
# 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
@@ -25,6 +27,33 @@
class Package(object):
+ def __new__(cls, package_name):
+ if package_name.startswith("pypi/") or package_name.startswith("generic/"):
+ package_name = "pkg:" + package_name
+ if package_name.startswith("pkg:"):
+ package_name = package_name.replace('_', '-')
+ if package_name.startswith("pkg:generic/"): # fast path
+ try:
+ pkg = cls(package_name[len("pkg:generic/"):].replace('-', '_'))
+ if pkg.purl == package_name:
+ return pkg # assume unique
+ except Exception:
+ pass
+ elif package_name.startswith("pkg:pypi/"): # fast path
+ try:
+ pkg = cls(package_name[len("pkg:pypi/"):].replace('-', '_'))
+ if pkg.purl == package_name:
+ return pkg # assume unique
+ except Exception:
+ pass
+ for pkg in cls.all():
+ if pkg.purl == package_name:
+ return pkg # assume unique
+ raise ValueError('no package for PURL {0}'.format(package_name))
+ self = object.__new__(cls)
+ self.__init__(package_name)
+ return self
+
def __init__(self, package_name):
"""
Sage Package
@@ -41,12 +70,22 @@ def __init__(self, package_name):
-- ``package_name`` -- string. Name of the package. The Sage
convention is that all package names are lower case.
"""
+ if any(package_name.startswith(prefix)
+ for prefix in ["pkg:", "pypi/", "generic"]):
+ # Already initialized
+ return
+ if package_name != package_name.lower():
+ raise ValueError('package names should be lowercase, got {0}'.format(package_name))
+ if '-' in package_name:
+ raise ValueError('package names use underscores, not dashes, got {0}'.format(package_name))
+
self.__name = package_name
self.__tarball = None
self._init_checksum()
self._init_version()
self._init_type()
self._init_version_requirements()
+ self._init_requirements()
self._init_dependencies()
self._init_trees()
@@ -323,7 +362,7 @@ def source(self):
"""
Return the package source type
"""
- if self.has_file('requirements.txt'):
+ if self.__requirements is not None:
return 'pip'
if self.tarball_filename:
if self.tarball_filename.endswith('.whl'):
@@ -346,15 +385,39 @@ def trees(self):
return self.__trees
if self.__version_requirements is not None:
return 'SAGE_VENV'
- if self.has_file('requirements.txt'):
+ if self.__requirements is not None:
return 'SAGE_VENV'
return 'SAGE_LOCAL'
+ @property
+ def purl(self):
+ """
+ Return a PURL (Package URL) for the package
+
+ OUTPUT:
+
+ A string in the format ``SCHEME:TYPE/NAMESPACE/NAME``,
+ i.e., without components for version, qualifiers, and subpath.
+ See https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#package-url-specification-v10x
+ for details
+ """
+ dist = self.distribution_name
+ if dist:
+ return 'pkg:pypi/' + dist.lower().replace('_', '-')
+ return 'pkg:generic/' + self.name.replace('_', '-')
+
@property
def distribution_name(self):
"""
Return the Python distribution name or ``None`` for non-Python packages
"""
+ if self.__requirements is not None:
+ for line in self.__requirements.split('\n'):
+ line = line.strip()
+ if line.startswith('#'):
+ continue
+ for part in line.split():
+ return part
if self.__version_requirements is None:
return None
for line in self.__version_requirements.split('\n'):
@@ -420,7 +483,7 @@ def all(cls):
continue
try:
yield cls(subdir)
- except BaseException:
+ except Exception:
log.error('Failed to open %s', subdir)
raise
@@ -517,6 +580,13 @@ def _init_version_requirements(self):
except IOError:
self.__version_requirements = None
+ def _init_requirements(self):
+ try:
+ with open(os.path.join(self.path, 'requirements.txt')) as f:
+ self.__requirements = f.read().strip()
+ except IOError:
+ self.__requirements = None
+
def _init_dependencies(self):
try:
with open(os.path.join(self.path, 'dependencies')) as f:
diff --git a/build/sage_bootstrap/pypi.py b/build/sage_bootstrap/pypi.py
index e3ca4e560c7..9427e9c8808 100644
--- a/build/sage_bootstrap/pypi.py
+++ b/build/sage_bootstrap/pypi.py
@@ -5,7 +5,8 @@
# ****************************************************************************
-# Copyright (C) 2016 Volker Braun
+# Copyright (C) 2016 Volker Braun
+# 2020-2023 Matthias Koeppe
#
# 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
@@ -106,6 +107,13 @@ def summary(self):
"""
return self.json['info']['summary']
+ @property
+ def requires_dist(self):
+ """
+ Return the dependencies
+ """
+ return self.json['info']['requires_dist']
+
def update(self, package=None):
if package is None:
package = Package(self.name)
diff --git a/build/sage_bootstrap/tarball.py b/build/sage_bootstrap/tarball.py
index 441d97cb3c6..c974d8ac310 100644
--- a/build/sage_bootstrap/tarball.py
+++ b/build/sage_bootstrap/tarball.py
@@ -4,7 +4,9 @@
"""
# ****************************************************************************
-# Copyright (C) 2015 Volker Braun
+# Copyright (C) 2014-2015 Volker Braun
+# 2017 Jeroen Demeyer
+# 2020 Matthias Koeppe
#
# 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
diff --git a/build/sage_bootstrap/uninstall.py b/build/sage_bootstrap/uninstall.py
index 08ce337386d..25496246ac7 100644
--- a/build/sage_bootstrap/uninstall.py
+++ b/build/sage_bootstrap/uninstall.py
@@ -24,7 +24,9 @@
are also removed.
"""
# ****************************************************************************
-# Copyright (C) 2017 Erik M. Bray
+# Copyright (C) 2017-2018 Erik M. Bray
+# 2019 Jeroen Demeyer
+# 2021-2022 Matthias Koeppe
#
# 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
diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-conf/VERSION.txt
+++ b/pkgs/sage-conf/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-conf_conda/VERSION.txt
+++ b/pkgs/sage-conf_conda/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-conf_pypi/VERSION.txt
+++ b/pkgs/sage-conf_pypi/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sage-conf_pypi/tox.ini b/pkgs/sage-conf_pypi/tox.ini
index fadf9a9cc6c..7160d4db678 100644
--- a/pkgs/sage-conf_pypi/tox.ini
+++ b/pkgs/sage-conf_pypi/tox.ini
@@ -1,6 +1,10 @@
[tox]
envlist = py39, py310, py311, py39-user, py310-user, py311-user
+requires =
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
+ tox<4.14.1
+
[testenv:.pkg]
basepython = py311
passenv =
diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-docbuild/VERSION.txt
+++ b/pkgs/sage-docbuild/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sage-docbuild/tox.ini b/pkgs/sage-docbuild/tox.ini
index 77da5a78ede..efa222028ff 100644
--- a/pkgs/sage-docbuild/tox.ini
+++ b/pkgs/sage-docbuild/tox.ini
@@ -8,6 +8,10 @@
#
[tox]
+requires =
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
+ tox<4.14.1
+
[testenv]
deps = -rrequirements.txt
diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-setup/VERSION.txt
+++ b/pkgs/sage-setup/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sage-setup/tox.ini b/pkgs/sage-setup/tox.ini
index fd935f2e978..e8a22f03101 100644
--- a/pkgs/sage-setup/tox.ini
+++ b/pkgs/sage-setup/tox.ini
@@ -12,6 +12,10 @@
#
[tox]
+requires =
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
+ tox<4.14.1
+
[testenv]
deps = -rrequirements.txt
diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sage-sws2rst/VERSION.txt
+++ b/pkgs/sage-sws2rst/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-bliss/VERSION.txt
+++ b/pkgs/sagemath-bliss/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-categories/VERSION.txt
+++ b/pkgs/sagemath-categories/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-categories/known-test-failures.json b/pkgs/sagemath-categories/known-test-failures.json
index ddae185a7d9..80705635b7e 100644
--- a/pkgs/sagemath-categories/known-test-failures.json
+++ b/pkgs/sagemath-categories/known-test-failures.json
@@ -26,7 +26,7 @@
"ntests": 28
},
"sage.categories.affine_weyl_groups": {
- "ntests": 14
+ "ntests": 15
},
"sage.categories.algebra_ideals": {
"failed": true,
@@ -38,7 +38,7 @@
},
"sage.categories.algebras": {
"failed": true,
- "ntests": 20
+ "ntests": 25
},
"sage.categories.algebras_with_basis": {
"failed": true,
@@ -126,7 +126,7 @@
},
"sage.categories.commutative_rings": {
"failed": true,
- "ntests": 39
+ "ntests": 41
},
"sage.categories.complete_discrete_valuation": {
"failed": true,
@@ -148,15 +148,19 @@
},
"sage.categories.coxeter_groups": {
"failed": true,
- "ntests": 362
+ "ntests": 392
},
"sage.categories.cw_complexes": {
"failed": true,
"ntests": 36
},
+ "sage.categories.dedekind_domains": {
+ "failed": true,
+ "ntests": 30
+ },
"sage.categories.discrete_valuation": {
"failed": true,
- "ntests": 23
+ "ntests": 28
},
"sage.categories.distributive_magmas_and_additive_magmas": {
"failed": true,
@@ -169,9 +173,6 @@
"failed": true,
"ntests": 7
},
- "sage.categories.drinfeld_modules": {
- "ntests": 2
- },
"sage.categories.dual": {
"failed": true,
"ntests": 1
@@ -297,18 +298,18 @@
},
"sage.categories.filtered_modules_with_basis": {
"failed": true,
- "ntests": 65
+ "ntests": 74
},
"sage.categories.finite_complex_reflection_groups": {
"failed": true,
- "ntests": 178
+ "ntests": 189
},
"sage.categories.finite_coxeter_groups": {
"ntests": 6
},
"sage.categories.finite_dimensional_algebras_with_basis": {
"failed": true,
- "ntests": 87
+ "ntests": 91
},
"sage.categories.finite_dimensional_bialgebras_with_basis": {
"failed": true,
@@ -328,11 +329,11 @@
},
"sage.categories.finite_dimensional_lie_algebras_with_basis": {
"failed": true,
- "ntests": 34
+ "ntests": 73
},
"sage.categories.finite_dimensional_modules_with_basis": {
"failed": true,
- "ntests": 55
+ "ntests": 89
},
"sage.categories.finite_dimensional_nilpotent_lie_algebras_with_basis": {
"failed": true,
@@ -493,10 +494,6 @@
"failed": true,
"ntests": 13
},
- "sage.categories.inner_product_spaces": {
- "failed": true,
- "ntests": 9
- },
"sage.categories.integral_domains": {
"failed": true,
"ntests": 22
@@ -530,7 +527,7 @@
},
"sage.categories.lie_algebras": {
"failed": true,
- "ntests": 125
+ "ntests": 135
},
"sage.categories.lie_algebras_with_basis": {
"ntests": 19
@@ -586,7 +583,7 @@
},
"sage.categories.modules_with_basis": {
"failed": true,
- "ntests": 221
+ "ntests": 240
},
"sage.categories.monoid_algebras": {
"failed": true,
@@ -598,7 +595,11 @@
},
"sage.categories.morphism": {
"failed": true,
- "ntests": 99
+ "ntests": 126
+ },
+ "sage.categories.noetherian_rings": {
+ "failed": true,
+ "ntests": 19
},
"sage.categories.number_fields": {
"failed": true,
@@ -668,14 +669,14 @@
},
"sage.categories.rings": {
"failed": true,
- "ntests": 141
+ "ntests": 146
},
"sage.categories.rngs": {
"ntests": 6
},
"sage.categories.schemes": {
"failed": true,
- "ntests": 23
+ "ntests": 41
},
"sage.categories.semigroups": {
"failed": true,
@@ -711,7 +712,7 @@
},
"sage.categories.simplicial_sets": {
"failed": true,
- "ntests": 57
+ "ntests": 98
},
"sage.categories.subobjects": {
"ntests": 2
@@ -765,7 +766,7 @@
},
"sage.categories.unital_algebras": {
"failed": true,
- "ntests": 39
+ "ntests": 37
},
"sage.categories.vector_spaces": {
"failed": true,
@@ -783,7 +784,7 @@
},
"sage.cpython.debug": {
"failed": true,
- "ntests": 14
+ "ntests": 13
},
"sage.cpython.dict_del_by_value": {
"failed": true,
@@ -808,30 +809,28 @@
},
"sage.doctest.control": {
"failed": true,
- "ntests": 0
+ "ntests": 230
},
"sage.doctest.external": {
"ntests": 42
},
"sage.doctest.fixtures": {
- "failed": true,
"ntests": 59
},
"sage.doctest.forker": {
"failed": true,
- "ntests": 433
+ "ntests": 413
},
"sage.doctest.parsing": {
"failed": true,
- "ntests": 321
+ "ntests": 313
},
"sage.doctest.reporting": {
- "failed": true,
- "ntests": 124
+ "ntests": 115
},
"sage.doctest.sources": {
"failed": true,
- "ntests": 378
+ "ntests": 343
},
"sage.doctest.test": {
"failed": true,
@@ -846,7 +845,7 @@
"ntests": 41
},
"sage.features": {
- "ntests": 145
+ "ntests": 143
},
"sage.features.all": {
"ntests": 14
@@ -865,17 +864,23 @@
},
"sage.features.databases": {
"failed": true,
- "ntests": 26
+ "ntests": 38
},
"sage.features.dvipng": {
"ntests": 4
},
+ "sage.features.ecm": {
+ "ntests": 4
+ },
"sage.features.ffmpeg": {
"ntests": 4
},
"sage.features.four_ti_2": {
"ntests": 6
},
+ "sage.features.fricas": {
+ "ntests": 6
+ },
"sage.features.gap": {
"ntests": 6
},
@@ -901,7 +906,11 @@
"sage.features.internet": {
"ntests": 5
},
+ "sage.features.jmol": {
+ "ntests": 4
+ },
"sage.features.join_feature": {
+ "failed": true,
"ntests": 25
},
"sage.features.kenzo": {
@@ -960,7 +969,7 @@
},
"sage.features.sagemath": {
"failed": true,
- "ntests": 147
+ "ntests": 151
},
"sage.features.singular": {
"ntests": 4
@@ -968,9 +977,16 @@
"sage.features.sphinx": {
"ntests": 4
},
+ "sage.features.symengine_py": {
+ "ntests": 4
+ },
"sage.features.tdlib": {
"ntests": 2
},
+ "sage.features.threejs": {
+ "failed": true,
+ "ntests": 6
+ },
"sage.misc.abstract_method": {
"failed": true,
"ntests": 33
@@ -1001,7 +1017,7 @@
},
"sage.misc.decorators": {
"failed": true,
- "ntests": 126
+ "ntests": 125
},
"sage.misc.fast_methods": {
"failed": true,
@@ -1059,7 +1075,7 @@
},
"sage.misc.package_dir": {
"failed": true,
- "ntests": 29
+ "ntests": 35
},
"sage.misc.persist": {
"failed": true,
@@ -1091,7 +1107,7 @@
},
"sage.misc.sageinspect": {
"failed": true,
- "ntests": 329
+ "ntests": 322
},
"sage.misc.superseded": {
"failed": true,
@@ -1157,7 +1173,7 @@
},
"sage.repl.interpreter": {
"failed": true,
- "ntests": 118
+ "ntests": 114
},
"sage.repl.ipython_extension": {
"failed": true,
@@ -1165,13 +1181,14 @@
},
"sage.repl.ipython_kernel.install": {
"failed": true,
- "ntests": 40
+ "ntests": 35
},
"sage.repl.ipython_kernel.interact": {
"failed": true,
"ntests": 42
},
"sage.repl.ipython_kernel.kernel": {
+ "failed": true,
"ntests": 12
},
"sage.repl.ipython_kernel.widgets": {
@@ -1188,7 +1205,7 @@
},
"sage.repl.load": {
"failed": true,
- "ntests": 42
+ "ntests": 38
},
"sage.repl.preparse": {
"failed": true,
@@ -1206,7 +1223,7 @@
},
"sage.repl.rich_output.backend_ipython": {
"failed": true,
- "ntests": 78
+ "ntests": 75
},
"sage.repl.rich_output.buffer": {
"failed": true,
@@ -1214,7 +1231,7 @@
},
"sage.repl.rich_output.display_manager": {
"failed": true,
- "ntests": 88
+ "ntests": 86
},
"sage.repl.rich_output.output_basic": {
"ntests": 47
@@ -1251,7 +1268,7 @@
},
"sage.rings.ring": {
"failed": true,
- "ntests": 337
+ "ntests": 298
},
"sage.sets.pythonclass": {
"failed": true,
@@ -1263,7 +1280,7 @@
},
"sage.structure.coerce": {
"failed": true,
- "ntests": 314
+ "ntests": 310
},
"sage.structure.coerce_actions": {
"failed": true,
@@ -1298,7 +1315,7 @@
},
"sage.structure.factorization": {
"failed": true,
- "ntests": 203
+ "ntests": 200
},
"sage.structure.factorization_integer": {
"failed": true,
@@ -1315,9 +1332,6 @@
"sage.structure.global_options": {
"ntests": 145
},
- "sage.structure.graphics_file": {
- "ntests": 8
- },
"sage.structure.indexed_generators": {
"failed": true,
"ntests": 90
@@ -1345,7 +1359,7 @@
},
"sage.structure.parent": {
"failed": true,
- "ntests": 300
+ "ntests": 303
},
"sage.structure.parent_gens": {
"failed": true,
@@ -1418,4 +1432,4 @@
"sage.typeset.unicode_characters": {
"ntests": 27
}
-}
\ No newline at end of file
+}
diff --git a/pkgs/sagemath-categories/requirements-editable.txt.m4 b/pkgs/sagemath-categories/requirements-editable.txt.m4
new file mode 100644
index 00000000000..1c0abbe40ae
--- /dev/null
+++ b/pkgs/sagemath-categories/requirements-editable.txt.m4
@@ -0,0 +1,7 @@
+include(`sage_spkg_versions.m4')dnl
+dnl Same as setup.cfg.m4 install_requires; FIXME: should pin to built wheels.
+SPKG_INSTALL_REQUIRES_gmpy2
+SPKG_INSTALL_REQUIRES_cysignals
+SPKG_INSTALL_REQUIRES_memory_allocator
+-e ../sagemath-environment
+-e ../sagemath-objects
diff --git a/pkgs/sagemath-categories/tox.ini b/pkgs/sagemath-categories/tox.ini
index b4bb49d132a..841209e88f1 100644
--- a/pkgs/sagemath-categories/tox.ini
+++ b/pkgs/sagemath-categories/tox.ini
@@ -13,7 +13,9 @@ envlist =
requires =
# Auto-provision a modern tox.
# [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
tox>=4.2
+ tox<4.14.1
[pkgenv]
# Environment in which to build the sdist.
@@ -49,6 +51,11 @@ passenv = {[pkgenv]passenv}
setenv = {[pkgenv]setenv}
# Sage scripts such as sage-runtests like to use $HOME/.sage
HOME={envdir}
+ # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL
+ SAGE_DOC=/doesnotexist
+ KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json
+ # See src/bin/sage-env
+ PYDEVD_DISABLE_FILE_VALIDATION=1
allowlist_externals =
bash
@@ -61,7 +68,7 @@ commands =
{envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); from sage.categories.all import *; SimplicialComplexes(); FunctionFields()'
bash -c 'cd $(python -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") \
- && sage-runtests -p --initial --environment=sage.all__sagemath_categories --probe all --baseline-stats-path={toxinidir}/known-test-failures.json --optional=sage --installed'
+ && sage-runtests -p --force-lib --initial --environment=sage.all__sagemath_categories --probe all --baseline-stats-path=$KNOWN_TEST_FAILURES --optional=sage --installed'
[testenv:.tox]
# Allow access to PyPI for auto-provisioning a suitable tox version
@@ -84,6 +91,11 @@ setenv = {[pkgenv]setenv}
basepython = {env:SAGE_VENV}/bin/python3
+[testenv:.pkg-sagepython-sagewheels-nopypi-editable]
+config_settings_build_editable =
+ editable_mode = strict
+
+
[testenv:sagepython]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
@@ -92,10 +104,6 @@ package_env = .pkg-sagepython
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython-sagewheels-nopypi
-[testenv:sagepython-sagewheels-nopypi-norequirements]
-basepython = {env:SAGE_VENV}/bin/python3
-package_env = .pkg-sagepython-sagewheels-nopypi
-
[testenv:sagepython-sagewheels]
basepython = {env:SAGE_VENV}/bin/python
package_env = .pkg-sagepython
@@ -103,3 +111,16 @@ package_env = .pkg-sagepython
[testenv:sagepython-norequirements]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
+
+
+[testenv:sagepython-sagewheels-nopypi-norequirements]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi
+
+[testenv:sagepython-sagewheels-nopypi-editable]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi-editable
+package = editable
+deps = -r requirements-editable.txt
+config_settings_build_editable =
+ editable_mode = strict
diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-coxeter3/VERSION.txt
+++ b/pkgs/sagemath-coxeter3/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-environment/VERSION.txt
+++ b/pkgs/sagemath-environment/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-environment/requirements-editable.txt.m4 b/pkgs/sagemath-environment/requirements-editable.txt.m4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/pkgs/sagemath-environment/tox.ini b/pkgs/sagemath-environment/tox.ini
index 6bf1f2a6ebb..5d741bd0b48 100644
--- a/pkgs/sagemath-environment/tox.ini
+++ b/pkgs/sagemath-environment/tox.ini
@@ -13,7 +13,9 @@ envlist =
requires =
# Auto-provision a modern tox.
# [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
tox>=4.2
+ tox<4.14.1
[pkgenv]
# Environment in which to build the sdist.
@@ -49,6 +51,9 @@ passenv = {[pkgenv]passenv}
setenv = {[pkgenv]setenv}
# Sage scripts such as sage-runtests like to use $HOME/.sage
HOME={envdir}
+ # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL
+ SAGE_DOC=/doesnotexist
+ KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json
allowlist_externals =
bash
@@ -78,6 +83,11 @@ setenv = {[pkgenv]setenv}
basepython = {env:SAGE_VENV}/bin/python3
+[testenv:.pkg-sagepython-sagewheels-nopypi-editable]
+config_settings_build_editable =
+ editable_mode = strict
+
+
[testenv:sagepython]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
@@ -98,3 +108,11 @@ package_env = .pkg-sagepython
[testenv:sagepython-sagewheels-nopypi-norequirements]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython-sagewheels-nopypi
+
+[testenv:sagepython-sagewheels-nopypi-editable]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi-editable
+package = editable
+deps = -r requirements-editable.txt
+config_settings_build_editable =
+ editable_mode = strict
diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-mcqd/VERSION.txt
+++ b/pkgs/sagemath-mcqd/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-meataxe/VERSION.txt
+++ b/pkgs/sagemath-meataxe/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-objects/VERSION.txt
+++ b/pkgs/sagemath-objects/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-objects/requirements-editable.txt.m4 b/pkgs/sagemath-objects/requirements-editable.txt.m4
new file mode 100644
index 00000000000..df9b22b43aa
--- /dev/null
+++ b/pkgs/sagemath-objects/requirements-editable.txt.m4
@@ -0,0 +1,4 @@
+include(`sage_spkg_versions.m4')dnl
+dnl Same as setup.cfg.m4 install_requires; FIXME: should pin to built wheels.
+SPKG_INSTALL_REQUIRES_gmpy2
+SPKG_INSTALL_REQUIRES_cysignals
diff --git a/pkgs/sagemath-objects/tox.ini b/pkgs/sagemath-objects/tox.ini
index a8f5a6d6a76..a7b91f55990 100644
--- a/pkgs/sagemath-objects/tox.ini
+++ b/pkgs/sagemath-objects/tox.ini
@@ -13,7 +13,9 @@ envlist =
requires =
# Auto-provision a modern tox.
# [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
tox>=4.2
+ tox<4.14.1
[pkgenv]
# Environment in which to build the sdist.
@@ -49,6 +51,9 @@ passenv = {[pkgenv]passenv}
setenv = {[pkgenv]setenv}
# Sage scripts such as sage-runtests like to use $HOME/.sage
HOME={envdir}
+ # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL
+ SAGE_DOC=/doesnotexist
+ KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json
allowlist_externals =
bash
@@ -82,6 +87,11 @@ setenv = {[pkgenv]setenv}
basepython = {env:SAGE_VENV}/bin/python3
+[testenv:.pkg-sagepython-sagewheels-nopypi-editable]
+config_settings_build_editable =
+ editable_mode = strict
+
+
[testenv:sagepython]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
@@ -102,3 +112,11 @@ package_env = .pkg-sagepython
[testenv:sagepython-sagewheels-nopypi-norequirements]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython-sagewheels-nopypi
+
+[testenv:sagepython-sagewheels-nopypi-editable]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi-editable
+package = editable
+deps = -r requirements-editable.txt
+config_settings_build_editable =
+ editable_mode = strict
diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-repl/VERSION.txt
+++ b/pkgs/sagemath-repl/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-repl/pyproject.toml.m4 b/pkgs/sagemath-repl/pyproject.toml.m4
index 2d2f13008be..459a599f1fb 100644
--- a/pkgs/sagemath-repl/pyproject.toml.m4
+++ b/pkgs/sagemath-repl/pyproject.toml.m4
@@ -13,8 +13,10 @@ description = "Sage: Open Source Mathematics Software: IPython kernel, Sage prep
dependencies = [
SPKG_INSTALL_REQUIRES_sagemath_objects
SPKG_INSTALL_REQUIRES_sagemath_environment
+ SPKG_INSTALL_REQUIRES_ipykernel
SPKG_INSTALL_REQUIRES_ipython
SPKG_INSTALL_REQUIRES_ipywidgets
+ SPKG_INSTALL_REQUIRES_jupyter_client
]
dynamic = ["version"]
include(`pyproject_toml_metadata.m4')dnl'
diff --git a/pkgs/sagemath-repl/requirements-editable.txt.m4 b/pkgs/sagemath-repl/requirements-editable.txt.m4
new file mode 100644
index 00000000000..fa89ade7621
--- /dev/null
+++ b/pkgs/sagemath-repl/requirements-editable.txt.m4
@@ -0,0 +1,11 @@
+include(`sage_spkg_versions.m4')dnl
+dnl Same as setup.cfg.m4 install_requires (+ their install-requires)
+dnl FIXME: should pin to built wheels.
+SPKG_INSTALL_REQUIRES_gmpy2
+SPKG_INSTALL_REQUIRES_cysignals
+SPKG_INSTALL_REQUIRES_memory_allocator
+SPKG_INSTALL_REQUIRES_ipython
+SPKG_INSTALL_REQUIRES_ipywidgets
+dnl To be added when ready for editable:
+-e ../sagemath-environment
+-e ../sagemath-objects
diff --git a/pkgs/sagemath-repl/tox.ini b/pkgs/sagemath-repl/tox.ini
index 679153a2947..d7b557761ac 100644
--- a/pkgs/sagemath-repl/tox.ini
+++ b/pkgs/sagemath-repl/tox.ini
@@ -13,7 +13,9 @@ envlist =
requires =
# Auto-provision a modern tox.
# [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
tox>=4.2
+ tox<4.14.1
[pkgenv]
# Environment in which to build the sdist.
@@ -49,6 +51,11 @@ passenv = {[pkgenv]passenv}
setenv = {[pkgenv]setenv}
# Sage scripts such as sage-runtests like to use $HOME/.sage
HOME={envdir}
+ # Stop 'sage -t --installed' from picking up doc installed in SAGE_LOCAL
+ SAGE_DOC=/doesnotexist
+ KNOWN_TEST_FAILURES={toxinidir}/known-test-failures.json
+ # See src/bin/sage-env
+ PYDEVD_DISABLE_FILE_VALIDATION=1
allowlist_externals =
bash
@@ -57,7 +64,7 @@ commands =
# Beware of the treacherous non-src layout. "./sage/" shadows the installed sage package.
{envpython} -c 'import sys; "" in sys.path and sys.path.remove(""); import sage.repl.all; import sage.doctest.all'
- bash -c 'cd $({envpython} -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") && sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path={toxinidir}/known-test-failures.json --initial --optional=sage sage/repl sage/doctest sage/misc/sage_input.py sage/misc/sage_eval.py'
+ bash -c 'cd $({envpython} -c "import sys; \"\" in sys.path and sys.path.remove(\"\"); from sage.env import SAGE_LIB; print(SAGE_LIB)") && sage-runtests -p --environment=sage.all__sagemath_repl --baseline-stats-path=$KNOWN_TEST_FAILURES --initial --optional=sage sage/repl sage/doctest sage/misc/sage_input.py sage/misc/sage_eval.py'
[testenv:.tox]
# Allow access to PyPI for auto-provisioning a suitable tox version
@@ -80,6 +87,11 @@ setenv = {[pkgenv]setenv}
basepython = {env:SAGE_VENV}/bin/python3
+[testenv:.pkg-sagepython-sagewheels-nopypi-editable]
+config_settings_build_editable =
+ editable_mode = strict
+
+
[testenv:sagepython]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
@@ -88,10 +100,6 @@ package_env = .pkg-sagepython
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython-sagewheels-nopypi
-[testenv:sagepython-sagewheels-nopypi-norequirements]
-basepython = {env:SAGE_VENV}/bin/python3
-package_env = .pkg-sagepython-sagewheels-nopypi
-
[testenv:sagepython-sagewheels]
basepython = {env:SAGE_VENV}/bin/python
package_env = .pkg-sagepython
@@ -99,3 +107,16 @@ package_env = .pkg-sagepython
[testenv:sagepython-norequirements]
basepython = {env:SAGE_VENV}/bin/python3
package_env = .pkg-sagepython
+
+
+[testenv:sagepython-sagewheels-nopypi-norequirements]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi
+
+[testenv:sagepython-sagewheels-nopypi-editable]
+basepython = {env:SAGE_VENV}/bin/python3
+package_env = .pkg-sagepython-sagewheels-nopypi-editable
+package = editable
+deps = -r requirements-editable.txt
+config_settings_build_editable =
+ editable_mode = strict
diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-sirocco/VERSION.txt
+++ b/pkgs/sagemath-sirocco/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/pkgs/sagemath-standard/pyproject.toml.m4 b/pkgs/sagemath-standard/pyproject.toml.m4
deleted file mode 120000
index 25dbae84866..00000000000
--- a/pkgs/sagemath-standard/pyproject.toml.m4
+++ /dev/null
@@ -1 +0,0 @@
-../../src/pyproject.toml.m4
\ No newline at end of file
diff --git a/pkgs/sagemath-standard/tox.ini b/pkgs/sagemath-standard/tox.ini
index 6aae1ef1bfa..c197f49d9be 100644
--- a/pkgs/sagemath-standard/tox.ini
+++ b/pkgs/sagemath-standard/tox.ini
@@ -62,7 +62,9 @@ envlist =
requires =
# Auto-provision a modern tox.
# [pkgenv] added in 4.2 - https://tox.wiki/en/latest/upgrading.html#packaging-configuration-and-inheritance
+ # Because of https://github.com/tox-dev/tox/issues/3238, need <4.14.1
tox>=4.2
+ tox<4.14.1
[pkgenv]
# Environment in which to build the sdist.
diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/pkgs/sagemath-tdlib/VERSION.txt
+++ b/pkgs/sagemath-tdlib/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/src/VERSION.txt b/src/VERSION.txt
index 957ecf95b0a..54ab6979ef2 100644
--- a/src/VERSION.txt
+++ b/src/VERSION.txt
@@ -1 +1 @@
-10.4.beta4
+10.4.beta6
diff --git a/src/bin/sage-notebook b/src/bin/sage-notebook
index 8f7687adcc5..bd898b2ae54 100755
--- a/src/bin/sage-notebook
+++ b/src/bin/sage-notebook
@@ -7,6 +7,8 @@ import ast
import argparse
import logging
import textwrap
+from contextlib import contextmanager
+
logging.basicConfig()
logger = logging.getLogger()
@@ -19,7 +21,6 @@ _system_jupyter_url = "https://doc.sagemath.org/html/en/installation/launching.h
class NotebookJupyter():
def print_banner(self):
- banner()
print('Please wait while the Sage Jupyter Notebook server starts...')
@classmethod
@@ -50,7 +51,6 @@ class NotebookJupyter():
class NotebookJupyterlab():
def print_banner(self):
- banner()
print('Please wait while the Jupyterlab server starts...')
@classmethod
@@ -77,7 +77,6 @@ class NotebookJupyterlab():
class SageNBExport(NotebookJupyter):
def print_banner(self):
- banner()
print('Please wait while the SageNB export server starts...')
@classmethod
@@ -131,7 +130,6 @@ EXAMPLES:
"""
-
notebook_launcher = {
'default': NotebookJupyter, # change this to change the default
'ipython': NotebookJupyter,
@@ -140,7 +138,6 @@ notebook_launcher = {
'export': SageNBExport,
}
-
notebook_names = ', '.join(notebook_launcher.keys())
@@ -187,6 +184,32 @@ def trac_23428_browser_workaround():
os.environ['BROWSER'] = 'open'
+@contextmanager
+def sage_doc_server():
+ from functools import partial
+ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
+ from threading import Thread
+ from sage.env import SAGE_DOC, SAGE_DOC_LOCAL_PORT as port
+
+ server = ThreadingHTTPServer(('127.0.0.1', int(port)),
+ partial(SimpleHTTPRequestHandler, directory=SAGE_DOC))
+
+ if port == '0':
+ port = str(server.server_address[1])
+ os.environ['SAGE_DOC_LOCAL_PORT'] = port
+
+ server_thread = Thread(target=server.serve_forever, name="sage_doc_server")
+ server_thread.start()
+ print(f'Sage doc server started running at http://127.0.0.1:{port}')
+
+ try:
+ yield
+ finally:
+ server.shutdown()
+ server_thread.join()
+ print(f'Sage doc server stopped runnning at http://127.0.0.1:{port}')
+
+
if __name__ == '__main__':
parser = make_parser()
args, unknown = parser.parse_known_args(sys.argv[1:])
@@ -227,4 +250,19 @@ if __name__ == '__main__':
launcher.print_help()
sys.exit(0)
- launcher(unknown)
+ banner()
+
+ # Start a Sage doc server if the Sage documentation is available locally.
+ # See the corresponding code in src/sage/repl/ipython_kernel/kernel.py.
+
+ from sage.env import SAGE_DOC_SERVER_URL
+ from sage.features.sagemath import sagemath_doc_html
+
+ if SAGE_DOC_SERVER_URL:
+ print(f'Sage doc server is running at {SAGE_DOC_SERVER_URL}')
+ launcher(unknown)
+ elif sagemath_doc_html().is_present():
+ with sage_doc_server():
+ launcher(unknown)
+ else:
+ launcher(unknown)
diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh
index c2aa055eac9..bab5675baae 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.4.beta4'
-SAGE_RELEASE_DATE='2024-04-27'
-SAGE_VERSION_BANNER='SageMath version 10.4.beta4, Release Date: 2024-04-27'
+SAGE_VERSION='10.4.beta6'
+SAGE_RELEASE_DATE='2024-05-12'
+SAGE_VERSION_BANNER='SageMath version 10.4.beta6, Release Date: 2024-05-12'
diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst
index c586b030ed8..8522fca3b9f 100644
--- a/src/doc/en/developer/coding_basics.rst
+++ b/src/doc/en/developer/coding_basics.rst
@@ -1303,11 +1303,11 @@ framework. Here is a comprehensive list:
that is, commas, hyphens, semicolons, ..., after the
first word ends the list of packages. Hyphens or colons between the
word ``optional`` and the first package name are allowed. Therefore,
- you should not write ``# optional - depends on package CHomP`` but simply
- ``# optional - CHomP``.
+ you should not write ``# optional - depends on package bliss`` but simply
+ ``# optional - bliss``.
- Optional tags are case-insensitive, so you could also write ``# optional -
- chOMP``.
+ Bliss``.
If ``# optional`` or ``# needs`` is placed right after the ``sage:`` prompt,
it is a block-scoped tag, which applies to all doctest lines until
diff --git a/src/doc/en/developer/coding_in_cython.rst b/src/doc/en/developer/coding_in_cython.rst
index 829b8234e7b..d0c7b0d521f 100644
--- a/src/doc/en/developer/coding_in_cython.rst
+++ b/src/doc/en/developer/coding_in_cython.rst
@@ -191,3 +191,23 @@ original object. As an example, the following code snippet is the
.. _python pickling documentation: http://docs.python.org/library/pickle.html#pickle-protocol
+Deprecation
+===========
+
+When making a **backward-incompatible** modification in Sage, the old code should
+keep working and display a message indicating how it should be updated/written
+in the future. We call this a *deprecation*.
+
+.. NOTE::
+
+ Deprecated code can only be removed one year after the first
+ stable release in which it appeared.
+
+Each deprecation warning contains the number of the GitHub PR that defines
+it. We use 666 in the example below.
+
+.. CODE-BLOCK:: cython
+
+ from sage.misc.superseded import deprecation_cython
+ deprecation_cython(666, "Do not use your computer to compute 1+1. Use your brain.")
+
diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst
index 37552e5bcfe..82dda02f209 100644
--- a/src/doc/en/developer/coding_in_python.rst
+++ b/src/doc/en/developer/coding_in_python.rst
@@ -748,6 +748,13 @@ documentation for more information on its behaviour and optional arguments.
from sage.misc.superseded import deprecation
deprecation(666, "Do not use your computer to compute 1+1. Use your brain.")
+Note that these decorators only work for (pure) Python. There is no implementation
+of decorators in Cython. Hence, when in need to rename a keyword/function/method/...
+in a Cython (.pyx) file and/or to deprecate something, forget about decorators and
+just use :func:`~sage.misc.superseded.deprecation_cython` instead. The usage of
+:func:`~sage.misc.superseded.deprecation_cython` is exactly the same as
+:func:`~sage.misc.superseded.deprecation`.
+
Experimental/unstable code
--------------------------
diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst
index 679d12f8be7..6a778c3a71a 100644
--- a/src/doc/en/developer/packaging.rst
+++ b/src/doc/en/developer/packaging.rst
@@ -14,7 +14,7 @@ The installation of packages is done through a bash script located in
:sage_root:`build/bin/sage-spkg`. This script is typically invoked by
giving the command::
- [alice@localhost sage]$ sage -i ...
+ [alice@localhost sage]$ ./sage -i ...
options can be:
@@ -640,6 +640,8 @@ and upper bounds). The constraints are in the format of the
`_
or `setup.py
`_.
+An exception are build time dependencies of Sage library, which should instead
+be declared in the ``requires`` block of ``pyproject.toml``.
Sage uses these version constraints for two purposes:
@@ -1047,10 +1049,10 @@ Creating packages
Assuming that you have downloaded
``$SAGE_ROOT/upstream/FoO-1.3.tar.gz``, you can use::
- [alice@localhost sage]$ sage --package create foo \
- --version 1.3 \
- --tarball FoO-VERSION.tar.gz \
- --type experimental
+ [alice@localhost sage]$ ./sage --package create foo \
+ --version 1.3 \
+ --tarball FoO-VERSION.tar.gz \
+ --type experimental
to create ``$SAGE_ROOT/build/pkgs/foo/package-version.txt``,
``checksums.ini``, and ``type`` in one step.
@@ -1059,36 +1061,47 @@ You can skip the manual downloading of the upstream tarball by using
the additional argument ``--upstream-url``. This command will also
set the ``upstream_url`` field in ``checksums.ini`` described above.
-For Python packages available from PyPI, you can use::
+For Python packages available from PyPI, use a PURL (Package URL,
+see `PEP 725 `_)::
- [alice@localhost sage]$ sage --package create scikit_spatial --pypi \
- --type optional
+ [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \
+ --type optional
-This automatically downloads the most recent version from PyPI and also
-obtains most of the necessary information by querying PyPI. In particular,
-the ``SPKG.rst`` file is created as a copy of the package's README file.
+An equivalent command uses the SPKG name of the new package::
+ [alice@localhost sage]$ ./sage --package create scikit_spatial --pypi \
+ --type optional
-The ``dependencies`` file may need editing (watch out for warnings regarding
-``--no-deps`` that Sage issues during installation of the package!).
+Either of these two commands automatically downloads the most recent version
+from PyPI and also obtains most of the necessary information by querying PyPI.
+In particular, the ``SPKG.rst`` file is created as a copy of the package's
+README file.
+
+By default, when the package is available as a platform-independent
+wheel, the ``sage --package`` creates a ``wheel`` package. In this case,
+the ``dependencies`` file is automatically generated from the information
+on PyPI, but may still need some manual editing.
+
+For ``normal`` and ``pip`` packages, the ``dependencies`` file is initialized
+to the bare minimum and will need manual editing. (Watch out for warnings
+regarding ``--no-deps`` that Sage issues during installation of the package!)
Also you may want to set lower and upper bounds for acceptable package versions
in the file ``version_requirements.txt``. (Make sure that the version in
``package-version.txt`` falls within this acceptable version range!)
-By default, when the package is available as a platform-independent
-wheel, the ``sage --package`` creates a wheel package. To create a normal package
-instead (for example, when the package requires patching), you can use::
+To create a ``normal`` package instead of a ``wheel`` package (for example, when the
+package requires patching), you can use::
- [alice@localhost sage]$ sage --package create scikit_spatial --pypi \
- --source normal \
- --type optional
+ [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \
+ --source normal \
+ --type optional
-To create a pip package rather than a normal or wheel package, you can use::
+To create a ``pip`` package rather than a ``normal`` or ``wheel`` package, you can use::
- [alice@localhost sage]$ sage --package create scikit_spatial --pypi \
- --source pip \
- --type optional
+ [alice@localhost sage]$ ./sage --package create pkg:pypi/scikit-spatial \
+ --source pip \
+ --type optional
When the package already exists, ``sage --package create`` overwrites it.
@@ -1099,14 +1112,14 @@ Updating packages to a new version
A package that has the ``upstream_url`` information can be updated by
simply typing::
- [alice@localhost sage]$ sage --package update numpy 3.14.59
+ [alice@localhost sage]$ ./sage --package update openblas 0.3.79
which will automatically download the archive and update the
-information in ``build/pkgs/numpy/``.
+information in ``build/pkgs/openblas/``.
For Python packages available from PyPI, there is another shortcut::
- [alice@localhost sage]$ sage --package update-latest matplotlib
+ [alice@localhost sage]$ ./sage --package update-latest pkg:pypi/matplotlib
Updating matplotlib: 3.3.0 -> 3.3.1
Downloading tarball to ...matplotlib-3.3.1.tar.bz2
[...............................................................]
@@ -1120,10 +1133,10 @@ version range!
If you pass the switch ``--commit``, the script will run ``git commit``
for you.
-If you prefer to make update a package ``foo`` by making manual
+If you prefer to update a package ``foo`` by making manual
changes to the files in ``build/pkgs/foo``, you will need to run::
- [alice@localhost sage]$ sage --package fix-checksum foo
+ [alice@localhost sage]$ ./sage --package fix-checksum foo
which will modify the ``checksums.ini`` file with the correct
checksums.
@@ -1136,7 +1149,7 @@ The command ``sage --package metrics`` computes machine-readable
aggregated metrics for all packages in the Sage distribution or a
given list of packages::
- [alice@localhost sage]$ sage --package metrics
+ [alice@localhost sage]$ ./sage --package metrics
has_file_distros_arch_txt=181
has_file_distros_conda_txt=289
has_file_distros_debian_txt=172
@@ -1210,20 +1223,20 @@ Sage (``FoO-1.3.tar.gz`` in the example of section
Now you can install the package using::
- [alice@localhost sage]$ sage -i package_name
+ [alice@localhost sage]$ ./sage -i package_name
or::
- [alice@localhost sage]$ sage -f package_name
+ [alice@localhost sage]$ ./sage -f package_name
to force a reinstallation. If your package contains a ``spkg-check``
script (see :ref:`section-spkg-check`) it can be run with::
- [alice@localhost sage]$ sage -i -c package_name
+ [alice@localhost sage]$ ./sage -i -c package_name
or::
- [alice@localhost sage]$ sage -f -c package_name
+ [alice@localhost sage]$ ./sage -f -c package_name
If all went fine, open a PR with the code under
:sage_root:`build/pkgs`.
diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst
index c7925b3392a..55cefd0bf60 100644
--- a/src/doc/en/developer/portability_testing.rst
+++ b/src/doc/en/developer/portability_testing.rst
@@ -349,7 +349,7 @@ Generating dockerfiles
Sage also provides a script for generating a ``Dockerfile``, which is
a recipe for automatically building a new image::
- [mkoeppe@sage sage]$ build/bin/write-dockerfile.sh debian ":standard: :optional:" > Dockerfile
+ [mkoeppe@sage sage]$ .ci/write-dockerfile.sh debian ":standard: :optional:" > Dockerfile
(The second argument is passed to ``sage -package list`` to find packages for the listed package types.)
@@ -360,7 +360,7 @@ new Docker image. Let us take a quick look at the generated file;
this is slightly simplified::
[mkoeppe@sage sage]$ cat Dockerfile
- # Automatically generated by SAGE_ROOT/build/bin/write-dockerfile.sh
+ # Automatically generated by SAGE_ROOT/.ci/write-dockerfile.sh
# the :comments: separate the generated file into sections
# to simplify writing scripts that customize this file
...
diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst
index 354df395e99..f5457ddca0a 100644
--- a/src/doc/en/faq/faq-general.rst
+++ b/src/doc/en/faq/faq-general.rst
@@ -334,8 +334,10 @@ Here is a BibTeX entry for Sage:
}
Adjust version/year as needed. You might also like to use DOI for Sage,
-as the note entry in the above record, or directly as DOI record.
-
+as the ``note`` entry in the above record, or directly as DOI record.
+The DOI :doi:`10.5281/zenodo.8042260` represents all versions of Sage;
+clicking on it will show the DOI for the latest Sage version
+(at the time of writing, 10.3, see :doi:`10.5281/zenodo.10841614`).
If you happen to use the Sage interface to PARI, GAP or Singular,
you should definitely reference them as well. Likewise, if you use
@@ -403,4 +405,4 @@ What are DOI records for Sage?
e.g. see `record for Sage 9.5 `_.
The corresponding :doi:`10.5281/zenodo.6259615`.
-There is also DOI for the latest version, :doi:`10.5281/zenodo.593563`.
+There is also DOI for the latest version, :doi:`10.5281/zenodo.8042260`.
diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst
index 583b331ea28..fbe7626b4e5 100644
--- a/src/doc/en/reference/curves/index.rst
+++ b/src/doc/en/reference/curves/index.rst
@@ -13,6 +13,7 @@ Curves
sage/schemes/curves/constructor
sage/schemes/curves/curve
sage/schemes/curves/affine_curve
+ sage/schemes/curves/plane_curve_arrangement
sage/schemes/curves/projective_curve
sage/schemes/curves/point
sage/schemes/curves/closed_point
diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst
index 7c0fb57bb1e..9dde6700341 100644
--- a/src/doc/en/reference/discrete_geometry/index.rst
+++ b/src/doc/en/reference/discrete_geometry/index.rst
@@ -12,6 +12,7 @@ Hyperplane arrangements
:maxdepth: 1
sage/geometry/hyperplane_arrangement/arrangement
+ sage/geometry/hyperplane_arrangement/ordered_arrangement
sage/geometry/hyperplane_arrangement/library
sage/geometry/hyperplane_arrangement/hyperplane
sage/geometry/hyperplane_arrangement/affine_subspace
@@ -79,6 +80,7 @@ Toric geometry
sage/geometry/toric_lattice
sage/geometry/cone
sage/geometry/cone_catalog
+ sage/geometry/cone_critical_angles
sage/geometry/fan
sage/geometry/fan_morphism
sage/geometry/point_collection
diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst
index eb7dab7193c..8b2fe6f250b 100644
--- a/src/doc/en/reference/game_theory/index.rst
+++ b/src/doc/en/reference/game_theory/index.rst
@@ -8,7 +8,6 @@ Game Theory
sage/game_theory/matching_game
sage/game_theory/normal_form_game
sage/game_theory/catalog_normal_form_games
- sage/game_theory/gambit_docs
sage/game_theory/parser
.. include:: ../footer.txt
diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst
index 8188233a52b..e1f2adabb5e 100644
--- a/src/doc/en/reference/homology/index.rst
+++ b/src/doc/en/reference/homology/index.rst
@@ -19,6 +19,5 @@ computing homology groups.
sage/homology/algebraic_topological_model
sage/homology/homology_morphism
sage/homology/matrix_utils
- sage/interfaces/chomp
.. include:: ../footer.txt
diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst
index 0b660cd9732..bdc30927a55 100644
--- a/src/doc/en/reference/misc/index.rst
+++ b/src/doc/en/reference/misc/index.rst
@@ -219,7 +219,6 @@ Distribution
:maxdepth: 1
sage/misc/package
- sage/misc/dist
Credits
diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst
index f69da256651..44c22814471 100644
--- a/src/doc/en/reference/references/index.rst
+++ b/src/doc/en/reference/references/index.rst
@@ -4217,6 +4217,10 @@ REFERENCES:
.. [Lei2013] Tom Leinster, *The magnitude of metric spaces*.
Doc. Math. 18 (2013), 857-905.
+.. [Ler2022] Antonin Leroux: *Quaternion algebras and isogeny-based cryptography*,
+ PhD Thesis, 2022.
+ https://www.lix.polytechnique.fr/Labo/Antonin.LEROUX/manuscrit_these.pdf
+
.. [Lev2014] Lionel Levine. *Threshold state and a conjecture of
Poghosyan, Poghosyan, Priezzhev and Ruelle*,
Communications in Mathematical Physics.
@@ -4999,6 +5003,11 @@ REFERENCES:
.. [Nie] Johan S. R. Nielsen, Codinglib,
https://bitbucket.org/jsrn/codinglib/.
+.. [Niez1998] Marek Niezgoda.
+ Group majorization and Schur type inequalities.
+ Linear Algebra and its Applications, 268(1):9-30, 1998.
+ :doi:`10.1016/S0024-3795(97)89322-6`.
+
.. [NW1978] \A. Nijenhuis and H. Wilf, Combinatorial Algorithms,
Academic Press (1978).
@@ -5111,6 +5120,13 @@ REFERENCES:
Electronic Journal of Linear Algebra, 34:444-458, 2018,
:doi:`10.13001/1081-3810.3782`.
+.. [Or2020] \M. Orlitzky. When a maximal angle among cones is nonobtuse.
+ Computational and Applied Mathematics 39(2), 2020.
+ :doi:`10.1007/s40314-020-1115-y`.
+
+.. [Or2024] \M. Orlitzky. Continuity of the conic hull.
+ Journal of Convex Analysis 31(1):255-264, 2024.
+
.. [ORS2013] Peter Ozsvath, Jacob Rasmussen, Zoltan Szabo,
*Odd Khovanov homology*,
Algebraic & Geometric Topology 13 (2013) 1465–1488,
@@ -6158,6 +6174,9 @@ REFERENCES:
:doi: `10.1017/fms.2022.38`.
:arxiv:`2106.11141`.
+.. [Ryom2015] Steen Ryom-Hansen. *Projective modules for the symmetric group and
+ Young's seminormal form*. J. Algebra **439** (2015) pp. 515-541.
+
.. [SV1970] \H. Schneider and M. Vidyasagar. Cross-positive matrices. SIAM
Journal on Numerical Analysis, 7:508-519, 1970.
diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst
index afa83bfa754..0c6be456348 100644
--- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst
+++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst
@@ -116,7 +116,6 @@ This base class provides a lot more methods than a general parent::
'_coerce_impl',
'_default_category',
'_gens',
- '_ideal_class_',
'_ideal_monoid',
'_latex_names',
'_list',
@@ -127,8 +126,6 @@ This base class provides a lot more methods than a general parent::
'_zero_ideal',
'algebraic_closure',
'base_extend',
- 'class_group',
- 'content',
'derivation',
'derivation_module',
'divides',
@@ -136,7 +133,6 @@ This base class provides a lot more methods than a general parent::
'extension',
'fraction_field',
'frobenius_endomorphism',
- 'gcd',
'gen',
'gens',
'ideal',
@@ -144,7 +140,6 @@ This base class provides a lot more methods than a general parent::
'integral_closure',
'is_commutative',
'is_field',
- 'is_integral_domain',
'is_integrally_closed',
'is_noetherian',
'is_prime_field',
@@ -858,7 +853,9 @@ The four axioms requested for coercions
rational field is a homomorphism of euclidean domains::
sage: QQ.coerce_map_from(ZZ).category_for()
- Join of Category of euclidean domains and Category of infinite sets
+ Join of Category of euclidean domains
+ and Category of noetherian rings
+ and Category of infinite sets
and Category of metric spaces
.. end of output
diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst
index 9a3cb8e8ee7..f5c17d619d9 100644
--- a/src/doc/en/tutorial/tour_coercion.rst
+++ b/src/doc/en/tutorial/tour_coercion.rst
@@ -118,6 +118,7 @@ implemented in Sage as well:
sage: ZZ.category()
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: ZZ.category().is_subcategory(Rings())
diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst
index 41ec9264ae6..e19487c0cbd 100644
--- a/src/doc/fr/tutorial/tour_coercion.rst
+++ b/src/doc/fr/tutorial/tour_coercion.rst
@@ -119,6 +119,7 @@ par ailleurs les catégories en tant que telles :
sage: ZZ.category()
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: ZZ.category().is_subcategory(Rings())
diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst
index 6aaf7aa2911..f13b48c782b 100644
--- a/src/doc/ja/tutorial/tour_coercion.rst
+++ b/src/doc/ja/tutorial/tour_coercion.rst
@@ -101,6 +101,7 @@ Sageのクラス階層と圏の階層構造にはそれなりに類似が見ら
sage: ZZ.category()
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: ZZ.category().is_subcategory(Rings())
diff --git a/src/doc/pt/tutorial/tour_coercion.rst b/src/doc/pt/tutorial/tour_coercion.rst
index b5eeaa85a9f..94efa8cec15 100644
--- a/src/doc/pt/tutorial/tour_coercion.rst
+++ b/src/doc/pt/tutorial/tour_coercion.rst
@@ -125,6 +125,7 @@ categorias matemáticas também são implementadas no Sage:
sage: ZZ.category()
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: ZZ.category().is_subcategory(Rings())
diff --git a/src/pyproject.toml b/src/pyproject.toml
new file mode 100644
index 00000000000..f70304534d9
--- /dev/null
+++ b/src/pyproject.toml
@@ -0,0 +1,23 @@
+[build-system]
+# Minimum requirements for the build system to execute.
+requires = [
+ # 68.1.0 Promote pyproject.toml's [tool.setuptools] out of beta.
+ # 68.1.1 Fix editable install finder handling of nested packages
+ 'setuptools >= 68.1.1',
+ # version constraint for macOS Big Sur support (see https://github.com/sagemath/sage/issues/31050)
+ 'wheel >=0.36.2',
+ 'cypari2 >=2.1.1',
+ 'cysignals >=1.10.2',
+ # Exclude 3.0.3 because of https://github.com/cython/cython/issues/5748
+ 'cython >=3.0, != 3.0.3, <4.0',
+ 'gmpy2 ~=2.1.b999',
+ 'memory_allocator',
+ 'numpy >=1.19',
+ 'pkgconfig',
+]
+build-backend = "setuptools.build_meta"
+
+[tool.conda-lock]
+platforms = [
+ 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64'
+]
diff --git a/src/pyproject.toml.m4 b/src/pyproject.toml.m4
deleted file mode 100644
index 26de30ea108..00000000000
--- a/src/pyproject.toml.m4
+++ /dev/null
@@ -1,23 +0,0 @@
-[build-system]
-# Minimum requirements for the build system to execute.
-requires = [
- esyscmd(`sage-get-system-packages install-requires-toml \
- setuptools \
- wheel \
- cypari \
- cysignals \
- cython \
- gmpy2 \
- jinja2 \
- jupyter_core \
- numpy \
- pkgconfig \
- pplpy \
- memory_allocator \
- ')]
-build-backend = "setuptools.build_meta"
-
-[tool.conda-lock]
-platforms = [
- 'osx-64', 'linux-64', 'linux-aarch64', 'osx-arm64'
-]
diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py
index f87d52c94ce..05a686f95d0 100644
--- a/src/sage/algebras/clifford_algebra.py
+++ b/src/sage/algebras/clifford_algebra.py
@@ -514,6 +514,7 @@ def __init__(self, Q, names, category=None):
sage: Cl.category()
Category of finite dimensional super algebras with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: TestSuite(Cl).run()
@@ -1093,6 +1094,7 @@ def lift_module_morphism(self, m, names=None):
sage: phi.category_for()
Category of finite dimensional super algebras with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: phi.matrix()
[ 1 0 0 0 7 -3 -7 0]
@@ -1177,6 +1179,7 @@ def lift_isometry(self, m, names=None):
sage: phi.category_for()
Category of finite dimensional super algebras with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: phi.matrix()
[ 1 0 0 0 1 2 5 0]
diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py
index e84a6b03f24..9564869df42 100644
--- a/src/sage/algebras/free_algebra.py
+++ b/src/sage/algebras/free_algebra.py
@@ -376,6 +376,10 @@ def is_FreeAlgebra(x) -> bool:
sage: from sage.algebras.free_algebra import is_FreeAlgebra
sage: is_FreeAlgebra(5)
+ doctest:warning...
+ DeprecationWarning: the function is_FreeAlgebra is deprecated;
+ use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead
+ See https://github.com/sagemath/sage/issues/37896 for details.
False
sage: is_FreeAlgebra(ZZ)
False
@@ -387,6 +391,8 @@ def is_FreeAlgebra(x) -> bool:
....: degrees=list(range(1,11))))
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37896, "the function is_FreeAlgebra is deprecated; use 'isinstance(..., (FreeAlgebra_generic, FreeAlgebra_letterplace))' instead")
return isinstance(x, (FreeAlgebra_generic, FreeAlgebra_letterplace))
@@ -742,7 +748,7 @@ def _coerce_map_from_(self, R):
return True
# free algebras in the same variable over any base that coerces in:
- if is_FreeAlgebra(R):
+ if isinstance(R, (FreeAlgebra_generic, FreeAlgebra_letterplace)):
if R.variable_names() == self.variable_names():
return self.base_ring().has_coerce_map_from(R.base_ring())
if isinstance(R, PBWBasisOfFreeAlgebra):
diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py
index 94be310a0b1..df19a18e3e8 100644
--- a/src/sage/algebras/free_algebra_quotient.py
+++ b/src/sage/algebras/free_algebra_quotient.py
@@ -60,13 +60,16 @@
# https://www.gnu.org/licenses/
# ****************************************************************************
-from sage.algebras.free_algebra import is_FreeAlgebra
+from sage.algebras.free_algebra import FreeAlgebra_generic
from sage.algebras.free_algebra_quotient_element import FreeAlgebraQuotientElement
from sage.categories.algebras import Algebras
+from sage.misc.lazy_import import lazy_import
from sage.modules.free_module import FreeModule
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.parent import Parent
+lazy_import('sage.algebras.letterplace.free_algebra_letterplace', 'FreeAlgebra_letterplace')
+
class FreeAlgebraQuotient(UniqueRepresentation, Parent):
@staticmethod
@@ -153,7 +156,7 @@ def __init__(self, A, mons, mats, names):
sage: TestSuite(H2).run()
"""
- if not is_FreeAlgebra(A):
+ if not isinstance(A, (FreeAlgebra_generic, FreeAlgebra_letterplace)):
raise TypeError("argument A must be a free algebra")
R = A.base_ring()
n = A.ngens()
diff --git a/src/sage/algebras/lie_algebras/quotient.py b/src/sage/algebras/lie_algebras/quotient.py
index e744cfbd252..db9831cbdd4 100644
--- a/src/sage/algebras/lie_algebras/quotient.py
+++ b/src/sage/algebras/lie_algebras/quotient.py
@@ -218,8 +218,10 @@ def __classcall_private__(cls, I, ambient=None, names=None,
index_set = [i for i in sorted_indices if i not in I_supp]
if names is None:
- amb_names = dict(zip(sorted_indices, ambient.variable_names()))
- names = [amb_names[i] for i in index_set]
+ if ambient._names is not None:
+ # ambient has assigned variable names, so use those
+ amb_names = dict(zip(sorted_indices, ambient.variable_names()))
+ names = [amb_names[i] for i in index_set]
elif isinstance(names, str):
if len(index_set) == 1:
names = [names]
@@ -252,7 +254,7 @@ def __init__(self, I, L, names, index_set, category=None):
B = L.basis()
sm = L.module().submodule_with_basis([I.reduce(B[i]).to_vector()
for i in index_set])
- SB = sm.basis()
+ SB = [L.from_vector(b) for b in sm.basis()]
# compute and normalize structural coefficients for the quotient
s_coeff = {}
@@ -260,7 +262,7 @@ def __init__(self, I, L, names, index_set, category=None):
for j in range(i + 1, len(index_set)):
ind_j = index_set[j]
- brkt = I.reduce(L.bracket(SB[i], SB[j]))
+ brkt = I.reduce(SB[i].bracket(SB[j]))
brktvec = sm.coordinate_vector(brkt.to_vector())
s_coeff[(ind_i, ind_j)] = dict(zip(index_set, brktvec))
s_coeff = LieAlgebraWithStructureCoefficients._standardize_s_coeff(
@@ -291,11 +293,26 @@ def _repr_(self):
L: General linear Lie algebra of rank 2 over Rational Field
I: Ideal ([0 0]
[0 1])
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: a,b,c = L.gens()
+ sage: I = L.ideal([a + 2*b, b + 3*c])
+ sage: Q = L.quotient(I)
+ sage: Q
+ Lie algebra quotient L/I of dimension 1 over Rational Field where
+ L: An example of a finite dimensional Lie algebra with basis:
+ the 3-dimensional abelian Lie algebra over Rational Field
+ I: Ideal ((1, 0, -6), (0, 1, 3))
"""
+ try:
+ ideal_repr = self._I._repr_short()
+ except AttributeError:
+ ideal_repr = repr(tuple(self._I.gens()))
+
return ("Lie algebra quotient L/I of dimension %s"
" over %s where\nL: %s\nI: Ideal %s" % (
self.dimension(), self.base_ring(),
- self.ambient(), self._I._repr_short()))
+ self.ambient(), ideal_repr))
def _repr_generator(self, i):
r"""
diff --git a/src/sage/algebras/lie_algebras/subalgebra.py b/src/sage/algebras/lie_algebras/subalgebra.py
index aee197edbbb..b7b5e2f9866 100644
--- a/src/sage/algebras/lie_algebras/subalgebra.py
+++ b/src/sage/algebras/lie_algebras/subalgebra.py
@@ -888,71 +888,6 @@ def is_ideal(self, A):
return True
return super().is_ideal(A)
- def reduce(self, X):
- r"""
- Reduce an element of the ambient Lie algebra modulo the
- ideal ``self``.
-
- INPUT:
-
- - ``X`` -- an element of the ambient Lie algebra
-
- OUTPUT:
-
- An element `Y` of the ambient Lie algebra that is contained in a fixed
- complementary submodule `V` to ``self`` such that `X = Y` mod ``self``.
-
- When the base ring of ``self`` is a field, the complementary submodule
- `V` is spanned by the elements of the basis that are not the leading
- supports of the basis of ``self``.
-
- EXAMPLES:
-
- An example reduction in a 6 dimensional Lie algebra::
-
- sage: sc = {('a','b'): {'d': 1}, ('a','c'): {'e': 1},
- ....: ('b','c'): {'f': 1}}
- sage: L. = LieAlgebra(QQ, sc)
- sage: I = L.ideal(c)
- sage: I.reduce(a + b + c + d + e + f)
- a + b + d
-
- The reduction of an element is zero if and only if the
- element belongs to the subalgebra::
-
- sage: I.reduce(c + e)
- 0
- sage: c + e in I
- True
-
- Over non-fields, the complementary submodule may not be spanned by
- a subset of the basis of the ambient Lie algebra::
-
- sage: L. = LieAlgebra(ZZ, {('X','Y'): {'Z': 3}})
- sage: I = L.ideal(Y)
- sage: I.basis()
- Family (Y, 3*Z)
- sage: I.reduce(3*Z)
- 0
- sage: I.reduce(Y + 14*Z)
- 2*Z
- """
- R = self.base_ring()
- for Y in self.basis():
- Y = self.lift(Y)
- k, c = Y.leading_item(key=self._order)
-
- if R.is_field():
- X = X - X[k] / c * Y
- else:
- try:
- q, _ = X[k].quo_rem(c)
- X = X - q * Y
- except AttributeError:
- pass
-
- return X
-
class Element(LieSubalgebraElementWrapper):
def adjoint_matrix(self, sparse=False):
"""
diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py
index 71c0379f4f6..5d9ca10e9f0 100644
--- a/src/sage/algebras/quatalg/quaternion_algebra.py
+++ b/src/sage/algebras/quatalg/quaternion_algebra.py
@@ -307,10 +307,16 @@ def is_QuaternionAlgebra(A):
EXAMPLES::
sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(QuaternionAlgebra(QQ,-1,-1))
+ doctest:warning...
+ DeprecationWarning: the function is_QuaternionAlgebra is deprecated;
+ use 'isinstance(..., QuaternionAlgebra_abstract)' instead
+ See https://github.com/sagemath/sage/issues/37896 for details.
True
sage: sage.algebras.quatalg.quaternion_algebra.is_QuaternionAlgebra(ZZ)
False
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37896, "the function is_QuaternionAlgebra is deprecated; use 'isinstance(..., QuaternionAlgebra_abstract)' instead")
return isinstance(A, QuaternionAlgebra_abstract)
@@ -515,7 +521,7 @@ def is_integral_domain(self, proof=True) -> bool:
def is_noetherian(self) -> bool:
"""
- Return ``True`` always, since any quaternion algebra is a noetherian
+ Return ``True`` always, since any quaternion algebra is a Noetherian
ring (because it is a finitely generated module over a field).
EXAMPLES::
@@ -3383,6 +3389,195 @@ def multiply_by_conjugate(self, J):
R = self.quaternion_algebra()
return R.ideal(basis, check=False)
+ def pushforward(self, J, side=None):
+ """
+ Compute the ideal which is the pushforward of ``self`` through an ideal ``J``.
+
+ Uses Lemma 2.1.7 of [Ler2022]_. Only works for integral ideals.
+
+ INPUT:
+
+ - ``J`` -- a fractional quaternion ideal with norm coprime to ``self`` and either
+ the same left order or right order as ``self``
+
+ - ``side`` -- string (optional, default ``None``) set to ``"left"`` or ``"right"`` to
+ perform pushforward of left or right ideals respectively. If ``None`` the side
+ is determined by the matching left or right orders
+
+ OUTPUT: a fractional quaternion ideal
+
+ EXAMPLES::
+
+ sage: B = QuaternionAlgebra(419)
+ sage: i,j,k = B.gens()
+ sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k])
+ sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k])
+ sage: I1.left_order() == I2.left_order()
+ True
+ sage: I1.pushforward(I2, side="left")
+ Fractional ideal (1/2 + 3/2*j + 5*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k)
+
+ TESTS::
+
+ sage: B = QuaternionAlgebra(419)
+ sage: i,j,k = B.gens()
+ sage: O0 = B.maximal_order()
+ sage: O0.unit_ideal().pushforward(O0.unit_ideal())
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J have same left and right orders, side of pushforward must be specified
+ sage: O0.unit_ideal().pushforward(O0.unit_ideal(), "left")
+ Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
+ sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k])
+ sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k])
+ sage: I1.pushforward(I2)
+ Fractional ideal (1/2 + 3/2*j + 5*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k)
+ sage: I1.pushforward(I2, side="right")
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J must have the same right orders
+ sage: I1.conjugate().pushforward(I2.conjugate())
+ Fractional ideal (1/2 + 3/2*j + 10*k, 1/10*i + 2*j + 39/10*k, 3*j, 15*k)
+ sage: I1.conjugate().pushforward(I2.conjugate(), side="left")
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J must have the same left orders
+ sage: I1.pushforward(I1, side="left")
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J must have coprime norms
+ sage: I3 = B.ideal([1/2 + 13/2*j + 6*k, 1/2*i + 3*j + 13/2*k, 9*j, 9*k])
+ sage: I3.pushforward(I3*(1/3), side="left")
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: quaternion ideal pushforward not implemented for non-integral ideals
+ """
+ if not isinstance(J, QuaternionFractionalIdeal_rational):
+ raise TypeError("can only pushforward through a quaternion ideal")
+
+ if side == "left":
+ if self.left_order() != J.left_order():
+ raise ValueError("self and J must have the same left orders")
+ if not self.is_integral() or not J.is_integral():
+ raise NotImplementedError("quaternion ideal pushforward not implemented for non-integral ideals")
+ Jnorm = J.norm()
+ if gcd(self.norm(), Jnorm) != 1:
+ raise ValueError("self and J must have coprime norms")
+ return (1 / Jnorm) * (J.conjugate() * self.intersection(J))
+
+ if side == "right":
+ if self.right_order() != J.right_order():
+ raise ValueError("self and J must have the same right orders")
+ return self.conjugate().pushforward(J.conjugate(), side="left").conjugate()
+
+ if side is None:
+ same_left_order = bool(self.left_order() == J.left_order())
+ same_right_order = bool(self.right_order() == J.right_order())
+ if not same_left_order and not same_right_order:
+ raise ValueError("self and J must share a left or right order")
+ if same_left_order and same_right_order:
+ raise ValueError("self and J have same left and right orders, side of pushforward must be specified")
+ if same_left_order:
+ return self.pushforward(J, side="left")
+ return self.pushforward(J, side="right")
+
+ raise ValueError('side must be "left", "right" or None')
+
+ def pullback(self, J, side=None):
+ """
+ Compute the ideal which is the pullback of ``self`` through an ideal ``J``.
+
+ Uses Lemma 2.1.7 of [Ler2022]_. Only works for integral ideals.
+
+ INPUT:
+
+ - ``J`` -- a fractional quaternion ideal with norm coprime to ``self`` and either
+ left order equal to the right order of ``self``, or vice versa
+
+ - ``side`` -- string (optional, default ``None``) set to ``"left"`` or ``"right"`` to
+ perform pullback of left or right ideals respectively. If ``None`` the side
+ is determined by the matching left and right orders
+
+ OUTPUT: a fractional quaternion ideal
+
+ EXAMPLES::
+
+ sage: B = QuaternionAlgebra(419)
+ sage: i,j,k = B.gens()
+ sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k])
+ sage: I2 = B.ideal([1/2 + 9/2*j, 1/2*i + 9/2*k, 5*j, 5*k])
+ sage: I3 = I1.pushforward(I2, side="left")
+ sage: I3.left_order() == I2.right_order()
+ True
+ sage: I3.pullback(I2, side="left") == I1
+ True
+
+ TESTS::
+
+ sage: B = QuaternionAlgebra(419)
+ sage: i,j,k = B.gens()
+ sage: O0 = B.maximal_order()
+ sage: O0.unit_ideal().pullback(O0.unit_ideal())
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J have same left and right orders, side of pullback must be specified
+ sage: O0.unit_ideal().pullback(O0.unit_ideal(), "left")
+ Fractional ideal (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
+ sage: I1 = B.ideal([1/2 + 3/2*j + 2*k, 1/2*i + j + 3/2*k, 3*j, 3*k])
+ sage: I2 = B.ideal([1/2 + 15/2*j + 2*k, 1/6*i + 43/3*j + 5/2*k, 15*j, 5*k])
+ sage: I2.pullback(I1)
+ Fractional ideal (1/2 + 5/2*j + 2*k, 1/2*i + 3*j + 5/2*k, 5*j, 5*k)
+ sage: I2.pullback(I1, side="right")
+ Traceback (most recent call last):
+ ...
+ ValueError: right order of self should be left order of J
+ sage: I2.conjugate().pullback(I1.conjugate(), side="right")
+ Fractional ideal (1/2 + 5/2*j + 3*k, 1/2*i + 3*j + 5/2*k, 5*j, 5*k)
+ sage: I2.conjugate().pullback(I1.conjugate(), side="left")
+ Traceback (most recent call last):
+ ...
+ ValueError: left order of self should be right order of J
+ sage: I1.pullback(I1.conjugate(), side="left")
+ Traceback (most recent call last):
+ ...
+ ValueError: self and J must have coprime norms
+ sage: I3 = B.ideal([1/2 + 13/2*j + 6*k, 1/2*i + 3*j + 13/2*k, 9*j, 9*k])
+ sage: I3.pullback(I3.conjugate()*(1/3), side="left")
+ Traceback (most recent call last):
+ ...
+ NotImplementedError: quaternion ideal pullback not implemented for non-integral ideals
+ """
+ if not isinstance(J, QuaternionFractionalIdeal_rational):
+ raise TypeError("can only pullback through a quaternion ideal")
+
+ if side == "left":
+ if self.left_order() != J.right_order():
+ raise ValueError("left order of self should be right order of J")
+ if not self.is_integral() or not J.is_integral():
+ raise NotImplementedError("quaternion ideal pullback not implemented for non-integral ideals")
+ N = self.norm()
+ if gcd(N, J.norm()) != 1:
+ raise ValueError("self and J must have coprime norms")
+ return J*self + N*J.left_order()
+
+ if side == "right":
+ if self.right_order() != J.left_order():
+ raise ValueError("right order of self should be left order of J")
+ return self.conjugate().pullback(J.conjugate(), side="left").conjugate()
+
+ if side is None:
+ is_side_left = bool(self.left_order() == J.right_order())
+ is_side_right = bool(self.right_order() == J.left_order())
+ if not is_side_left and not is_side_right:
+ raise ValueError("left order of self must equal right order of J, or vice versa")
+ if is_side_left and is_side_right:
+ raise ValueError("self and J have same left and right orders, side of pullback must be specified")
+ if is_side_left:
+ return self.pullback(J, side="left")
+ return self.pullback(J, side="right")
+
+ raise ValueError('side must be "left", "right" or None')
+
def is_equivalent(self, J, B=10, certificate=False, side=None):
r"""
Checks whether ``self`` and ``J`` are equivalent as ideals.
diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py
index 3f9153cf962..f3f5447245f 100644
--- a/src/sage/algebras/steenrod/steenrod_algebra.py
+++ b/src/sage/algebras/steenrod/steenrod_algebra.py
@@ -3054,7 +3054,7 @@ def is_integral_domain(self, proof=True):
def is_noetherian(self):
"""
- This algebra is noetherian if and only if it is finite.
+ This algebra is Noetherian if and only if it is finite.
EXAMPLES::
diff --git a/src/sage/calculus/functional.py b/src/sage/calculus/functional.py
index 109de21a5d9..d457c448dd1 100644
--- a/src/sage/calculus/functional.py
+++ b/src/sage/calculus/functional.py
@@ -215,8 +215,8 @@ def integral(f, *args, **kwds):
symbolically::
sage: f(x) = 1/(sqrt(2*pi)) * e^(-x^2/2)
- sage: P = plot(f, -4, 4, hue=0.8, thickness=2)
- sage: P.show(ymin=0, ymax=0.4)
+ sage: P = plot(f, -4, 4, hue=0.8, thickness=2) # needs sage.plot
+ sage: P.show(ymin=0, ymax=0.4) # needs sage.plot
sage: numerical_integral(f, -4, 4) # random output
(0.99993665751633376, 1.1101527003413533e-14)
sage: integrate(f, x)
diff --git a/src/sage/calculus/interpolators.pyx b/src/sage/calculus/interpolators.pyx
index 0e1f5fc7209..221b52369e2 100644
--- a/src/sage/calculus/interpolators.pyx
+++ b/src/sage/calculus/interpolators.pyx
@@ -50,13 +50,14 @@ def polygon_spline(pts):
sage: ps = polygon_spline(pts)
sage: fx = lambda x: ps.value(x).real
sage: fy = lambda x: ps.value(x).imag
- sage: show(parametric_plot((fx, fy), (0, 2*pi))) # needs sage.plot
+ sage: show(parametric_plot((fx, fy), (0, 2*pi))) # needs sage.plot sage.symbolic
sage: m = Riemann_Map([lambda x: ps.value(real(x))],
....: [lambda x: ps.derivative(real(x))], 0)
sage: show(m.plot_colored() + m.plot_spiderweb()) # needs sage.plot
Polygon approximation of a circle::
+ sage: # needs sage.symbolic
sage: pts = [e^(I*t / 25) for t in range(25)]
sage: ps = polygon_spline(pts)
sage: ps.derivative(2)
diff --git a/src/sage/calculus/riemann.pyx b/src/sage/calculus/riemann.pyx
index a4f9545bffb..9527736872d 100644
--- a/src/sage/calculus/riemann.pyx
+++ b/src/sage/calculus/riemann.pyx
@@ -408,7 +408,7 @@ cdef class Riemann_Map:
sage: m = Riemann_Map([f], [fprime], 0)
sage: sz = m.get_szego(boundary=0)
sage: points = m.get_szego(absolute_value=True)
- sage: list_plot(points)
+ sage: list_plot(points) # needs sage.plot
Graphics object consisting of 1 graphics primitive
Extending the points by a spline::
@@ -416,7 +416,7 @@ cdef class Riemann_Map:
sage: s = spline(points)
sage: s(3*pi / 4)
0.0012158...
- sage: plot(s,0,2*pi) # plot the kernel
+ sage: plot(s,0,2*pi) # plot the kernel # needs sage.plot
Graphics object consisting of 1 graphics primitive
The unit circle with a small hole::
@@ -483,7 +483,7 @@ cdef class Riemann_Map:
sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t)
sage: m = Riemann_Map([f], [fprime], 0)
sage: points = m.get_theta_points()
- sage: list_plot(points)
+ sage: list_plot(points) # needs sage.plot
Graphics object consisting of 1 graphics primitive
Extending the points by a spline::
@@ -740,12 +740,12 @@ cdef class Riemann_Map:
Default plot::
- sage: m.plot_boundaries()
+ sage: m.plot_boundaries() # needs sage.plot
Graphics object consisting of 1 graphics primitive
Big blue collocation points::
- sage: m.plot_boundaries(plotjoined=False, rgbcolor=[0,0,1], thickness=6)
+ sage: m.plot_boundaries(plotjoined=False, rgbcolor=[0,0,1], thickness=6) # needs sage.plot
Graphics object consisting of 1 graphics primitive
"""
from sage.plot.all import list_plot
@@ -905,17 +905,18 @@ cdef class Riemann_Map:
Default plot::
- sage: m.plot_spiderweb()
+ sage: m.plot_spiderweb() # needs sage.plot
Graphics object consisting of 21 graphics primitives
Simplified plot with many discrete points::
- sage: m.plot_spiderweb(spokes=4, circles=1, pts=400, linescale=0.95, plotjoined=False)
+ sage: m.plot_spiderweb(spokes=4, circles=1, pts=400, # needs sage.plot
+ ....: linescale=0.95, plotjoined=False)
Graphics object consisting of 6 graphics primitives
Plot with thick, red lines::
- sage: m.plot_spiderweb(rgbcolor=[1,0,0], thickness=3)
+ sage: m.plot_spiderweb(rgbcolor=[1,0,0], thickness=3) # needs sage.plot
Graphics object consisting of 21 graphics primitives
To generate the unit circle map, it's helpful to see what the
@@ -924,7 +925,7 @@ cdef class Riemann_Map:
sage: f(t) = e^(I*t)
sage: fprime(t) = I*e^(I*t)
sage: m = Riemann_Map([f], [fprime], 0, 1000)
- sage: m.plot_spiderweb()
+ sage: m.plot_spiderweb() # needs sage.plot
Graphics object consisting of 21 graphics primitives
A multiply connected region with corners. We set ``min_mag`` higher
@@ -934,8 +935,10 @@ cdef class Riemann_Map:
sage: z1 = lambda t: ps.value(t); z1p = lambda t: ps.derivative(t)
sage: z2(t) = -2+exp(-I*t); z2p(t) = -I*exp(-I*t)
sage: z3(t) = 2+exp(-I*t); z3p(t) = -I*exp(-I*t)
- sage: m = Riemann_Map([z1,z2,z3],[z1p,z2p,z3p],0,ncorners=4) # long time
- sage: p = m.plot_spiderweb(withcolor=True,plot_points=500, thickness = 2.0, min_mag=0.1) # long time
+ sage: m = Riemann_Map([z1,z2,z3], [z1p,z2p,z3p], 0, # long time
+ ....: ncorners=4)
+ sage: p = m.plot_spiderweb(withcolor=True, plot_points=500, # long time, needs sage.plot
+ ....: thickness=2.0, min_mag=0.1)
"""
from sage.plot.complex_plot import ComplexPlot
from sage.plot.all import list_plot, Graphics
@@ -1028,17 +1031,17 @@ cdef class Riemann_Map:
sage: f(t) = e^(I*t) - 0.5*e^(-I*t)
sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t)
sage: m = Riemann_Map([f], [fprime], 0)
- sage: m.plot_colored()
+ sage: m.plot_colored() # needs sage.plot
Graphics object consisting of 1 graphics primitive
Plot zoomed in on a specific spot::
- sage: m.plot_colored(plot_range=[0,1,.25,.75])
+ sage: m.plot_colored(plot_range=[0,1,.25,.75]) # needs sage.plot
Graphics object consisting of 1 graphics primitive
High resolution plot::
- sage: m.plot_colored(plot_points=1000) # long time (29s on sage.math, 2012)
+ sage: m.plot_colored(plot_points=1000) # long time (29s on sage.math, 2012), needs sage.plot
Graphics object consisting of 1 graphics primitive
To generate the unit circle map, it's helpful to see what the
@@ -1047,7 +1050,7 @@ cdef class Riemann_Map:
sage: f(t) = e^(I*t)
sage: fprime(t) = I*e^(I*t)
sage: m = Riemann_Map([f], [fprime], 0, 1000)
- sage: m.plot_colored()
+ sage: m.plot_colored() # needs sage.plot
Graphics object consisting of 1 graphics primitive
"""
from sage.plot.complex_plot import ComplexPlot
@@ -1081,7 +1084,7 @@ cdef comp_pt(clist, loop=True):
sage: f(t) = e^(I*t) - 0.5*e^(-I*t)
sage: fprime(t) = I*e^(I*t) + 0.5*I*e^(-I*t)
sage: m = Riemann_Map([f], [fprime], 0)
- sage: m.plot_spiderweb()
+ sage: m.plot_spiderweb() # needs sage.plot
Graphics object consisting of 21 graphics primitives
"""
list2 = [(c.real, c.imag) for c in clist]
diff --git a/src/sage/calculus/transforms/dft.py b/src/sage/calculus/transforms/dft.py
index 1b165fa6503..cd0c76bfc07 100644
--- a/src/sage/calculus/transforms/dft.py
+++ b/src/sage/calculus/transforms/dft.py
@@ -294,13 +294,15 @@ def dft(self, chi=None):
Indexed sequence: [6, 0, 0, 0, 0, 0]
indexed by [0, 1, 2, 3, 4, 5]
- sage: # needs sage.groups
+ sage: # needs sage.combinat sage.groups
sage: G = SymmetricGroup(3)
sage: J = G.conjugacy_classes_representatives()
sage: s = IndexedSequence([1,2,3], J) # 1,2,3 are the values of a class fcn on G
sage: s.dft() # the "scalar-valued Fourier transform" of this class fcn
Indexed sequence: [8, 2, 2]
indexed by [(), (1,2), (1,2,3)]
+
+ sage: # needs sage.rings.number_field
sage: J = AbelianGroup(2, [2,3], names='ab')
sage: s = IndexedSequence([1,2,3,4,5,6], J)
sage: s.dft() # the precision of output is somewhat random and architecture dependent.
@@ -311,6 +313,8 @@ def dft(self, chi=None):
-0.00000000000000976996261670137 - 0.0000000000000159872115546022*I,
-0.00000000000000621724893790087 - 0.0000000000000106581410364015*I]
indexed by Multiplicative Abelian group isomorphic to C2 x C3
+
+ sage: # needs sage.groups sage.rings.number_field
sage: J = CyclicPermutationGroup(6)
sage: s = IndexedSequence([1,2,3,4,5,6], J)
sage: s.dft() # the precision of output is somewhat random and architecture dependent.
diff --git a/src/sage/calculus/transforms/fft.pyx b/src/sage/calculus/transforms/fft.pyx
index 363fa65a836..ca7fd73cb64 100644
--- a/src/sage/calculus/transforms/fft.pyx
+++ b/src/sage/calculus/transforms/fft.pyx
@@ -243,7 +243,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base):
EXAMPLES::
sage: a = FastFourierTransform(4)
- sage: a._plot_polar(0,2) # needs sage.plot
+ sage: a._plot_polar(0,2) # needs sage.plot sage.symbolic
Graphics object consisting of 2 graphics primitives
"""
@@ -304,9 +304,9 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base):
- ``style`` -- Style of the plot, options are ``"rect"`` or ``"polar"``
- - ``rect`` -- height represents real part, color represents
+ - ``"rect"`` -- height represents real part, color represents
imaginary part.
- - ``polar`` -- height represents absolute value, color
+ - ``"polar"`` -- height represents absolute value, color
represents argument.
- ``xmin`` -- The lower bound of the slice to plot. 0 by default.
@@ -319,13 +319,14 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base):
EXAMPLES::
+ sage: # needs sage.plot
sage: a = FastFourierTransform(16)
sage: for i in range(16): a[i] = (random(),random())
- sage: A = plot(a) # needs sage.plot
- sage: B = plot(a, style='polar') # needs sage.plot
- sage: type(A) # needs sage.plot
+ sage: A = plot(a)
+ sage: B = plot(a, style='polar') # needs sage.symbolic
+ sage: type(A)
- sage: type(B) # needs sage.plot
+ sage: type(B) # needs sage.symbolic
sage: a = FastFourierTransform(125)
sage: b = FastFourierTransform(125)
@@ -333,7 +334,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base):
sage: for i in range(1, 60): b[i]=1
sage: a.forward_transform()
sage: a.inverse_transform()
- sage: a.plot() + b.plot() # needs sage.plot
+ sage: a.plot() + b.plot()
Graphics object consisting of 250 graphics primitives
"""
diff --git a/src/sage/categories/bimodules.py b/src/sage/categories/bimodules.py
index 4e92f890cd0..f45f42c52dc 100644
--- a/src/sage/categories/bimodules.py
+++ b/src/sage/categories/bimodules.py
@@ -77,6 +77,7 @@ def _make_named_class_key(self, name):
and Category of metric spaces,
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces)
@@ -85,6 +86,7 @@ def _make_named_class_key(self, name):
(Category of fields,
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces)
diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py
index b71331c5ab5..ce316e50138 100644
--- a/src/sage/categories/category.py
+++ b/src/sage/categories/category.py
@@ -1228,7 +1228,7 @@ def structure(self):
sage: structure(Rings())
(Category of unital magmas, Category of additive unital additive magmas)
sage: structure(Fields())
- (Category of euclidean domains,)
+ (Category of euclidean domains, Category of noetherian rings)
sage: structure(Algebras(QQ))
(Category of unital magmas,
Category of right modules over Rational Field,
@@ -2840,6 +2840,7 @@ def _make_named_class_key(self, name):
sage: Algebras(ZZ)._make_named_class_key("parent_class")
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
@@ -2852,6 +2853,7 @@ def _make_named_class_key(self, name):
and Category of metric spaces,
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces)
@@ -2979,6 +2981,7 @@ def _make_named_class_key(self, name):
sage: Modules(ZZ)._make_named_class_key('element_class')
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: Modules(QQ)._make_named_class_key('parent_class')
diff --git a/src/sage/categories/category_types.py b/src/sage/categories/category_types.py
index d80f5aa7bac..46736d03620 100644
--- a/src/sage/categories/category_types.py
+++ b/src/sage/categories/category_types.py
@@ -226,7 +226,8 @@ def _make_named_class_key(self, name):
sage: Modules(ZZ)._make_named_class_key('element_class')
Join of Category of Dedekind domains
- and Category of euclidean domains
+ and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: Modules(QQ)._make_named_class_key('parent_class')
diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py
index 4e63eb8b594..38ae0d22ada 100644
--- a/src/sage/categories/commutative_rings.py
+++ b/src/sage/categories/commutative_rings.py
@@ -65,6 +65,39 @@ def is_commutative(self) -> bool:
"""
return True
+ def _ideal_class_(self, n=0):
+ r"""
+ Return a callable object that can be used to create ideals in this
+ commutative ring.
+
+ This class can depend on `n`, the number of generators of the ideal.
+ The default input of `n=0` indicates an unspecified number of generators,
+ in which case a class that works for any number of generators is returned.
+
+ EXAMPLES::
+
+ sage: ZZ._ideal_class_()
+
+ sage: RR._ideal_class_()
+
+ sage: R. = GF(5)[]
+ sage: R._ideal_class_(1)
+
+ sage: S = R.quo(x^3 - y^2)
+ sage: S._ideal_class_(1)
+
+ sage: S._ideal_class_(2)
+
+ sage: T. = S[] # needs sage.libs.singular
+ sage: T._ideal_class_(5) # needs sage.libs.singular
+
+ sage: T._ideal_class_(1) # needs sage.libs.singular
+
+ """
+ # One might need more than just n
+ from sage.rings.ideal import Ideal_generic, Ideal_principal
+ return Ideal_principal if n == 1 else Ideal_generic
+
def _test_divides(self, **options):
r"""
Run generic tests on the method :meth:`divides`.
@@ -248,6 +281,18 @@ class Finite(CategoryWithAxiom):
....: GF(5)]) in Rings().Commutative().Finite()
True
"""
+ def extra_super_categories(self):
+ r"""
+ Let Sage know that finite commutative rings are Noetherian.
+
+ EXAMPLES::
+
+ sage: CommutativeRings().Finite().extra_super_categories()
+ [Category of noetherian rings]
+ """
+ from sage.categories.noetherian_rings import NoetherianRings
+ return [NoetherianRings()]
+
class ParentMethods:
def cyclotomic_cosets(self, q, cosets=None):
r"""
@@ -367,7 +412,7 @@ def cyclotomic_cosets(self, q, cosets=None):
try:
~q
except ZeroDivisionError:
- raise ValueError("%s is not invertible in %s" % (q,self))
+ raise ValueError("%s is not invertible in %s" % (q, self))
if cosets is None:
rest = set(self)
@@ -378,7 +423,7 @@ def cyclotomic_cosets(self, q, cosets=None):
while rest:
x0 = rest.pop()
o = [x0]
- x = q*x0
+ x = q * x0
while x != x0:
o.append(x)
rest.discard(x)
diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py
index 08150e61360..513ef129c8a 100644
--- a/src/sage/categories/enumerated_sets.py
+++ b/src/sage/categories/enumerated_sets.py
@@ -990,8 +990,12 @@ def map(self, f, name=None, *, is_injective=True):
....: '_test_enumerated_set_contains',
....: '_test_some_elements'])
"""
- from sage.combinat.combinat import MapCombinatorialClass
- return MapCombinatorialClass(self, f, name, is_injective=is_injective)
+ from sage.sets.image_set import ImageSubobject
+
+ image = ImageSubobject(f, self, is_injective=is_injective)
+ if name:
+ image.rename(name)
+ return image
#
# Consistency test suite for an enumerated set:
diff --git a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py
index e8db64d11a9..452cf12aa1e 100644
--- a/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py
+++ b/src/sage/categories/examples/finite_dimensional_lie_algebras_with_basis.py
@@ -95,6 +95,10 @@ def __init__(self, R, n=None, M=None, ambient=None):
self._ambient = ambient
Parent.__init__(self, base=R, category=cat)
+ from sage.categories.lie_algebras import LiftMorphism
+ self._lift_uea = LiftMorphism(self, self._construct_UEA())
+ self._lift_uea.register_as_coercion()
+
def _repr_(self):
"""
EXAMPLES::
@@ -135,6 +139,57 @@ def _element_constructor_(self, x):
x = x.value
return self.element_class(self, self._M(x))
+ def lift(self, x):
+ r"""
+ Return the lift of ``self``.
+
+ EXAMPLES::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: a, b, c = L.gens()
+ sage: L.lift(a)
+ b0
+ sage: L.lift(b).parent() is L.universal_enveloping_algebra()
+ True
+
+ sage: I = L.ideal([a + 2*b, b + 3*c])
+ sage: I.lift(I.basis()[0])
+ (1, 0, -6)
+ """
+ # FIXME: This method can likely be simplified or removed once we
+ # disentangle the UEA lift from the generic lift
+ A = self._ambient
+ if A is self:
+ return self._lift_uea(x)
+ return A.element_class(A, A._M(x.value))
+
+ def universal_enveloping_algebra(self):
+ r"""
+ Return the universal enveloping algebra of ``self``.
+
+ EXAMPLES::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: L.universal_enveloping_algebra()
+ Noncommutative Multivariate Polynomial Ring in b0, b1, b2
+ over Rational Field, nc-relations: {}
+ """
+ # FIXME: This method can likely be removed once we
+ # disentangle the UEA lift from the generic lift
+ return self._lift_uea.codomain()
+
+ def _order(self, x):
+ r"""
+ Return a key for sorting for the index ``x``.
+
+ TESTS::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: L._order(2)
+ 2
+ """
+ return x
+
@cached_method
def zero(self):
"""
@@ -194,6 +249,8 @@ def subalgebra(self, gens):
[ 1 0 -1/2]
[ 0 1 1]
"""
+ if isinstance(gens, AbelianLieAlgebra):
+ gens = [self(g) for g in gens.gens()]
N = self._M.subspace([g.value for g in gens])
return AbelianLieAlgebra(self.base_ring(), M=N, ambient=self._ambient)
@@ -252,7 +309,7 @@ def gens(self) -> tuple:
sage: L.gens()
((1, 0, 0), (0, 1, 0), (0, 0, 1))
"""
- return tuple(self._M.basis())
+ return tuple([self.element_class(self, b) for b in self._M.basis()])
def module(self):
"""
@@ -302,7 +359,36 @@ def from_vector(self, v, order=None):
"""
return self.element_class(self, self._M(v))
+ def leading_monomials(self):
+ r"""
+ Return the set of leading monomials of the basis of ``self``.
+
+ EXAMPLES::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: a, b, c = L.lie_algebra_generators()
+ sage: I = L.ideal([2*a + b, b + c])
+ sage: I.leading_monomials()
+ ((1, 0, 0), (0, 1, 0))
+ """
+ # for free modules, the leading monomial is actually the trailing monomial
+ return tuple([self._ambient._M(b.value).trailing_monomial()
+ for b in self.basis()])
+
class Element(BaseExample.Element):
+ def __init__(self, parent, value):
+ """
+ Initialize ``self``.
+
+ EXAMPLES::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: a, b, c = L.lie_algebra_generators()
+ sage: TestSuite(a).run()
+ """
+ value.set_immutable()
+ super().__init__(parent, value)
+
def __iter__(self):
"""
Iterate over ``self`` by returning pairs ``(i, c)`` where ``i``
diff --git a/src/sage/categories/examples/lie_algebras.py b/src/sage/categories/examples/lie_algebras.py
index 135c122efa2..15ace1987db 100644
--- a/src/sage/categories/examples/lie_algebras.py
+++ b/src/sage/categories/examples/lie_algebras.py
@@ -198,6 +198,20 @@ def __ne__(self, rhs):
"""
return not self.__eq__(rhs)
+ def __hash__(self):
+ r"""
+ Return the hash of ``self``.
+
+ EXAMPLES::
+
+ sage: # needs sage.combinat sage.groups
+ sage: L = LieAlgebras(QQ).example()
+ sage: x, y = L.lie_algebra_generators()
+ sage: hash(x) == hash(x.value)
+ True
+ """
+ return hash(self.value)
+
def __bool__(self) -> bool:
"""
Check non-zero.
diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py
index 7407632a93f..e0b03fa0901 100644
--- a/src/sage/categories/fields.py
+++ b/src/sage/categories/fields.py
@@ -18,6 +18,7 @@
from sage.categories.category_singleton import Category_contains_method_by_parent_class
from sage.categories.euclidean_domains import EuclideanDomains
from sage.categories.division_rings import DivisionRings
+from sage.categories.noetherian_rings import NoetherianRings
from sage.structure.element import coerce_binop
@@ -33,7 +34,9 @@ class Fields(CategoryWithAxiom):
sage: K
Category of fields
sage: Fields().super_categories()
- [Category of euclidean domains, Category of division rings]
+ [Category of euclidean domains,
+ Category of division rings,
+ Category of noetherian rings]
sage: K(IntegerRing())
Rational Field
@@ -54,10 +57,9 @@ def extra_super_categories(self):
EXAMPLES::
sage: Fields().extra_super_categories()
- [Category of euclidean domains]
-
+ [Category of euclidean domains, Category of noetherian rings]
"""
- return [EuclideanDomains()]
+ return [EuclideanDomains(), NoetherianRings()]
def __contains__(self, x):
"""
@@ -165,7 +167,9 @@ def _call_(self, x):
sage: K
Category of fields
sage: Fields().super_categories()
- [Category of euclidean domains, Category of division rings]
+ [Category of euclidean domains,
+ Category of division rings,
+ Category of noetherian rings]
sage: K(IntegerRing()) # indirect doctest
Rational Field
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 4c8e00b9fc2..4477401a909 100644
--- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py
+++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py
@@ -123,12 +123,21 @@ def _construct_UEA(self):
Universal enveloping algebra of
The 6-Witt Lie algebra over Ring of integers modulo 6
in the Poincare-Birkhoff-Witt basis
+
+ Corner case for the trivial (0-dimensional) Lie algebra::
+
+ sage: L. = LieAlgebra(QQ, abelian=True)
+ sage: I = L.product_space(L)
+ sage: I._construct_UEA()
+ Free Algebra on 0 generators () over Rational Field
"""
from sage.algebras.free_algebra import FreeAlgebra
# Create the UEA relations
# We need to get names for the basis elements, not just the generators
I = self._basis_ordering
+ if not I: # trivial Lie algebra
+ return FreeAlgebra(self.base_ring(), [])
try:
names = [str(x) for x in I]
@@ -522,6 +531,7 @@ def centralizer(self, S):
"""
return self.subalgebra(self.centralizer_basis(S))
+ @cached_method
def center(self):
"""
Return the center of ``self``.
@@ -1065,14 +1075,19 @@ def derived_series(self):
sage: # needs sage.combinat sage.modules
sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}})
- sage: L.derived_series() # not implemented
+ sage: L.derived_series()
(Lie algebra on 2 generators (x, y) over Rational Field,
- Subalgebra generated of
- Lie algebra on 2 generators (x, y) over Rational Field
- with basis: (x,),
- Subalgebra generated of
- Lie algebra on 2 generators (x, y) over Rational Field
- with basis: ())
+ Ideal (x) of Lie algebra on 2 generators (x, y) over Rational Field,
+ Ideal () of Lie algebra on 2 generators (x, y) over Rational Field)
+
+ sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1},
+ ....: ('b','d'): {'b':1}, ('b','e'): {'a':1},
+ ....: ('d','e'): {'c':1}}
+ sage: L. = LieAlgebra(QQ, scoeffs)
+ sage: L.derived_series()
+ (Lie algebra on 5 generators (a, b, c, d, e) over Rational Field,
+ Ideal (a, b, c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field,
+ Ideal () of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field)
"""
L = [self]
while L[-1].dimension() > 0:
@@ -1135,11 +1150,18 @@ def lower_central_series(self, submodule=False):
sage: # needs sage.combinat sage.modules
sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}})
- sage: L.lower_central_series() # not implemented
+ sage: L.lower_central_series()
(Lie algebra on 2 generators (x, y) over Rational Field,
- Subalgebra generated of
- Lie algebra on 2 generators (x, y) over Rational Field
- with basis: (x,))
+ Ideal (x) of Lie algebra on 2 generators (x, y) over Rational Field)
+
+ sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1},
+ ....: ('b','d'): {'b':1}, ('b','e'): {'a':1},
+ ....: ('d','e'): {'c':1}}
+ sage: L. = LieAlgebra(QQ, scoeffs)
+ sage: L.lower_central_series()
+ (Lie algebra on 5 generators (a, b, c, d, e) over Rational Field,
+ Ideal (a, b, c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field,
+ Ideal (a, b) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field)
"""
if submodule:
L = [self.module()]
@@ -1152,6 +1174,82 @@ def lower_central_series(self, submodule=False):
L.append(s)
return tuple(L)
+ @cached_method
+ def upper_central_series(self):
+ r"""
+ Return the upper central series `(Z_i(\mathfrak{g}))_i`
+ of ``self`` where the rightmost
+ `Z_k(\mathfrak{g}) = Z_{k+1}(\mathfrak{g}) = \cdots`.
+
+ The *upper central series* of a Lie algebra `\mathfrak{g}` is
+ defined recursively by `Z_0(\mathfrak{g}) := Z(\mathfrak{g})` and
+
+ .. MATH::
+
+ Z_{k+1}(\mathfrak{g}) / Z_k(\mathfrak{g})
+ = Z(\mathfrak{g} / Z_k(\mathfrak{g}),
+
+ and recall that `Z(\mathfrak{g})` is the :meth:`center`
+ of `\mathfrak{g}`.
+
+ EXAMPLES::
+
+ sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example()
+ sage: L.upper_central_series()
+ [An example of a finite dimensional Lie algebra with basis:
+ the 3-dimensional abelian Lie algebra over Rational Field]
+
+ sage: L. = LieAlgebra(QQ, {('x','y'): {'x':1}})
+ sage: L.upper_central_series()
+ [Subalgebra generated by () of Lie algebra on 2 generators (x, y) over Rational Field]
+
+ sage: scoeffs = {('a','d'): {'a':1}, ('a','e'): {'b':-1},
+ ....: ('b','d'): {'b':1}, ('b','e'): {'a':1},
+ ....: ('d','e'): {'c':1}}
+ sage: L. = LieAlgebra(QQ, scoeffs)
+ sage: L.upper_central_series()
+ [Subalgebra generated by (c) of Lie algebra on 5 generators (a, b, c, d, e) over Rational Field]
+
+ sage: L = lie_algebras.Heisenberg(QQ, 3)
+ sage: L.upper_central_series()
+ [Subalgebra generated by (z) of Heisenberg algebra of rank 3 over Rational Field,
+ Heisenberg algebra of rank 3 over Rational Field]
+ """
+ I = self.center()
+ if I.dimension() == 0:
+ return [I]
+ ret = [I]
+ dim = self.dimension()
+ while True:
+ Q = self.quotient(I)
+ Z = Q.center()
+ if not Z.dimension(): # we did not add anything
+ return ret
+ new_gens = [Q.lift(b.value) for b in Z.basis()]
+ I = self.ideal(list(I.basis()) + new_gens)
+ if I.dimension() == dim:
+ ret.append(self)
+ return ret
+ ret.append(I)
+
+ def hypercenter(self):
+ r"""
+ Return the hypercenter of ``self``.
+
+ EXAMPLES::
+
+ sage: SGA3 = SymmetricGroup(3).algebra(QQ)
+ sage: L = LieAlgebra(associative=SGA3)
+ sage: L.hypercenter()
+ Subalgebra generated by ((), (1,2,3) + (1,3,2), (2,3) + (1,2) + (1,3))
+ of Lie algebra of Symmetric group algebra of order 3 over Rational Field
+
+ sage: L = lie_algebras.Heisenberg(QQ, 3)
+ sage: L.hypercenter()
+ Heisenberg algebra of rank 3 over Rational Field
+ """
+ return self.upper_central_series()[-1]
+
def is_abelian(self):
"""
Return if ``self`` is an abelian Lie algebra.
@@ -2254,3 +2352,71 @@ def basis_matrix(self):
[ 1 0 -1/2]
[ 0 1 1]
"""
+
+ def reduce(self, X):
+ r"""
+ Reduce an element of the ambient Lie algebra modulo the
+ ideal ``self``.
+
+ INPUT:
+
+ - ``X`` -- an element of the ambient Lie algebra
+
+ OUTPUT:
+
+ An element `Y` of the ambient Lie algebra that is contained
+ in a fixed complementary submodule `V` to ``self`` such that
+ `X = Y` mod ``self``.
+
+ When the base ring of ``self`` is a field, the complementary
+ submodule `V` is spanned by the elements of the basis that
+ are not the leading supports of the basis of ``self``.
+
+ EXAMPLES:
+
+ An example reduction in a 6 dimensional Lie algebra::
+
+ sage: sc = {('a','b'): {'d': 1}, ('a','c'): {'e': 1},
+ ....: ('b','c'): {'f': 1}}
+ sage: L. = LieAlgebra(QQ, sc)
+ sage: I = L.ideal(c)
+ sage: I.reduce(a + b + c + d + e + f)
+ a + b + d
+
+ The reduction of an element is zero if and only if the
+ element belongs to the subalgebra::
+
+ sage: I.reduce(c + e)
+ 0
+ sage: c + e in I
+ True
+
+ Over non-fields, the complementary submodule may not be spanned
+ by a subset of the basis of the ambient Lie algebra::
+
+ sage: L. = LieAlgebra(ZZ, {('X','Y'): {'Z': 3}})
+ sage: I = L.ideal(Y)
+ sage: I.basis()
+ Family (Y, 3*Z)
+ sage: I.reduce(3*Z)
+ 0
+ sage: I.reduce(Y + 14*Z)
+ 2*Z
+ """
+ R = self.base_ring()
+ from sage.categories.fields import Fields
+ is_field = R in Fields()
+ for Y in self.basis():
+ Y = self.lift(Y)
+ k, c = Y.leading_item(key=self._order)
+
+ if is_field:
+ X -= (X[k] / c) * Y
+ else:
+ try:
+ q, _ = X[k].quo_rem(c)
+ X -= q * Y
+ except AttributeError:
+ break
+
+ return X
diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py
index 5208522a11a..58916af2aaf 100644
--- a/src/sage/categories/finite_dimensional_modules_with_basis.py
+++ b/src/sage/categories/finite_dimensional_modules_with_basis.py
@@ -679,6 +679,96 @@ def matrix(self, base_ring=None, side="left"):
m.set_immutable()
return m
+ def _repr_matrix(self):
+ r"""
+ Return a string representation of this morphism (as a matrix).
+
+ EXAMPLES::
+
+ sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]],
+ ....: column_keys=['a', 'b', 'c'],
+ ....: row_keys=['v', 'w']); M
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'v', 'w'} over Integer Ring
+ sage: M._repr_ = M._repr_matrix
+ sage: M # indirect doctest
+ a b c
+ v[1 0 0]
+ w[0 1 0]
+ """
+ matrix = self.matrix()
+
+ from sage.matrix.constructor import options
+
+ if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols():
+ return matrix.str(top_border=self.domain().basis().keys(),
+ left_border=self.codomain().basis().keys())
+
+ return repr(matrix)
+
+ def _ascii_art_matrix(self):
+ r"""
+ Return an ASCII art representation of this morphism (as a matrix).
+
+ EXAMPLES::
+
+ sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]],
+ ....: column_keys=['a', 'b', 'c'],
+ ....: row_keys=['v', 'w']); M
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'v', 'w'} over Integer Ring
+ sage: M._ascii_art_ = M._ascii_art_matrix
+ sage: ascii_art(M) # indirect doctest
+ a b c
+ v[1 0 0]
+ w[0 1 0]
+ """
+ matrix = self.matrix()
+
+ from sage.matrix.constructor import options
+
+ if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols():
+ return matrix.str(character_art=True,
+ top_border=self.domain().basis().keys(),
+ left_border=self.codomain().basis().keys())
+
+ from sage.typeset.ascii_art import AsciiArt
+
+ return AsciiArt(repr(self).splitlines())
+
+ def _unicode_art_matrix(self):
+ r"""
+ Return a unicode art representation of this morphism (as a matrix).
+
+ EXAMPLES::
+
+ sage: M = matrix(ZZ, [[1, 0, 0], [0, 1, 0]],
+ ....: column_keys=['a', 'b', 'c'],
+ ....: row_keys=['v', 'w']); M
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'v', 'w'} over Integer Ring
+ sage: M._unicode_art_ = M._unicode_art_matrix
+ sage: unicode_art(M) # indirect doctest
+ a b c
+ v⎛1 0 0⎞
+ w⎝0 1 0⎠
+ """
+ matrix = self.matrix()
+
+ from sage.matrix.constructor import options
+
+ if matrix.nrows() <= options.max_rows() and matrix.ncols() <= options.max_cols():
+ return matrix.str(unicode=True, character_art=True,
+ top_border=self.domain().basis().keys(),
+ left_border=self.codomain().basis().keys())
+
+ from sage.typeset.unicode_art import UnicodeArt
+
+ return UnicodeArt(repr(self).splitlines())
+
def __invert__(self):
"""
Return the inverse morphism of ``self``.
diff --git a/src/sage/categories/group_algebras.py b/src/sage/categories/group_algebras.py
index c5cceb53633..6732a49b150 100644
--- a/src/sage/categories/group_algebras.py
+++ b/src/sage/categories/group_algebras.py
@@ -358,7 +358,7 @@ def is_integral_domain(self, proof=True):
return ans
# I haven't written is_noetherian(), because I don't know when group
- # algebras are noetherian, and I haven't written is_prime_field(), because
+ # algebras are Noetherian, and I haven't written is_prime_field(), because
# I don't know if that means "is canonically isomorphic to a prime field"
# or "is identical to a prime field".
diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py
index deb489a9042..29f4c7df69a 100644
--- a/src/sage/categories/homset.py
+++ b/src/sage/categories/homset.py
@@ -1245,7 +1245,7 @@ def reversed(self):
the principal ideal domain Integer Ring to Ambient free module
of rank 3 over the principal ideal domain Integer Ring in
Category of finite dimensional modules with basis over (Dedekind
- domains and euclidean domains
+ domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
sage: type(H)
@@ -1254,7 +1254,7 @@ def reversed(self):
the principal ideal domain Integer Ring to Ambient free module
of rank 2 over the principal ideal domain Integer Ring in
Category of finite dimensional modules with basis over (Dedekind
- domains and euclidean domains
+ domains and euclidean domains and noetherian rings
and infinite enumerated sets and metric spaces)
sage: type(H.reversed())
diff --git a/src/sage/categories/integral_domains.py b/src/sage/categories/integral_domains.py
index b6e6f59a196..d29b2b159f0 100644
--- a/src/sage/categories/integral_domains.py
+++ b/src/sage/categories/integral_domains.py
@@ -103,6 +103,8 @@ def is_integral_domain(self, proof=True):
EXAMPLES::
+ sage: ZZ.is_integral_domain()
+ True
sage: QQ.is_integral_domain()
True
sage: Parent(QQ, category=IntegralDomains()).is_integral_domain()
@@ -113,6 +115,9 @@ def is_integral_domain(self, proof=True):
True
sage: L.is_integral_domain(proof=True) # needs sage.combinat
True
+
+ sage: ZZ['x'].is_integral_domain()
+ True
"""
return True
diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py
index c5b1aa2afb5..952ca09fbde 100644
--- a/src/sage/categories/modules.py
+++ b/src/sage/categories/modules.py
@@ -885,7 +885,8 @@ def __init_extra__(self):
sage: M.category() # needs sage.modules
Category of Cartesian products of modules with basis
over (Dedekind domains and euclidean domains
- and infinite enumerated sets and metric spaces)
+ and noetherian rings
+ and infinite enumerated sets and metric spaces)
sage: M.base_ring() # needs sage.modules
Integer Ring
diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx
index d4b412dd126..57e00943eda 100644
--- a/src/sage/categories/morphism.pyx
+++ b/src/sage/categories/morphism.pyx
@@ -179,9 +179,12 @@ cdef class Morphism(Map):
sage: f.category()
Category of endsets of unital magmas and right modules over
(Dedekind domains and euclidean domains
+ and noetherian rings
+ and infinite enumerated sets and metric spaces)
+ and left modules over
+ (Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
- and left modules over (Dedekind domains and euclidean domains
- and infinite enumerated sets and metric spaces)
sage: # needs sage.rings.number_field
sage: K = CyclotomicField(12)
diff --git a/src/sage/categories/noetherian_rings.py b/src/sage/categories/noetherian_rings.py
new file mode 100644
index 00000000000..bdffb796243
--- /dev/null
+++ b/src/sage/categories/noetherian_rings.py
@@ -0,0 +1,86 @@
+r"""
+Noetherian rings
+
+EXAMPLES::
+
+ sage: from sage.categories.noetherian_rings import NoetherianRings
+ sage: GF(4, "a") in NoetherianRings() # needs sage.rings.finite_rings
+ True
+ sage: QQ in NoetherianRings()
+ True
+ sage: ZZ in NoetherianRings()
+ True
+ sage: IntegerModRing(4) in NoetherianRings()
+ True
+ sage: IntegerModRing(5) in NoetherianRings()
+ True
+"""
+# ****************************************************************************
+# Copyright (C) 2008 Teresa Gomez-Diaz (CNRS)
+# 2012 Nicolas M. Thiery
+#
+# Distributed under the terms of the GNU General Public License (GPL)
+# https://www.gnu.org/licenses/
+# *****************************************************************************
+
+from sage.categories.category import Category
+from sage.categories.commutative_rings import CommutativeRings
+
+
+class NoetherianRings(Category):
+ """
+ The category of Noetherian rings
+
+ A Noetherian ring is a commutative ring in which
+ every ideal is finitely generated.
+
+ See :wikipedia:`Noetherian ring`
+
+ EXAMPLES::
+
+ sage: from sage.categories.noetherian_rings import NoetherianRings
+ sage: C = NoetherianRings(); C
+ Category of noetherian rings
+ sage: sorted(C.super_categories(), key=str)
+ [Category of commutative rings]
+
+ TESTS::
+
+ sage: TestSuite(C).run()
+ """
+ def super_categories(self):
+ """
+ EXAMPLES::
+
+ sage: from sage.categories.noetherian_rings import NoetherianRings
+ sage: NoetherianRings().super_categories()
+ [Category of commutative rings]
+ """
+ return [CommutativeRings()]
+
+ class ParentMethods:
+ def is_noetherian(self, proof=True):
+ r"""
+ Return ``True``, since this in an object of the category
+ of Noetherian rings.
+
+ EXAMPLES::
+
+ sage: ZZ.is_noetherian()
+ True
+ sage: QQ.is_noetherian()
+ True
+ sage: ZZ['x'].is_noetherian()
+ True
+ sage: R. = PolynomialRing(QQ)
+ sage: R.is_noetherian()
+ True
+
+ sage: L. = LazyLaurentSeriesRing(QQ) # needs sage.combinat
+ sage: L.is_noetherian() # needs sage.combinat
+ True
+ """
+ return True
+
+ class ElementMethods:
+ pass
diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py
index c7339b712e3..aa47130b205 100644
--- a/src/sage/categories/primer.py
+++ b/src/sage/categories/primer.py
@@ -352,18 +352,20 @@
sage: ZZ.category()
Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces
sage: ZZ.categories()
[Join of Category of Dedekind domains
and Category of euclidean domains
+ and Category of noetherian rings
and Category of infinite enumerated sets
and Category of metric spaces,
Category of Dedekind domains,
Category of euclidean domains, Category of principal ideal domains,
Category of unique factorization domains, Category of gcd domains,
- Category of integral domains, Category of domains,
+ Category of integral domains, Category of domains, ...
Category of commutative rings, Category of rings, ...
Category of magmas and additive magmas, ...
Category of monoids, Category of semigroups,
diff --git a/src/sage/categories/principal_ideal_domains.py b/src/sage/categories/principal_ideal_domains.py
index f020cfb383d..6118e06b1c7 100644
--- a/src/sage/categories/principal_ideal_domains.py
+++ b/src/sage/categories/principal_ideal_domains.py
@@ -1,16 +1,17 @@
r"""
Principal ideal domains
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2008 Teresa Gomez-Diaz (CNRS)
#
# Distributed under the terms of the GNU General Public License (GPL)
-# http://www.gnu.org/licenses/
-#******************************************************************************
+# https://www.gnu.org/licenses/
+# *****************************************************************************
from sage.categories.category_singleton import Category_singleton
from sage.categories.unique_factorization_domains import UniqueFactorizationDomains
+
class PrincipalIdealDomains(Category_singleton):
"""
The category of (constructive) principal ideal domains
@@ -94,33 +95,175 @@ def _test_gcd_vs_xgcd(self, **options):
# there are some strange things in Sage doctests... so it is better
# to cut the list in order to avoid lists of size 531441.
elts = elts[:10]
- pairs = [(x,y) for x in elts for y in elts]
+ pairs = [(x, y) for x in elts for y in elts]
try:
- xgcds = [x.xgcd(y) for x,y in pairs]
- except (AttributeError,NotImplementedError):
+ xgcds = [x.xgcd(y) for x, y in pairs]
+ except (AttributeError, NotImplementedError):
return
has_gcd = True
try:
- gcds = [x.gcd(y) for x,y in pairs]
- except (AttributeError,NotImplementedError):
+ gcds = [x.gcd(y) for x, y in pairs]
+ except (AttributeError, NotImplementedError):
has_gcd = False
tester.assertTrue(has_gcd,
"The ring {} provides a xgcd but no gcd".format(self))
- for (x,y),gcd,xgcd in zip(pairs,gcds,xgcds):
+ for (x, y), gcd, xgcd in zip(pairs, gcds, xgcds):
tester.assertTrue(gcd.parent() == self,
"The parent of the gcd is {} for element of {}".format(
gcd.parent(), self))
tester.assertTrue(xgcd[0].parent() == self and
- xgcd[1].parent() == self and xgcd[2].parent() == self,
- "The parent of output in xgcd is different from "
- "the parent of input for elements in {}".format(self))
+ xgcd[1].parent() == self == xgcd[2].parent(),
+ "The parent of output in xgcd is different from "
+ "the parent of input for elements in {}".format(self))
tester.assertTrue(gcd == xgcd[0],
"The methods gcd and xgcd disagree on {}:\n"
" gcd({},{}) = {}\n"
- " xgcd({},{}) = {}\n".format(self,x,y,gcd,x,y,xgcd))
+ " xgcd({},{}) = {}\n".format(self, x, y, gcd, x, y, xgcd))
+
+ def is_noetherian(self) -> bool:
+ """
+ Every principal ideal domain is Noetherian, so we return ``True``.
+
+ EXAMPLES::
+
+ sage: Zp(5).is_noetherian() # needs sage.rings.padics
+ True
+ """
+ return True
+
+ def class_group(self):
+ """
+ Return the trivial group, since the class group of a PID is trivial.
+
+ EXAMPLES::
+
+ sage: QQ.class_group() # needs sage.groups
+ Trivial Abelian group
+ """
+ from sage.groups.abelian_gps.abelian_group import AbelianGroup
+ return AbelianGroup([])
+
+ def gcd(self, x, y, coerce=True):
+ r"""
+ Return the greatest common divisor of ``x`` and ``y``, as elements
+ of ``self``.
+
+ EXAMPLES:
+
+ The integers are a principal ideal domain and hence a GCD domain::
+
+ sage: ZZ.gcd(42, 48)
+ 6
+ sage: 42.factor(); 48.factor()
+ 2 * 3 * 7
+ 2^4 * 3
+ sage: ZZ.gcd(2^4*7^2*11, 2^3*11*13)
+ 88
+ sage: 88.factor()
+ 2^3 * 11
+
+ In a field, any nonzero element is a GCD of any nonempty set
+ of nonzero elements. In previous versions, Sage used to return
+ 1 in the case of the rational field. However, since :issue:`10771`,
+ the rational field is considered as the
+ *fraction field* of the integer ring. For the fraction field
+ of an integral domain that provides both GCD and LCM, it is
+ possible to pick a GCD that is compatible with the GCD of the
+ base ring::
+
+ sage: QQ.gcd(ZZ(42), ZZ(48)); type(QQ.gcd(ZZ(42), ZZ(48)))
+ 6
+
+ sage: QQ.gcd(1/2, 1/3)
+ 1/6
+
+ Polynomial rings over fields are GCD domains as well. Here is a simple
+ example over the ring of polynomials over the rationals as well as
+ over an extension ring. Note that ``gcd`` requires x and y to be
+ coercible::
+
+ sage: # needs sage.rings.number_field
+ sage: R. = PolynomialRing(QQ)
+ sage: S. = NumberField(x^2 - 2, 'a')
+ sage: f = (x - a)*(x + a); g = (x - a)*(x^2 - 2)
+ sage: print(f); print(g)
+ x^2 - 2
+ x^3 - a*x^2 - 2*x + 2*a
+ sage: f in R
+ True
+ sage: g in R
+ False
+ sage: R.gcd(f, g)
+ Traceback (most recent call last):
+ ...
+ TypeError: Unable to coerce 2*a to a rational
+ sage: R.base_extend(S).gcd(f,g)
+ x^2 - 2
+ sage: R.base_extend(S).gcd(f, (x - a)*(x^2 - 3))
+ x - a
+ """
+ if coerce:
+ x = self(x)
+ y = self(y)
+ return x.gcd(y)
+
+ def content(self, x, y, coerce=True):
+ r"""
+ Return the content of `x` and `y`.
+
+ This is the unique element `c` of
+ ``self`` such that `x/c` and `y/c`
+ are coprime and integral.
+
+ EXAMPLES::
+
+ sage: QQ.content(ZZ(42), ZZ(48)); type(QQ.content(ZZ(42), ZZ(48)))
+ 6
+
+ sage: QQ.content(1/2, 1/3)
+ 1/6
+ sage: factor(1/2); factor(1/3); factor(1/6)
+ 2^-1
+ 3^-1
+ 2^-1 * 3^-1
+ sage: a = (2*3)/(7*11); b = (13*17)/(19*23)
+ sage: factor(a); factor(b); factor(QQ.content(a,b))
+ 2 * 3 * 7^-1 * 11^-1
+ 13 * 17 * 19^-1 * 23^-1
+ 7^-1 * 11^-1 * 19^-1 * 23^-1
+
+ Note the changes to the second entry::
+
+ sage: c = (2*3)/(7*11); d = (13*17)/(7*19*23)
+ sage: factor(c); factor(d); factor(QQ.content(c,d))
+ 2 * 3 * 7^-1 * 11^-1
+ 7^-1 * 13 * 17 * 19^-1 * 23^-1
+ 7^-1 * 11^-1 * 19^-1 * 23^-1
+ sage: e = (2*3)/(7*11); f = (13*17)/(7^3*19*23)
+ sage: factor(e); factor(f); factor(QQ.content(e,f))
+ 2 * 3 * 7^-1 * 11^-1
+ 7^-3 * 13 * 17 * 19^-1 * 23^-1
+ 7^-3 * 11^-1 * 19^-1 * 23^-1
+ """
+ if coerce:
+ x = self(x)
+ y = self(y)
+ return x.content(y)
+
+ def _ideal_class_(self, n=0):
+ """
+ Ideals in PIDs have their own special class.
+
+ EXAMPLES::
+
+ sage: ZZ._ideal_class_()
+
+ """
+ from sage.rings.ideal import Ideal_pid
+ return Ideal_pid
class ElementMethods:
pass
diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py
index e8eee82998d..61f0f841720 100644
--- a/src/sage/categories/pushout.py
+++ b/src/sage/categories/pushout.py
@@ -35,7 +35,7 @@
lazy_import('sage.categories.commutative_rings', 'CommutativeRings')
lazy_import('sage.categories.groups', 'Groups')
lazy_import('sage.categories.objects', 'Objects')
-lazy_import('sage.categories.rings', 'Rings', at_startup=True)
+lazy_import('sage.categories.rings', 'Rings')
# TODO, think through the rankings, and override pushout where necessary.
diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py
index 9685fb80dd4..02e81ff1f63 100644
--- a/src/sage/categories/rings.py
+++ b/src/sage/categories/rings.py
@@ -339,6 +339,101 @@ def is_commutative(self) -> bool:
"""
return False
+ def is_integral_domain(self, proof=True) -> bool:
+ """
+ Return ``True`` if this ring is an integral domain.
+
+ INPUT:
+
+ - ``proof`` -- (default: ``True``) Determines what to do in unknown
+ cases
+
+ ALGORITHM:
+
+ If the parameter ``proof`` is set to ``True``, the returned value is
+ correct but the method might throw an error. Otherwise, if it is set
+ to ``False``, the method returns ``True`` if it can establish that ``self``
+ is an integral domain and ``False`` otherwise.
+
+ EXAMPLES::
+
+ sage: QQ.is_integral_domain()
+ True
+ sage: ZZ.is_integral_domain()
+ True
+ sage: ZZ['x,y,z'].is_integral_domain()
+ True
+ sage: Integers(8).is_integral_domain()
+ False
+ sage: Zp(7).is_integral_domain() # needs sage.rings.padics
+ True
+ sage: Qp(7).is_integral_domain() # needs sage.rings.padics
+ True
+ sage: R. = QQ[]
+ sage: S. = R.quo((b^3)) # needs sage.libs.singular
+ sage: S.is_integral_domain() # needs sage.libs.singular
+ False
+ sage: R = ZZ.quotient(ZZ.ideal(10)); R.is_integral_domain()
+ False
+
+ This illustrates the use of the ``proof`` parameter::
+
+ sage: R. = ZZ[]
+ sage: S. = R.quo((b^3)) # needs sage.libs.singular
+ sage: S.is_integral_domain(proof=True) # needs sage.libs.singular
+ Traceback (most recent call last):
+ ...
+ NotImplementedError
+ sage: S.is_integral_domain(proof=False) # needs sage.libs.singular
+ False
+
+ TESTS:
+
+ Make sure :issue:`10481` is fixed::
+
+ sage: x = polygen(ZZ, 'x')
+ sage: R. = ZZ['x'].quo(x^2) # needs sage.libs.pari
+ sage: R.fraction_field() # needs sage.libs.pari
+ Traceback (most recent call last):
+ ...
+ TypeError: self must be an integral domain.
+ sage: R.is_integral_domain() # needs sage.libs.pari
+ False
+
+ Forward the proof flag to ``is_field``, see :issue:`22910`::
+
+ sage: # needs sage.libs.singular
+ sage: R1. = GF(5)[]
+ sage: F1 = R1.quotient_ring(x^2 + x + 1)
+ sage: R2. = F1[]
+ sage: F2 = R2.quotient_ring(x^2 + x + 1)
+ sage: F2.is_integral_domain(False)
+ False
+ """
+ if self.is_field(proof):
+ return True
+
+ if self.is_zero():
+ return False
+
+ if proof:
+ raise NotImplementedError
+
+ return False
+
+ def is_noetherian(self):
+ """
+ Return ``True`` if this ring is Noetherian.
+
+ EXAMPLES::
+
+ sage: QQ.is_noetherian()
+ True
+ sage: ZZ.is_noetherian()
+ True
+ """
+ return False
+
def is_zero(self) -> bool:
"""
Return ``True`` if this is the zero ring.
@@ -579,6 +674,26 @@ def ideal_monoid(self):
from sage.rings.noncommutative_ideals import IdealMonoid_nc
return IdealMonoid_nc(self)
+ def _ideal_class_(self, n=0):
+ r"""
+ Return a callable object that can be used to create ideals in this
+ ring.
+
+ EXAMPLES::
+
+ sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules
+ sage: MS._ideal_class_() # needs sage.modules
+
+
+ Since :issue:`7797`, non-commutative rings have ideals as well::
+
+ sage: A = SteenrodAlgebra(2) # needs sage.combinat sage.modules
+ sage: A._ideal_class_() # needs sage.combinat sage.modules
+
+ """
+ from sage.rings.noncommutative_ideals import Ideal_nc
+ return Ideal_nc
+
def characteristic(self):
"""
Return the characteristic of this ring.
@@ -734,62 +849,6 @@ def ideal(self, *args, **kwds):
gens = gens[0]
return C(self, gens, **kwds)
- def _ideal_class_(self, n=0):
- """
- Return the class that is used to implement ideals of this ring.
-
- .. NOTE::
-
- We copy the code from :class:`~sage.rings.ring.Ring`. This is
- necessary because not all rings inherit from that class, such
- as matrix algebras.
-
- INPUT:
-
- - ``n`` (optional integer, default 0): The number of generators
- of the ideal to be created.
-
- OUTPUT:
-
- The class that is used to implement ideals of this ring with
- ``n`` generators.
-
- .. NOTE::
-
- Often principal ideals (``n==1``) are implemented via
- a different class.
-
- EXAMPLES::
-
- sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules
- sage: MS._ideal_class_() # needs sage.modules
-
-
- We do not know of a commutative ring in Sage that does not inherit
- from the base class of rings. So, we need to cheat in the next
- example::
-
- sage: super(Ring,QQ)._ideal_class_.__module__
- 'sage.categories.rings'
- sage: super(Ring,QQ)._ideal_class_()
-
- sage: super(Ring,QQ)._ideal_class_(1)
-
- sage: super(Ring,QQ)._ideal_class_(2)
-
- """
- from sage.rings.noncommutative_ideals import Ideal_nc
- try:
- if not self.is_commutative():
- return Ideal_nc
- except (NotImplementedError, AttributeError):
- return Ideal_nc
- from sage.rings.ideal import Ideal_generic, Ideal_principal
- if n == 1:
- return Ideal_principal
- return Ideal_generic
-
- ##
# Quotient rings
def quotient(self, I, names=None, **kwds):
"""
diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py
index 193d1f1c966..3cdff883922 100644
--- a/src/sage/combinat/affine_permutation.py
+++ b/src/sage/combinat/affine_permutation.py
@@ -2244,7 +2244,7 @@ def one(self):
True
sage: TestSuite(A).run()
"""
- return self([i for i in range(1,self.k+2)])
+ return self(list(range(1, self.k + 2)))
#------------------------
#Type-unique methods.
diff --git a/src/sage/combinat/all.py b/src/sage/combinat/all.py
index d1f391013e2..4ab35568bb4 100644
--- a/src/sage/combinat/all.py
+++ b/src/sage/combinat/all.py
@@ -56,8 +56,7 @@
from sage.misc.lazy_import import lazy_import
-from .combinat import (CombinatorialClass, CombinatorialObject,
- MapCombinatorialClass,
+from .combinat import (CombinatorialObject,
bell_number, bell_polynomial, bernoulli_polynomial,
catalan_number, euler_number,
fibonacci, fibonacci_sequence, fibonacci_xrange,
@@ -66,12 +65,6 @@
polygonal_number, stirling_number1, stirling_number2,
tuples, unordered_tuples)
-lazy_import('sage.combinat.combinat',
- ('InfiniteAbstractCombinatorialClass', 'UnionCombinatorialClass',
- 'FilteredCombinatorialClass'),
- deprecation=(31545, 'this class is deprecated, do not use'))
-
-
from .expnums import expnums
from sage.combinat.chas.all import *
diff --git a/src/sage/combinat/cartesian_product.py b/src/sage/combinat/cartesian_product.py
index c5a16d3f8ee..ef151e55ffa 100644
--- a/src/sage/combinat/cartesian_product.py
+++ b/src/sage/combinat/cartesian_product.py
@@ -112,6 +112,16 @@ def iterfunc():
category=category,
cache=False)
+ def __hash__(self):
+ r"""
+ EXAMPLES::
+
+ sage: from sage.combinat.cartesian_product import CartesianProduct_iters
+ sage: cp = CartesianProduct_iters((1,2), (3,4))
+ sage: hash(cp) == CartesianProduct_iters((1,2), (3,4))
+ """
+ return hash(tuple(self.iters))
+
def __contains__(self, x):
"""
EXAMPLES::
diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py
index 3fc3499ef67..37103b623f4 100644
--- a/src/sage/combinat/cluster_algebra_quiver/quiver.py
+++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py
@@ -787,9 +787,8 @@ def qmu_save(self, filename=None):
string.append('1')
string.append('//Matrix')
string.append(str(m) + ' ' + str(m))
- for i in range(m):
- string.append(' '.join(str(M[i, j])
- for j in range(m)))
+ string.extend(' '.join(str(M[i, j]) for j in range(m))
+ for i in range(m))
string.append('//Points')
for i in range(m):
diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py
index 38348c41c1d..382c915db77 100644
--- a/src/sage/combinat/combinat.py
+++ b/src/sage/combinat/combinat.py
@@ -180,8 +180,6 @@
from sage.misc.lazy_import import lazy_import
from sage.misc.lazy_attribute import lazy_attribute
from .combinat_cython import _stirling_number2
-from sage.categories.enumerated_sets import EnumeratedSets
-from sage.misc.classcall_metaclass import ClasscallMetaclass
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
from sage.structure.element import Element
@@ -1581,1011 +1579,6 @@ def __init__(self, parent, *args, **kwds):
super().__init__(L)
super(CombinatorialObject, self).__init__(parent)
-
-class CombinatorialClass(Parent, metaclass=ClasscallMetaclass):
- """
- This class is deprecated, and will disappear as soon as all derived
- classes in Sage's library will have been fixed. Please derive
- directly from Parent and use the category :class:`EnumeratedSets`,
- :class:`FiniteEnumeratedSets`, or :class:`InfiniteEnumeratedSets`, as
- appropriate.
-
- For examples, see::
-
- sage: FiniteEnumeratedSets().example()
- An example of a finite enumerated set: {1,2,3}
- sage: InfiniteEnumeratedSets().example()
- An example of an infinite enumerated set: the non negative integers
- """
-
- def __init__(self, category=None):
- """
- TESTS::
-
- sage: C = sage.combinat.combinat.CombinatorialClass()
- sage: C.category()
- Category of enumerated sets
- sage: C.__class__
-
- sage: isinstance(C, Parent)
- True
- sage: C = sage.combinat.combinat.CombinatorialClass(category = FiniteEnumeratedSets())
- sage: C.category()
- Category of finite enumerated sets
- """
- Parent.__init__(self, category=EnumeratedSets().or_subcategory(category))
-
- def is_finite(self) -> bool:
- """
- Return whether ``self`` is finite or not.
-
- EXAMPLES::
-
- sage: Partitions(5).is_finite() # needs sage.combinat
- True
- sage: Permutations().is_finite()
- False
- """
- return self.cardinality() != infinity
-
- def __getitem__(self, i):
- """
- Return the combinatorial object of rank i.
-
- EXAMPLES::
-
- sage: class C(CombinatorialClass):
- ....: def __iter__(self):
- ....: return iter([1,2,3])
- sage: c = C()
- sage: c[0]
- 1
- sage: c[2]
- 3
- sage: c[4]
- Traceback (most recent call last):
- ...
- ValueError: the value must be between 0 and 2 inclusive
- """
- return self.unrank(i)
-
- def __str__(self) -> str:
- """
- Return a string representation of self.
-
- EXAMPLES::
-
- sage: str(Partitions(5)) # needs sage.combinat
- 'Partitions of the integer 5'
- """
- return repr(self)
-
- def _repr_(self) -> str:
- """
- EXAMPLES::
-
- sage: repr(Partitions(5)) # indirect doctest # needs sage.combinat
- 'Partitions of the integer 5'
- """
- if hasattr(self, '_name') and self._name:
- return self._name
- else:
- return "Combinatorial Class -- REDEFINE ME!"
-
- def __contains__(self, x) -> bool:
- """
- Test whether or not the combinatorial class contains the object x.
- This raises a NotImplementedError as a default since _all_
- subclasses of CombinatorialClass should override this.
-
- Note that we could replace this with a default implementation that
- just iterates through the elements of the combinatorial class and
- checks for equality. However, since we use __contains__ for
- type checking, this operation should be cheap and should be
- implemented manually for each combinatorial class.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: x in C # needs sage.symbolic
- Traceback (most recent call last):
- ...
- NotImplementedError
- """
- raise NotImplementedError
-
- def __eq__(self, other):
- """
- Compare two different combinatorial classes.
-
- For now, the comparison is done just on their repr's.
-
- EXAMPLES::
-
- sage: # needs sage.combinat
- sage: p5 = Partitions(5)
- sage: p6 = Partitions(6)
- sage: repr(p5) == repr(p6)
- False
- sage: p5 == p6
- False
- """
- return repr(self) == repr(other)
-
- def __ne__(self, other):
- """
- Test unequality of ``self`` and ``other``.
-
- EXAMPLES::
-
- sage: p5 = Partitions(5) # needs sage.combinat
- sage: p6 = Partitions(6) # needs sage.combinat
- sage: p5 != p6 # needs sage.combinat
- True
- """
- return not (self == other)
-
- def __hash__(self):
- """
- Create a hash value. This is based on the string representation.
-
- Note that in Python 3 objects that define __eq__ do not inherit their __hash__
- function. Without an explicit __hash__ they are no longer hashable.
-
- TESTS::
-
- sage: C = CombinatorialClass()
- sage: hash(C) == hash(repr(C))
- True
- """
- return hash(repr(self))
-
- def __cardinality_from_iterator(self) -> Integer | infinity:
- """
- Default implementation of cardinality which just goes through the iterator
- of the combinatorial class to count the number of objects.
-
- EXAMPLES::
-
- sage: class C(CombinatorialClass):
- ....: def __iter__(self):
- ....: return iter([1,2,3])
- sage: C().cardinality() #indirect doctest
- 3
- """
- c = Integer(0)
- one = Integer(1)
- for _ in self:
- c += one
- return c
-
- cardinality = __cardinality_from_iterator
-
- # __call__, element_class, and _element_constructor_ are poor
- # man's versions of those from Parent. This is for transition,
- # until all combinatorial classes are proper parents (in Parent)
- # and use coercion, etcc
-
- def __call__(self, x):
- """
- Return x as an element of the combinatorial class's object class.
-
- EXAMPLES::
-
- sage: # needs sage.combinat
- sage: p5 = Partitions(5)
- sage: a = [2,2,1]
- sage: type(a)
-
- sage: a = p5(a)
- sage: type(a)
-
- sage: p5([2,1])
- Traceback (most recent call last):
- ...
- ValueError: [2, 1] is not an element of Partitions of the integer 5
- """
- if x in self:
- return self._element_constructor_(x)
- else:
- raise ValueError("%s not in %s" % (x, self))
-
- Element = CombinatorialObject # mostly for backward compatibility
-
- @lazy_attribute
- def element_class(self):
- """
- This function is a temporary helper so that a CombinatorialClass
- behaves as a parent for creating elements. This will disappear when
- combinatorial classes will be turned into actual parents (in the
- category EnumeratedSets).
-
- TESTS::
-
- sage: P5 = Partitions(5) # needs sage.combinat
- sage: P5.element_class # needs sage.combinat
-
- """
- # assert not isinstance(self, Parent) # Raises an alert if we override the proper definition from Parent
- return self.Element
-
- def _element_constructor_(self, x):
- """
- This function is a temporary helper so that a CombinatorialClass
- behaves as a parent for creating elements. This will disappear when
- combinatorial classes will be turned into actual parents (in the
- category EnumeratedSets).
-
- TESTS::
-
- sage: P5 = Partitions(5) # needs sage.combinat
- sage: p = P5([3,2]) # indirect doctest # needs sage.combinat
- sage: type(p) # needs sage.combinat
-
- """
- # assert not isinstance(self, Parent) # Raises an alert if we override the proper definition from Parent
- return self.element_class(x)
-
- def __list_from_iterator(self):
- """
- The default implementation of list which builds the list from the
- iterator.
-
- EXAMPLES::
-
- sage: class C(CombinatorialClass):
- ....: def __iter__(self):
- ....: return iter([1,2,3])
- sage: C().list() #indirect doctest
- [1, 2, 3]
- """
- return [x for x in self]
-
- # Set list to the default implementation
- list = __list_from_iterator
-
- # Set the default object class to be CombinatorialObject
- Element = CombinatorialObject
-
- def __iterator_from_next(self) -> Iterator:
- """
- An iterator to use when the .first() and .next(x) methods are provided.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.first = lambda: 0
- sage: C.next = lambda c: c+1
- sage: it = iter(C) # indirect doctest
- sage: [next(it) for _ in range(4)]
- [0, 1, 2, 3]
- """
- f = self.first()
- yield f
- while True:
- try:
- f = self.next(f)
- except (TypeError, ValueError):
- break
-
- if f is None or f is False:
- break
- else:
- yield f
-
- def __iterator_from_previous(self):
- """
- An iterator to use when .last() and .previous() are provided. Note
- that this requires the combinatorial class to be finite. It is not
- recommended to implement combinatorial classes using last and
- previous.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.last = lambda: 4
- sage: def prev(c):
- ....: if c <= 1:
- ....: return None
- ....: else:
- ....: return c-1
- sage: C.previous = prev
- sage: it = iter(C) # indirect doctest
- sage: [next(it) for _ in range(4)]
- [1, 2, 3, 4]
- """
- l = self.last()
- li = [l]
- while True:
- try:
- l = self.previous(l)
- except (TypeError, ValueError):
- break
-
- if l is None:
- break
- else:
- li.append(l)
- return reversed(li)
-
- def __iterator_from_unrank(self) -> Iterator:
- """
- An iterator to use when .unrank() is provided.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: l = [1,2,3]
- sage: C.unrank = lambda c: l[c]
- sage: list(C) # indirect doctest
- [1, 2, 3]
- """
- r = 0
- u = self.unrank(r)
- yield u
- while True:
- r += 1
- try:
- u = self.unrank(r)
- except (TypeError, ValueError, IndexError):
- break
-
- if u is None:
- break
- else:
- yield u
-
- def __iterator_from_list(self) -> Iterator:
- """
- An iterator to use when .list() is provided()
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1, 2, 3]
- sage: list(C) # indirect doctest
- [1, 2, 3]
- """
- yield from self.list()
-
- def __iter__(self):
- """
- Allows the combinatorial class to be treated as an iterator. Default
- implementation.
-
- EXAMPLES::
-
- sage: p5 = Partitions(5) # needs sage.combinat
- sage: [i for i in p5] # needs sage.combinat
- [[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
- sage: C = CombinatorialClass()
- sage: iter(C)
- Traceback (most recent call last):
- ...
- NotImplementedError: iterator called but not implemented
- """
- # Check whether .first() and .next(x) are overridden in the subclass
- if (self.first != self.__first_from_iterator and
- self.next != self.__next_from_iterator):
- return self.__iterator_from_next()
- # Check whether .last() and .previous() are overridden in the subclass
- elif (self.last != self.__last_from_iterator and
- self.previous != self.__previous_from_iterator):
- return self.__iterator_from_previous()
- # Check whether .unrank() is overridden in the subclass
- elif self.unrank != self.__unrank_from_iterator:
- return self.__iterator_from_unrank()
- # Check whether .list() is overridden in the subclass
- elif self.list != self.__list_from_iterator:
- return self.__iterator_from_list()
- else:
- raise NotImplementedError("iterator called but not implemented")
-
- def __unrank_from_iterator(self, r):
- """
- Default implementation of unrank which goes through the iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.unrank(1) # indirect doctest
- 2
- """
- counter = 0
- for u in self:
- if counter == r:
- return u
- counter += 1
- raise ValueError("the value must be between %s and %s inclusive" % (0, counter - 1))
-
- # Set the default implementation of unrank
- unrank = __unrank_from_iterator
-
- def __random_element_from_unrank(self):
- """
- Default implementation of random which uses unrank.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.random_element() # random # indirect doctest
- 1
- """
- c = self.cardinality()
- r = randint(0, c - 1)
- return self.unrank(r)
-
- # Set the default implementation of random
- random_element = __random_element_from_unrank
-
- def __rank_from_iterator(self, obj):
- """
- Default implementation of rank which uses iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.rank(3) # indirect doctest
- 2
- """
- r = 0
- for i in self:
- if i == obj:
- return r
- r += 1
- raise ValueError
-
- rank = __rank_from_iterator
-
- def __first_from_iterator(self):
- """
- Default implementation for first which uses iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.first() # indirect doctest
- 1
- """
- for i in self:
- return i
-
- first = __first_from_iterator
-
- def __last_from_iterator(self):
- """
- Default implementation for first which uses iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.last() # indirect doctest
- 3
- """
- for i in self:
- pass
- return i
-
- last = __last_from_iterator
-
- def __next_from_iterator(self, obj):
- """
- Default implementation for next which uses iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.next(2) # indirect doctest
- 3
- """
- found = False
- for i in self:
- if found:
- return i
- if i == obj:
- found = True
- return None
-
- next = __next_from_iterator
-
- def __previous_from_iterator(self, obj):
- """
- Default implementation for next which uses iterator.
-
- EXAMPLES::
-
- sage: C = CombinatorialClass()
- sage: C.list = lambda: [1,2,3]
- sage: C.previous(2) # indirect doctest
- 1
- """
- prev = None
- for i in self:
- if i == obj:
- break
- prev = i
- return prev
-
- previous = __previous_from_iterator
-
- def filter(self, f, name=None):
- """
- Return the combinatorial subclass of f which consists of the
- elements x of ``self`` such that f(x) is ``True``.
-
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- sage: P.list() # needs sage.combinat
- [[3, 2, 1]]
- """
- return FilteredCombinatorialClass(self, f, name=name)
-
- def union(self, right_cc, name=None):
- """
- Return the combinatorial class representing the union of ``self`` and
- ``right_cc``.
-
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(2).union(Permutations_CC(1))
- sage: P.list()
- [[1, 2], [2, 1], [1]]
- """
- if not isinstance(right_cc, CombinatorialClass):
- raise TypeError("right_cc must be a CombinatorialClass")
- return UnionCombinatorialClass(self, right_cc, name=name)
-
- def map(self, f, name=None, *, is_injective=True):
- r"""
- Return the image `\{f(x) | x \in \text{self}\}` of this combinatorial
- class by `f`, as a combinatorial class.
-
- INPUT:
-
- - ``is_injective`` -- boolean (default: ``True``) whether to assume
- that ``f`` is injective.
-
- EXAMPLES::
-
- sage: R = Permutations(3).map(attrcall('reduced_word')); R
- Image of Standard permutations of 3 by
- The map *.reduced_word() from Standard permutations of 3
- sage: R.cardinality()
- 6
- sage: R.list()
- [[], [2], [1], [1, 2], [2, 1], [2, 1, 2]]
- sage: [ r for r in R]
- [[], [2], [1], [1, 2], [2, 1], [2, 1, 2]]
-
- If the function is not injective, then there may be repeated elements::
-
- sage: P = Partitions(4) # needs sage.combinat
- sage: P.list() # needs sage.combinat
- [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
- sage: P.map(len).list() # needs sage.combinat
- [1, 2, 2, 3, 4]
-
- Use ``is_injective=False`` to get a correct result in this case::
-
- sage: P.map(len, is_injective=False).list() # needs sage.combinat
- [1, 2, 3, 4]
-
- TESTS::
-
- sage: R = Permutations(3).map(attrcall('reduced_word'))
- sage: R == loads(dumps(R))
- True
- """
- return MapCombinatorialClass(self, f, name, is_injective=is_injective)
-
-
-class FilteredCombinatorialClass(CombinatorialClass):
- def __init__(self, combinatorial_class, f, name=None):
- """
- A filtered combinatorial class F is a subset of another
- combinatorial class C specified by a function f that takes in an
- element c of C and returns True if and only if c is in F.
-
- TESTS::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- Filtered subclass of Standard permutations of 3
- """
- self.f = f
- self.combinatorial_class = combinatorial_class
- self._name = name
-
- def __repr__(self):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- sage: P.__repr__()
- 'Filtered subclass of Standard permutations of 3'
- sage: P._name = 'Permutations avoiding [1, 2]'
- sage: P.__repr__()
- 'Permutations avoiding [1, 2]'
- """
- if self._name:
- return self._name
- else:
- return "Filtered subclass of " + repr(self.combinatorial_class)
-
- def __contains__(self, x) -> bool:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- sage: 'cat' in P
- False
- sage: [4,3,2,1] in P
- False
- sage: Permutation([1,2,3]) in P # needs sage.combinat
- False
- sage: Permutation([3,2,1]) in P # needs sage.combinat
- True
- """
- return x in self.combinatorial_class and self.f(x)
-
- def cardinality(self) -> Integer:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- sage: P.cardinality() # needs sage.combinat
- 1
- """
- c = 0
- for _ in self:
- c += 1
- return c
-
- def __iter__(self) -> Iterator:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).filter(lambda x: x.avoids([1,2]))
- sage: list(P) # needs sage.combinat
- [[3, 2, 1]]
- """
- for x in self.combinatorial_class:
- if self.f(x):
- yield x
-
-
-class UnionCombinatorialClass(CombinatorialClass):
- def __init__(self, left_cc, right_cc, name=None):
- """
- A UnionCombinatorialClass is a union of two other combinatorial
- classes.
-
- TESTS::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P == loads(dumps(P))
- True
- """
- self.left_cc = left_cc
- self.right_cc = right_cc
- self._name = name
-
- def __repr__(self) -> str:
- """
- TESTS::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: print(repr(Permutations_CC(3).union(Permutations_CC(2))))
- Union combinatorial class of
- Standard permutations of 3
- and
- Standard permutations of 2
- """
- if self._name:
- return self._name
- else:
- return "Union combinatorial class of \n %s\nand\n %s" % (self.left_cc, self.right_cc)
-
- def __contains__(self, x) -> bool:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: [1,2] in P
- True
- sage: [3,2,1] in P
- True
- sage: [1,2,3,4] in P
- False
- """
- return x in self.left_cc or x in self.right_cc
-
- def cardinality(self) -> Integer | infinity:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.cardinality()
- 8
- """
- return self.left_cc.cardinality() + self.right_cc.cardinality()
-
- def list(self):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.list()
- [[1, 2, 3],
- [1, 3, 2],
- [2, 1, 3],
- [2, 3, 1],
- [3, 1, 2],
- [3, 2, 1],
- [1, 2],
- [2, 1]]
- """
- return self.left_cc.list() + self.right_cc.list()
-
- def __iter__(self) -> Iterator:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: list(P)
- [[1, 2, 3],
- [1, 3, 2],
- [2, 1, 3],
- [2, 3, 1],
- [3, 1, 2],
- [3, 2, 1],
- [1, 2],
- [2, 1]]
- """
- for x in self.left_cc:
- yield x
- for x in self.right_cc:
- yield x
-
- def first(self):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.first()
- [1, 2, 3]
- """
- return self.left_cc.first()
-
- def last(self):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.last()
- [2, 1]
- """
- return self.right_cc.last()
-
- def rank(self, x):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.rank(Permutation([2,1]))
- 7
- sage: P.rank(Permutation([1,2,3]))
- 0
- """
- try:
- return self.left_cc.rank(x)
- except (TypeError, ValueError):
- return self.left_cc.cardinality() + self.right_cc.rank(x)
-
- def unrank(self, x):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3).union(Permutations_CC(2))
- sage: P.unrank(7)
- [2, 1]
- sage: P.unrank(0)
- [1, 2, 3]
- """
- try:
- return self.left_cc.unrank(x)
- except (TypeError, ValueError):
- return self.right_cc.unrank(x - self.left_cc.cardinality())
-
-
-class Permutations_CC(CombinatorialClass):
- """
- A testing class for :class:`CombinatorialClass` since :class:`Permutations`
- no longer inherits from :class:`CombinatorialClass` in :issue:`14772`.
- """
-
- def __init__(self, n):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(4)
- sage: loads(dumps(P)) == P
- True
- """
- from sage.combinat.permutation import StandardPermutations_n
- self._permutations = StandardPermutations_n(n)
-
- def __repr__(self) -> str:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: Permutations_CC(3)
- Standard permutations of 3
- """
- return repr(self._permutations)
-
- def __contains__(self, x) -> bool:
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3)
- sage: [1, 3, 2] in P
- True
- """
- return x in self._permutations
-
- def __iter__(self):
- """
- EXAMPLES::
-
- sage: from sage.combinat.combinat import Permutations_CC
- sage: P = Permutations_CC(3)
- sage: P.list()
- [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
- """
- return iter(self._permutations)
-
-
-##############################################################################
-from sage.sets.image_set import ImageSubobject
-
-
-class MapCombinatorialClass(ImageSubobject, CombinatorialClass):
- r"""
- The image of a combinatorial class through a function.
-
- INPUT:
-
- - ``is_injective`` -- boolean (default: ``True``) whether to assume
- that ``f`` is injective.
-
- See :meth:`CombinatorialClass.map` for examples
-
- EXAMPLES::
-
- sage: # needs sage.groups
- sage: R = SymmetricGroup(10).map(attrcall('reduced_word'))
- sage: R.an_element()
- [9, 8, 7, 6, 5, 4, 3, 2]
- sage: R.cardinality()
- 3628800
- sage: i = iter(R)
- sage: next(i), next(i), next(i)
- ([], [1, 2, 3, 4, 5, 6, 7, 8, 9], [1])
- """
-
- def __init__(self, cc, f, name=None, *, is_injective=True):
- """
- TESTS::
-
- sage: Partitions(3).map(attrcall('conjugate')) # needs sage.combinat
- Image of Partitions of the integer 3 by The map *.conjugate()
- from Partitions of the integer 3
- """
- ImageSubobject.__init__(self, f, cc, is_injective=is_injective)
- self.cc = cc
- self.f = f
- if name:
- self.rename(name)
-
-
-##############################################################################
-class InfiniteAbstractCombinatorialClass(CombinatorialClass):
- r"""
- This is an internal class that should not be used directly. A class which
- inherits from InfiniteAbstractCombinatorialClass inherits the standard
- methods list and count.
-
- If self._infinite_cclass_slice exists then self.__iter__ returns an
- iterator for self, otherwise raise NotImplementedError. The method
- self._infinite_cclass_slice is supposed to accept any integer as an
- argument and return something which is iterable.
- """
-
- def cardinality(self) -> Integer | infinity:
- """
- Count the elements of the combinatorial class.
-
- EXAMPLES::
-
- sage: R = InfiniteAbstractCombinatorialClass()
- doctest:warning...
- DeprecationWarning: this class is deprecated, do not use
- See https://github.com/sagemath/sage/issues/31545 for details.
-
- sage: R.cardinality()
- +Infinity
- """
- return infinity
-
- def list(self):
- """
- Return an error since ``self`` is an infinite combinatorial class.
-
- EXAMPLES::
-
- sage: R = InfiniteAbstractCombinatorialClass()
- sage: R.list()
- Traceback (most recent call last):
- ...
- NotImplementedError: infinite list
- """
- raise NotImplementedError("infinite list")
-
- def __iter__(self) -> Iterator:
- """
- Return an iterator for the infinite combinatorial class ``self`` if
- possible or raise a NotImplementedError.
-
- EXAMPLES::
-
- sage: R = InfiniteAbstractCombinatorialClass()
- sage: next(iter(R))
- Traceback (most recent call last):
- ...
- NotImplementedError
-
- sage: c = iter(Compositions()) # indirect doctest
- sage: next(c), next(c), next(c), next(c), next(c), next(c)
- ([], [1], [1, 1], [2], [1, 1, 1], [1, 2])
- sage: next(c), next(c), next(c), next(c), next(c), next(c)
- ([2, 1], [3], [1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3])
- """
- try:
- finite = self._infinite_cclass_slice
- except AttributeError:
- raise NotImplementedError
- i = 0
- while True:
- yield from finite(i)
- i += 1
-
-
#####################################################
# combinatorial sets/lists
diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py
index 0ad3151925e..652ddb4b898 100644
--- a/src/sage/combinat/constellation.py
+++ b/src/sage/combinat/constellation.py
@@ -400,7 +400,7 @@ def __copy__(self):
sage: c is copy(c)
False
"""
- return self.parent()([gg for gg in self._g],
+ return self.parent()(list(self._g),
check=False,
mutable=self._mutable)
@@ -417,7 +417,7 @@ def mutable_copy(self):
sage: d.is_mutable()
True
"""
- return self.parent()([gg for gg in self._g],
+ return self.parent()(list(self._g),
check=False,
mutable=True)
diff --git a/src/sage/combinat/crystals/affine_factorization.py b/src/sage/combinat/crystals/affine_factorization.py
index b1d820032af..81fa6a74d0a 100644
--- a/src/sage/combinat/crystals/affine_factorization.py
+++ b/src/sage/combinat/crystals/affine_factorization.py
@@ -323,7 +323,7 @@ def bracketing(self, i):
right_n.remove(min(l))
else:
left_unbracketed += [m]
- return [[j for j in left_unbracketed],[j for j in right_n]]
+ return [list(left_unbracketed), list(right_n)]
def to_tableau(self):
"""
@@ -473,16 +473,16 @@ def _call_(self, x):
"""
p = []
q = []
- for i,factor in enumerate(reversed(x.value)):
+ for i, factor in enumerate(reversed(x.value)):
word = factor.reduced_word()
- p += [i+1]*len(word)
+ p += [i + 1] * len(word)
# We sort for those pesky commutative elements
# The word is most likely in reverse order to begin with
q += sorted(reversed(word))
C = self.codomain()
return C(RSK(p, q, insertion=RSK.rules.EG)[1])
- def is_isomorphism(self):
+ def is_isomorphism(self) -> bool:
"""
Return ``True`` as this is an isomorphism.
diff --git a/src/sage/combinat/crystals/fast_crystals.py b/src/sage/combinat/crystals/fast_crystals.py
index aebc499e9fb..fe32db2a419 100644
--- a/src/sage/combinat/crystals/fast_crystals.py
+++ b/src/sage/combinat/crystals/fast_crystals.py
@@ -154,14 +154,14 @@ def __init__(self, ct, shape, format):
self.shape = shape
for i in range(self.size):
- target = [x for x in self.delpat[i]]
+ target = list(self.delpat[i])
target[0] = target[0]-1
e1 = None if target not in self.delpat else self.delpat.index(target)
target[0] = target[0]+1+1
f1 = None if target not in self.delpat else self.delpat.index(target)
- target = [x for x in self.gampat[i]]
+ target = list(self.gampat[i])
target[0] = target[0]-1
e2 = None if target not in self.gampat else self.gampat.index(target)
target[0] = target[0]+1+1
diff --git a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py
index e63e1a4a3ce..d11846d7766 100644
--- a/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py
+++ b/src/sage/combinat/crystals/fully_commutative_stable_grothendieck.py
@@ -728,8 +728,8 @@ def bracketing(self, i):
m = P.factors
L = list(self.value[m-i-1])
R = list(self.value[m-i])
- right_n = [j for j in R]
- left_n = [j for j in L]
+ right_n = list(R)
+ left_n = list(L)
left_unbracketed = []
while left_n:
m = max(left_n)
@@ -739,7 +739,7 @@ def bracketing(self, i):
right_n.remove(min(l))
else:
left_unbracketed += [m]
- return [[j for j in left_unbracketed], [j for j in right_n]]
+ return [list(left_unbracketed), list(right_n)]
####################
diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py
index 6219da38391..8b2053bec8b 100644
--- a/src/sage/combinat/crystals/infinity_crystals.py
+++ b/src/sage/combinat/crystals/infinity_crystals.py
@@ -250,7 +250,7 @@ def module_generator(self):
[[1, 1, 1], [2, 2], [3]]
"""
n = self._cartan_type.rank()
- p = Partition([x for x in reversed(range(1, n+1))])
+ p = Partition(list(reversed(range(1, n + 1))))
# The column canonical tableau, read by columns
module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n)])
return self(list=[self.letters(x) for x in module_generator])
@@ -624,7 +624,7 @@ def module_generator(self):
[[1, 1, 1], [2, 2], [3]]
"""
n = self._cartan_type.rank()
- p = Partition([x for x in reversed(range(1, n))])
+ p = Partition(list(reversed(range(1, n))))
# The column canonical tableau, read by columns
module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n-1)])
return self(list=[self.letters(x) for x in module_generator])
diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py
index 1e58c8ead35..c1872c793be 100644
--- a/src/sage/combinat/crystals/kirillov_reshetikhin.py
+++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py
@@ -3138,7 +3138,7 @@ def promotion_on_highest_weight_vectors(self):
ind.remove(1)
C = T.cartan_type()
n = C.n
- sh = [i for i in T.shapes[0]]
+ sh = list(T.shapes[0])
sh[n-1] = -sh[n-1]
T_dual = CrystalOfTableaux(C, shape=sh)
hw = [t for t in T if t.is_highest_weight(index_set=ind)]
@@ -3997,7 +3997,7 @@ def horizontal_dominoes_removed(r, s):
sage: sage.combinat.crystals.kirillov_reshetikhin.horizontal_dominoes_removed(3,2)
[[], [2], [2, 2], [2, 2, 2]]
"""
- ulist = [ [y for y in x] + [0]*(r-x.length()) for x in partitions_in_box(r, s//2) ]
+ ulist = [list(x) + [0]*(r-x.length()) for x in partitions_in_box(r, s//2)]
two = lambda x : 2 * (x - s // 2) + s
return [Partition([two(y) for y in x]) for x in ulist]
diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py
index 726c974e781..e0482ec74eb 100644
--- a/src/sage/combinat/crystals/tensor_product.py
+++ b/src/sage/combinat/crystals/tensor_product.py
@@ -583,8 +583,6 @@ def __iter__(self):
for x in self.cartesian_product:
yield self(*x)
-# list = CombinatorialClass._CombinatorialClass__list_from_iterator
-
def cardinality(self):
"""
Return the cardinality of ``self``.
diff --git a/src/sage/combinat/decorated_permutation.py b/src/sage/combinat/decorated_permutation.py
index f687818ef25..817be65223d 100644
--- a/src/sage/combinat/decorated_permutation.py
+++ b/src/sage/combinat/decorated_permutation.py
@@ -189,7 +189,7 @@ def __contains__(self, pi):
if isinstance(pi, DecoratedPermutation):
return len(pi) == self._n
- values = [v for v in pi]
+ values = list(pi)
if len(values) != self._n:
return False
abs_values = [abs(v) for v in values]
diff --git a/src/sage/combinat/derangements.py b/src/sage/combinat/derangements.py
index e9e1de647e8..6b2d868b90d 100644
--- a/src/sage/combinat/derangements.py
+++ b/src/sage/combinat/derangements.py
@@ -422,7 +422,7 @@ def cardinality(self):
R = PolynomialRing(QQ, 'x', len(A))
S = sum(R.gens())
e = prod((S - x)**y for (x, y) in zip(R.gens(), A))
- return Integer(e.coefficient(dict([(x, y) for (x, y) in zip(R.gens(), A)])))
+ return Integer(e.coefficient(dict(zip(R.gens(), A))))
return self._count_der(len(self._set))
def _rand_der(self):
diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py
index e4d80442f3c..943f7c9ba22 100644
--- a/src/sage/combinat/designs/database.py
+++ b/src/sage/combinat/designs/database.py
@@ -4118,7 +4118,7 @@ def RBIBD_120_8_1():
if p in B:
equiv.append([x for x in B if x not in hyperoval])
else:
- new_BIBD.append([x for x in B])
+ new_BIBD.append(list(B))
BIBD = new_BIBD
@@ -4658,7 +4658,7 @@ def BIBD_79_13_2():
permAction = libgap.Action(G, points, action)
- baseBlocks = [libgap.Set(list(map(lambda p: libgap.Position(points, p), B))) for B in [B1, B2, B3, B4]]
+ baseBlocks = [libgap.Set([libgap.Position(points, p) for p in B]) for B in [B1, B2, B3, B4]]
B3Orbit = libgap.Orbit(permAction, baseBlocks[2], libgap.OnSets)
B4Orbit = libgap.Orbit(permAction, baseBlocks[3], libgap.OnSets)
diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py
index 8c2b2b0b01c..a569a722104 100644
--- a/src/sage/combinat/designs/difference_family.py
+++ b/src/sage/combinat/designs/difference_family.py
@@ -2936,7 +2936,7 @@ def complementary_difference_setsII(n, check=True):
if t % 2 == 0:
rho = G.multiplicative_generator()
C0 = list({el**8 for el in G if el != 0})
- C1, C2, C3, C6, C7 = map(lambda i: [rho**i * el for el in C0], [1, 2, 3, 6, 7])
+ C1, C2, C3, C6, C7 = ([rho**i * el for el in C0] for i in [1, 2, 3, 6, 7])
A = C0 + C1 + C2 + C3
B = C0 + C1 + C6 + C7
else:
diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py
index 2227a1e9f63..75df1889c41 100644
--- a/src/sage/combinat/designs/ext_rep.py
+++ b/src/sage/combinat/designs/ext_rep.py
@@ -922,7 +922,7 @@ def _end_element(self, name):
self.block_design_proc(self.current_node[2][0])
if self.save_designs:
init_bd = XTree(self.current_node[2][0])
- self.list_of_designs.append((init_bd.v, [b for b in init_bd.blocks]))
+ self.list_of_designs.append((init_bd.v, list(init_bd.blocks)))
#print_subxt(self.current_node[2][0], level=2, outf=self.outf)
self._init()
elif name == 'info':
diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py
index b4a14b0e7b6..4ba9b60ecc2 100644
--- a/src/sage/combinat/designs/orthogonal_arrays.py
+++ b/src/sage/combinat/designs/orthogonal_arrays.py
@@ -1563,7 +1563,7 @@ def OA_relabel(OA, k, n, blocks=tuple(), matrix=None, symbol_list=None):
OA = [[matrix[i][j] if j is not None else None for i,j in enumerate(R)] for R in OA]
if symbol_list:
- mapping = {index: symbol for index, symbol in enumerate(symbol_list)}
+ mapping = dict(enumerate(symbol_list))
OA = [[mapping[element] for element in row] for row in OA]
return OA
diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py
index 0f82ee78430..ce1758b15c4 100644
--- a/src/sage/combinat/diagram_algebras.py
+++ b/src/sage/combinat/diagram_algebras.py
@@ -1069,7 +1069,7 @@ def bijection_on_free_nodes(self, two_line=False):
sage: elm2.bijection_on_free_nodes(two_line=True)
[[1, 2, 3], [-2, -3, -1]]
"""
- terms = sorted(sorted(list(v), reverse=True) for v in self.diagram()
+ terms = sorted(sorted(v, reverse=True) for v in self.diagram()
if max(v) > 0 and min(v) < 0)
if two_line:
terms = [[t[i] for t in terms] for i in range(2)]
diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py
index 12bbd4385db..069ebb88fb6 100644
--- a/src/sage/combinat/free_module.py
+++ b/src/sage/combinat/free_module.py
@@ -1381,7 +1381,7 @@ def __init__(self, modules, **options):
"""
self._sets = modules
indices = CartesianProduct_iters(*[module.basis().keys()
- for module in modules]).map(tuple)
+ for module in modules]).map(tuple, is_injective=True)
CombinatorialFreeModule.__init__(self, modules[0].base_ring(), indices, **options)
# the following is not the best option, but it's better than nothing.
if 'tensor_symbol' in options:
diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py
index 7540aca9a07..9a28d632c85 100644
--- a/src/sage/combinat/integer_vector_weighted.py
+++ b/src/sage/combinat/integer_vector_weighted.py
@@ -240,7 +240,7 @@ def __iter__(self):
perm = Word(self._weights).standard_permutation()
perm = [len(self._weights) - i for i in perm]
- l = [x for x in sorted(self._weights, reverse=True)]
+ l = sorted(self._weights, reverse=True)
for x in iterator_fast(self._n, l):
yield self.element_class(self, [x[i] for i in perm])
# .action(x)
diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py
index 1c0215fa093..b8b62c6a440 100644
--- a/src/sage/combinat/knutson_tao_puzzles.py
+++ b/src/sage/combinat/knutson_tao_puzzles.py
@@ -817,8 +817,8 @@ def __repr__(self) -> str:
Nablas : [a\b/c, b\c/a, c\a/b]
Deltas : [a/c\b, b/a\c, c/b\a]
"""
- s = "Nablas : %s\n" % sorted([p for p in self._nabla_pieces], key=str)
- s += "Deltas : %s" % sorted([p for p in self._delta_pieces], key=str)
+ s = "Nablas : %s\n" % sorted(self._nabla_pieces, key=str)
+ s += "Deltas : %s" % sorted(self._delta_pieces, key=str)
return s
def delta_pieces(self):
diff --git a/src/sage/combinat/lr_tableau.py b/src/sage/combinat/lr_tableau.py
index a94f576a134..364291cee7c 100644
--- a/src/sage/combinat/lr_tableau.py
+++ b/src/sage/combinat/lr_tableau.py
@@ -304,5 +304,5 @@ def _tableau_join(t1, t2, shift=0):
sage: _tableau_join([[1,2]],[[None,None,2],[3]],shift=5)
[[1, 2, 7], [8]]
"""
- return [[e1 for e1 in row1] + [e2+shift for e2 in row2 if e2 is not None]
+ return [list(row1) + [e2+shift for e2 in row2 if e2 is not None]
for (row1, row2) in zip_longest(t1, t2, fillvalue=[])]
diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py
index 14d6a30a8f5..8c6b2f10395 100644
--- a/src/sage/combinat/matrices/hadamard_matrix.py
+++ b/src/sage/combinat/matrices/hadamard_matrix.py
@@ -592,7 +592,7 @@ def pmtoZ(s):
if n not in db:
raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n)
- a, b, c, d = map(lambda s: vector(pmtoZ(s)), db[n])
+ a, b, c, d = (vector(pmtoZ(s)) for s in db[n])
return a, b, c, d
@@ -736,7 +736,7 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W):
def autocorrelation(seq, j):
return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)])
for j in range(1, n):
- assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0
+ assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0
T1 = X + Z
T2 = X + [-z for z in Z]
@@ -812,7 +812,7 @@ def autocorrelation(seq, j):
return sum([seq[i]*seq[i+j] for i in range(len(seq)-j)])
for j in range(1, n):
- assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0
+ assert sum(autocorrelation(seq, j) for seq in [X, Y, Z, W]) == 0
def alternate(seq1, seq2):
return [seq1[i//2] if i % 2 == 0 else seq2[(i-1)//2] for i in range(len(seq1)+len(seq2))]
diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py
index 967f6ad93af..55d6a9aeaf9 100755
--- a/src/sage/combinat/multiset_partition_into_sets_ordered.py
+++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py
@@ -246,9 +246,9 @@ def _repr_normal(self):
"""
# TODO: simplify if/once ``_repr_`` method for ``Set`` sorts its elements.
if self._n:
- string_parts = map(lambda k: str(sorted(k)), self)
+ string_parts = (str(sorted(k)) for k in self)
else:
- string_parts = map(lambda k: str(sorted(k, key=str)), self)
+ string_parts = (str(sorted(k, key=str)) for k in self)
string_parts = ", ".join(string_parts).replace("[","{").replace("]","}")
return "[" + string_parts + "]"
diff --git a/src/sage/combinat/nu_dyck_word.py b/src/sage/combinat/nu_dyck_word.py
index ddc54a03612..bdc2c0f6e3a 100644
--- a/src/sage/combinat/nu_dyck_word.py
+++ b/src/sage/combinat/nu_dyck_word.py
@@ -829,7 +829,7 @@ def _latex_(self):
if latex_options['show_grid']:
grid = [((0, 0), (self.width(), self.height()))]
for v1, v2 in grid:
- res += " \\draw[dotted] %s grid %s;" % (str(v1), str(v2))
+ res += f" \\draw[dotted] {v1} grid {v2};"
res += "\n"
# Add points if wanted
@@ -838,8 +838,8 @@ def _latex_(self):
radius = 0.15 + .03 * latex_options['line width']
for v in self.points():
res += " \\draw[line width=2,"
- res += "color=%s,fill=%s]" % (pt_color, pt_color)
- res += "%s circle (%s);" % (str(v), str(radius))
+ res += f"color={pt_color},fill={pt_color}]"
+ res += f"{v} circle ({radius});"
res += "\n"
# Add nu if wanted
@@ -853,7 +853,7 @@ def _latex_(self):
res += ";\n"
# setup Path
- res += " \\draw[rounded corners=1, color=%s, line width=%s]" % (
+ res += " \\draw[rounded corners=1, color={}, line width={}]".format(
latex_options['color'],
str(latex_options['line width'])
)
@@ -1210,46 +1210,46 @@ class options(GlobalOptions):
"""
NAME = 'NuDyckWords'
module = 'sage.combinat.nu_dyck_path'
- display = dict(default="list",
- description='Specifies how nu Dyck words should be printed',
- values=dict(list='displayed as a list',
- lattice='displayed on the lattice defined by ``diagram_style``'),
- case_sensitive=False)
- ascii_art = dict(default="pretty_output",
- description='Specifies how the ascii art of nu Dyck words should be printed',
- values=dict(pretty_output="Using pretty printing"),
- alias=dict(pretty_print="pretty_output",),
- case_sensitive=False)
- diagram_style = dict(default="grid",
- values=dict(
- grid='printing as paths on a grid using N and E steps',),
- alias={'N-E': 'grid'},
- case_sensitive=False)
- latex_tikz_scale = dict(default=1,
- description='The default value for the tikz scale when latexed',
- checker=lambda x: True) # More trouble than it's worth to check
- latex_line_width_scalar = dict(default=2,
- description='The default value for the line width as a '
- 'multiple of the tikz scale when latexed',
- checker=lambda x: True) # More trouble than it's worth to check
- latex_color = dict(default="black",
- description='The default value for the color when latexed',
- checker=lambda x: isinstance(x, str))
- latex_show_points = dict(default=False,
- description='The default value for showing points',
- checker=lambda x: isinstance(x, bool))
- latex_points_color = dict(default='black',
- description='The default value for path color.',
- checker=lambda x: isinstance(x, str))
- latex_show_grid = dict(default=True,
- description='The default value for showing grid',
- checker=lambda x: isinstance(x, bool))
- latex_show_nu = dict(default=True,
- description='The default value for showing nu',
- checker=lambda x: isinstance(x, bool))
- latex_nu_options = dict(default='rounded corners=1, color=red, line width=1',
- description='The default value for options for nu path',
- checker=lambda x: isinstance(x, str))
+ display = {'default': "list",
+ 'description': 'Specifies how nu Dyck words should be printed',
+ 'values': {'list': 'displayed as a list',
+ 'lattice': 'displayed on the lattice defined by ``diagram_style``'},
+ 'case_sensitive': False}
+ ascii_art = {'default': "pretty_output",
+ 'description': 'Specifies how the ascii art of nu Dyck words should be printed',
+ 'values': {'pretty_output': "Using pretty printing"},
+ 'alias': {'pretty_print': "pretty_output"},
+ 'case_sensitive': False}
+ diagram_style = {'default': "grid",
+ 'values': {
+ 'grid': 'printing as paths on a grid using N and E steps'},
+ 'alias': {'N-E': 'grid'},
+ 'case_sensitive': False}
+ latex_tikz_scale = {'default': 1,
+ 'description': 'The default value for the tikz scale when latexed',
+ 'checker': lambda x: True} # More trouble than it's worth to check
+ latex_line_width_scalar = {'default': 2,
+ 'description': 'The default value for the line width as a '
+ 'multiple of the tikz scale when latexed',
+ 'checker': lambda x: True} # More trouble than it's worth to check
+ latex_color = {'default': "black",
+ 'description': 'The default value for the color when latexed',
+ 'checker': lambda x: isinstance(x, str)}
+ latex_show_points = {'default': False,
+ 'description': 'The default value for showing points',
+ 'checker': lambda x: isinstance(x, bool)}
+ latex_points_color = {'default': 'black',
+ 'description': 'The default value for path color.',
+ 'checker': lambda x: isinstance(x, str)}
+ latex_show_grid = {'default': True,
+ 'description': 'The default value for showing grid',
+ 'checker': lambda x: isinstance(x, bool)}
+ latex_show_nu = {'default': True,
+ 'description': 'The default value for showing nu',
+ 'checker': lambda x: isinstance(x, bool)}
+ latex_nu_options = {'default': 'rounded corners=1, color=red, line width=1',
+ 'description': 'The default value for options for nu path',
+ 'checker': lambda x: isinstance(x, str)}
def _element_constructor_(self, word):
"""
diff --git a/src/sage/combinat/nu_tamari_lattice.py b/src/sage/combinat/nu_tamari_lattice.py
index d28faf6c7ed..56cfa020194 100644
--- a/src/sage/combinat/nu_tamari_lattice.py
+++ b/src/sage/combinat/nu_tamari_lattice.py
@@ -263,7 +263,7 @@ def AltNuTamariLattice(nu, delta=None):
deltamax = [len(a) for a in nu.split(sep='1')[1:]]
if delta is None:
delta = deltamax
- elif len(delta) != len(deltamax) or any([delta[i] > deltamax[i] for i in range(len(delta))]):
+ elif len(delta) != len(deltamax) or any(delta[i] > deltamax[i] for i in range(len(delta))):
raise ValueError("delta is not a valid increment vector")
def covers(p):
diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py
index cb65aec8aec..539d63a867b 100644
--- a/src/sage/combinat/partition.py
+++ b/src/sage/combinat/partition.py
@@ -713,8 +713,8 @@ def _repr_exp_low(self):
if not self._list:
return '-'
exp = self.to_exp()
- return '%s' % ', '.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e)
- for (m,e) in enumerate(exp) if e > 0)
+ return '%s' % ', '.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e)
+ for (m,e) in enumerate(exp) if e > 0)
def _repr_exp_high(self):
"""
@@ -733,7 +733,7 @@ def _repr_exp_high(self):
return '-'
exp = self.to_exp()[::-1] # reversed list of exponents
M = max(self)
- return ', '.join('%s%s' % (M - m, '' if e == 1 else '^%s' % e)
+ return ', '.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e)
for m, e in enumerate(exp) if e > 0)
def _repr_compact_low(self):
@@ -751,8 +751,8 @@ def _repr_compact_low(self):
if not self._list:
return '-'
exp = self.to_exp()
- return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^%s' % e)
- for (m,e) in enumerate(exp) if e > 0)
+ return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^%s' % e)
+ for (m,e) in enumerate(exp) if e > 0)
def _repr_compact_high(self):
"""
@@ -770,8 +770,8 @@ def _repr_compact_high(self):
return '-'
exp = self.to_exp()[::-1] # reversed list of exponents
M = max(self)
- return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^%s' % e)
- for (m,e) in enumerate(exp) if e > 0)
+ return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^%s' % e)
+ for (m,e) in enumerate(exp) if e > 0)
def _repr_diagram(self):
r"""
@@ -945,8 +945,8 @@ def _latex_exp_low(self):
if not self._list:
return "{\\emptyset}"
exp = self.to_exp()
- return '%s' % ','.join('%s%s' % (m+1, '' if e == 1 else '^{%s}' % e)
- for (m,e) in enumerate(exp) if e > 0)
+ return '%s' % ','.join('{}{}'.format(m + 1, '' if e == 1 else '^{%s}' % e)
+ for (m,e) in enumerate(exp) if e > 0)
def _latex_exp_high(self):
r"""
@@ -963,8 +963,8 @@ def _latex_exp_high(self):
return "{\\emptyset}"
exp = self.to_exp()[::-1] # reversed list of exponents
M = max(self)
- return '%s' % ','.join('%s%s' % (M-m, '' if e == 1 else '^{%s}' % e)
- for (m,e) in enumerate(exp) if e > 0)
+ return '%s' % ','.join('{}{}'.format(M - m, '' if e == 1 else '^{%s}' % e)
+ for (m,e) in enumerate(exp) if e > 0)
def ferrers_diagram(self):
r"""
@@ -1851,7 +1851,7 @@ def down_list(self):
sage: Partition([]).down_list() #checks :issue:`11435`
[]
"""
- return [p for p in self.down()]
+ return list(self.down())
@combinatorial_map(name="cell poset")
def cell_poset(self, orientation="SE"):
@@ -2339,11 +2339,7 @@ def cells(self):
sage: Partition([3,2]).cells()
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1)]
"""
- res = []
- for i in range(len(self)):
- for j in range(self[i]):
- res.append( (i,j) )
- return res
+ return [(i, j) for i, si in enumerate(self) for j in range(si)]
def generalized_pochhammer_symbol(self, a, alpha):
r"""
@@ -2925,7 +2921,7 @@ def top_garnir_tableau(self,e,cell):
"""
(row,col) = cell
if row+1 >= len(self) or col >= self[row+1]:
- raise ValueError('(%s,%s)=(row+1,col) must be inside the diagram' % (row+1,col))
+ raise ValueError(f'({row+1},{col})=(row+1,col) must be inside the diagram')
g = self.garnir_tableau(cell) # start with the Garnir tableau and modify
@@ -2945,6 +2941,110 @@ def top_garnir_tableau(self,e,cell):
t[row+1][col-b+1:col+1] = [m+a+col-b+1+i for i in range(b)]
return tableau.StandardTableau(t)
+ def ladder_tableau(self, e, ladder_lengths=False):
+ r"""
+ Return the ladder tableau of shape ``self``.
+
+ The `e`-*ladder tableau* is the standard Young tableau obtained
+ by reading the *ladders*, the set of cells `(i, j)` that differ
+ from `(i+e-1, j-1)`, of the partition `\lambda` from left-to-right.
+
+ INPUT:
+
+ - ``e`` -- a nonnegative integer; ``0`` is considered as `\infty`
+ (analogous to the characteristic of a ring)
+ - ``ladder_sizes`` -- (default: ``False``) if ``True``, also return
+ the sizes of the ladders
+
+ .. SEEALSO::
+
+ :meth:`ladders`
+
+ EXAMPLES::
+
+ sage: la = Partition([6, 5, 3, 1])
+ sage: ascii_art(la.ladder_tableau(3))
+ 1 2 3 5 7 10
+ 4 6 8 11 13
+ 9 12 14
+ 15
+ sage: la.ladder_tableau(3, ladder_lengths=True)[1]
+ [1, 1, 2, 2, 3, 3, 3]
+
+ sage: ascii_art(la.ladder_tableau(0))
+ 1 2 3 4 5 6
+ 7 8 9 10 11
+ 12 13 14
+ 15
+ sage: all(ll == 1 for ll in la.ladder_tableau(0, ladder_lengths=True)[1])
+ True
+ """
+ Tlad = [[None] * val for val in self]
+ counter = 0
+ start = 0
+ n = sum(self)
+ sizes = []
+ e = e - 1 if e > 0 else n # change to the slope
+ while counter < n:
+ cur = start
+ size = 0
+ for i, val in enumerate(self):
+ if cur < 0:
+ break
+ if cur < val:
+ counter += 1
+ Tlad[i][cur] = counter
+ size += 1
+ cur -= e
+ if ladder_lengths and size:
+ sizes.append(size)
+ start += 1
+ ret = tableau.StandardTableaux(self)(Tlad)
+ if ladder_lengths:
+ return (ret, sizes)
+ return ret
+
+ def ladders(self, e):
+ r"""
+ Return a dictionary containing the ladders in the diagram of ``self``.
+
+ For `e > 0`, a node `(i, j)` in a partition belongs to the `l`-th
+ `e`-ladder if `l = (e - 1) r + c`.
+
+ INPUT:
+
+ - ``e`` -- a nonnegative integer; if ``0``, then we
+ set ``e = self.size() + 1``
+
+ EXAMPLES::
+
+ sage: Partition([3, 2]).ladders(3)
+ {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2), (1, 0)], 3: [(1, 1)]}
+
+ When ``e`` is ``0``, the cells are in bijection with the ladders,
+ but the index of the ladder depends on the size of the partition::
+
+ sage: Partition([3, 2]).ladders(0)
+ {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 6: [(1, 1)]}
+ sage: Partition([3, 2, 1]).ladders(0)
+ {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 6: [(1, 0)], 7: [(1, 1)],
+ 12: [(2, 0)]}
+ sage: Partition([3, 1, 1]).ladders(0)
+ {0: [(0, 0)], 1: [(0, 1)], 2: [(0, 2)], 5: [(1, 0)], 10: [(2, 0)]}
+ sage: Partition([1, 1, 1]).ladders(0)
+ {0: [(0, 0)], 3: [(1, 0)], 6: [(2, 0)]}
+ """
+ if e == 0:
+ e = sum(self) + 1
+ ladders = {}
+ for row, val in enumerate(self):
+ for col in range(val):
+ ell = col + row * (e - 1)
+ if ell not in ladders:
+ ladders[ell] = []
+ ladders[ell].append((row, col))
+ return ladders
+
@cached_method
def young_subgroup(self):
r"""
@@ -3336,16 +3436,16 @@ def attacking_pairs(self):
attacking_pairs = []
for i, r in enumerate(self):
for j in range(r):
- #c is in position (i,j)
- #Find the d that satisfy condition 1
- for k in range(j+1, r):
- attacking_pairs.append( ((i,j),(i,k)) )
+ # c is in position (i,j)
+ # Find the d that satisfy condition 1
+ attacking_pairs.extend(((i, j), (i, k))
+ for k in range(j + 1, r))
- #Find the d that satisfy condition 2
+ # Find the d that satisfy condition 2
if i == 0:
continue
- for k in range(j):
- attacking_pairs.append( ((i,j),(i-1,k)) )
+ attacking_pairs.extend(((i, j), (i - 1, k))
+ for k in range(j))
return attacking_pairs
@@ -4017,7 +4117,7 @@ def is_restricted(self, e, multicharge=(0,)):
return (not self
or ( self[-1] < e and all(self[r]-self[r+1] < e for r in range(len(self)-1)) ))
- def is_regular(self, e, multicharge=(0,)):
+ def is_regular(self, e, multicharge=(0,)) -> bool:
"""
Return ``True`` is this is an ``e``-regular partition.
@@ -4049,9 +4149,9 @@ def conjugacy_class_size(self):
sage: Partition([2,1,1]).conjugacy_class_size()
6
"""
- return factorial(sum(self))/self.centralizer_size()
+ return factorial(sum(self)) / self.centralizer_size()
- def corners(self):
+ def corners(self) -> list:
r"""
Return a list of the corners of the partition ``self``.
@@ -4224,9 +4324,8 @@ def rim(self):
p = self
res = []
prevLen = 1
- for i in range(len(p)-1, -1, -1):
- for c in range(prevLen-1, p[i]):
- res.append((i,c))
+ for i in range(len(p) - 1, -1, -1):
+ res.extend((i, c) for c in range(prevLen - 1, p[i]))
prevLen = p[i]
return res
@@ -4263,9 +4362,8 @@ def outer_rim(self):
p = self
res = []
prevLen = 0
- for i in range(len(p)-1, -1, -1):
- for c in range(prevLen, p[i]+1):
- res.append((i+1,c))
+ for i in range(len(p) - 1, -1, -1):
+ res.extend((i + 1, c) for c in range(prevLen, p[i] + 1))
prevLen = p[i]
res.append((0, prevLen))
return res
@@ -4316,7 +4414,7 @@ def zero_one_sequence(self):
sage: all(Partitions().from_zero_one(mu.zero_one_sequence()) == mu for n in range(10) for mu in Partitions(n))
True
"""
- tmp = set(self[i] - i for i in range(len(self)))
+ tmp = {si - i for i, si in enumerate(self)}
return [Integer(i not in tmp)
for i in range(-len(self) + 1, self.get_part(0) + 1)]
@@ -4958,7 +5056,7 @@ def horizontal_border_strip_cells(self, k):
[(3, 0), (3, 1)]]
"""
if k == 0:
- return list()
+ return []
L = self._list
shelf = [k] # the number of boxes which will fit in a row
@@ -5099,11 +5197,8 @@ def atom(self):
sage: Partition([3,2,1]).atom()
[[[1, 2, 3, 6], [4, 5]], [[1, 2, 3], [4, 5], [6]]]
"""
- res = []
- for tab in tableau.StandardTableaux_size(self.size()):
- if tab.atom() == self:
- res.append(tab)
- return res
+ return [tab for tab in tableau.StandardTableaux_size(self.size())
+ if tab.atom() == self]
def k_atom(self, k):
r"""
@@ -6059,7 +6154,7 @@ def __classcall_private__(cls, n=None, **kwargs):
return RestrictedPartitions_n(n, kwargs['restricted'])
# FIXME: should inherit from IntegerListLex, and implement repr, or _name as a lazy attribute
- kwargs['name'] = "Partitions of the integer %s satisfying constraints %s" % (n, ", ".join( ["%s=%s" % (key, kwargs[key]) for key in sorted(kwargs)] ))
+ kwargs['name'] = "Partitions of the integer {} satisfying constraints {}".format(n, ", ".join( ["{}={}".format(key, kwargs[key]) for key in sorted(kwargs)] ))
# min_part is at least 1, and it is 1 by default
kwargs['min_part'] = max(1, kwargs.get('min_part', 1))
@@ -6195,34 +6290,34 @@ class options(GlobalOptions):
"""
NAME = 'Partitions'
module = 'sage.combinat.partition'
- display = dict(default="list",
- description='Specifies how partitions should be printed',
- values=dict(list='displayed as a list',
- exp_low='in exponential form (lowest first)',
- exp_high='in exponential form (highest first)',
- diagram='as a Ferrers diagram',
- compact_low='compact form of ``exp_low``',
- compact_high='compact form of ``exp_high``'),
- alias=dict(exp="exp_low", compact="compact_low", array="diagram",
- ferrers_diagram="diagram", young_diagram="diagram"),
- case_sensitive=False)
- latex = dict(default="young_diagram",
- description='Specifies how partitions should be latexed',
- values=dict(diagram='latex as a Ferrers diagram',
- young_diagram='latex as a Young diagram',
- list='latex as a list',
- exp_high='latex as a list in exponential notation (highest first)',
- exp_low='as a list latex in exponential notation (lowest first)'),
- alias=dict(exp="exp_low", array="diagram", ferrers_diagram="diagram"),
- case_sensitive=False)
- diagram_str = dict(default="*",
- description='The character used for the cells when printing Ferrers diagrams',
- checker=lambda char: isinstance(char,str))
- latex_diagram_str = dict(default="\\ast",
- description='The character used for the cells when latexing Ferrers diagrams',
- checker=lambda char: isinstance(char,str))
- convention = dict(link_to=(tableau.Tableaux.options,'convention'))
- notation = dict(alt_name='convention')
+ display = {'default': "list",
+ 'description': 'Specifies how partitions should be printed',
+ 'values': {'list': 'displayed as a list',
+ 'exp_low': 'in exponential form (lowest first)',
+ 'exp_high': 'in exponential form (highest first)',
+ 'diagram': 'as a Ferrers diagram',
+ 'compact_low': 'compact form of ``exp_low``',
+ 'compact_high': 'compact form of ``exp_high``'},
+ 'alias': {'exp': "exp_low", 'compact': "compact_low", 'array': "diagram",
+ 'ferrers_diagram': "diagram", 'young_diagram': "diagram"},
+ 'case_sensitive': False}
+ latex = {'default': "young_diagram",
+ 'description': 'Specifies how partitions should be latexed',
+ 'values': {'diagram': 'latex as a Ferrers diagram',
+ 'young_diagram': 'latex as a Young diagram',
+ 'list': 'latex as a list',
+ 'exp_high': 'latex as a list in exponential notation (highest first)',
+ 'exp_low': 'as a list latex in exponential notation (lowest first)'},
+ 'alias': {'exp': "exp_low", 'array': "diagram", 'ferrers_diagram': "diagram"},
+ 'case_sensitive': False}
+ diagram_str = {'default': "*",
+ 'description': 'The character used for the cells when printing Ferrers diagrams',
+ 'checker': lambda char: isinstance(char,str)}
+ latex_diagram_str = {'default': "\\ast",
+ 'description': 'The character used for the cells when latexing Ferrers diagrams',
+ 'checker': lambda char: isinstance(char,str)}
+ convention = {'link_to': (tableau.Tableaux.options,'convention')}
+ notation = {'alt_name': 'convention'}
def __reversed__(self):
"""
@@ -6282,7 +6377,7 @@ def _element_constructor_(self, lst):
# trailing zeros are removed in Partition.__init__
return self.element_class(self, lst)
- raise ValueError('%s is not an element of %s' % (lst, self))
+ raise ValueError(f'{lst} is not an element of {self}')
def __contains__(self, x):
"""
@@ -7122,7 +7217,7 @@ def _repr_(self):
sage: Partitions(5, length=2) # indirect doctest
Partitions of the integer 5 of length 2
"""
- return "Partitions of the integer {} of length {}".format(self.n, self.k)
+ return f"Partitions of the integer {self.n} of length {self.k}"
def _an_element_(self):
"""
@@ -7344,7 +7439,7 @@ def _repr_(self):
sage: Partitions(5, parts_in=[1,2,3]) # indirect doctest
Partitions of the integer 5 with parts in [1, 2, 3]
"""
- return "Partitions of the integer %s with parts in %s" % (self.n, self.parts)
+ return f"Partitions of the integer {self.n} with parts in {self.parts}"
def cardinality(self):
r"""
@@ -7877,7 +7972,7 @@ def _repr_(self):
sage: PartitionsInBox(2,2) # indirect doctest
Integer partitions which fit in a 2 x 2 box
"""
- return "Integer partitions which fit in a %s x %s box" % (self.h, self.w)
+ return f"Integer partitions which fit in a {self.h} x {self.w} box"
def __contains__(self, x):
"""
@@ -8161,7 +8256,7 @@ def _repr_(self):
sage: RegularPartitions_all(3)
3-Regular Partitions
"""
- return "{}-Regular Partitions".format(self._ell)
+ return f"{self._ell}-Regular Partitions"
def __iter__(self):
"""
@@ -8251,7 +8346,7 @@ def _repr_(self):
sage: RegularPartitions_truncated(4, 3)
4-Regular Partitions with max length 3
"""
- return "{}-Regular Partitions with max length {}".format(self._ell, self._max_len)
+ return f"{self._ell}-Regular Partitions with max length {self._max_len}"
def __iter__(self):
"""
@@ -8369,7 +8464,7 @@ def _repr_(self):
sage: RegularPartitions_bounded(4, 3)
4-Regular 3-Bounded Partitions
"""
- return "{}-Regular {}-Bounded Partitions".format(self._ell, self.k)
+ return f"{self._ell}-Regular {self.k}-Bounded Partitions"
def __iter__(self):
"""
@@ -8432,7 +8527,7 @@ def _repr_(self):
sage: RegularPartitions_n(3, 5)
5-Regular Partitions of the integer 3
"""
- return "{}-Regular Partitions of the integer {}".format(self._ell, self.n)
+ return f"{self._ell}-Regular Partitions of the integer {self.n}"
def __contains__(self, x):
"""
@@ -8724,7 +8819,7 @@ def _repr_(self):
sage: PartitionsGreatestLE(10, 2) # indirect doctest
Partitions of 10 having parts less than or equal to 2
"""
- return "Partitions of %s having parts less than or equal to %s" % (self.n, self.k)
+ return f"Partitions of {self.n} having parts less than or equal to {self.k}"
def cardinality(self):
"""
@@ -8814,7 +8909,7 @@ def _repr_(self):
sage: PartitionsGreatestEQ(10, 2) # indirect doctest
Partitions of 10 having greatest part equal to 2
"""
- return "Partitions of %s having greatest part equal to %s" % (self.n, self.k)
+ return f"Partitions of {self.n} having greatest part equal to {self.k}"
def cardinality(self):
"""
@@ -8996,7 +9091,7 @@ def _repr_(self):
sage: RestrictedPartitions_all(3)
3-Restricted Partitions
"""
- return "{}-Restricted Partitions".format(self._ell)
+ return f"{self._ell}-Restricted Partitions"
def __iter__(self):
"""
@@ -9051,7 +9146,7 @@ def _repr_(self):
sage: RestrictedPartitions_n(3, 5)
5-Restricted Partitions of the integer 3
"""
- return "{}-Restricted Partitions of the integer {}".format(self._ell, self.n)
+ return f"{self._ell}-Restricted Partitions of the integer {self.n}"
def __contains__(self, x):
"""
diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py
index 182c0b8813d..b4d15d2dc55 100644
--- a/src/sage/combinat/partition_tuple.py
+++ b/src/sage/combinat/partition_tuple.py
@@ -767,7 +767,7 @@ def components(self):
***
**
"""
- return [t for t in self]
+ return list(self)
def diagram(self):
r"""
@@ -883,7 +883,7 @@ def up(self):
"""
for c in range(len(self)):
for nu in self[c].up():
- up = [tau for tau in self]
+ up = list(self)
up[c] = nu
yield PartitionTuple(up)
@@ -900,7 +900,7 @@ def up_list(self):
[([1], [], [], []), ([], [1], [], []), ([], [], [1], []), ([], [], [], [1])]
"""
- return [mu for mu in self.up()]
+ return list(self.up())
def down(self):
r"""
@@ -917,7 +917,7 @@ def down(self):
"""
for c in range(len(self)):
for nu in self[c].down():
- down = [tau for tau in self]
+ down = list(self)
down[c] = nu
yield PartitionTuple(down)
@@ -933,7 +933,7 @@ def down_list(self):
sage: PartitionTuple([[],[],[]]).down_list()
[]
"""
- return [mu for mu in self.down()]
+ return list(self.down())
def cells(self):
"""
@@ -1515,7 +1515,7 @@ def young_subgroup_generators(self):
m = 0
for comp in self:
for row in comp:
- gens.extend([c for c in range(m + 1, m + row)])
+ gens.extend(list(range(m + 1, m + row)))
m += row
return gens
diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py
index 3919d9866da..0ec9451a9a9 100644
--- a/src/sage/combinat/permutation.py
+++ b/src/sage/combinat/permutation.py
@@ -239,7 +239,7 @@
# https://www.gnu.org/licenses/
# ****************************************************************************
from __future__ import annotations
-from typing import Iterator
+from collections.abc import Iterator
import itertools
import operator
@@ -680,9 +680,9 @@ def _latex_(self):
redword = self.reduced_word()
if not redword:
return self.parent().options.latex_empty_str
- return " ".join("%s_{%s}" % (let, i) for i in redword)
+ return " ".join(f"{let}_{{{i}}}" for i in redword)
if display == "twoline":
- return "\\begin{pmatrix} %s \\\\ %s \\end{pmatrix}" % (
+ return r"\begin{{pmatrix}} {} \\ {} \end{{pmatrix}}".format(
" & ".join("%s" % i for i in range(1, len(self._list)+1)),
" & ".join("%s" % i for i in self._list))
if display == "list":
@@ -1406,7 +1406,7 @@ def __call__(self, i):
"""
if isinstance(i, (int, Integer)) and 1 <= i <= len(self):
return self[i - 1]
- raise TypeError("i (= %s) must be between 1 and %s" % (i, len(self)))
+ raise TypeError(f"i (= {i}) must be between 1 and {len(self)}")
########
# Rank #
@@ -2780,7 +2780,7 @@ def destandardize(self, weight, ordered_alphabet=None):
for a in weight:
partial.append(partial[-1]+a)
if not set(ides).issubset(set(partial)):
- raise ValueError("Standardization with weight {} is not possible!".format(weight))
+ raise ValueError(f"Standardization with weight {weight} is not possible!")
if ordered_alphabet is None:
ordered_alphabet = list(range(1,len(weight)+1))
else:
@@ -4221,7 +4221,7 @@ def right_permutohedron_interval_iterator(self, other):
[2, 1, 5, 4, 3], [2, 5, 1, 4, 3], [2, 5, 4, 1, 3]]
"""
if len(self) != len(other):
- raise ValueError("len({}) and len({}) must be equal".format(self, other))
+ raise ValueError(f"len({self}) and len({other}) must be equal")
if not self.permutohedron_lequal(other):
raise ValueError("{} must be lower or equal than {} for the right permutohedron order".format(self, other))
d = DiGraph()
@@ -6079,7 +6079,7 @@ def _repr_(self) -> str:
sage: Permutations(3,2)
Permutations of {1,...,3} of length 2
"""
- return "Permutations of {1,...,%s} of length %s" % (self.n, self._k)
+ return f"Permutations of {{1,...,{self.n}}} of length {self._k}"
def __iter__(self) -> Iterator[Permutation]:
"""
@@ -6710,7 +6710,7 @@ def _repr_(self):
sage: Permutations([1,2,2], 2)
Permutations of the multi-set [1, 2, 2] of length 2
"""
- return "Permutations of the multi-set %s of length %s" % (list(self.mset), self._k)
+ return f"Permutations of the multi-set {list(self.mset)} of length {self._k}"
def cardinality(self):
"""
@@ -6797,8 +6797,7 @@ def _repr_(self):
sage: repr(Permutations([1,2,4],2))
'Permutations of the set [1, 2, 4] of length 2'
"""
- return "Permutations of the set %s of length %s" % (list(self._set),
- self._k)
+ return f"Permutations of the set {list(self._set)} of length {self._k}"
def __iter__(self):
"""
@@ -6914,7 +6913,7 @@ def _repr_(self):
sage: Arrangements([1,2,2],2)
Arrangements of the multi-set [1, 2, 2] of length 2
"""
- return "Arrangements of the multi-set %s of length %s" % (list(self.mset), self._k)
+ return f"Arrangements of the multi-set {list(self.mset)} of length {self._k}"
class Arrangements_setk(Arrangements, Permutations_setk):
@@ -6929,8 +6928,7 @@ def _repr_(self):
sage: Arrangements([1,2,3],2)
Arrangements of the set [1, 2, 3] of length 2
"""
- return "Arrangements of the set %s of length %s" % (list(self._set),
- self._k)
+ return f"Arrangements of the set {list(self._set)} of length {self._k}"
###############################################################
# Standard permutations
@@ -7420,8 +7418,8 @@ def element_in_conjugacy_classes(self, nu):
from sage.combinat.partition import Partition
nu = Partition(nu)
if nu.size() > self.n:
- raise ValueError("the size of the partition (=%s) should be at most"
- " the size of the permutations (=%s)" % (nu.size(), self.n))
+ raise ValueError("the size of the partition (={}) should be at most"
+ " the size of the permutations (={})".format(nu.size(), self.n))
l = []
i = 0
for nui in nu:
@@ -8307,7 +8305,7 @@ def _repr_(self):
sage: Permutations(descents=([1,0,4,8],12))
Standard permutations of 12 with descents [0, 1, 4, 8]
"""
- return "Standard permutations of %s with descents %s" % (self.n, list(self._d))
+ return f"Standard permutations of {self.n} with descents {list(self._d)}"
def cardinality(self):
"""
@@ -9239,8 +9237,7 @@ def _repr_(self):
sage: CyclicPermutationsOfPartition([[1,2,3,4],[5,6,7]])
Cyclic permutations of partition [[1, 2, 3, 4], [5, 6, 7]]
"""
- return "Cyclic permutations of partition {}".format(
- [list(_) for _ in self.partition])
+ return f"Cyclic permutations of partition {[list(_) for _ in self.partition]}"
def __iter__(self, distinct=False):
"""
@@ -9498,7 +9495,7 @@ def _repr_(self):
sage: Permutations(3, avoiding=[[2, 1, 3],[1,2,3]])
Standard permutations of 3 avoiding [[2, 1, 3], [1, 2, 3]]
"""
- return "Standard permutations of %s avoiding %s" % (self.n, list(self._a))
+ return f"Standard permutations of {self.n} avoiding {list(self._a)}"
def __iter__(self):
"""
diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py
index 33de9927c12..535787875b5 100644
--- a/src/sage/combinat/rigged_configurations/rigged_configurations.py
+++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py
@@ -1549,7 +1549,7 @@ def to_virtual(self, rc):
for a,rp in enumerate(rc):
g = gamma[a+1]
for i in sigma[a+1]:
- partitions[i-1] = RiggedPartition([row_len for row_len in rp._list],
+ partitions[i-1] = RiggedPartition(list(rp._list),
[rig_val*g for rig_val in rp.rigging],
[vac_num*g for vac_num in rp.vacancy_numbers])
return self.virtual.element_class(self.virtual, partitions, use_vacancy_numbers=True)
@@ -1585,7 +1585,7 @@ def from_virtual(self, vrc):
for a in range(n):
rp = vrc[sigma[a+1][0] - 1]
g = gamma[a+1]
- partitions[a] = RiggedPartition([row_len for row_len in rp._list],
+ partitions[a] = RiggedPartition(list(rp._list),
[rig_val//g for rig_val in rp.rigging],
[vac_val//g for vac_val in rp.vacancy_numbers])
return self.element_class(self, partitions, use_vacancy_numbers=True)
@@ -1874,7 +1874,7 @@ def to_virtual(self, rc):
for a,rp in enumerate(rc):
g = gammatilde[a+1]
for i in sigma[a+1]:
- partitions[i-1] = RiggedPartition([row_len for row_len in rp._list],
+ partitions[i-1] = RiggedPartition(list(rp._list),
[rig_val*g for rig_val in rp.rigging])
return self.virtual.element_class(self.virtual, partitions)
@@ -1910,7 +1910,7 @@ def from_virtual(self, vrc):
for a in range(n):
rp = vrc[sigma[a+1][0] - 1]
g = gammatilde[a+1]
- partitions[a] = RiggedPartition([row_len for row_len in rp._list],
+ partitions[a] = RiggedPartition(list(rp._list),
[rig_val/g for rig_val in rp.rigging])
return self.element_class(self, partitions)
diff --git a/src/sage/combinat/root_system/fundamental_group.py b/src/sage/combinat/root_system/fundamental_group.py
index 8905471a5ab..6dc13e302ec 100644
--- a/src/sage/combinat/root_system/fundamental_group.py
+++ b/src/sage/combinat/root_system/fundamental_group.py
@@ -393,7 +393,7 @@ def leading_support(beta):
cartan_type = cartan_type.dual()
if cartan_type.is_untwisted_affine():
cartan_type_classical = cartan_type.classical()
- I = [i for i in cartan_type_classical.index_set()]
+ I = list(cartan_type_classical.index_set())
Q = RootSystem(cartan_type_classical).root_lattice()
alpha = Q.simple_roots()
omega = RootSystem(cartan_type_classical).weight_lattice().fundamental_weights()
diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py
index 1dc8d16ad59..d5eb99adbb7 100644
--- a/src/sage/combinat/root_system/hecke_algebra_representation.py
+++ b/src/sage/combinat/root_system/hecke_algebra_representation.py
@@ -637,8 +637,8 @@ def Y_lambdacheck(self, lambdacheck):
# works for our two main examples (action of affine W on W,
# and Macdonald polynomials)
if self._side == "left":
- word = tuple([x for x in reversed(word)])
- signs = tuple([x for x in reversed(signs)])
+ word = tuple(reversed(word))
+ signs = tuple(reversed(signs))
# The power of q implements the fact that Y^\deltacheck = 1/q.
# The classical simple coroots have no \deltacheck term.
# alpha[0] has a \deltacheck with coefficient one
diff --git a/src/sage/combinat/root_system/plot.py b/src/sage/combinat/root_system/plot.py
index 5d591febe43..483c510eb61 100644
--- a/src/sage/combinat/root_system/plot.py
+++ b/src/sage/combinat/root_system/plot.py
@@ -1412,7 +1412,7 @@ def cone(self, rays=[], lines=[], color="black", thickness=1, alpha=1, wireframe
# TODO: we currently convert lines into rays, which simplify a
# bit the calculation of the intersection. But it would be
# nice to benefit from the new ``lines`` option of Polyhedra
- rays = list(rays) + [ray for ray in lines] + [-ray for ray in lines]
+ rays = list(rays) + list(lines) + [-ray for ray in lines]
# Compute the intersection at level 1, if needed
if self.level:
diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py
index 0b80aa73a71..3287b516d08 100644
--- a/src/sage/combinat/root_system/reflection_group_complex.py
+++ b/src/sage/combinat/root_system/reflection_group_complex.py
@@ -499,7 +499,7 @@ def distinguished_reflections(self):
"""
# makes sure that the simple reflections come first
gens = self.gens()
- R = [t for t in gens]
+ R = list(gens)
# Then import all distinguished reflections from gap,
# the Set is used as every such appears multiple times.
for r in self._gap_group.Reflections():
diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py
index ea1f4d285b2..f03d1d44e7f 100644
--- a/src/sage/combinat/root_system/root_lattice_realizations.py
+++ b/src/sage/combinat/root_system/root_lattice_realizations.py
@@ -18,6 +18,7 @@
from sage.misc.lazy_import import LazyImport
from sage.categories.coxeter_groups import CoxeterGroups
from sage.categories.category_types import Category_over_base_ring
+from sage.categories.enumerated_sets import EnumeratedSets
from sage.categories.modules_with_basis import ModulesWithBasis
from sage.structure.element import Element
from sage.sets.family import Family
@@ -715,7 +716,8 @@ def positive_roots(self, index_set=None):
index_set = tuple(self.cartan_type().index_set())
return RecursivelyEnumeratedSet([self.simple_root(i) for i in index_set],
attrcall('pred', index_set=index_set),
- structure='graded', enumeration='breadth')
+ structure='graded', enumeration='breadth',
+ category=EnumeratedSets().Finite())
@cached_method
def nonparabolic_positive_roots(self, index_set=None):
@@ -1229,7 +1231,7 @@ def negative_roots(self):
"""
if not self.cartan_type().is_finite():
raise ValueError("%s is not a finite Cartan type" % self.cartan_type())
- return self.positive_roots().map(attrcall('__neg__'))
+ return self.positive_roots().map(attrcall('__neg__'), is_injective=True)
##########################################################################
# coroots
@@ -4279,7 +4281,7 @@ def weyl_action(self, element, inverse=False):
# TODO, some day: accept an iterator
if isinstance(element, (tuple, list, range)):
# Action by a (reduced) word
- the_word = [x for x in element]
+ the_word = list(element)
I = self.parent().index_set()
if not all(i in I for i in the_word):
raise ValueError("Not all members of %s are in the index set of the %s" % (element, self.parent()))
diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py
index a375b3b8074..54d3c3d71f2 100644
--- a/src/sage/combinat/root_system/root_space.py
+++ b/src/sage/combinat/root_system/root_space.py
@@ -443,7 +443,7 @@ def max_quantum_element(self):
word = []
while self != Qvee.zero():
beta = self.max_coroot_le()
- word += [x for x in beta.associated_reflection()]
+ word += list(beta.associated_reflection())
self = self - beta.associated_coroot()
W = self.parent().weyl_group()
return (W.demazure_product(word)).reduced_word()
diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py
index d2d9d6385e5..aab1cf6d2bb 100644
--- a/src/sage/combinat/sf/sfa.py
+++ b/src/sage/combinat/sf/sfa.py
@@ -246,6 +246,10 @@ def is_SymmetricFunctionAlgebra(x):
sage: from sage.combinat.sf.sfa import is_SymmetricFunctionAlgebra
sage: is_SymmetricFunctionAlgebra(5)
+ doctest:warning...
+ DeprecationWarning: the function is_SymmetricFunctionAlgebra is deprecated;
+ use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead
+ See https://github.com/sagemath/sage/issues/37896 for details.
False
sage: is_SymmetricFunctionAlgebra(ZZ)
False
@@ -258,6 +262,8 @@ def is_SymmetricFunctionAlgebra(x):
sage: is_SymmetricFunctionAlgebra(SymmetricFunctions(FractionField(QQ['q','t'])).macdonald().P())
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37896, "the function is_SymmetricFunctionAlgebra is deprecated; use 'isinstance(..., SymmetricFunctionAlgebra_generic)' instead")
return isinstance(x, SymmetricFunctionAlgebra_generic)
diff --git a/src/sage/combinat/sf/witt.py b/src/sage/combinat/sf/witt.py
index c856e8fe399..cf14fa826c6 100644
--- a/src/sage/combinat/sf/witt.py
+++ b/src/sage/combinat/sf/witt.py
@@ -666,7 +666,7 @@ def _omega_even(self, lam_even):
True
"""
dct = self._h(self.monomial(lam_even)).monomial_coefficients(copy=False)
- eelt = self._e.element_class(self._e, {mu: c for mu, c in dct.items()})
+ eelt = self._e.element_class(self._e, dict(dct.items()))
return self(eelt)
class Element(multiplicative.SymmetricFunctionAlgebra_multiplicative.Element):
diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py
index a05a8b0bee8..b678c84171a 100644
--- a/src/sage/combinat/shifted_primed_tableau.py
+++ b/src/sage/combinat/shifted_primed_tableau.py
@@ -357,7 +357,7 @@ def _repr_list(self):
sage: ShiftedPrimedTableau([['2p',3],[2,2]], skew=[2])._repr_list()
"[(None, None, 2', 3), (2, 2)]"
"""
- return repr([row for row in self])
+ return repr(list(self))
def _repr_tab(self):
"""
diff --git a/src/sage/combinat/species/structure.py b/src/sage/combinat/species/structure.py
index b3003ed0f13..c5629212f5d 100644
--- a/src/sage/combinat/species/structure.py
+++ b/src/sage/combinat/species/structure.py
@@ -37,8 +37,10 @@
#
# http://www.gnu.org/licenses/
#*****************************************************************************
-from sage.combinat.combinat import CombinatorialClass, CombinatorialObject
+from sage.categories.enumerated_sets import EnumeratedSets
+from sage.combinat.combinat import CombinatorialObject
from sage.rings.integer import Integer
+from sage.structure.parent import Parent
from copy import copy
@@ -326,7 +328,7 @@ def change_labels(self, labels):
##############################################################
-class SpeciesWrapper(CombinatorialClass):
+class SpeciesWrapper(Parent):
def __init__(self, species, labels, iterator, generating_series, name, structure_class):
"""
This is a abstract base class for the set of structures of a
@@ -350,6 +352,7 @@ def __init__(self, species, labels, iterator, generating_series, name, structure
sage: S.cardinality()
1
"""
+ Parent.__init__(self, category=EnumeratedSets().Finite())
self._species = species
self._labels = labels
self._iterator = iterator
@@ -357,6 +360,46 @@ def __init__(self, species, labels, iterator, generating_series, name, structure
self._name = "%s for %s with labels %s" % (name, species, labels)
self._structure_class = structure_class if structure_class is not None else species._default_structure_class
+ def __eq__(self, other) -> bool:
+ r"""
+ EXAMPLES::
+
+ sage: from sage.combinat.species.structure import SpeciesWrapper
+ sage: F = species.SetSpecies()
+ sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None)
+ sage: S == SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None)
+ True
+ """
+ return ((self._species, self._labels,
+ self._iterator, self._generating_series,
+ self._name, self._structure_class) == (other._species, other._labels,
+ other._iterator, other._generating_series,
+ other._name, other._structure_class))
+
+ def __ne__(self, other) -> bool:
+ r"""
+ EXAMPLES::
+
+ sage: from sage.combinat.species.structure import SpeciesWrapper
+ sage: F = species.SetSpecies()
+ sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None)
+ sage: S != SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None)
+ False
+ """
+ return not (self == other)
+
+ def _repr_(self) -> str:
+ """
+ EXAMPLES::
+
+ sage: from sage.combinat.species.structure import SpeciesWrapper
+ sage: F = species.SetSpecies()
+ sage: S = SpeciesWrapper(F, [1,2,3], "_structures", "generating_series", 'Structures', None)
+ sage: repr(S) # indirect doctest
+ 'Structures for Set species with labels [1, 2, 3]'
+ """
+ return self._name
+
def labels(self):
"""
Returns the labels used on these structures. If `X` is the
diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py
index 3b7673a5425..f79413e2be3 100644
--- a/src/sage/combinat/symmetric_group_algebra.py
+++ b/src/sage/combinat/symmetric_group_algebra.py
@@ -1244,6 +1244,100 @@ def _blocks_dictionary(self):
blocks[c] = [la]
return blocks
+ def ladder_idemponent(self, la):
+ r"""
+ Return the ladder idempontent of ``self``.
+
+ Let `F` be a field of characteristic `p`. The *ladder idempotent*
+ of shape `\lambda` is the idempotent of `F[S_n]` defined as follows.
+ Let `T` be the :meth:`ladder tableau
+ ` of shape `\lambda`.
+ Let `[T]` be the set of standard tableaux whose residue sequence
+ is the same as for `T`. Let `\alpha` be the sizes of the ladders
+ of `\lambda`. Then the ladder idempontent is constructed as
+
+ .. MATH::
+
+ \widetilde{e}_{\lambda} := \frac{1}{\alpha!}
+ \left( \sum_{\sigma \in S_{\alpha}} \sigma \right)
+ \left( \overline{\sum_{U \in [T]} E_U} \right),
+
+ where `E_{UU}` is the :meth:`seminormal_basis` element over `\QQ`
+ and we project the sum to `F`, `S_{\alpha}` is the Young subgroup
+ corresponding to `\alpha`, and `\alpha! = \alpha_1! \cdots \alpha_k!`.
+
+ EXAMPLES::
+
+ sage: SGA = SymmetricGroupAlgebra(GF(3), 4)
+ sage: for la in Partitions(SGA.n):
+ ....: idem = SGA.ladder_idemponent(la)
+ ....: print(la)
+ ....: print(idem)
+ ....: assert idem^2 == idem
+ [4]
+ 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3]
+ + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1]
+ [3, 1]
+ 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3]
+ + [3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + [4, 3, 2, 1]
+ [2, 2]
+ 2*[1, 2, 3, 4] + 2*[1, 2, 4, 3] + 2*[2, 1, 3, 4] + 2*[2, 1, 4, 3]
+ + 2*[3, 4, 1, 2] + 2*[3, 4, 2, 1] + 2*[4, 3, 1, 2] + 2*[4, 3, 2, 1]
+ [2, 1, 1]
+ 2*[1, 2, 3, 4] + [1, 2, 4, 3] + 2*[1, 3, 2, 4] + [1, 3, 4, 2]
+ + [1, 4, 2, 3] + 2*[1, 4, 3, 2] + 2*[2, 1, 3, 4] + [2, 1, 4, 3]
+ + 2*[2, 3, 1, 4] + [2, 3, 4, 1] + [2, 4, 1, 3] + 2*[2, 4, 3, 1]
+ + 2*[3, 1, 2, 4] + [3, 1, 4, 2] + 2*[3, 2, 1, 4] + [3, 2, 4, 1]
+ + [4, 1, 2, 3] + 2*[4, 1, 3, 2] + [4, 2, 1, 3] + 2*[4, 2, 3, 1]
+ [1, 1, 1, 1]
+ 2*[1, 2, 3, 4] + [1, 2, 4, 3] + [2, 1, 3, 4] + 2*[2, 1, 4, 3]
+ + 2*[3, 4, 1, 2] + [3, 4, 2, 1] + [4, 3, 1, 2] + 2*[4, 3, 2, 1]
+
+ When `p = 0`, these idempotents will generate all of the simple
+ modules (which are the :meth:`Specht modules `
+ and also projective modules)::
+
+ sage: SGA = SymmetricGroupAlgebra(QQ, 5)
+ sage: for la in Partitions(SGA.n):
+ ....: idem = SGA.ladder_idemponent(la)
+ ....: assert idem^2 == idem
+ ....: print(la, SGA.principal_ideal(idem).dimension())
+ [5] 1
+ [4, 1] 4
+ [3, 2] 5
+ [3, 1, 1] 6
+ [2, 2, 1] 5
+ [2, 1, 1, 1] 4
+ [1, 1, 1, 1, 1] 1
+ sage: [StandardTableaux(la).cardinality() for la in Partitions(SGA.n)]
+ [1, 4, 5, 6, 5, 4, 1]
+
+ REFERENCES:
+
+ - [Ryom2015]_
+ """
+ R = self.base_ring()
+ p = R.characteristic()
+ n = self.n
+ if not p:
+ p = n + 1
+ la = _Partitions(la)
+ if sum(la) != n:
+ raise ValueError(f"{la} is not a partition of {n}")
+ Tlad, alpha = la.ladder_tableau(p, ladder_lengths=True)
+ if not all(val < p for val in alpha):
+ raise ValueError(f"{la} is not {p}-ladder restricted")
+ Tclass = Tlad.residue_sequence(p).standard_tableaux()
+ Elad = sum(epsilon_ik(T, T) for T in Tclass)
+ Elad = self.element_class(self, {sigma: R(c) for sigma, c in Elad._monomial_coefficients.items()})
+ from sage.groups.perm_gps.permgroup_named import SymmetricGroup
+ YG = SymmetricGroup(n).young_subgroup(alpha)
+ coeff = ~R.prod(factorial(val) for val in alpha)
+ G = self.group()
+ eprod = self.element_class(self, {G(list(elt.tuple())): coeff
+ for elt in YG})
+ return Elad * eprod
+
@cached_method
def algebra_generators(self):
r"""
diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py
index bb1aeaf28be..fda9458f0f2 100644
--- a/src/sage/combinat/tableau.py
+++ b/src/sage/combinat/tableau.py
@@ -1381,7 +1381,7 @@ def schuetzenberger_involution(self, n=None, check=True):
t = t.bump(k)
if isinstance(self, StandardTableau):
return StandardTableau(list(t))
- elif isinstance(self, SemistandardTableau):
+ if isinstance(self, SemistandardTableau):
return SemistandardTableau(list(t))
return t
@@ -1910,7 +1910,7 @@ def vertical_flip(self):
if not self.is_rectangular():
raise TypeError("the tableau must be rectangular to use vertical_flip()")
- return Tableau([row for row in reversed(self)])
+ return Tableau(list(reversed(self)))
def rotate_180(self):
"""
@@ -1926,7 +1926,7 @@ def rotate_180(self):
if not self.is_rectangular():
raise TypeError("the tableau must be rectangular to use rotate_180()")
- return Tableau([[rline for rline in reversed(row)] for row in reversed(self)])
+ return Tableau([list(reversed(row)) for row in reversed(self)])
def cells(self):
"""
@@ -2447,7 +2447,7 @@ def insert_word(self, w, left=False):
[[1, 3], [2, 5], [4]]
"""
if left:
- w = [i for i in reversed(w)]
+ w = list(reversed(w))
res = self
for i in w:
res = res.schensted_insert(i, left=left)
@@ -3891,7 +3891,7 @@ def flush(self):
for s in S:
if (s[0][0] != len(self)-1 and s[1] == len(self[s[0][0]+1])
and self[s[0][0]+1][-1] <= s[0][1]) \
- or (s[0][0] == len(self)-1 and s[1] == 0):
+ or (s[0][0] == len(self)-1 and s[1] == 0):
f += 1
else:
for t in S:
@@ -4491,7 +4491,7 @@ def __classcall_private__(self, t):
"""
if isinstance(t, SemistandardTableau):
return t
- elif t in SemistandardTableaux():
+ if t in SemistandardTableaux():
return SemistandardTableaux_all().element_class(SemistandardTableaux_all(), t)
# t is not a semistandard tableau so we give an appropriate error message
@@ -4676,9 +4676,9 @@ def check(self):
super().check()
# We have checked that t is tableau, so it remains to check that
# the entries of t are positive integers that increase along rows.
- flatx = sorted(sum((list(row) for row in self), []))
+ flatx = sorted(c for row in self for c in row)
if (flatx != list(range(1, len(flatx)+1))
- or any(row[i] >= row[i+1] for row in self for i in range(len(row)-1))):
+ or any(row[i] >= row[i+1] for row in self for i in range(len(row)-1))):
raise ValueError("the entries in a row standard tableau must increase"
" along rows and contain the numbers 1,2,...,n")
@@ -5657,13 +5657,13 @@ class options(GlobalOptions):
alias=dict(array="diagram", ferrers_diagram="diagram", young_diagram="diagram"),
case_sensitive=False)
convention = dict(default="English",
- description='Sets the convention used for displaying tableaux and partitions',
- values=dict(
- English='use the English convention',
- French='use the French convention',
- Russian='use the Russian convention',
- ),
- case_sensitive=False)
+ description='Sets the convention used for displaying tableaux and partitions',
+ values=dict(
+ English='use the English convention',
+ French='use the French convention',
+ Russian='use the Russian convention',
+ ),
+ case_sensitive=False)
notation = dict(alt_name="convention")
def _element_constructor_(self, t):
@@ -5726,7 +5726,7 @@ def __contains__(self, x):
from sage.combinat.partition import _Partitions
if isinstance(x, Tableau):
return True
- elif isinstance(x, list):
+ if isinstance(x, list):
try:
for row in x:
iter(row)
@@ -6062,7 +6062,7 @@ def __classcall_private__(cls, *args, **kwargs):
if size is not None:
if not isinstance(size, (int, Integer)):
raise ValueError("size must be an integer")
- elif size < 0:
+ if size < 0:
raise ValueError("size must be non-negative")
if shape is not None:
@@ -6269,9 +6269,9 @@ def __contains__(self, t):
return (self.max_entry is None or
len(t) == 0 or
max(max(row) for row in t) <= self.max_entry)
- elif not t:
+ if not t:
return True
- elif Tableaux.__contains__(self, t):
+ if Tableaux.__contains__(self, t):
for row in t:
if not all(c > 0 for c in row):
return False
@@ -6648,7 +6648,7 @@ def random_element(self):
tot += weights[pos]
# we now have pos elements over the diagonal and n - 2 * pos on it
m = diagonal_matrix(list(IntegerVectors(self.size - 2 * pos,
- self.max_entry).random_element()))
+ self.max_entry).random_element()))
above_diagonal = list(IntegerVectors(pos, kchoose2m1 + 1).random_element())
index = 0
for i in range(self.max_entry - 1):
@@ -7309,10 +7309,10 @@ def __contains__(self, x):
"""
if isinstance(x, RowStandardTableau):
return True
- elif Tableaux.__contains__(self, x):
- flatx = sorted(sum((list(row) for row in x), []))
+ if Tableaux.__contains__(self, x):
+ flatx = sorted(c for row in x for c in row)
return (flatx == list(range(1, len(flatx)+1))
- and all(row[i] < row[i+1] for row in x for i in range(len(row)-1)))
+ and all(row[i] < row[i+1] for row in x for i in range(len(row)-1)))
return False
@@ -7561,7 +7561,7 @@ def cardinality(self):
sage: RowStandardTableaux([]).cardinality()
1
"""
- return Integer(multinomial([m for m in self.shape]))
+ return Integer(multinomial(list(self.shape)))
########################
@@ -7710,12 +7710,12 @@ def __contains__(self, x):
if isinstance(x, StandardTableau):
return True
elif Tableaux.__contains__(self, x):
- flatx = sorted(sum((list(row) for row in x), []))
+ flatx = sorted(c for row in x for c in row)
return flatx == list(range(1, len(flatx)+1)) and (len(x) == 0 or
(all(row[i] < row[i+1] for row in x for i in range(len(row)-1)) and
- all(x[r][c] < x[r+1][c] for r in range(len(x)-1)
- for c in range(len(x[r+1])))
- ))
+ all(x[r][c] < x[r+1][c] for r in range(len(x)-1)
+ for c in range(len(x[r+1])))
+ ))
return False
@@ -7936,7 +7936,7 @@ def random_element(self):
# We add the number of involutions with ``fixed_point_number``
# fixed points.
partial_sum += binomial(self.size, fixed_point_number) * \
- prod(range(1, self.size - fixed_point_number, 2))
+ prod(range(1, self.size - fixed_point_number, 2))
# If the partial sum is greater than the involution index,
# then the random involution that we want to generate has
# ``fixed_point_number`` fixed points.
@@ -7954,7 +7954,7 @@ def random_element(self):
matching = PerfectMatchings(set(range(1, self.size + 1))
- set(fixed_point_positions)).random_element()
permutation_cycle_rep = ([(fixed_point,) for fixed_point in fixed_point_positions]
- + [(a, b) for a, b in matching])
+ + [tuple(ab) for ab in matching])
return from_cycles(self.size, permutation_cycle_rep).robinson_schensted()[0]
@@ -8198,7 +8198,7 @@ def list(self):
[[1, 2, 4], [3, 5], [6]],
[[1, 2, 3], [4, 5], [6]]]
"""
- return [y for y in self]
+ return list(self)
def random_element(self):
"""
diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py
index 1bdd38e92c7..1b5dcb3b03e 100644
--- a/src/sage/combinat/words/finite_word.py
+++ b/src/sage/combinat/words/finite_word.py
@@ -293,7 +293,7 @@ def _repr_(self):
if word_options['old_repr']:
if word_options['truncate'] and \
self.length() > word_options['truncate_length']:
- return "Finite word of length %s over %s" % (self.length(), str(self.parent().alphabet())[17:])
+ return "Finite word of length {} over {}".format(self.length(), str(self.parent().alphabet())[17:])
return word_options['identifier'] + self.string_rep()
def coerce(self, other):
@@ -331,7 +331,7 @@ def coerce(self, other):
self = other.parent()(self)
self.parent()._check(self, length=None)
except Exception:
- raise TypeError("no coercion rule between %r and %r" % (self.parent(), other.parent()))
+ raise TypeError("no coercion rule between {!r} and {!r}".format(self.parent(), other.parent()))
return self, other
def __hash__(self):
@@ -1261,7 +1261,7 @@ def number_of_factors(self, n=None, algorithm='suffix tree'):
elif algorithm == 'naive':
return ZZ(len(self.factor_set(n, algorithm='naive')))
else:
- raise ValueError('Unknown algorithm (={})'.format(algorithm))
+ raise ValueError(f'Unknown algorithm (={algorithm})')
def factor_iterator(self, n=None):
r"""
@@ -1437,7 +1437,7 @@ def factor_set(self, n=None, algorithm='suffix tree'):
S.add(self[i:i+n])
return Set(S)
else:
- raise ValueError('Unknown algorithm (={})'.format(algorithm))
+ raise ValueError(f'Unknown algorithm (={algorithm})')
def topological_entropy(self, n):
r"""
@@ -2067,8 +2067,7 @@ def _conjugates_list(self):
[word: a]
"""
S = [self]
- for i in range(1, self.length()):
- S.append(self.conjugate(i))
+ S.extend(self.conjugate(i) for i in range(1, self.length()))
return S
def conjugates_iterator(self):
@@ -2853,9 +2852,9 @@ def length_maximal_palindrome(self, j, m=None, f=None):
# Initialize the next (left) position to check
i = (jj - m - 1) / 2
if not i.is_integer():
- raise ValueError("(2*j-m-1)/2(={}) must be an integer, i.e., "
- "2*j(={}) and m(={}) can't "
- "have the same parity".format(i, jj, m))
+ raise ValueError(f"(2*j-m-1)/2(={i}) must be an integer, i.e., "
+ f"2*j(={jj}) and m(={m}) can't "
+ "have the same parity")
i = Integer(i)
# Compute
@@ -2970,8 +2969,7 @@ def lps_lengths(self, f=None):
for j in range(1, 2 * len(self) + 1):
Nj = j + LPC[j]
if Nj > Nk:
- for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2):
- LPS.append(i - j)
+ LPS.extend(i - j for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2))
Nk = Nj
return LPS
@@ -3858,8 +3856,7 @@ def subword_complementaries(self, other):
if len(m) == 1:
temp.append([j]+m[0])
if len(m) > 1:
- for sw in m:
- temp.append([j]+sw)
+ temp.extend([j] + sw for sw in m)
Mpos[i][j] = temp
# Create the list of positions for occurrences of `self` as a subword
@@ -3875,7 +3872,7 @@ def subword_complementaries(self, other):
comp_words.append(Word([other[i] for i in comp_pos]))
return comp_words
- def is_lyndon(self):
+ def is_lyndon(self) -> bool:
r"""
Return ``True`` if ``self`` is a Lyndon word, and ``False``
otherwise.
diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py
index 9424a3370b7..46207beab1c 100644
--- a/src/sage/combinat/words/morphism.py
+++ b/src/sage/combinat/words/morphism.py
@@ -883,18 +883,15 @@ def _latex_(self):
A = self.domain().alphabet()
latex_layout = self.latex_layout()
if latex_layout == 'oneliner':
- L = [r"%s \mapsto %s" % (a, self.image(a)) for a in A]
- return LatexExpr(r','.join(L))
- elif latex_layout == 'array':
+ lines = (fr"{a} \mapsto {self.image(a)}" for a in A)
+ return LatexExpr(r','.join(lines))
+ if latex_layout == 'array':
s = r"\begin{array}{l}" + '\n'
- lines = []
- for a in A:
- lines.append(r"%s \mapsto %s" % (a, self.image(a)))
+ lines = (fr"{a} \mapsto {self.image(a)}" for a in A)
s += '\\\\\n'.join(lines)
s += '\n' + r"\end{array}"
return LatexExpr(s)
- else:
- raise ValueError('unknown latex_layout(=%s)' % latex_layout)
+ raise ValueError('unknown latex_layout(=%s)' % latex_layout)
def __mul__(self, other):
r"""
@@ -1693,8 +1690,7 @@ def is_prolongable(self, letter):
TypeError: codomain of self must be an instance of Words
"""
if letter not in self.domain().alphabet():
- raise TypeError("letter (=%s) is not in the domain alphabet (=%s)"
- % (letter, self.domain().alphabet()))
+ raise TypeError("letter (={}) is not in the domain alphabet (={})".format(letter, self.domain().alphabet()))
image = self.image(letter)
return not image.is_empty() and letter == image[0]
@@ -1893,11 +1889,9 @@ def fixed_points(self):
[]
"""
- L = []
- for letter in self.domain().alphabet():
- if self.is_prolongable(letter=letter):
- L.append(self.fixed_point(letter=letter))
- return L
+ return [self.fixed_point(letter=letter)
+ for letter in self.domain().alphabet()
+ if self.is_prolongable(letter=letter)]
def periodic_point(self, letter):
r"""
diff --git a/src/sage/combinat/words/paths.py b/src/sage/combinat/words/paths.py
index f5506152d44..21709124f64 100644
--- a/src/sage/combinat/words/paths.py
+++ b/src/sage/combinat/words/paths.py
@@ -1489,11 +1489,11 @@ def is_tangent(self):
class FiniteWordPath_2d(FiniteWordPath_all):
- def plot(self, pathoptions=dict(rgbcolor='red', thickness=3),
- fill=True, filloptions=dict(rgbcolor='red', alpha=0.2),
- startpoint=True, startoptions=dict(rgbcolor='red', pointsize=100),
- endarrow=True, arrowoptions=dict(rgbcolor='red', arrowsize=20, width=3),
- gridlines=False, gridoptions=dict()):
+ def plot(self, pathoptions={"rgbcolor": 'red', "thickness": 3},
+ fill=True, filloptions={"rgbcolor": 'red', "alpha": 0.2},
+ startpoint=True, startoptions={"rgbcolor": 'red', "pointsize": 100},
+ endarrow=True, arrowoptions={"rgbcolor": 'red', "arrowsize": 20, "width": 3},
+ gridlines=False, gridoptions={}):
r"""
Return a 2d Graphics illustrating the path.
@@ -1698,7 +1698,7 @@ def animate(self):
return animate(images, **kwds)
- def plot_directive_vector(self, options=dict(rgbcolor='blue')):
+ def plot_directive_vector(self, options={"rgbcolor": 'blue'}):
r"""
Return an arrow 2d graphics that goes from the start of the path
to the end.
@@ -2003,8 +2003,8 @@ def ymax(self):
class FiniteWordPath_3d(FiniteWordPath_all):
- def plot(self, pathoptions=dict(rgbcolor='red', arrow_head=True, thickness=3),
- startpoint=True, startoptions=dict(rgbcolor='red', size=10)):
+ def plot(self, pathoptions={"rgbcolor": 'red', "arrow_head": True, "thickness": 3},
+ startpoint=True, startoptions={"rgbcolor": 'red', "size": 10}):
r"""
INPUT:
diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py
index 8c56ba3d4b3..c2bda4caf96 100644
--- a/src/sage/combinat/words/suffix_trees.py
+++ b/src/sage/combinat/words/suffix_trees.py
@@ -1740,7 +1740,7 @@ def treat_node(current_node, parent):
D = self.transition_function_dictionary()
string_depth = {0: 0}
n = len(self.word())
- labeling = dict()
+ labeling = {}
treat_node(0, None)
return labeling
@@ -1827,7 +1827,7 @@ def treat_node(current_node, i, j):
walk_chain(current_node, child, l, square_start)
prelabeling = self._partial_labeling()
- labeling = dict()
+ labeling = {}
D = self.transition_function_dictionary()
treat_node(0, 0, 0)
return labeling
diff --git a/src/sage/combinat/yang_baxter_graph.py b/src/sage/combinat/yang_baxter_graph.py
index f2fdb7a5a47..6b7684e077a 100644
--- a/src/sage/combinat/yang_baxter_graph.py
+++ b/src/sage/combinat/yang_baxter_graph.py
@@ -166,7 +166,7 @@ def _successors(self, u):
if v != u:
yield (v, op)
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
EXAMPLES::
@@ -176,7 +176,7 @@ def __repr__(self):
sage: Y.__repr__()
'Yang-Baxter graph with root vertex (1, 2, 3)'
"""
- return "Yang-Baxter graph with root vertex %s" % (self._root,)
+ return f"Yang-Baxter graph with root vertex {self._root}"
@lazy_attribute
def _digraph(self):
@@ -216,7 +216,7 @@ def __hash__(self):
# used in containers but are mutable.
return hash(self._digraph.copy(immutable=True))
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
r"""
EXAMPLES::
@@ -238,7 +238,7 @@ def __eq__(self, other):
"""
return type(self) is type(other) and self._digraph == other._digraph
- def __ne__(self, other):
+ def __ne__(self, other) -> bool:
r"""
Test non-equality.
@@ -369,7 +369,7 @@ def root(self):
"""
return self._root
- def successors(self, v):
+ def successors(self, v) -> list:
r"""
Return the successors of the vertex ``v``.
@@ -405,7 +405,7 @@ def plot(self, *args, **kwds):
kwds["vertex_labels"] = True
return self._digraph.plot(*args, **kwds)
- def vertices(self, sort=False):
+ def vertices(self, sort=False) -> list:
r"""
Return the vertices of ``self``.
@@ -439,7 +439,7 @@ def edges(self):
"""
return self._digraph.edges(sort=True)
- def vertex_relabelling_dict(self, v, relabel_operator):
+ def vertex_relabelling_dict(self, v, relabel_operator) -> dict:
r"""
Return a dictionary pairing vertices ``u`` of ``self`` with
the object obtained from ``v`` by applying the
@@ -590,7 +590,7 @@ def __init__(self, partition):
for i in range(sum(partition) - 1)]
super().__init__(root, operators)
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
EXAMPLES::
@@ -598,7 +598,7 @@ def __repr__(self):
sage: Y.__repr__() # needs sage.combinat
'Yang-Baxter graph of [3, 2], with top vertex (1, 0, 2, 1, 0)'
"""
- return "Yang-Baxter graph of %s, with top vertex %s" % (self._partition, self._root)
+ return f"Yang-Baxter graph of {self._partition}, with top vertex {self._root}"
def __copy__(self):
r"""
@@ -695,7 +695,7 @@ def _swap_operator(self, operator, u):
"""
return operator(u)
- def vertex_relabelling_dict(self, v):
+ def vertex_relabelling_dict(self, v) -> dict:
r"""
Return a dictionary pairing vertices ``u`` of ``self`` with the object
obtained from ``v`` by applying transpositions corresponding to the
@@ -792,7 +792,7 @@ def __hash__(self):
"""
return hash(self._position)
- def __eq__(self, other):
+ def __eq__(self, other) -> bool:
r"""
Compare two swap operators.
@@ -811,7 +811,7 @@ def __eq__(self, other):
return False
return self._position == other._position
- def __ne__(self, other):
+ def __ne__(self, other) -> bool:
"""
Check whether ``self`` is not equal to ``other``.
@@ -826,7 +826,7 @@ def __ne__(self, other):
"""
return not (self == other)
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
Representation string.
@@ -840,7 +840,7 @@ def __repr__(self):
pos = self._position
return f"Swap positions {pos} and {pos + 1}"
- def __str__(self):
+ def __str__(self) -> str:
r"""
A short str representation (used, for example, in labelling edges of
graphs).
@@ -889,7 +889,7 @@ def position(self):
class SwapIncreasingOperator(SwapOperator):
- def __repr__(self):
+ def __repr__(self) -> str:
r"""
Representation string.
diff --git a/src/sage/data_structures/bitset.pyx b/src/sage/data_structures/bitset.pyx
index 8a5e91c0a9a..e90ffea8fca 100644
--- a/src/sage/data_structures/bitset.pyx
+++ b/src/sage/data_structures/bitset.pyx
@@ -72,7 +72,7 @@ cdef class FrozenBitset:
- string -- If a nonempty string, then the bitset is initialized by
including an element if the index of the string is ``1``. If the
- string is empty, then raise a ``ValueError``.
+ string is empty, then raise a :class:`ValueError`.
- iterable -- If an iterable, then it is assumed to contain a list of
nonnegative integers and those integers are placed in the set.
@@ -1798,8 +1798,10 @@ cdef class Bitset(FrozenBitset):
cpdef remove(self, unsigned long n):
"""
- Update the bitset by removing ``n``. Raises ``KeyError`` if ``n`` is
- not contained in the bitset.
+ Update the bitset by removing ``n``.
+
+ This raises a :class:`KeyError` if ``n`` is not contained
+ in the bitset.
EXAMPLES::
@@ -1871,8 +1873,9 @@ cdef class Bitset(FrozenBitset):
cpdef pop(self):
"""
- Remove and return an arbitrary element from the set. Raises
- ``KeyError`` if the set is empty.
+ Remove and return an arbitrary element from the set.
+
+ This raises a :class:`KeyError` if the set is empty.
EXAMPLES::
@@ -1899,7 +1902,7 @@ cdef class Bitset(FrozenBitset):
cpdef clear(self):
"""
- Removes all elements from the bitset.
+ Remove all elements from the bitset.
EXAMPLES::
diff --git a/src/sage/data_structures/bitset_base.pxd b/src/sage/data_structures/bitset_base.pxd
index df40d666d86..f8949d05e78 100644
--- a/src/sage/data_structures/bitset_base.pxd
+++ b/src/sage/data_structures/bitset_base.pxd
@@ -455,7 +455,9 @@ cdef inline bint bitset_not_in(fused_bitset_t bits, mp_bitcnt_t n) noexcept:
cdef inline bint bitset_remove(fused_bitset_t bits, mp_bitcnt_t n) except -1:
"""
- Remove n from bits. Raise KeyError if n is not contained in bits.
+ Remove ``n`` from ``bits``.
+
+ This raises a :class:`KeyError` if ``n`` is not contained in ``bits``.
"""
if not bitset_in(bits, n):
raise KeyError(n)
@@ -558,8 +560,9 @@ cdef inline long bitset_first_in_complement(fused_bitset_t a) noexcept:
cdef inline long bitset_pop(fused_bitset_t a) except -1:
"""
- Remove and return an arbitrary element from the set. Raise
- KeyError if the set is empty.
+ Remove and return an arbitrary element from the set.
+
+ This raises a :class:`KeyError` if the set is empty.
"""
cdef long i = bitset_first(a)
if i == -1:
diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py
index 03b5a7019c2..72ec82d3bb4 100644
--- a/src/sage/data_structures/mutable_poset.py
+++ b/src/sage/data_structures/mutable_poset.py
@@ -2299,21 +2299,21 @@ def remove(self, key, raise_key_error=True):
- ``key`` -- the key of an object.
- ``raise_key_error`` -- (default: ``True``) switch raising
- ``KeyError`` on and off.
+ :class:`KeyError` on and off.
OUTPUT:
Nothing.
If the element is not a member and ``raise_key_error`` is set
- (default), raise a ``KeyError``.
+ (default), raise a :class:`KeyError`.
.. NOTE::
As with Python's ``set``, the methods :meth:`remove`
and :meth:`discard` only differ in their behavior when an
element is not contained in the poset: :meth:`remove`
- raises a ``KeyError`` whereas :meth:`discard` does not
+ raises a :class:`KeyError` whereas :meth:`discard` does not
raise any exception.
This default behavior can be overridden with the
@@ -2479,21 +2479,21 @@ def discard(self, key, raise_key_error=False):
- ``key`` -- the key of an object.
- ``raise_key_error`` -- (default: ``False``) switch raising
- ``KeyError`` on and off.
+ :class:`KeyError` on and off.
OUTPUT:
Nothing.
If the element is not a member and ``raise_key_error`` is set
- (not default), raise a ``KeyError``.
+ (not default), raise a :class:`KeyError`.
.. NOTE::
As with Python's ``set``, the methods :meth:`remove`
and :meth:`discard` only differ in their behavior when an
element is not contained in the poset: :meth:`remove`
- raises a ``KeyError`` whereas :meth:`discard` does not
+ raises a :class:`KeyError` whereas :meth:`discard` does not
raise any exception.
This default behavior can be overridden with the
diff --git a/src/sage/databases/conway.py b/src/sage/databases/conway.py
index b1f76292ce8..54912a2471c 100644
--- a/src/sage/databases/conway.py
+++ b/src/sage/databases/conway.py
@@ -138,11 +138,16 @@ def __len__(self):
"""
Return the number of polynomials in this database.
- TESTS::
+ TESTS:
+
+ The database currently contains `35357` polynomials, but due to
+ :issue:`35357` it will be extended by Conway polynomials of
+ degrees `1`, `2` and `3` for primes between `65537` and `110000`,
+ thus leading to a new total of `47090` entries::
sage: c = ConwayPolynomials()
- sage: len(c)
- 35357
+ sage: len(c) in [35357, 47090]
+ True
"""
try:
return self._len
diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py
index 172d212fc97..169f2e7c31a 100644
--- a/src/sage/databases/cremona.py
+++ b/src/sage/databases/cremona.py
@@ -953,13 +953,14 @@ def data_from_coefficients(self, ainvs):
def elliptic_curve_from_ainvs(self, ainvs):
"""
- Return the elliptic curve in the database of with minimal
- ``ainvs``, if it exists, or raises a ``RuntimeError`` exception
- otherwise.
+ Return the elliptic curve in the database of with minimal ``ainvs``
+ if it exists.
+
+ This raises a :class:`RuntimeError` exception otherwise.
INPUT:
- - ``ainvs`` - list (5-tuple of int's); the minimal
+ - ``ainvs`` -- list (5-tuple of int's); the minimal
Weierstrass model for an elliptic curve
OUTPUT: EllipticCurve
diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py
index 767a9978a87..ba78f9bc2a6 100644
--- a/src/sage/doctest/control.py
+++ b/src/sage/doctest/control.py
@@ -608,8 +608,8 @@ def second_on_modern_computer(self):
Float. The wall time on your computer that would be equivalent
to one second on a modern computer. Unless you have kick-ass
- hardware this should always be >= 1.0. Raises a
- ``RuntimeError`` if there are no stored timings to use as
+ hardware this should always be >= 1.0. This raises a
+ :class:`RuntimeError` if there are no stored timings to use as
benchmark.
EXAMPLES::
@@ -1577,8 +1577,9 @@ def run(self):
self.log("Features detected for doctesting: "
+ ','.join(available_software.seen()))
if self.options.hidden_features:
- features_hidden = [f.name for f in self.options.hidden_features if f.unhide()]
- self.log("Features that have been hidden: " + ','.join(features_hidden))
+ for f in self.options.hidden_features:
+ f.unhide()
+ self.log("Features that have been hidden: " + ','.join(available_software.hidden()))
self.cleanup()
return self.reporter.error_status
diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py
index 85eefc27119..4628db45f5c 100644
--- a/src/sage/doctest/external.py
+++ b/src/sage/doctest/external.py
@@ -429,6 +429,7 @@ def __init__(self):
self._features = sorted(features, key=lambda feature: feature.name)
self._indices = {feature.name: idx for idx, feature in enumerate(self._features)}
self._seen = Array('i', len(self._features)) # initialized to zeroes
+ self._hidden = Array('i', len(self._features)) # initialized to zeroes
def __contains__(self, item):
"""
@@ -444,19 +445,26 @@ def __contains__(self, item):
idx = self._indices[item]
except KeyError:
return False
- if not self._seen[idx]:
- if not self._allow_external and self._features[idx] in self._external_features:
- self._seen[idx] = -1 # not available
- elif self._features[idx].is_present():
- self._seen[idx] = 1 # available
+ feature = self._features[idx]
+ if feature.is_hidden():
+ if not self._hidden[idx]:
+ self._hidden[idx] = 1
+ available = False # a hidden feature is considered to be not available
+ else:
+ if not self._allow_external and feature in self._external_features:
+ # an external feature is considered to be not available
+ # if this is not allowed
+ available = False
+ elif feature.is_present():
+ available = True
else:
- self._seen[idx] = -1 # not available
- if self._seen[idx] == 1:
+ available = False
+ if available:
+ if not self._seen[idx]:
+ self._seen[idx] = 1
return True
- elif self._seen[idx] == -1:
- return False
else:
- raise AssertionError("Invalid value for self.seen")
+ return False
def issuperset(self, other):
"""
@@ -495,5 +503,29 @@ def seen(self):
for feature, seen in zip(self._features, self._seen)
if seen > 0]
+ def hidden(self):
+ """
+ Return the list of detected hidden external software.
+
+ EXAMPLES::
+
+ sage: # needs conway_polynomials database_cremona_mini_ellcurve database_ellcurves database_graphs
+ sage: from sage.doctest.external import available_software
+ sage: from sage.features.databases import all_features
+ sage: for f in all_features():
+ ....: f.hide()
+ ....: if f._spkg_type() == 'standard':
+ ....: test = f.name in available_software
+ ....: f.unhide()
+ sage: sorted(available_software.hidden())
+ [...'conway_polynomials',...
+ 'database_cremona_mini_ellcurve',...
+ 'database_ellcurves',...
+ 'database_graphs'...]
+ """
+ return [feature.name
+ for feature, hidden in zip(self._features, self._hidden)
+ if hidden > 0]
+
available_software = AvailableSoftware()
diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py
index 600a18b0e8d..68da2754fc9 100644
--- a/src/sage/doctest/reporting.py
+++ b/src/sage/doctest/reporting.py
@@ -41,6 +41,7 @@
# https://www.gnu.org/licenses/
# ****************************************************************************
+import re
from sys import stdout
from signal import (SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL,
SIGINT, SIGKILL, SIGPIPE, SIGQUIT, SIGSEGV, SIGTERM)
@@ -145,7 +146,8 @@ def were_doctests_with_optional_tag_run(self, tag):
When latex is available, doctests marked with optional tag
``latex`` are run by default since :issue:`32174`::
- sage: filename = os.path.join(SAGE_SRC,'sage','misc','latex.py')
+ sage: # needs SAGE_SRC
+ sage: filename = os.path.join(SAGE_SRC, 'sage', 'misc', 'latex.py')
sage: DC = DocTestController(DocTestDefaults(), [filename])
sage: DTR = DocTestReporter(DC)
sage: DTR.were_doctests_with_optional_tag_run('latex') # optional - latex
@@ -217,6 +219,69 @@ def report_head(self, source, fail_msg=None):
cmd += f" [failed in baseline: {failed}]"
return cmd
+ def _log_failure(self, source, fail_msg, event, output=None):
+ r"""
+ Report on the result of a failed doctest run.
+
+ INPUT:
+
+ - ``source`` -- a source from :mod:`sage.doctest.sources`
+
+ - ``fail_msg`` -- a string
+
+ - ``event`` -- a string
+
+ - ``output`` -- optional string
+
+ EXAMPLES::
+
+ sage: from sage.doctest.reporting import DocTestReporter
+ sage: from sage.doctest.control import DocTestController, DocTestDefaults
+ sage: from sage.doctest.sources import FileDocTestSource
+ sage: from sage.env import SAGE_SRC
+ sage: import os
+ sage: filename = os.path.join(SAGE_SRC, 'sage', 'doctest', 'reporting.py')
+ sage: DD = DocTestDefaults()
+ sage: FDS = FileDocTestSource(filename, DD)
+ sage: DC = DocTestController(DD,[filename])
+ sage: DTR = DocTestReporter(DC)
+ sage: DTR._log_failure(FDS, "Timed out", "process (pid=1234) timed out", "Output so far...")
+ Timed out
+ **********************************************************************
+ Tests run before process (pid=1234) timed out:
+ Output so far...
+ **********************************************************************
+ """
+ log = self.controller.log
+ format = self.controller.options.format
+ if format == 'sage':
+ stars = "*" * 70
+ log(f" {fail_msg}\n{stars}\n")
+ if output:
+ log(f"Tests run before {event}:")
+ log(output)
+ log(stars)
+ elif format == 'github':
+ # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions
+ command = f'::error title={fail_msg}'
+ command += f',file={source.printpath}'
+ if output:
+ if m := re.search("## line ([0-9]+) ##\n-{40,100}\n(.*)", output, re.MULTILINE | re.DOTALL):
+ lineno = m.group(1)
+ message = m.group(2)
+ command += f',line={lineno}'
+ else:
+ message = output
+ # Urlencoding trick for multi-line annotations
+ # https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448
+ message = message.replace('\n', '%0A')
+ else:
+ message = ""
+ command += f'::{message}'
+ log(command)
+ else:
+ raise ValueError(f'unknown format option: {format}')
+
def report(self, source, timeout, return_code, results, output, pid=None):
"""
Report on the result of running doctests on a given source.
@@ -434,9 +499,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
fail_msg += " (and interrupt failed)"
else:
fail_msg += " (with %s after interrupt)" % signal_name(sig)
- log(" %s\n%s\nTests run before %s timed out:" % (fail_msg, "*"*70, process_name))
- log(output)
- log("*"*70)
+ self._log_failure(source, fail_msg, f"{process_name} timed out", output)
postscript['lines'].append(self.report_head(source, fail_msg))
stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests}
if not baseline.get('failed', False):
@@ -448,9 +511,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
fail_msg = "Killed due to %s" % signal_name(-return_code)
if ntests > 0:
fail_msg += " after testing finished"
- log(" %s\n%s\nTests run before %s failed:" % (fail_msg,"*"*70, process_name))
- log(output)
- log("*"*70)
+ self._log_failure(source, fail_msg, f"{process_name} failed", output)
postscript['lines'].append(self.report_head(source, fail_msg))
stats[basename] = {"failed": True, "walltime": 1e6, "ntests": ntests}
if not baseline.get('failed', False):
@@ -465,15 +526,11 @@ def report(self, source, timeout, return_code, results, output, pid=None):
else:
cpu = 1e6
if result_dict.err == 'badresult':
- log(" Error in doctesting framework (bad result returned)\n%s\nTests run before error:" % ("*"*70))
- log(output)
- log("*"*70)
+ self._log_failure(source, "Error in doctesting framework (bad result returned)", "error", output)
postscript['lines'].append(self.report_head(source, "Testing error: bad result"))
self.error_status |= 64
elif result_dict.err == 'noresult':
- log(" Error in doctesting framework (no result returned)\n%s\nTests run before error:" % ("*"*70))
- log(output)
- log("*"*70)
+ self._log_failure(source, "Error in doctesting framework (no result returned)", "error", output)
postscript['lines'].append(self.report_head(source, "Testing error: no result"))
self.error_status |= 64
elif result_dict.err == 'tab':
@@ -499,11 +556,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
else:
err = repr(result_dict.err)
fail_msg = "%s in doctesting framework" % err
-
- log(" %s\n%s" % (fail_msg, "*"*70))
- if output:
- log("Tests run before doctest exception:\n" + output)
- log("*"*70)
+ self._log_failure(source, fail_msg, "exception", output)
postscript['lines'].append(self.report_head(source, fail_msg))
if hasattr(result_dict, 'tb'):
log(result_dict.tb)
diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py
index 353eb1ea041..4b42ad87b1f 100644
--- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py
+++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py
@@ -6056,8 +6056,7 @@ def reduced_form(self, **kwds):
sage: f.reduced_form(prec=50, smallest_coeffs=False) # this needs 2 periodic
Traceback (most recent call last):
...
- ValueError: accuracy of Newton's root not within tolerance(0.000066... > 1e-06),
- increase precision
+ ValueError: accuracy of Newton's root not within tolerance(0.00006... > 1e-06), increase precision
sage: f.reduced_form(smallest_coeffs=False)
(
Dynamical System of Projective Space of dimension 1 over Rational Field
@@ -6107,7 +6106,7 @@ def reduced_form(self, **kwds):
sage: f.reduced_form(prec=30, smallest_coeffs=False)
Traceback (most recent call last):
...
- ValueError: accuracy of Newton's root not within tolerance(0.00008... > 1e-06), increase precision
+ ValueError: accuracy of Newton's root not within tolerance(0.00009... > 1e-06), increase precision
sage: f.reduced_form(smallest_coeffs=False)
(
Dynamical System of Projective Space of dimension 1 over Rational Field
diff --git a/src/sage/env.py b/src/sage/env.py
index bb228e7f041..3d336aa18ea 100644
--- a/src/sage/env.py
+++ b/src/sage/env.py
@@ -187,6 +187,11 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
SAGE_PKGS = var("SAGE_PKGS", join(SAGE_ROOT, "build", "pkgs"))
SAGE_ROOT_GIT = var("SAGE_ROOT_GIT", join(SAGE_ROOT, ".git"))
+# Sage doc server (local server with PORT if URL is not given)
+SAGE_DOC_SERVER_URL = var("SAGE_DOC_SERVER_URL")
+# The default port is 0 so that the system will assign a random unused port > 1024
+SAGE_DOC_LOCAL_PORT = var("SAGE_DOC_LOCAL_PORT", "0")
+
# ~/.sage
DOT_SAGE = var("DOT_SAGE", join(os.environ.get("HOME"), ".sage"))
SAGE_STARTUP_FILE = var("SAGE_STARTUP_FILE", join(DOT_SAGE, "init.sage"))
diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx
index 52fbc8f7406..3db7d6fa1dc 100644
--- a/src/sage/ext/fast_eval.pyx
+++ b/src/sage/ext/fast_eval.pyx
@@ -31,20 +31,20 @@ AUTHORS:
from sage.ext.fast_callable import fast_callable, Wrapper
-def fast_float(f, *vars, old=None, expect_one_var=False):
+def fast_float(f, *vars, expect_one_var=False):
"""
- Tries to create a function that evaluates f quickly using
- floating-point numbers, if possible. There are two implementations
- of fast_float in Sage; by default we use the newer, which is
- slightly faster on most tests.
+ Try to create a function that evaluates f quickly using
+ floating-point numbers, if possible.
On failure, returns the input unchanged.
+ This is an alternative interface to :func:`sage.ext.fast_callable.fast_callable`.
+ :issue:`32268` proposes to deprecate this function.
+
INPUT:
- ``f`` -- an expression
- ``vars`` -- the names of the arguments
- - ``old`` -- deprecated, do not use
- ``expect_one_var`` -- don't give deprecation warning if ``vars`` is
omitted, as long as expression has only one var
@@ -67,12 +67,6 @@ def fast_float(f, *vars, old=None, expect_one_var=False):
sage: f(1,2)
1.0
"""
- if old:
- raise ValueError("the old implementation of fast_float has been removed")
- if old is not None:
- from sage.misc.superseded import deprecation
- deprecation(32234, "passing old=False to fast_float is deprecated")
-
if isinstance(f, (tuple, list)):
return tuple([fast_float(x, *vars, expect_one_var=expect_one_var) for x in f])
diff --git a/src/sage/ext/memory_allocator.pxd b/src/sage/ext/memory_allocator.pxd
deleted file mode 100644
index 5fc54a1cd27..00000000000
--- a/src/sage/ext/memory_allocator.pxd
+++ /dev/null
@@ -1,145 +0,0 @@
-cimport cython
-from libc.stdint cimport uintptr_t
-
-
-cdef extern from *:
- int unlikely(int) nogil # defined by Cython
-
-
-cdef inline void* align(void* ptr, size_t alignment) noexcept:
- """
- Round up ``ptr`` to the nearest multiple of ``alignment``, which
- must be a power of 2
- """
- cdef uintptr_t x = ptr
- x = (x + alignment - 1) & ~(alignment - 1)
- return x
-
-
-@cython.final
-cdef class MemoryAllocator:
- cdef size_t n
- cdef size_t size
- cdef void** pointers
- cdef void* static_pointers[16] # If n <= 16, store pointers here
-
- cdef void* malloc(self, size_t size) except? NULL
- cdef void* calloc(self, size_t nmemb, size_t size) except? NULL
- cdef void* allocarray(self, size_t nmemb, size_t size) except? NULL
- cdef void* realloc(self, void* ptr, size_t size) except? NULL
- cdef void* reallocarray(self, void* ptr, size_t nmemb,
- size_t size) except? NULL
-
- cdef int resize(self, size_t new_size) except -1
- cdef void** find_pointer(self, void* ptr) except NULL
-
- cdef inline int enlarge_if_needed(self) except -1:
- r"""
- Enlarge the list of pointers if needed such that there is at
- least one free entry.
- """
- if unlikely(self.n >= self.size):
- return self.resize(self.size * 2)
-
- cdef inline void* aligned_malloc(self, size_t alignment,
- size_t size) except? NULL:
- r"""
- Returns new aligned pointer. Stores it to be automatically freed later.
-
- Alignment must be a power of two.
-
- .. NOTE::
-
- If you want to allocate multiple (small) aligned arrays with the
- same alignment and really want to be memory efficient, you can
- allocate one large aligned array instead.
-
- TESTS::
-
- sage: cython( # needs sage.misc.cython
- ....: '''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: cdef void* ptr
- ....: for i in range(12):
- ....: ptr = mem.aligned_malloc(2**i, 4048)
- ....: assert ptr == ( ptr) & ~(2**i-1)
- ....: ''')
- doctest:...: DeprecationWarning: this class is deprecated;
- use the class from the python package `memory_allocator`
- See https://github.com/sagemath/sage/issues/31591 for details.
- """
- cdef size_t extra = alignment - 1
- return align(self.malloc(size + extra), alignment)
-
- cdef inline void* aligned_calloc(self, size_t alignment, size_t nmemb,
- size_t size) except? NULL:
- r"""
- Returns new aligned pointer. Stores it to be automatically freed later.
-
- Alignment must be a power of two.
-
- .. NOTE::
-
- If you want to allocate multiple (small) aligned arrays with the
- same alignment and really want to be memory efficient, you can
- allocate one large aligned array instead.
-
- TESTS::
-
- sage: cython( # needs sage.misc.cython
- ....: '''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: def foo():
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: cdef void* ptr
- ....: for i in range(12):
- ....: ptr = mem.aligned_calloc(2**i, i, 2**i)
- ....: assert ptr == ( ptr) & ~(2**i-1)
- ....: ''')
- sage: foo() # needs sage.misc.cython
- doctest:...: DeprecationWarning: this class is deprecated;
- use the class from the python package `memory_allocator`
- See https://github.com/sagemath/sage/issues/31591 for details.
- """
- # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1
- # ⇔ extra * size >= alignment - 1
- # ⇔ extra >= ceil( (alignment - 1) / size)
- # ⇔ extra >= (alignment - 1 + size - 1) // size
- cdef size_t extra = (alignment + size - 2) // size
- return align(self.calloc(nmemb + extra, size), alignment)
-
- cdef inline void* aligned_allocarray(self, size_t alignment, size_t nmemb,
- size_t size) except? NULL:
- r"""
- Returns new aligned pointer. Stores it to be automatically freed later.
-
- Alignment must be a power of two.
-
- .. NOTE::
-
- If you want to allocate multiple (small) aligned arrays with the
- same alignment and really want to be memory efficient, you can
- allocate one large aligned array instead.
-
- TESTS::
-
- sage: cython( # needs sage.misc.cython
- ....: '''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: def foo():
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: cdef void* ptr
- ....: for i in range(12):
- ....: ptr = mem.aligned_allocarray(2**i, i, 2**i)
- ....: assert ptr == ( ptr) & ~(2**i-1)
- ....: ''')
- sage: foo() # random # might raise deprecation warning # needs sage.misc.cython
- sage: foo() # needs sage.misc.cython
- """
- # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1
- # ⇔ extra * size >= alignment - 1
- # ⇔ extra >= ceil( (alignment - 1) / size)
- # ⇔ extra >= (alignment - 1 + size - 1) // size
- cdef size_t extra = (alignment + size - 2) // size
- return align(self.allocarray(nmemb + extra, size), alignment)
diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx
deleted file mode 100644
index 0c2814658e9..00000000000
--- a/src/sage/ext/memory_allocator.pyx
+++ /dev/null
@@ -1,180 +0,0 @@
-# sage.doctest: needs sage.misc.cython
-
-from cysignals.memory cimport *
-from sage.misc.superseded import deprecation
-
-
-cdef class MemoryAllocator:
- r"""
- An object for memory allocation, whose resources are freed upon
- ``__dealloc__``.
-
- EXAMPLES::
-
- sage: cython(
- ....: '''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: cdef void* ptr
- ....: for n in range(100):
- ....: ptr = mem.malloc(n)
- ....: mem.realloc(ptr, 2*n)
- ....: mem.calloc(n, n)
- ....: ptr = mem.allocarray(n, n)
- ....: mem.reallocarray(ptr, n + 1, n)
- ....: mem.aligned_malloc(32, (n//32 + 1)*32)
- ....: mem.aligned_calloc(16, n, 16)
- ....: mem.aligned_allocarray(8, n, 8)
- ....: ''')
- doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator`
- See https://github.com/sagemath/sage/issues/31591 for details.
- """
- def __cinit__(self):
- """
- EXAMPLES::
-
- sage: cython(
- ....: '''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: def foo():
- ....: cdef MemoryAllocator mem = MemoryAllocator.__new__(MemoryAllocator)
- ....: mem.malloc(10000)
- ....: print(mem.n)
- ....: print(mem.size)
- ....: ''')
- sage: foo()
- doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator`
- See https://github.com/sagemath/sage/issues/31591 for details.
- 1
- 16
- """
- deprecation(31591, "this class is deprecated; use the class from the python package `memory_allocator`")
- self.n = 0
- self.size = 16
- self.pointers = self.static_pointers
-
- cdef int resize(self, size_t new_size) except -1:
- r"""
- Resize the list of pointers to contain ``new_size`` elements.
-
- It is required that ``new_size`` is at least ``self.n``, but
- this condition is not checked.
- """
- cdef size_t i
- if self.pointers == self.static_pointers:
- # Case 1: allocate pointers for the first time
- self.pointers = check_allocarray(new_size, sizeof(void*))
- for i in range(self.n):
- self.pointers[i] = self.static_pointers[i]
- else:
- # Case 2: resize pointers
- self.pointers = check_reallocarray(self.pointers, new_size, sizeof(void*))
- self.size = new_size
-
- cdef void** find_pointer(self, void* ptr) except NULL:
- r"""
- Return the address in the list of stored pointers where ``ptr``
- is stored. If ``ptr`` is not found in the existing pointers and
- ``ptr`` is not ``NULL``, then an exception is raised. If ``ptr``
- is ``NULL``, then we simply add ``NULL`` as an additional
- pointer and return the address of that.
- """
- cdef size_t i = 0
- for i in range(self.n):
- if self.pointers[i] == ptr:
- return &self.pointers[i]
- if ptr != NULL:
- raise ValueError("given pointer not found in MemoryAllocator")
- self.enlarge_if_needed()
- addr = &self.pointers[self.n]
- self.n += 1
- return addr
-
- cdef void* malloc(self, size_t size) except? NULL:
- r"""
- Returns a new pointer and stores it to be automatically freed later.
- """
- self.enlarge_if_needed()
- cdef void* val = check_malloc(size)
- self.pointers[self.n] = val
- self.n += 1
- return val
-
- cdef void* calloc(self, size_t nmemb, size_t size) except? NULL:
- r"""
- Returns a new pointer and stores it to be automatically freed later.
- """
- self.enlarge_if_needed()
- cdef void* val = check_calloc(nmemb, size)
- self.pointers[self.n] = val
- self.n += 1
- return val
-
- cdef void* allocarray(self, size_t nmemb, size_t size) except? NULL:
- r"""
- Returns a new pointer and stores it to be automatically freed later.
- """
- self.enlarge_if_needed()
- cdef void* val = check_allocarray(nmemb, size)
- self.pointers[self.n] = val
- self.n += 1
- return val
-
- cdef void* realloc(self, void* ptr, size_t size) except? NULL:
- r"""
- Re-allocates `ptr` and automatically frees it later.
-
- TESTS::
-
- sage: cython('''
- ....: from sage.ext.memory_allocator cimport MemoryAllocator
- ....: def test_realloc_good():
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: ptr = mem.malloc(20)
- ....: mem.realloc(ptr, 21)
- ....: def test_realloc_NULL():
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: mem.realloc(NULL, 21)
- ....: def test_realloc_bad():
- ....: cdef MemoryAllocator mem = MemoryAllocator()
- ....: cdef MemoryAllocator mem2 = MemoryAllocator()
- ....: ptr = mem.malloc(20)
- ....: mem2.realloc(ptr, 21)
- ....: ''')
- sage: test_realloc_good() # random # might raise deprecation warning
- sage: test_realloc_good()
- sage: test_realloc_NULL()
- sage: test_realloc_bad()
- Traceback (most recent call last):
- ...
- ValueError: given pointer not found in MemoryAllocator
- """
- cdef void** addr = self.find_pointer(ptr)
- cdef void* val = check_realloc(ptr, size)
- addr[0] = val
- return val
-
- cdef void* reallocarray(self, void* ptr, size_t nmemb,
- size_t size) except? NULL:
- r"""
- Re-allocates `ptr` and automatically frees it later.
- """
- cdef void** addr = self.find_pointer(ptr)
- cdef void* val = check_reallocarray(ptr, nmemb, size)
- addr[0] = val
- return val
-
- def __dealloc__(self):
- r"""
- Free the allocated resources
-
- EXAMPLES::
-
- sage: from sage.ext.memory_allocator import MemoryAllocator
- sage: _ = MemoryAllocator()
- """
- cdef size_t i
- for i in range(self.n):
- sig_free(self.pointers[i])
- if self.pointers != self.static_pointers:
- sig_free(self.pointers)
diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py
index a8b11ef7db5..86a777ee915 100644
--- a/src/sage/features/__init__.py
+++ b/src/sage/features/__init__.py
@@ -158,12 +158,6 @@ def __init__(self, name, spkg=None, url=None, description=None, type='optional')
self._hidden = False
self._type = type
- # For multiprocessing of doctests, the data self._num_hidings should be
- # shared among subprocesses. Thus we use the Value class from the
- # multiprocessing module (cf. self._seen of class AvailableSoftware)
- from multiprocessing import Value
- self._num_hidings = Value('i', 0, lock=False)
-
try:
from sage.misc.package import spkg_type
except ImportError: # may have been surgically removed in a downstream distribution
@@ -220,10 +214,6 @@ def is_present(self):
self._cache_is_present = res
if self._hidden:
- if self._num_hidings.value > 0:
- self._num_hidings.value += 1
- elif self._cache_is_present:
- self._num_hidings.value = 1
return FeatureTestResult(self, False, reason="Feature `{name}` is hidden.".format(name=self.name))
return self._cache_is_present
@@ -354,7 +344,6 @@ def is_standard(self):
sage: from sage.features.databases import DatabaseCremona
sage: DatabaseCremona().is_standard()
False
-
"""
if self.name.startswith('sage.'):
return True
@@ -369,7 +358,6 @@ def is_optional(self):
sage: from sage.features.databases import DatabaseCremona
sage: DatabaseCremona().is_optional()
True
-
"""
return self._spkg_type() == 'optional'
@@ -394,9 +382,7 @@ def hide(self):
Use method `unhide` to make it available again.
sage: Benzene().unhide() # optional - benzene, needs sage.graphs
- 1
sage: len(list(graphs.fusenes(2))) # optional - benzene, needs sage.graphs
- 1
"""
self._hidden = True
@@ -404,8 +390,6 @@ def unhide(self):
r"""
Revert what :meth:`hide` did.
- OUTPUT: The number of events a present feature has been hidden.
-
EXAMPLES:
sage: from sage.features.sagemath import sage__plot
@@ -413,15 +397,28 @@ def unhide(self):
sage: sage__plot().is_present()
FeatureTestResult('sage.plot', False)
sage: sage__plot().unhide() # needs sage.plot
- 1
sage: sage__plot().is_present() # needs sage.plot
FeatureTestResult('sage.plot', True)
"""
- num_hidings = self._num_hidings.value
- self._num_hidings.value = 0
self._hidden = False
- return int(num_hidings)
+ def is_hidden(self):
+ r"""
+ Return whether ``self`` is present but currently hidden.
+
+ EXAMPLES:
+
+ sage: from sage.features.sagemath import sage__plot
+ sage: sage__plot().hide()
+ sage: sage__plot().is_hidden() # needs sage.plot
+ True
+ sage: sage__plot().unhide()
+ sage: sage__plot().is_hidden()
+ False
+ """
+ if self._hidden and self._is_present():
+ return True
+ return False
class FeatureNotPresentError(RuntimeError):
r"""
@@ -948,7 +945,10 @@ def _is_present(self):
# Available since https://setuptools.pypa.io/en/latest/history.html#v59-0-0
from setuptools.errors import CCompilerError
except ImportError:
- from distutils.errors import CCompilerError
+ try:
+ from distutils.errors import CCompilerError
+ except ImportError:
+ CCompilerError = ()
with open(tmp_filename(ext=".pyx"), 'w') as pyx:
pyx.write(self.test_code)
try:
diff --git a/src/sage/features/join_feature.py b/src/sage/features/join_feature.py
index 24c6583c123..81a5b7fb6e9 100644
--- a/src/sage/features/join_feature.py
+++ b/src/sage/features/join_feature.py
@@ -154,8 +154,6 @@ def unhide(self):
r"""
Revert what :meth:`hide` did.
- OUTPUT: The number of events a present feature has been hidden.
-
EXAMPLES::
sage: from sage.features.sagemath import sage__groups
@@ -167,14 +165,11 @@ def unhide(self):
FeatureTestResult('sage.groups.perm_gps.permgroup', False)
sage: f.unhide()
- 4
sage: f.is_present() # optional sage.groups
FeatureTestResult('sage.groups', True)
sage: f._features[0].is_present() # optional sage.groups
FeatureTestResult('sage.groups.perm_gps.permgroup', True)
"""
- num_hidings = 0
for f in self._features:
- num_hidings += f.unhide()
- num_hidings += super().unhide()
- return num_hidings
+ f.unhide()
+ super().unhide()
diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py
index 75a925895c5..2001f793a2f 100644
--- a/src/sage/features/sagemath.py
+++ b/src/sage/features/sagemath.py
@@ -43,6 +43,31 @@
from .join_feature import JoinFeature
+class SAGE_SRC(StaticFile):
+ r"""
+ A :class:`~sage.features.Feature` which describes the presence of the
+ monolithic source tree of the Sage library.
+
+ """
+ def __init__(self):
+ r"""
+ TESTS::
+
+ sage: from sage.features.sagemath import SAGE_SRC
+ sage: isinstance(SAGE_SRC(), SAGE_SRC)
+ True
+ """
+ from sage.env import SAGE_SRC
+ # We check the file bin/sage-src-env-config.in, which by design is:
+ # - never installed,
+ # - not included in the sagemath-standard sdist,
+ # - included only in one modularized sdist, of pkgs/sage-conf_pypi,
+ # where it appears in a subdirectory (sage_root/src/bin/)
+ StaticFile.__init__(self, 'SAGE_SRC',
+ filename='bin/sage-src-env-config.in',
+ search_path=(SAGE_SRC,) if SAGE_SRC else ())
+
+
class sagemath_doc_html(StaticFile):
r"""
A :class:`~sage.features.Feature` which describes the presence of the documentation
@@ -1095,7 +1120,8 @@ def all_features():
sage: list(all_features())
[...Feature('sage.combinat'), ...]
"""
- return [sagemath_doc_html(),
+ return [SAGE_SRC(),
+ sagemath_doc_html(),
sage__combinat(),
sage__geometry__polyhedron(),
sage__graphs(),
diff --git a/src/sage/functions/error.py b/src/sage/functions/error.py
index 76509ce5ef0..db6312af807 100644
--- a/src/sage/functions/error.py
+++ b/src/sage/functions/error.py
@@ -265,7 +265,7 @@ def _evalf_(self, x, parent=None, algorithm=None):
sage: gp.set_real_precision(59) # random # needs sage.libs.pari
38
- sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)) # needs sage.libs.pari
+ sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)) # needs mpmath sage.libs.pari
0.84270079294971486934122063508260925929606699796630290845994
0.84270079294971486934122063508260925929606699796630290845994
diff --git a/src/sage/functions/exp_integral.py b/src/sage/functions/exp_integral.py
index 913cb577bef..b5638f8d35f 100644
--- a/src/sage/functions/exp_integral.py
+++ b/src/sage/functions/exp_integral.py
@@ -1470,7 +1470,7 @@ def exponential_integral_1(x, n=0):
EXAMPLES::
- sage: # needs sage.libs.pari
+ sage: # needs sage.libs.pari sage.rings.real_mpfr
sage: exponential_integral_1(2)
0.0489005107080611
sage: exponential_integral_1(2, 4) # abs tol 1e-18
@@ -1519,7 +1519,7 @@ def exponential_integral_1(x, n=0):
....: if e >= c:
....: print("exponential_integral_1(%s, %s)[%s] with precision %s has error of %s >= %s"%(a, n, i, prec, e, c))
- ALGORITHM: use the PARI C-library function ``eint1``.
+ ALGORITHM: use the PARI C-library function :pari:`eint1`.
REFERENCE:
diff --git a/src/sage/functions/gamma.py b/src/sage/functions/gamma.py
index 0af99e5dc20..be2d60d0861 100644
--- a/src/sage/functions/gamma.py
+++ b/src/sage/functions/gamma.py
@@ -40,9 +40,9 @@ def __init__(self):
EXAMPLES::
sage: from sage.functions.gamma import gamma1
- sage: gamma1(CDF(0.5, 14)) # needs sage.libs.pari
+ sage: gamma1(CDF(0.5, 14)) # needs sage.libs.pari sage.rings.complex_double
-4.0537030780372815e-10 - 5.773299834553605e-10*I
- sage: gamma1(CDF(I)) # needs sage.libs.pari sage.symbolic
+ sage: gamma1(CDF(I)) # needs sage.libs.pari sage.rings.complex_double sage.symbolic
-0.15494982830181067 - 0.49801566811835607*I
Recall that `\Gamma(n)` is `n-1` factorial::
@@ -99,7 +99,7 @@ def __init__(self):
1*x^(-2) + (-2*euler_gamma)*x^(-1)
+ (2*euler_gamma^2 + 1/6*pi^2) + Order(x)
- To prevent automatic evaluation use the ``hold`` argument::
+ To prevent automatic evaluation, use the ``hold`` argument::
sage: gamma1(1/2, hold=True) # needs sage.symbolic
gamma(1/2)
@@ -138,9 +138,9 @@ def __init__(self):
Infinity
sage: (-1.).gamma() # needs sage.rings.real_mpfr
NaN
- sage: CC(-1).gamma() # needs sage.libs.pari
+ sage: CC(-1).gamma() # needs sage.libs.pari sage.rings.real_mpfr
Infinity
- sage: RDF(-1).gamma()
+ sage: RDF(-1).gamma() # needs sage.rings.real_mpfr
NaN
sage: CDF(-1).gamma() # needs sage.libs.pari sage.rings.complex_double
Infinity
@@ -691,9 +691,9 @@ def gamma(a, *args, **kwds):
::
- sage: gamma(CDF(I)) # needs sage.libs.pari sage.symbolic
+ sage: gamma(CDF(I)) # needs sage.libs.pari sage.rings.complex_double sage.symbolic
-0.15494982830181067 - 0.49801566811835607*I
- sage: gamma(CDF(0.5, 14)) # needs sage.libs.pari
+ sage: gamma(CDF(0.5, 14)) # needs sage.libs.pari sage.rings.complex_double
-4.0537030780372815e-10 - 5.773299834553605e-10*I
Use ``numerical_approx`` to get higher precision from
@@ -721,7 +721,8 @@ def gamma(a, *args, **kwds):
sage: gamma(i) # needs sage.rings.number_field sage.symbolic
Traceback (most recent call last):
...
- TypeError: cannot coerce arguments: no canonical coercion from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring
+ TypeError: cannot coerce arguments: no canonical coercion
+ from Number Field in i with defining polynomial x^2 + 1 to Symbolic Ring
.. SEEALSO::
@@ -1004,9 +1005,9 @@ def __init__(self):
INPUT:
- - ``p`` - number or symbolic expression
+ - ``p`` -- number or symbolic expression
- - ``q`` - number or symbolic expression
+ - ``q`` -- number or symbolic expression
OUTPUT: number or symbolic expression (if input is symbolic)
@@ -1016,18 +1017,18 @@ def __init__(self):
sage: # needs sage.symbolic
sage: beta(3, 2)
1/12
- sage: beta(3,1)
+ sage: beta(3, 1)
1/3
sage: beta(1/2, 1/2)
beta(1/2, 1/2)
- sage: beta(-1,1)
+ sage: beta(-1, 1)
-1
- sage: beta(-1/2,-1/2)
+ sage: beta(-1/2, -1/2)
0
sage: ex = beta(x/2, 3)
sage: set(ex.operands()) == set([1/2*x, 3])
True
- sage: beta(.5,.5)
+ sage: beta(.5, .5)
3.14159265358979
sage: beta(1, 2.0+I)
0.400000000000000 - 0.200000000000000*I
diff --git a/src/sage/functions/generalized.py b/src/sage/functions/generalized.py
index 5e66d267828..ba8222121b9 100644
--- a/src/sage/functions/generalized.py
+++ b/src/sage/functions/generalized.py
@@ -290,7 +290,7 @@ class FunctionUnitStep(GinacFunction):
INPUT:
- - ``x`` - a real number or a symbolic expression
+ - ``x`` -- a real number or a symbolic expression
DEFINITION:
@@ -331,7 +331,7 @@ def __init__(self):
INPUT:
- - ``x`` - a real number or a symbolic expression
+ - ``x`` -- a real number or a symbolic expression
EXAMPLES::
@@ -378,7 +378,7 @@ class FunctionSignum(BuiltinFunction):
INPUT:
- - ``x`` - a real number or a symbolic expression
+ - ``x`` -- a real number or a symbolic expression
DEFINITION:
@@ -548,8 +548,8 @@ class FunctionKroneckerDelta(BuiltinFunction):
INPUT:
- - ``m`` - a number or a symbolic expression
- - ``n`` - a number or a symbolic expression
+ - ``m`` -- a number or a symbolic expression
+ - ``n`` -- a number or a symbolic expression
DEFINITION:
@@ -560,9 +560,9 @@ class FunctionKroneckerDelta(BuiltinFunction):
EXAMPLES::
- sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field
0
- sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field
1
sage: m, n = var('m,n') # needs sage.symbolic
sage: kronecker_delta(m, n) # needs sage.symbolic
@@ -579,9 +579,9 @@ def __init__(self):
EXAMPLES::
- sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field
0
- sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field
1
sage: y = var('y') # needs sage.symbolic
sage: kronecker_delta(x, y)._sympy_() # needs sympy sage.symbolic
@@ -598,9 +598,9 @@ def _eval_(self, m, n):
EXAMPLES::
- sage: kronecker_delta(1,2) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 2) # needs sage.rings.complex_interval_field
0
- sage: kronecker_delta(1,1) # needs sage.rings.complex_interval_field
+ sage: kronecker_delta(1, 1) # needs sage.rings.complex_interval_field
1
Kronecker delta is a symmetric function. We keep arguments sorted to
diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py
index 7c94892ba9d..6098a6de0bb 100644
--- a/src/sage/functions/hyperbolic.py
+++ b/src/sage/functions/hyperbolic.py
@@ -53,7 +53,7 @@ def __init__(self):
EXAMPLES::
- sage: sinh(3.1415)
+ sage: sinh(3.1415) # needs sage.rings.real_mpfr
11.5476653707437
sage: # needs sage.symbolic
@@ -91,7 +91,7 @@ def __init__(self):
EXAMPLES::
- sage: cosh(3.1415)
+ sage: cosh(3.1415) # needs sage.rings.real_mpfr
11.5908832931176
sage: # needs sage.symbolic
@@ -129,9 +129,9 @@ def __init__(self):
EXAMPLES::
- sage: tanh(3.1415)
+ sage: tanh(3.1415) # needs sage.rings.real_mpfr
0.996271386633702
- sage: tan(3.1415/4)
+ sage: tan(3.1415/4) # needs sage.rings.real_mpfr
0.999953674278156
sage: # needs sage.symbolic
@@ -197,7 +197,7 @@ def __init__(self):
EXAMPLES::
- sage: coth(3.1415)
+ sage: coth(3.1415) # needs sage.rings.real_mpfr
1.00374256795520
sage: coth(complex(1, 2)) # abs tol 1e-15 # needs sage.rings.complex_double
(0.8213297974938518+0.17138361290918508j)
@@ -256,7 +256,7 @@ def __init__(self):
EXAMPLES::
- sage: sech(3.1415)
+ sage: sech(3.1415) # needs sage.rings.real_mpfr
0.0862747018248192
sage: # needs sage.symbolic
@@ -313,7 +313,7 @@ def __init__(self):
EXAMPLES::
- sage: csch(3.1415)
+ sage: csch(3.1415) # needs sage.rings.real_mpfr
0.0865975907592133
sage: # needs sage.symbolic
@@ -375,7 +375,7 @@ def __init__(self):
sage: asinh
arcsinh
- sage: asinh(0.5)
+ sage: asinh(0.5) # needs sage.rings.real_mpfr
0.481211825059603
sage: asinh(1/2) # needs sage.symbolic
arcsinh(1/2)
@@ -527,7 +527,7 @@ def __init__(self):
EXAMPLES::
- sage: atanh(0.5)
+ sage: atanh(0.5) # needs sage.rings.real_mpfr
0.549306144334055
sage: atanh(1/2) # needs sage.symbolic
1/2*log(3)
diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py
index 6e2b26d284a..010c61febe0 100644
--- a/src/sage/functions/hypergeometric.py
+++ b/src/sage/functions/hypergeometric.py
@@ -14,13 +14,14 @@
Examples from :issue:`9908`::
+ sage: # needs sage.symbolic
sage: maxima('integrate(bessel_j(2, x), x)').sage()
1/24*x^3*hypergeometric((3/2,), (5/2, 3), -1/4*x^2)
sage: sum(((2*I)^x/(x^3 + 1)*(1/4)^x), x, 0, oo)
hypergeometric((1, 1, -1/2*I*sqrt(3) - 1/2, 1/2*I*sqrt(3) - 1/2),...
(2, -1/2*I*sqrt(3) + 1/2, 1/2*I*sqrt(3) + 1/2), 1/2*I)
sage: res = sum((-1)^x/((2*x + 1)*factorial(2*x + 1)), x, 0, oo)
- sage: res # not tested - depends on maxima version
+ sage: res # not tested (depends on maxima version)
hypergeometric((1/2,), (3/2, 3/2), -1/4)
sage: res in [hypergeometric((1/2,), (3/2, 3/2), -1/4), sin_integral(1)]
True
@@ -28,6 +29,7 @@
Simplification (note that ``simplify_full`` does not yet call
``simplify_hypergeometric``)::
+ sage: # needs sage.symbolic
sage: hypergeometric([-2], [], x).simplify_hypergeometric()
x^2 - 2*x + 1
sage: hypergeometric([], [], x).simplify_hypergeometric()
@@ -41,10 +43,10 @@
Equality testing::
- sage: bool(hypergeometric([], [], x).derivative(x) ==
+ sage: bool(hypergeometric([], [], x).derivative(x) == # needs sage.symbolic
....: hypergeometric([], [], x)) # diff(e^x, x) == e^x
True
- sage: bool(hypergeometric([], [], x) == hypergeometric([], [1], x))
+ sage: bool(hypergeometric([], [], x) == hypergeometric([], [1], x)) # needs sage.symbolic
False
Computing terms and series::
@@ -79,14 +81,14 @@
1 + (-1/2)*z^2 + 1/24*z^4 + (-1/720)*z^6 + 1/40320*z^8 +...
(-1/3628800)*z^10 + Order(z^11)
- sage: hypergeometric([1], [5], x).series(x, 5)
+ sage: hypergeometric([1], [5], x).series(x, 5) # needs sage.symbolic
1 + 1/5*x + 1/30*x^2 + 1/210*x^3 + 1/1680*x^4 + Order(x^5)
sage: sum(hypergeometric([1, 2], [3], 1/3).terms(6)).n() # needs sage.symbolic
1.29788359788360
sage: hypergeometric([1, 2], [3], 1/3).n() # needs sage.symbolic
1.29837194594696
- sage: hypergeometric([], [], x).series(x, 20)(x=1).n() == e.n()
+ sage: hypergeometric([], [], x).series(x, 20)(x=1).n() == e.n() # needs sage.symbolic
True
Plotting::
@@ -96,7 +98,7 @@
sage: plot(f, x, -30, 30) # needs sage.plot
Graphics object consisting of 1 graphics primitive
sage: g(x) = hypergeometric([x], [], 2)
- sage: complex_plot(g, (-1, 1), (-1, 1))
+ sage: complex_plot(g, (-1, 1), (-1, 1)) # needs sage.plot
Graphics object consisting of 1 graphics primitive
Numeric evaluation::
@@ -121,7 +123,7 @@
sage: maxima(hypergeometric([1, 1, 1], [3, 3, 3], x)) # needs sage.symbolic
hypergeometric([1,1,1],[3,3,3],_SAGE_VAR_x)
- sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() # needs sage.symbolic
+ sage: hypergeometric((5, 4), (4, 4), 3)._sympy_() # needs sympy sage.symbolic
hyper((5, 4), (4, 4), 3)
sage: hypergeometric((5, 4), (4, 4), 3)._mathematica_init_() # needs sage.symbolic
'HypergeometricPFQ[{5,4},{4,4},3]'
@@ -154,7 +156,7 @@
1 + 1*x + 1/2*x^2 + Order(x^3)
sage: hypergeometric_U(2, 2, x).series(x == 3, 100).subs(x=1).n() # needs sage.symbolic
0.403652637676806
- sage: hypergeometric_U(2, 2, 1).n() # needs mpmath
+ sage: hypergeometric_U(2, 2, 1).n() # needs mpmath sage.symbolic
0.403652637676806
"""
@@ -451,7 +453,7 @@ def eliminate_parameters(self, a, b, z):
sage: hypergeometric([1, 1, 2, 5], [5, 1, 4], # needs sage.symbolic
....: 1/2).eliminate_parameters()
hypergeometric((1, 2), (4,), 1/2)
- sage: hypergeometric([x], [x], x).eliminate_parameters()
+ sage: hypergeometric([x], [x], x).eliminate_parameters() # needs sage.symbolic
hypergeometric((), (), x)
sage: hypergeometric((5, 4), (4, 4), 3).eliminate_parameters() # needs sage.symbolic
hypergeometric((5,), (4,), 3)
@@ -538,11 +540,11 @@ def is_terminating(self, a, b, z):
EXAMPLES::
- sage: hypergeometric([1, 2], [3, 4], x).is_terminating()
+ sage: hypergeometric([1, 2], [3, 4], x).is_terminating() # needs sage.symbolic
False
- sage: hypergeometric([1, -2], [3, 4], x).is_terminating()
+ sage: hypergeometric([1, -2], [3, 4], x).is_terminating() # needs sage.symbolic
True
- sage: hypergeometric([1, -2], [], x).is_terminating()
+ sage: hypergeometric([1, -2], [], x).is_terminating() # needs sage.symbolic
True
"""
if z == 0:
@@ -642,11 +644,11 @@ def terms(self, a, b, z, n=None):
EXAMPLES::
- sage: list(hypergeometric([-2, 1], [3, 4], x).terms())
+ sage: list(hypergeometric([-2, 1], [3, 4], x).terms()) # needs sage.symbolic
[1, -1/6*x, 1/120*x^2]
- sage: list(hypergeometric([-2, 1], [3, 4], x).terms(2))
+ sage: list(hypergeometric([-2, 1], [3, 4], x).terms(2)) # needs sage.symbolic
[1, -1/6*x]
- sage: list(hypergeometric([-2, 1], [3, 4], x).terms(0))
+ sage: list(hypergeometric([-2, 1], [3, 4], x).terms(0)) # needs sage.symbolic
[]
"""
if n is None:
@@ -675,8 +677,7 @@ def deflated(self, a, b, z):
sage: # needs sage.symbolic
sage: x = hypergeometric([6, 1], [3, 4, 5], 10)
- sage: y = x.deflated()
- sage: y
+ sage: y = x.deflated(); y
1/252*hypergeometric((4,), (7, 8), 10)
+ 1/12*hypergeometric((3,), (6, 7), 10)
+ 1/2*hypergeometric((2,), (5, 6), 10)
@@ -687,8 +688,7 @@ def deflated(self, a, b, z):
sage: # needs sage.symbolic
sage: x = hypergeometric([6, 7], [3, 4, 5], 10)
- sage: y = x.deflated()
- sage: y
+ sage: y = x.deflated(); y
25/27216*hypergeometric((), (11,), 10)
+ 25/648*hypergeometric((), (10,), 10)
+ 265/504*hypergeometric((), (9,), 10)
@@ -710,8 +710,7 @@ def _deflated(self, a, b, z):
sage: # needs sage.symbolic
sage: x = hypergeometric([5], [4], 3)
- sage: y = x.deflated()
- sage: y
+ sage: y = x.deflated(); y
7/4*hypergeometric((), (), 3)
sage: x.n(); y.n()
35.1496896155784
@@ -958,21 +957,21 @@ class Hypergeometric_M(BuiltinFunction):
EXAMPLES::
- sage: # needs mpmath
+
+ sage: hypergeometric_M(1, 1, 1.) # needs mpmath
+ 2.71828182845905
+
+ sage: # needs sage.symbolic
sage: hypergeometric_M(1, 1, 1)
hypergeometric_M(1, 1, 1)
- sage: hypergeometric_M(1, 1, 1.)
- 2.71828182845905
- sage: hypergeometric_M(1, 1, 1).n(70)
+ sage: hypergeometric_M(1, 1, 1).n(70) # needs mpmath
2.7182818284590452354
sage: hypergeometric_M(1, 1, 1).simplify_hypergeometric()
e
sage: hypergeometric_M(1, 3/2, 1).simplify_hypergeometric()
1/2*sqrt(pi)*erf(1)*e
-
- sage: hypergeometric_M(1, 1/2, x).simplify_hypergeometric() # needs sage.symbolic
+ sage: hypergeometric_M(1, 1/2, x).simplify_hypergeometric()
(-I*sqrt(pi)*x*erf(I*sqrt(-x))*e^x + sqrt(-x))/sqrt(-x)
-
"""
def __init__(self):
r"""
@@ -980,7 +979,7 @@ def __init__(self):
sage: maxima(hypergeometric_M(1,1,x)) # needs sage.symbolic
kummer_m(1,1,_SAGE_VAR_x)
- sage: latex(hypergeometric_M(1,1,x))
+ sage: latex(hypergeometric_M(1,1,x)) # needs sage.symbolic
M\left(1, 1, x\right)
"""
BuiltinFunction.__init__(self, 'hypergeometric_M', nargs=3,
@@ -995,8 +994,8 @@ def _eval_(self, a, b, z, **kwargs):
"""
TESTS::
- sage: (a,b)=var('a,b') # needs sage.symbolic
- sage: hypergeometric_M(a,b,0) # needs sage.symbolic
+ sage: a, b = var('a,b') # needs sage.symbolic
+ sage: hypergeometric_M(a, b, 0) # needs sage.symbolic
1
"""
if not isinstance(z, Expression) and z == 0:
@@ -1007,7 +1006,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None):
"""
TESTS::
- sage: hypergeometric_M(1,1,1).n() # needs mpmath
+ sage: hypergeometric_M(1,1,1).n() # needs mpmath sage.symbolic
2.71828182845905
"""
return _mpmath_utils_call(_mpmath_hyp1f1, a, b, z, parent=parent)
@@ -1016,9 +1015,9 @@ def _derivative_(self, a, b, z, diff_param):
"""
TESTS::
- sage: diff(hypergeometric_M(1,1,x),x,3)
+ sage: diff(hypergeometric_M(1, 1, x), x, 3) # needs sage.symbolic
hypergeometric_M(4, 4, x)
- sage: diff(hypergeometric_M(x,1,1),x,3)
+ sage: diff(hypergeometric_M(x, 1, 1), x, 3) # needs sage.symbolic
Traceback (most recent call last):
...
NotImplementedError: derivative of hypergeometric function with respect to parameters
@@ -1076,7 +1075,9 @@ class Hypergeometric_U(BuiltinFunction):
hypergeometric_U(1, 1, 1)
sage: hypergeometric_U(1, 1, 1.)
0.596347362323194
- sage: hypergeometric_U(1, 1, 1).n(70)
+
+ sage: # needs sage.symbolic
+ sage: hypergeometric_U(1, 1, 1).n(70) # needs mpmath
0.59634736232319407434
sage: hypergeometric_U(10^4, 1/3, 1).n() # needs sage.libs.pari
6.60377008885811e-35745
@@ -1092,9 +1093,9 @@ def __init__(self):
r"""
TESTS::
- sage: maxima(hypergeometric_U(1,1,x)) # needs sage.symbolic
+ sage: maxima(hypergeometric_U(1, 1, x)) # needs sage.symbolic
kummer_u(1,1,_SAGE_VAR_x)
- sage: latex(hypergeometric_U(1,1,x))
+ sage: latex(hypergeometric_U(1, 1, x)) # needs sage.symbolic
U\left(1, 1, x\right)
"""
BuiltinFunction.__init__(self, 'hypergeometric_U', nargs=3,
@@ -1112,7 +1113,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None):
"""
TESTS::
- sage: hypergeometric_U(1,1,1).n() # needs mpmath
+ sage: hypergeometric_U(1, 1, 1).n() # needs mpmath sage.symbolic
0.596347362323194
"""
return _mpmath_utils_call(_mpmath_hyperu, a, b, z, parent=parent)
@@ -1121,9 +1122,9 @@ def _derivative_(self, a, b, z, diff_param):
"""
TESTS::
- sage: diff(hypergeometric_U(1,1,x),x,3)
+ sage: diff(hypergeometric_U(1, 1, x), x, 3) # needs sage.symbolic
-6*hypergeometric_U(4, 4, x)
- sage: diff(hypergeometric_U(x,1,1),x,3)
+ sage: diff(hypergeometric_U(x, 1, 1), x, 3) # needs sage.symbolic
Traceback (most recent call last):
...
NotImplementedError: derivative of hypergeometric function with respect to parameters
@@ -1140,13 +1141,14 @@ def generalized(self, a, b, z):
EXAMPLES::
- sage: var('a b z') # needs sage.symbolic
+ sage: # needs sage.symbolic
+ sage: var('a b z')
(a, b, z)
- sage: hypergeometric_U(a, b, z).generalized() # needs sage.symbolic
+ sage: hypergeometric_U(a, b, z).generalized()
hypergeometric((a, a - b + 1), (), -1/z)/z^a
- sage: hypergeometric_U(1, 3, 1/2).generalized() # needs mpmath
+ sage: hypergeometric_U(1, 3, 1/2).generalized()
2*hypergeometric((1, -1), (), -2)
- sage: hypergeometric_U(3, I, 2).generalized() # needs sage.symbolic
+ sage: hypergeometric_U(3, I, 2).generalized()
1/8*hypergeometric((3, -I + 4), (), -1/2)
"""
diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py
index 37e5f601e59..903bb7cfe75 100644
--- a/src/sage/functions/log.py
+++ b/src/sage/functions/log.py
@@ -63,7 +63,7 @@ class Function_exp(GinacFunction):
sage: exp(float(2.5))
12.182493960703473
- sage: exp(RDF('2.5'))
+ sage: exp(RDF('2.5')) # needs sage.symbolic
12.182493960703473
To prevent automatic evaluation, use the ``hold`` parameter::
diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py
index 8976001bc62..1de970e0aa4 100644
--- a/src/sage/functions/orthogonal_polys.py
+++ b/src/sage/functions/orthogonal_polys.py
@@ -439,7 +439,7 @@ class OrthogonalFunction(BuiltinFunction):
def __init__(self, name, nargs=2, latex_name=None, conversions=None):
"""
:class:`OrthogonalFunction` class needs the same input parameter as
- it's parent class.
+ its parent class.
EXAMPLES::
@@ -466,7 +466,7 @@ def eval_formula(self, *args):
sage: from sage.functions.orthogonal_polys import OrthogonalFunction
sage: P = OrthogonalFunction('testo_P')
- sage: P.eval_formula(1,2.0)
+ sage: P.eval_formula(1, 2.0)
Traceback (most recent call last):
...
NotImplementedError: no explicit calculation of values implemented
@@ -543,8 +543,8 @@ class ChebyshevFunction(OrthogonalFunction):
"""
def __call__(self, n, *args, **kwds):
"""
- This overides the call method from SageObject to avoid problems with coercions,
- since the _eval_ method is able to handle more data types than symbolic functions
+ This overides the call method from :class:`SageObject` to avoid problems with coercions,
+ since the ``_eval_`` method is able to handle more data types than symbolic functions
would normally allow.
Thus we have the distinction between algebraic objects (if n is an integer),
and else as symbolic function.
@@ -563,7 +563,7 @@ def __call__(self, n, *args, **kwds):
Univariate Polynomial Ring in x over Rational Field
sage: chebyshev_T(5, 2, hold=True) # needs sage.symbolic
chebyshev_T(5, 2)
- sage: chebyshev_T(1,2,3)
+ sage: chebyshev_T(1, 2, 3)
Traceback (most recent call last):
...
TypeError: Symbolic function chebyshev_T takes exactly 2 arguments (3 given)
@@ -599,11 +599,11 @@ def _eval_(self, n, x):
chebyshev_T(3/2, x)
sage: R. = QQ[]
- sage: chebyshev_T(2,t)
+ sage: chebyshev_T(2, t)
2*t^2 - 1
- sage: chebyshev_U(2,t)
+ sage: chebyshev_U(2, t)
4*t^2 - 1
- sage: parent(chebyshev_T(4, RIF(5)))
+ sage: parent(chebyshev_T(4, RIF(5))) # needs sage.rings.real_interval_field
Real Interval Field with 53 bits of precision
sage: RR2 = RealField(5) # needs sage.rings.real_mpfr
sage: chebyshev_T(100000, RR2(2)) # needs sage.rings.real_mpfr
@@ -779,8 +779,9 @@ def _evalf_(self, n, x, **kwds):
sage: chebyshev_T._evalf_(10^6, 0.1) # needs sage.rings.real_mpfr
Traceback (most recent call last):
...
- NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms.
- sage: chebyshev_T(10^6, 0.1)
+ NoConvergence: Hypergeometric series converges too slowly.
+ Try increasing maxterms.
+ sage: chebyshev_T(10^6, 0.1) # needs sage.rings.real_mpfr
0.636384327171504
"""
try:
@@ -976,9 +977,9 @@ class Func_chebyshev_U(ChebyshevFunction):
EXAMPLES::
sage: R. = QQ[]
- sage: chebyshev_U(2,t)
+ sage: chebyshev_U(2, t)
4*t^2 - 1
- sage: chebyshev_U(3,t)
+ sage: chebyshev_U(3, t)
8*t^3 - 4*t
"""
def __init__(self):
@@ -1052,7 +1053,7 @@ def eval_formula(self, n, x):
1
sage: chebyshev_U.eval_formula(1, x)
2*x
- sage: chebyshev_U.eval_formula(2,0.1) == chebyshev_U._evalf_(2,0.1)
+ sage: chebyshev_U.eval_formula(2, 0.1) == chebyshev_U._evalf_(2, 0.1)
True
"""
if n < -1:
@@ -1086,9 +1087,9 @@ def eval_algebraic(self, n, x):
Ring of integers modulo 9
sage: chebyshev_U(-3, x) + chebyshev_U(1, x) # needs sage.symbolic
0
- sage: chebyshev_U(-1,Mod(5,8))
+ sage: chebyshev_U(-1, Mod(5,8))
0
- sage: parent(chebyshev_U(-1,Mod(5,8)))
+ sage: parent(chebyshev_U(-1, Mod(5,8)))
Ring of integers modulo 8
sage: R. = ZZ[]
sage: chebyshev_U.eval_algebraic(-2, t)
@@ -1102,8 +1103,10 @@ def eval_algebraic(self, n, x):
sage: n = 97; x = RIF(pi/n) # needs sage.symbolic
sage: chebyshev_U(n - 1, cos(x)).contains_zero() # needs sage.symbolic
True
- sage: R. = Zp(2, 6, 'capped-abs')[] # needs sage.rings.padics
- sage: chebyshev_U(10^6 + 1, t) # needs sage.rings.padics
+
+ sage: # needs sage.rings.padics
+ sage: R. = Zp(2, 6, 'capped-abs')[]
+ sage: chebyshev_U(10^6 + 1, t)
(2 + O(2^6))*t + O(2^6)
"""
if n == -1:
@@ -1142,7 +1145,7 @@ def _evalf_(self, n, x, **kwds):
EXAMPLES::
- sage: chebyshev_U(5,-4+3.*I) # needs sage.symbolic
+ sage: chebyshev_U(5, -4 + 3.*I) # needs sage.symbolic
98280.0000000000 - 11310.0000000000*I
sage: chebyshev_U(10, 3).n(75) # needs sage.symbolic
4.661117900000000000000e7
@@ -1676,7 +1679,7 @@ class Func_assoc_legendre_P(BuiltinFunction):
-sqrt(-x^2 + 1)
sage: gen_legendre_P(1, 1, 0.5) # abs tol 1e-14 # needs mpmath
-0.866025403784439
- sage: gen_legendre_P.eval_gen_poly(1, 1, 0.5) # abs tol 1e-14
+ sage: gen_legendre_P.eval_gen_poly(1, 1, 0.5) # abs tol 1e-14 # needs sage.rings.real_mpfr
-0.866025403784439
sage: gen_legendre_P._evalf_(1, 1, 0.5) # abs tol 1e-14 # needs mpmath
-0.866025403784439
@@ -1831,7 +1834,7 @@ def _evalf_(self, n, m, x, parent=None, **kwds):
sage: gen_legendre_P(10, 2, 3).n() # abs tol 1e-14 # needs sage.symbolic
-7.19496360000000e8
- sage: gen_legendre_P(5/2,2,1.+I) # needs sage.symbolic
+ sage: gen_legendre_P(5/2, 2, 1. + I) # needs sage.symbolic
14.3165258449040 - 12.7850496155152*I
sage: gen_legendre_P(5/2, 2, ComplexField(70)(1+I)) # needs sage.rings.real_mpfr sage.symbolic
14.316525844904028532 - 12.785049615515157033*I
@@ -2145,9 +2148,9 @@ class Func_jacobi_P(OrthogonalFunction):
EXAMPLES::
sage: x = PolynomialRing(QQ, 'x').gen()
- sage: jacobi_P(2,0,0,x)
+ sage: jacobi_P(2, 0, 0, x) # needs sage.libs.flint sage.symbolic
3/2*x^2 - 1/2
- sage: jacobi_P(2,1,2,1.2) # needs sage.symbolic
+ sage: jacobi_P(2, 1, 2, 1.2) # needs sage.libs.flint
5.01000000000000
"""
def __init__(self):
@@ -2205,18 +2208,18 @@ def _eval_(self, n, a, b, x):
Check that :issue:`17192` is fixed::
sage: x = PolynomialRing(QQ, 'x').gen()
- sage: jacobi_P(0,0,0,x)
+ sage: jacobi_P(0, 0, 0, x) # needs sage.libs.flint sage.symbolic
1
- sage: jacobi_P(-1,0,0,x)
+ sage: jacobi_P(-1, 0, 0, x) # needs sage.libs.flint sage.symbolic
1
- sage: jacobi_P(-1,1,1,x)
+ sage: jacobi_P(-1, 1, 1, x) # needs sage.libs.flint sage.symbolic
Traceback (most recent call last):
...
ValueError: n must be greater than -1, got n = -1
- sage: jacobi_P(-7,0,0,x)
+ sage: jacobi_P(-7, 0, 0, x) # needs sage.libs.flint sage.symbolic
231/16*x^6 - 315/16*x^4 + 105/16*x^2 - 5/16
- sage: jacobi_P(-7,0,2,x)
+ sage: jacobi_P(-7, 0, 2, x) # needs sage.symbolic
Traceback (most recent call last):
...
ValueError: n must be greater than -1, got n = -7
@@ -2324,11 +2327,11 @@ class Func_ultraspherical(GinacFunction):
sage: # needs sage.symbolic
sage: gegenbauer(2, -3, x)
12*x^2 + 3
- sage: gegenbauer(120,-99/2,3)
+ sage: gegenbauer(120, -99/2, 3)
1654502372608570682112687530178328494861923493372493824
sage: gegenbauer(5, 9/2, x)
21879/8*x^5 - 6435/4*x^3 + 1287/8*x
- sage: gegenbauer(15,3/2,5)
+ sage: gegenbauer(15, 3/2, 5)
3903412392243800
sage: derivative(gegenbauer(n, a, x), x) # needs sage.symbolic
@@ -2484,7 +2487,7 @@ def _pol_laguerre(self, n, x):
1/24*x^4 - 2/3*x^3 + 3*x^2 - 4*x + 1
sage: laguerre(4, x + 1) # needs mpmath
1/24*(x + 1)^4 - 2/3*(x + 1)^3 + 3*(x + 1)^2 - 4*x - 3
- sage: laguerre(10,1+I) # needs sage.symbolic
+ sage: laguerre(10, 1 + I) # needs sage.symbolic
142511/113400*I + 95867/22680
"""
if hasattr(x, 'pyobject'):
@@ -2503,7 +2506,7 @@ def _evalf_(self, n, x, **kwds):
sage: laguerre(100, RealField(300)(pi)) # needs sage.symbolic
-0.638322077840648311606324...
- sage: laguerre(10,1.+I) # needs sage.symbolic
+ sage: laguerre(10, 1. + I) # needs sage.symbolic
4.22694003527337 + 1.25671075837743*I
sage: laguerre(-9, 2.) # needs sage.symbolic
1566.22186244286
@@ -2638,7 +2641,7 @@ def _pol_gen_laguerre(self, n, a, x):
1/24*x^4 - 7/12*x^3 + 35/16*x^2 - 35/16*x + 35/128
sage: gen_laguerre(4, -1/2, x + 1) # needs mpmath
1/24*(x + 1)^4 - 7/12*(x + 1)^3 + 35/16*(x + 1)^2 - 35/16*x - 245/128
- sage: gen_laguerre(10, 1, 1+I) # needs sage.symbolic
+ sage: gen_laguerre(10, 1, 1 + I) # needs sage.symbolic
25189/2100*I + 11792/2835
"""
return sum(binomial(n + a, n - k) * (-1)**k / factorial(k) * x**k
diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py
index 3da7beec8ce..23450acb02e 100644
--- a/src/sage/functions/other.py
+++ b/src/sage/functions/other.py
@@ -173,7 +173,7 @@ def _eval_floor_ceil(self, x, method, bits=0, **kwds):
These do not work but fail gracefully::
- sage: ceil(Infinity)
+ sage: ceil(Infinity) # needs sage.rings.real_interval_field
Traceback (most recent call last):
...
ValueError: Calling ceil() on infinity or NaN
@@ -714,9 +714,9 @@ def __init__(self):
EXAMPLES::
- sage: frac(5.4)
+ sage: frac(5.4) # needs sage.rings.real_mpfr
0.400000000000000
- sage: type(frac(5.4))
+ sage: type(frac(5.4)) # needs sage.rings.real_mpfr
sage: frac(456/123)
29/41
@@ -812,9 +812,9 @@ class Function_real_nth_root(BuiltinFunction):
For numeric input, it gives a numerical approximation. ::
- sage: real_nth_root(2., 3)
+ sage: real_nth_root(2., 3) # needs sage.rings.real_mpfr
1.25992104989487
- sage: real_nth_root(-2., 3)
+ sage: real_nth_root(-2., 3) # needs sage.rings.real_mpfr
-1.25992104989487
Some symbolic calculus::
@@ -914,9 +914,9 @@ def _eval_(self, base, exp):
sage: real_nth_root(x, 3) # needs sage.symbolic
real_nth_root(x, 3)
- sage: real_nth_root(RIF(2), 3)
+ sage: real_nth_root(RIF(2), 3) # needs sage.rings.real_interval_field
1.259921049894873?
- sage: real_nth_root(RBF(2), 3)
+ sage: real_nth_root(RBF(2), 3) # needs sage.libs.flint
[1.259921049894873 +/- 3.92e-16]
"""
if not isinstance(base, Expression) and not isinstance(exp, Expression):
@@ -1145,9 +1145,9 @@ def __init__(self):
sage: real(5/3)
5/3
sage: a = 2.5
- sage: real(a)
+ sage: real(a) # needs sage.rings.real_mpfr
2.50000000000000
- sage: type(real(a))
+ sage: type(real(a)) # needs sage.rings.real_mpfr
sage: real(1.0r)
1.0
diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py
index f621c324cc4..677f5a06bd4 100644
--- a/src/sage/functions/piecewise.py
+++ b/src/sage/functions/piecewise.py
@@ -113,7 +113,7 @@ def __call__(self, function_pieces, **kwds):
OUTPUT:
- A piecewise-defined function. A ``ValueError`` will be raised
+ A piecewise-defined function. A :class:`ValueError` will be raised
if the domains of the pieces are not pairwise disjoint.
EXAMPLES::
@@ -213,17 +213,16 @@ def _subs_(self, subs_map, options, parameters, x):
piecewise(x|-->-x^sin(y) on (-2, 0), x|-->x - sin(y) on [0, 2]; x)
"""
point = subs_map.apply_to(x, 0)
- if point == x:
+ if ((point.is_numeric() or point.is_constant()) and (point.is_real())):
+ if hasattr(point, 'pyobject'):
+ # unwrap any numeric values
+ point = point.pyobject()
+ elif point == x: # this comparison may be very slow (see #37925)
# substitution only in auxiliary variables
new_params = []
for domain, func in parameters:
new_params.append((domain, subs_map.apply_to(func, 0)))
return piecewise(new_params, var=x)
- if ((point.is_numeric() or point.is_constant())
- and (point.is_real())):
- if hasattr(point, 'pyobject'):
- # unwrap any numeric values
- point = point.pyobject()
else:
raise ValueError('substituting the piecewise variable must result in real number')
@@ -291,7 +290,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds):
EXAMPLES::
- sage: f = piecewise([ [(-1,1), x**2], [(1,3), x**3]])
+ sage: f = piecewise([[(-1,1), x**2], [(1,3), x**3]])
sage: f.diff()
piecewise(x|-->2*x on (-1, 1), x|-->3*x^2 on (1, 3); x)
sage: f.diff(x,x)
@@ -300,7 +299,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds):
This still fails miserably::
sage: y = SR.var('y')
- sage: f = piecewise([ [(-6,0), x+y], [(0,8), x*y]],var=x)
+ sage: f = piecewise([[(-6,0), x+y], [(0,8), x*y]],var=x)
sage: f.derivative(x) # known bug
piecewise(x|-->1 on (-6, 0), x|-->y on (0, 8); x)
sage: f.derivative(y) # known bug
@@ -308,7 +307,7 @@ def _tderivative_(self, parameters, variable, *args, **kwds):
TESTS::
- sage: f = piecewise([((-oo, -1),0), ((-1, 1),exp(-1/(1 - x^2))), ((1, oo),0)])
+ sage: f = piecewise([((-oo, -1), 0), ((-1, 1), exp(-1/(1 - x^2))), ((1, oo), 0)])
sage: f.diff()
piecewise(x|-->0 on (-oo, -1), x|-->-2*x*e^(1/(x^2 - 1))/(x^2 - 1)^2 on (-1, 1), x|-->0 on (1, +oo); x)
"""
@@ -330,7 +329,7 @@ def __pow__(self, parameters, variable, n):
EXAMPLES::
sage: f1(x) = -abs(x) + 1; f2(x) = abs(x - 2) - 1
- sage: f = piecewise([ [(-1,1), f1], [(1,3), f2]])
+ sage: f = piecewise([[(-1,1), f1], [(1,3), f2]])
sage: (f^2).integral(definite=True)
4/3
"""
@@ -635,9 +634,9 @@ def end_points(self, parameters, variable):
EXAMPLES::
sage: f1(x) = 1
- sage: f2(x) = 1-x
- sage: f3(x) = x^2-5
- sage: f = piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]])
+ sage: f2(x) = 1 - x
+ sage: f3(x) = x^2 - 5
+ sage: f = piecewise([[(0,1), f1], [(1,2), f2], [(2,3), f3]])
sage: f.end_points()
[0, 1, 2, 3]
sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f
@@ -665,7 +664,9 @@ def piecewise_add(self, parameters, variable, other):
sage: f = piecewise([([0,1], 1), ((2,3), x)])
sage: g = piecewise([((1/2, 2), x)])
sage: f.piecewise_add(g).unextend_zero()
- piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) ∪ (2, 3); x)
+ piecewise(x|-->1 on (0, 1/2],
+ x|-->x + 1 on (1/2, 1],
+ x|-->x on (1, 2) ∪ (2, 3); x)
"""
points = ([minus_infinity] +
sorted(set(self.end_points() + other.end_points())) +
@@ -731,7 +732,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
r"""
By default, return the indefinite integral of the function.
- If definite=True is given, returns the definite integral.
+ If ``definite=True`` is given, returns the definite integral.
AUTHOR:
@@ -739,8 +740,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
EXAMPLES::
- sage: f1(x) = 1-x
- sage: f = piecewise([((0,1),1), ((1,2),f1)])
+ sage: f1(x) = 1 - x
+ sage: f = piecewise([((0,1), 1), ((1,2), f1)])
sage: f.integral(definite=True)
1/2
@@ -748,7 +749,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
sage: f1(x) = -1
sage: f2(x) = 2
- sage: f = piecewise([((0,pi/2),f1), ((pi/2,pi),f2)])
+ sage: f = piecewise([((0,pi/2), f1), ((pi/2,pi), f2)])
sage: f.integral(definite=True)
1/2*pi
@@ -764,8 +765,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
sage: f3(y) = -y - 1
sage: f4(y) = y^2 - 1
sage: f5(y) = 3
- sage: f = piecewise([[[-4,-3],f1], [(-3,-2),f2], [[-2,0],f3],
- ....: [(0,2),f4], [[2,3],f5]])
+ sage: f = piecewise([[[-4,-3], f1], [(-3,-2), f2], [[-2,0], f3],
+ ....: [(0,2), f4], [[2,3], f5]])
sage: F = f.integral(y); F
piecewise(y|-->-y - 4 on [-4, -3],
y|-->1/2*y^2 + 3*y + 7/2 on (-3, -2),
@@ -795,7 +796,8 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
sage: f1(y) = (y+3)^2
sage: f2(y) = y+3
sage: f3(y) = 3
- sage: f = piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]])
+ sage: f = piecewise([[(-infinity, -3), f1], [(-3, 0), f2],
+ ....: [(0, infinity), f3]])
sage: f.integral()
piecewise(y|-->1/3*y^3 + 3*y^2 + 9*y + 9 on (-oo, -3),
y|-->1/2*y^2 + 3*y + 9/2 on (-3, 0),
@@ -823,7 +825,7 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
Verify that piecewise integrals of zero work (:issue:`10841`)::
sage: f0(x) = 0
- sage: f = piecewise([[[0,1],f0]])
+ sage: f = piecewise([[[0,1], f0]])
sage: f.integral(x,0,1)
0
sage: f = piecewise([[[0,1], 0]])
@@ -836,9 +838,9 @@ def integral(self, parameters, variable, x=None, a=None, b=None, definite=False,
Check that the algorithm keyword can be used::
sage: ex = piecewise([([0, 1], 1), ((1, oo), 1/x**2)])
- sage: integral(ex,x,0,100,algorithm='giac')
+ sage: integral(ex, x, 0, 100, algorithm='giac')
199/100
- sage: integral(ex,x,algorithm='giac')
+ sage: integral(ex, x, algorithm='giac')
piecewise(x|-->x on [0, 1], x|-->-1/x + 2 on (1, +oo); x)
"""
if a is not None and b is not None:
@@ -901,7 +903,7 @@ def critical_points(self, parameters, variable):
sage: f1 = x^0
sage: f2 = 10*x - x^2
sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x
- sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]])
+ sage: f = piecewise([[(0,3), f1], [(3,10), f2], [(10,20), f3]])
sage: expected = [5, 12, 13, 14]
sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points()))
True
@@ -914,7 +916,7 @@ def critical_points(self, parameters, variable):
sage: f1 = y^0
sage: f2 = 10*y - y^2
sage: f3 = 3*y^4 - 156*y^3 + 3036*y^2 - 26208*y
- sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]])
+ sage: f = piecewise([[(0,3), f1], [(3,10), f2], [(10,20), f3]])
sage: expected = [5, 12, 13, 14]
sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points()))
True
@@ -940,15 +942,22 @@ def convolution(self, parameters, variable, other):
EXAMPLES::
- sage: x = PolynomialRing(QQ,'x').gen()
- sage: f = piecewise([[[0,1],1]]) ## example 0
+ sage: x = PolynomialRing(QQ, 'x').gen()
+
+ Example 0::
+
+ sage: f = piecewise([[[0,1], 1]])
sage: g = f.convolution(f); g
- piecewise(x|-->x on (0, 1], x|-->-x + 2 on (1, 2]; x)
+ piecewise(x|-->x on (0, 1],
+ x|-->-x + 2 on (1, 2]; x)
sage: h = f.convolution(g); h
piecewise(x|-->1/2*x^2 on (0, 1],
x|-->-x^2 + 3*x - 3/2 on (1, 2],
x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x)
- sage: f = piecewise([[(0,1),1], [(1,2),2], [(2,3),1]]) ## example 1
+
+ Example 1::
+
+ sage: f = piecewise([[(0,1), 1], [(1,2), 2], [(2,3), 1]])
sage: g = f.convolution(f)
sage: h = f.convolution(g); h
piecewise(x|-->1/2*x^2 on (0, 1],
@@ -958,13 +967,16 @@ def convolution(self, parameters, variable, other):
x|-->-2*x^2 + 15*x - 15/2 on (5, 6],
x|-->2*x^2 - 33*x + 273/2 on (6, 8],
x|-->1/2*x^2 - 9*x + 81/2 on (8, 9]; x)
- sage: f = piecewise([[(-1,1),1]]) ## example 2
- sage: g = piecewise([[(0,3),x]])
+
+ Example 2::
+
+ sage: f = piecewise([[(-1,1), 1]])
+ sage: g = piecewise([[(0,3), x]])
sage: f.convolution(g)
piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 1],
x|-->2*x on (1, 2],
x|-->-1/2*x^2 + x + 4 on (2, 4]; x)
- sage: g = piecewise([[(0,3),1], [(3,4),2]])
+ sage: g = piecewise([[(0,3), 1], [(3,4), 2]])
sage: f.convolution(g)
piecewise(x|-->x + 1 on (-1, 1],
x|-->2 on (1, 2],
@@ -1037,7 +1049,8 @@ def trapezoid(self, parameters, variable, N):
EXAMPLES::
- sage: f = piecewise([[[0,1], x^2], [RealSet.open_closed(1,2), 5-x^2]])
+ sage: f = piecewise([[[0,1], x^2],
+ ....: [RealSet.open_closed(1,2), 5 - x^2]])
sage: f.trapezoid(2)
piecewise(x|-->1/2*x on (0, 1/2),
x|-->3/2*x - 1/2 on (1/2, 1),
@@ -1056,8 +1069,8 @@ def trapezoid(self, parameters, variable, N):
sage: R. = QQ[]
sage: f1 = y^2
- sage: f2 = 5-y^2
- sage: f = piecewise([[[0,1],f1], [RealSet.open_closed(1,2),f2]])
+ sage: f2 = 5 - y^2
+ sage: f = piecewise([[[0,1], f1], [RealSet.open_closed(1,2), f2]])
sage: f.trapezoid(2)
piecewise(y|-->1/2*y on (0, 1/2),
y|-->3/2*y - 1/2 on (1/2, 1),
@@ -1082,14 +1095,14 @@ def func(x0, x1):
def laplace(self, parameters, variable, x='x', s='t'):
r"""
- Returns the Laplace transform of self with respect to the variable
+ Return the Laplace transform of ``self`` with respect to the variable
var.
INPUT:
- - ``x`` - variable of self
+ - ``x`` -- variable of ``self``
- - ``s`` - variable of Laplace transform.
+ - ``s`` -- variable of Laplace transform.
We assume that a piecewise function is 0 outside of its domain and
that the left-most endpoint of the domain is 0.
@@ -1097,7 +1110,7 @@ def laplace(self, parameters, variable, x='x', s='t'):
EXAMPLES::
sage: x, s, w = var('x, s, w')
- sage: f = piecewise([[(0,1),1], [[1,2], 1 - x]])
+ sage: f = piecewise([[(0,1), 1], [[1,2], 1 - x]])
sage: f.laplace(x, s)
-e^(-s)/s + (s + 1)*e^(-2*s)/s^2 + 1/s - e^(-s)/s^2
sage: f.laplace(x, w)
@@ -1116,8 +1129,8 @@ def laplace(self, parameters, variable, x='x', s='t'):
sage: t = var('t')
sage: f1(t) = -t
sage: f2(t) = 2
- sage: f = piecewise([[[0,1],f1], [(1,infinity),f2]])
- sage: f.laplace(t,s)
+ sage: f = piecewise([[[0,1], f1], [(1,infinity), f2]])
+ sage: f.laplace(t, s)
(s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2
"""
from sage.symbolic.assumptions import assume, forget
@@ -1172,7 +1185,7 @@ def fourier_series_cosine_coefficient(self, parameters,
A triangle wave function of period 2::
- sage: f = piecewise([((0,1), x), ((1,2), 2-x)])
+ sage: f = piecewise([((0,1), x), ((1,2), 2 - x)])
sage: f.fourier_series_cosine_coefficient(0)
1
sage: f.fourier_series_cosine_coefficient(3)
@@ -1203,13 +1216,13 @@ def fourier_series_cosine_coefficient(self, parameters,
Other examples::
sage: f(x) = x^2
- sage: f = piecewise([[(-1,1),f]])
+ sage: f = piecewise([[(-1,1), f]])
sage: f.fourier_series_cosine_coefficient(2)
pi^(-2)
sage: f1(x) = -1
sage: f2(x) = 2
- sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]])
- sage: f.fourier_series_cosine_coefficient(5,pi)
+ sage: f = piecewise([[(-pi, pi/2), f1], [(pi/2, pi), f2]])
+ sage: f.fourier_series_cosine_coefficient(5, pi)
-3/5/pi
"""
diff --git a/src/sage/functions/spike_function.py b/src/sage/functions/spike_function.py
index 4739ca7ffb4..c2fb6113e39 100644
--- a/src/sage/functions/spike_function.py
+++ b/src/sage/functions/spike_function.py
@@ -71,7 +71,7 @@ def __init__(self, v, eps=0.0000001):
A spike function with spikes at [-3.0, -1.0, 2.0]
sage: S.height
[4.0, 1.0, 3.0]
- sage: S.eps
+ sage: S.eps # needs sage.rings.real_mpfr
0.00100000000000000
"""
if not v:
diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py
index c0debecdc0d..236646bae52 100644
--- a/src/sage/functions/transcendental.py
+++ b/src/sage/functions/transcendental.py
@@ -173,7 +173,7 @@ def __init__(self):
INPUT:
- - ``n`` - non-negative integer
+ - ``n`` -- non-negative integer
EXAMPLES::
@@ -265,11 +265,11 @@ def _evalf_(self, s, x, parent=None, algorithm=None):
r"""
TESTS::
- sage: hurwitz_zeta(11/10, 1/2).n() # needs sage.symbolic
+ sage: hurwitz_zeta(11/10, 1/2).n() # needs mpmath sage.symbolic
12.1038134956837
- sage: hurwitz_zeta(11/10, 1/2).n(100) # needs sage.symbolic
+ sage: hurwitz_zeta(11/10, 1/2).n(100) # needs mpmath sage.symbolic
12.103813495683755105709077413
- sage: hurwitz_zeta(11/10, 1 + 1j).n()
+ sage: hurwitz_zeta(11/10, 1 + 1j).n() # needs mpmath sage.rings.real_mpfr
9.85014164287853 - 1.06139499403981*I
"""
return _mpmath_utils_call(_mpmath_zeta, s, x, parent=parent)
@@ -571,48 +571,54 @@ def power_series(self, n, abs_prec):
"""
This function returns the power series about `n+1/2` used
to evaluate Dickman's function. It is scaled such that the interval
- `[n,n+1]` corresponds to x in `[-1,1]`.
+ `[n,n+1]` corresponds to `x` in `[-1,1]`.
INPUT:
- - ``n`` - the lower endpoint of the interval for which
+ - ``n`` -- the lower endpoint of the interval for which
this power series holds
- - ``abs_prec`` - the absolute precision of the
+ - ``abs_prec`` -- the absolute precision of the
resulting power series
EXAMPLES::
+ sage: # needs sage.rings.real_mpfr
sage: f = dickman_rho.power_series(2, 20); f
- -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032
+ -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8
+ - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4
+ - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032
sage: f(-1), f(0), f(1)
(0.30685, 0.13032, 0.048608)
- sage: dickman_rho(2), dickman_rho(2.5), dickman_rho(3) # needs sage.symbolic
+ sage: dickman_rho(2), dickman_rho(2.5), dickman_rho(3)
(0.306852819440055, 0.130319561832251, 0.0486083882911316)
"""
return self._compute_power_series(n, abs_prec, cache_ring=None)
def _compute_power_series(self, n, abs_prec, cache_ring=None):
"""
- Compute the power series giving Dickman's function on [n, n+1], by
- recursion in n. For internal use; self.power_series() is a wrapper
+ Compute the power series giving Dickman's function on `[n, n+1]`, by
+ recursion in `n`. For internal use; ``self.power_series()`` is a wrapper
around this intended for the user.
INPUT:
- - ``n`` - the lower endpoint of the interval for which
+ - ``n`` -- the lower endpoint of the interval for which
this power series holds
- - ``abs_prec`` - the absolute precision of the
+ - ``abs_prec`` -- the absolute precision of the
resulting power series
- - ``cache_ring`` - for internal use, caches the power
+ - ``cache_ring`` -- for internal use, caches the power
series at this precision.
EXAMPLES::
+ sage: # needs sage.rings.real_mpfr
sage: f = dickman_rho.power_series(2, 20); f
- -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8 - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4 - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032
+ -9.9376e-8*x^11 + 3.7722e-7*x^10 - 1.4684e-6*x^9 + 5.8783e-6*x^8
+ - 0.000024259*x^7 + 0.00010341*x^6 - 0.00045583*x^5 + 0.0020773*x^4
+ - 0.0097336*x^3 + 0.045224*x^2 - 0.11891*x + 0.13032
"""
if n <= 1:
if n <= -1:
diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py
index d4e723ec6e2..b767fbbd5d4 100644
--- a/src/sage/functions/trig.py
+++ b/src/sage/functions/trig.py
@@ -210,6 +210,7 @@ def __init__(self):
EXAMPLES::
+ sage: # needs sage.rings.real_mpfr
sage: tan(3.1415)
-0.0000926535900581913
sage: tan(3.1415/4)
@@ -323,20 +324,20 @@ def __init__(self):
TESTS::
- sage: cot(float(0)) # needs sage.symbolic
+ sage: # needs sage.symbolic
+ sage: cot(float(0))
Infinity
- sage: cot(SR(0)) # needs sage.symbolic
+ sage: cot(SR(0))
Infinity
- sage: cot(float(0.1)) # needs sage.symbolic
+ sage: cot(float(0.1))
9.966644423259238
sage: type(_)
<... 'float'>
-
- sage: cot(float(0)) # needs sage.symbolic
+ sage: cot(float(0))
Infinity
- sage: cot(SR(0)) # needs sage.symbolic
+ sage: cot(SR(0))
Infinity
- sage: cot(float(0.1)) # needs sage.symbolic
+ sage: cot(float(0.1))
9.966644423259238
sage: type(_)
<... 'float'>
@@ -520,7 +521,7 @@ def __init__(self):
EXAMPLES::
- sage: arcsin(0.5)
+ sage: arcsin(0.5) # needs sage.rings.real_mpfr
0.523598775598299
sage: arcsin(1/2) # needs sage.symbolic
1/6*pi
@@ -584,7 +585,7 @@ def __init__(self):
EXAMPLES::
- sage: arccos(0.5)
+ sage: arccos(0.5) # needs sage.rings.real_mpfr
1.04719755119660
sage: arccos(1/2) # needs sage.symbolic
1/3*pi
@@ -940,7 +941,7 @@ def __init__(self):
sage: maxima.atan2(1, -1) # needs sage.symbolic
(3*%pi)/4
- sage: math.atan2(1,-1)
+ sage: math.atan2(1, -1)
2.356194490192345
More examples::
diff --git a/src/sage/functions/wigner.py b/src/sage/functions/wigner.py
index 0bafe13a246..5a19010f073 100644
--- a/src/sage/functions/wigner.py
+++ b/src/sage/functions/wigner.py
@@ -616,11 +616,11 @@ def gaunt(l_1, l_2, l_3, m_1, m_2, m_3, prec=None):
It is an error to use non-integer values for `l` or `m`::
- sage: gaunt(1.2,0,1.2,0,0,0)
+ sage: gaunt(1.2,0,1.2,0,0,0) # needs sage.rings.real_mpfr
Traceback (most recent call last):
...
TypeError: Attempt to coerce non-integral RealNumber to Integer
- sage: gaunt(1,0,1,1.1,0,-1.1)
+ sage: gaunt(1,0,1,1.1,0,-1.1) # needs sage.rings.real_mpfr
Traceback (most recent call last):
...
TypeError: Attempt to coerce non-integral RealNumber to Integer
diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py
deleted file mode 100644
index b2d8af991e9..00000000000
--- a/src/sage/game_theory/gambit_docs.py
+++ /dev/null
@@ -1,139 +0,0 @@
-"""
-Using Gambit as a standalone package
-
-This file contains some information and tests for the use of
-`Gambit `_ as a stand alone package.
-
-To install gambit as an optional package run (from root of Sage)::
-
- $ sage -i gambit
-
-The `python API documentation for gambit
-`_ shows various examples
-that can be run easily in IPython. To run the IPython packaged with Sage run
-(from root of Sage)::
-
- $ ./sage --ipython
-
-Here is an example that constructs the Prisoner's Dilemma::
-
- In [1]: import gambit
- In [2]: g = gambit.Game.new_table([2,2])
- In [3]: g.title = "A prisoner's dilemma game"
- In [4]: g.players[0].label = "Alphonse"
- In [5]: g.players[1].label = "Gaston"
- In [6]: g
- Out[6]:
- NFG 1 R "A prisoner's dilemma game" { "Alphonse" "Gaston" }
-
- { { "1" "2" }
- { "1" "2" }
- }
- ""
-
- {
- { "" 0, 0 }
- { "" 0, 0 }
- { "" 0, 0 }
- { "" 0, 0 }
- }
- 1 2 3 4
-
- In [7]: g.players[0].strategies
- Out[7]: [,
- ]
- In [8]: len(g.players[0].strategies)
- Out[8]: 2
-
- In [9]: g.players[0].strategies[0].label = "Cooperate"
- In [10]: g.players[0].strategies[1].label = "Defect"
- In [11]: g.players[0].strategies
- Out[11]: [,
- ]
-
- In [12]: g[0,0][0] = 8
- In [13]: g[0,0][1] = 8
- In [14]: g[0,1][0] = 2
- In [15]: g[0,1][1] = 10
- In [16]: g[1,0][0] = 10
- In [17]: g[1,1][1] = 2
- In [18]: g[1,0][1] = 2
- In [19]: g[1,1][0] = 5
- In [20]: g[1,1][1] = 5
-
-Here is a list of the various solvers available in gambit:
-
-- ExternalEnumPureSolver
-- ExternalEnumMixedSolver
-- ExternalLPSolver
-- ExternalLCPSolver
-- ExternalSimpdivSolver
-- ExternalGlobalNewtonSolver
-- ExternalEnumPolySolver
-- ExternalLyapunovSolver
-- ExternalIteratedPolymatrixSolver
-- ExternalLogitSolver
-
-Here is how to use the ``ExternalEnumPureSolver``::
-
- In [21]: solver = gambit.nash.ExternalEnumPureSolver()
- In [22]: solver.solve(g)
- Out[22]: []
-
-Note that the above finds the equilibria by investigating all potential pure
-pure strategy pairs. This will fail to find all Nash equilibria in certain
-games. For example here is an implementation of Matching Pennies::
-
- In [1]: import gambit
- In [2]: g = gambit.Game.new_table([2,2])
- In [3]: g[0, 0][0] = 1
- In [4]: g[0, 0][1] = -1
- In [5]: g[0, 1][0] = -1
- In [6]: g[0, 1][1] = 1
- In [7]: g[1, 0][0] = -1
- In [8]: g[1, 0][1] = 1
- In [9]: g[1, 1][0] = 1
- In [10]: g[1, 1][1] = -1
- In [11]: solver = gambit.nash.ExternalEnumPureSolver()
- In [12]: solver.solve(g)
- Out[12]: []
-
-If we solve this with the ``LCP`` solver we get the expected Nash equilibrium::
-
- In [13]: solver = gambit.nash.ExternalLCPSolver()
- In [14]: solver.solve(g)
- Out[14]: []
-
-Note that the above examples only show how to build and find equilibria for
-two player strategic form games. Gambit supports multiple player games as well
-as extensive form games: for more details see http://www.gambit-project.org/.
-
-If one really wants to use gambit directly in Sage (without using the
-``NormalFormGame`` class as a wrapper) then integers must first be
-converted to Python integers (due to the preparser). Here is an example
-showing the Battle of the Sexes::
-
- sage: # optional - gambit
- sage: import gambit
- sage: g = gambit.Game.new_table([2,2])
- sage: g[int(0), int(0)][int(0)] = int(2)
- sage: g[int(0), int(0)][int(1)] = int(1)
- sage: g[int(0), int(1)][int(0)] = int(0)
- sage: g[int(0), int(1)][int(1)] = int(0)
- sage: g[int(1), int(0)][int(0)] = int(0)
- sage: g[int(1), int(0)][int(1)] = int(0)
- sage: g[int(1), int(1)][int(0)] = int(1)
- sage: g[int(1), int(1)][int(1)] = int(2)
- sage: solver = gambit.nash.ExternalLCPSolver()
- sage: solver.solve(g)
- [,
- ,
- ]
-
-AUTHOR:
-
-- Vince Knight (11-2014): Original version
-
-"""
diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py
index b053f3160b8..93cfe25a883 100644
--- a/src/sage/game_theory/normal_form_game.py
+++ b/src/sage/game_theory/normal_form_game.py
@@ -236,8 +236,7 @@
`Gambit `_ [Gambit]_. At present this is
the only gambit algorithm available in sage but further development will
hope to implement more algorithms
- (in particular for games with more than 2 players). To install it,
- type ``sage -i gambit`` in the shell.
+ (in particular for games with more than 2 players).
* ``'enumeration'``: Support enumeration for 2 player games. This
algorithm is hard coded in Sage and checks through all potential
@@ -648,7 +647,6 @@
from sage.matrix.constructor import vector
from sage.misc.temporary_file import tmp_filename
from sage.numerical.mip import MixedIntegerLinearProgram
-from sage.misc.package import PackageNotFoundError
from sage.cpython.string import bytes_to_str
try:
@@ -1705,7 +1703,7 @@ def obtain_nash(self, algorithm=False, maximization=True, solver=None):
if algorithm == "LCP":
if Game is None:
- raise PackageNotFoundError("gambit")
+ raise RuntimeError("gambit not found") # should later become a FeatureNotFoundError
return self._solve_LCP(maximization)
if algorithm.startswith('lp'):
diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py
index 3b7dcf756d8..e4b4d933bc4 100644
--- a/src/sage/geometry/all.py
+++ b/src/sage/geometry/all.py
@@ -16,4 +16,5 @@
lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram')
lazy_import('sage.geometry.ribbon_graph', 'RibbonGraph')
lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements')
+lazy_import('sage.geometry.hyperplane_arrangement.ordered_arrangement', 'OrderedHyperplaneArrangements')
lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements')
diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py
index fc0b3731945..0d9689b4d2e 100644
--- a/src/sage/geometry/cone.py
+++ b/src/sage/geometry/cone.py
@@ -211,7 +211,6 @@
from sage.combinat.posets.posets import FinitePoset
from sage.geometry.point_collection import PointCollection
from sage.geometry.polyhedron.constructor import Polyhedron
-from sage.geometry.polyhedron.base import is_Polyhedron
from sage.geometry.hasse_diagram import lattice_from_incidences
from sage.geometry.toric_lattice import (ToricLattice, is_ToricLattice,
is_ToricLatticeQuotient)
@@ -259,6 +258,9 @@ def is_Cone(x):
sage: from sage.geometry.cone import is_Cone
sage: is_Cone(1)
+ doctest:warning...
+ DeprecationWarning: is_Cone is deprecated, use isinstance instead
+ See https://github.com/sagemath/sage/issues/34307 for details.
False
sage: quadrant = Cone([(1,0), (0,1)])
sage: quadrant
@@ -266,6 +268,8 @@ def is_Cone(x):
sage: is_Cone(quadrant)
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(34307, "is_Cone is deprecated, use isinstance instead")
return isinstance(x, ConvexRationalPolyhedralCone)
@@ -439,7 +443,7 @@ def Cone(rays, lattice=None, check=True, normalize=True):
0-d cone in 2-d lattice N
"""
# Cone from Polyhedron
- if is_Polyhedron(rays):
+ if isinstance(rays, sage.geometry.abc.Polyhedron):
polyhedron = rays
if lattice is None:
lattice = ToricLattice(polyhedron.ambient_dim())
@@ -1894,7 +1898,7 @@ def cartesian_product(self, other, lattice=None):
N+N(0, 1)
in 2-d lattice N+N
"""
- assert is_Cone(other)
+ assert isinstance(other, sage.geometry.abc.ConvexRationalPolyhedralCone)
rc = super().cartesian_product(other, lattice)
return ConvexRationalPolyhedralCone(rc.rays(), rc.lattice())
@@ -1952,7 +1956,7 @@ def __richcmp__(self, right, op):
sage: c2 is c3
False
"""
- if is_Cone(right):
+ if isinstance(right, sage.geometry.abc.ConvexRationalPolyhedralCone):
# We don't care about particular type of right in this case
return richcmp((self.lattice(), self.rays()),
(right.lattice(), right.rays()), op)
@@ -2413,7 +2417,7 @@ def embed(self, cone):
ValueError: 2-d cone in 3-d lattice N is not a face
of 3-d cone in 3-d lattice N!
"""
- assert is_Cone(cone)
+ assert isinstance(cone, sage.geometry.abc.ConvexRationalPolyhedralCone)
if cone.ambient() is self:
return cone
if self.is_strictly_convex():
@@ -2935,7 +2939,7 @@ def facet_of(self):
L = self._ambient._face_lattice_function()
H = L.hasse_diagram()
return self._sort_faces(
- f for f in H.neighbors_out(L(self)) if is_Cone(f))
+ f for f in H.neighbors_out(L(self)) if isinstance(f, sage.geometry.abc.ConvexRationalPolyhedralCone))
def facets(self):
r"""
@@ -6193,6 +6197,201 @@ def Z_operators_gens(self):
"""
return [ -cp for cp in self.cross_positive_operators_gens() ]
+ def max_angle(self, other=None, exact=True, epsilon=0):
+ r"""
+ Return the maximal angle between ``self`` and ``other``.
+
+ The maximal angle between two closed convex cones is the
+ unique largest angle formed by any two unit-norm vectors in
+ those cones. In pathological cases, this computation can fail.
+
+ If it fails when ``exact`` is ``True`` and if each of the
+ cones :meth:`is_strictly_convex`, then a second attempt will
+ be made using inexact arithmetic. (This sometimes avoids the
+ problem noted in [Or2024]_). If the computation fails when the
+ cones are not strictly convex or when ``exact`` is ``False``,
+ a :class:`ValueError` is raised.
+
+ INPUT:
+
+ - ``other`` -- (default: ``None``) a rational, polyhedral
+ convex cone
+
+ - ``exact`` -- (default: ``True``) whether or not to use exact
+ rational arithmetic instead of floating point computations;
+ beware that ``True`` is not guaranteed to avoid floating
+ point computations if the algorithm runs into trouble in
+ rational arithmetic
+
+ - ``epsilon`` -- (default: ``0``) the tolerance to use when
+ making comparisons
+
+ .. WARNING::
+
+ Using inexact arithmetic (``exact=False``) is faster, but
+ this computation is only known to be stable when both of
+ the cones are strictly convex (or when one of them is the
+ entire space, but the maximal angle is obviously `\pi` in
+ that case).
+
+ OUTPUT:
+
+ A triple `\left( \theta_{\max}, u, v \right)`
+ containing:
+
+ - the maximal angle `\theta_{\max}` between ``self`` and
+ ``other``
+
+ - a vector `u` in ``self`` that achieves the maximal angle
+
+ - a vector `v` in ``other`` that achieves the maximal angle
+
+ If ``other`` is ``None`` (the default), then the maximal angle
+ within this cone (between this cone and itself) is returned.
+
+ If an eigenspace of dimension greater than one is encountered
+ and if the corresponding angle cannot be ruled out as a
+ maximum, the behavior of this function depends on
+ ``exact``:
+
+ - If ``exact`` is ``True`` and if both ``self`` and ``other``
+ are strictly convex, then the algorithm will fall back to
+ inexact arithmetic. In that case, the returned angle and
+ vectors will be over :class:`sage.rings.real_double.RDF`.
+
+ - If ``exact`` is ``False`` or if either cone is not strictly
+ convex, then a :class:`ValueError` is raised to indicate
+ that we have failed; i.e. we cannot say with certainty what
+ the maximal angle is.
+
+ REFERENCES:
+
+ - [IS2005]_
+ - [SS2016]_
+ - [Or2020]_
+ - [Or2024]_
+
+ ALGORITHM:
+
+ Algorithm 3 in [Or2020]_ is used. If a potentially-maximal
+ angle corresponds to an eigenspace of dimension two or more,
+ we sometimes fall back to inexact arithmetic which has the
+ effect of perturbing the cones. That this will not affect the
+ answer too much is one conclusion of [Or2024]_.
+
+ EXAMPLES:
+
+ The maximal angle in a single ray is zero::
+
+ sage: K = random_cone(min_rays=1, max_rays=1, max_ambient_dim=5)
+ sage: K.max_angle()[0]
+ 0
+
+ The maximal angle in the nonnegative octant is `\pi/2`::
+
+ sage: K = cones.nonnegative_orthant(3)
+ sage: K.max_angle()[0]
+ 1/2*pi
+
+ The maximal angle between the nonnegative quintant and the
+ Schur cone of dimension 5 is about `0.8524 \pi`. The same
+ result can be obtained faster using inexact arithmetic, but
+ only confidently so because we already know the answer::
+
+ sage: # long time
+ sage: P = cones.nonnegative_orthant(5)
+ sage: Q = cones.schur(5)
+ sage: actual = P.max_angle(Q)[0]
+ sage: expected = 0.8524*pi
+ sage: bool( (actual - expected).abs() < 0.0001 )
+ True
+ sage: actual = P.max_angle(Q,exact=False)[0]
+ sage: bool( (actual - expected).abs() < 0.0001 )
+ True
+
+ The maximal angle within the Schur cone is known explicitly
+ via Gourion and Seeger's Proposition 2 [GS2010]_::
+
+ sage: n = 3
+ sage: K = cones.schur(n)
+ sage: bool(K.max_angle()[0] == ((n-1)/n)*pi)
+ True
+
+ Sage can't prove that the actual and expected results are
+ equal in the next two cases without a little nudge in the
+ right direction, and, moreover, it's crashy about it::
+
+ sage: n = 4
+ sage: K = cones.schur(n)
+ sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_()
+ sage: expected = ((n-1)/n)*pi
+ sage: bool( actual == expected )
+ True
+ sage: n = 5
+ sage: K = cones.schur(n)
+ sage: actual = K.max_angle()[0].simplify()._sympy_()._sage_()
+ sage: expected = ((n-1)/n)*pi
+ sage: bool( actual == expected )
+ True
+
+ When there's a unit norm vector in ``self`` whose negation is
+ in ``other``, they form a maximal angle of `\pi`::
+
+ sage: P = Cone([(5,1), (1,-1)])
+ sage: Q = Cone([(-1,0), (-1,0)])
+ sage: P.max_angle(Q)[0]
+ pi
+
+ TESTS:
+
+ When ``self`` and ``-other`` have nontrivial intersection, we
+ expect the maximal angle to be `\pi`::
+
+ sage: # long time
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone )
+ sage: n = ZZ.random_element(1,3)
+ sage: P = _random_admissible_cone(ambient_dim=n)
+ sage: Q = _random_admissible_cone(ambient_dim=n)
+ sage: expected = P.intersection(-Q).is_trivial()
+ sage: actual = bool(P.max_angle(Q)[0] != pi)
+ sage: actual == expected
+ True
+
+ In particular, this should happen when either cone is the full
+ space::
+
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone )
+ sage: n = ZZ.random_element(1,5)
+ sage: P = _random_admissible_cone(ambient_dim=n)
+ sage: Q = cones.trivial(n, P.dual().lattice()).dual()
+ sage: Q.is_full_space()
+ True
+ sage: P.max_angle(Q)[0]
+ pi
+ sage: Q.max_angle(P)[0]
+ pi
+ """
+ # We do the argument checking here, in the public cone method,
+ # because the error message should say something like "this
+ # cone" and not reference the argument of a function the user
+ # has never heard of in some internal module.
+ if self.is_trivial():
+ raise ValueError("cone should not be trivial")
+
+ if other is None:
+ other = self
+ else:
+ if (other.lattice_dim() != self.lattice_dim()):
+ raise ValueError("lattice dimensions of self and other "
+ "must agree")
+ if other.is_trivial():
+ raise ValueError("other cone cannot be trivial")
+
+ from sage.geometry.cone_critical_angles import max_angle
+ return max_angle(self, other, exact, epsilon)
+
def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None,
min_rays=0, max_rays=None, strictly_convex=None, solid=None):
@@ -6387,9 +6586,8 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None,
It's hard to test the output of a random process, but we can at
least make sure that we get a cone back::
- sage: from sage.geometry.cone import is_Cone
sage: K = random_cone(max_ambient_dim=6, max_rays=10)
- sage: is_Cone(K)
+ sage: isinstance(K, sage.geometry.abc.ConvexRationalPolyhedralCone)
True
The upper/lower bounds are respected::
diff --git a/src/sage/geometry/cone_catalog.py b/src/sage/geometry/cone_catalog.py
index d6898d40087..a56de80b412 100644
--- a/src/sage/geometry/cone_catalog.py
+++ b/src/sage/geometry/cone_catalog.py
@@ -4,6 +4,7 @@
This module provides shortcut functions, grouped under the
globally-available ``cones`` prefix, to create some common cones:
+- The downward-monotone cone,
- The nonnegative orthant,
- The rearrangement cone of order ``p``,
- The Schur cone,
@@ -18,6 +19,15 @@
Here are some typical usage examples::
+ sage: cones.downward_monotone(3).rays()
+ N( 1, 0, 0),
+ N( 1, 1, 0),
+ N( 1, 1, 1),
+ N(-1, -1, -1)
+ in 3-d lattice N
+
+::
+
sage: cones.nonnegative_orthant(2).rays()
N(1, 0),
N(0, 1)
@@ -150,6 +160,143 @@ def _preprocess_args(ambient_dim, lattice):
return (ambient_dim, lattice)
+def downward_monotone(ambient_dim=None, lattice=None):
+ r"""
+ The downward-monotone cone in ``ambient_dim`` dimensions, or
+ living in ``lattice``.
+
+ The elements of the downward-monotone cone are vectors whose
+ components are arranged in non-increasing order. Vectors whose
+ entries are arranged in the reverse (non-decreasing) order are
+ sometimes called isotone vectors, and are used in statistics
+ for isotonic regression.
+
+ The downward-monotone cone is the dual of the Schur cone. It
+ is also often referred to as the downward-monotone cone.
+
+ INPUT:
+
+ - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the
+ dimension of the ambient space
+
+ - ``lattice`` -- a toric lattice (default: ``None``); the lattice in
+ which the cone will live
+
+ If ``ambient_dim`` is omitted, then it will be inferred from the
+ rank of ``lattice``. If the ``lattice`` is omitted, then the
+ default lattice of rank ``ambient_dim`` will be used.
+
+ A :class:`ValueError` is raised if neither ``ambient_dim`` nor
+ ``lattice`` are specified. It is also a :class:`ValueError` to
+ specify both ``ambient_dim`` and ``lattice`` unless the rank of
+ ``lattice`` is equal to ``ambient_dim``.
+
+ OUTPUT:
+
+ A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living
+ in ``lattice`` whose elements' entries are arranged in
+ nonincreasing order. Each generating ray has the integer ring as
+ its base ring.
+
+ A :class:`ValueError` can be raised if the inputs are incompatible
+ or insufficient. See the INPUT documentation for details.
+
+ .. SEEALSO::
+
+ :func:`schur`
+
+ REFERENCES:
+
+ - [GS2010]_, Section 3.1
+
+ - [Niez1998]_, Example 2.2
+
+ EXAMPLES:
+
+ The entries of the elements of the downward-monotone cone are in
+ non-increasing order::
+
+ sage: ambient_dim = ZZ.random_element(10)
+ sage: K = cones.downward_monotone(ambient_dim)
+ sage: all( x[i] >= x[i + 1]
+ ....: for i in range(ambient_dim - 1)
+ ....: for x in K.rays() )
+ True
+ sage: x = K.random_element()
+ sage: all( x[i] >= x[i + 1] for i in range(ambient_dim - 1) )
+ True
+
+ A nontrivial downward-monotone cone is solid but not proper,
+ since it contains both the vector of all ones and its negation;
+ that, however, is the only subspace it contains::
+
+ sage: ambient_dim = ZZ.random_element(1,10)
+ sage: K = cones.downward_monotone(ambient_dim)
+ sage: K.is_solid()
+ True
+ sage: K.is_proper()
+ False
+ sage: K.lineality()
+ 1
+
+ The dual of the downward-monotone cone is the Schur cone
+ [GS2010]_ that induces the majorization preordering::
+
+ sage: ambient_dim = ZZ.random_element(10)
+ sage: K = cones.downward_monotone(ambient_dim).dual()
+ sage: J = cones.schur(ambient_dim, K.lattice())
+ sage: K.is_equivalent(J)
+ True
+
+ TESTS:
+
+ We can construct the trivial cone as the downward-monotone cone
+ in a trivial vector space::
+
+ sage: cones.downward_monotone(0)
+ 0-d cone in 0-d lattice N
+
+ If a ``lattice`` was given, it is actually used::
+
+ sage: L = ToricLattice(3, 'M')
+ sage: cones.downward_monotone(lattice=L)
+ 3-d cone in 3-d lattice M
+
+ Unless the rank of the lattice disagrees with ``ambient_dim``::
+
+ sage: L = ToricLattice(1, 'M')
+ sage: cones.downward_monotone(3, lattice=L)
+ Traceback (most recent call last):
+ ...
+ ValueError: lattice rank=1 and ambient_dim=3 are incompatible
+
+ We also get an error if no arguments are given::
+
+ sage: cones.downward_monotone()
+ Traceback (most recent call last):
+ ...
+ ValueError: either the ambient dimension or the lattice must
+ be specified
+ """
+ from sage.geometry.cone import Cone
+ from sage.matrix.constructor import matrix
+ from sage.rings.integer_ring import ZZ
+
+ ambient_dim, lattice = _preprocess_args(ambient_dim, lattice)
+
+ # The generators for this cone are mentioned in Niezgoda's
+ # Example 2.2 if you don't want to compute them yourself.
+ G = matrix.identity(ZZ, ambient_dim)
+ for i in range(1, ambient_dim):
+ G.add_multiple_of_row(i, i - 1, 1)
+
+ if G.nrows() > 0:
+ # Special case for when the ambient space is trivial.
+ G = G.insert_row(ambient_dim, -1*G.row(-1))
+
+ return Cone(G.rows(), lattice)
+
+
def nonnegative_orthant(ambient_dim=None, lattice=None):
r"""
The nonnegative orthant in ``ambient_dim`` dimensions, or living
@@ -170,20 +317,20 @@ def nonnegative_orthant(ambient_dim=None, lattice=None):
rank of ``lattice``. If the ``lattice`` is omitted, then the
default lattice of rank ``ambient_dim`` will be used.
- A ``ValueError`` is raised if neither ``ambient_dim`` nor
- ``lattice`` are specified. It is also a ``ValueError`` to specify
- both ``ambient_dim`` and ``lattice`` unless the rank of
+ A :class:`ValueError` is raised if neither ``ambient_dim`` nor
+ ``lattice`` are specified. It is also a :class:`ValueError` to
+ specify both ``ambient_dim`` and ``lattice`` unless the rank of
``lattice`` is equal to ``ambient_dim``.
OUTPUT:
- A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living in ``lattice``
- and having ``ambient_dim`` standard basis vectors as its
- generators. Each generating ray has the integer ring as its
+ A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` living
+ in ``lattice`` and having ``ambient_dim`` standard basis vectors
+ as its generators. Each generating ray has the integer ring as its
base ring.
- A ``ValueError`` can be raised if the inputs are incompatible or
- insufficient. See the INPUT documentation for details.
+ A :class:`ValueError` can be raised if the inputs are incompatible
+ or insufficient. See the INPUT documentation for details.
REFERENCES:
@@ -238,7 +385,7 @@ def nonnegative_orthant(ambient_dim=None, lattice=None):
from sage.matrix.constructor import matrix
from sage.rings.integer_ring import ZZ
- (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice)
+ ambient_dim, lattice = _preprocess_args(ambient_dim, lattice)
I = matrix.identity(ZZ, ambient_dim)
return Cone(I.rows(), lattice)
@@ -278,22 +425,22 @@ def rearrangement(p, ambient_dim=None, lattice=None):
rank of ``lattice``. If the ``lattice`` is omitted, then the
default lattice of rank ``ambient_dim`` will be used.
- A ``ValueError`` is raised if neither ``ambient_dim`` nor
- ``lattice`` are specified. It is also a ``ValueError`` to specify
- both ``ambient_dim`` and ``lattice`` unless the rank of
+ A :class:`ValueError` is raised if neither ``ambient_dim`` nor
+ ``lattice`` are specified. It is also a :class:`ValueError` to
+ specify both ``ambient_dim`` and ``lattice`` unless the rank of
``lattice`` is equal to ``ambient_dim``.
- It is also a ``ValueError`` to specify a non-integer ``p``.
+ It is also a :class:`ValueError` to specify a non-integer ``p``.
OUTPUT:
- A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the
- rearrangement cone of order ``p`` living in ``lattice``, with
- ambient dimension ``ambient_dim``. Each generating ray has the
- integer ring as its base ring.
+ A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone`
+ representing the rearrangement cone of order ``p`` living in
+ ``lattice``, with ambient dimension ``ambient_dim``. Each
+ generating ray has the integer ring as its base ring.
- A ``ValueError`` can be raised if the inputs are incompatible or
- insufficient. See the INPUT documentation for details.
+ A :class:`ValueError` can be raised if the inputs are incompatible
+ or insufficient. See the INPUT documentation for details.
ALGORITHM:
@@ -464,7 +611,7 @@ def rearrangement(p, ambient_dim=None, lattice=None):
from sage.matrix.constructor import matrix
from sage.rings.integer_ring import ZZ
- (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice)
+ ambient_dim, lattice = _preprocess_args(ambient_dim, lattice)
if p < 1 or p > ambient_dim or p not in ZZ:
raise ValueError("order p=%s should be an integer between 1 "
@@ -481,11 +628,11 @@ def schur(ambient_dim=None, lattice=None):
The Schur cone in ``ambient_dim`` dimensions, or living
in ``lattice``.
- The Schur cone in `n` dimensions induces the majorization ordering
- on the ambient space. If `\left\{e_{1}, e_{2}, \ldots,
+ The Schur cone in `n` dimensions induces the majorization
+ preordering on the ambient space. If `\left\{e_{1}, e_{2}, \ldots,
e_{n}\right\}` is the standard basis for the space, then its
generators are `\left\{e_{i} - e_{i+1}\ |\ 1 \le i \le
- n-1\right\}`. Its dual is the downward monotonic cone.
+ n-1\right\}`. Its dual is the downward monotone cone.
INPUT:
@@ -499,19 +646,24 @@ def schur(ambient_dim=None, lattice=None):
rank of ``lattice``. If the ``lattice`` is omitted, then the
default lattice of rank ``ambient_dim`` will be used.
- A ``ValueError`` is raised if neither ``ambient_dim`` nor
- ``lattice`` are specified. It is also a ``ValueError`` to specify
- both ``ambient_dim`` and ``lattice`` unless the rank of
+ A :class:`ValueError` is raised if neither ``ambient_dim`` nor
+ ``lattice`` are specified. It is also a :class:`ValueError` to
+ specify both ``ambient_dim`` and ``lattice`` unless the rank of
``lattice`` is equal to ``ambient_dim``.
OUTPUT:
- A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the Schur
- cone living in ``lattice``, with ambient dimension ``ambient_dim``.
- Each generating ray has the integer ring as its base ring.
+ A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone`
+ representing the Schur cone living in ``lattice``, with ambient
+ dimension ``ambient_dim``. Each generating ray has the integer
+ ring as its base ring.
+
+ A :class:`ValueError` can be raised if the inputs are incompatible
+ or insufficient. See the INPUT documentation for details.
+
+ .. SEEALSO::
- A ``ValueError`` can be raised if the inputs are incompatible or
- insufficient. See the INPUT documentation for details.
+ :func:`downward_monotone`
REFERENCES:
@@ -537,13 +689,13 @@ def schur(ambient_dim=None, lattice=None):
sage: abs(actual - expected).n() < 1e-12
True
- The dual of the Schur cone is the "downward monotonic cone"
+ The dual of the Schur cone is the downward-monotone cone
[GS2010]_, whose elements' entries are in non-increasing order::
sage: ambient_dim = ZZ.random_element(10)
sage: K = cones.schur(ambient_dim).dual()
- sage: x = K.random_element()
- sage: all( x[i] >= x[i+1] for i in range(ambient_dim-1) )
+ sage: J = cones.downward_monotone(ambient_dim, K.lattice())
+ sage: K.is_equivalent(J)
True
TESTS:
@@ -553,21 +705,25 @@ def schur(ambient_dim=None, lattice=None):
sage: cones.schur(0).is_trivial()
True
- The Schur cone induces the majorization ordering, as in Iusem
- and Seeger's [IS2005]_ Example 7.3::
+ The Schur cone induces the majorization preordering, as in Iusem
+ and Seeger's [IS2005]_ Example 7.3 or Niezgoda's [Niez1998]_
+ Example 2.2::
+ sage: ambient_dim = ZZ.random_element(10)
+ sage: V = VectorSpace(QQ, ambient_dim)
+ sage: rearrange = lambda z: V(sorted(z.list(),reverse=True))
sage: def majorized_by(x,y):
+ ....: x = rearrange(x)
+ ....: y = rearrange(y)
....: return (all(sum(x[0:i]) <= sum(y[0:i])
....: for i in range(x.degree()-1))
....: and sum(x) == sum(y))
- sage: ambient_dim = ZZ.random_element(10)
- sage: V = VectorSpace(QQ, ambient_dim)
sage: S = cones.schur(ambient_dim)
sage: majorized_by(V.zero(), S.random_element())
True
sage: x = V.random_element()
sage: y = V.random_element()
- sage: majorized_by(x,y) == ( (y-x) in S )
+ sage: majorized_by(x,y) == ((rearrange(y) - rearrange(x)) in S)
True
If a ``lattice`` was given, it is actually used::
@@ -596,7 +752,7 @@ def schur(ambient_dim=None, lattice=None):
from sage.matrix.constructor import matrix
from sage.rings.integer_ring import ZZ
- (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice)
+ ambient_dim, lattice = _preprocess_args(ambient_dim, lattice)
def _f(i,j):
if i == j:
@@ -629,19 +785,19 @@ def trivial(ambient_dim=None, lattice=None):
rank of ``lattice``. If the ``lattice`` is omitted, then the
default lattice of rank ``ambient_dim`` will be used.
- A ``ValueError`` is raised if neither ``ambient_dim`` nor
- ``lattice`` are specified. It is also a ``ValueError`` to specify
- both ``ambient_dim`` and ``lattice`` unless the rank of
+ A :class:`ValueError` is raised if neither ``ambient_dim`` nor
+ ``lattice`` are specified. It is also a :class:`ValueError` to
+ specify both ``ambient_dim`` and ``lattice`` unless the rank of
``lattice`` is equal to ``ambient_dim``.
OUTPUT:
- A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone` representing the
- trivial cone with no nonzero generators living in ``lattice``,
- with ambient dimension ``ambient_dim``.
+ A :class:`~sage.geometry.cone.ConvexRationalPolyhedralCone`
+ representing the trivial cone with no nonzero generators living in
+ ``lattice``, with ambient dimension ``ambient_dim``.
- A ``ValueError`` can be raised if the inputs are incompatible or
- insufficient. See the INPUT documentation for details.
+ A :class:`ValueError` can be raised if the inputs are incompatible
+ or insufficient. See the INPUT documentation for details.
EXAMPLES:
@@ -684,6 +840,6 @@ def trivial(ambient_dim=None, lattice=None):
"""
from sage.geometry.cone import Cone
- (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice)
+ ambient_dim, lattice = _preprocess_args(ambient_dim, lattice)
return Cone([], lattice)
diff --git a/src/sage/geometry/cone_critical_angles.py b/src/sage/geometry/cone_critical_angles.py
new file mode 100644
index 00000000000..3e39102426a
--- /dev/null
+++ b/src/sage/geometry/cone_critical_angles.py
@@ -0,0 +1,1024 @@
+r"""
+Find maximal angles between polyhedral convex cones
+
+.. WARNING::
+
+ This module is considered internal and its contents are subject to
+ change at any time without (deprecation) warning. The stable
+ interface is
+ :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`.
+
+Finding the maximal (or equivalently, the minimal) angle between two
+polyhedral convex cones is a hard nonconvex optimization problem. The
+problem for a single cone was introduced in [IS2005]_, and was later
+extended in [SS2016]_ to two cones as a generalization of the
+principal angle between two vector subspaces.
+
+Seeger and Sossa proposed an algorithm in [SS2016]_ to find maximal
+angles, and [Or2020]_ elaborates on that algorithm. It is this latest
+improvement that is implemented (more or less) by this module. The
+fact that perturbations of pointed cones may not change the answer too
+much [Or2024]_ is taken into consideration to avoid pathological
+cases.
+
+This module is internal to SageMath; the interface presented to users
+consists of a public method,
+:meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle` for
+polyhedral convex cones. Even though all of the functions in this
+module are internal, some are more internal than others. There are a
+few functions that are used only in doctests, and not by any code that
+an end-user would run. Breaking somewhat with tradition, only those
+methods have been prefixed with an underscore.
+"""
+
+from sage.functions.trig import arccos, cos
+from sage.matrix.constructor import matrix
+from sage.misc.misc import powerset
+from sage.rings.integer_ring import ZZ
+from sage.rings.qqbar import AA
+from sage.rings.rational_field import QQ
+from sage.rings.real_double import RDF
+from sage.symbolic.constants import pi
+
+def _normalize_gevp_solution(gevp_solution):
+ r"""
+ Normalize the results of :func:`solve_gevp_nonzero` and
+ :func:`_solve_gevp_naive`.
+
+ Those two functions return solutions (pairs of vectors) to an
+ eigenvalue problem, but eigenvectors are only unique up to a
+ scalar multiple. This function normalizes those results so that
+ every eigenvector has a leading entry of positive one. This allows
+ us to identity equivalent solutions to those problems.
+
+ INPUT:
+
+ A quartet ``gevp_solution`` whose components are, in order:
+
+ - ``eigenvalue`` -- ignored
+
+ - ``xi`` -- first component of the `( \xi, \eta )` eigenvector
+
+ - ``eta`` -- second component of the `( \xi, \eta )` eigenvector
+
+ - ``multiplicity`` -- ignored
+
+ OUTPUT:
+
+ If `c` is the first nonzero component of the concatenated `( \xi,
+ \eta )` vector, then a quartet whose components are, in order:
+
+ - ``eigenvalue`` -- the unmodified ``eigenvalue`` argument
+
+ - ``xi*(1/c)`` -- the `\xi` component normalized so that the first
+ nonzero component of the concatenated vector
+ `( \xi, \eta )` is positive one
+
+ - ``eta*(1/c)`` -- the `\eta` component normalized so that the
+ first nonzero component of the concatenated vector
+ `( \xi, \eta )` is positive one
+
+ - ``multiplicity`` -- the unmodified ``multiplicity`` argument
+
+ If there is no such `c` (that is, if both `\xi` and `\eta` are
+ zero), then the entire input quartet is returned unmodified.
+
+ EXAMPLES::
+
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _normalize_gevp_solution)
+ sage: s1 = (-1, vector(QQ,[0,-2]), vector(QQ,[1]), 1)
+ sage: _normalize_gevp_solution(s1)
+ (-1, (0, 1), (-1/2), 1)
+ sage: s2 = (1, vector(QQ,[0,0]), vector(QQ,[0,0,-1]), 2)
+ sage: _normalize_gevp_solution(s2)
+ (1, (0, 0), (0, 0, 1), 2)
+ """
+ eigenvalue, xi, eta, multiplicity = gevp_solution
+ from itertools import chain
+
+ # We'll use this default of zero as the scaling factor if we don't
+ # find a better one; that is, if xi = eta = 0 -- in which case the
+ # additional multiplication by zero is a no-op.
+ scale = 0
+ for c in chain(xi, eta):
+ if c != 0:
+ scale = ~c
+ break
+
+ xi *= scale
+ xi.set_immutable()
+ eta *= scale
+ eta.set_immutable()
+ return (eigenvalue, xi, eta, multiplicity)
+
+
+def _random_admissible_cone(ambient_dim):
+ r"""
+ Generate a random cone in a lattice of dimension
+ ``ambient_dim`` that isn't trivial.
+
+ This is a convenience method used to simplify some test cases. The
+ number of rays that the cone possesses is limited to two more than
+ ``ambient_dim``; so, for example, you will not get more than five
+ rays in a three-dimensional space. This limits the amount of time
+ spent in any one test case. In contrast with the definition in
+ [Or2020]_ we consider the full ambient space to be admissible (it
+ doesn't hurt anything, and [Or2024]_ was forced to allow it).
+
+ INPUT:
+
+ - ``ambient_dim`` -- a positive integer representing the dimension
+ of the ambient lattice in which the returned cone lives
+
+ OUTPUT:
+
+ A "random" nontrivial closed convex cone in a lattice of dimension
+ ``ambient_dim``.
+
+ A :class:`ValueError` is raised if ``ambient_dim`` is not
+ positive.
+
+ EXAMPLES:
+
+ The result has all of the desired properties::
+
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone )
+ sage: K = _random_admissible_cone(5)
+ sage: K.lattice_dim()
+ 5
+ sage: K.is_trivial()
+ False
+
+ Unless the ``ambient_dim`` argument is nonsense::
+
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone )
+ sage: K = _random_admissible_cone(0)
+ Traceback (most recent call last):
+ ...
+ ValueError: there are no nontrivial cones in dimension 0
+ """
+ if ambient_dim < 1 or ambient_dim not in ZZ:
+ # The random_cone() method already crashes if we ask the
+ # impossible of it, but having this here emits a more sensible
+ # error message.
+ raise ValueError("there are no nontrivial cones in dimension %d"
+ % ambient_dim)
+
+ args = { 'min_ambient_dim': ambient_dim,
+ 'max_ambient_dim': ambient_dim,
+ 'min_rays': 1,
+ 'max_rays': ambient_dim+2 }
+
+ from sage.geometry.cone import random_cone
+ return random_cone(**args)
+
+ return K
+
+
+def gevp_licis(G):
+ r"""
+ Return all nonempty subsets of indices for the columns of
+ ``G`` that correspond to linearly independent sets (of columns of
+ ``G``).
+
+ Mnemonic: linearly independent column-index subsets (LICIS).
+
+ The returned lists are all sorted in the same (the natural) order;
+ and are returned as lists so that they may be used to index into
+ the rows/columns of matrices.
+
+ INPUT:
+
+ - ``G`` -- the matrix whose linearly independent column index sets
+ we want
+
+ OUTPUT:
+
+ A generator that returns sorted lists of natural numbers. Each
+ generated list ``I`` is a set of indices corresponding to columns
+ of ``G`` that, when considered as a set, is linearly independent.
+
+ EXAMPLES:
+
+ The linearly independent subsets of the matrix corresponding to a
+ line (with two generators pointing in opposite directions) are the
+ one-element subsets, since the only two-element subset isn't
+ linearly independent::
+
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
+ sage: K = Cone([(1,0),(-1,0)])
+ sage: G = matrix.column(K.rays())
+ sage: list(gevp_licis(G))
+ [[0], [1]]
+
+ The matrix for the trivial cone has no linearly independent
+ subsets, since we require them to be nonempty::
+
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
+ sage: trivial_cone = cones.trivial(0)
+ sage: trivial_cone.is_trivial()
+ True
+ sage: list(gevp_licis(matrix.column(trivial_cone.rays())))
+ []
+
+ All rays in the nonnegative orthant of `R^{n}` are
+ linearly independent, so we should get back `2^{n} - 1` subsets
+ after accounting for the absence of the empty set::
+
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
+ sage: K = cones.nonnegative_orthant(3)
+ sage: G = matrix.column(K.rays())
+ sage: len(list(gevp_licis(G))) == 2^(K.nrays()) - 1
+ True
+
+ TESTS:
+
+ All sets corresponding to the returned indices should be linearly
+ independent::
+
+ sage: from sage.geometry.cone_critical_angles import gevp_licis
+ sage: K = random_cone(max_rays=8)
+ sage: G = matrix.column(K.rays())
+ sage: all( len(s) == K.rays(s).dimension() for s in gevp_licis(G) )
+ True
+ """
+ from sage.matroids.linear_matroid import LinearMatroid
+
+ # There's a fast implementation of this for matroids, but we need
+ # to drop the empty set from its output and convert the rest to
+ # lists that are all sorted in the same order.
+ return map(sorted, filter(bool, LinearMatroid(G).independent_sets()))
+
+
+def _solve_gevp_naive(GG, HH, M, I, J):
+ r"""
+ Solve the generalized eigenvalue problem in Theorem 3
+ [Or2020]_ in a very naive way, by (slowly) inverting the matrices
+ and finding the eigenvalues in the product space.
+
+ This is used only for testing, to ensure that the smart way of
+ solving the generalized eigenvalue problem via
+ :func:`solve_gevp_zero` and :func:`solve_gevp_nonzero` returns the
+ same answers as the dumb way.
+
+ This returns a generator, like :func:`solve_gevp_zero` and
+ :func:`solve_gevp_nonzero`.
+
+ INPUT:
+
+ See the arguments for :func:`solve_gevp_nonzero`.
+
+ ALGORITHM:
+
+ We construct the two matrices `A` and `B` in Theorem 3 [Or2020]_
+ in block form, and then use the naive "inverse" method on `B` to
+ move it to the left and obtain `M = B^{-1}A`. We then compute the
+ right eigenvectors of the whole big matrix `M`.
+
+ EXAMPLES:
+
+ A simple usage example, that also appears as Example 3 in
+ [Or2020]_::
+
+ sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive
+ sage: K = cones.nonnegative_orthant(2)
+ sage: G = matrix.column(K.rays())
+ sage: GG = G.transpose() * G
+ sage: I = [0]
+ sage: J = [1]
+ sage: list(_solve_gevp_naive(GG,GG,GG,I,J))
+ [(0, (1), (0), 2), (0, (0), (1), 2)]
+
+ Check Example 4 [Or2020]_ symbolically to ensure that we get
+ eigenspaces of dimension `n=2` corresponding to the eigenvalues
+ `\cos\theta = -1` and `\cos\theta = 1`::
+
+ sage: from sage.geometry.cone_critical_angles import _solve_gevp_naive
+ sage: g11,g12,g21,g22 = SR.var('g11,g12,g21,g22', domain='real')
+ sage: h11,h12,h21,h22 = SR.var('h11,h12,h21,h22', domain='real')
+ sage: gs = [[g11,g12], [g21,g22]]
+ sage: hs = [[h11,h12], [h21,h22]]
+ sage: G = matrix.column(gs)
+ sage: H = matrix.column(hs)
+ sage: GG = G.transpose() * G
+ sage: HH = H.transpose() * H
+ sage: M = G.transpose() * H
+ sage: I = [0, 1]
+ sage: J = [0, 1]
+ sage: all( v in [-1,1] and m == 2
+ ....: for (v,_,_,m) in _solve_gevp_naive(GG,HH,M,I,J) )
+ True
+ """
+ A = matrix.block([
+ [ZZ.zero(), M[I,J]],
+ [M.transpose()[J,I], ZZ.zero()]
+ ])
+ B = matrix.block([
+ [GG[I,I], ZZ.zero()],
+ [ZZ.zero(), HH[J,J]]
+ ])
+ M = B.inverse() * A
+
+ # We'll format the result to match the solve_gevp_nonzero() return value.
+ for (evalue, evectors, multiplicity) in M.eigenvectors_right():
+ for z in evectors:
+ xi = z[0:len(I)]
+ xi.set_immutable()
+ eta = z[len(I):]
+ eta.set_immutable()
+ yield (evalue, xi, eta, multiplicity)
+
+
+def solve_gevp_zero(M, I, J):
+ r"""
+ Solve the generalized eigenvalue problem in Theorem 3
+ [Or2020]_ for a zero eigenvalue using Propositions 3 and 4
+ [Or2020]_.
+
+ INPUT:
+
+ - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product
+ of `g_{i}` and `h_{j}` as in Proposition 6 [Or2020]_
+
+ - ``I`` -- a linearly independent column-index set for the matrix
+ `G` that appears in Theorem 3 [Or2020]_
+
+ - ``J`` -- a linearly independent column-index set for the matrix
+ `H` that appears in Theorem 3 [Or2020]_
+
+ OUTPUT:
+
+ A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets
+ where
+
+ - ``eigenvalue`` is zero (the eigenvalue of the system)
+
+ - ``xi`` is the first (length ``len(I)``) component of an
+ eigenvector associated with ``eigenvalue``
+
+ - ``eta`` is the second (length ``len(J)``) component of an
+ eigenvector associated with ``eigenvalue``
+
+ - ``multiplicity`` is the dimension of the eigenspace associated
+ with ``eigenvalue``
+
+ ALGORITHM:
+
+ Proposition 4 in [Or2020]_ is used.
+
+ EXAMPLES:
+
+ This particular configuration results in the zero matrix in the
+ eigenvalue problem, so the only solutions correspond to the
+ eigenvalue zero::
+
+ sage: from sage.geometry.cone_critical_angles import solve_gevp_zero
+ sage: K = cones.nonnegative_orthant(2)
+ sage: G = matrix.column(K.rays())
+ sage: GG = G.transpose() * G
+ sage: I = [0]
+ sage: J = [1]
+ sage: list(solve_gevp_zero(GG, I, J))
+ [(0, (1), (0), 2), (0, (0), (1), 2)]
+ """
+ # A Cartesian product would be more appropriate here, but Sage
+ # isn't smart enough to figure out a basis for the product. So,
+ # we use the direct sum and then chop it up.
+ M_IJ = M[I,J]
+ xi_space = M_IJ.left_kernel()
+ eta_space = M_IJ.right_kernel()
+
+ fake_cartprod = xi_space.direct_sum(eta_space)
+ multiplicity = fake_cartprod.dimension()
+
+ for z in fake_cartprod.basis():
+ z1 = z[0:len(I)]
+ z1.set_immutable()
+ z2 = z[len(I):]
+ z2.set_immutable()
+
+ # The base ring of M will either be RDF or AA, which is enough
+ # to contain any eigenvalues that will arise... meaning that
+ # if we use the corresponding "zero" here, it will match the
+ # field of the eigenvalues returned by the nonzero function.
+ yield (M.base_ring().zero(), z1, z2, multiplicity)
+
+
+def solve_gevp_nonzero(GG, HH, M, I, J):
+ r"""
+ Solve the generalized eigenvalue problem in Theorem 3
+ [Or2020]_ for a nonzero eigenvalue using Propositions 3 and 5
+ [Or2020]_.
+
+ INPUT:
+
+ - ``GG`` -- the matrix whose `(i,j)`-th entry is the inner product
+ of `g_{i}` and `g_{j}`, which are in turn the `i`-th and `j`-th
+ columns of the matrix `G` in Theorem 3 [Or2020]_
+
+ - ``HH`` -- the matrix whose `(i,j)`-th entry is the inner product
+ of `h_{i}` and `h_{j}`, which are in turn the `i`-th and `j`-th
+ columns of the matrix `H` in Theorem 3 [Or2020]_
+
+ - ``M`` -- the matrix whose `(i,j)`-th entry is the inner product
+ of `g_{i}` and `h_{j}` as in Proposition 6 in [Or2020]_
+
+ - ``I`` -- a linearly independent column-index set for the matrix
+ `G` that appears in Theorem 3 [Or2020]_
+
+ - ``J`` -- a linearly independent column-index set for the matrix
+ `H` that appears in Theorem 3 [Or2020]_
+
+ OUTPUT:
+
+ A generator of ``(eigenvalue, xi, eta, multiplicity)`` quartets
+ where
+
+ - ``eigenvalue`` is a real eigenvalue of the system
+
+ - ``xi`` is the first (length ``len(I)``) component of an
+ eigenvector associated with ``eigenvalue``
+
+ - ``eta`` is the second (length ``len(J)``) component of an
+ eigenvector associated with ``eigenvalue``
+
+ - ``multiplicity`` is the dimension of the eigenspace associated
+ with ``eigenvalue``
+
+ Note that we do not return a basis for each eigenspace along with
+ its eigenvalue. For the application we have in mind, an eigenspace
+ of dimension greater than one (so, ``multiplicity > 1``) is an
+ error. As such, our return value is optimized for convenience in
+ the non-error case, where there is only one eigenvector (spanning
+ a one-dimensional eigenspace) associated with each eigenvalue.
+
+ ALGORITHM:
+
+ According to Proposition 5 [Or2020]_, the solutions corresponding
+ to non-zero eigenvalues can be found by solving a smaller
+ eigenvalue problem in only the variable `\xi`. So, we do that, and
+ then solve for `\eta` in terms of `\xi` as described in the
+ proposition.
+
+ EXAMPLES:
+
+ When the zero solutions are included, this function returns the
+ same solutions as the naive method on the Schur cone in three
+ dimensions::
+
+ sage: from itertools import chain
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _normalize_gevp_solution,
+ ....: _solve_gevp_naive,
+ ....: gevp_licis,
+ ....: solve_gevp_nonzero,
+ ....: solve_gevp_zero)
+ sage: K = cones.schur(3)
+ sage: gs = [g.change_ring(AA).normalized() for g in K]
+ sage: G = matrix.column(gs)
+ sage: GG = G.transpose() * G
+ sage: G_index_sets = list(gevp_licis(G))
+ sage: all(
+ ....: set(
+ ....: _normalize_gevp_solution(s)
+ ....: for s in
+ ....: chain(
+ ....: solve_gevp_zero(GG, I, J),
+ ....: solve_gevp_nonzero(GG, GG, GG, I, J)
+ ....: )
+ ....: )
+ ....: ==
+ ....: set(
+ ....: _normalize_gevp_solution(s)
+ ....: for s in
+ ....: _solve_gevp_naive(GG,GG,GG,I,J)
+ ....: )
+ ....: for I in G_index_sets
+ ....: for J in G_index_sets
+ ....: )
+ True
+
+ TESTS:
+
+ This function should return the same solutions (with zero included,
+ of course) as the naive implementation even for random cones::
+
+ sage: # long time
+ sage: from itertools import chain
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _normalize_gevp_solution,
+ ....: _random_admissible_cone,
+ ....: _solve_gevp_naive,
+ ....: gevp_licis,
+ ....: solve_gevp_nonzero,
+ ....: solve_gevp_zero)
+ sage: n = ZZ.random_element(1,3)
+ sage: P = _random_admissible_cone(ambient_dim=n)
+ sage: Q = _random_admissible_cone(ambient_dim=n)
+ sage: gs = [g.change_ring(AA).normalized() for g in P]
+ sage: G = matrix.column(gs)
+ sage: GG = G.transpose() * G
+ sage: hs = [h.change_ring(AA).normalized() for h in Q]
+ sage: H = matrix.column(hs)
+ sage: HH = H.transpose() * H
+ sage: M = G.transpose() * H
+ sage: G_index_sets = list(gevp_licis(G))
+ sage: H_index_sets = list(gevp_licis(H))
+ sage: all(
+ ....: set(
+ ....: _normalize_gevp_solution(s)
+ ....: for s in
+ ....: chain(
+ ....: solve_gevp_zero(M, I, J),
+ ....: solve_gevp_nonzero(GG, HH, M, I, J)
+ ....: )
+ ....: )
+ ....: ==
+ ....: set(
+ ....: _normalize_gevp_solution(s)
+ ....: for s in
+ ....: _solve_gevp_naive(GG, HH, M, I, J)
+ ....: )
+ ....: for I in G_index_sets
+ ....: for J in H_index_sets
+ ....: )
+ True
+
+ According to Proposition 7 [Or2020]_, the only eigenvalues that
+ arise when either ``G`` or ``H`` is invertible are `-1`, `0`, and
+ `1`::
+
+ sage: # long time
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone,
+ ....: gevp_licis,
+ ....: solve_gevp_nonzero)
+ sage: n = ZZ.random_element(1,3)
+ sage: P = _random_admissible_cone(ambient_dim=n)
+ sage: Q = _random_admissible_cone(ambient_dim=n)
+ sage: gs = [g.change_ring(AA).normalized() for g in P]
+ sage: hs = [h.change_ring(AA).normalized() for h in Q]
+ sage: G = matrix.column(gs)
+ sage: GG = G.transpose() * G
+ sage: H = matrix.column(hs)
+ sage: HH = H.transpose() * H
+ sage: M = G.transpose() * H
+ sage: from itertools import product
+ sage: all(
+ ....: (v in [-1,0,1]
+ ....: for (v,_,_,_) in solve_gevp_nonzero(GG, HH, M, I, J))
+ ....: for (I,J) in product(gevp_licis(G),gevp_licis(H))
+ ....: if len(I) == n or len(J) == n )
+ True
+ """
+ if len(J) < len(I):
+ # We can always opt to solve the smaller problem. Reading the
+ # first three assignments below, you should be able to
+ # convince yourself that switching GG <-> HH, I <-> J, and
+ # transposing M does in fact switch from the "xi problem" to
+ # the "eta problem."
+ yield from ((l, xi, eta, m)
+ for (l, eta, xi, m)
+ in solve_gevp_nonzero(HH, GG, M.transpose(), J, I))
+ else:
+ M_IJ = M[I,J]
+ G_I_pinv_H_J = GG[I,I].inverse_positive_definite() * M_IJ
+ H_J_pinv_G_I = HH[J,J].inverse_positive_definite() * M_IJ.transpose()
+ L = (G_I_pinv_H_J * H_J_pinv_G_I)
+
+ for (sigma, xis, m) in L.eigenvectors_right():
+ if sigma > 0:
+ # Avoid recomputing these for each xi in xis
+ sigma_sqrt = sigma.sqrt()
+ inv_sqrt = ~sigma_sqrt
+ pm_sqrt_inv_pairs = [
+ (-sigma_sqrt, -inv_sqrt),
+ (sigma_sqrt, inv_sqrt)
+ ]
+
+ for xi in xis:
+ for l, li in pm_sqrt_inv_pairs:
+ eta = li * H_J_pinv_G_I*xi
+ eta.set_immutable()
+ yield (l, xi, eta, m)
+
+
+def compute_gevp_M(gs, hs):
+ r"""
+ Compute the matrix `M` whose `(i,j)`-th entry is the inner
+ product of ``gs[i]`` and ``hs[j]``.
+
+ This is the "generalized Gram matrix" appearing in Proposition 6
+ in [Or2020]_. For efficiency, we also return the minimal pair,
+ whose inner product is minimal among the entries of `M`. This
+ allows our consumer to bail out immediately (knowing the optimal
+ pair!) if it turns out that the maximal angle is acute; i.e. if
+ the smallest entry of `M` is nonnegative.
+
+ INPUT:
+
+ - ``gs`` -- a linearly independent list of unit-norm generators
+ for the cone `P`
+
+ - ``hs`` -- a linearly independent list of unit-norm generators
+ for the cone `Q`
+
+ OUTPUT:
+
+ A tuple containing four elements, in order:
+
+ - The matrix `M` described in Proposition 6
+
+ - The minimal entry in the matrix `M`
+
+ - A vector in ``gs`` that achieves that minimal inner product
+ along with the next element of the tuple
+
+ - A vector in ``hs`` that achieves the minimal inner product
+ along with the previous element in the tuple
+
+ EXAMPLES::
+
+ sage: from sage.geometry.cone_critical_angles import compute_gevp_M
+ sage: P = Cone([ (1,2,0), (3,4,0) ])
+ sage: Q = Cone([ (-1,4,1), (5,-2,-1), (-1,-1,5) ])
+ sage: gs = [g.change_ring(QQ) for g in P]
+ sage: hs = [h.change_ring(QQ) for h in Q]
+ sage: M = compute_gevp_M(gs, hs)[0]
+ sage: all( M[i][j] == gs[i].inner_product(hs[j])
+ ....: for i in range(P.nrays())
+ ....: for j in range(Q.nrays()) )
+ True
+
+ TESTS:
+
+ The products `(G_{I})^{T}H_{J}` correspond to
+ submatrices of the "generalized Gram matrix" `M` in Proposition
+ 6. Note that SageMath does (row,column) indexing but [Or2020]_
+ does (column,row) indexing::
+
+ sage: from sage.geometry.cone_critical_angles import (
+ ....: _random_admissible_cone,
+ ....: compute_gevp_M,
+ ....: gevp_licis)
+ sage: n = ZZ.random_element(1,4)
+ sage: n = ZZ.random_element(1,8) # long time
+ sage: P = _random_admissible_cone(ambient_dim=n)
+ sage: Q = _random_admissible_cone(ambient_dim=n)
+ sage: gs = [g.change_ring(QQ) for g in P]
+ sage: hs = [h.change_ring(QQ) for h in Q]
+ sage: M = compute_gevp_M(gs,hs)[0]
+ sage: f = lambda i,j: gs[i].inner_product(hs[j])
+ sage: expected_M = matrix(QQ, P.nrays(), Q.nrays(), f)
+ sage: M == expected_M
+ True
+ sage: G = matrix.column(gs)
+ sage: H = matrix.column(hs)
+ sage: def _test_indexing(I,J):
+ ....: G_I = G.matrix_from_columns(I)
+ ....: H_J = H.matrix_from_columns(J)
+ ....: return (G_I.transpose()*H_J == M[I,J]
+ ....: and
+ ....: H_J.transpose()*G_I == M.transpose()[J,I])
+ sage: G_index_sets = list(gevp_licis(G))
+ sage: H_index_sets = list(gevp_licis(H))
+ sage: all( _test_indexing(I,J) for I in G_index_sets
+ ....: for J in H_index_sets )
+ True
+ """
+ min_u = gs[0]
+ min_v = hs[0]
+ min_ip = min_u.inner_product(min_v)
+
+ M = []
+ for g in gs:
+ M_i = []
+ for h in hs:
+ val = g.inner_product(h)
+ M_i.append(val)
+ if (val < min_ip):
+ min_ip = val
+ min_u = g
+ min_v = h
+ M.append(M_i)
+
+ return (matrix(M), min_ip, min_u, min_v)
+
+
+def check_gevp_feasibility(cos_theta, xi, eta, G_I, G_I_c_T,
+ H_J, H_J_c_T, epsilon):
+ r"""
+ Determine if a solution to the generalized eigenvalue problem
+ in Theorem 3 [Or2020]_ is feasible.
+
+ Implementation detail: we take four matrices that we are capable
+ of computing as parameters instead, because we will be called in a
+ nested loop "for all `I`... and for all `J`..." The data
+ corresponding to `I` should be computed only once, which means
+ that we can't do it here -- it needs to be done outside of the `J`
+ loop. For symmetry (and to avoid relying on too many
+ cross-function implementation details), we also insist that the
+ `J` data be passed in.
+
+ INPUT:
+
+ - ``cos_theta`` -- an eigenvalue corresponding to
+ `( \xi, \eta )`
+
+ - ``xi`` -- first component of the `( \xi, \eta )` eigenvector
+
+ - ``eta`` -- second component of the `( \xi, \eta )` eigenvector
+
+ - ``G_I`` -- the submatrix of `G` with columns indexed by `I`
+
+ - ``G_I_c_T`` -- a matrix whose rows are the non-`I` columns of `G`
+
+ - ``H_J`` -- the submatrix of `H` with columns indexed by `J`
+
+ - ``H_J_c_T`` -- a matrix whose rows are the non-`J` columns of `H`
+
+ - ``epsilon`` -- the tolerance to use when making comparisons
+
+ OUTPUT:
+
+ A triple containing (in order),
+
+ - a boolean,
+ - a vector in the cone `P` (of the same length as ``xi``), and
+ - a vector in the cone `Q` (of the same length as ``eta``).
+
+ If `( \xi, \eta )` is feasible, we return ``(True, u, v)`` where `u`
+ and `v` are the vectors in `P` and `Q` respectively that form the
+ the angle `\theta`.
+
+ If `( \xi, \eta )` is **not** feasible, then we return ``(False, 0, 0)``
+ where ``0`` should be interpreted to mean the zero vector in the
+ appropriate space.
+
+ EXAMPLES:
+
+ If `\xi` has any components less than "zero," it isn't feasible::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [-1,1])
+ sage: eta = vector(QQ, [1,1,1])
+ sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0)
+ (False, (0, 0), (0, 0, 0))
+
+ If `\eta` has any components less than "zero," it isn't feasible::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [2])
+ sage: eta = vector(QQ, [1,-4,4,5])
+ sage: check_gevp_feasibility(0,xi,eta,None,None,None,None,0)
+ (False, (0), (0, 0, 0, 0))
+
+ If `\xi` and `\eta` are equal and if `G_{I}` and `H_{J}` are not,
+ then the copy of `\eta` that's been scaled by the norm of `G_{I}\xi`
+ generally won't satisfy its norm-equality constraint::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [1,1])
+ sage: eta = xi
+ sage: G_I = matrix.identity(QQ,2)
+ sage: H_J = 2*G_I
+ sage: check_gevp_feasibility(0,xi,eta,G_I,None,H_J,None,0)
+ (False, (0, 0), (0, 0))
+
+ When `\cos\theta` is zero, the inequality (42) in Theorem 7.3
+ [SS2016]_ is just an inner product with `v` which we can make
+ positive by ensuring that all of the entries of `H_{J}` are
+ positive. So, if any of the rows of ``G_I_c_T`` contain a negative
+ entry, (42) will fail::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
+ sage: eta = xi
+ sage: G_I = matrix.identity(QQ,4)
+ sage: G_I_c_T = matrix(QQ, [[0,-1,0,0]])
+ sage: H_J = G_I
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,None,0)
+ (False, (0, 0, 0, 0), (0, 0, 0, 0))
+
+ Likewise we can make (43) fail in exactly the same way::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
+ sage: eta = xi
+ sage: G_I = matrix.identity(QQ,4)
+ sage: G_I_c_T = matrix(QQ, [[0,1,0,0]])
+ sage: H_J = G_I
+ sage: H_J_c_T = matrix(QQ, [[0,-1,0,0]])
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0)
+ (False, (0, 0, 0, 0), (0, 0, 0, 0))
+
+ Finally, if we ensure that everything works, we get back a feasible
+ result along with the vectors (scaled `\xi` and `\eta`) that worked::
+
+ sage: from sage.geometry.cone_critical_angles import(
+ ....: check_gevp_feasibility)
+ sage: xi = vector(QQ, [1/2,1/2,1/2,1/2])
+ sage: eta = xi
+ sage: G_I = matrix.identity(QQ,4)
+ sage: G_I_c_T = matrix(QQ, [[0,1,0,0]])
+ sage: H_J = G_I
+ sage: H_J_c_T = matrix(QQ, [[0,1,0,0]])
+ sage: check_gevp_feasibility(0,xi,eta,G_I,G_I_c_T,H_J,H_J_c_T,0)
+ (True, (1/2, 1/2, 1/2, 1/2), (1/2, 1/2, 1/2, 1/2))
+ """
+ infeasible_result = (False, 0*xi, 0*eta)
+ if min(xi) <= -epsilon or min(eta) <= -epsilon:
+ # xi or eta isn't in the interior of the nonnegative orthant,
+ # so skip this (non-)solution.
+ return infeasible_result
+
+ # Rescale xi to satisfy (44), and rescale eta by the same amount,
+ # because (xi,eta) needs to remain in the same one-dimensional
+ # eigenspace.
+ scale = ~((G_I*xi).norm())
+ xi_hat = xi * scale
+ eta_hat = eta * scale
+
+ # Now check that (45) is satisfied.
+ if ((H_J*eta_hat).norm() - 1).abs() > epsilon:
+ return infeasible_result
+
+ # And check that (42,43) are satisfied.
+ v = H_J * eta_hat
+ rhs = v - cos_theta*G_I*xi_hat
+
+ if any(x < -epsilon for x in G_I_c_T * rhs):
+ return infeasible_result
+
+ u = G_I * xi_hat
+ rhs = u - cos_theta*H_J*eta_hat
+ if any(x < -epsilon for x in H_J_c_T * rhs):
+ return infeasible_result
+
+ return (True, u, v)
+
+
+def max_angle(P, Q, exact, epsilon):
+ r"""
+ Find the maximal angle between the cones `P` and `Q`.
+
+ This implements
+ :meth:`sage.geometry.cone.ConvexRationalPolyhedralCone.max_angle`,
+ which should be fully documented.
+
+ EXAMPLES:
+
+ For the sake of the user interface, the argument validation for
+ this function is performed in the associated cone method; we can
+ therefore crash it by feeding it invalid input like an
+ inadmissible cone::
+
+ sage: from sage.geometry.cone_critical_angles import max_angle
+ sage: K = cones.trivial(3)
+ sage: max_angle(K,K,True,0)
+ Traceback (most recent call last):
+ ...
+ IndexError: list index out of range
+ """
+ # The lattice dimensions of P and Q are guaranteed to be equal
+ # because the cone method checks it before calling us.
+ n = P.lattice_dim()
+
+ ring = RDF
+ if exact:
+ ring = AA
+ # For some reason we can go RR -> QQ -> AA, but not
+ # straight from RR to AA.
+ epsilon = QQ(epsilon)
+ epsilon = ring(epsilon)
+
+ # First check if P is contained in the dual of Q. Keep track of
+ # the minimum inner product (and associated vectors) while doing
+ # so; then if P is contained in dual(Q), we just return the pair
+ # with the smallest inner product.
+ gs = [g.change_ring(ring).normalized() for g in P]
+ Q_is_P = (P == Q) # This is used again later
+ if Q_is_P:
+ hs = gs
+ else:
+ hs = [h.change_ring(ring).normalized() for h in Q]
+
+ (M, min_ip, min_u, min_v) = compute_gevp_M(gs,hs)
+
+ if min_ip >= 0: # The maximal angle is acute!
+ return (arccos(min_ip), min_u, min_v)
+
+ # Also check to see if the maximal angle is pi. In particular this
+ # is true when either P or Q is the entire ambient space.
+ P_and_negative_Q = P.intersection(-Q)
+ if not (P_and_negative_Q.is_trivial()):
+ u = P_and_negative_Q.ray(0).change_ring(ring).normalized()
+ v = -u
+ return (pi, u, v)
+
+ # When P == Q, GG and HH are both just M. We rule out the
+ # cardinality ``n`` in the index sets because it will eventually
+ # result in a GEVP whose only solutions are lambda in {-1,0,1};
+ # none of which we want! We rule out 0 and 1 with the acute check,
+ # and -1 with the pi (P_and_negative_Q) check.
+ #
+ # It's VERY IMPORTANT that we construct lists from the index set
+ # generators, because we're going to use them in a nested loop!
+ G = matrix.column(gs)
+ G_index_sets = [s for s in gevp_licis(G) if not len(s) == n]
+
+ if Q_is_P:
+ GG = M
+ H = G
+ HH = M
+ H_index_sets = G_index_sets
+ else:
+ GG = G.transpose() * G
+ H = matrix.column(hs)
+ HH = H.transpose() * H
+ H_index_sets = [s for s in gevp_licis(H) if not len(s) == n]
+
+ # Keep track of the (cos-theta, xi, eta, multiplicity) tuples with
+ # multiplicity > 1. These are only a problem if they could
+ # potentially be maximal. Therefore, we want to inspect them AFTER
+ # we've checked all of the multiplicity=1 angles and found the
+ # largest. This allows us to ignore most of the problematic
+ # eigenspaces, independent of the order in which we run through I,J.
+ big_eigenspaces = []
+
+ for I in G_index_sets:
+ G_I = G.matrix_from_columns(I)
+ I_complement = [i for i in range(P.nrays()) if i not in I]
+ G_I_c_T = G.matrix_from_columns(I_complement).transpose()
+
+ for J in H_index_sets:
+ J_complement = [j for j in range(Q.nrays()) if j not in J]
+ H_J = H.matrix_from_columns(J)
+ H_J_c_T = H.matrix_from_columns(J_complement).transpose()
+
+ for (cos_theta,xi,eta,mult) in solve_gevp_nonzero(GG, HH, M, I, J):
+
+ if cos_theta >= min_ip:
+ # This potential critical angle is smaller than or
+ # equal to one that we've already found. Why
+ # bother?
+ continue
+
+ if cos_theta == -1:
+ # We already ruled this case out with the
+ # "P_and_negative_Q" trick.
+ continue
+
+ (is_feasible, u, v) = check_gevp_feasibility(cos_theta,
+ xi,
+ eta,
+ G_I,
+ G_I_c_T,
+ H_J,
+ H_J_c_T,
+ epsilon)
+
+ if is_feasible:
+ min_ip = cos_theta
+ min_u = u
+ min_v = v
+ elif mult > 1:
+ # Save this for later. The eigenvalue cos_theta
+ # might be so big that we can ignore it.
+ big_eigenspaces.append((cos_theta, xi, eta, mult))
+ continue
+
+ for (cos_theta, xi, eta, mult) in big_eigenspaces:
+ if cos_theta < min_ip:
+ # The existence of a big eigenspace is only a problem if
+ # cos_theta could actually be minimal.
+
+ if exact and P.is_strictly_convex():
+ if Q_is_P or Q.is_strictly_convex():
+ # The maximal angle composed with the conic hull
+ # is continuous at (gs,hs), so we can retry with
+ # inexact arithmetic. That will "perturb"
+ # everything, hopefully eliminating any larger
+ # eigenspaces and without changing the answer too
+ # much.
+ return max_angle(P, Q, False, epsilon)
+
+ # Either we don't know that the maximal angle of the conic
+ # hull is continuous at (gs,hs), or we're already using
+ # inexact arithmetic. There's nothing left to try. (Note
+ # that the case where either P or Q is the ambient space
+ # was handled much earlier, since in that case the maximal
+ # angle is obviously pi.)
+ raise ValueError('eigenspace of dimension %d > 1 '
+ 'corresponding to eigenvalue %s'
+ % (mult, cos_theta))
+
+ return (arccos(min_ip), min_u, min_v)
diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py
index 832a76bb65d..0fffcdeb58c 100644
--- a/src/sage/geometry/fan.py
+++ b/src/sage/geometry/fan.py
@@ -238,6 +238,8 @@
from copy import copy
from warnings import warn
+import sage.geometry.abc
+
from sage.structure.richcmp import richcmp_method, richcmp
from sage.combinat.combination import Combinations
from sage.combinat.posets.posets import FinitePoset
@@ -245,7 +247,6 @@
Cone,
ConvexRationalPolyhedralCone,
IntegralRayCollection,
- is_Cone,
normalize_rays)
from sage.geometry.hasse_diagram import lattice_from_incidences
from sage.geometry.point_collection import PointCollection
@@ -571,7 +572,7 @@ def result():
cones = ((), )
rays = ()
return result()
- if is_Cone(cones[0]):
+ if isinstance(cones[0], sage.geometry.abc.ConvexRationalPolyhedralCone):
# Construct the fan from Cone objects
if lattice is None:
lattice = cones[0].lattice()
@@ -749,11 +750,10 @@ def FaceFan(polytope, lattice=None):
ValueError: face fans are defined only for
polytopes containing the origin as an interior point!
"""
- from sage.geometry.lattice_polytope import is_LatticePolytope
interior_point_error = ValueError(
"face fans are defined only for polytopes containing "
"the origin as an interior point!")
- if is_LatticePolytope(polytope):
+ if isinstance(polytope, sage.geometry.abc.LatticePolytope):
if any(d <= 0 for d in polytope.distances([0] * polytope.dim())):
raise interior_point_error
cones = (f.ambient_vertex_indices() for f in polytope.facets())
@@ -843,8 +843,7 @@ def NormalFan(polytope, lattice=None):
"""
dimension_error = ValueError(
'the normal fan is only defined for full-dimensional polytopes')
- from sage.geometry.lattice_polytope import is_LatticePolytope
- if is_LatticePolytope(polytope):
+ if isinstance(polytope, sage.geometry.abc.LatticePolytope):
if polytope.dim() != polytope.lattice_dim():
raise dimension_error
rays = polytope.facet_normals()
@@ -2425,7 +2424,7 @@ def embed(self, cone):
ValueError: 2-d cone in 3-d lattice N does not belong
to Rational polyhedral fan in 3-d lattice N!
"""
- if not is_Cone(cone):
+ if not isinstance(cone, sage.geometry.abc.ConvexRationalPolyhedralCone):
raise TypeError("%s is not a cone!" % cone)
if cone.ambient() is self:
return cone
diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py
index bb1e04efabe..19ae8698f8c 100644
--- a/src/sage/geometry/hyperplane_arrangement/arrangement.py
+++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py
@@ -242,7 +242,7 @@
\chi(x) := \sum_{w\in P} \mu(w) x^{dim(w)}
-where the sum is `P` is the
+where `P` is the
:meth:`~HyperplaneArrangementElement.intersection_poset` of the
arrangement and `\mu` is the Möbius function of `P`::
@@ -335,7 +335,7 @@
arrangements.
"""
-#*****************************************************************************
+# *****************************************************************************
# Copyright (C) 2013 David Perkinson
# Volker Braun
#
@@ -344,25 +344,24 @@
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
-#*****************************************************************************
+# *****************************************************************************
# Possible extensions for hyperplane_arrangement.py:
# - the big face lattice
# - create ties with the Sage matroid methods
# - hyperplane arrangements over other fields
+from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane
+from sage.matrix.constructor import matrix, vector
+from sage.misc.cachefunc import cached_method
+from sage.modules.free_module import VectorSpace
+from sage.rings.integer_ring import ZZ
+from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
+from sage.rings.rational_field import QQ
from sage.structure.parent import Parent
from sage.structure.element import Element
from sage.structure.richcmp import richcmp
from sage.structure.unique_representation import UniqueRepresentation
-from sage.rings.integer_ring import ZZ
-from sage.rings.rational_field import QQ
-from sage.misc.cachefunc import cached_method
-from sage.matrix.constructor import matrix, vector
-from sage.modules.free_module import VectorSpace
-from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
-
-from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane
class HyperplaneArrangementElement(Element):
@@ -385,10 +384,9 @@ def __init__(self, parent, hyperplanes, check=True, backend=None):
- ``hyperplanes`` -- a tuple of hyperplanes
- - ``check`` -- boolean (optional; default ``True``); whether
- to check input
+ - ``check`` -- boolean (default: ``True``); whether to check input
- - ``backend`` -- string (optional; default: ``None``); the backend to
+ - ``backend`` -- string (optional); the backend to
use for the related polyhedral objects
EXAMPLES::
@@ -499,7 +497,7 @@ def hyperplanes(self):
OUTPUT:
- An integer.
+ A tuple
EXAMPLES::
@@ -659,10 +657,10 @@ def union(self, other):
sage: H. = HyperplaneArrangements(QQ)
sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0])
sage: B = H([1,1,1], [1,-1,1], [1,0,-1])
- sage: A.union(B)
- Arrangement of 8 hyperplanes of dimension 2 and rank 2
- sage: A | B # syntactic sugar
+ sage: C = A.union(B); C
Arrangement of 8 hyperplanes of dimension 2 and rank 2
+ sage: C == A | B # syntactic sugar
+ True
A single hyperplane is coerced into a hyperplane arrangement
if necessary::
@@ -678,9 +676,10 @@ def union(self, other):
Arrangement of 6 hyperplanes of dimension 2 and rank 2
"""
P = self.parent()
- other = P(other)
- hyperplanes = self._hyperplanes + other._hyperplanes
- return P(*hyperplanes, backend=self._backend)
+ other_h = P(other)
+ hyperplanes = self._hyperplanes + other_h._hyperplanes
+ result = P(*hyperplanes, backend=self._backend)
+ return result
add_hyperplane = union
@@ -713,9 +712,10 @@ def cone(self, variable='t'):
OUTPUT:
- A new hyperplane arrangement. Its equations consist of
- `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the
- original arrangement and the equation `[0, 1, 0, \ldots, 0]`.
+ A new hyperplane arrangement `L`.
+ Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each
+ `[d, a_1, \ldots, a_n]` in the original arrangement and the
+ equation `[0, 1, 0, \ldots, 0]` (maybe not in this order).
.. WARNING::
@@ -724,7 +724,8 @@ def cone(self, variable='t'):
no guarantee that the order in which they appear in
``self.hyperplanes()`` will match the order in which their
counterparts in ``self.cone()`` will appear in
- ``self.cone().hyperplanes()``!
+ ``self.cone().hyperplanes()``! This warning does not apply
+ to ordered hyperplane arrangements.
EXAMPLES::
@@ -758,7 +759,7 @@ def cone(self, variable='t'):
hyperplanes.append([0, 1] + [0] * self.dimension())
P = self.parent()
names = (variable,) + P._names
- H = HyperplaneArrangements(self.parent().base_ring(), names=names)
+ H = type(P).__base__(P.base_ring(), names=names)
return H(*hyperplanes, backend=self._backend)
@cached_method
@@ -853,16 +854,16 @@ def intersection_poset(self, element_label="int"):
W = Vector space of dimension 2 over Rational Field]
"""
if element_label == "int":
- def update(mapping, val, I):
+ def update(mapping, val, I0):
mapping[val] = len(mapping)
elif element_label == "subset":
from sage.sets.set import Set
- def update(mapping, val, I):
+ def update(mapping, val, I0):
mapping[val] = Set(val)
elif element_label == "subspace":
- def update(mapping, val, I):
- mapping[val] = I
+ def update(mapping, val, I0):
+ mapping[val] = I0
else:
raise ValueError("invalid element label type")
@@ -885,13 +886,13 @@ def update(mapping, val, I):
for label, T in cur_level:
edges = []
for i, H in enumerate(hyperplanes):
- I = H.intersection(T)
- if I is not None and I != T:
+ I0 = H.intersection(T)
+ if I0 is not None and I0 != T:
try:
- target = new_level[I]
+ target = new_level[I0]
except KeyError:
target = set(label)
- new_level[I] = target
+ new_level[I0] = target
target.add(i)
edges.append(target)
hasse[label] = edges
@@ -1216,7 +1217,7 @@ def deletion(self, hyperplanes):
raise ValueError('hyperplane is not in the arrangement')
return parent(*planes, backend=self._backend)
- def restriction(self, hyperplane):
+ def restriction(self, hyperplane, repetitions=False):
r"""
Return the restriction to a hyperplane.
@@ -1224,10 +1225,13 @@ def restriction(self, hyperplane):
- ``hyperplane`` -- a hyperplane of the hyperplane arrangement
+ - ``repetitions`` -- boolean (default: ``False``); eliminate
+ repetitions for ordered arrangements
+
OUTPUT:
- The restriction of the hyperplane arrangement to the given
- ``hyperplane``.
+ The restriction `\mathcal{A}_H` of the
+ hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`.
EXAMPLES::
@@ -1236,8 +1240,12 @@ def restriction(self, hyperplane):
Arrangement of 6 hyperplanes of dimension 4 and rank 3
sage: H = A[0]; H
Hyperplane 0*u + 0*x + y - z + 0
- sage: R = A.restriction(H); R
+ sage: R = A.restriction(H); R
Arrangement
+ sage: A.add_hyperplane(z).restriction(z)
+ Arrangement of 6 hyperplanes of dimension 3 and rank 3
+ sage: A.add_hyperplane(u).restriction(u)
+ Arrangement of 6 hyperplanes of dimension 3 and rank 3
sage: D = A.deletion(H); D
Arrangement of 5 hyperplanes of dimension 4 and rank 3
sage: ca = A.characteristic_polynomial()
@@ -1281,8 +1289,19 @@ def restriction(self, hyperplane):
hyperplanes.append([A, b])
names = list(parent._names)
names.pop(pivot)
- H = HyperplaneArrangements(parent.base_ring(), names=tuple(names))
- return H(*hyperplanes, signed=False, backend=self._backend)
+ from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements
+ if isinstance(parent, OrderedHyperplaneArrangements):
+ H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names))
+ if not repetitions:
+ L = list(hyperplanes)
+ hyperplanes = ()
+ for h in L:
+ if h not in hyperplanes:
+ hyperplanes += (h,)
+ else:
+ H = HyperplaneArrangements(parent.base_ring(), names=tuple(names))
+ result = H(*hyperplanes, signed=False, backend=self._backend)
+ return result
def change_ring(self, base_ring):
"""
@@ -1665,7 +1684,8 @@ def essentialization(self):
OUTPUT:
- The essentialization as a new hyperplane arrangement.
+ The essentialization `\mathcal{A}'` of `\mathcal{A}` as a
+ new hyperplane arrangement.
EXAMPLES::
@@ -2091,7 +2111,7 @@ def regions(self):
for hyperplane in self:
ieq = vector(R, hyperplane.dense_coefficient_list())
- pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be)
+ pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be)
neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be)
if not regions:
# See comment above.
@@ -2129,8 +2149,8 @@ def regions(self):
else:
# In this case, at least one of the vertices is not on the hyperplane.
# So we check if any ray or line pokes the hyperplane.
- if ( any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or
- any(ieq[1:]*l[:] != 0 for l in region_lines)):
+ if (any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or
+ any(ieq[1:]*ll[:] != 0 for ll in region_lines)):
splits = True
if splits:
@@ -2158,10 +2178,10 @@ def poset_of_regions(self, B=None, numbered_labels=True):
INPUT:
- - ``B`` -- a region (optional; default: ``None``); if ``None``, then
+ - ``B`` -- a region (optional); if ``None``, then
an arbitrary region is chosen as the base region.
- - ``numbered_labels`` -- bool (optional; default: ``True``); if ``True``,
+ - ``numbered_labels`` -- bool (default: ``True``); if ``True``,
then the elements of the poset are numbered. Else they are labelled
with the regions themselves.
@@ -2345,12 +2365,13 @@ def closed_faces(self, labelled=True):
((-1, -1), A 2-dimensional polyhedron in QQ^2 defined
as the convex hull of 1 vertex and 2 rays, (-1, -2))]
- sage: a = hyperplane_arrangements.braid(3) # needs sage.graphs
- sage: a.hyperplanes() # needs sage.graphs
+ sage: # needs sage.graphs
+ sage: a = hyperplane_arrangements.braid(3)
+ sage: a.hyperplanes()
(Hyperplane 0*t0 + t1 - t2 + 0,
Hyperplane t0 - t1 + 0*t2 + 0,
Hyperplane t0 + 0*t1 - t2 + 0)
- sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] # needs sage.graphs
+ sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()]
[((0, 0, 0), A 1-dimensional polyhedron in QQ^3 defined
as the convex hull of 1 vertex and 1 line, (0, 0, 0)),
((0, 1, 1), A 2-dimensional polyhedron in QQ^3 defined
@@ -2422,7 +2443,7 @@ def closed_faces(self, labelled=True):
zero_half = Polyhedron(eqns=[ieq], base_ring=R, backend=be)
# ``zero_half`` is the hyperplane ``hyperplane`` itself
# (viewed as a polyhedron).
- pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be)
+ pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be)
neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be)
subdivided = []
for signs, face in faces:
@@ -2904,8 +2925,9 @@ def whitney_data(self):
EXAMPLES::
+ sage: # needs sage.combinat
sage: A = hyperplane_arrangements.Shi(3)
- sage: A.whitney_data() # needs sage.combinat
+ sage: A.whitney_data()
(
[ 1 -6 9] [ 1 6 6]
[ 0 6 -15] [ 0 6 15]
@@ -3247,7 +3269,7 @@ def orlik_solomon_algebra(self, base_ring=None, ordering=None, **kwds):
"""
if base_ring is None:
base_ring = self.base_ring()
- return self.matroid().orlik_solomon_algebra(base_ring, ordering,**kwds)
+ return self.matroid().orlik_solomon_algebra(base_ring, ordering, **kwds)
def orlik_terao_algebra(self, base_ring=None, ordering=None, **kwds):
"""
@@ -3394,9 +3416,10 @@ def derivation_module_free_chain(self):
EXAMPLES::
- sage: W = WeylGroup(['A',3], prefix='s') # needs sage.combinat sage.groups
- sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups
- sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) # needs sage.combinat sage.groups
+ sage: # needs sage.combinat sage.groups
+ sage: W = WeylGroup(['A',3], prefix='s')
+ sage: A = W.long_element().inversion_arrangement()
+ sage: for M in A.derivation_module_free_chain(): print("%s\n"%M)
[ 1 0 0]
[ 0 1 0]
[ 0 0 a3]
@@ -3539,9 +3562,10 @@ def derivation_module_basis(self, algorithm="singular"):
EXAMPLES::
- sage: W = WeylGroup(['A', 2], prefix='s') # needs sage.combinat sage.groups
- sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups
- sage: A.derivation_module_basis() # needs sage.combinat sage.groups
+ sage: # needs sage.combinat sage.groups
+ sage: W = WeylGroup(['A', 2], prefix='s')
+ sage: A = W.long_element().inversion_arrangement()
+ sage: A.derivation_module_basis()
[(a1, a2), (0, a1*a2 + a2^2)]
TESTS:
@@ -3560,7 +3584,7 @@ def derivation_module_basis(self, algorithm="singular"):
....: else:
....: assert exponents(B) == exponents(Bp)
"""
- alg = algorithm # prevent possible changes to a global variable
+ alg = algorithm # prevent possible changes to a global variable
if alg == "singular":
# import sage.libs.singular.function_factory
# syz = sage.libs.singular.function_factory.ff.syz
@@ -3575,7 +3599,7 @@ def derivation_module_basis(self, algorithm="singular"):
# Check using Saito's criterion
if det / f in f.parent().base_ring() and not det.is_zero():
return basis.rows()
- except ValueError: # Non-square matrix or det = 0
+ except ValueError: # Non-square matrix or det = 0
pass
# Check if it is free
if not self.is_free(algorithm=alg):
@@ -3586,7 +3610,7 @@ def derivation_module_basis(self, algorithm="singular"):
if alg == "BC":
C = self.derivation_module_free_chain()
if C is not None:
- if not C: # C is an empty list
+ if not C: # C is an empty list
S = self.parent().ambient_space().symmetric_space()
return matrix.identity(S, self.dimension()).rows()
from sage.misc.misc_c import prod
@@ -3739,15 +3763,15 @@ def _element_constructor_(self, *args, **kwds):
hyperplane; alternatively, a single polytope or a single
hyperplane arrangement
- - ``signed`` -- boolean (optional, default: ``True``); whether to
+ - ``signed`` -- boolean (default: ``True``); whether to
preserve signs of hyperplane equations
- - ``warn_duplicates`` -- boolean (optional, default: ``False``);
+ - ``warn_duplicates`` -- boolean (default: ``False``);
whether to issue a warning if duplicate hyperplanes were
passed -- note that duplicate hyperplanes are always removed,
whether or not there is a warning shown
- - ``check`` -- boolean (optional, default: ``True``); whether to
+ - ``check`` -- boolean (default: ``True``); whether to
perform argument checking.
EXAMPLES::
diff --git a/src/sage/geometry/hyperplane_arrangement/hyperplane.py b/src/sage/geometry/hyperplane_arrangement/hyperplane.py
index 9ca1a7c42ac..dfd1e4c6169 100644
--- a/src/sage/geometry/hyperplane_arrangement/hyperplane.py
+++ b/src/sage/geometry/hyperplane_arrangement/hyperplane.py
@@ -108,9 +108,10 @@
# http://www.gnu.org/licenses/
# *****************************************************************************
+import sage.geometry.abc
-from sage.misc.cachefunc import cached_method
from sage.geometry.linear_expression import LinearExpression, LinearExpressionModule
+from sage.misc.cachefunc import cached_method
class Hyperplane(LinearExpression):
@@ -460,9 +461,8 @@ def intersection(self, other):
sage: h.intersection(polytopes.cube())
A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices
"""
- from sage.geometry.polyhedron.base import is_Polyhedron
from sage.geometry.polyhedron.constructor import Polyhedron
- if not is_Polyhedron(other):
+ if not isinstance(other, sage.geometry.abc.Polyhedron):
try:
other = other.polyhedron()
except AttributeError:
diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py
index cdfaca2faf0..98b03ceac70 100644
--- a/src/sage/geometry/hyperplane_arrangement/library.py
+++ b/src/sage/geometry/hyperplane_arrangement/library.py
@@ -156,7 +156,7 @@ def bigraphical(self, G, A=None, K=QQ, names=None):
for u, v in G.edge_iterator(labels=False, sort_vertices=False):
i = vertex_to_int[u]
j = vertex_to_int[v]
- hyperplanes.append( x[i] - x[j] - A[i][j])
+ hyperplanes.append(x[i] - x[j] - A[i][j])
hyperplanes.append(-x[i] + x[j] - A[j][i])
return H(*hyperplanes)
@@ -794,7 +794,7 @@ def Shi(self, data, K=QQ, names=None, m=1):
hyperplanes = []
for a in PR:
- for const in range(-m+1,m+1):
+ for const in range(-m + 1, m + 1):
hyperplanes.append(sum(a[j]*x[j] for j in range(d))-const)
A = H(*hyperplanes)
x = polygen(QQ, 'x')
diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py
new file mode 100644
index 00000000000..bb16768e13b
--- /dev/null
+++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py
@@ -0,0 +1,650 @@
+r"""
+Ordered Hyperplane Arrangements
+
+The :class:`HyperplaneArrangements` orders the hyperplanes in a arrangement
+independently of the way the hyperplanes are introduced. The class
+:class:`OrderedHyperplaneArrangements` fixes an order specified by
+the user. This can be needed for certain properties, e.g., fundamental group with
+information about meridians, braid monodromy with information about the strands;
+in the future, it may be useful for combinatorial properties.
+There are no other differences with usual hyperplane arrangements.
+
+An ordered arrangement is an arrangement where the hyperplanes are sorted
+by the user::
+
+ sage: H0. = HyperplaneArrangements(QQ)
+ sage: H0(t0 - t1, t1 - t2, t0 - t2)
+ Arrangement
+ sage: H. = OrderedHyperplaneArrangements(QQ)
+ sage: H(t0 - t1, t1 - t2, t0 - t2)
+ Arrangement
+
+Some methods are adapted, e.g., :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.hyperplanes`,
+and some new ones are created, regarding
+hyperplane sections and fundamental groups::
+
+ sage: H. = HyperplaneArrangements(QQ)
+ sage: H1. = OrderedHyperplaneArrangements(QQ)
+ sage: A1 = H1(x, y); A = H(A1)
+ sage: A.hyperplanes()
+ (Hyperplane 0*x + y + 0, Hyperplane x + 0*y + 0)
+ sage: A1.hyperplanes()
+ (Hyperplane x + 0*y + 0, Hyperplane 0*x + y + 0)
+
+We see the differences in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.union`::
+
+ sage: H. = HyperplaneArrangements(QQ)
+ sage: H1. = OrderedHyperplaneArrangements(QQ)
+ sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0])
+ sage: B = H([1,1,1], [1,-1,1], [1,0,-1])
+ sage: C = A.union(B)
+ sage: A1 = H1(A); B1 = H1(B); C1 = A1.union(B1)
+ sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()]
+ [0, 5, 6, 1, 2, 3, 7, 4]
+
+Also in meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.cone`::
+
+ sage: # needs sage.combinat
+ sage: a. = hyperplane_arrangements.semiorder(3)
+ sage: H. = OrderedHyperplaneArrangements(QQ)
+ sage: a1 = H(a)
+ sage: b = a.cone(); b1 = a1.cone()
+ sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()]
+ [0, 2, 4, 6, 1, 3, 5]
+
+And in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.restriction`::
+
+ sage: # needs sage.graphs
+ sage: A. = hyperplane_arrangements.braid(4)
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: A1 = L(A)
+ sage: H = A[0]; H
+ Hyperplane 0*u + 0*x + y - z + 0
+ sage: A.restriction(H)
+ Arrangement
+ sage: A1.restriction(H)
+ Arrangement
+ sage: A1.restriction(H, repetitions=True)
+ Arrangement of 5 hyperplanes of dimension 3 and rank 2
+
+AUTHORS:
+
+- Enrique Artal (2023-12): initial version
+
+This module adds some features to the *unordered* one for some
+properties which depend on the order.
+"""
+
+# *****************************************************************************
+# Copyright (C) 2023 Enrique Artal
+#
+# 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
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+# http://www.gnu.org/licenses/
+# *****************************************************************************
+
+from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangementElement
+from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements
+from sage.geometry.hyperplane_arrangement.hyperplane import Hyperplane
+from sage.matrix.constructor import matrix, vector
+from sage.misc.misc_c import prod
+from sage.groups.free_group import FreeGroup
+from sage.rings.integer_ring import ZZ
+from sage.rings.qqbar import QQbar
+from sage.schemes.curves.plane_curve_arrangement import AffinePlaneCurveArrangements
+from sage.schemes.curves.plane_curve_arrangement import ProjectivePlaneCurveArrangements
+
+
+class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement):
+ """
+ An ordered hyperplane arrangement.
+
+ .. WARNING::
+
+ You should never create
+ :class:`OrderedHyperplaneArrangementElement` instances directly,
+ always use the parent.
+ """
+ def __init__(self, parent, hyperplanes, check=True, backend=None):
+ """
+ Construct an ordered hyperplane arrangement.
+
+ INPUT:
+
+ - ``parent`` -- the parent :class:`OrderedHyperplaneArrangements`
+
+ - ``hyperplanes`` -- a tuple of hyperplanes
+
+ - ``check`` -- boolean (default ``True``); whether
+ to check input
+
+ - ``backend`` -- string (default: ``None``); the backend to
+ use for the related polyhedral objects
+
+ EXAMPLES::
+
+ sage: H. = OrderedHyperplaneArrangements(QQ)
+ sage: elt = H(x, y); elt
+ Arrangement
+ sage: TestSuite(elt).run()
+ """
+ super().__init__(parent, hyperplanes, check=check, backend=backend)
+ self._affine_fundamental_group = None
+ self._affine_meridians = None
+ self._projective_fundamental_group = None
+ self._projective_meridians = None
+
+ def hyperplane_section(self, proj=True):
+ r"""
+ Compute a generic hyperplane section of ``self``.
+
+ INPUT:
+
+ - ``proj`` -- (default: ``True``); if the
+ ambient space is affine or projective
+
+ OUTPUT:
+
+ An arrangement `\mathcal{A}` obtained by intersecting with a
+ generic hyperplane
+
+ EXAMPLES::
+
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: L(x, y - 1, z).hyperplane_section()
+ Traceback (most recent call last):
+ ...
+ TypeError: the arrangement is not projective
+
+ sage: # needs sage.graphs
+ sage: A0. = hyperplane_arrangements.braid(4); A0
+ Arrangement of 6 hyperplanes of dimension 4 and rank 3
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: A = L(A0)
+ sage: M = A.matroid()
+ sage: A1 = A.hyperplane_section()
+ sage: A1
+ Arrangement of 6 hyperplanes of dimension 3 and rank 3
+ sage: M1 = A1.matroid()
+ sage: A2 = A1.hyperplane_section(); A2
+ Arrangement of 6 hyperplanes of dimension 2 and rank 2
+ sage: M2 = A2.matroid()
+ sage: T1 = M1.truncation()
+ sage: T1.is_isomorphic(M2)
+ True
+ sage: T1.isomorphism(M2)
+ {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
+
+ sage: # needs sage.combinat
+ sage: a0 = hyperplane_arrangements.semiorder(3); a0
+ Arrangement of 6 hyperplanes of dimension 3 and rank 2
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: a = L(a0)
+ sage: ca = a.cone()
+ sage: m = ca.matroid()
+ sage: a1 = a.hyperplane_section(proj=False)
+ sage: a1
+ Arrangement of 6 hyperplanes of dimension 2 and rank 2
+ sage: ca1 = a1.cone()
+ sage: m1 = ca1.matroid()
+ sage: m.isomorphism(m1)
+ {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
+ sage: p0 = hyperplane_arrangements.Shi(4)
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: p = L(p0)
+ sage: a = p.hyperplane_section(proj=False); a
+ Arrangement of 12 hyperplanes of dimension 3 and rank 3
+ sage: ca = a.cone()
+ sage: m = ca.matroid().truncation()
+ sage: a1 = a.hyperplane_section(proj=False); a1
+ Arrangement of 12 hyperplanes of dimension 2 and rank 2
+ sage: ca1 = a1.cone()
+ sage: m1 = ca1.matroid()
+ sage: m1.is_isomorphism(m, {j: j for j in range(13)})
+ True
+ """
+ if proj and not self.is_linear():
+ raise TypeError('the arrangement is not projective')
+ n0 = self.dimension()
+ if not proj:
+ H = self.cone()
+ H1 = H.hyperplane_section()
+ mat = matrix(h.coefficients()[1:] for h in H1)
+ m = mat.nrows()
+ for j in range(mat.ncols()):
+ if mat[m - 1, j] != 0:
+ mat.swap_columns(0, j)
+ break
+ for j in range(1, mat.ncols()):
+ mat.add_multiple_of_column(j, 0, -mat[m - 1, j] / mat[m - 1, 0])
+ vrs = H1.parent().variable_names()[1:]
+ A1 = OrderedHyperplaneArrangements(self.base_ring(), names=vrs)
+ mat_rows = mat.rows()[:-1]
+ H1b = A1(mat_rows)
+ return H1b
+ P = self.intersection_poset(element_label="subspace")
+ center = P.maximal_elements()[0].linear_part()
+ n1 = center.dimension()
+ U = []
+ for p in P:
+ if p.dimension() == n1 + 1:
+ B = [u for u in p.linear_part().basis() if u not in center]
+ U.append(B[0])
+ # U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1]
+ U0 = sum(U)
+ # We look for a linear hyperplane with integer coefficients
+ # defining a transversal hyperplane
+ for v in ZZ**n0:
+ v1 = v + U0
+ if 0 not in [w * v1 for w in U]:
+ break
+ h0 = self.parent()((0,) + tuple(v1))
+ H1 = self.add_hyperplane(h0)
+ return H1.restriction(h0)
+
+ def affine_fundamental_group(self):
+ r"""
+ Return the fundamental group of the complement of an affine
+ hyperplane arrangement in `\CC^n` whose equations have
+ coefficients in a subfield of `\QQbar`.
+
+ OUTPUT:
+
+ A finitely presented fundamental group.
+
+ .. NOTE::
+
+ This functionality requires the ``sirocco`` package to be installed.
+
+ EXAMPLES::
+
+ sage: # needs sirocco
+ sage: A. = OrderedHyperplaneArrangements(QQ)
+ sage: L = [y + x, y + x - 1]
+ sage: H = A(L)
+ sage: H.affine_fundamental_group()
+ Finitely presented group < x0, x1 | >
+ sage: L = [x, y, x + 1, y + 1, x - y]
+ sage: A(L).affine_fundamental_group()
+ Finitely presented group
+ < x0, x1, x2, x3, x4 | x4*x0*x4^-1*x0^-1,
+ x0*x2*x3*x2^-1*x0^-1*x3^-1,
+ x1*x2*x4*x2^-1*x1^-1*x4^-1,
+ x2*x3*x0*x2^-1*x0^-1*x3^-1,
+ x2*x4*x1*x2^-1*x1^-1*x4^-1,
+ x4*x1*x4^-1*x3^-1*x2^-1*x1^-1*x2*x3 >
+ sage: H = A(x, y, x + y)
+ sage: H.affine_fundamental_group()
+ Finitely presented group
+ < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 >
+ sage: H.affine_fundamental_group() # repeat to use the attribute
+ Finitely presented group
+ < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 >
+ sage: T. = QQ[]
+ sage: K. = NumberField(t^3 + t + 1)
+ sage: L. = OrderedHyperplaneArrangements(K)
+ sage: H = L(a*x + y -1, x + a*y + 1, x - 1, y - 1)
+ sage: H.affine_fundamental_group()
+ Traceback (most recent call last):
+ ...
+ TypeError: the base field is not in QQbar
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: L([t - j for j in range(4)]).affine_fundamental_group()
+ Finitely presented group < x0, x1, x2, x3 | >
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: L(L.gens() + (x + y + z + 1,)).affine_fundamental_group().sorted_presentation()
+ Finitely presented group
+ < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1,
+ x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1,
+ x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 >
+ sage: A = OrderedHyperplaneArrangements(QQ, names=())
+ sage: H = A(); H
+ Empty hyperplane arrangement of dimension 0
+ sage: H.affine_fundamental_group()
+ Finitely presented group < | >
+ """
+ K = self.base_ring()
+ if not K.is_subring(QQbar):
+ raise TypeError('the base field is not in QQbar')
+ if self._affine_fundamental_group:
+ return self._affine_fundamental_group
+ n = self.dimension()
+ r = len(self)
+ if n == 0:
+ return FreeGroup(0) / []
+ if n == 1:
+ G = FreeGroup(r) / []
+ dic = {j: G.gen(j) for j in range(r)}
+ dic[r] = [prod(G.gens()) ** -1]
+ self._affine_fundamental_group = G
+ self._affine_meridians = dic
+ return G
+ if n == 2:
+ S = self.parent().ambient_space().symmetric_space()
+ coord = vector((1,) + S.gens())
+ Af = AffinePlaneCurveArrangements(K, names=self.parent().variable_names())
+ L = Af([vector(line.coefficients()) * coord for line in self])
+ G = L.fundamental_group()
+ self._affine_fundamental_group = G
+ self._affine_meridians = L._meridians_simpl_vertical
+ return G
+ H1 = self.hyperplane_section(proj=False)
+ G = H1.affine_fundamental_group()
+ self._affine_fundamental_group = G
+ self._affine_meridians = H1._affine_meridians
+ return G
+
+ def affine_meridians(self):
+ r"""
+ Return the meridians of each hyperplane (including the one at infinity).
+
+ OUTPUT:
+
+ A dictionary
+
+ .. NOTE::
+
+ This functionality requires the ``sirocco`` package to be installed.
+
+ EXAMPLES::
+
+ sage: # needs sirocco
+ sage: A. = OrderedHyperplaneArrangements(QQ)
+ sage: L = [y + x, y + x - 1]
+ sage: H = A(L)
+ sage: g = H.affine_fundamental_group()
+ sage: g
+ Finitely presented group < x0, x1 | >
+ sage: H.affine_meridians()
+ {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]}
+ sage: H1 = H.add_hyperplane(y - x)
+ sage: H1.affine_meridians()
+ {0: [x0], 1: [x1], 2: [x2], 3: [x2^-1*x1^-1*x0^-1]}
+ """
+ if self._affine_meridians is None:
+ self.affine_fundamental_group()
+ return dict(self._affine_meridians)
+
+ def projective_fundamental_group(self):
+ r"""
+ Return the fundamental group of the complement of a projective
+ hyperplane arrangement.
+
+ OUTPUT:
+
+ The finitely presented group of the complement
+ in the projective space whose equations have
+ coefficients in a subfield of `\QQbar`.
+
+ .. NOTE::
+
+ This functionality requires the ``sirocco`` package to be installed.
+
+ EXAMPLES::
+
+ sage: # needs sirocco
+ sage: A. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A(x, y, x + y)
+ sage: H.projective_fundamental_group()
+ Finitely presented group < x0, x1 | >
+
+ sage: # needs sirocco sage.graphs
+ sage: A3. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A3(hyperplane_arrangements.braid(4).essentialization())
+ sage: G3 = H.projective_fundamental_group(); G3.sorted_presentation()
+ Finitely presented group
+ < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1,
+ x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0,
+ x4^-1*x1^-1*x0^-1*x4*x0*x1,
+ x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2,
+ x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2,
+ x3^-1*x1^-1*x3*x1 >
+ sage: G3.abelian_invariants()
+ (0, 0, 0, 0, 0)
+ sage: A4. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A4(hyperplane_arrangements.braid(4))
+ sage: G4 = H.projective_fundamental_group(); G4.sorted_presentation()
+ Finitely presented group
+ < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1,
+ x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0,
+ x4^-1*x1^-1*x0^-1*x4*x0*x1,
+ x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2,
+ x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2,
+ x3^-1*x1^-1*x3*x1 >
+ sage: G4.abelian_invariants()
+ (0, 0, 0, 0, 0)
+
+ sage: # needs sirocco
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: H = hyperplane_arrangements.coordinate(5)
+ sage: H = L(H)
+ sage: g = H.projective_fundamental_group()
+ sage: g.is_abelian(), g.abelian_invariants()
+ (True, (0, 0, 0, 0))
+ sage: L(t0, t1, t2, t3, t4, t0 - 1).projective_fundamental_group()
+ Traceback (most recent call last):
+ ...
+ TypeError: the arrangement is not projective
+ sage: T. = QQ[]
+ sage: K. = NumberField(t^3 + t + 1)
+ sage: L. = OrderedHyperplaneArrangements(K)
+ sage: H = L(a*x + y - z, x + a*y + z, x - z, y - z)
+ sage: H.projective_fundamental_group()
+ Traceback (most recent call last):
+ ...
+ TypeError: the base field is not in QQbar
+ sage: A. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A(); H
+ Empty hyperplane arrangement of dimension 1
+ sage: H.projective_fundamental_group()
+ Finitely presented group < | >
+ """
+ K = self.base_ring()
+ if not K.is_subring(QQbar):
+ raise TypeError('the base field is not in QQbar')
+ if not self.is_linear():
+ raise TypeError('the arrangement is not projective')
+ if self._projective_fundamental_group:
+ return self._projective_fundamental_group
+ n = self.dimension()
+ r = len(self)
+ if n == 1:
+ return FreeGroup(0) / []
+ if n == 2:
+ G = FreeGroup(r - 1) / []
+ dic = {j: G.gen(j) for j in range(r - 1)}
+ dic[r - 1] = [prod(G.gens()) ** -1]
+ self._projective_fundamental_group = G
+ self._projective_meridians = dic
+ return G
+ if n == 3:
+ S = self.parent().ambient_space().symmetric_space()
+ coord = vector(S.gens())
+ Proj = ProjectivePlaneCurveArrangements(K,
+ names=self.parent().variable_names())
+ L = Proj([vector(line.coefficients()[1:]) * coord for line in self])
+ G = L.fundamental_group()
+ self._projective_fundamental_group = G
+ self._projective_meridians = L._meridians_simpl
+ return G
+ H1 = self.hyperplane_section()
+ G = H1.projective_fundamental_group()
+ self._projective_fundamental_group = G
+ self._projective_meridians = H1._projective_meridians
+ return G
+
+ def projective_meridians(self):
+ r"""
+ Return the meridian of each hyperplane.
+
+ OUTPUT:
+
+ A dictionary
+
+ .. NOTE::
+
+ This functionality requires the ``sirocco`` package to be installed.
+
+ EXAMPLES::
+
+ sage: # needs sirocco
+ sage: A. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A(x, y, x + y)
+ sage: H.projective_meridians()
+ {0: x0, 1: x1, 2: [x1^-1*x0^-1]}
+
+ sage: # needs sirocco sage.graphs
+ sage: A3. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A3(hyperplane_arrangements.braid(4).essentialization())
+ sage: H.projective_meridians()
+ {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1],
+ 1: [x3], 2: [x4], 3: [x1], 4: [x2], 5: [x0]}
+ sage: A4. = OrderedHyperplaneArrangements(QQ)
+ sage: H = A4(hyperplane_arrangements.braid(4))
+ sage: H.projective_meridians()
+ {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], 1: [x3],
+ 2: [x4], 3: [x0], 4: [x2], 5: [x1]}
+
+ sage: # needs sirocco
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: H = hyperplane_arrangements.coordinate(5)
+ sage: H = L(H)
+ sage: H.projective_meridians()
+ {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]}
+ """
+ if self._projective_meridians is None:
+ self.projective_fundamental_group()
+ return dict(self._projective_meridians)
+
+
+class OrderedHyperplaneArrangements(HyperplaneArrangements):
+ """
+ Ordered Hyperplane arrangements.
+
+ For more information on hyperplane arrangements, see
+ :mod:`sage.geometry.hyperplane_arrangement.arrangement`.
+
+ INPUT:
+
+ - ``base_ring`` -- ring; the base ring
+
+ - ``names`` -- tuple of strings; the variable names
+
+ EXAMPLES::
+
+ sage: H. = HyperplaneArrangements(QQ)
+ sage: x
+ Hyperplane x + 0*y + 0
+ sage: x + y
+ Hyperplane x + y + 0
+ sage: H(x, y, x-1, y-1)
+ Arrangement
+ """
+ Element = OrderedHyperplaneArrangementElement
+
+ def _element_constructor_(self, *args, **kwds):
+ """
+ Construct an element of ``self``.
+
+ INPUT:
+
+ - ``*args`` -- positional arguments, each defining a
+ hyperplane; alternatively, a single polytope or a single
+ hyperplane arrangement
+
+ - ``signed`` -- boolean (default: ``True``); whether to
+ preserve signs of hyperplane equations
+
+ - ``check`` -- boolean (default: ``True``); whether to
+ perform argument checking.
+
+ EXAMPLES::
+
+ sage: L. = OrderedHyperplaneArrangements(QQ)
+ sage: L(x)
+ Arrangement
+ sage: L(x, y)
+ Arrangement
+ sage: L([x, y])
+ Arrangement
+ sage: L([0, 1, 0], [0, 0, 1])
+ Arrangement
+ sage: L([[0, 0, 1], [0, 1, 0]])
+ Arrangement
+
+ sage: L(polytopes.hypercube(2))
+ Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1>
+
+ sage: L(-x, x + y - 1, signed=False)
+ Arrangement
+
+ TESTS::
+
+ sage: L()
+ Empty hyperplane arrangement of dimension 2
+ sage: L(0) # zero is equivalent to no argument, Issue #8648
+ Empty hyperplane arrangement of dimension 2
+ sage: L(0*x) # degenerate hyperplane is NOT allowed
+ Traceback (most recent call last):
+ ...
+ ValueError: linear expression must be non-constant to define a hyperplane
+ sage: L(0*x, y) # ditto
+ Traceback (most recent call last):
+ ...
+ ValueError: linear expression must be non-constant to define a hyperplane
+ """
+ if len(args) == 1:
+ arg = args[0]
+ if isinstance(arg, HyperplaneArrangementElement) and arg.parent() is self:
+ # optimization if argument is already a hyperplane arrangement
+ return arg
+ if arg == 0 and not isinstance(arg, Hyperplane):
+ # zero = neutral element under addition = the empty hyperplane arrangement
+ args = []
+ # process keyword arguments
+ not_char2 = (self.base_ring().characteristic() != 2)
+ signed = kwds.pop('signed', not_char2)
+ check = kwds.pop('check', True)
+ backend = kwds.pop('backend', None)
+ if kwds:
+ raise ValueError('unknown keyword argument')
+ # process positional arguments
+ AA = self.ambient_space()
+ try:
+ hyperplanes = [AA(a) for a in args]
+ except (TypeError, ValueError, AttributeError):
+ if len(args) > 1:
+ raise
+ arg = args[0]
+ if hasattr(arg, 'Hrepresentation'):
+ hyperplanes = [AA(h) for h in arg.Hrepresentation()]
+ else:
+ hyperplanes = [AA(a) for a in arg]
+ hyperplanes = [h.primitive(signed) for h in hyperplanes]
+ if check:
+ if signed and not not_char2:
+ raise ValueError('cannot be signed in characteristic 2')
+ for h in hyperplanes:
+ if h.A() == 0:
+ raise ValueError('linear expression must be non-constant to define a hyperplane')
+ if not_char2 and -h in hyperplanes:
+ raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane')
+ return self.element_class(self, tuple(hyperplanes), backend=backend)
+
+ def _repr_(self):
+ """
+ Return a string representation.
+
+ OUTPUT:
+
+ A string.
+
+ EXAMPLES::
+
+ sage: L. = OrderedHyperplaneArrangements(QQ); L
+ Ordered hyperplane arrangements in 2-dimensional linear space
+ over Rational Field with coordinates x, y
+ """
+ return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space())
diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py
index b652e68c946..666548b9f39 100644
--- a/src/sage/geometry/lattice_polytope.py
+++ b/src/sage/geometry/lattice_polytope.py
@@ -482,6 +482,9 @@ def is_LatticePolytope(x):
sage: from sage.geometry.lattice_polytope import is_LatticePolytope
sage: is_LatticePolytope(1)
+ doctest:warning...
+ DeprecationWarning: is_LatticePolytope is deprecated, use isinstance instead
+ See https://github.com/sagemath/sage/issues/34307 for details.
False
sage: p = LatticePolytope([(1,0), (0,1), (-1,-1)])
sage: p # needs palp
@@ -489,6 +492,8 @@ def is_LatticePolytope(x):
sage: is_LatticePolytope(p)
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(34307, "is_LatticePolytope is deprecated, use isinstance instead")
return isinstance(x, LatticePolytopeClass)
@richcmp_method
@@ -989,7 +994,7 @@ def _palp(self, command, reduce_dimension=False):
sage: o = lattice_polytope.cross_polytope(3)
sage: o._palp("poly.x -f") # needs palp
'M:7 6 N:27 8 Pic:17 Cor:0\n'
- sage: print(o._palp("nef.x -f -N -p")) # random time information # needs palp
+ sage: print(o._palp("nef.x -f -N -p")) # random time information # needs palp
M:27 8 N:7 6 codim=2 #part=5
H:[0] P:0 V:2 4 5 0sec 0cpu
H:[0] P:2 V:3 4 5 0sec 0cpu
@@ -1234,7 +1239,7 @@ def _read_nef_partitions(self, data):
sage: o = lattice_polytope.cross_polytope(3)
sage: s = o.nef_x("-p -N -Lv") # needs palp
- sage: print(s) # random time values # needs palp
+ sage: print(s) # random time values # needs palp
M:27 8 N:7 6 codim=2 #part=5
3 6 Vertices in N-lattice:
1 0 0 -1 0 0
@@ -3139,7 +3144,7 @@ def normal_form(self, algorithm="palp_native", permutation=False):
M( 12, -1, -9, -6, 6),
M( 12, -1, -6, -3, 3)
in 5-d lattice M
- sage: P.normal_form(algorithm="palp_modified") # not tested (22s; MemoryError on 32 bit), needs sage.groups
+ sage: P.normal_form(algorithm="palp_modified") # not tested (22s; MemoryError on 32 bit), needs sage.groups
M( 6, 0, 0, 0, 0),
M( -6, 0, 0, 0, 0),
M( 0, 1, 0, 0, 0),
@@ -5264,7 +5269,7 @@ def _read_nef_x_partitions(data):
sage: o = lattice_polytope.cross_polytope(3)
sage: s = o.nef_x("-N -p") # needs palp
- sage: print(s) # random # needs palp
+ sage: print(s) # random # needs palp
M:27 8 N:7 6 codim=2 #part=5
P:0 V:2 4 5 0sec 0cpu
P:2 V:3 4 5 0sec 0cpu
diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py
index 28101e70646..c8e185d01e6 100644
--- a/src/sage/geometry/newton_polygon.py
+++ b/src/sage/geometry/newton_polygon.py
@@ -14,6 +14,8 @@
# https://www.gnu.org/licenses/
#############################################################################
+import sage.geometry.abc
+
from sage.structure.unique_representation import UniqueRepresentation
from sage.structure.parent import Parent
from sage.structure.element import Element
@@ -22,7 +24,6 @@
from sage.rings.infinity import Infinity
from sage.geometry.polyhedron.constructor import Polyhedron
-from sage.geometry.polyhedron.base import is_Polyhedron
class NewtonPolygon_element(Element):
@@ -716,7 +717,7 @@ def _element_constructor_(self, arg, sort_slopes=True, last_slope=Infinity):
sage: NewtonPolygon(1)
Finite Newton polygon with 1 vertex: (0, 0)
"""
- if is_Polyhedron(arg):
+ if isinstance(arg, sage.geometry.abc.Polyhedron):
return self.element_class(arg, parent=self)
if arg == 0:
polyhedron = Polyhedron(base_ring=self.base_ring(), ambient_dim=2)
diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py
index 09200a60be9..981404eb174 100644
--- a/src/sage/geometry/polyhedral_complex.py
+++ b/src/sage/geometry/polyhedral_complex.py
@@ -110,9 +110,11 @@
# ****************************************************************************
from copy import copy
+
+import sage.geometry.abc
+
from sage.topology.cell_complex import GenericCellComplex
from sage.geometry.polyhedron.constructor import Polyhedron
-from sage.geometry.polyhedron.base import is_Polyhedron
from sage.modules.free_module_element import vector
from sage.rings.integer_ring import ZZ
from sage.graphs.graph import Graph
@@ -308,7 +310,7 @@ def __init__(self, maximal_cells=None, backend=None, maximality_check=True,
ambient_dim = next(iter(cells_dict[self._dim])).ambient_dim()
self._ambient_dim = ambient_dim
self._maximal_cells = cells_dict
- if not all((is_Polyhedron(cell) and
+ if not all((isinstance(cell, sage.geometry.abc.Polyhedron) and
cell.ambient_dim() == self._ambient_dim)
for cell in self.maximal_cell_iterator()):
raise ValueError("the given cells are not polyhedra " +
@@ -959,7 +961,7 @@ def __contains__(self, x):
sage: (0, 0) in pc # not a polyhedron
False
"""
- if not is_Polyhedron(x):
+ if not isinstance(x, sage.geometry.abc.Polyhedron):
return False
dim = x.dimension()
return dim in self.cells() and x in self.cells()[dim]
@@ -2028,7 +2030,7 @@ def add_cell(self, cell):
"""
if self._is_immutable:
raise ValueError("this polyhedral complex is not mutable")
- if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim:
+ if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim:
raise ValueError("the given cell is not a polyhedron " +
"in the same ambient space")
# if cell is already in self, do nothing.
@@ -2191,7 +2193,7 @@ def remove_cell(self, cell, check=False):
"""
if self._is_immutable:
raise ValueError("this polyhedral complex is not mutable")
- if not is_Polyhedron(cell) or cell.ambient_dim() != self._ambient_dim:
+ if not isinstance(cell, sage.geometry.abc.Polyhedron) or cell.ambient_dim() != self._ambient_dim:
raise ValueError("the given cell is not a polyhedron " +
"in the same ambient space")
# if cell is not in self, delete nothing.
diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py
index e59f05d09a3..a333defe55c 100644
--- a/src/sage/geometry/polyhedron/backend_cdd.py
+++ b/src/sage/geometry/polyhedron/backend_cdd.py
@@ -22,9 +22,6 @@
from .base import Polyhedron_base
from .base_QQ import Polyhedron_QQ
-from sage.misc.lazy_import import lazy_import
-lazy_import('sage.geometry.polyhedron.backend_cdd_rdf', 'Polyhedron_RDF_cdd', deprecation=32592)
-
class Polyhedron_cdd(Polyhedron_base):
r"""
diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py
index f9ce84be09d..82e92b72bd3 100644
--- a/src/sage/geometry/polyhedron/base.py
+++ b/src/sage/geometry/polyhedron/base.py
@@ -74,10 +74,15 @@ def is_Polyhedron(X):
sage: p = polytopes.hypercube(2)
sage: from sage.geometry.polyhedron.base import is_Polyhedron
sage: is_Polyhedron(p)
+ doctest:warning...
+ DeprecationWarning: is_Polyhedron is deprecated, use isinstance instead
+ See https://github.com/sagemath/sage/issues/34307 for details.
True
sage: is_Polyhedron(123456)
False
"""
+ from sage.misc.superseded import deprecation
+ deprecation(34307, "is_Polyhedron is deprecated, use isinstance instead")
return isinstance(X, Polyhedron_base)
diff --git a/src/sage/geometry/polyhedron/base3.py b/src/sage/geometry/polyhedron/base3.py
index d64cf38d26d..3b57c4f2055 100644
--- a/src/sage/geometry/polyhedron/base3.py
+++ b/src/sage/geometry/polyhedron/base3.py
@@ -372,7 +372,7 @@ def _test_combinatorial_polyhedron(self, tester=None, **options):
prefix=tester._prefix+" ")
tester.info(tester._prefix + " ", newline=False)
- def face_generator(self, face_dimension=None, algorithm=None, **kwds):
+ def face_generator(self, face_dimension=None, algorithm=None):
r"""
Return an iterator over the faces of given dimension.
@@ -590,22 +590,6 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds):
sage: f.ambient_Hrepresentation()
(An equation (1, 1, 1) x - 6 == 0,)
- The ``dual`` keyword is deprecated::
-
- sage: P = polytopes.hypercube(4)
- sage: list(P.face_generator(dual=False))[:4]
- doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead
- See https://github.com/sagemath/sage/issues/33646 for details.
- [A 4-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 16 vertices,
- A -1-dimensional face of a Polyhedron in ZZ^4,
- A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices,
- A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices]
- sage: list(P.face_generator(True))[:4]
- [A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices,
- A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices,
- A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices,
- A 1-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 2 vertices]
-
Check that we catch incorrect algorithms::
sage: list(P.face_generator(2, algorithm='integrate'))[:4]
@@ -618,19 +602,9 @@ def face_generator(self, face_dimension=None, algorithm=None, **kwds):
dual = False
elif algorithm == 'dual':
dual = True
- elif algorithm in (False, True):
- from sage.misc.superseded import deprecation
- deprecation(33646, "the keyword dual is deprecated; use algorithm instead")
- dual = algorithm
elif algorithm is not None:
raise ValueError("algorithm must be 'primal', 'dual' or None")
- if kwds:
- from sage.misc.superseded import deprecation
- deprecation(33646, "the keyword dual is deprecated; use algorithm instead")
- if 'dual' in kwds and dual is None:
- dual = kwds['dual']
-
from sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator import FaceIterator_geom
return FaceIterator_geom(self, output_dimension=face_dimension, dual=dual)
diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd
index a04a1186876..dd5ee0bf472 100644
--- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd
+++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd
@@ -34,7 +34,6 @@ cdef class CombinatorialPolyhedron(SageObject):
cdef tuple Vrep(self)
cdef tuple facet_names(self)
cdef tuple equations(self)
- cdef tuple equalities(self)
cdef unsigned int n_Vrepresentation(self) noexcept
cdef unsigned int n_Hrepresentation(self) noexcept
cdef bint is_bounded(self) noexcept
diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx
index 367049b9fc0..d21b824da0c 100644
--- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx
+++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx
@@ -1359,7 +1359,7 @@ cdef class CombinatorialPolyhedron(SageObject):
adjacency_matrix.set_immutable()
return adjacency_matrix
- def ridges(self, add_equations=False, names=True, add_equalities=False, algorithm=None):
+ def ridges(self, add_equations=False, names=True, algorithm=None):
r"""
Return the ridges.
@@ -1453,21 +1453,7 @@ cdef class CombinatorialPolyhedron(SageObject):
sage: C = CombinatorialPolyhedron(polytopes.simplex())
sage: C.ridges(names=False, add_equations=True)
((2, 3), (1, 3), (0, 3), (1, 2), (0, 2), (0, 1))
-
- The keyword ``add_equalities`` is deprecated::
-
- sage: C = CombinatorialPolyhedron(polytopes.simplex())
- sage: r = C.ridges(add_equations=True)
- sage: r1 = C.ridges(add_equalities=True)
- doctest:...: DeprecationWarning: the keyword ``add_equalities`` is deprecated; use ``add_equations``
- See https://github.com/sagemath/sage/issues/31834 for details.
- sage: r == r1
- True
"""
- if add_equalities:
- from sage.misc.superseded import deprecation
- deprecation(31834, "the keyword ``add_equalities`` is deprecated; use ``add_equations``", 3)
- add_equations = True
self._compute_ridges(self._algorithm_to_dual(algorithm))
cdef size_t n_ridges = self._ridges.length
@@ -2673,7 +2659,7 @@ cdef class CombinatorialPolyhedron(SageObject):
"""
return self.face_generator().meet_of_Hrep(*indices)
- def face_generator(self, dimension=None, algorithm=None, **kwds):
+ def face_generator(self, dimension=None, algorithm=None):
r"""
Iterator over all proper faces of specified dimension.
@@ -2760,16 +2746,6 @@ cdef class CombinatorialPolyhedron(SageObject):
(A ray in the direction (1, 0), A vertex at (1, 0))
(A ray in the direction (0, 1), A vertex at (0, 1))
- TESTS:
-
- The kewword ``dual`` is deprecated::
-
- sage: C = CombinatorialPolyhedron([[0,1,2],[0,1,3],[0,2,3],[1,2,3]])
- sage: it = C.face_generator(1, False)
- doctest:...: DeprecationWarning: the keyword dual is deprecated; use algorithm instead
- See https://github.com/sagemath/sage/issues/33646 for details.
- sage: it = C.face_generator(1, dual=True)
-
.. SEEALSO::
:class:`~sage.geometry.polyhedron.combinatorial_polyhedron.face_iterator.FaceIterator`,
@@ -2777,18 +2753,7 @@ cdef class CombinatorialPolyhedron(SageObject):
"""
cdef int dual
- if algorithm in (False, True):
- from sage.misc.superseded import deprecation
- deprecation(33646, "the keyword dual is deprecated; use algorithm instead")
- dual = int(algorithm)
- else:
- dual = self._algorithm_to_dual(algorithm)
-
- if kwds:
- from sage.misc.superseded import deprecation
- deprecation(33646, "the keyword dual is deprecated; use algorithm instead")
- if 'dual' in kwds and dual == -1 and kwds['dual'] in (False, True):
- dual = int(kwds['dual'])
+ dual = self._algorithm_to_dual(algorithm)
if dual == -1:
# Determine the faster way, to iterate through all faces.
@@ -3273,11 +3238,6 @@ cdef class CombinatorialPolyhedron(SageObject):
"""
return self._equations
- cdef tuple equalities(self):
- from sage.misc.superseded import deprecation
- deprecation(31834, "the method equalities of CombinatorialPolyhedron is deprecated; use equations", 3)
- return self.equations()
-
cdef unsigned int n_Vrepresentation(self) noexcept:
r"""
Return the number of elements in the Vrepresentation.
diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py
index 7dafad76437..5fad17dc55a 100644
--- a/src/sage/geometry/polyhedron/parent.py
+++ b/src/sage/geometry/polyhedron/parent.py
@@ -9,6 +9,8 @@
# https://www.gnu.org/licenses/
# *****************************************************************************
+import sage.geometry.abc
+
from sage.structure.parent import Parent
from sage.structure.element import get_coercion_model
from sage.structure.unique_representation import UniqueRepresentation
@@ -22,8 +24,6 @@
from sage.categories.fields import Fields
from sage.categories.rings import Rings
from sage.categories.modules import Modules
-
-from sage.geometry.polyhedron.base import is_Polyhedron
from .representation import Inequality, Equation, Vertex, Ray, Line
@@ -691,7 +691,7 @@ def convert_base_ring_Hrep(lstlst):
if convert and Vrep:
Vrep = [convert_base_ring(_) for _ in Vrep]
return self.element_class(self, Vrep, Hrep, **kwds)
- if nargs == 1 and is_Polyhedron(args[0]):
+ if nargs == 1 and isinstance(args[0], sage.geometry.abc.Polyhedron):
copy = kwds.pop('copy', args[0].parent() is not self)
mutable = kwds.pop('mutable', False)
diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py
index 0d039d8db05..02998662ba0 100644
--- a/src/sage/graphs/generators/families.py
+++ b/src/sage/graphs/generators/families.py
@@ -496,7 +496,7 @@ def HammingGraph(n, q, X=None):
def BalancedTree(r, h):
r"""
- Returns the perfectly balanced tree of height `h \geq 1`,
+ Return the perfectly balanced tree of height `h \geq 1`,
whose root has degree `r \geq 2`.
The number of vertices of this graph is
@@ -513,23 +513,18 @@ def BalancedTree(r, h):
OUTPUT:
The perfectly balanced tree of height `h \geq 1` and whose root has
- degree `r \geq 2`. A :exc:`~networkx.exception.NetworkXError` is raised
- if `r < 2` or `h < 1`.
-
- ALGORITHM:
-
- Uses the :ref:`NetworkX ` function
- :func:`~networkx.generators.classic.balanced_tree`.
+ degree `r \geq 2`.
EXAMPLES:
A balanced tree whose root node has degree `r = 2`, and of height
`h = 1`, has order 3 and size 2::
- sage: G = graphs.BalancedTree(2, 1); G # needs networkx
+ sage: G = graphs.BalancedTree(2, 1); G
Balanced tree: Graph on 3 vertices
- sage: G.order(); G.size() # needs networkx
+ sage: G.order()
3
+ sage: G.size()
2
sage: r = 2; h = 1
sage: v = 1 + r
@@ -539,8 +534,9 @@ def BalancedTree(r, h):
Plot a balanced tree of height 5, whose root node has degree `r = 3`::
- sage: G = graphs.BalancedTree(3, 5) # needs networkx
- sage: G.show() # long time # needs networkx sage.plot
+ sage: G = graphs.BalancedTree(3, 5)
+ sage: G.plot() # long time # needs sage.plot
+ Graphics object consisting of 728 graphics primitives
A tree is bipartite. If its vertex set is finite, then it is planar. ::
@@ -563,17 +559,40 @@ def BalancedTree(r, h):
has degree `r \geq 2`, but the construction degenerates
gracefully::
- sage: graphs.BalancedTree(1, 10) # needs networkx
+ sage: graphs.BalancedTree(1, 10)
Balanced tree: Graph on 11 vertices
Similarly, we usually want the tree must have height `h \geq 1`
but the algorithm also degenerates gracefully here::
- sage: graphs.BalancedTree(3, 0) # needs networkx
+ sage: graphs.BalancedTree(3, 0)
Balanced tree: Graph on 1 vertex
+
+ The construction is the same as the one of networkx::
+
+ sage: # needs networkx
+ sage: import networkx
+ sage: r = randint(2, 4); h = randint(1, 5)
+ sage: T = graphs.BalancedTree(r, h)
+ sage: N = Graph(networkx.balanced_tree(r, h), name="Balanced tree")
+ sage: T.is_isomorphic(N)
+ True
"""
- import networkx
- return Graph(networkx.balanced_tree(r, h), name="Balanced tree")
+ # Compute the number of vertices per level of the tree
+ order = [r**l for l in range(h + 1)]
+ # Compute the first index of the vertices of a level
+ begin = [0]
+ begin.extend(begin[-1] + val for val in order)
+ # The number of vertices of the tree is the first index of level h + 1
+ T = Graph(begin[-1], name="Balanced tree")
+
+ # Add edges of the r-ary tree
+ for level in range(h):
+ start = begin[level + 1]
+ for u in range(begin[level], begin[level + 1]):
+ T.add_edges((u, v) for v in range(start, start + r))
+ start += r
+ return T
def BarbellGraph(n1, n2):
diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py
index 4d147efcd2f..eee4c61b69c 100644
--- a/src/sage/graphs/generic_graph.py
+++ b/src/sage/graphs/generic_graph.py
@@ -1900,6 +1900,72 @@ def to_dictionary(self, edge_labels=False, multiple_edges=False):
return d
+ def _vertex_indices_and_keys(self, vertices=None, *, sort=None):
+ r"""
+ Process a ``vertices`` parameter.
+
+ This is a helper function for :meth:`adjacency_matrix`,
+ :meth:`incidence_matrix`, :meth:`weighted_adjacency_matrix`,
+ and :meth:`kirchhoff_matrix`.
+
+ INPUT:
+
+ - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``)
+
+ - when a list, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering of ``vertices``,
+ - when ``None``, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering given by
+ :meth:`GenericGraph.vertices`
+ - when ``True``, construct an endomorphism of a free module instead of
+ a matrix, where the module's basis is indexed by the vertices.
+
+ - ``sort`` -- boolean or ``None`` (default); passed to :meth:`vertices`
+ when ``vertices`` is not a list.
+
+ OUTPUT: pair of:
+
+ - ``vertex_indices`` -- a dictionary mapping vertices to numerical indices,
+ - ``keys`` -- either a tuple of basis keys (when using a
+ :class:`CombinatorialFreeModule`) or ``None`` (when using a
+ :class:`FreeModule`, :func:`matrix`).
+
+ EXAMPLES::
+
+ sage: G = graphs.PathGraph(5)
+ sage: G.relabel(['o....', '.o...', '..o..', '...o.', '....o'])
+ sage: G._vertex_indices_and_keys(None)
+ ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4},
+ None)
+ sage: G._vertex_indices_and_keys(None, sort=False)
+ ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0},
+ None)
+ sage: G._vertex_indices_and_keys(['..o..', '.o...', '...o.', 'o....', '....o'])
+ ({'....o': 4, '...o.': 2, '..o..': 0, '.o...': 1, 'o....': 3},
+ None)
+ sage: G._vertex_indices_and_keys(True)
+ ({'....o': 4, '...o.': 3, '..o..': 2, '.o...': 1, 'o....': 0},
+ ('o....', '.o...', '..o..', '...o.', '....o'))
+ sage: G._vertex_indices_and_keys(True, sort=True)
+ ({'....o': 0, '...o.': 1, '..o..': 2, '.o...': 3, 'o....': 4},
+ ('....o', '...o.', '..o..', '.o...', 'o....'))
+ """
+ n = self.order()
+ keys = None
+ if vertices is True:
+ vertices = self.vertices(sort=sort if sort is not None else False)
+ keys = tuple(vertices) # tuple to make it hashable
+ elif vertices is None:
+ try:
+ vertices = self.vertices(sort=sort if sort is not None else True)
+ except TypeError:
+ raise TypeError("Vertex labels are not comparable. You must "
+ "specify an ordering using parameter 'vertices'")
+ elif (len(vertices) != n or
+ set(vertices) != set(self.vertex_iterator())):
+ raise ValueError("parameter 'vertices' must be a permutation of the vertices")
+ return {v: i for i, v in enumerate(vertices)}, keys
+
def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds):
r"""
Return the adjacency matrix of the (di)graph.
@@ -1911,10 +1977,16 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds
- ``sparse`` -- boolean (default: ``None``); whether to represent with a
sparse matrix
- - ``vertices`` -- list (default: ``None``); the ordering of
- the vertices defining how they should appear in the
- matrix. By default, the ordering given by
- :meth:`GenericGraph.vertices` with ``sort=True`` is used.
+ - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``);
+
+ - when a list, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering of ``vertices``,
+ - when ``None``, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering given by
+ :meth:`GenericGraph.vertices` with ``sort=True``.
+ - when ``True``, construct an endomorphism of a free module instead of
+ a matrix, where the module's basis is indexed by the vertices.
+
If the vertices are not comparable, the keyword ``vertices`` must be
used to specify an ordering, or a :class:`TypeError` exception will
be raised.
@@ -2025,27 +2097,45 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds
ValueError: matrix is immutable; please change a copy instead
(i.e., use copy(M) to change a copy of M).
+ Creating a module endomorphism::
+
+ sage: # needs sage.modules
+ sage: D12 = posets.DivisorLattice(12).hasse_diagram()
+ sage: phi = D12.adjacency_matrix(vertices=True); phi
+ Generic endomorphism of
+ Free module generated by {1, 2, 3, 4, 6, 12} over Integer Ring
+ sage: print(phi._unicode_art_matrix())
+ 1 2 3 4 6 12
+ 1⎛ 0 1 1 0 0 0⎞
+ 2⎜ 0 0 0 1 1 0⎟
+ 3⎜ 0 0 0 0 1 0⎟
+ 4⎜ 0 0 0 0 0 1⎟
+ 6⎜ 0 0 0 0 0 1⎟
+ 12⎝ 0 0 0 0 0 0⎠
+
TESTS::
- sage: graphs.CubeGraph(8).adjacency_matrix().parent() # needs sage.modules
+ sage: # needs sage.modules
+ sage: graphs.CubeGraph(8).adjacency_matrix().parent()
Full MatrixSpace of 256 by 256 dense matrices over Integer Ring
- sage: graphs.CubeGraph(9).adjacency_matrix().parent() # needs sage.modules
+ sage: graphs.CubeGraph(9).adjacency_matrix().parent()
Full MatrixSpace of 512 by 512 sparse matrices over Integer Ring
- sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # needs sage.modules
+ sage: Graph([(i, i+1) for i in range(500)] + [(0,1),],
....: multiedges=True).adjacency_matrix().parent()
Full MatrixSpace of 501 by 501 dense matrices over Integer Ring
- sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # needs sage.modules
+ sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0])
Traceback (most recent call last):
...
- ValueError: parameter vertices must be a permutation of the vertices
- sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # needs sage.modules
+ ValueError: parameter 'vertices' must be a permutation of the vertices
+ sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3])
Traceback (most recent call last):
...
- ValueError: parameter vertices must be a permutation of the vertices
+ ValueError: parameter 'vertices' must be a permutation of the vertices
+
sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix()
Traceback (most recent call last):
...
- TypeError: Vertex labels are not comparable. You must specify an ordering using parameter ``vertices``
+ TypeError: Vertex labels are not comparable. You must specify an ordering using parameter 'vertices'
sage: Graph ([[0, 42, 'John'], [(42, 'John')]]).adjacency_matrix(vertices=['John', 42, 0])
[0 1 0]
[1 0 0]
@@ -2056,25 +2146,17 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds
sparse = True
if self.has_multiple_edges() or n <= 256 or self.density() > 0.05:
sparse = False
+ vertex_indices, keys = self._vertex_indices_and_keys(vertices)
+ if keys is not None:
+ kwds = copy(kwds)
+ kwds['row_keys'] = kwds['column_keys'] = keys
- if vertices is None:
- try:
- vertices = self.vertices(sort=True)
- except TypeError:
- raise TypeError("Vertex labels are not comparable. You must "
- "specify an ordering using parameter "
- "``vertices``")
- elif (len(vertices) != n or
- set(vertices) != set(self.vertex_iterator())):
- raise ValueError("parameter vertices must be a permutation of the vertices")
-
- new_indices = {v: i for i, v in enumerate(vertices)}
D = {}
directed = self._directed
multiple_edges = self.allows_multiple_edges()
for u, v, l in self.edge_iterator():
- i = new_indices[u]
- j = new_indices[v]
+ i = vertex_indices[u]
+ j = vertex_indices[v]
if multiple_edges and (i, j) in D:
D[i, j] += 1
if not directed and i != j:
@@ -2126,15 +2208,23 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None
- ``sparse`` -- boolean (default: ``True``); whether to use a sparse or
a dense matrix
- - ``vertices`` -- list (default: ``None``); when specified, the `i`-th
- row of the matrix corresponds to the `i`-th vertex in the ordering of
- ``vertices``, otherwise, the `i`-th row of the matrix corresponds to
- the `i`-th vertex in the ordering given by method :meth:`vertices`.
+ - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``);
+
+ - when a list, the `i`-th row of the matrix corresponds to the `i`-th
+ vertex in the ordering of ``vertices``,
+ - when ``None``, the `i`-th row of the matrix corresponds to
+ the `i`-th vertex in the ordering given by method :meth:`vertices`,
+ - when ``True``, construct a morphism of free modules instead of a matrix,
+ where the codomain's basis is indexed by the vertices.
- - ``edges`` -- list (default: ``None``); when specified, the `i`-th
- column of the matrix corresponds to the `i`-th edge in the ordering of
- ``edges``, otherwise, the `i`-th column of the matrix corresponds to
- the `i`-th edge in the ordering given by method :meth:`edge_iterator`.
+ - ``edges`` -- list, ``None``, or ``True`` (default: ``None``);
+
+ - when a list, the `i`-th column of the matrix corresponds to the `i`-th
+ edge in the ordering of ``edges``,
+ - when ``None``, the `i`-th column of the matrix corresponds to
+ the `i`-th edge in the ordering given by method :meth:`edge_iterator`,
+ - when ``True``, construct a morphism of free modules instead of a matrix,
+ where the domain's basis is indexed by the edges.
- ``base_ring`` -- a ring (default: ``ZZ``); the base ring of the matrix
space to use.
@@ -2258,13 +2348,39 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None
ValueError: matrix is immutable; please change a copy instead
(i.e., use copy(M) to change a copy of M).
+ Creating a module morphism::
+
+ sage: # needs sage.modules
+ sage: D12 = posets.DivisorLattice(12).hasse_diagram()
+ sage: phi_VE = D12.incidence_matrix(vertices=True, edges=True); phi_VE
+ Generic morphism:
+ From: Free module generated by
+ {(1, 2), (1, 3), (2, 4), (2, 6), (3, 6), (4, 12), (6, 12)}
+ over Integer Ring
+ To: Free module generated by {1, 2, 3, 4, 6, 12} over Integer Ring
+ sage: print(phi_VE._unicode_art_matrix())
+ (1, 2) (1, 3) (2, 4) (2, 6) (3, 6) (4, 12) (6, 12)
+ 1⎛ -1 -1 0 0 0 0 0⎞
+ 2⎜ 1 0 -1 -1 0 0 0⎟
+ 3⎜ 0 1 0 0 -1 0 0⎟
+ 4⎜ 0 0 1 0 0 -1 0⎟
+ 6⎜ 0 0 0 1 1 0 -1⎟
+ 12⎝ 0 0 0 0 0 1 1⎠
+ sage: E = phi_VE.domain()
+ sage: P1 = E.monomial((2, 4)) + E.monomial((4, 12)); P1
+ B[(2, 4)] + B[(4, 12)]
+ sage: P2 = E.monomial((2, 6)) + E.monomial((6, 12)); P2
+ B[(2, 6)] + B[(6, 12)]
+ sage: phi_VE(P1 - P2)
+ 0
+
TESTS::
sage: P5 = graphs.PathGraph(5)
sage: P5.incidence_matrix(vertices=[1] * P5.order()) # needs sage.modules
Traceback (most recent call last):
...
- ValueError: parameter vertices must be a permutation of the vertices
+ ValueError: parameter 'vertices' must be a permutation of the vertices
sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # needs sage.modules
Traceback (most recent call last):
...
@@ -2279,27 +2395,27 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None
if oriented is None:
oriented = self.is_directed()
- if vertices is None:
- vertices = self.vertices(sort=False)
- elif (len(vertices) != self.num_verts() or
- set(vertices) != set(self.vertex_iterator())):
- raise ValueError("parameter vertices must be a permutation of the vertices")
+ vertex_indices, row_keys = self._vertex_indices_and_keys(vertices, sort=False)
- verts = {v: i for i, v in enumerate(vertices)}
- if edges is None:
- edges = self.edge_iterator(labels=False)
+ column_keys = None
+ use_edge_labels = kwds.pop('use_edge_labels', False)
+ if edges is True:
+ edges = self.edges(labels=use_edge_labels)
+ column_keys = tuple(edges) # because an EdgesView is not hashable
+ elif edges is None:
+ edges = self.edge_iterator(labels=use_edge_labels)
elif len(edges) != self.size():
raise ValueError("parameter edges must be a permutation of the edges")
else:
# We check that we have the same set of unlabeled edges
if oriented:
- i_edges = [(verts[e[0]], verts[e[1]]) for e in edges]
- s_edges = [(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)]
+ i_edges = [(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges]
+ s_edges = [(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)]
else:
def reorder(u, v):
return (u, v) if u <= v else (v, u)
- i_edges = [reorder(verts[e[0]], verts[e[1]]) for e in edges]
- s_edges = [reorder(verts[u], verts[v]) for u, v in self.edge_iterator(labels=False)]
+ i_edges = [reorder(vertex_indices[e[0]], vertex_indices[e[1]]) for e in edges]
+ s_edges = [reorder(vertex_indices[u], vertex_indices[v]) for u, v in self.edge_iterator(labels=False)]
if sorted(i_edges) != sorted(s_edges):
raise ValueError("parameter edges must be a permutation of the edges")
@@ -2312,15 +2428,20 @@ def reorder(u, v):
if oriented:
for i, e in enumerate(edges):
if e[0] != e[1]:
- m[verts[e[0]], i] = -1
- m[verts[e[1]], i] = +1
+ m[vertex_indices[e[0]], i] = -1
+ m[vertex_indices[e[1]], i] = +1
else:
for i, e in enumerate(edges):
- m[verts[e[0]], i] += 1
- m[verts[e[1]], i] += 1
+ m[vertex_indices[e[0]], i] += 1
+ m[vertex_indices[e[1]], i] += 1
+
+ if row_keys is not None or column_keys is not None:
+ m.set_immutable()
+ return matrix(m, row_keys=row_keys, column_keys=column_keys)
if immutable:
m.set_immutable()
+
return m
def distance_matrix(self, vertices=None, *, base_ring=None, **kwds):
@@ -2476,10 +2597,19 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None,
- ``sparse`` -- boolean (default: ``True``); whether to use a sparse or
a dense matrix
- - ``vertices`` -- list (default: ``None``); when specified, each vertex
- is represented by its position in the list ``vertices``, otherwise
- each vertex is represented by its position in the list returned by
- method :meth:`vertices`
+ - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``);
+
+ - when a list, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering of ``vertices``,
+ - when ``None``, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering given by
+ :meth:`GenericGraph.vertices` with ``sort=True``.
+ - when ``True``, construct an endomorphism of a free module instead of
+ a matrix, where the module's basis is indexed by the vertices.
+
+ If the vertices are not comparable, the keyword ``vertices`` must be
+ used to specify an ordering, or a :class:`TypeError` exception will
+ be raised.
- ``default_weight`` -- (default: ``None``); specifies the weight to
replace any ``None`` edge label. When not specified an error is raised
@@ -2531,6 +2661,21 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None,
ValueError: matrix is immutable; please change a copy instead
(i.e., use copy(M) to change a copy of M).
+ Creating a module morphism::
+
+ sage: # needs sage.modules
+ sage: G = Graph(sparse=True, weighted=True)
+ sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)])
+ sage: phi = G.weighted_adjacency_matrix(vertices=True); phi
+ Generic endomorphism of
+ Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring
+ sage: print(phi._unicode_art_matrix())
+ A B C D
+ A⎛0 1 3 4⎞
+ B⎜1 0 2 0⎟
+ C⎜3 2 0 0⎟
+ D⎝4 0 0 0⎠
+
TESTS:
The following doctest verifies that :issue:`4888` is fixed::
@@ -2561,11 +2706,10 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None,
if self.has_multiple_edges():
raise NotImplementedError("don't know how to represent weights for a multigraph")
- if vertices is None:
- vertices = self.vertices(sort=True)
- elif (len(vertices) != self.num_verts() or
- set(vertices) != set(self.vertex_iterator())):
- raise ValueError("parameter vertices must be a permutation of the vertices")
+ vertex_indices, row_column_keys = self._vertex_indices_and_keys(vertices)
+ if row_column_keys is not None:
+ kwds = copy(kwds)
+ kwds['row_keys'] = kwds['column_keys'] = row_column_keys
# Method for checking edge weights and setting default weight
if default_weight is None:
@@ -2580,18 +2724,16 @@ def func(u, v, label):
return default_weight
return label
- new_indices = {v: i for i,v in enumerate(vertices)}
-
D = {}
if self._directed:
for u, v, label in self.edge_iterator():
- i = new_indices[u]
- j = new_indices[v]
+ i = vertex_indices[u]
+ j = vertex_indices[v]
D[i, j] = func(u, v, label)
else:
for u, v, label in self.edge_iterator():
- i = new_indices[u]
- j = new_indices[v]
+ i = vertex_indices[u]
+ j = vertex_indices[v]
label = func(u, v, label)
D[i, j] = label
D[j, i] = label
@@ -2659,6 +2801,20 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl
- Else, `D-M` is used in calculation of Kirchhoff matrix
+ - ``vertices`` -- list, ``None``, or ``True`` (default: ``None``);
+
+ - when a list, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering of ``vertices``,
+ - when ``None``, the `i`-th row and column of the matrix correspond to
+ the `i`-th vertex in the ordering given by
+ :meth:`GenericGraph.vertices` with ``sort=True``.
+ - when ``True``, construct an endomorphism of a free module instead of
+ a matrix, where the module's basis is indexed by the vertices.
+
+ If the vertices are not comparable, the keyword ``vertices`` must be
+ used to specify an ordering, or a :class:`TypeError` exception will
+ be raised.
+
Note that any additional keywords will be passed on to either the
:meth:`~GenericGraph.adjacency_matrix` or
:meth:`~GenericGraph.weighted_adjacency_matrix` method.
@@ -2740,18 +2896,36 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl
sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # needs sage.modules
sage: M.is_immutable() # needs sage.modules
True
+
+ Creating a module morphism::
+
+ sage: # needs sage.modules
+ sage: G = Graph(sparse=True, weighted=True)
+ sage: G.add_edges([('A', 'B', 1), ('B', 'C', 2), ('A', 'C', 3), ('A', 'D', 4)])
+ sage: phi = G.laplacian_matrix(weighted=True, vertices=True); phi
+ Generic endomorphism of
+ Free module generated by {'A', 'B', 'C', 'D'} over Integer Ring
+ sage: print(phi._unicode_art_matrix())
+ A B C D
+ A⎛ 8 -1 -3 -4⎞
+ B⎜-1 3 -2 0⎟
+ C⎜-3 -2 5 0⎟
+ D⎝-4 0 0 4⎠
+
"""
- from sage.matrix.constructor import diagonal_matrix
+ from sage.matrix.constructor import diagonal_matrix, matrix
set_immutable = kwds.pop('immutable', False)
+ vertex_indices, keys = self._vertex_indices_and_keys(kwds.pop('vertices', None))
+
if weighted is None:
weighted = self._weighted
if weighted:
- M = self.weighted_adjacency_matrix(immutable=True, **kwds)
+ M = self.weighted_adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds)
else:
- M = self.adjacency_matrix(immutable=True, **kwds)
+ M = self.adjacency_matrix(vertices=list(vertex_indices), immutable=True, **kwds)
D = M.parent(0)
@@ -2791,6 +2965,8 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl
else:
ret = D - M
+ if keys is not None:
+ return matrix(ret, row_keys=keys, column_keys=keys)
if set_immutable:
ret.set_immutable()
return ret
@@ -15671,7 +15847,7 @@ def cluster_triangles(self, nbunch=None, implementation=None):
sage: F = graphs.FruchtGraph()
sage: list(F.cluster_triangles().values())
- [1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0]
+ [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0]
sage: F.cluster_triangles()
{0: 1, 1: 1, 2: 0, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 0, 9: 1, 10: 1, 11: 0}
sage: F.cluster_triangles(nbunch=[0, 1, 2])
diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py
index d4f2e18a555..d8e88b023f0 100644
--- a/src/sage/graphs/graph.py
+++ b/src/sage/graphs/graph.py
@@ -6917,27 +6917,29 @@ def cliques_number_of(self, vertices=None, cliques=None):
EXAMPLES::
sage: C = Graph('DJ{')
- sage: C.cliques_number_of() # needs networkx
+ sage: C.cliques_number_of()
{0: 1, 1: 1, 2: 1, 3: 1, 4: 2}
sage: E = C.cliques_maximal()
sage: E
[[0, 4], [1, 2, 3, 4]]
- sage: C.cliques_number_of(cliques=E) # needs networkx
+ sage: C.cliques_number_of(cliques=E)
{0: 1, 1: 1, 2: 1, 3: 1, 4: 2}
sage: F = graphs.Grid2dGraph(2,3)
- sage: F.cliques_number_of() # needs networkx
+ sage: F.cliques_number_of()
{(0, 0): 2, (0, 1): 3, (0, 2): 2, (1, 0): 2, (1, 1): 3, (1, 2): 2}
- sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)]) # needs networkx
+ sage: F.cliques_number_of(vertices=[(0, 1), (1, 2)])
{(0, 1): 3, (1, 2): 2}
sage: F.cliques_number_of(vertices=(0, 1))
3
sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]})
sage: G.show(figsize=[2,2]) # needs sage.plot
- sage: G.cliques_number_of() # needs networkx
+ sage: G.cliques_number_of()
{0: 2, 1: 2, 2: 1, 3: 1}
"""
if cliques is None:
- cliques = self.cliques_maximal()
+ # We use IndependentSets to avoid the construction of the list of
+ # cliques as currently done by method cliques_maximal.
+ cliques = IndependentSets(self, maximal=True, complement=True)
if vertices in self: # single vertex
return sum(1 for c in cliques if vertices in c)
@@ -6952,7 +6954,7 @@ def cliques_number_of(self, vertices=None, cliques=None):
@doc_index("Clique-related methods")
def cliques_get_max_clique_graph(self):
- """
+ r"""
Return the clique graph.
Vertices of the result are the maximal cliques of the graph, and edges
@@ -6968,26 +6970,52 @@ def cliques_get_max_clique_graph(self):
EXAMPLES::
- sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG # needs networkx
+ sage: MCG = graphs.ChvatalGraph().cliques_get_max_clique_graph(); MCG
Graph on 24 vertices
- sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot
+ sage: MCG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot
sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]})
sage: G.show(figsize=[2,2]) # needs sage.plot
- sage: G.cliques_get_max_clique_graph() # needs networkx
+ sage: G.cliques_get_max_clique_graph()
Graph on 2 vertices
- sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs networkx sage.plot
+ sage: G.cliques_get_max_clique_graph().show(figsize=[2,2]) # needs sage.plot
+
+ TESTS::
+
+ sage: # needs networkx
+ sage: import networkx
+ sage: CG = graphs.ChvatalGraph()
+ sage: S = CG.cliques_get_max_clique_graph()
+ sage: N = Graph(networkx.make_max_clique_graph(CG.networkx_graph(),
+ ....: create_using=networkx.MultiGraph()),
+ ....: multiedges=False)
+ sage: S.is_isomorphic(N)
+ True
"""
- import networkx
- return Graph(networkx.make_max_clique_graph(self.networkx_graph(), create_using=networkx.MultiGraph()),
- multiedges=False)
+ # Associate each maximal clique an integer index and record for each
+ # vertex of self the cliques it belongs to.
+ # We use IndependentSets to avoid the construction of the list of
+ # cliques as currently done by method cliques_maximal.
+ cliques_of_vertex = {u: [] for u in self}
+ for n, clique in enumerate(IndependentSets(self, maximal=True, complement=True)):
+ for u in clique:
+ cliques_of_vertex[u].append(n)
+
+ # Build a graph with one vertex per maximal clique and an edge between
+ # cliques sharing a vertex of self
+ G = Graph(n, multiedges=False)
+ for block in cliques_of_vertex.values():
+ G.add_clique(block)
+ return G
@doc_index("Clique-related methods")
def cliques_get_clique_bipartite(self, **kwds):
- """
- Return a bipartite graph constructed such that maximal cliques are the
- right vertices and the left vertices are retained from the given
- graph. Right and left vertices are connected if the bottom vertex
- belongs to the clique represented by a top vertex.
+ r"""
+ Return the vertex-clique bipartite graph of ``self``.
+
+ In the returned bipartite graph, the ``left`` vertices are the vertices
+ of ``self`` and the ``right`` vertices represent the maximal cliques of
+ ``self``. There is an edge from vertex `v` to clique `C` in the
+ bipartite graph if and only if `v` belongs to `C`.
.. NOTE::
@@ -6996,18 +7024,32 @@ def cliques_get_clique_bipartite(self, **kwds):
EXAMPLES::
- sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG # needs networkx
+ sage: CBG = graphs.ChvatalGraph().cliques_get_clique_bipartite(); CBG
Bipartite graph on 36 vertices
- sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs networkx sage.plot
+ sage: CBG.show(figsize=[2,2], vertex_size=20, vertex_labels=False) # needs sage.plot
sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]})
sage: G.show(figsize=[2,2]) # needs sage.plot
- sage: G.cliques_get_clique_bipartite() # needs networkx
+ sage: G.cliques_get_clique_bipartite()
Bipartite graph on 6 vertices
- sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs networkx sage.plot
+ sage: G.cliques_get_clique_bipartite().show(figsize=[2,2]) # needs sage.plot
+
+ TESTS::
+
+ sage: # needs networkx
+ sage: import networkx
+ sage: CG = graphs.ChvatalGraph()
+ sage: S = CG.cliques_get_clique_bipartite()
+ sage: N = BipartiteGraph(networkx.make_clique_bipartite(CG.networkx_graph()))
+ sage: S.is_isomorphic(N)
+ True
"""
- from .bipartite_graph import BipartiteGraph
- import networkx
- return BipartiteGraph(networkx.make_clique_bipartite(self.networkx_graph(), **kwds))
+ G = Graph([self, []], format='vertices_and_edges')
+ for i, clique in enumerate(IndependentSets(self, maximal=True, complement=True)):
+ idx = - i - 1
+ G.add_vertex(idx)
+ G.add_edges((u, idx) for u in clique)
+ from sage.graphs.bipartite_graph import BipartiteGraph
+ return BipartiteGraph(G, check=False)
@doc_index("Algorithmically hard stuff")
def independent_set(self, algorithm="Cliquer", value_only=False, reduction_rules=True,
diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py
index 6b1e825ba2e..03e9231bd65 100644
--- a/src/sage/graphs/graph_decompositions/modular_decomposition.py
+++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py
@@ -371,17 +371,17 @@ def print_md_tree(root):
sage: from sage.graphs.graph_decompositions.modular_decomposition import *
sage: print_md_tree(modular_decomposition(graphs.IcosahedralGraph()))
PRIME
+ 3
+ 4
+ 7
+ 9
+ 11
1
5
- 7
8
- 11
0
2
6
- 3
- 9
- 4
10
"""
@@ -494,17 +494,17 @@ def habib_maurer_algorithm(graph, g_classes=None):
sage: from sage.graphs.graph_decompositions.modular_decomposition import *
sage: print_md_tree(habib_maurer_algorithm(graphs.IcosahedralGraph()))
PRIME
+ 3
+ 4
+ 7
+ 9
+ 11
1
5
- 7
8
- 11
0
2
6
- 3
- 9
- 4
10
The Octahedral graph is not Prime::
diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py
index 52941853359..ce1f8916423 100644
--- a/src/sage/graphs/graph_input.py
+++ b/src/sage/graphs/graph_input.py
@@ -462,6 +462,14 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv
sage: g.is_isomorphic(graphs.PetersenGraph())
True
+ The resulting order of vertices is unspecified but deterministic::
+
+ sage: from sage.graphs.graph_input import from_dict_of_dicts
+ sage: g = Graph()
+ sage: from_dict_of_dicts(g, {i: {} for i in range(99, 90, -1)})
+ sage: g.vertices(sort=False)
+ [99, 98, 97, 96, 95, 94, 93, 92, 91]
+
TESTS:
:issue:`32831` is fixed::
@@ -493,8 +501,11 @@ def from_dict_of_dicts(G, M, loops=False, multiedges=False, weighted=False, conv
G.allow_loops(loops, check=False)
G.allow_multiple_edges(multiedges, check=False)
- verts = set().union(M.keys(), *M.values())
- G.add_vertices(verts)
+ # Use keys of a dictionary instead of a set, to preserve insertion order
+ verts = dict(M)
+ for d in M.values():
+ verts.update(d)
+ G.add_vertices(verts.keys())
if convert_empty_dict_labels_to_None:
def relabel(x):
return x if x != {} else None
@@ -504,7 +515,7 @@ def relabel(x):
is_directed = G.is_directed()
if not is_directed and multiedges:
- v_to_id = {v: i for i, v in enumerate(verts)}
+ v_to_id = {v: i for i, v in enumerate(verts.keys())}
for u in M:
for v in M[u]:
if v_to_id[u] <= v_to_id[v] or v not in M or u not in M[v] or u == v:
@@ -543,8 +554,18 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False):
sage: from_dict_of_lists(g, graphs.PetersenGraph().to_dictionary())
sage: g.is_isomorphic(graphs.PetersenGraph())
True
+
+ The resulting order of vertices is unspecified but deterministic::
+
+ sage: from sage.graphs.graph_input import from_dict_of_lists
+ sage: g = Graph()
+ sage: from_dict_of_lists(g, {i: [] for i in range(99, 90, -1)})
+ sage: g.vertices(sort=False)
+ [99, 98, 97, 96, 95, 94, 93, 92, 91]
"""
- verts = set().union(D.keys(), *D.values())
+ # Use keys of a dictionary instead of a set, to preserve insertion order
+ verts = dict(D)
+ verts.update({v: None for l in D.values() for v in l})
if not loops:
if any(u in neighb for u, neighb in D.items()):
if loops is False:
@@ -567,11 +588,11 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False):
multiedges = False
G.allow_loops(loops, check=False)
G.allow_multiple_edges(multiedges, check=False)
- G.add_vertices(verts)
+ G.add_vertices(verts.keys())
is_directed = G.is_directed()
if not is_directed and multiedges:
- v_to_id = {v: i for i, v in enumerate(verts)}
+ v_to_id = {v: i for i, v in enumerate(verts.keys())}
for u in D:
for v in D[u]:
if (v_to_id[u] <= v_to_id[v] or
diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py
index 8a0c5167b16..07ca7c7444e 100644
--- a/src/sage/groups/abelian_gps/abelian_group.py
+++ b/src/sage/groups/abelian_gps/abelian_group.py
@@ -458,10 +458,16 @@ def is_AbelianGroup(x):
sage: F = AbelianGroup(5,[5,5,7,8,9], names=list("abcde")); F
Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9
sage: is_AbelianGroup(F)
+ doctest:warning...
+ DeprecationWarning: the function is_AbelianGroup is deprecated;
+ use 'isinstance(..., AbelianGroup_class)' instead
+ See https://github.com/sagemath/sage/issues/37898 for details.
True
sage: is_AbelianGroup(AbelianGroup(7, [3]*7))
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37898, "the function is_AbelianGroup is deprecated; use 'isinstance(..., AbelianGroup_class)' instead")
return isinstance(x, AbelianGroup_class)
@@ -569,7 +575,7 @@ def is_isomorphic(left, right):
sage: G1.is_isomorphic(G2)
True
"""
- if not is_AbelianGroup(right):
+ if not isinstance(right, AbelianGroup_class):
return False
return left.elementary_divisors() == right.elementary_divisors()
diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py
index 86966cf74d6..24c910d96d9 100644
--- a/src/sage/groups/abelian_gps/dual_abelian_group.py
+++ b/src/sage/groups/abelian_gps/dual_abelian_group.py
@@ -86,6 +86,10 @@ def is_DualAbelianGroup(x):
sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde"))
sage: Fd = F.dual_group()
sage: is_DualAbelianGroup(Fd)
+ doctest:warning...
+ DeprecationWarning: the function is_DualAbelianGroup is deprecated;
+ use 'isinstance(..., DualAbelianGroup_class)' instead
+ See https://github.com/sagemath/sage/issues/37898 for details.
True
sage: F = AbelianGroup(3,[1,2,3], names='a')
sage: Fd = F.dual_group()
@@ -94,6 +98,8 @@ def is_DualAbelianGroup(x):
sage: F.gens()
(1, a1, a2)
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37898, "the function is_DualAbelianGroup is deprecated; use 'isinstance(..., DualAbelianGroup_class)' instead")
return isinstance(x, DualAbelianGroup_class)
diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx
index 0cf0e8bfb31..ddba766e94d 100644
--- a/src/sage/groups/libgap_wrapper.pyx
+++ b/src/sage/groups/libgap_wrapper.pyx
@@ -602,14 +602,11 @@ cdef class ElementLibGAP(MultiplicativeGroupElement):
sage: from sage.groups.libgap_group import GroupLibGAP
sage: G = GroupLibGAP(libgap.FreeGroup('a', 'b'))
sage: g = G.gen(0) * G.gen(1)
- sage: g._latex_()
- "ab%\n"
- """
- try:
- return self.gap().LaTeX()
- except ValueError:
- from sage.misc.latex import latex
- return latex(self._repr_())
+ sage: latex(g)
+ \text{\texttt{a*b}}
+ """
+ from sage.misc.latex import latex
+ return latex(self._repr_())
cpdef _mul_(left, right):
"""
diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py
index 709a88a6d8e..ef05b55c190 100644
--- a/src/sage/groups/matrix_gps/matrix_group.py
+++ b/src/sage/groups/matrix_gps/matrix_group.py
@@ -78,6 +78,10 @@ def is_MatrixGroup(x):
sage: from sage.groups.matrix_gps.matrix_group import is_MatrixGroup
sage: is_MatrixGroup(MatrixSpace(QQ, 3))
+ doctest:warning...
+ DeprecationWarning: the function is_MatrixGroup is deprecated;
+ use 'isinstance(..., MatrixGroup_base)' instead
+ See https://github.com/sagemath/sage/issues/37898 for details.
False
sage: is_MatrixGroup(Mat(QQ, 3))
False
@@ -86,6 +90,8 @@ def is_MatrixGroup(x):
sage: is_MatrixGroup(MatrixGroup([matrix(2, [1,1,0,1])]))
True
"""
+ from sage.misc.superseded import deprecation
+ deprecation(37898, "the function is_MatrixGroup is deprecated; use 'isinstance(..., MatrixGroup_base)' instead")
return isinstance(x, MatrixGroup_base)
###################################################################
@@ -499,7 +505,7 @@ def __richcmp__(self, other, op):
sage: G != H
False
"""
- if not is_MatrixGroup(other):
+ if not isinstance(other, MatrixGroup_base):
return NotImplemented
if self is other:
diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd
index 408fe26a15a..a71fa3cadd8 100644
--- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd
+++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd
@@ -114,7 +114,7 @@ cdef inline int OP_find(OrbitPartition *OP, int n) noexcept:
OP.parent[n] = OP_find(OP, OP.parent[n])
return OP.parent[n]
-cdef inline int OP_join(OrbitPartition *OP, int m, int n) noexcept:
+cdef inline void OP_join(OrbitPartition *OP, int m, int n) noexcept:
"""
Join the cells containing m and n, if they are different.
"""
diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py
index e64bbf5b40e..7f7cfb5e27b 100644
--- a/src/sage/homology/chain_complex.py
+++ b/src/sage/homology/chain_complex.py
@@ -1141,58 +1141,6 @@ def __ne__(self, other):
"""
return not self == other
- def _homology_chomp(self, deg, base_ring, verbose, generators):
- """
- Helper function for :meth:`homology`.
-
- This function is deprecated.
-
- INPUT:
-
- - ``deg`` -- integer (one specific homology group) or ``None``
- (all of those that can be non-zero)
-
- - ``base_ring`` -- the base ring (must be the integers
- or a prime field)
-
- - ``verbose`` -- boolean, whether to print some messages
-
- - ``generators`` -- boolean, whether to also return generators
- for homology
-
- EXAMPLES::
-
- sage: C = ChainComplex({0: matrix(ZZ, 2, 3, [3, 0, 0, 0, 0, 0])}, base_ring=GF(2))
- sage: C._homology_chomp(None, GF(2), False, False) # optional - chomp, needs sage.rings.finite_rings
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- {0: Vector space of dimension 2 over Finite Field of size 2, 1: Vector space of dimension 1 over Finite Field of size 2}
-
- sage: D = ChainComplex({0: matrix(ZZ,1,0,[]), 1: matrix(ZZ,1,1,[0]),
- ....: 2: matrix(ZZ,0,1,[])})
- sage: D._homology_chomp(None, GF(2), False, False) # optional - chomp, needs sage.rings.finite_rings
- {1: Vector space of dimension 1 over Finite Field of size 2,
- 2: Vector space of dimension 1 over Finite Field of size 2}
- """
- deprecation(33777, "the CHomP interface is deprecated; hence so is this function")
- from sage.interfaces.chomp import homchain
- H = homchain(self, base_ring=base_ring, verbose=verbose,
- generators=generators)
- if H is None:
- raise RuntimeError('ran CHomP, but no output')
- if deg is None:
- # all the homology groups that could be non-zero
- # one has to complete the answer of chomp
- result = H
- for idx in self.nonzero_degrees():
- if idx not in H:
- result[idx] = HomologyGroup(0, base_ring)
- return result
- if deg in H:
- return H[deg]
- else:
- return HomologyGroup(0, base_ring)
-
def homology(self, deg=None, base_ring=None, generators=False,
verbose=False, algorithm='pari'):
r"""
@@ -1223,7 +1171,6 @@ def homology(self, deg=None, base_ring=None, generators=False,
* ``'auto'``
* ``'dhsw'``
* ``'pari'``
- * ``'chomp'`` (this option is deprecated)
See below for descriptions.
@@ -1254,12 +1201,6 @@ def homology(self, deg=None, base_ring=None, generators=False,
forces the named algorithm to be used regardless of the size
of the matrices.
- Finally, if ``algorithm`` is set to ``'chomp'``, then use
- CHomP. CHomP is available at the web page
- http://chomp.rutgers.edu/, although the software has not been
- tested recently in Sage. The use of this option is deprecated;
- see :issue:`33777`.
-
As of this writing, ``'pari'`` is the fastest standard option.
.. WARNING::
@@ -1319,10 +1260,8 @@ def homology(self, deg=None, base_ring=None, generators=False,
if not (base_ring.is_field() or base_ring is ZZ):
raise NotImplementedError('can only compute homology if the base ring is the integers or a field')
- if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp', 'chomp']:
+ if algorithm not in ['dhsw', 'pari', 'auto', 'no_chomp']:
raise NotImplementedError('algorithm not recognized')
- if algorithm == 'chomp':
- return self._homology_chomp(deg, base_ring, verbose, generators)
if deg is None:
deg = self.nonzero_degrees()
@@ -1686,76 +1625,6 @@ def shift(self, n=1):
return ChainComplex({k-shift: sgn * self._diff[k] for k in self._diff},
degree_of_differential=deg)
- def _chomp_repr_(self):
- r"""
- String representation of ``self`` suitable for use by the CHomP
- program.
-
- This function is deprecated.
-
- Since CHomP can only handle chain complexes, not cochain
- complexes, and since it likes its complexes to start in degree
- 0, flip the complex over if necessary, and shift it to start
- in degree 0. Note also that CHomP only works over the
- integers or a finite prime field.
-
- EXAMPLES::
-
- sage: C = ChainComplex({-2: matrix(ZZ, 1, 3, [3, 0, 0])}, degree=-1)
- sage: C._chomp_repr_()
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- 'chain complex\n\nmax dimension = 1\n\ndimension 0\n boundary a1 = 0\n\ndimension 1\n boundary a1 = + 3 * a1 \n boundary a2 = 0\n boundary a3 = 0\n\n'
- sage: C = ChainComplex({-2: matrix(ZZ, 1, 3, [3, 0, 0])}, degree=1)
- sage: C._chomp_repr_()
- 'chain complex\n\nmax dimension = 1\n\ndimension 0\n boundary a1 = 0\n\ndimension 1\n boundary a1 = + 3 * a1 \n boundary a2 = 0\n boundary a3 = 0\n\n'
- """
- deprecation(33777, "the CHomP interface is deprecated; hence so is this function")
- deg = self.degree_of_differential()
- if (self.grading_group() != ZZ or
- (deg != 1 and deg != -1)):
- raise ValueError('CHomP only works on Z-graded chain complexes with '
- 'differential of degree 1 or -1')
- base_ring = self.base_ring()
- if (base_ring == QQ) or (base_ring != ZZ and not (base_ring.is_prime_field())):
- raise ValueError('CHomP doesn\'t compute over the rationals, only over Z or F_p')
- if deg == -1:
- diffs = self.differential()
- else:
- diffs = self._flip_().differential()
-
- if len(diffs) == 0:
- diffs = {0: matrix(ZZ, 0, 0)}
-
- maxdim = max(diffs)
- mindim = min(diffs)
- # will shift chain complex by subtracting mindim from
- # dimensions, so its bottom dimension is zero.
- s = "chain complex\n\nmax dimension = %s\n\n" % (maxdim - mindim - 1,)
-
- for i in range(0, maxdim - mindim):
- s += "dimension %s\n" % i
- mat = diffs.get(i + mindim, matrix(base_ring, 0, 0))
- for idx in range(mat.ncols()):
- s += " boundary a%s = " % (idx + 1)
- # construct list of bdries
- col = mat.column(idx)
- nonzero_pos = col.nonzero_positions()
- if nonzero_pos:
- for j in nonzero_pos:
- entry = col[j]
- if entry > 0:
- sgn = "+"
- else:
- sgn = "-"
- entry = -entry
- s += "%s %s * a%s " % (sgn, entry, j+1)
- else:
- s += "0"
- s += "\n"
- s += "\n"
- return s
-
def _repr_(self):
"""
Print representation.
diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py
index 1fc391376a8..206617843a9 100644
--- a/src/sage/homology/tests.py
+++ b/src/sage/homology/tests.py
@@ -1,33 +1,10 @@
# sage.doctest: needs sage.modules
"""
Tests for chain complexes, simplicial complexes, etc.
-
-These test whether CHomP gives the same answers as Sage's built-in
-homology calculator.
-
-Since the CHomP interface has been deprecated --- see :issue:`33777`
---- so are many of the functions in is this module.
-
-TESTS::
-
- sage: from sage.homology.tests import test_random_chain_complex
- sage: test_random_chain_complex(trials=20) # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- sage: test_random_chain_complex(level=2, trials=20) # optional - CHomP
- sage: test_random_chain_complex(level=3, trials=20) # long time # optional - CHomP
-
- sage: from sage.homology.tests import test_random_simplicial_complex
- sage: test_random_simplicial_complex(level=1, trials=20) # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- sage: test_random_simplicial_complex(level=2, trials=20) # optional - CHomP
- sage: test_random_simplicial_complex(level=5/2, trials=10) # long time # optional - CHomP
"""
from sage.misc.random_testing import random_testing
from sage.misc.prandom import randint
-from sage.misc.superseded import deprecation
from sage.matrix.constructor import random_matrix
from sage.homology.chain_complex import ChainComplex
from sage.rings.integer_ring import ZZ
@@ -66,43 +43,6 @@ def random_chain_complex(level=1):
return ChainComplex({dim: mat}, degree=deg)
-@random_testing
-def test_random_chain_complex(level=1, trials=1, verbose=False):
- """
- Compute the homology of a random chain complex with and without
- CHomP, and compare the results. If they are not the same, raise
- an error.
-
- This function is deprecated: see :issue:`33777`.
-
- :param level: measure of complexity of the chain complex -- see
- :func:`random_chain_complex`
- :type level: positive integer; optional, default 1
- :param trials: number of trials to conduct
- :type trials: positive integer; optional, default 1
- :param verbose: if ``True``, print verbose messages
- :type verbose: boolean; optional, default ``False``
-
- EXAMPLES::
-
- sage: from sage.homology.tests import test_random_chain_complex
- sage: test_random_chain_complex(trials=2) # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- """
- deprecation(33777, 'the CHomP interface is deprecated; hence so is this function')
- for i in range(trials):
- C = random_chain_complex(level=level)
- for d in C.differential():
- chomp = C.homology(d, verbose=verbose)
- no_chomp = C.homology(d, algorithm='no_chomp', verbose=verbose)
- if chomp != no_chomp:
- print("Homology in dimension %s according to CHomP: %s" % (d, chomp))
- print("Homology in dimension %s according to Sage: %s" % (d, no_chomp))
- print("Chain complex: %s" % C.differential())
- raise ValueError
-
-
def random_simplicial_complex(level=1, p=0.5):
"""
Return a random simplicial complex.
@@ -118,7 +58,7 @@ def random_simplicial_complex(level=1, p=0.5):
sage: from sage.homology.tests import random_simplicial_complex
sage: X = random_simplicial_complex()
- sage: X # random
+ sage: X # random
Simplicial complex with vertex set (0, 1, 2, 3, 4, 5, 6, 7) and 31 facets
sage: X.dimension() < 11
True
@@ -126,40 +66,3 @@ def random_simplicial_complex(level=1, p=0.5):
n = randint(2, 4 * level)
dim = randint(1, n)
return RandomComplex(n, dim, p)
-
-
-@random_testing
-def test_random_simplicial_complex(level=1, trials=1, verbose=False):
- """
- Compute the homology of a random simplicial complex with and
- without CHomP, and compare the results. If they are not the same,
- raise an error.
-
- :param level: measure of complexity of the simplicial complex --
- see :func:`random_simplicial_complex`
- :type level: positive integer; optional, default 1
- :param trials: number of trials to conduct
- :type trials: positive integer; optional, default 1
- :param verbose: if ``True``, print verbose messages
- :type verbose: boolean; optional, default ``False``
-
- This gets pretty slow if ``level`` is more than 3.
-
- EXAMPLES::
-
- sage: from sage.homology.tests import test_random_simplicial_complex
- sage: test_random_simplicial_complex(trials=2) # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- """
- deprecation(33777, 'the CHomP interface is deprecated; hence so is this function')
- for i in range(trials):
- X = random_simplicial_complex(level=level)
- chomp = X.homology(verbose=verbose)
- no_chomp = X.homology(algorithm='no_chomp', verbose=verbose)
- if chomp != no_chomp:
- print("Homology according to CHomP: %s" % chomp)
- print("Homology according to Sage: %s" % no_chomp)
- print("Simplicial complex: %s" % X)
- print("Its chain complex: %s" % X.chain_complex())
- raise ValueError
diff --git a/src/sage/interfaces/chomp.py b/src/sage/interfaces/chomp.py
deleted file mode 100644
index ad3bf9c734e..00000000000
--- a/src/sage/interfaces/chomp.py
+++ /dev/null
@@ -1,923 +0,0 @@
-r"""
-Interface to CHomP
-
-This module is deprecated: see :issue:`33777`.
-
-CHomP stands for "Computation Homology Program", and is good at
-computing homology of simplicial complexes, cubical complexes, and
-chain complexes. It can also compute homomorphisms induced on
-homology by maps. See the CHomP web page http://chomp.rutgers.edu/
-for more information.
-
-AUTHOR:
-
-- John H. Palmieri
-"""
-
-import re
-
-from sage.misc.superseded import deprecation
-
-_have_chomp = {}
-
-
-def have_chomp(program='homsimpl'):
- """
- Return True if this computer has ``program`` installed.
-
- The first time it is run, this function caches its result in the
- variable ``_have_chomp`` -- a dictionary indexed by program name
- -- and any subsequent time, it just checks the value of the
- variable.
-
- This program is used in the routine CHomP.__call__.
-
- If this computer doesn't have CHomP installed, you may obtain it
- from http://chomp.rutgers.edu/.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import have_chomp
- sage: have_chomp() # random -- depends on whether CHomP is installed
- doctest:...: DeprecationWarning: the CHomP interface is deprecated; hence so is this function
- See https://github.com/sagemath/sage/issues/33777 for details.
- True
- sage: 'homsimpl' in sage.interfaces.chomp._have_chomp
- True
- sage: sage.interfaces.chomp._have_chomp['homsimpl'] == have_chomp()
- True
- """
- deprecation(33777, "the CHomP interface is deprecated; hence so is this function")
- global _have_chomp
- if program not in _have_chomp:
- from sage.misc.sage_ostools import have_program
- _have_chomp[program] = have_program(program)
- return _have_chomp[program]
-
-
-class CHomP:
- r"""
- Interface to the CHomP package.
-
- :param program: which CHomP program to use
- :type program: string
- :param complex: a simplicial or cubical complex
- :param subcomplex: a subcomplex of ``complex`` or None (the default)
- :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`.
- :type base_ring: ring; optional, default `\ZZ`
- :param generators: if True, also return list of generators
- :type generators: boolean; optional, default False
- :param verbose: if True, print helpful messages as the computation
- progresses
- :type verbose: boolean; optional, default False
- :param extra_opts: options passed directly to ``program``
- :type extra_opts: string
- :return: homology groups as a dictionary indexed by dimension
-
- The programs ``homsimpl``, ``homcubes``, and ``homchain`` are
- available through this interface. ``homsimpl`` computes the
- relative or absolute homology groups of simplicial complexes.
- ``homcubes`` computes the relative or absolute homology groups of
- cubical complexes. ``homchain`` computes the homology groups of
- chain complexes. For consistency with Sage's other homology
- computations, the answers produced by ``homsimpl`` and
- ``homcubes`` in the absolute case are converted to reduced
- homology.
-
- Note also that CHomP can only compute over the integers or
- `\GF{p}`. CHomP is fast enough, though, that if you want
- rational information, you should consider using CHomP with integer
- coefficients, or with mod `p` coefficients for a sufficiently
- large `p`, rather than using Sage's built-in homology algorithms.
-
- See also the documentation for the functions :func:`homchain`,
- :func:`homcubes`, and :func:`homsimpl` for more examples,
- including illustrations of some of the optional parameters.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import CHomP
- sage: T = cubical_complexes.Torus()
- sage: CHomP()('homcubes', T) # optional - CHomP
- {0: 0, 1: Z x Z, 2: Z}
-
- Relative homology of a segment relative to its endpoints::
-
- sage: edge = simplicial_complexes.Simplex(1)
- sage: ends = edge.n_skeleton(0)
- sage: CHomP()('homsimpl', edge) # optional - CHomP
- {0: 0}
- sage: CHomP()('homsimpl', edge, ends) # optional - CHomP
- {0: 0, 1: Z}
-
- Homology of a chain complex::
-
- sage: C = ChainComplex({3: 2 * identity_matrix(ZZ, 2)}, degree=-1)
- sage: CHomP()('homchain', C) # optional - CHomP
- {2: C2 x C2}
- """
- def __repr__(self):
- """
- String representation.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import CHomP
- sage: CHomP() # indirect doctest
- CHomP interface
- """
- return "CHomP interface"
-
- def __call__(self, program, complex, subcomplex=None, **kwds):
- """
- Call a CHomP program to compute the homology of a chain
- complex, simplicial complex, or cubical complex.
-
- Deprecated: see :issue:`33777`.
-
- See :class:`CHomP` for full documentation.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import CHomP
- sage: T = cubical_complexes.Torus()
- sage: CHomP()('homcubes', T) # indirect doctest, optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- {0: 0, 1: Z x Z, 2: Z}
- """
- from sage.misc.temporary_file import tmp_filename
- from sage.topology.cubical_complex import CubicalComplex, cubical_complexes
- from sage.topology.simplicial_complex import SimplicialComplex, Simplex
- from sage.homology.chain_complex import HomologyGroup
- from subprocess import Popen, PIPE
- from sage.rings.integer_ring import ZZ
- from sage.rings.rational_field import QQ
- from sage.modules.free_module import VectorSpace
- from sage.modules.free_module_element import free_module_element as vector
- from sage.combinat.free_module import CombinatorialFreeModule
-
- deprecation(33777, "the CHomP interface is deprecated")
-
- if not have_chomp(program):
- raise OSError("Program %s not found" % program)
-
- verbose = kwds.get('verbose', False)
- generators = kwds.get('generators', False)
- extra_opts = kwds.get('extra_opts', '')
- base_ring = kwds.get('base_ring', ZZ)
-
- if extra_opts:
- extra_opts = extra_opts.split()
- else:
- extra_opts = []
-
- # type of complex:
- cubical = False
- simplicial = False
- chain = False
- # CHomP seems to have problems with cubical complexes if the
- # first interval in the first cube defining the complex is
- # degenerate. So replace the complex X with [0,1] x X.
- if isinstance(complex, CubicalComplex):
- cubical = True
- edge = cubical_complexes.Cube(1)
- original_complex = complex
- complex = edge.product(complex)
- if verbose:
- print("Cubical complex")
- elif isinstance(complex, SimplicialComplex):
- simplicial = True
- if verbose:
- print("Simplicial complex")
- else:
- chain = True
- base_ring = kwds.get('base_ring', complex.base_ring())
- if verbose:
- print("Chain complex over %s" % base_ring)
-
- if base_ring == QQ:
- raise ValueError("CHomP doesn't compute over the rationals, only over Z or F_p.")
- if base_ring.is_prime_field():
- p = base_ring.characteristic()
- extra_opts.append('-p%s' % p)
- mod_p = True
- else:
- mod_p = False
-
- #
- # complex
- #
- try:
- data = complex._chomp_repr_()
- except AttributeError:
- raise AttributeError("Complex cannot be converted to use with CHomP.")
-
- datafile = tmp_filename()
- with open(datafile, 'w') as f:
- f.write(data)
-
- #
- # subcomplex
- #
- if subcomplex is None:
- if cubical:
- subcomplex = CubicalComplex([complex.n_cells(0)[0]])
- elif simplicial:
- m = re.search(r'\(([^,]*),', data)
- v = int(m.group(1))
- subcomplex = SimplicialComplex([[v]])
- else:
- # replace subcomplex with [0,1] x subcomplex.
- if cubical:
- subcomplex = edge.product(subcomplex)
- #
- # generators
- #
- if generators:
- genfile = tmp_filename()
- extra_opts.append('-g%s' % genfile)
-
- #
- # call program
- #
- if subcomplex is not None:
- try:
- sub = subcomplex._chomp_repr_()
- except AttributeError:
- raise AttributeError("Subcomplex cannot be converted to use with CHomP.")
- subfile = tmp_filename()
- with open(subfile, 'w') as f:
- f.write(sub)
- else:
- subfile = ''
- if verbose:
- print("Popen called with arguments", end="")
- print([program, datafile, subfile] + extra_opts)
- print("")
- print("CHomP output:")
- print("")
- # output = Popen([program, datafile, subfile, extra_opts],
- cmd = [program, datafile]
- if subfile:
- cmd.append(subfile)
- if extra_opts:
- cmd.extend(extra_opts)
- output = Popen(cmd, stdout=PIPE).communicate()[0]
- if verbose:
- print(output)
- print("End of CHomP output")
- print("")
- if generators:
- with open(genfile, 'r') as f:
- gens = f.read()
- if verbose:
- print("Generators:")
- print(gens)
- #
- # process output
- #
- if output.find('ERROR') != -1:
- raise RuntimeError('error inside CHomP')
- # output contains substrings of one of the forms
- # "H_1 = Z", "H_1 = Z_2 + Z", "H_1 = Z_2 + Z^2",
- # "H_1 = Z + Z_2 + Z"
- if output.find('trivial') != -1:
- if mod_p:
- return {0: VectorSpace(base_ring, 0)}
- else:
- return {0: HomologyGroup(0, ZZ)}
- d = {}
- h = re.compile("^H_([0-9]*) = (.*)$", re.M)
- tors = re.compile("Z_([0-9]*)")
- #
- # homology groups
- #
- for m in h.finditer(output):
- if verbose:
- print(m.groups())
- # dim is the dimension of the homology group
- dim = int(m.group(1))
- # hom_str is the right side of the equation "H_n = Z^r + Z_k + ..."
- hom_str = m.group(2)
- # need to read off number of summands and their invariants
- if hom_str.find("0") == 0:
- if mod_p:
- hom = VectorSpace(base_ring, 0)
- else:
- hom = HomologyGroup(0, ZZ)
- else:
- rk = 0
- if hom_str.find("^") != -1:
- rk_srch = re.search(r'\^([0-9]*)\s?', hom_str)
- rk = int(rk_srch.group(1))
- rk += len(re.findall(r"(Z$)|(Z\s)", hom_str))
- if mod_p:
- rk = rk if rk != 0 else 1
- if verbose:
- print("dimension = %s, rank of homology = %s" % (dim, rk))
- hom = VectorSpace(base_ring, rk)
- else:
- n = rk
- invts = []
- for t in tors.finditer(hom_str):
- n += 1
- invts.append(int(t.group(1)))
- for i in range(rk):
- invts.append(0)
- if verbose:
- print("dimension = %s, number of factors = %s, invariants = %s" % (dim, n, invts))
- hom = HomologyGroup(n, ZZ, invts)
-
- #
- # generators
- #
- if generators:
- if cubical:
- g = process_generators_cubical(gens, dim)
- if verbose:
- print("raw generators: %s" % g)
- if g:
- module = CombinatorialFreeModule(base_ring,
- original_complex.n_cells(dim),
- prefix="",
- bracket=True)
- basis = module.basis()
- output = []
- for x in g:
- v = module(0)
- for term in x:
- v += term[0] * basis[term[1]]
- output.append(v)
- g = output
- elif simplicial:
- g = process_generators_simplicial(gens, dim, complex)
- if verbose:
- print("raw generators: %s" % gens)
- if g:
- module = CombinatorialFreeModule(base_ring,
- complex.n_cells(dim),
- prefix="",
- bracket=False)
- basis = module.basis()
- output = []
- for x in g:
- v = module(0)
- for term in x:
- if complex._is_numeric():
- v += term[0] * basis[term[1]]
- else:
- translate = complex._translation_from_numeric()
- simplex = Simplex([translate[a] for a in term[1]])
- v += term[0] * basis[simplex]
- output.append(v)
- g = output
- elif chain:
- g = process_generators_chain(gens, dim, base_ring)
- if verbose:
- print("raw generators: %s" % gens)
- if g:
- if not mod_p:
- # sort generators to match up with corresponding invariant
- g = [_[1] for _ in sorted(zip(invts, g), key=lambda x: x[0])]
- d[dim] = (hom, g)
- else:
- d[dim] = hom
- else:
- d[dim] = hom
-
- if chain:
- new_d = {}
- diff = complex.differential()
- if len(diff) == 0:
- return {}
- bottom = min(diff)
- top = max(diff)
- for dim in d:
- if complex._degree_of_differential == -1: # chain complex
- new_dim = bottom + dim
- else: # cochain complex
- new_dim = top - dim
- if isinstance(d[dim], tuple):
- # generators included.
- group = d[dim][0]
- gens = d[dim][1]
- new_gens = []
- dimension = complex.differential(new_dim).ncols()
- # make sure that each vector is embedded in the
- # correct ambient space: pad with a zero if
- # necessary.
- for v in gens:
- v_dict = v.dict()
- if dimension - 1 not in v.dict():
- v_dict[dimension - 1] = 0
- new_gens.append(vector(base_ring, v_dict))
- else:
- new_gens.append(v)
- new_d[new_dim] = (group, new_gens)
- else:
- new_d[new_dim] = d[dim]
- d = new_d
- return d
-
- def help(self, program):
- """
- Print a help message for ``program``, a program from the CHomP suite.
-
- :param program: which CHomP program to use
- :type program: string
- :return: nothing -- just print a message
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import CHomP
- sage: CHomP().help('homcubes') # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- HOMCUBES, ver. ... Copyright (C) ... by Pawel Pilarczyk...
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from subprocess import Popen, PIPE
- print(Popen([program, '-h'], stdout=PIPE).communicate()[0])
-
-
-def homsimpl(complex=None, subcomplex=None, **kwds):
- r"""
- Compute the homology of a simplicial complex using the CHomP
- program ``homsimpl``. If the argument ``subcomplex`` is present,
- compute homology of ``complex`` relative to ``subcomplex``.
-
- This function is deprecated: see :issue:`33777`.
-
- :param complex: a simplicial complex
- :param subcomplex: a subcomplex of ``complex`` or None (the default)
- :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`.
- :type base_ring: ring; optional, default `\ZZ`
- :param generators: if True, also return list of generators
- :type generators: boolean; optional, default False
- :param verbose: if True, print helpful messages as the computation
- progresses
- :type verbose: boolean; optional, default False
- :param help: if True, just print a help message and exit
- :type help: boolean; optional, default False
- :param extra_opts: options passed directly to ``program``
- :type extra_opts: string
- :return: homology groups as a dictionary indexed by dimension
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import homsimpl
- sage: T = simplicial_complexes.Torus()
- sage: M8 = simplicial_complexes.MooreSpace(8)
- sage: M4 = simplicial_complexes.MooreSpace(4)
- sage: X = T.disjoint_union(T).disjoint_union(T).disjoint_union(M8).disjoint_union(M4)
- sage: homsimpl(X)[1] # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- Z^6 x C4 x C8
-
- Relative homology::
-
- sage: S = simplicial_complexes.Simplex(3)
- sage: bdry = S.n_skeleton(2)
- sage: homsimpl(S, bdry)[3] # optional - CHomP
- Z
-
- Generators: these are given as a list after the homology group.
- Each generator is specified as a linear combination of simplices::
-
- sage: homsimpl(S, bdry, generators=True)[3] # optional - CHomP
- (Z, [(0, 1, 2, 3)])
-
- sage: homsimpl(simplicial_complexes.Sphere(1), generators=True) # optional - CHomP
- {0: 0, 1: (Z, [(0, 1) - (0, 2) + (1, 2)])}
-
- TESTS:
-
- Generators for a simplicial complex whose vertices are not integers::
-
- sage: S1 = simplicial_complexes.Sphere(1)
- sage: homsimpl(S1.join(S1), generators=True, base_ring=GF(2))[3][1] # optional - CHomP
- [('L0', 'L1', 'R0', 'R1') + ('L0', 'L1', 'R0', 'R2') + ('L0', 'L1', 'R1', 'R2') + ('L0', 'L2', 'R0', 'R1') + ('L0', 'L2', 'R0', 'R2') + ('L0', 'L2', 'R1', 'R2') + ('L1', 'L2', 'R0', 'R1') + ('L1', 'L2', 'R0', 'R2') + ('L1', 'L2', 'R1', 'R2')]
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.topology.simplicial_complex import SimplicialComplex
- help = kwds.get('help', False)
- if help:
- return CHomP().help('homsimpl')
- # Check types here, because if you pass homsimpl a cubical
- # complex, it tries to compute its homology as if it were a
- # simplicial complex and gets terribly wrong answers.
- if (isinstance(complex, SimplicialComplex)
- and (subcomplex is None or isinstance(subcomplex, SimplicialComplex))):
- return CHomP()('homsimpl', complex, subcomplex=subcomplex, **kwds)
- else:
- raise TypeError("Complex and/or subcomplex are not simplicial complexes.")
-
-
-def homcubes(complex=None, subcomplex=None, **kwds):
- r"""
- Compute the homology of a cubical complex using the CHomP program
- ``homcubes``. If the argument ``subcomplex`` is present, compute
- homology of ``complex`` relative to ``subcomplex``.
-
- This function is deprecated: see :issue:`33777`.
-
- :param complex: a cubical complex
- :param subcomplex: a subcomplex of ``complex`` or None (the default)
- :param base_ring: ring over which to perform computations -- must be `\ZZ` or `\GF{p}`.
- :type base_ring: ring; optional, default `\ZZ`
- :param generators: if True, also return list of generators
- :type generators: boolean; optional, default False
- :param verbose: if True, print helpful messages as the computation progresses
- :type verbose: boolean; optional, default False
- :param help: if True, just print a help message and exit
- :type help: boolean; optional, default False
- :param extra_opts: options passed directly to ``homcubes``
- :type extra_opts: string
- :return: homology groups as a dictionary indexed by dimension
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import homcubes
- sage: S = cubical_complexes.Sphere(3)
- sage: homcubes(S)[3] # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- Z
-
- Relative homology::
-
- sage: C3 = cubical_complexes.Cube(3)
- sage: bdry = C3.n_skeleton(2)
- sage: homcubes(C3, bdry) # optional - CHomP
- {0: 0, 1: 0, 2: 0, 3: Z}
-
- Generators: these are given as a list after the homology group.
- Each generator is specified as a linear combination of cubes::
-
- sage: homcubes(cubical_complexes.Sphere(1), generators=True, base_ring=GF(2))[1][1] # optional - CHomP
- [[[1,1] x [0,1]] + [[0,1] x [1,1]] + [[0,1] x [0,0]] + [[0,0] x [0,1]]]
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.topology.cubical_complex import CubicalComplex
- help = kwds.get('help', False)
- if help:
- return CHomP().help('homcubes')
- # Type-checking is here for the same reason as in homsimpl above.
- if (isinstance(complex, CubicalComplex)
- and (subcomplex is None or isinstance(subcomplex, CubicalComplex))):
- return CHomP()('homcubes', complex, subcomplex=subcomplex, **kwds)
- else:
- raise TypeError("Complex and/or subcomplex are not cubical complexes.")
-
-
-def homchain(complex=None, **kwds):
- r"""
- Compute the homology of a chain complex using the CHomP program
- ``homchain``.
-
- This function is deprecated: see :issue:`33777`.
-
- :param complex: a chain complex
- :param generators: if True, also return list of generators
- :type generators: boolean; optional, default False
- :param verbose: if True, print helpful messages as the computation progresses
- :type verbose: boolean; optional, default False
- :param help: if True, just print a help message and exit
- :type help: boolean; optional, default False
- :param extra_opts: options passed directly to ``homchain``
- :type extra_opts: string
- :return: homology groups as a dictionary indexed by dimension
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import homchain
- sage: C = cubical_complexes.Sphere(3).chain_complex()
- sage: homchain(C)[3] # optional - CHomP
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- Z
-
- Generators: these are given as a list after the homology group.
- Each generator is specified as a cycle, an element in the
- appropriate free module over the base ring::
-
- sage: C2 = delta_complexes.Sphere(2).chain_complex()
- sage: homchain(C2, generators=True)[2] # optional - CHomP
- (Z, [(1, -1)])
- sage: homchain(C2, generators=True, base_ring=GF(2))[2] # optional - CHomP
- (Vector space of dimension 1 over Finite Field of size 2, [(1, 1)])
-
- TESTS:
-
- Chain complexes concentrated in negative dimensions, cochain complexes, etc.::
-
- sage: C = ChainComplex({-5: 4 * identity_matrix(ZZ, 2)}, degree=-1)
- sage: homchain(C) # optional - CHomP
- {-6: C4 x C4}
- sage: C = ChainComplex({-5: 4 * identity_matrix(ZZ, 2)}, degree=1)
- sage: homchain(C, generators=True) # optional - CHomP
- {-4: (C4 x C4, [(1, 0), (0, 1)])}
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.homology.chain_complex import ChainComplex_class
- help = kwds.get('help', False)
- if help:
- return CHomP().help('homchain')
- # Type-checking just in case.
- if isinstance(complex, ChainComplex_class):
- return CHomP()('homchain', complex, **kwds)
- else:
- raise TypeError("Complex is not a chain complex.")
-
-
-def process_generators_cubical(gen_string, dim):
- r"""
- Process CHomP generator information for cubical complexes.
-
- This function is deprecated: see :issue:`33777`.
-
- :param gen_string: generator output from CHomP
- :type gen_string: string
- :param dim: dimension in which to find generators
- :type dim: integer
- :return: list of generators in each dimension, as described below
-
- ``gen_string`` has the form ::
-
- The 2 generators of H_1 follow:
- generator 1
- -1 * [(0,0,0,0,0)(0,0,0,0,1)]
- 1 * [(0,0,0,0,0)(0,0,1,0,0)]
- ...
- generator 2
- -1 * [(0,1,0,1,1)(1,1,0,1,1)]
- -1 * [(0,1,0,0,1)(0,1,0,1,1)]
- ...
-
- Each line consists of a coefficient multiplied by a cube; the cube
- is specified by its "bottom left" and "upper right" corners.
-
- For technical reasons, we remove the first coordinate of each
- tuple, and using regular expressions, the remaining parts get
- converted from a string to a pair ``(coefficient, Cube)``, with
- the cube represented as a product of tuples. For example, the
- first line in "generator 1" gets turned into ::
-
- (-1, [0,0] x [0,0] x [0,0] x [0,1])
-
- representing an element in the free abelian group with basis given
- by cubes. Each generator is a list of such pairs, representing
- the sum of such elements. These are reassembled in
- :meth:`CHomP.__call__` to actual elements in the free module
- generated by the cubes of the cubical complex in the appropriate
- dimension.
-
- Therefore the return value is a list of lists of pairs, one list
- of pairs for each generator.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import process_generators_cubical
- sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * [(0,0,0,0,0)(0,0,0,0,1)]\n1 * [(0,0,0,0,0)(0,0,1,0,0)]"
- sage: process_generators_cubical(s, 1)
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- [[(-1, [0,0] x [0,0] x [0,0] x [0,1]), (1, [0,0] x [0,1] x [0,0] x [0,0])]]
- sage: len(process_generators_cubical(s, 1)) # only one generator
- 1
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.topology.cubical_complex import Cube
- # each dim in gen_string starts with "The generator for
- # H_3 follows:". So search for "T" to find the
- # end of the current list of generators.
- g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim)
- g = g_srch.search(gen_string)
- output = []
- cubes_list = []
- if g:
- g = g.group(1)
- if g:
- # project g to one end of the cylinder [0,1] x complex:
- #
- # drop the first coordinate and eliminate duplicates, at least
- # in positive dimensions, drop any line containing a
- # degenerate cube
- g = re.sub(r'\([01],', '(', g)
- if dim > 0:
- lines = g.splitlines()
- newlines = []
- for l in lines:
- x = re.findall(r'(\([0-9,]*\))', l)
- if x:
- left, right = x
- left = [int(a) for a in left.strip('()').split(',')]
- right = [int(a) for a in right.strip('()').split(',')]
- if sum([xx - yy for (xx, yy) in zip(right, left)]) == dim:
- newlines.append(l)
- else: # line like "generator 2"
- newlines.append(l)
- g = newlines
- cubes_list = []
- for l in g:
- cubes = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?\[\(([-0-9,]+)\)\(([-0-9,]+)\)\]', l)
- if cubes:
- if cubes.group(1) and re.search("-", cubes.group(1)):
- sign = -1
- else:
- sign = 1
- if cubes.group(2) and len(cubes.group(2)) > 0:
- coeff = sign * int(cubes.group(2))
- else:
- coeff = sign * 1
- cube1 = [int(a) for a in cubes.group(3).split(',')]
- cube2 = [int(a) for a in cubes.group(4).split(',')]
- cube = Cube(zip(cube1, cube2))
- cubes_list.append((coeff, cube))
- else:
- if cubes_list:
- output.append(cubes_list)
- cubes_list = []
- if cubes_list:
- output.append(cubes_list)
- return output
- else:
- return None
-
-
-def process_generators_simplicial(gen_string, dim, complex):
- r"""
- Process CHomP generator information for simplicial complexes.
-
- This function is deprecated: see :issue:`33777`
-
- :param gen_string: generator output from CHomP
- :type gen_string: string
- :param dim: dimension in which to find generators
- :type dim: integer
- :param complex: simplicial complex under consideration
- :return: list of generators in each dimension, as described below
-
- ``gen_string`` has the form ::
-
- The 2 generators of H_1 follow:
- generator 1
- -1 * (1,6)
- 1 * (1,4)
- ...
- generator 2
- -1 * (1,6)
- 1 * (1,4)
- ...
-
- where each line contains a coefficient and a simplex. Each line
- is converted, using regular expressions, from a string to a pair
- ``(coefficient, Simplex)``, like ::
-
- (-1, (1,6))
-
- representing an element in the free abelian group with basis given
- by simplices. Each generator is a list of such pairs,
- representing the sum of such elements. These are reassembled in
- :meth:`CHomP.__call__` to actual elements in the free module
- generated by the simplices of the simplicial complex in the
- appropriate dimension.
-
- Therefore the return value is a list of lists of pairs, one list
- of pairs for each generator.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import process_generators_simplicial
- sage: s = "The 2 generators of H_1 follow:\ngenerator 1:\n-1 * (1,6)\n1 * (1,4)"
- sage: process_generators_simplicial(s, 1, simplicial_complexes.Torus())
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- [[(-1, (1, 6)), (1, (1, 4))]]
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.topology.simplicial_complex import Simplex
- # each dim in gen_string starts with "The generator for H_3
- # follows:". So search for "T" to find the end of the current
- # list of generators.
- g_srch = re.compile(r'H_%s\sfollow[s]?:\n([^T]*)(?:T|$)' % dim)
- g = g_srch.search(gen_string)
- output = []
- simplex_list = []
- if g:
- g = g.group(1)
- if g:
- lines = g.splitlines()
- for l in lines:
- simplex = re.search(r'([+-]?)\s?([0-9]+)?\s?[*]?\s?(\([0-9,]*\))', l)
- if simplex:
- if simplex.group(1) and re.search("-", simplex.group(1)):
- sign = -1
- else:
- sign = 1
- if simplex.group(2) and len(simplex.group(2)) > 0:
- coeff = sign * int(simplex.group(2))
- else:
- coeff = sign * 1
- simplex = Simplex([int(a) for a in simplex.group(3).strip('()').split(',')])
- simplex_list.append((coeff, simplex))
- else:
- if simplex_list:
- output.append(simplex_list)
- simplex_list = []
- if simplex_list:
- output.append(simplex_list)
- return output
- else:
- return None
-
-
-def process_generators_chain(gen_string, dim, base_ring=None):
- r"""
- Process CHomP generator information for simplicial complexes.
-
- This function is deprecated: see :issue:`33777`.
-
- :param gen_string: generator output from CHomP
- :type gen_string: string
- :param dim: dimension in which to find generators
- :type dim: integer
- :param base_ring: base ring over which to do the computations
- :type base_ring: optional, default ZZ
- :return: list of generators in each dimension, as described below
-
- ``gen_string`` has the form ::
-
- [H_0]
- a1
-
- [H_1]
- a2
- a3
-
- [H_2]
- a1 - a2
-
- For each homology group, each line lists a homology generator as a
- linear combination of generators ``ai`` of the group of chains in
- the appropriate dimension. The elements ``ai`` are indexed
- starting with `i=1`. Each generator is converted, using regular
- expressions, from a string to a vector (an element in the free
- module over ``base_ring``), with ``ai`` representing the unit
- vector in coordinate `i-1`. For example, the string ``a1 - a2``
- gets converted to the vector ``(1, -1)``.
-
- Therefore the return value is a list of vectors.
-
- EXAMPLES::
-
- sage: from sage.interfaces.chomp import process_generators_chain
- sage: s = "[H_0]\na1\n\n[H_1]\na2\na3\n"
- sage: process_generators_chain(s, 1)
- doctest:...: DeprecationWarning: the CHomP interface is deprecated
- See https://github.com/sagemath/sage/issues/33777 for details.
- [(0, 1), (0, 0, 1)]
- sage: s = "[H_0]\na1\n\n[H_1]\n5 * a2 - a1\na3\n"
- sage: process_generators_chain(s, 1, base_ring=ZZ)
- [(-1, 5), (0, 0, 1)]
- sage: process_generators_chain(s, 1, base_ring=GF(2))
- [(1, 1), (0, 0, 1)]
- """
- deprecation(33777, "the CHomP interface is deprecated")
- from sage.modules.free_module_element import vector
- from sage.rings.integer_ring import ZZ
- if base_ring is None:
- base_ring = ZZ
- # each dim in gens starts with a string like
- # "[H_3]". So search for "[" to find the end of
- # the current list of generators.
- g_srch = re.compile(r'\[H_%s\]\n([^]]*)(?:\[|$)' % dim)
- g = g_srch.search(gen_string)
- if g:
- g = g.group(1)
- if g:
- # each line in the string g is a linear
- # combination of things like "a2", "a31", etc.
- # indexing on the a's starts at 1.
- lines = g.splitlines()
- new_gens = []
- for l in lines:
- gen = re.compile(r"([+-]?)\s?([0-9]+)?\s?[*]?\s?a([0-9]*)(?:\s|$)")
- v = {}
- for term in gen.finditer(l):
- if term.group(1) and re.search("-", term.group(1)):
- sign = -1
- else:
- sign = 1
- if term.group(2) and len(term.group(2)) > 0:
- coeff = sign * int(term.group(2))
- else:
- coeff = sign * 1
- idx = int(term.group(3))
- v[idx-1] = coeff
- if v:
- new_gens.append(vector(base_ring, v))
- g = new_gens
- return g
diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py
index 87feb554d16..7d4dddabba9 100644
--- a/src/sage/interfaces/fricas.py
+++ b/src/sage/interfaces/fricas.py
@@ -647,7 +647,8 @@ def _convert_prod(x, y):
def explicitly_not_implemented(*args):
raise NotImplementedError("the translation of the FriCAS Expression '%s' to sage is not yet implemented" % args)
- register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}, 2)
+ register_symbol(lambda *args: explicitly_not_implemented("rootOfADE"), {'fricas': 'rootOfADE'}, 2) # to be removed once we fully on FriCAS 1.3.10+
+ register_symbol(lambda *args: explicitly_not_implemented("FEseries"), {'fricas': 'FEseries'}, 2)
register_symbol(lambda *args: explicitly_not_implemented("rootOfRec"), {'fricas': 'rootOfRec'}, 2)
def set(self, var, value):
@@ -802,7 +803,7 @@ def get_unparsed_InputForm(self, var):
'(1..3)$Segment(PositiveInteger())'
"""
- return self.get_string('unparse((%s)::InputForm)' % str(var))
+ return self.get_string('unparse((%s)::InputForm)' % var)
def get_InputForm(self, var):
"""
@@ -1184,7 +1185,7 @@ def _latex_(self):
\frac{{{\log \left( {{e+1}} \right)} \ {\sin \left( {{y+x}} \right)}}}{{{e} ^{z}}}
sage: latex(fricas("matrix([[1,2],[3,4]])"))
- \left[ \begin{array}{cc} 1 & 2 \\ 3 & 4\end{array} \right]
+ \left[ \begin{array}{cc} 1 & 2 \\ 3 & 4...\end{array}...\right]
sage: latex(fricas("integrate(sin(x+1/x),x)"))
\int ^{\displaystyle x} {{\sin \left( {{\frac{{{{ \%...} ^{2}}+1}}{ \%...}}} \right)} \ {d \%...}}
@@ -1715,7 +1716,6 @@ def _sage_expression(fricas_InputForm):
sage: f[1].sage()
-1/2*sqrt(1/3)*sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) + 1/2*sqrt(-(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3) + 6*sqrt(1/3)/sqrt((3*(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(2/3) + 4)/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3)) - 4/3/(1/18*I*sqrt(229)*sqrt(3) + 1/2)^(1/3))
-
"""
# a FriCAS expressions may contain implicit references to a
# rootOf expression within itself, as for example in the
@@ -1836,7 +1836,7 @@ def _sage_(self):
sage: fricas(x+3).sage()
x + 3
sage: fricas(x+3).domainOf()
- Polynomial(Integer())
+ Polynomial(Integer...)
sage: fricas(matrix([[2,3],[4,x+5]])).diagonal().sage()
(2, x + 5)
@@ -1904,7 +1904,7 @@ def _sage_(self):
sage: s.sage()
Traceback (most recent call last):
...
- NotImplementedError: the translation of the FriCAS Expression 'rootOfADE' to sage is not yet implemented
+ NotImplementedError: the translation of the FriCAS Expression 'FEseries' to sage is not yet implemented
sage: s = fricas("series(sqrt(1+x), x=0)"); s
1 1 2 1 3 5 4 7 5 21 6 33 7 429 8
@@ -1994,19 +1994,24 @@ def _sage_(self):
return R([self.coefficient(i).sage()
for i in range(ZZ(self.degree()) + 1)])
- # finally translate domains with InputForm
- try:
- unparsed_InputForm = P.get_unparsed_InputForm(self._name)
- except RuntimeError as error:
- raise NotImplementedError("the translation of the FriCAS object\n\n%s\n\nto sage is not yet implemented:\n%s" % (self, error))
+ # finally translate domains with InputForm - we do this
+ # lazily, because sometimes we can use unparse, sometimes we
+ # need our custom sageprint
+
+ def unparsed_InputForm():
+ try:
+ return P.get_unparsed_InputForm(self._name)
+ except RuntimeError as error:
+ raise NotImplementedError("the translation of the FriCAS object\n\n%s\n\nto sage is not yet implemented:\n%s" % (self, error))
+
if head == "Boolean":
- return unparsed_InputForm == "true"
+ return unparsed_InputForm() == "true"
if head in ["Integer", "NonNegativeInteger", "PositiveInteger"]:
- return ZZ(unparsed_InputForm)
+ return ZZ(unparsed_InputForm())
if head == "String":
- return unparsed_InputForm
+ return unparsed_InputForm()
if head == "Float":
# Warning: precision$Float gives the current precision,
@@ -2014,22 +2019,28 @@ def _sage_(self):
# self.
prec = max(P.new("length mantissa(%s)" % self._name).sage(), 53)
R = RealField(prec)
- x, e, b = unparsed_InputForm.lstrip('float(').rstrip(')').split(',')
+ x, e, b = unparsed_InputForm().lstrip('float(').rstrip(')').split(',')
return R(ZZ(x) * ZZ(b)**ZZ(e))
if head == "DoubleFloat":
- return RDF(unparsed_InputForm)
+ return RDF(unparsed_InputForm())
if head == "AlgebraicNumber":
- s = unparsed_InputForm[:-len("::AlgebraicNumber()")]
+ s = unparsed_InputForm()[:-len("::AlgebraicNumber()")]
return sage_eval("QQbar(" + s + ")")
if head == "IntegerMod" or head == "PrimeField":
# one might be tempted not to go via InputForm here, but
# it turns out to be safer to do it.
- n = unparsed_InputForm[len("index("):]
- n = n[:n.find(")")]
- return self._get_sage_type(domain)(n)
+ s = unparsed_InputForm()[len("index("):]
+ s = s[:s.find(")")]
+ return self._get_sage_type(domain)(s)
+
+ if head == 'DistributedMultivariatePolynomial':
+ base_ring = self._get_sage_type(domain[2])
+ vars = domain[1].car()
+ R = PolynomialRing(base_ring, vars)
+ return R(unparsed_InputForm())
if head == "Polynomial":
base_ring = self._get_sage_type(domain[1])
@@ -2039,29 +2050,24 @@ def _sage_(self):
# the following is a bad hack, we should be getting a list here
vars = P.get_unparsed_InputForm("variables(%s)" % self._name)[1:-1]
+ s = unparsed_InputForm()
if vars == "":
- return base_ring(unparsed_InputForm)
- else:
- R = PolynomialRing(base_ring, vars)
- return R(unparsed_InputForm)
+ return base_ring(s)
+
+ R = PolynomialRing(base_ring, vars)
+ return R(s)
if head in ["OrderedCompletion", "OnePointCompletion"]:
# it would be more correct to get the type parameter
# (which might not be Expression Integer) and recurse
return FriCASElement._sage_expression(P.get_InputForm(self._name))
- if head == "Expression" or head == "Pi":
+ if head == "Expression" or head == "Pi" or head == "PiDomain":
# we treat Expression Integer and Expression Complex
# Integer just the same
return FriCASElement._sage_expression(P.get_InputForm(self._name))
- if head == 'DistributedMultivariatePolynomial':
- base_ring = self._get_sage_type(domain[2])
- vars = domain[1].car()
- R = PolynomialRing(base_ring, vars)
- return R(unparsed_InputForm)
-
- raise NotImplementedError("the translation of the FriCAS object %s to sage is not yet implemented" % (unparsed_InputForm))
+ raise NotImplementedError("the translation of the FriCAS object %s to sage is not yet implemented" % (unparsed_InputForm()))
@instancedoc
diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py
index ee5861ac785..2e49e4c106f 100644
--- a/src/sage/interfaces/gap.py
+++ b/src/sage/interfaces/gap.py
@@ -1562,16 +1562,10 @@ def _latex_(self):
sage: s = gap("[[1,2], [3/4, 5/6]]")
sage: latex(s)
- \left(\begin{array}{rr} 1&2\\ 3/4&\frac{5}{6}\\ \end{array}\right)
+ \left[\left[1, 2\right], \left[\frac{3}{4}, \frac{5}{6}\right]\right]
"""
- P = self._check_valid()
- try:
- s = P.eval('LaTeXObj(%s)' % self.name())
- s = s.replace('\\\\', '\\').replace('"', '')
- s = s.replace('%\\n', ' ')
- return s
- except RuntimeError:
- return str(self)
+ from sage.misc.latex import latex
+ return latex(self._sage_())
@cached_method
def _tab_completion(self):
diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py
index aecddae452b..40d1b7319be 100644
--- a/src/sage/interfaces/kash.py
+++ b/src/sage/interfaces/kash.py
@@ -527,12 +527,7 @@ def _quit_string(self):
return 'quit;'
def _start(self):
- try:
- Expect._start(self)
- except RuntimeError:
- # TODO: replace this error with something more accurate.
- from sage.misc.package import PackageNotFoundError
- raise PackageNotFoundError("kash")
+ Expect._start(self)
# Turn off the annoying timer.
self.eval('Time(false);')
diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py
index dedbebab99c..4697b9763d3 100644
--- a/src/sage/interfaces/singular.py
+++ b/src/sage/interfaces/singular.py
@@ -1211,14 +1211,14 @@ def current_ring(self):
polynomial ring, over a field, global ordering
// coefficients: ZZ/127
// number of vars : 3
- // block 1 : ordering rp
+ // block 1 : ordering ip
// : names x y z
// block 2 : ordering C
sage: singular.current_ring()
polynomial ring, over a field, global ordering
// coefficients: ZZ/127
// number of vars : 3
- // block 1 : ordering rp
+ // block 1 : ordering ip
// : names x y z
// block 2 : ordering C
"""
@@ -1402,6 +1402,14 @@ def _repr_(self):
if self._name in s:
if self.get_custom_name() is None and self.type() == 'matrix':
s = self.parent().eval('pmat(%s,20)' % (self.name()))
+ # compatibility for singular 4.3.2p10 and before
+ if s.startswith("polynomial ring,"):
+ from sage.rings.polynomial.term_order import singular_name_mapping
+ from sage.repl.rich_output import get_display_manager
+ # this is our cue that singular uses `rp` instead of `ip`
+ if singular_name_mapping['invlex'] == 'rp' and 'doctest' in str(get_display_manager()):
+ s = re.sub('^(// .*block.* : ordering )rp$', '\\1ip',
+ s, 0, re.MULTILINE)
return s
def __copy__(self):
@@ -2025,6 +2033,10 @@ def _sage_(self, R=None):
sage: type(singular(int(5)).sage())
+ Test that bigintvec can be coerced::
+
+ sage: singular('hilb((ideal(x)), 1)').sage()
+ (1, -1, 0, 0, -1, 1, 0)
"""
typ = self.type()
if typ == 'poly':
@@ -2040,6 +2052,9 @@ def _sage_(self, R=None):
elif typ == 'intvec':
from sage.modules.free_module_element import vector
return vector([sage.rings.integer.Integer(str(e)) for e in self])
+ elif typ == 'bigintvec':
+ from sage.modules.free_module_element import vector
+ return vector([sage.rings.rational.Rational(str(e)) for e in self])
elif typ == 'intmat':
from sage.matrix.constructor import matrix
from sage.rings.integer_ring import ZZ
diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py
index 7ddbe5879c3..097021e2dd4 100644
--- a/src/sage/knots/link.py
+++ b/src/sage/knots/link.py
@@ -3606,36 +3606,57 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None,
# such that the resulting regions are in fact closed regions
# with straight angles, and using the minimal number of bends.
regions = sorted(self.regions(), key=len)
- regions = regions[:-1]
edges = list(set(flatten(pd_code)))
edges.sort()
MLP = MixedIntegerLinearProgram(maximization=False, solver=solver)
# v will be the list of variables in the MLP problem. There will be
- # two variables for each edge: number of right bendings and number of
- # left bendings (at the end, since we are minimizing the total, only one
- # of each will be nonzero
+ # two variables for each edge counting the number of bendings needed.
+ # The one with even index corresponds to the flow of this number from
+ # the left-hand-side region to the right-hand-side region if the edge
+ # is positive oriented. The one with odd index corresponds to the
+ # flow in the opposite direction. For a negative oriented edge the
+ # same is true but with exchanged directions. At the end, since we
+ # are minimizing the total, only one of each will be nonzero.
v = MLP.new_variable(nonnegative=True, integer=True)
+
+ def flow_from_source(e):
+ r"""
+ Return the flow variable from the source.
+ """
+ if e > 0:
+ return v[2*edges.index(e)]
+ else:
+ return v[2*edges.index(-e)+1]
+
+ def flow_to_sink(e):
+ r"""
+ Return the flow variable to the sink.
+ """
+ return flow_from_source(-e)
+
# one condition for each region
- for i in range(len(regions)):
- cond = 0
+ lr = len(regions)
+ for i in range(lr):
r = regions[i]
- for e in r:
- if e > 0:
- cond = cond + v[2*edges.index(e)] - v[2*edges.index(e) + 1]
- else:
- cond = cond - v[2*edges.index(-e)] + v[2*edges.index(-e) + 1]
- MLP.add_constraint(cond == 4 - len(r))
+ if i < lr - 1:
+ # capacity of interior region, sink if positive, source if negative
+ capacity = len(r) - 4
+ else:
+ # capacity of exterior region, only sink (added to fix :issue:`37587`).
+ capacity = len(r) + 4
+ flow = sum(flow_to_sink(e) - flow_from_source(e) for e in r)
+ MLP.add_constraint(flow == capacity) # exterior region only sink
+
MLP.set_objective(MLP.sum(v.values()))
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}
pieces = {tuple(i): [i] for j in segments.values() for i in j}
nregions = []
- for r in regions:
+ for r in regions[:-1]: # interior regions
nregion = []
for e in r:
if e > 0:
@@ -3688,16 +3709,16 @@ def plot(self, gap=0.1, component_gap=0.5, solver=None,
pieces[tuple(badregion[b][0])].append(N2)
if a < b:
- r1 = badregion[:a] + [[badregion[a][0],0], [N1,1]] + badregion[b:]
- r2 = badregion[a+1:b] + [[N2,1],[N1,1]]
+ r1 = badregion[:a] + [[badregion[a][0], 0], [N1, 1]] + badregion[b:]
+ 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:]
+ r1 = badregion[b:a] + [[badregion[a][0], 0], [N1, 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)
diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py
index 7b2b07b1f3f..6c15997c09f 100644
--- a/src/sage/libs/eclib/interface.py
+++ b/src/sage/libs/eclib/interface.py
@@ -728,7 +728,7 @@ class mwrank_MordellWeil(SageObject):
P1 = [-3:0:1] is generator number 1
saturating up to 20...Saturation index bound (for points of good reduction) = 3
Reducing saturation bound from given value 20 to computed index bound 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 7)
@@ -738,7 +738,7 @@ class mwrank_MordellWeil(SageObject):
P2 = [-2:3:1] is generator number 2
saturating up to 20...Saturation index bound (for points of good reduction) = 4
Reducing saturation bound from given value 20 to computed index bound 4
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
possible kernel vector = [1,1]
@@ -753,7 +753,7 @@ class mwrank_MordellWeil(SageObject):
P3 = [-14:25:8] is generator number 3
saturating up to 20...Saturation index bound (for points of good reduction) = 3
Reducing saturation bound from given value 20 to computed index bound 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
@@ -908,7 +908,7 @@ def process(self, v, saturation_bound=0):
saturating basis...Saturation index bound (for points of good reduction) = 93
Only p-saturating for p up to given value 2.
The resulting points may not be p-saturated for p between this and the computed index bound 93
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 ]
Checking 2-saturation
possible kernel vector = [1,0,0]
@@ -930,7 +930,7 @@ def process(self, v, saturation_bound=0):
saturating basis...Saturation index bound (for points of good reduction) = 46
Only p-saturating for p up to given value 3.
The resulting points may not be p-saturated for p between this and the computed index bound 46
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
@@ -954,7 +954,7 @@ def process(self, v, saturation_bound=0):
saturating basis...Saturation index bound (for points of good reduction) = 15
Only p-saturating for p up to given value 5.
The resulting points may not be p-saturated for p between this and the computed index bound 15
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 5 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
@@ -978,7 +978,7 @@ def process(self, v, saturation_bound=0):
0.417143558758384
sage: EQ.saturate() # points are now saturated
saturating basis...Saturation index bound (for points of good reduction) = 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
@@ -1189,7 +1189,7 @@ def saturate(self, max_prime=-1, min_prime=2):
sage: EQ.saturate() # points are now saturated
saturating basis...Saturation index bound (for points of good reduction) = 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
@@ -1217,7 +1217,7 @@ def saturate(self, max_prime=-1, min_prime=2):
sage: EQ.saturate()
saturating basis...Saturation index bound (for points of good reduction) = 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx
index bc475f907b6..c685c329926 100644
--- a/src/sage/libs/eclib/mwrank.pyx
+++ b/src/sage/libs/eclib/mwrank.pyx
@@ -590,7 +590,7 @@ cdef class _mw:
P1 = [-3:0:1] is generator number 1
saturating up to 20...Saturation index bound (for points of good reduction) = 3
Reducing saturation bound from given value 20 to computed index bound 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 7)
@@ -600,7 +600,7 @@ cdef class _mw:
P2 = [-2:3:1] is generator number 2
saturating up to 20...Saturation index bound (for points of good reduction) = 4
Reducing saturation bound from given value 20 to computed index bound 4
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
possible kernel vector = [1,1]
@@ -615,7 +615,7 @@ cdef class _mw:
P3 = [-14:25:8] is generator number 3
saturating up to 20...Saturation index bound (for points of good reduction) = 3
Reducing saturation bound from given value 20 to computed index bound 3
- Tamagawa index primes are [ 2 ]
+ Tamagawa index primes are [ 2 ]...
Checking saturation at [ 2 3 ]
Checking 2-saturation
Points were proved 2-saturated (max q used = 11)
diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd
index e36216d6395..3affdb34e11 100644
--- a/src/sage/libs/singular/decl.pxd
+++ b/src/sage/libs/singular/decl.pxd
@@ -48,6 +48,13 @@ cdef extern from "factory/factory.h":
cdef int SW_USE_NTL_SORT
cdef extern from "singular/Singular/libsingular.h":
+ """
+ // compatibility for singular 4.3.2p10 and before
+ #if SINGULAR_VERSION <= 4330
+ #define ringorder_ip ringorder_rp
+ #define BIGINTVEC_CMD INTVEC_CMD
+ #endif
+ """
#
# OPTIONS
@@ -243,7 +250,7 @@ cdef extern from "singular/Singular/libsingular.h":
ringorder_s
ringorder_lp
ringorder_dp
- ringorder_rp
+ ringorder_ip
ringorder_Dp
ringorder_wp
ringorder_Wp
@@ -291,6 +298,10 @@ cdef extern from "singular/Singular/libsingular.h":
int row
int col
+ cdef cppclass bigintmat:
+ int (*length)()
+ number* (*get)(int i)
+
# omalloc bins
ctypedef struct omBin "omBin_s"
@@ -921,6 +932,7 @@ cdef extern from "singular/Singular/libsingular.h":
cdef int MATRIX_CMD
cdef int LIST_CMD
cdef int INTVEC_CMD
+ cdef int BIGINTVEC_CMD
cdef int NONE
cdef int RESOLUTION_CMD
cdef int PACKAGE_CMD
diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx
index 4a5ab6d78f6..5c333e8d7e6 100644
--- a/src/sage/libs/singular/function.pyx
+++ b/src/sage/libs/singular/function.pyx
@@ -98,7 +98,7 @@ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence_g
from sage.libs.singular.decl cimport *
from sage.libs.singular.option import opt_ctx
from sage.libs.singular.polynomial cimport singular_vector_maximal_component
-from sage.libs.singular.singular cimport sa2si, si2sa, si2sa_intvec
+from sage.libs.singular.singular cimport sa2si, si2sa, si2sa_intvec, si2sa_bigintvec
from sage.libs.singular.singular import error_messages
from sage.interfaces.singular import get_docstring
@@ -954,6 +954,8 @@ cdef class Converter(SageObject):
return si2sa(to_convert.data, self._singular_ring, self._sage_ring.base_ring())
elif rtyp == INTVEC_CMD:
return si2sa_intvec( to_convert.data)
+ elif rtyp == BIGINTVEC_CMD:
+ return si2sa_bigintvec( to_convert.data)
elif rtyp == STRING_CMD:
# TODO: Need to determine what kind of data can be returned by a
# STRING_CMD--is it just ASCII strings or can it be an arbitrary
@@ -1048,6 +1050,17 @@ cdef class LibraryCallHandler(BaseCallHandler):
"""
return False
+# mapping int --> string for function arity
+arity_dict = {
+ CMD_1: "CMD_1",
+ CMD_2: "CMD_2",
+ CMD_3: "CMD_3",
+ CMD_12: "CMD_12",
+ CMD_13: "CMD_13",
+ CMD_23: "CMD_23",
+ CMD_123: "CMD_123",
+ CMD_M: "CMD_M"
+}
cdef class KernelCallHandler(BaseCallHandler):
"""
@@ -1125,8 +1138,9 @@ cdef class KernelCallHandler(BaseCallHandler):
errorreported += 1
error_messages.append(
- "Wrong number of arguments (got {} arguments, arity code is {})"
- .format(number_of_arguments, self.arity))
+ "Wrong number of arguments (got {} arguments, arity is {})"
+ .format(number_of_arguments,
+ arity_dict.get(self.arity) or self.arity))
return NULL
cdef bint free_res(self) noexcept:
@@ -1231,7 +1245,7 @@ cdef class SingularFunction(SageObject):
Traceback (most recent call last):
...
RuntimeError: error in Singular function call 'size':
- Wrong number of arguments (got 2 arguments, arity code is 302)
+ Wrong number of arguments (got 2 arguments, arity is CMD_1)
sage: size('foobar', ring=P)
6
@@ -1634,17 +1648,17 @@ def singular_function(name):
Traceback (most recent call last):
...
RuntimeError: error in Singular function call 'factorize':
- Wrong number of arguments (got 0 arguments, arity code is 305)
+ Wrong number of arguments (got 0 arguments, arity is CMD_12)
sage: factorize(f, 1, 2)
Traceback (most recent call last):
...
RuntimeError: error in Singular function call 'factorize':
- Wrong number of arguments (got 3 arguments, arity code is 305)
+ Wrong number of arguments (got 3 arguments, arity is CMD_12)
sage: factorize(f, 1, 2, 3)
Traceback (most recent call last):
...
RuntimeError: error in Singular function call 'factorize':
- Wrong number of arguments (got 4 arguments, arity code is 305)
+ Wrong number of arguments (got 4 arguments, arity is CMD_12)
The Singular function ``list`` can be called with any number of
arguments::
diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx
index 0efff45904d..b3295206e5b 100644
--- a/src/sage/libs/singular/ring.pyx
+++ b/src/sage/libs/singular/ring.pyx
@@ -16,7 +16,7 @@ AUTHORS:
# https://www.gnu.org/licenses/
# ****************************************************************************
-from sage.cpython.string cimport str_to_bytes
+from sage.cpython.string cimport str_to_bytes, bytes_to_str
from sage.libs.gmp.types cimport __mpz_struct
from sage.libs.gmp.mpz cimport mpz_init_set_ui
@@ -24,7 +24,7 @@ from sage.libs.gmp.mpz cimport mpz_init_set_ui
from sage.libs.singular.decl cimport ring, currRing
from sage.libs.singular.decl cimport rChangeCurrRing, rComplete, rDelete, idInit
from sage.libs.singular.decl cimport omAlloc0, omStrDup, omAlloc
-from sage.libs.singular.decl cimport ringorder_dp, ringorder_Dp, ringorder_lp, ringorder_rp, ringorder_ds, ringorder_Ds, ringorder_ls, ringorder_M, ringorder_c, ringorder_C, ringorder_wp, ringorder_Wp, ringorder_ws, ringorder_Ws, ringorder_a, rRingOrder_t
+from sage.libs.singular.decl cimport ringorder_dp, ringorder_Dp, ringorder_lp, ringorder_ip, ringorder_ds, ringorder_Ds, ringorder_ls, ringorder_M, ringorder_c, ringorder_C, ringorder_wp, ringorder_Wp, ringorder_ws, ringorder_Ws, ringorder_a, rRingOrder_t
from sage.libs.singular.decl cimport prCopyR
from sage.libs.singular.decl cimport n_unknown, n_algExt, n_transExt, n_Z, n_Zn, n_Znm, n_Z2m
from sage.libs.singular.decl cimport n_coeffType
@@ -60,7 +60,7 @@ order_dict = {
"dp": ringorder_dp,
"Dp": ringorder_Dp,
"lp": ringorder_lp,
- "rp": ringorder_rp,
+ "ip": ringorder_ip,
"ds": ringorder_ds,
"Ds": ringorder_Ds,
"ls": ringorder_ls,
@@ -71,6 +71,16 @@ order_dict = {
"a": ringorder_a,
}
+cdef extern from "singular/Singular/libsingular.h":
+ cdef char * rSimpleOrdStr(rRingOrder_t)
+
+if bytes_to_str(rSimpleOrdStr(ringorder_ip)) == "rp":
+ # compatibility for singular 4.3.2p10 and before
+ order_dict["rp"] = ringorder_ip
+ # also patch term_order mappings
+ from sage.rings.polynomial import term_order
+ term_order.singular_name_mapping['invlex'] = 'rp'
+ term_order.inv_singular_name_mapping['rp'] = 'invlex'
#############################################################################
cdef ring *singular_ring_new(base_ring, n, names, term_order) except NULL:
diff --git a/src/sage/libs/singular/singular.pxd b/src/sage/libs/singular/singular.pxd
index 05f32b68079..ca31d02456c 100644
--- a/src/sage/libs/singular/singular.pxd
+++ b/src/sage/libs/singular/singular.pxd
@@ -1,4 +1,4 @@
-from sage.libs.singular.decl cimport ring, poly, number, intvec
+from sage.libs.singular.decl cimport ring, poly, number, intvec, bigintmat
from sage.libs.singular.function cimport Resolution
from sage.rings.rational cimport Rational
@@ -29,6 +29,7 @@ cdef object si2sa_ZZmod(number *n, ring *_ring, object base)
cdef object si2sa_NF(number *n, ring *_ring, object base)
cdef object si2sa_intvec(intvec *v)
+cdef object si2sa_bigintvec(bigintmat *v)
# dispatches to all the above.
cdef object si2sa(number *n, ring *_ring, object base)
diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx
index e256949298e..3074b859529 100644
--- a/src/sage/libs/singular/singular.pyx
+++ b/src/sage/libs/singular/singular.pyx
@@ -1544,7 +1544,6 @@ cdef inline number *sa2si_ZZmod(IntegerMod_abstract d, ring *_ring) noexcept:
sage: P(3)
3
"""
- nr2mModul = d.parent().characteristic()
if _ring != currRing: rChangeCurrRing(_ring)
cdef number *nn
@@ -1699,6 +1698,25 @@ cdef object si2sa_intvec(intvec *v):
l.append(v.get(r))
return tuple(l)
+cdef object si2sa_bigintvec(bigintmat *v):
+ r"""
+ create a sage tuple from a singular vector of big integers
+
+ INPUT:
+
+ - ``v`` -- a (pointer to) singular bigintmat
+
+ OUTPUT:
+
+ a sage tuple
+ """
+ cdef int r
+ cdef list l = list()
+ for r in range(v.length()):
+ n = v.get(r)
+ l.append(si2sa_QQ(n, &n, currRing))
+ return tuple(l)
+
# ==============
# Initialisation
# ==============
diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py
index bb09ed8885d..91b0997de7b 100644
--- a/src/sage/manifolds/catalog.py
+++ b/src/sage/manifolds/catalog.py
@@ -127,14 +127,19 @@ def Kerr(m=1, a=0, coordinates="BL", names=None):
4-dimensional Lorentzian manifold M
sage: K.atlas()
[Chart (M, (t, r, th, ph))]
+
+ The Kerr metric in Boyer-Lindquist coordinates (cf. :wikipedia:`Kerr_metric`)::
+
sage: K.metric().display()
g = (2*m*r/(a^2*cos(th)^2 + r^2) - 1) dt⊗dt
- + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph
+ - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dt⊗dph
+ (a^2*cos(th)^2 + r^2)/(a^2 - 2*m*r + r^2) dr⊗dr
+ (a^2*cos(th)^2 + r^2) dth⊗dth
- + 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt
+ - 2*a*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) dph⊗dt
+ (2*a^2*m*r*sin(th)^2/(a^2*cos(th)^2 + r^2) + a^2 + r^2)*sin(th)^2 dph⊗dph
+ The Schwarzschild spacetime with the mass parameter set to 1::
+
sage: K. = manifolds.Kerr()
sage: K
4-dimensional Lorentzian manifold M
@@ -144,6 +149,9 @@ def Kerr(m=1, a=0, coordinates="BL", names=None):
sage: K.default_chart().coord_range()
t: (-oo, +oo); r: (0, +oo); th: (0, pi); ph: [-pi, pi] (periodic)
+
+ The Kerr spacetime in Kerr coordinates::
+
sage: m, a = var('m, a')
sage: K. = manifolds.Kerr(m, a, coordinates="Kerr")
sage: K
@@ -205,7 +213,7 @@ def Kerr(m=1, a=0, coordinates="BL", names=None):
g[0, 0], g[1, 1], g[2, 2], g[3, 3] = -(1-2*m*r/rho**2), \
rho**2/(r**2-2*m*r+a**2), rho**2, \
(r**2+a**2+2*m*r*a**2/rho**2*sin(th)**2)*sin(th)**2
- g[0, 3] = 2*m*r*a*sin(th)**2/rho**2
+ g[0, 3] = -2*m*r*a*sin(th)**2/rho**2
return M
raise NotImplementedError("coordinates system not implemented, see help"
diff --git a/src/sage/manifolds/differentiable/affine_connection.py b/src/sage/manifolds/differentiable/affine_connection.py
index 1a45b219872..5a3dc99ae6c 100644
--- a/src/sage/manifolds/differentiable/affine_connection.py
+++ b/src/sage/manifolds/differentiable/affine_connection.py
@@ -149,10 +149,10 @@ class AffineConnection(SageObject):
Unset components are initialized to zero::
- sage: nab[:] # list of coefficients relative to the manifold's default vector frame
+ sage: nab[:] # list of coefficients relative to the manifold's default vector frame
[[[0, x^2, 0], [0, 0, 0], [0, 0, 0]],
- [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
- [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]]
+ [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
+ [[0, 0, 0], [0, 0, y*z], [0, 0, 0]]]
The treatment of connection coefficients in a given vector frame is similar
to that of tensor components; see therefore the class
@@ -2377,7 +2377,7 @@ def curvature_form(self, i, j, frame=None):
2-form curvature (1,1) of connection nabla w.r.t. Vector frame
(M, (e_1,e_2,e_3)) on the 3-dimensional differentiable manifold M
sage: nab.curvature_form(1,1,e).display(e) # long time (if above is skipped)
- curvature (1,1) of connection nabla w.r.t. Vector frame
+ curvature (1,1) of connection nabla w.r.t. Vector frame
(M, (e_1,e_2,e_3)) =
(y^3*z^4 + 2*x*y*z + (x*y^4 - x*y)*z^2) e^1∧e^2
+ (x^4*y*z^2 - x^2*y^2) e^1∧e^3 + (x^5*y*z^3 - x*z^2) e^2∧e^3
diff --git a/src/sage/manifolds/differentiable/bundle_connection.py b/src/sage/manifolds/differentiable/bundle_connection.py
index a3353b820cb..8870b408d35 100644
--- a/src/sage/manifolds/differentiable/bundle_connection.py
+++ b/src/sage/manifolds/differentiable/bundle_connection.py
@@ -98,11 +98,11 @@ class BundleConnection(SageObject, Mutability):
sage: nab[e, 1, 1][:] = [x+z, y-z, x*y*z]
sage: nab.display()
connection (1,1) of bundle connection nabla w.r.t. Local frame
- (E|_M, (e_1,e_2)) = (x + z) dx + (y - z) dy + x*y*z dz
- connection (1,2) of bundle connection nabla w.r.t. Local frame
- (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz
- connection (2,1) of bundle connection nabla w.r.t. Local frame
- (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz
+ (E|_M, (e_1,e_2)) = (x + z) dx + (y - z) dy + x*y*z dz
+ connection (1,2) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (e_1,e_2)) = x*z dx + y*z dy + z^2 dz
+ connection (2,1) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (e_1,e_2)) = x dx + x^2 dy + x^3 dz
Notice, when we omit the frame, the default frame of the vector bundle is
assumed (in this case ``e``)::
@@ -189,14 +189,14 @@ class BundleConnection(SageObject, Mutability):
sage: nab.display(frame=f)
connection (1,1) of bundle connection nabla w.r.t. Local frame
(E|_M, (f_1,f_2)) = ((x^3 + x)*z + 2*x)/(x^2 + 1) dx + y*z dy + z^2 dz
- connection (1,2) of bundle connection nabla w.r.t. Local frame
- (E|_M, (f_1,f_2)) = -(x^3 + x)*z dx - (x^2 + 1)*y*z dy -
+ connection (1,2) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (f_1,f_2)) = -(x^3 + x)*z dx - (x^2 + 1)*y*z dy -
(x^2 + 1)*z^2 dz
- connection (2,1) of bundle connection nabla w.r.t. Local frame
- (E|_M, (f_1,f_2)) = (x*z - x)/(x^2 + 1) dx -
+ connection (2,1) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (f_1,f_2)) = (x*z - x)/(x^2 + 1) dx -
(x^2 - y*z)/(x^2 + 1) dy - (x^3 - z^2)/(x^2 + 1) dz
- connection (2,2) of bundle connection nabla w.r.t. Local frame
- (E|_M, (f_1,f_2)) = -x*z dx - y*z dy - z^2 dz
+ connection (2,2) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (f_1,f_2)) = -x*z dx - y*z dy - z^2 dz
The new connection 1-forms obey the defining formula, too::
@@ -214,14 +214,14 @@ class BundleConnection(SageObject, Mutability):
....: print(Omega(i ,j, e).display())
curvature (1,1) of bundle connection nabla w.r.t. Local frame
(E|_M, (e_1,e_2)) = -(x^3 - x*y)*z dx∧dy + (-x^4*z + x*z^2) dx∧dz +
- (-x^3*y*z + x^2*z^2) dy∧dz
- curvature (1,2) of bundle connection nabla w.r.t. Local frame
+ (-x^3*y*z + x^2*z^2) dy∧dz
+ curvature (1,2) of bundle connection nabla w.r.t. Local frame
(E|_M, (e_1,e_2)) = -x dx∧dz - y dy∧dz
- curvature (2,1) of bundle connection nabla w.r.t. Local frame
+ curvature (2,1) of bundle connection nabla w.r.t. Local frame
(E|_M, (e_1,e_2)) = 2*x dx∧dy + 3*x^2 dx∧dz
- curvature (2,2) of bundle connection nabla w.r.t. Local frame
+ curvature (2,2) of bundle connection nabla w.r.t. Local frame
(E|_M, (e_1,e_2)) = (x^3 - x*y)*z dx∧dy + (x^4*z - x*z^2) dx∧dz +
- (x^3*y*z - x^2*z^2) dy∧dz
+ (x^3*y*z - x^2*z^2) dy∧dz
The derived forms certainly obey the structure equations, see
:meth:`curvature_form` for details::
@@ -483,17 +483,17 @@ def _new_forms(self, frame):
sage: forms = nab._new_forms(e)
sage: [forms[k] for k in sorted(forms)]
[1-form connection (1,1) of bundle connection nabla w.r.t. Local
- frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
- manifold M,
- 1-form connection (1,2) of bundle connection nabla w.r.t. Local
- frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
- manifold M,
- 1-form connection (2,1) of bundle connection nabla w.r.t. Local
- frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
- manifold M,
- 1-form connection (2,2) of bundle connection nabla w.r.t. Local
- frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
- manifold M]
+ frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
+ manifold M,
+ 1-form connection (1,2) of bundle connection nabla w.r.t. Local
+ frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
+ manifold M,
+ 1-form connection (2,1) of bundle connection nabla w.r.t. Local
+ frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
+ manifold M,
+ 1-form connection (2,2) of bundle connection nabla w.r.t. Local
+ frame (E|_M, (e_1,e_2)) on the 2-dimensional differentiable
+ manifold M]
"""
dom = frame._domain
@@ -1307,8 +1307,8 @@ def display(self, frame=None, vector_frame=None, chart=None,
sage: nab.display()
connection (1,1) of bundle connection nabla w.r.t. Local frame
(E|_M, (e_1,e_2)) = x dx + y dy + z dz
- connection (2,2) of bundle connection nabla w.r.t. Local frame
- (E|_M, (e_1,e_2)) = x^2 dx + y^2 dy + z^2 dz
+ connection (2,2) of bundle connection nabla w.r.t. Local frame
+ (E|_M, (e_1,e_2)) = x^2 dx + y^2 dy + z^2 dz
sage: latex(nab.display())
\begin{array}{lcl} \omega^1_{\ \, 1} = x \mathrm{d} x +
y \mathrm{d} y + z \mathrm{d} z \\ \omega^2_{\ \, 2} = x^{2}
diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py
index 8a64694b38b..1c2d71d23b4 100644
--- a/src/sage/manifolds/differentiable/diff_map.py
+++ b/src/sage/manifolds/differentiable/diff_map.py
@@ -132,9 +132,9 @@ class is
sage: Phi.display()
Phi: S^2 → R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
- (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
+ (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1),
- -(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
+ -(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
It is possible to create the map via the method
:meth:`~sage.manifolds.differentiable.manifold.DifferentiableManifold.diff_map`
@@ -162,7 +162,7 @@ class is
sage: Phi1.display()
Phi: S^2 → R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
- (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
+ (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
The definition can be completed by means of the method
:meth:`~sage.manifolds.continuous_map.ContinuousMap.add_expr`::
@@ -172,9 +172,9 @@ class is
sage: Phi1.display()
Phi: S^2 → R^3
on U: (x, y) ↦ (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1),
- (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
+ (x^2 + y^2 - 1)/(x^2 + y^2 + 1))
on V: (u, v) ↦ (X, Y, Z) = (2*u/(u^2 + v^2 + 1), 2*v/(u^2 + v^2 + 1),
- -(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
+ -(u^2 + v^2 - 1)/(u^2 + v^2 + 1))
At this stage, ``Phi1`` and ``Phi`` are fully equivalent::
@@ -226,6 +226,14 @@ class is
Basis (∂/∂X,∂/∂Y,∂/∂Z) on the Tangent space at Point Phi(N) on the
3-dimensional differentiable manifold R^3
+ A convenient way to display the matrix of the differential::
+
+ sage: Phi.differential(np).display()
+ ∂/∂u ∂/∂v
+ ∂/∂X⎛ 2 0⎞
+ ∂/∂Y⎜ 0 2⎟
+ ∂/∂Z⎝ 0 0⎠
+
Differentiable maps can be composed by means of the operator ``*``: let
us introduce the map `\RR^3\rightarrow \RR^2` corresponding to
the projection from the point `(X,Y,Z)=(0,0,1)` onto the equatorial plane
diff --git a/src/sage/manifolds/differentiable/differentiable_submanifold.py b/src/sage/manifolds/differentiable/differentiable_submanifold.py
index 7387ea3a44e..aec0dce99e8 100644
--- a/src/sage/manifolds/differentiable/differentiable_submanifold.py
+++ b/src/sage/manifolds/differentiable/differentiable_submanifold.py
@@ -69,14 +69,14 @@ class DifferentiableSubmanifold(DifferentiableManifold, TopologicalSubmanifold):
- ``field`` -- field `K` on which the sub manifold is defined; allowed
values are
- - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for
- a manifold over `\RR`
- - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``)
- for a manifold over `\CC`
- - an object in the category of topological fields (see
- :class:`~sage.categories.fields.Fields` and
- :class:`~sage.categories.topological_spaces.TopologicalSpaces`)
- for other types of manifolds
+ - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for
+ a manifold over `\RR`
+ - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``)
+ for a manifold over `\CC`
+ - an object in the category of topological fields (see
+ :class:`~sage.categories.fields.Fields` and
+ :class:`~sage.categories.topological_spaces.TopologicalSpaces`)
+ for other types of manifolds
- ``structure`` -- manifold structure (see
:class:`~sage.manifolds.structure.TopologicalStructure` or
@@ -136,7 +136,7 @@ class DifferentiableSubmanifold(DifferentiableManifold, TopologicalSubmanifold):
sage: phi_inv_t = M.scalar_field({CM: z-x^2-y^2})
sage: phi_inv_t.display()
M → ℝ
- (x, y, z) ↦ -x^2 - y^2 + z
+ (x, y, z) ↦ -x^2 - y^2 + z
`\phi` can then be declared as an embedding `N\to M`::
diff --git a/src/sage/manifolds/differentiable/multivectorfield.py b/src/sage/manifolds/differentiable/multivectorfield.py
index 9a4b03fb369..8de30fdd711 100644
--- a/src/sage/manifolds/differentiable/multivectorfield.py
+++ b/src/sage/manifolds/differentiable/multivectorfield.py
@@ -1040,8 +1040,7 @@ def wedge(self, other):
sage: b.display()
b = y^2 ∂/∂x∧∂/∂y + (x + z) ∂/∂x∧∂/∂z + z^2 ∂/∂y∧∂/∂z
sage: s = a.wedge(b); s
- 3-vector field a∧b on the 3-dimensional differentiable
- manifold M
+ 3-vector field a∧b on the 3-dimensional differentiable manifold M
sage: s.display()
a∧b = (-x^2 + (y^3 - x - 1)*z + 2*z^2 - x) ∂/∂x∧∂/∂y∧∂/∂z
diff --git a/src/sage/manifolds/differentiable/tangent_space.py b/src/sage/manifolds/differentiable/tangent_space.py
index 98a60cede2a..a938d77e237 100644
--- a/src/sage/manifolds/differentiable/tangent_space.py
+++ b/src/sage/manifolds/differentiable/tangent_space.py
@@ -177,28 +177,32 @@ class TangentSpace(FiniteRankFreeModule):
[0 1]
sage: W_Tp_xy = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_xy)
sage: Tp.bases()[0]
- Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the 2-dimensional differentiable manifold M
- sage: phi_Tp_xy = Tp.isomorphism_with_fixed_basis(Tp.bases()[0], codomain=W_Tp_xy); phi_Tp_xy
+ Basis (∂/∂x,∂/∂y) on the Tangent space at Point p on the
+ 2-dimensional differentiable manifold M
+ sage: phi_Tp_xy = Tp.isomorphism_with_fixed_basis(Tp.bases()[0], codomain=W_Tp_xy)
+ sage: phi_Tp_xy
Generic morphism:
- From: Tangent space at Point p on the 2-dimensional differentiable manifold M
- To: Ambient quadratic space of dimension 2 over Symbolic Ring
- Inner product matrix:
- [1 0]
- [0 1]
+ From: Tangent space at Point p on the 2-dimensional differentiable manifold M
+ To: Ambient quadratic space of dimension 2 over Symbolic Ring
+ Inner product matrix:
+ [1 0]
+ [0 1]
sage: Q_Tp_uv = g[c_uv.frame(),:](*p.coordinates(c_uv)); Q_Tp_uv
[1/2 0]
[ 0 1/2]
sage: W_Tp_uv = VectorSpace(SR, 2, inner_product_matrix=Q_Tp_uv)
sage: Tp.bases()[1]
- Basis (∂/∂u,∂/∂v) on the Tangent space at Point p on the 2-dimensional differentiable manifold M
- sage: phi_Tp_uv = Tp.isomorphism_with_fixed_basis(Tp.bases()[1], codomain=W_Tp_uv); phi_Tp_uv
+ Basis (∂/∂u,∂/∂v) on the Tangent space at Point p on the
+ 2-dimensional differentiable manifold M
+ sage: phi_Tp_uv = Tp.isomorphism_with_fixed_basis(Tp.bases()[1], codomain=W_Tp_uv)
+ sage: phi_Tp_uv
Generic morphism:
- From: Tangent space at Point p on the 2-dimensional differentiable manifold M
- To: Ambient quadratic space of dimension 2 over Symbolic Ring
- Inner product matrix:
- [1/2 0]
- [ 0 1/2]
+ From: Tangent space at Point p on the 2-dimensional differentiable manifold M
+ To: Ambient quadratic space of dimension 2 over Symbolic Ring
+ Inner product matrix:
+ [1/2 0]
+ [ 0 1/2]
sage: t1, t2 = Tp.tensor((1,0)), Tp.tensor((1,0))
sage: t1[:] = (8, 15)
@@ -211,7 +215,8 @@ class TangentSpace(FiniteRankFreeModule):
541
sage: Tp_xy_to_uv = M.change_of_frame(c_xy.frame(), c_uv.frame()).at(p); Tp_xy_to_uv
- Automorphism of the Tangent space at Point p on the 2-dimensional differentiable manifold M
+ Automorphism of the Tangent space at Point p on the
+ 2-dimensional differentiable manifold M
sage: Tp.set_change_of_basis(Tp.bases()[0], Tp.bases()[1], Tp_xy_to_uv)
sage: t1[Tp.bases()[1],:]
[23, -7]
diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py
index df54270dde0..d2135c1e5a9 100644
--- a/src/sage/manifolds/differentiable/tensorfield.py
+++ b/src/sage/manifolds/differentiable/tensorfield.py
@@ -4695,6 +4695,20 @@ def apply_map(self, fun, frame=None, chart=None,
sage: v.display(X.frame(), X)
(x + y)*(x - y) ∂/∂x + 2*pi*(pi - 1)*x ∂/∂y
+ TESTS:
+
+ Check that the cached quantities derived from the components are
+ erased::
+
+ sage: w = M.vector_field(a*x, 0)
+ sage: diff(w[[0]]).display()
+ a dx
+ sage: w.apply_map(lambda t: t.subs(a=-2))
+ sage: w.display()
+ -2*x ∂/∂x
+ sage: diff(w[[0]]).display()
+ -2 dx
+
"""
# The dictionary of components w.r.t. frame:
if keep_other_components:
@@ -4712,3 +4726,4 @@ def apply_map(self, fun, frame=None, chart=None,
for ch, fct in scalar._express.items():
cfunc_dict[ch] = ch.function(fun(fct.expr()))
scalar._express = cfunc_dict
+ scalar._del_derived()
diff --git a/src/sage/manifolds/differentiable/vector_bundle.py b/src/sage/manifolds/differentiable/vector_bundle.py
index 18d1169064d..36a3c48c712 100644
--- a/src/sage/manifolds/differentiable/vector_bundle.py
+++ b/src/sage/manifolds/differentiable/vector_bundle.py
@@ -768,7 +768,7 @@ def set_change_of_frame(self, frame1, frame2, change_of_frame,
:class:`~sage.tensor.modules.free_module_automorphism.FreeModuleAutomorphism`
describing the automorphism `P` that relates the basis `(e_i)` to
the basis `(f_i)` according to `f_i = P(e_i)`
- - ``compute_inverse`` (default: True) -- if set to True, the inverse
+ - ``compute_inverse`` (default: ``True``) -- if set to ``True``, the inverse
automorphism is computed and the change from basis `(f_i)` to `(e_i)`
is set to it in the internal dictionary ``self._frame_changes``
@@ -1476,7 +1476,7 @@ def ambient_domain(self):
sage: Phi.display()
Phi: R → M
t ↦ (x, y) = (cos(t), sin(t))
- sage: PhiT11 = R.tensor_bundle(1, 1, dest_map=Phi)
+ sage: PhiT11 = R.tensor_bundle(1, 1, dest_map=Phi)
sage: PhiT11.ambient_domain()
2-dimensional differentiable manifold M
@@ -1724,15 +1724,15 @@ def orientation(self):
[]
sage: T11.set_orientation([c_xy.frame(), c_uv.frame()])
sage: T11.orientation()
- [Coordinate frame (U, (∂/∂x,∂/∂y)), Coordinate frame
- (V, (∂/∂u,∂/∂v))]
+ [Coordinate frame (U, (∂/∂x,∂/∂y)),
+ Coordinate frame (V, (∂/∂u,∂/∂v))]
If the destination map is the identity, the orientation is
automatically set for the manifold, too::
sage: M.orientation()
- [Coordinate frame (U, (∂/∂x,∂/∂y)), Coordinate frame
- (V, (∂/∂u,∂/∂v))]
+ [Coordinate frame (U, (∂/∂x,∂/∂y)),
+ Coordinate frame (V, (∂/∂u,∂/∂v))]
Conversely, if one sets an orientation on the manifold,
the orientation on its tensor bundles is set accordingly::
@@ -1740,8 +1740,8 @@ def orientation(self):
sage: c_tz. = U.chart()
sage: M.set_orientation([c_tz, c_uv])
sage: T11.orientation()
- [Coordinate frame (U, (∂/∂t,∂/∂z)), Coordinate frame
- (V, (∂/∂u,∂/∂v))]
+ [Coordinate frame (U, (∂/∂t,∂/∂z)),
+ Coordinate frame (V, (∂/∂u,∂/∂v))]
"""
if self._dest_map.is_identity():
diff --git a/src/sage/matrix/args.pxd b/src/sage/matrix/args.pxd
index 9ab004e1887..8deb4d434c9 100644
--- a/src/sage/matrix/args.pxd
+++ b/src/sage/matrix/args.pxd
@@ -47,6 +47,7 @@ cdef class MatrixArgs:
cdef public Parent space # parent of matrix
cdef public Parent base # parent of entries
cdef public long nrows, ncols
+ cdef public object row_keys, column_keys
cdef public object entries
cdef entries_type typ
cdef public bint sparse
@@ -54,6 +55,7 @@ cdef class MatrixArgs:
cdef bint is_finalized
cpdef Matrix matrix(self, bint convert=?)
+ cpdef element(self, bint immutable=?)
cpdef list list(self, bint convert=?)
cpdef dict dict(self, bint convert=?)
@@ -89,7 +91,11 @@ cdef class MatrixArgs:
raise ArithmeticError("number of columns must be non-negative")
cdef long p = self.ncols
if p != -1 and p != n:
- raise ValueError(f"inconsistent number of columns: should be {p} but got {n}")
+ raise ValueError(f"inconsistent number of columns: should be {p} "
+ f"but got {n}")
+ if self.column_keys is not None and n != len(self.column_keys):
+ raise ValueError(f"inconsistent number of columns: should be cardinality of {self.column_keys} "
+ f"but got {n}")
self.ncols = n
cdef inline int set_nrows(self, long n) except -1:
@@ -102,8 +108,23 @@ cdef class MatrixArgs:
cdef long p = self.nrows
if p != -1 and p != n:
raise ValueError(f"inconsistent number of rows: should be {p} but got {n}")
+ if self.row_keys is not None and n != len(self.row_keys):
+ raise ValueError(f"inconsistent number of rows: should be cardinality of {self.row_keys} "
+ f"but got {n}")
self.nrows = n
+ cdef inline int _ensure_nrows_ncols(self) except -1:
+ r"""
+ Make sure that the number of rows and columns is set.
+ If ``row_keys`` or ``column_keys`` is not finite, this can raise an exception.
+ """
+ if self.nrows == -1:
+ self.nrows = len(self.row_keys)
+ if self.ncols == -1:
+ self.ncols = len(self.column_keys)
+
+ cpdef int set_column_keys(self, column_keys) except -1
+ cpdef int set_row_keys(self, row_keys) except -1
cpdef int set_space(self, space) except -1
cdef int finalize(self) except -1
diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx
index 5613a63dfd5..4f578440df6 100644
--- a/src/sage/matrix/args.pyx
+++ b/src/sage/matrix/args.pyx
@@ -312,7 +312,8 @@ cdef class MatrixArgs:
self.sparse = -1
self.kwds = {}
- def __init__(self, *args, base_ring=None, nrows=None, ncols=None, entries=None, sparse=None, space=None, **kwds):
+ def __init__(self, *args, base_ring=None, nrows=None, ncols=None, entries=None,
+ sparse=None, row_keys=None, column_keys=None, space=None, **kwds):
"""
Parse arguments for creating a new matrix.
@@ -360,6 +361,10 @@ cdef class MatrixArgs:
self.entries = entries
if sparse is not None:
self.sparse = sparse
+ if row_keys is not None:
+ self.set_row_keys(row_keys)
+ if column_keys is not None:
+ self.set_column_keys(column_keys)
if space is not None:
self.set_space(space)
self.kwds.update(kwds)
@@ -375,15 +380,21 @@ cdef class MatrixArgs:
if self.entries is None and isinstance(arg, (list, tuple, dict)):
self.entries = arg
argc -= 1
- if argi == argc: return
+ if argi == argc:
+ return
# check for base ring argument
if self.base is None and isinstance(args[argi], Parent):
self.base = args[argi]
argi += 1
- if argi == argc: return
-
- # check nrows and ncols argument
+ if argi == argc:
+ return
+
+ # check positional nrows and ncols argument
+ # even if redundant with row_keys, column_keys given as keywords;
+ # but do not check for positional row_keys, column_keys arguments
+ # -- we do not allow those, as they would be too easy to
+ # confuse with entries
cdef int k
cdef long v
if self.nrows == -1 and self.ncols == -1:
@@ -403,7 +414,8 @@ cdef class MatrixArgs:
else:
self.set_ncols(v)
argi += 1
- if argi == argc: return
+ if argi == argc:
+ return
# check for entries argument
if self.entries is None:
@@ -550,12 +562,14 @@ cdef class MatrixArgs:
if sparse:
pass
else:
+ self._ensure_nrows_ncols()
zero = self.base.zero()
for i in range(self.nrows):
for j in range(self.ncols):
sig_check()
yield zero
elif self.typ == MA_ENTRIES_SCALAR:
+ self._ensure_nrows_ncols()
diag = self.entries
if convert and self.need_to_convert(diag):
diag = self.base(diag)
@@ -570,6 +584,7 @@ cdef class MatrixArgs:
sig_check()
yield diag if (i == j) else zero
elif self.typ == MA_ENTRIES_SEQ_SEQ:
+ self._ensure_nrows_ncols()
row_iter = sized_iter(self.entries, self.nrows)
for i in range(self.nrows):
row = sized_iter(next(row_iter), self.ncols)
@@ -583,6 +598,7 @@ cdef class MatrixArgs:
else:
yield x
elif self.typ == MA_ENTRIES_SEQ_FLAT:
+ self._ensure_nrows_ncols()
it = sized_iter(self.entries, self.nrows * self.ncols)
for i in range(self.nrows):
for j in range(self.ncols):
@@ -606,8 +622,14 @@ cdef class MatrixArgs:
raise TypeError("dense iteration is not supported for sparse input")
elif self.typ == MA_ENTRIES_CALLABLE:
f = self.entries
- for i in range(self.nrows):
- for j in range(self.ncols):
+ row_keys = self.row_keys
+ if row_keys is None:
+ row_keys = range(self.nrows)
+ column_keys = self.column_keys
+ if column_keys is None:
+ column_keys = range(self.ncols)
+ for i in row_keys:
+ for j in column_keys:
sig_check()
x = f(i, j)
if convert and self.need_to_convert(x):
@@ -640,6 +662,7 @@ cdef class MatrixArgs:
42
"""
self.finalize()
+ self._ensure_nrows_ncols()
return self.nrows * self.ncols
cpdef Matrix matrix(self, bint convert=True):
@@ -726,13 +749,67 @@ cdef class MatrixArgs:
M = M.__copy__()
break
else:
- M = self.space(self, coerce=convert)
+ space = self.space
+ if not isinstance(space, MatrixSpace):
+ space = space.zero().matrix(side='left').parent()
+ M = space(self, coerce=convert)
# Also store the matrix to support multiple calls of matrix()
self.entries = M
self.typ = MA_ENTRIES_MATRIX
return M
+ cpdef element(self, bint immutable=False):
+ r"""
+ Return the matrix or morphism.
+
+ INPUT:
+
+ - ``immutable`` -- boolean; if ``True``, the result will be immutable.
+
+ OUTPUT: an element of ``self.space``
+
+ .. NOTE::
+
+ This may change ``self.entries``, making it unsafe to access the
+ ``self.entries`` attribute after calling this method.
+
+ EXAMPLES::
+
+ sage: from sage.matrix.args import MatrixArgs
+ sage: M = matrix(2, 3, range(6), sparse=True)
+ sage: ma = MatrixArgs(M); ma.finalized()
+
+ sage: M2 = ma.element(immutable=True); M2.parent()
+ Full MatrixSpace of 2 by 3 sparse matrices over Integer Ring
+ sage: M2.is_immutable()
+ True
+
+ sage: ma = MatrixArgs(M, row_keys=['u','v'], column_keys=['a','b','c'])
+ sage: ma.finalized()
+
+ sage: phi = ma.element(); phi
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'u', 'v'} over Integer Ring
+ """
+ self.finalize()
+ cdef Matrix M = self.matrix(convert=True)
+ if immutable:
+ M.set_immutable()
+ if isinstance(self.space, MatrixSpace):
+ return M
+ return self.space(matrix=M, side='left')
+
cpdef list list(self, bint convert=True):
"""
Return the entries of the matrix as a flat list of scalars.
@@ -835,13 +912,86 @@ cdef class MatrixArgs:
D[se.i, se.j] = x
return D
+ cpdef int set_column_keys(self, column_keys) except -1:
+ """
+ Set the column keys with consistency checking.
+
+ If the value was previously set, it must remain the same.
+
+ EXAMPLES::
+
+ sage: from sage.matrix.args import MatrixArgs
+ sage: ma = MatrixArgs(2, 4)
+ sage: ma.set_column_keys('xyz')
+ Traceback (most recent call last):
+ ...
+ ValueError: inconsistent column keys: should be of cardinality 4 but got xyz
+ sage: ma.set_column_keys('abcd')
+ 0
+ sage: ma.finalized()
+
+ """
+ if self.column_keys is not None and self.column_keys != column_keys:
+ raise ValueError(f"inconsistent column keys: should be {self.column_keys} "
+ f"but got {column_keys}")
+ cdef long p = self.ncols
+ if p != -1 and p != len(column_keys):
+ raise ValueError(f"inconsistent column keys: should be of cardinality {self.ncols} "
+ f"but got {column_keys}")
+ self.column_keys = column_keys
+
+ cpdef int set_row_keys(self, row_keys) except -1:
+ """
+ Set the row keys with consistency checking.
+
+ If the value was previously set, it must remain the same.
+
+ EXAMPLES::
+
+ sage: from sage.matrix.args import MatrixArgs
+ sage: ma = MatrixArgs(2, 4)
+ sage: ma.set_row_keys('xyz')
+ Traceback (most recent call last):
+ ...
+ ValueError: inconsistent row keys: should be of cardinality 2 but got xyz
+ sage: ma.set_row_keys(['u', 'v'])
+ 0
+ sage: ma.finalized()
+
+ """
+ if self.row_keys is not None and self.row_keys != row_keys:
+ raise ValueError(f"inconsistent row keys: should be {self.row_keys} "
+ f"but got {row_keys}")
+ if self.nrows != -1 and self.nrows != len(row_keys):
+ raise ValueError(f"inconsistent row keys: should be of cardinality {self.nrows} "
+ f"but got {row_keys}")
+ self.row_keys = row_keys
+
cpdef int set_space(self, space) except -1:
"""
Set inputs from a given matrix space.
INPUT:
- - ``space`` -- a :class:`MatrixSpace`
+ - ``space`` -- a :class:`MatrixSpace` or a homset of modules with basis
EXAMPLES::
@@ -858,12 +1008,37 @@ cdef class MatrixArgs:
[0 0]
sage: M.parent() is S
True
+
+ From a homset::
+
+ sage: C = CombinatorialFreeModule(ZZ, ['a', 'b', 'c'])
+ sage: R = CombinatorialFreeModule(ZZ, ['u', 'v'])
+ sage: S = Hom(C, R); S
+ Set of Morphisms
+ from Free module generated by {'a', 'b', 'c'} over Integer Ring
+ to Free module generated by {'u', 'v'} over Integer Ring
+ in Category of finite dimensional modules with basis over Integer Ring
+ sage: ma = MatrixArgs()
+ sage: _ = ma.set_space(S)
+ sage: ma.finalized()
+
"""
+ if self.space is not None:
+ return 0 # TODO: ??????
self.space = space
- self.set_nrows(space.nrows())
- self.set_ncols(space.ncols())
- self.base = space._base
- self.sparse = space.is_sparse()
+ try:
+ self.set_nrows(space.nrows())
+ self.set_ncols(space.ncols())
+ self.base = space._base
+ self.sparse = space.is_sparse()
+ except AttributeError:
+ self.set_row_keys(space.codomain().basis().keys())
+ self.set_column_keys(space.domain().basis().keys())
+ self.base = space.base_ring()
def finalized(self):
"""
@@ -945,6 +1120,7 @@ cdef class MatrixArgs:
# Can we assume a square matrix?
if self.typ & MA_FLAG_ASSUME_SQUARE:
+ # TODO: Handle column_keys/row_keys
if self.ncols == -1:
if self.nrows != -1:
self.ncols = self.nrows
@@ -977,7 +1153,7 @@ cdef class MatrixArgs:
# Error if size is required
if self.typ & MA_FLAG_DIM_REQUIRED:
- if self.nrows == -1 or self.ncols == -1:
+ if (self.nrows == -1 and self.row_keys is None) or (self.ncols == -1 and self.column_keys is None):
raise TypeError("the dimensions of the matrix must be specified")
# Determine base in easy cases
@@ -993,7 +1169,9 @@ cdef class MatrixArgs:
if self.base is None:
raise TypeError(f"unable to determine base of {self.entries!r}")
- if self.nrows == -1 or self.ncols == -1 or self.base is None:
+ if ((self.nrows == -1 and self.row_keys is None)
+ or (self.ncols == -1 and self.column_keys is None)
+ or self.base is None):
# Determine dimensions or base in the cases where we
# really need to look at the entries.
if self.typ == MA_ENTRIES_SEQ_SEQ:
@@ -1013,9 +1191,9 @@ cdef class MatrixArgs:
self.typ = MA_ENTRIES_ZERO
except Exception:
# "not self.entries" has failed, self.entries cannot be determined to be zero
- if self.nrows != self.ncols:
+ if self.nrows != self.ncols or self.row_keys != self.column_keys:
raise TypeError("scalar matrix must be square if the value cannot be determined to be zero")
- if self.typ == MA_ENTRIES_SCALAR and self.nrows != self.ncols:
+ if self.typ == MA_ENTRIES_SCALAR and (self.nrows != self.ncols or self.row_keys != self.column_keys):
# self.typ is still SCALAR -> "not self.entries" has successfully evaluated, to False
raise TypeError("nonzero scalar matrix must be square")
@@ -1026,8 +1204,17 @@ cdef class MatrixArgs:
global MatrixSpace
if MatrixSpace is None:
from sage.matrix.matrix_space import MatrixSpace
- self.space = MatrixSpace(self.base, self.nrows, self.ncols,
- sparse=self.sparse, **self.kwds)
+ nrows = self.nrows
+ if nrows == -1:
+ nrows = None
+ ncols = self.ncols
+ if ncols == -1:
+ ncols = None
+ self.space = MatrixSpace(self.base, nrows, ncols,
+ sparse=self.sparse,
+ row_keys=self.row_keys,
+ column_keys=self.column_keys,
+ **self.kwds)
self.is_finalized = True
@@ -1197,10 +1384,11 @@ cdef class MatrixArgs:
e = PySequence_Fast(self.entries, "not a sequence")
self.set_nrows(len(e))
if self.nrows == 0:
- if self.ncols == -1: self.ncols = 0
+ if self.ncols == -1 and self.column_keys is None:
+ self.set_ncols(0)
self.setdefault_base(ZZ)
return 0
- elif self.ncols != -1 and self.base is not None:
+ elif (self.ncols != -1 or self.column_keys is not None) and self.base is not None:
# Everything known => OK
return 0
@@ -1246,14 +1434,20 @@ cdef class MatrixArgs:
self.nrows = 0
if self.ncols == -1:
self.ncols = 0
- elif self.ncols == -1:
+ elif self.ncols == -1 and self.column_keys is None:
if self.nrows == -1:
- # Assume row matrix
- self.nrows = 1
- self.ncols = N
+ if self.row_keys is None:
+ # Assume row matrix
+ self.nrows = 1
+ self.ncols = N
+ else:
+ self.nrows = len(self.row_keys)
+ self.ncols = N // self.nrows
else:
self.ncols = N // self.nrows
- elif self.nrows == -1:
+ elif self.nrows == -1 and self.row_keys is None:
+ if self.ncols == -1:
+ self.ncols = len(self.column_keys)
self.nrows = N // self.ncols
self.set_seq_flat(entries)
diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx
index 6e2e4f7dda8..f343fd82578 100644
--- a/src/sage/matrix/constructor.pyx
+++ b/src/sage/matrix/constructor.pyx
@@ -62,14 +62,17 @@ def matrix(*args, **kwds):
determine this from the given entries, falling back to ``ZZ`` if
no entries are given.
- - ``nrows`` -- the number of rows in the matrix.
+ - ``nrows`` -- the number of rows in the matrix, or a finite or
+ enumerated family of arbitrary objects that index the rows of the matrix
- - ``ncols`` -- the number of columns in the matrix.
+ - ``ncols`` -- the number of columns in the matrix, or a finite or
+ enumerated family of arbitrary objects that index the columns of the matrix
- ``entries`` -- see examples below.
- If either ``nrows`` or ``ncols`` is given as keyword argument, then
- no positional arguments ``nrows`` and ``ncols`` may be given.
+ If any of ``nrows``, ``ncols``, ``row_keys``, ``column_keys`` is
+ given as keyword argument, then none of these may be given as
+ positional arguments.
Keyword-only arguments:
@@ -77,17 +80,22 @@ def matrix(*args, **kwds):
``True`` when the entries are given as a dictionary, otherwise
defaults to ``False``.
+ - ``row_keys`` -- a finite or enumerated family of arbitrary objects
+ that index the rows of the matrix
+
+ - ``column_keys`` -- a finite or enumerated family of arbitrary objects
+ that index the columns of the matrix
+
- ``space`` -- matrix space which will be the parent of the output
- matrix. This determines ``base_ring``, ``nrows``, ``ncols`` and
- ``sparse``.
+ matrix. This determines ``base_ring``, ``nrows``, ``row_keys``,
+ ``ncols``, ``column_keys``, and ``sparse``.
- ``immutable`` -- (boolean) make the matrix immutable. By default,
the output matrix is mutable.
- OUTPUT:
-
- a matrix
+ OUTPUT: a matrix or, more generally, a homomorphism between free
+ modules
EXAMPLES::
@@ -241,6 +249,26 @@ def matrix(*args, **kwds):
ValueError: matrix is immutable; please change a copy instead
(i.e., use copy(M) to change a copy of M).
+ Using ``row_keys`` and ``column_keys``::
+
+ sage: M = matrix([[1,2,3], [4,5,6]],
+ ....: column_keys=['a','b','c'], row_keys=['u','v']); M
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'u', 'v'} over Integer Ring
+ sage: print(M._unicode_art_matrix())
+ a b c
+ u⎛1 2 3⎞
+ v⎝4 5 6⎠
+
+ It is allowed to specify dimensions redundantly::
+
+ sage: M = matrix(2, 3, [[1,2,3], [4,5,6]],
+ ....: column_keys=['a','b','c'], row_keys=['u','v']); M
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c'} over Integer Ring
+ To: Free module generated by {'u', 'v'} over Integer Ring
+
TESTS:
There are many ways to create an empty matrix::
@@ -645,10 +673,7 @@ def matrix(*args, **kwds):
:class:`MatrixArgs`, see :issue:`24742`
"""
immutable = kwds.pop('immutable', False)
- M = MatrixArgs(*args, **kwds).matrix()
- if immutable:
- M.set_immutable()
- return M
+ return MatrixArgs(*args, **kwds).element(immutable=immutable)
Matrix = matrix
diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx
index 3dc47b9df07..c1e538a441e 100644
--- a/src/sage/matrix/matrix0.pyx
+++ b/src/sage/matrix/matrix0.pyx
@@ -1958,7 +1958,7 @@ cdef class Matrix(sage.structure.element.Matrix):
sage: K = (A - e).kernel()
sage: P = K.basis_matrix()
sage: P.str()
- '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]'
+ '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]'
Use single-row delimiters where appropriate::
diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx
index 9a7fdd393c5..e36d913bcd9 100644
--- a/src/sage/matrix/matrix2.pyx
+++ b/src/sage/matrix/matrix2.pyx
@@ -10453,23 +10453,23 @@ cdef class Matrix(Matrix1):
....: [-1, 1, -6, -6, 5]])
sage: Q, R = A.QR()
sage: Q
- [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?]
- [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?]
- [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?]
- [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?]
+ [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?]
+ [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?]
+ [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?]
+ [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?]
[ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?]
sage: R
[ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?]
[ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?]
- [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?]
- [ 0 0 0 1.027626039419836? -3.619300149686620?]
- [ 0 0 0 0 0.024551430807012?]
+ [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?]
+ [ 0 0 0 1.027626039419836? -3.61930014968662?]
+ [ 0 0 0 0 0.02455143080702?]
sage: Q.conjugate_transpose()*Q
- [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13]
- [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13]
- [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13]
- [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13]
- [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?]
+ [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12]
+ [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12]
+ [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12]
+ [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12]
+ [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?]
sage: Q * R == A
True
@@ -10485,24 +10485,24 @@ cdef class Matrix(Matrix1):
sage: Q, R = A.QR()
sage: Q
[ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I]
- [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I]
- [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I]
+ [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I]
+ [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I]
[ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I]
sage: R
[ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I]
- [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I]
+ [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I]
[ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I]
[ 0 0 0 1.942963944258992? + 0.?e-16*I]
sage: Q.conjugate_transpose()*Q
[1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I]
[ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I]
- [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I]
- [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I]
+ [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I]
+ [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I]
sage: Q*R - A
[ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I]
- [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I]
+ [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I]
[0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I]
- [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I]
+ [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I]
A rank-deficient rectangular matrix, with both values of the ``full`` keyword. ::
@@ -12192,9 +12192,9 @@ cdef class Matrix(Matrix1):
sage: # needs sage.combinat sage.libs.pari
sage: _, T = A.is_similar(B, transformation=True)
sage: T
- [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I]
- [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I]
- [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I]
+ [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I]
+ [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I]
+ [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I]
sage: T.change_ring(QQ)
[ 1 0 0]
[-2/3 1/6 -5/6]
@@ -18431,12 +18431,13 @@ def _generic_clear_column(m):
I = ideal_or_fractional(R, a[0, 0]) # need to make sure we change this when a[0,0] changes
for k in range(1, a.nrows()):
if a[k, 0] not in I:
+ new_ideal = ideal_or_fractional(R, a[0, 0], a[k, 0])
try:
- v = ideal_or_fractional(R, a[0, 0], a[k, 0]).gens_reduced()
+ v = new_ideal.gens_reduced()
except Exception as msg:
raise ArithmeticError("%s\nCan't create ideal on %s and %s" % (msg, a[0, 0], a[k, 0]))
if len(v) > 1:
- raise ArithmeticError("Ideal %s not principal" % ideal_or_fractional(R, a[0, 0], a[k, 0]))
+ raise ArithmeticError("Ideal %s not principal" % new_ideal)
B = v[0]
# now we find c,d, using the fact that c * (a_{0,0}/B) - d *
diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py
index f7efb5432c8..5d0db5fb742 100644
--- a/src/sage/matrix/matrix_space.py
+++ b/src/sage/matrix/matrix_space.py
@@ -58,6 +58,7 @@
from sage.features import PythonModule
lazy_import('sage.matrix.matrix_gfpn_dense', ['Matrix_gfpn_dense'],
feature=PythonModule('sage.matrix.matrix_gfpn_dense', spkg='meataxe'))
+lazy_import('sage.groups.matrix_gps.matrix_group', ['MatrixGroup_base'])
_Rings = Rings()
_Fields = Fields()
@@ -468,7 +469,7 @@ class MatrixSpace(UniqueRepresentation, Parent):
- ``'numpy'`` -- for real and complex floating point numbers
- OUTPUT: a matrix space or, more generally, a homspace between free modules.
+ OUTPUT: a matrix space or, more generally, a homspace between free modules
This factory function creates instances of various specialized classes
depending on the input. Not all combinations of options are
@@ -493,10 +494,12 @@ class MatrixSpace(UniqueRepresentation, Parent):
sage: MatrixSpace(ZZ, 10, 5).category()
Category of infinite enumerated finite dimensional modules with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: MatrixSpace(ZZ, 10, 10).category()
Category of infinite enumerated finite dimensional algebras with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: MatrixSpace(QQ, 10).category()
Category of infinite finite dimensional algebras with basis over
@@ -554,10 +557,12 @@ class MatrixSpace(UniqueRepresentation, Parent):
sage: MatrixSpace(ZZ, 10, 5).category()
Category of infinite enumerated finite dimensional modules with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: MatrixSpace(ZZ, 10, 10).category()
Category of infinite enumerated finite dimensional algebras with basis over
(Dedekind domains and euclidean domains
+ and noetherian rings
and infinite enumerated sets and metric spaces)
sage: MatrixSpace(QQ, 10).category()
Category of infinite finite dimensional algebras with basis over
@@ -777,6 +782,61 @@ def __classcall__(cls, base_ring,
def __init__(self, base_ring, nrows, ncols, sparse, implementation):
r"""
+ INPUT:
+
+ - ``base_ring``
+
+ - ``nrows`` -- (positive integer) the number of rows
+
+ - ``ncols`` -- (positive integer, default nrows) the number of
+ columns
+
+ - ``sparse`` -- (boolean, default ``False``) whether or not matrices
+ are given a sparse representation
+
+ - ``implementation`` -- (optional, a string or a matrix class) a possible
+ implementation. Depending on the base ring the string can be
+
+ - ``'generic'`` -- on any base rings
+
+ - ``'flint'`` -- for integers and rationals
+
+ - ``'meataxe'`` -- finite fields, needs to install the optional package meataxe
+
+ - ``m4ri`` -- for characteristic 2 using M4RI library
+
+ - ``linbox-float`` -- for integer mod rings up to `2^8 = 256`
+
+ - ``linbox-double`` -- for integer mod rings up to
+ `floor(2^26*sqrt(2) + 1/2) = 94906266`
+
+ - ``numpy`` -- for real and complex floating point numbers
+
+ EXAMPLES::
+
+ sage: MatrixSpace(QQ, 2)
+ Full MatrixSpace of 2 by 2 dense matrices over Rational Field
+ sage: MatrixSpace(ZZ, 3, 2)
+ Full MatrixSpace of 3 by 2 dense matrices over Integer Ring
+ sage: MatrixSpace(ZZ, 3, sparse=False)
+ Full MatrixSpace of 3 by 3 dense matrices over Integer Ring
+
+ sage: MatrixSpace(ZZ,10,5)
+ Full MatrixSpace of 10 by 5 dense matrices over Integer Ring
+ sage: MatrixSpace(ZZ,10,5).category()
+ Category of infinite enumerated finite dimensional modules with basis over
+ (Dedekind domains and euclidean domains
+ and noetherian rings
+ and infinite enumerated sets and metric spaces)
+ sage: MatrixSpace(ZZ,10,10).category()
+ Category of infinite enumerated finite dimensional algebras with basis over
+ (Dedekind domains and euclidean domains
+ and noetherian rings
+ and infinite enumerated sets and metric spaces)
+ sage: MatrixSpace(QQ,10).category()
+ Category of infinite finite dimensional algebras with basis over
+ (number fields and quotient fields and metric spaces)
+
TESTS:
We test that in the real or complex double dense case,
@@ -1392,14 +1452,8 @@ def _coerce_map_from_(self, S):
pass
else:
MS = meth_matrix_space()
-
- try:
- from sage.groups.matrix_gps.matrix_group import is_MatrixGroup
- except ImportError:
- pass
- else:
- if is_MatrixGroup(S):
- return self.has_coerce_map_from(MS)
+ if isinstance(S, MatrixGroup_base):
+ return self.has_coerce_map_from(MS)
try:
from sage.modular.arithgroup.arithgroup_generic import is_ArithmeticSubgroup
diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx
index 30038d1d3c0..7e6d565a868 100644
--- a/src/sage/matroids/basis_exchange_matroid.pyx
+++ b/src/sage/matroids/basis_exchange_matroid.pyx
@@ -1923,7 +1923,7 @@ cdef class BasisExchangeMatroid(Matroid):
sage: M = matroids.catalog.N1()
sage: M._characteristic_setsystem()
- Iterator over a system of subsets
+ SetSystem of 23 sets over 10 elements
sage: len(M._characteristic_setsystem())
23
"""
diff --git a/src/sage/matroids/circuits_matroid.pyx b/src/sage/matroids/circuits_matroid.pyx
index 6beaf02d379..aec280a35f7 100644
--- a/src/sage/matroids/circuits_matroid.pyx
+++ b/src/sage/matroids/circuits_matroid.pyx
@@ -481,7 +481,7 @@ cdef class CircuitsMatroid(Matroid):
sage: from sage.matroids.circuits_matroid import CircuitsMatroid
sage: M = CircuitsMatroid(matroids.Uniform(2, 4))
sage: M.circuits()
- Iterator over a system of subsets
+ SetSystem of 4 sets over 4 elements
sage: list(M.circuits(0))
[]
sage: sorted(M.circuits(3), key=str)
@@ -544,7 +544,7 @@ cdef class CircuitsMatroid(Matroid):
sage: from sage.matroids.circuits_matroid import CircuitsMatroid
sage: M = CircuitsMatroid(matroids.Uniform(2, 4))
sage: M.nonspanning_circuits()
- Iterator over a system of subsets
+ SetSystem of 0 sets over 4 elements
"""
cdef list NSC = []
for i in self._k_C:
diff --git a/src/sage/matroids/constructor.py b/src/sage/matroids/constructor.py
index cecfead568a..07e8597fdb8 100644
--- a/src/sage/matroids/constructor.py
+++ b/src/sage/matroids/constructor.py
@@ -188,6 +188,8 @@ def Matroid(groundset=None, data=None, **kwds):
``reduced_matrix = A``
then the matroid is represented by `[I\ \ A]` where `I` is an
appropriately sized identity matrix.
+ - ``morphism`` -- A morphism representation of the matroid.
+ - ``reduced_morphism`` -- A reduced morphism representation of the matroid.
- ``rank_function`` -- A function that computes the rank of each subset.
Can only be provided together with a groundset.
- ``circuit_closures`` -- Either a list of tuples ``(k, C)`` with ``C``
@@ -533,6 +535,48 @@ def Matroid(groundset=None, data=None, **kwds):
sage: M.base_ring()
Integer Ring
+ A morphism representation of a :class:`LinearMatroid` can also be used as
+ input::
+
+ sage: M = matroids.catalog.Fano()
+ sage: A = M.representation(order=True); A
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'} over
+ Finite Field of size 2
+ To: Free module generated by {0, 1, 2} over Finite Field of size 2
+ sage: A._unicode_art_matrix()
+ a b c d e f g
+ 0⎛1 0 0 0 1 1 1⎞
+ 1⎜0 1 0 1 0 1 1⎟
+ 2⎝0 0 1 1 1 0 1⎠
+ sage: N = Matroid(A); N
+ Binary matroid of rank 3 on 7 elements, type (3, 0)
+ sage: N.groundset()
+ frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'})
+ sage: M == N
+ True
+
+ The keywords ``morphism`` and ``reduced_morphism`` are also available::
+
+ sage: M = matroids.catalog.RelaxedNonFano("abcdefg")
+ sage: A = M.representation(order=True, reduced=True); A
+ Generic morphism:
+ From: Free module generated by {'d', 'e', 'f', 'g'} over
+ Finite Field in w of size 2^2
+ To: Free module generated by {'a', 'b', 'c'} over
+ Finite Field in w of size 2^2
+ sage: A._unicode_art_matrix()
+ d e f g
+ a⎛1 1 0 1⎞
+ b⎜1 0 1 1⎟
+ c⎝0 1 w 1⎠
+ sage: N = Matroid(reduced_morphism=A); N
+ Quaternary matroid of rank 3 on 7 elements
+ sage: N.groundset()
+ frozenset({'a', 'b', 'c', 'd', 'e', 'f', 'g'})
+ sage: M == N
+ True
+
#. Rank function:
Any function mapping subsets to integers can be used as input::
@@ -713,8 +757,8 @@ def Matroid(groundset=None, data=None, **kwds):
if data is None:
for k in ['bases', 'independent_sets', 'circuits',
'nonspanning_circuits', 'flats', 'graph', 'matrix',
- 'reduced_matrix', 'rank_function', 'revlex',
- 'circuit_closures', 'matroid']:
+ 'reduced_matrix', 'morphism', 'reduced_morphism',
+ 'rank_function', 'revlex', 'circuit_closures', 'matroid']:
if k in kwds:
data = kwds.pop(k)
key = k
@@ -732,8 +776,13 @@ def Matroid(groundset=None, data=None, **kwds):
Graph = ()
if isinstance(data, Graph):
key = 'graph'
- elif is_Matrix(data):
+ elif is_Matrix(data) or (
+ isinstance(data, tuple) and is_Matrix(data[0])):
key = 'matrix'
+ elif isinstance(data, sage.modules.with_basis.morphism.ModuleMorphism) or (
+ isinstance(data, tuple) and
+ isinstance(data[0], sage.modules.with_basis.morphism.ModuleMorphism)):
+ key = 'morphism'
elif isinstance(data, sage.matroids.matroid.Matroid):
key = 'matroid'
elif isinstance(data, str):
@@ -856,9 +905,22 @@ def Matroid(groundset=None, data=None, **kwds):
M = GraphicMatroid(G, groundset=groundset)
# Matrices:
- elif key in ['matrix', 'reduced_matrix']:
+ elif key in ['matrix', 'reduced_matrix', 'morphism', 'reduced_morphism']:
A = data
- is_reduced = (key == 'reduced_matrix')
+ is_reduced = (key == 'reduced_matrix' or key == 'reduced_morphism')
+ if isinstance(data, tuple):
+ A = data[0]
+ if key == 'matrix' or key == 'reduced_matrix':
+ if groundset is None:
+ groundset = data[1]
+ if is_reduced:
+ groundset += data[2]
+ if key == 'morphism' or key == 'reduced_morphism':
+ if groundset is None:
+ groundset = list(A.domain().basis().keys())
+ if is_reduced:
+ groundset = list(A.codomain().basis().keys()) + groundset
+ A = A.matrix()
# Fix the representation
if not is_Matrix(A):
diff --git a/src/sage/matroids/graphic_matroid.pxd b/src/sage/matroids/graphic_matroid.pxd
new file mode 100644
index 00000000000..ced452ad963
--- /dev/null
+++ b/src/sage/matroids/graphic_matroid.pxd
@@ -0,0 +1,35 @@
+from .matroid cimport Matroid
+from sage.graphs.generic_graph_pyx cimport GenericGraph_pyx
+
+cdef class GraphicMatroid(Matroid):
+ cdef frozenset _groundset
+ cdef readonly GenericGraph_pyx _G
+ cdef dict _vertex_map
+ cdef dict _groundset_edge_map
+ cpdef groundset(self)
+ cpdef _rank(self, X)
+ cpdef _vertex_stars(self)
+ cpdef _minor(self, contractions, deletions)
+ cpdef _has_minor(self, N, bint certificate=*)
+ cpdef _corank(self, X)
+ cpdef _is_circuit(self, X)
+ cpdef _closure(self, X)
+ cpdef _max_independent(self, X)
+ cpdef _max_coindependent(self, X)
+ cpdef _circuit(self, X)
+ cpdef _coclosure(self, X)
+ cpdef _is_closed(self, X)
+ cpdef _is_isomorphic(self, other, certificate=*)
+ cpdef _isomorphism(self, other)
+ cpdef is_valid(self)
+ cpdef graph(self)
+ cpdef vertex_map(self)
+ cpdef groundset_to_edges(self, X)
+ cpdef _groundset_to_edges(self, X)
+ cpdef subgraph_from_set(self, X)
+ cpdef _subgraph_from_set(self, X)
+ cpdef graphic_extension(self, u, v=*, element=*)
+ cpdef graphic_coextension(self, u, v=*, X=*, element=*)
+ cpdef twist(self, X)
+ cpdef one_sum(self, X, u, v)
+ cpdef regular_matroid(self)
diff --git a/src/sage/matroids/graphic_matroid.py b/src/sage/matroids/graphic_matroid.pyx
similarity index 87%
rename from src/sage/matroids/graphic_matroid.py
rename to src/sage/matroids/graphic_matroid.pyx
index 76447037974..295034259a4 100644
--- a/src/sage/matroids/graphic_matroid.py
+++ b/src/sage/matroids/graphic_matroid.pyx
@@ -22,9 +22,9 @@
Graphic matroids do not have a representation matrix or any of the
functionality of regular matroids. It is possible to get an instance of the
-:class:`~sage.matroids.linear_matroid.RegularMatroid` class
-by using the ``regular`` keyword when constructing the matroid.
-It is also possible to cast a GraphicMatroid as a RegularMatroid with the
+:class:`~sage.matroids.linear_matroid.RegularMatroid` class by using the
+``regular`` keyword when constructing the matroid. It is also possible to cast
+a class:`GraphicMatroid` as a class:`RegularMatroid` with the
:meth:`~sage.matroids.graphic_matroids.GraphicMatroid.regular_matroid`
method::
@@ -74,10 +74,8 @@
AUTHORS:
- Zachary Gershkoff (2017-07-07): initial version
-
-Methods
-=======
"""
+
# ****************************************************************************
# Copyright (C) 2017 Zachary Gershkoff
#
@@ -87,32 +85,29 @@
# the License, or (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************
-from .matroid import Matroid
+from .matroid cimport Matroid
from copy import copy, deepcopy
from .utilities import newlabel, split_vertex, sanitize_contractions_deletions
from itertools import combinations
from sage.rings.integer import Integer
+from sage.sets.disjoint_set cimport DisjointSet_of_hashables
-
-class GraphicMatroid(Matroid):
+cdef class GraphicMatroid(Matroid):
r"""
The graphic matroid class.
INPUT:
- - ``G`` -- a Graph
- - ``groundset`` -- (optional) a list in 1-1 correspondence with
- ``G.edge_iterator()``
+ - ``G`` -- class:`Graph`
+ - ``groundset`` -- list (optional); in 1-1 correspondence with ``G.edge_iterator()``
- OUTPUT: a ``GraphicMatroid`` instance where the groundset elements are the
- edges of ``G``
+ OUTPUT: class:`GraphicMatroid` where the groundset elements are the edges of `G`
.. NOTE::
- If a disconnected graph is given as input, the instance of
- ``GraphicMatroid`` will connect the graph components and store
- this as its graph.
+ If a disconnected graph is given as input, the instance of class:`GraphicMatroid`
+ will connect the graph components and store this as its graph.
EXAMPLES::
@@ -153,7 +148,7 @@ class GraphicMatroid(Matroid):
True
"""
- # Necessary:
+ # necessary (__init__, groundset, _rank)
def __init__(self, G, groundset=None):
"""
@@ -164,8 +159,7 @@ def __init__(self, G, groundset=None):
sage: from sage.matroids.advanced import *
sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
sage: G = G1.disjoint_union(G2)
- sage: M = GraphicMatroid(G)
- sage: M
+ sage: M = GraphicMatroid(G); M
Graphic matroid of rank 5 on 8 elements
sage: M.graph()
Looped multi-graph on 6 vertices
@@ -217,16 +211,16 @@ def __init__(self, G, groundset=None):
self._vertex_map[e[1]], groundset[i]))
# If the matroid is empty, have the internal graph be a single vertex
if edge_list:
- self._G = Graph(edge_list, loops=True, multiedges=True, weighted=True,
- data_structure='static_sparse')
+ self._G = Graph(edge_list, loops=True, multiedges=True,
+ weighted=True, data_structure='static_sparse')
else:
- self._G = Graph(1, loops=True, multiedges=True, weighted=True,
- data_structure='static_sparse')
+ self._G = Graph(1, loops=True, multiedges=True,
+ weighted=True, data_structure='static_sparse')
# Map groundset elements to graph edges:
# The edge labels should already be the elements.
self._groundset_edge_map = ({l: (u, v) for (u, v, l) in self._G.edge_iterator()})
- def groundset(self):
+ cpdef groundset(self):
"""
Return the groundset of the matroid as a frozenset.
@@ -244,12 +238,12 @@ def groundset(self):
"""
return self._groundset
- def _rank(self, X):
+ cpdef _rank(self, X):
"""
Return the rank of a set ``X``.
- This method does no checking on ``X``, and
- ``X`` may be assumed to have the same interface as ``frozenset``.
+ This method does no checking on ``X``, and ``X`` may be assumed to have
+ the same interface as ``frozenset``.
INPUT:
@@ -277,18 +271,17 @@ def _rank(self, X):
sage: M.rank([0,3])
1
"""
- from sage.sets.disjoint_set import DisjointSet
-
- edges = self.groundset_to_edges(X)
- vertices = set([u for (u, v, l) in edges]).union(
- [v for (u, v, l) in edges])
+ cdef DisjointSet_of_hashables DS_vertices
+ cdef list edges = self.groundset_to_edges(X)
+ cdef set vertices = set([u for (u, v, l) in edges]).union(
+ [v for (u, v, l) in edges])
# This counts components:
- DS_vertices = DisjointSet(vertices)
+ DS_vertices = DisjointSet_of_hashables(vertices)
for (u, v, l) in edges:
DS_vertices.union(u, v)
return (len(vertices) - DS_vertices.number_of_subsets())
- # Representation:
+ # representation:
def _repr_(self):
"""
@@ -296,21 +289,19 @@ def _repr_(self):
EXAMPLES::
- sage: M = Matroid(graphs.CompleteGraph(5))
- sage: M
+ sage: M = Matroid(graphs.CompleteGraph(5)); M
Graphic matroid of rank 4 on 10 elements
sage: G = Graph([(0, 0), (0, 1), (0, 2), (1, 1), (2, 2)], loops=True)
- sage: M = Matroid(G)
- sage: M
+ sage: M = Matroid(G); M
Graphic matroid of rank 2 on 5 elements
"""
- self._mrank = str(self._rank(self._groundset))
- self._elts = str(len(self._groundset))
- return f'Graphic matroid of rank {self._mrank} on {self._elts} elements'
+ r = self._rank(self._groundset)
+ n = len(self._groundset)
+ return f'Graphic matroid of rank {r} on {n} elements'
- # Comparison:
+ # comparison:
- def _vertex_stars(self):
+ cpdef _vertex_stars(self):
"""
Compute the set of edge labels around each vertex.
@@ -380,7 +371,7 @@ def __eq__(self, other):
INPUT:
- - ``other`` -- a matroid
+ - ``other`` -- matroid
OUTPUT: ``True`` if ``self`` and ``other`` have the same graph;
``False`` otherwise
@@ -428,7 +419,7 @@ def __ne__(self, other):
INPUT:
- - ``other`` -- a matroid
+ - ``other`` -- matroid
OUTPUT: ``False`` if ``self`` and ``other`` have the same graph;
``True`` otherwise
@@ -447,7 +438,7 @@ def __ne__(self, other):
"""
return (not self == other)
- # Copying, loading, saving:
+ # copying, loading, saving
def __reduce__(self):
"""
@@ -466,30 +457,28 @@ def __reduce__(self):
version = 0
return unpickle_graphic_matroid, (version, data)
- # Overrides:
+ # overrides
- def _minor(self, contractions=frozenset([]), deletions=frozenset([])):
+ cpdef _minor(self, contractions, deletions):
"""
Return a minor.
INPUT:
- - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be
- contracted
- - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be
- deleted
+ - ``contractions`` -- frozenset; subset of ``self.groundset()`` to be contracted
+ - ``deletions`` -- frozenset; subset of ``self.groundset()`` to be deleted
Assumptions: contractions are independent, deletions are coindependent,
contractions and deletions are disjoint.
- OUTPUT: an instance of ``GraphicMatroid``
+ OUTPUT: class:`GraphicMatroid`
EXAMPLES::
sage: M = matroids.CompleteGraphic(5)
- sage: M._minor(deletions=frozenset([0,1,2]))
+ sage: M._minor(deletions=frozenset([0,1,2]), contractions=frozenset([]))
Graphic matroid of rank 4 on 7 elements
- sage: M._minor(contractions=frozenset([0,1,2]))
+ sage: M._minor(deletions=frozenset([]), contractions=frozenset([0,1,2]))
Graphic matroid of rank 1 on 7 elements
sage: M = Matroid(range(15), graphs.PetersenGraph())
sage: N = M._minor(deletions=frozenset([0, 3, 5, 9]),
@@ -497,22 +486,21 @@ def _minor(self, contractions=frozenset([]), deletions=frozenset([])):
sage: N
Graphic matroid of rank 6 on 8 elements
"""
- g = self.graph()
- cont_edges = self._groundset_to_edges(contractions)
- del_edges = self._groundset_to_edges(deletions)
+ cdef GenericGraph_pyx g = self.graph()
+ cdef list cont_edges = self._groundset_to_edges(contractions)
+ cdef list del_edges = self._groundset_to_edges(deletions)
# deletions first so contractions don't mess up the vertices
g.delete_edges(del_edges)
g.contract_edges(cont_edges)
-
return GraphicMatroid(g)
- def _has_minor(self, N, certificate=False):
+ cpdef _has_minor(self, N, bint certificate=False):
"""
- Check if the matroid has a minor isomorphic to M(H).
+ Check if the matroid has a minor isomorphic to `M(H)`.
INPUT:
- - ``N`` - a matroid
+ - ``N`` - matroid
- ``certificate`` - (default: ``False``) if ``True``, returns the
certificate isomorphism from the minor of ``self`` to ``N``
@@ -558,8 +546,8 @@ def _has_minor(self, N, certificate=False):
sage: N.has_minor(M, certificate=True)
(False, None)
- If the matroids are not 3-connected, then the default matroid algorithms
- are used::
+ If the matroids are not 3-connected, then the default matroid
+ algorithms are used::
sage: M = matroids.CompleteGraphic(6)
sage: N = Matroid(graphs.CycleGraph(4))
@@ -637,7 +625,7 @@ def _has_minor(self, N, certificate=False):
N = N.regular_matroid()
return M._has_minor(N, certificate=certificate)
- def _corank(self, X):
+ cpdef _corank(self, X):
"""
Return the corank of the set `X` in the matroid.
@@ -645,7 +633,7 @@ def _corank(self, X):
INPUT:
- - ``X`` -- an iterable container of ground set elements
+ - ``X`` -- an iterable container of groundset elements
OUTPUT: integer
@@ -657,22 +645,21 @@ def _corank(self, X):
sage: M._corank([1,2,3])
3
"""
- from sage.sets.disjoint_set import DisjointSet
-
- all_vertices = self._G.vertices(sort=False)
- not_our_edges = self.groundset_to_edges(self._groundset.difference(X))
- DS_vertices = DisjointSet(all_vertices)
+ cdef DisjointSet_of_hashables DS_vertices
+ cdef list all_vertices = self._G.vertices(sort=False)
+ cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X))
+ DS_vertices = DisjointSet_of_hashables(all_vertices)
for u, v, l in not_our_edges:
DS_vertices.union(u, v)
return len(X) - (DS_vertices.number_of_subsets() - Integer(1))
- def _is_circuit(self, X):
+ cpdef _is_circuit(self, X):
"""
Test if input is a circuit.
INPUT:
- - ``X`` -- an iterable container of ground set elements
+ - ``X`` -- an iterable container of groundset elements
OUTPUT: boolean
@@ -686,16 +673,16 @@ def _is_circuit(self, X):
sage: M._is_circuit([0,1,3])
False
"""
- g = self._subgraph_from_set(X)
+ cdef GenericGraph_pyx g = self._subgraph_from_set(X)
return g.is_cycle()
- def _closure(self, X):
+ cpdef _closure(self, X):
"""
Return the closure of a set.
INPUT:
- - ``X`` -- an iterable container of ground set elements
+ - ``X`` -- an iterable container of groundset elements
OUTPUT: a subset of the groundset as a :class:`frozenset`
@@ -720,32 +707,33 @@ def _closure(self, X):
sage: sorted(M._closure([4]))
[0, 4, 5]
"""
- X = set(X)
- Y = self.groundset().difference(X)
- edgelist = self._groundset_to_edges(Y)
- g = self._subgraph_from_set(X)
- V = g.vertices(sort=False)
- components = g.connected_components_number()
+ cdef set XX = set(X)
+ cdef frozenset Y = self._groundset.difference(XX)
+ cdef list edgelist = self._groundset_to_edges(Y)
+ cdef GenericGraph_pyx g = self._subgraph_from_set(XX)
+ cdef list V = g.vertices(sort=False)
+ cdef int components = g.connected_components_number()
+ cdef tuple e
for e in edgelist:
# a non-loop edge is in the closure iff both its vertices are
# in the induced subgraph, and the edge doesn't connect components
if e[0] in V and e[1] in V:
g.add_edge(e)
if g.connected_components_number() >= components:
- X.add(e[2])
+ XX.add(e[2])
else:
g.delete_edge(e)
# add all loops
- X.update(set([l for (u, v, l) in self._G.loops()]))
- return frozenset(X)
+ XX.update(set([l for (u, v, l) in self._G.loops()]))
+ return frozenset(XX)
- def _max_independent(self, X):
+ cpdef _max_independent(self, X):
"""
Compute a maximal independent subset.
INPUT:
- - ``X`` -- An object with Python's ``frozenset`` interface containing
+ - ``X`` -- an object with Python's ``frozenset`` interface containing
a subset of ``self.groundset()``
OUTPUT: a subset of the groundset as a :class:`frozenset`
@@ -765,27 +753,26 @@ def _max_independent(self, X):
sage: sorted(N._max_independent(frozenset(['a'])))
[]
"""
- from sage.sets.disjoint_set import DisjointSet
-
- edges = self.groundset_to_edges(X)
- vertices = set([u for (u, v, l) in edges])
+ cdef DisjointSet_of_hashables DS_vertices
+ cdef list edges = self.groundset_to_edges(X)
+ cdef set vertices = set([u for (u, v, l) in edges])
vertices.update([v for (u, v, l) in edges])
- our_set = set()
- DS_vertices = DisjointSet(vertices)
+ cdef set our_set = set()
+ DS_vertices = DisjointSet_of_hashables(vertices)
for (u, v, l) in edges:
if DS_vertices.find(u) != DS_vertices.find(v):
DS_vertices.union(u, v)
our_set.add(l)
return frozenset(our_set)
- def _max_coindependent(self, X):
+ cpdef _max_coindependent(self, X):
"""
Compute a maximal coindependent subset.
INPUT:
- - ``X`` -- an iterable container of ground set elements
+ - ``X`` -- an iterable container of groundset elements
OUTPUT: a subset of the groundset as a :class:`frozenset`
@@ -800,14 +787,13 @@ def _max_coindependent(self, X):
sage: sorted(N.max_coindependent([0,1,2,5]))
[1, 2, 5]
"""
- from sage.sets.disjoint_set import DisjointSet
-
- edges = self.groundset_to_edges(X)
- all_vertices = self._G.vertices(sort=False)
- not_our_edges = self.groundset_to_edges(self._groundset.difference(X))
+ cdef DisjointSet_of_hashables DS_vertices
+ cdef list edges = self.groundset_to_edges(X)
+ cdef list all_vertices = self._G.vertices(sort=False)
+ cdef list not_our_edges = self.groundset_to_edges(self._groundset.difference(X))
- our_set = set()
- DS_vertices = DisjointSet(all_vertices)
+ cdef set our_set = set()
+ DS_vertices = DisjointSet_of_hashables(all_vertices)
for (u, v, l) in not_our_edges:
DS_vertices.union(u, v)
@@ -818,18 +804,16 @@ def _max_coindependent(self, X):
DS_vertices.union(u, v)
return frozenset(our_set)
- def _circuit(self, X):
+ cpdef _circuit(self, X):
"""
Return a minimal dependent subset.
INPUT:
- - ``X`` -- an iterable container of ground set elements
-
- OUTPUT:
+ - ``X`` -- an iterable container of groundset elements
- ``frozenset`` instance containing a subset of ``X``.
- A :class:`ValueError` is raised if the set contains no circuit.
+ OUTPUT: ``frozenset`` instance containing a subset of ``X``;
+ a :class:`ValueError` is raised if the set contains no circuit
EXAMPLES::
@@ -864,14 +848,19 @@ def _circuit(self, X):
sage: sorted(M._circuit(M.groundset()))
[4, 5]
"""
- from sage.sets.disjoint_set import DisjointSet
+ cdef list edges = self.groundset_to_edges(X)
+ cdef set vertices = set()
+ cdef list vertex_list = []
+ cdef list leaves
+ cdef tuple leaf
+ cdef set edge_set = set()
+ cdef DisjointSet_of_hashables DS_vertices
- edges = self.groundset_to_edges(X)
- vertices = set([u for (u, v, l) in edges]).union(
- set([v for (u, v, l) in edges]))
- edge_set = set()
- DS_vertices = DisjointSet(vertices)
- for u, v, l in edges:
+ for (u, v, l) in edges:
+ vertices.add(u)
+ vertices.add(v)
+ DS_vertices = DisjointSet_of_hashables(vertices)
+ for (u, v, l) in edges:
edge_set.add((u, v, l))
if DS_vertices.find(u) != DS_vertices.find(v):
DS_vertices.union(u, v)
@@ -880,7 +869,8 @@ def _circuit(self, X):
else:
raise ValueError("no circuit in independent set")
- vertex_list = [u for u, v, l in edge_set] + [v for u, v, l in edge_set]
+ for (u, v, l) in edge_set:
+ vertex_list.extend([u, v])
leaves = [(u, v, l) for (u, v, l) in edge_set
if vertex_list.count(u) == 1 or vertex_list.count(v) == 1]
while leaves:
@@ -893,13 +883,13 @@ def _circuit(self, X):
return frozenset([l for (u, v, l) in edge_set])
- def _coclosure(self, X):
+ cpdef _coclosure(self, X):
"""
Return the coclosure of a set.
INPUT:
- - ``X`` -- an iterable container of ground set elements
+ - ``X`` -- an iterable container of groundset elements
OUTPUT: a subset of the groundset as a :class:`frozenset`
@@ -917,19 +907,19 @@ def _coclosure(self, X):
sage: sorted(N._coclosure([3]))
[3, 4, 5]
"""
- g = self.graph()
+ cdef GenericGraph_pyx g = self.graph()
g.delete_edges(self._groundset_to_edges(X))
- components = g.connected_components_number()
- X = set(X)
- Y = self.groundset().difference(X)
+ cdef int components = g.connected_components_number()
+ cdef set XX = set(X)
+ cdef frozenset Y = self.groundset().difference(XX)
for e in self._groundset_to_edges(Y):
g.delete_edge(e)
if g.connected_components_number() > components:
- X.add(e[2])
+ XX.add(e[2])
g.add_edge(e)
- return frozenset(X)
+ return frozenset(XX)
- def _is_closed(self, X):
+ cpdef _is_closed(self, X):
"""
Test if input is a closed set.
@@ -955,18 +945,19 @@ def _is_closed(self, X):
# Take the set of vertices of the edges corresponding to the elements,
# and check if there are other edges incident with two of those vertices.
# Also, there must not be loops outside of X.
- X = set(X)
- loop_labels = set([l for (u, v, l) in self._G.loops()])
- if not loop_labels.issubset(X):
+ cdef set XX = set(X)
+ cdef set loop_labels = set([l for (u, v, l) in self._G.loops()])
+ if not loop_labels.issubset(XX):
return False
- # Remove loops from input since we don't want to count them as components
- X.difference_update(loop_labels)
- edge_list = self._groundset_to_edges(X)
+ # Remove loops from input since we don't want to count them as
+ # components
+ XX.difference_update(loop_labels)
+ cdef list edge_list = self._groundset_to_edges(XX)
- vertex_set = set()
- Y = self.groundset().difference(X)
- edge_list2 = self._groundset_to_edges(Y)
+ cdef set vertex_set = set()
+ cdef frozenset Y = self.groundset().difference(XX)
+ cdef list edge_list2 = self._groundset_to_edges(Y)
for e in edge_list:
vertex_set.add(e[0])
vertex_set.add(e[1])
@@ -975,13 +966,13 @@ def _is_closed(self, X):
return False
return True
- def _is_isomorphic(self, other, certificate=False):
+ cpdef _is_isomorphic(self, other, certificate=False):
"""
Test if ``self`` is isomorphic to ``other``.
INPUT:
- - ``other`` -- a matroid
+ - ``other`` -- matroid
- ``certificate`` -- boolean
OUTPUT:
@@ -1064,7 +1055,7 @@ def _is_isomorphic(self, other, certificate=False):
return (True, {e: iso2[iso1[e]] for e in iso1})
return M._is_isomorphic(other)
- def _isomorphism(self, other):
+ cpdef _isomorphism(self, other):
"""
Return isomorphism from ``self`` to ``other``, if such an isomorphism
exists.
@@ -1073,9 +1064,9 @@ def _isomorphism(self, other):
INPUT:
- - ``other`` -- a matroid
+ - ``other`` -- matroid
- OUTPUT: a dictionary, or ``None``
+ OUTPUT: dictionary or ``None``
EXAMPLES::
@@ -1103,7 +1094,7 @@ def _isomorphism(self, other):
"""
return self.is_isomorphic(other, certificate=True)[1]
- def is_valid(self):
+ cpdef is_valid(self):
"""
Test if the data obey the matroid axioms.
@@ -1148,15 +1139,15 @@ def is_regular(self):
"""
return True
- # Graphic methods:
+ # graphic methods
- def graph(self):
+ cpdef graph(self):
"""
Return the graph that represents the matroid.
The graph will always have loops and multiedges enabled.
- OUTPUT: a graph
+ OUTPUT: graph
EXAMPLES::
@@ -1170,7 +1161,7 @@ def graph(self):
# Return a mutable graph
return self._G.copy(data_structure='sparse')
- def vertex_map(self):
+ cpdef vertex_map(self):
"""
Return a dictionary mapping the input vertices to the current vertices.
@@ -1180,7 +1171,7 @@ def vertex_map(self):
input graph as keys, and the corresponding vertex label after any
merging as values.
- OUTPUT: a dictionary
+ OUTPUT: dictionary
EXAMPLES::
@@ -1204,22 +1195,22 @@ def vertex_map(self):
"""
return copy(self._vertex_map)
- def groundset_to_edges(self, X):
+ cpdef groundset_to_edges(self, X):
"""
Return a list of edges corresponding to a set of groundset elements.
INPUT:
- - ``X`` -- a subset of the groundset
+ - ``X`` -- subset of the groundset
- OUTPUT: a list of graph edges
+ OUTPUT: list of graph edges
EXAMPLES::
sage: M = Matroid(range(5), graphs.DiamondGraph())
- sage: M.groundset_to_edges([2,3,4])
+ sage: M.groundset_to_edges([2, 3, 4])
[(1, 2, 2), (1, 3, 3), (2, 3, 4)]
- sage: M.groundset_to_edges([2,3,4,5])
+ sage: M.groundset_to_edges([2, 3, 4, 5])
Traceback (most recent call last):
...
ValueError: input must be a subset of the groundset
@@ -1229,33 +1220,33 @@ def groundset_to_edges(self, X):
raise ValueError("input must be a subset of the groundset")
return self._groundset_to_edges(X)
- def _groundset_to_edges(self, X):
+ cpdef _groundset_to_edges(self, X):
"""
Return a list of edges corresponding to a set of groundset elements.
INPUT:
- - ``X`` -- a subset of the groundset
+ - ``X`` -- subset of the groundset
- OUTPUT: a list of graph edges
+ OUTPUT: list of graph edges
EXAMPLES::
sage: M = Matroid(range(5), graphs.DiamondGraph())
- sage: M._groundset_to_edges([2,3,4])
+ sage: M._groundset_to_edges([2, 3, 4])
[(1, 2, 2), (1, 3, 3), (2, 3, 4)]
"""
return [(self._groundset_edge_map[x][0], self._groundset_edge_map[x][1], x) for x in X]
- def subgraph_from_set(self, X):
+ cpdef subgraph_from_set(self, X):
"""
Return the subgraph corresponding to the matroid restricted to `X`.
INPUT:
- - ``X`` -- a subset of the groundset
+ - ``X`` -- subset of the groundset
- OUTPUT: a graph
+ OUTPUT: graph
EXAMPLES::
@@ -1272,28 +1263,27 @@ def subgraph_from_set(self, X):
raise ValueError("input must be a subset of the groundset")
return self._subgraph_from_set(X)
- def _subgraph_from_set(self, X):
+ cpdef _subgraph_from_set(self, X):
"""
Return the subgraph corresponding to `M` restricted to `X`.
INPUT:
- - ``X`` -- a subset of the groundset
+ - ``X`` -- subset of the groundset
- OUTPUT: a graph
+ OUTPUT: graph
EXAMPLES::
sage: M = Matroid(range(5), graphs.DiamondGraph())
- sage: M._subgraph_from_set([0,1,2])
+ sage: M._subgraph_from_set([0, 1, 2])
Looped multi-graph on 3 vertices
"""
from sage.graphs.graph import Graph
-
edge_list = self._groundset_to_edges(X)
return Graph(edge_list, loops=True, multiedges=True)
- def graphic_extension(self, u, v=None, element=None):
+ cpdef graphic_extension(self, u, v=None, element=None):
"""
Return a graphic matroid extended by a new element.
@@ -1302,15 +1292,15 @@ def graphic_extension(self, u, v=None, element=None):
INPUT:
- - ``u`` -- a vertex in the matroid's graph
+ - ``u`` -- vertex in the matroid's graph
- ``v`` -- (optional) another vertex
- ``element`` -- (optional) the label of the new element
OUTPUT:
- A GraphicMatroid with the specified element added. Note that if ``v``
- is not specifies or if ``v`` is ``u``, then the new element will be a
- loop. If the new element's label is not specified, it will be
+ A class:`GraphicMatroid` with the specified element added. Note that if
+ ``v`` is not specified or if ``v`` is ``u``, then the new element will
+ be a loop. If the new element's label is not specified, it will be
generated automatically.
EXAMPLES::
@@ -1380,8 +1370,8 @@ def graphic_extensions(self, element=None, vertices=None, simple=False):
OUTPUT:
- An iterable containing instances of ``GraphicMatroid``. If ``vertices``
- is not specified, every vertex is used.
+ An iterable containing instances of class:`GraphicMatroid`. If
+ ``vertices`` is not specified, every vertex is used.
.. NOTE::
@@ -1436,7 +1426,7 @@ def graphic_extensions(self, element=None, vertices=None, simple=False):
yield GraphicMatroid(G)
G.delete_edge(p[0], p[1], element)
- def graphic_coextension(self, u, v=None, X=None, element=None):
+ cpdef graphic_coextension(self, u, v=None, X=None, element=None):
"""
Return a matroid coextended by a new element.
@@ -1455,8 +1445,8 @@ def graphic_coextension(self, u, v=None, X=None, element=None):
OUTPUT:
- An instance of GraphicMatroid coextended by the new element. If ``X``
- is not specified, the new element will be a coloop.
+ An instance of class:`GraphicMatroid` coextended by the new element.
+ If ``X`` is not specified, the new element will be a coloop.
.. NOTE::
@@ -1589,8 +1579,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal
OUTPUT:
- An iterable containing instances of ``GraphicMatroid``. If ``vertices``
- is not specified, the method iterates over all vertices.
+ An iterable containing instances of class:`GraphicMatroid`. If
+ ``vertices`` is not specified, the method iterates over all vertices.
EXAMPLES::
@@ -1653,7 +1643,8 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal
non-cosimple, ie. a coloop and one for every coseries class.
12 total::
- sage: edgedict = {0:[1,2,3], 1:[2,4], 2:[3], 3:[6], 4:[5,7], 5:[6,7], 6:[7]}
+ sage: edgedict = {0: [1, 2, 3], 1: [2, 4], 2: [3], 3: [6],
+ ....: 4: [5, 7], 5: [6, 7], 6: [7]}
sage: M = Matroid(range(12), Graph(edgedict))
sage: sorted(M.coclosure([4]))
[4, 6]
@@ -1706,7 +1697,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal
# If a vertex has degree 1, or 2, or 3, we already handled it.
for u in vertices:
if G.degree(u) > 3:
- elts_incident = [ll for (_, _, ll) in G.edges_incident(u)]
+ elts_incident = [l for (_, _, l) in G.edges_incident(u)]
x = elts_incident.pop()
for i in range(1, (len(elts_incident) - Integer(1))):
groups = combinations(elts_incident, i)
@@ -1716,7 +1707,7 @@ def graphic_coextensions(self, vertices=None, v=None, element=None, cosimple=Fal
yield self.graphic_coextension(
X=g, u=u, v=v, element=element)
- def twist(self, X):
+ cpdef twist(self, X):
"""
Perform a Whitney twist on the graph.
@@ -1730,18 +1721,17 @@ def twist(self, X):
- ``X`` -- the set of elements to be twisted with respect
to the rest of the matroid
- OUTPUT:
-
- An instance of ``GraphicMatroid`` isomorphic to this matroid but with
- a graph that is not necessarily isomorphic.
+ OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but
+ with a graph that is not necessarily isomorphic
EXAMPLES::
- sage: edgelist = [(0,1,0), (1,2,1), (1,2,2), (2,3,3), (2,3,4), (2,3,5), (3,0,6)]
+ sage: edgelist = [(0, 1, 0), (1, 2, 1), (1, 2, 2), (2, 3, 3),
+ ....: (2, 3, 4), (2, 3, 5), (3, 0, 6)]
sage: M = Matroid(Graph(edgelist, multiedges=True))
- sage: M1 = M.twist([0,1,2]); M1.graph().edges(sort=True)
+ sage: M1 = M.twist([0, 1, 2]); M1.graph().edges(sort=True)
[(0, 1, 1), (0, 1, 2), (0, 3, 6), (1, 2, 0), (2, 3, 3), (2, 3, 4), (2, 3, 5)]
- sage: M2 = M.twist([0,1,3])
+ sage: M2 = M.twist([0, 1, 3])
Traceback (most recent call last):
...
ValueError: the input must display a 2-separation that is not a 1-separation
@@ -1825,7 +1815,7 @@ def twist(self, X):
G.add_edge(u, v, l)
return GraphicMatroid(G)
- def one_sum(self, X, u, v):
+ cpdef one_sum(self, X, u, v):
"""
Arrange matroid components in the graph.
@@ -1837,14 +1827,12 @@ def one_sum(self, X, u, v):
INPUT:
- - ``X`` -- a subset of the groundset
- - ``u`` -- a vertex spanned by the edges of the elements in ``X``
- - ``v`` -- a vertex spanned by the edges of the elements not in ``X``
-
- OUTPUT:
+ - ``X`` -- subset of the groundset
+ - ``u`` -- vertex spanned by the edges of the elements in ``X``
+ - ``v`` -- vertex spanned by the edges of the elements not in ``X``
- An instance of ``GraphicMatroid`` isomorphic to this matroid but with
- a graph that is not necessarily isomorphic.
+ OUTPUT: class:`GraphicMatroid` isomorphic to this matroid but
+ with a graph that is not necessarily isomorphic
EXAMPLES::
@@ -1960,9 +1948,10 @@ def one_sum(self, X, u, v):
return GraphicMatroid(G)
- def regular_matroid(self):
+ cpdef regular_matroid(self):
"""
- Return an instance of RegularMatroid isomorphic to this GraphicMatroid.
+ Return an instance of class:`RegularMatroid` isomorphic to this
+ class:`GraphicMatroid`.
EXAMPLES::
diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx
index 19b87336033..0027d85073f 100644
--- a/src/sage/matroids/linear_matroid.pyx
+++ b/src/sage/matroids/linear_matroid.pyx
@@ -495,17 +495,26 @@ cdef class LinearMatroid(BasisExchangeMatroid):
- ``B`` -- (default: ``None``) a subset of elements. When provided,
the representation is such that a basis `B'` that maximally
intersects `B` is an identity matrix.
+
- ``reduced`` -- (default: ``False``) when ``True``, return a reduced
matrix `D` (so `[I\ \ D]` is a representation of the matroid).
Otherwise return a full representation matrix.
+
- ``labels`` -- (default: ``None``) when ``True``, return additionally
a list of column labels (if ``reduced=False``) or a list of row
labels and a list of column labels (if ``reduced=True``).
The default setting, ``None``, will not return the labels for a full
matrix, but will return the labels for a reduced matrix.
- - ``order`` -- (default: ``None``) an ordering of the groundset
- elements. If provided, the columns (and, in case of a reduced
- representation, rows) will be presented in the given order.
+
+ - ``order`` -- sequence or ``None`` or ``True`` (default: ``None``);
+
+ - when a sequence, it should be an ordering of the groundset
+ elements, and the columns (and, in case of a reduced
+ representation, rows) will be presented in the given order,
+ - when ``None``, use the same ordering that :meth:`groundset_list`
+ uses,
+ - when ``True``, return a morphism of free modules instead of a matrix.
+
- ``lift_map`` -- (default: ``None``) a dictionary containing the cross
ratios of the representing matrix in its domain. If provided, the
representation will be transformed by mapping its cross ratios according
@@ -578,9 +587,47 @@ cdef class LinearMatroid(BasisExchangeMatroid):
[ 1 0 0 1 0 1 1 1]
[ 0 1 0 -z + 1 1 0 0 1]
[ 0 0 1 0 1 -1 z - 1 0]
+
+ As morphisms::
+
+ sage: M = matroids.catalog.Fano()
+ sage: A = M.representation(order=True); A
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'}
+ over Finite Field of size 2
+ To: Free module generated by {0, 1, 2} over Finite Field of size 2
+ sage: print(A._unicode_art_matrix())
+ a b c d e f g
+ 0⎛1 0 0 0 1 1 1⎞
+ 1⎜0 1 0 1 0 1 1⎟
+ 2⎝0 0 1 1 1 0 1⎠
+ sage: A = M.representation(B='efg', order=True); A
+ Generic morphism:
+ From: Free module generated by {'a', 'b', 'c', 'd', 'e', 'f', 'g'}
+ over Finite Field of size 2
+ To: Free module generated by {0, 1, 2} over Finite Field of size 2
+ sage: print(A._unicode_art_matrix())
+ a b c d e f g
+ 0⎛1 1 0 1 1 0 0⎞
+ 1⎜1 0 1 1 0 1 0⎟
+ 2⎝1 1 1 0 0 0 1⎠
+ sage: A = M.representation(B='abc', order=True, reduced=True); A
+ Generic morphism:
+ From: Free module generated by {'d', 'e', 'f', 'g'}
+ over Finite Field of size 2
+ To: Free module generated by {'a', 'b', 'c'} over Finite Field of size 2
+ sage: print(A._unicode_art_matrix())
+ d e f g
+ a⎛0 1 1 1⎞
+ b⎜1 0 1 1⎟
+ c⎝1 1 0 1⎠
"""
cdef LeanMatrix A
- if order is None:
+ column_keys = None
+ if order is True:
+ order = self.groundset_list()
+ column_keys = tuple(order)
+ elif order is None:
order = self.groundset_list()
else:
if not frozenset(order) == self.groundset():
@@ -607,16 +654,14 @@ cdef class LinearMatroid(BasisExchangeMatroid):
B = self._subset_internal(B)
A = self._basic_representation(B)
A = A.matrix_from_rows_and_columns(range(A.nrows()), order_idx)
- if lift_map is None:
- if labels:
- return (A._matrix_(), order)
- else:
- return A._matrix_()
- else:
- if labels:
- return (lift_cross_ratios(A._matrix_(), lift_map), order)
- else:
- return lift_cross_ratios(A._matrix_(), lift_map)
+ Am = A._matrix_()
+ if lift_map is not None:
+ Am = lift_cross_ratios(Am, lift_map)
+ if column_keys is not None:
+ Am = matrix(Am, row_keys=range(A.nrows()), column_keys=column_keys)
+ if labels:
+ return Am, order
+ return Am
else:
if B is None:
B = frozenset(self.basis())
@@ -637,16 +682,14 @@ cdef class LinearMatroid(BasisExchangeMatroid):
Ci.append(C.index(e))
Cl.append(e)
A = A.matrix_from_rows_and_columns(Ri, Ci)
- if lift_map is None:
- if labels or labels is None:
- return (A._matrix_(), Rl, Cl)
- else:
- return A._matrix_()
- else:
- if labels or labels is None:
- return (lift_cross_ratios(A._matrix_(), lift_map), Rl, Cl)
- else:
- return lift_cross_ratios(A._matrix_(), lift_map)
+ Am = A._matrix_()
+ if lift_map is not None:
+ Am = lift_cross_ratios(Am, lift_map)
+ if column_keys is not None:
+ Am = matrix(Am, row_keys=tuple(Rl), column_keys=tuple(Cl))
+ if labels or (labels is None and column_keys is None):
+ return Am, Rl, Cl
+ return Am
cpdef _current_rows_cols(self, B=None):
"""
diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx
index d4e306abfdd..1fc0ed4546a 100644
--- a/src/sage/matroids/set_system.pyx
+++ b/src/sage/matroids/set_system.pyx
@@ -40,7 +40,7 @@ cdef class SetSystem:
sage: M = matroids.catalog.Fano()
sage: M.circuits()
- Iterator over a system of subsets
+ SetSystem of 14 sets over 7 elements
To access the sets in this structure, simply iterate over them. The
simplest way must be::
@@ -75,7 +75,7 @@ cdef class SetSystem:
sage: from sage.matroids.set_system import SetSystem
sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]])
sage: S
- Iterator over a system of subsets
+ SetSystem of 3 sets over 4 elements
"""
cdef long i
if not isinstance(groundset, tuple):
@@ -110,7 +110,7 @@ cdef class SetSystem:
sage: from sage.matroids.set_system import SetSystem
sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]])
sage: S
- Iterator over a system of subsets
+ SetSystem of 3 sets over 4 elements
sage: sorted(S[1])
[3, 4]
sage: for s in S: print(sorted(s))
@@ -138,7 +138,7 @@ cdef class SetSystem:
sage: from sage.matroids.set_system import SetSystem
sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]])
sage: S
- Iterator over a system of subsets
+ SetSystem of 3 sets over 4 elements
sage: len(S)
3
"""
@@ -196,9 +196,9 @@ cdef class SetSystem:
sage: from sage.matroids.set_system import SetSystem
sage: S = SetSystem([1, 2, 3, 4], [[1, 2], [3, 4], [1, 2, 4]])
sage: repr(S) # indirect doctest
- 'Iterator over a system of subsets'
+ 'SetSystem of 3 sets over 4 elements'
"""
- return "Iterator over a system of subsets"
+ return f'SetSystem of {self._len} sets over {self._groundset_size} elements'
cdef copy(self):
cdef SetSystem S
diff --git a/src/sage/matroids/union_matroid.pyx b/src/sage/matroids/union_matroid.pyx
index 817f9c4a222..12e9d1e7718 100644
--- a/src/sage/matroids/union_matroid.pyx
+++ b/src/sage/matroids/union_matroid.pyx
@@ -21,7 +21,7 @@ cdef class MatroidUnion(Matroid):
{}
Matroid of rank 1 on 2 elements with 2 bases
sage: M.bases()
- Iterator over a system of subsets
+ SetSystem of 2 sets over 5 elements
sage: list(M.circuits())
[frozenset({3, 4})]
diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py
index a0cf14f5276..d5e680fc6b9 100644
--- a/src/sage/matroids/utilities.py
+++ b/src/sage/matroids/utilities.py
@@ -70,7 +70,7 @@ def setprint(X):
sage: from sage.matroids.advanced import setprint
sage: M = matroids.catalog.Fano().delete('efg')
sage: M.bases()
- Iterator over a system of subsets
+ SetSystem of 3 sets over 4 elements
sage: setprint(M.bases())
[{'a', 'b', 'c'}, {'a', 'b', 'd'}, {'a', 'c', 'd'}]
diff --git a/src/sage/misc/abstract_method.py b/src/sage/misc/abstract_method.py
index 6179c5a2b8f..bd78b05c7d5 100644
--- a/src/sage/misc/abstract_method.py
+++ b/src/sage/misc/abstract_method.py
@@ -138,7 +138,7 @@ def abstract_method(f=None, optional=False):
return AbstractMethod(f, optional)
-class AbstractMethod():
+class AbstractMethod:
def __init__(self, f, optional=False):
"""
Constructor for abstract methods
diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py
index 8aee092368c..585db5a05eb 100644
--- a/src/sage/misc/all.py
+++ b/src/sage/misc/all.py
@@ -131,11 +131,9 @@
lazy_import('sage.misc.dev_tools', 'runsnake', deprecation=34259)
lazy_import('sage.misc.edit_module', 'set_edit_template', deprecation=34259)
lazy_import('sage.misc.profiler', 'Profiler', deprecation=34259)
-lazy_import('sage.misc.dist', 'install_scripts', deprecation=34259)
lazy_import('sage.misc.trace', 'trace', deprecation=34259)
lazy_import('sage.misc.package', ('installed_packages', 'is_package_installed',
- 'standard_packages', 'optional_packages',
- 'experimental_packages', 'package_versions'),
+ 'package_versions'),
deprecation=34259)
lazy_import('sage.misc.benchmark', 'benchmark', deprecation=34259)
lazy_import('sage.repl.interpreter', 'logstr', deprecation=34259)
diff --git a/src/sage/misc/binary_tree.pyx b/src/sage/misc/binary_tree.pyx
index 8451e0b1e04..2d41ce45e8c 100644
--- a/src/sage/misc/binary_tree.pyx
+++ b/src/sage/misc/binary_tree.pyx
@@ -310,7 +310,7 @@ cdef class BinaryTree:
sage: t = BinaryTree()
sage: t.contains(1)
False
- sage: t.insert(1,1)
+ sage: t.insert(1, 1)
sage: t.contains(1)
True
"""
diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx
index befaa7b5b32..437a3da23cf 100644
--- a/src/sage/misc/c3_controlled.pyx
+++ b/src/sage/misc/c3_controlled.pyx
@@ -295,7 +295,7 @@ Depending on the linear extension `l` it was necessary to add between
one and five bases for control; for example, `216` linear extensions
required the addition of four bases::
- sage: sorted(Word(stats).evaluation_sparse()) # needs sage.graphs sage.modules
+ sage: sorted(Word(stats).evaluation_sparse()) # needs sage.combinat sage.graphs sage.modules
[(1, 36), (2, 108), (3, 180), (4, 216), (5, 180)]
We now consider a hierarchy of categories::
@@ -319,9 +319,9 @@ For a typical category, few bases, if any, need to be added to force
sage: x.mro == x.mro_standard
False
sage: x.all_bases_len()
- 70
+ 72
sage: x.all_bases_controlled_len()
- 74
+ 76
sage: C = GradedHopfAlgebrasWithBasis(QQ)
sage: x = HierarchyElement(C, attrcall("super_categories"), attrgetter("_cmp_key"))
diff --git a/src/sage/misc/call.py b/src/sage/misc/call.py
index 6de02981882..1d3055faf78 100644
--- a/src/sage/misc/call.py
+++ b/src/sage/misc/call.py
@@ -17,7 +17,7 @@
#############################################
# Operators
#############################################
-class AttrCallObject():
+class AttrCallObject:
def __init__(self, name, args, kwds):
"""
TESTS::
diff --git a/src/sage/misc/classgraph.py b/src/sage/misc/classgraph.py
index ae33fe9277e..9db5ec99639 100644
--- a/src/sage/misc/classgraph.py
+++ b/src/sage/misc/classgraph.py
@@ -92,7 +92,7 @@ def class_graph(top, depth=5, name_filter=None, classes=None, as_graph=True):
# (first recursive call)
if classes is None:
- classes = dict()
+ classes = {}
# Build the list ``children`` of submodules (resp. base classes)
# of ``top`` the function will recurse through
diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py
index 0cd7a47916e..659bf223c50 100644
--- a/src/sage/misc/converting_dict.py
+++ b/src/sage/misc/converting_dict.py
@@ -1,7 +1,7 @@
r"""
Converting Dictionary
-At the moment, the only class contained in this model is a key
+At the moment, the only class contained in this module is a key
converting dictionary, which applies some function (e.g. type
conversion function) to all arguments used as keys.
diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py
index 93ce994c537..fbf22dea2c1 100644
--- a/src/sage/misc/cython.py
+++ b/src/sage/misc/cython.py
@@ -348,7 +348,7 @@ def cython(filename, verbose=0, compile_message=False,
libraries=standard_libs,
library_dirs=standard_libdirs)
- directives = dict(language_level=3, cdivision=True)
+ directives = {'language_level': 3, 'cdivision': True}
try:
# Change directories to target_dir so that Cython produces the correct
diff --git a/src/sage/misc/decorators.py b/src/sage/misc/decorators.py
index 9ded4260e3b..f0b34b8843d 100644
--- a/src/sage/misc/decorators.py
+++ b/src/sage/misc/decorators.py
@@ -177,7 +177,7 @@ def f(wrapper, assigned=assigned, updated=updated):
# Infix operator decorator
-class infix_operator():
+class infix_operator:
"""
A decorator for functions which allows for a hack that makes
the function behave like an infix operator.
@@ -259,7 +259,7 @@ def __call__(self, func):
return wrapper_inst
-class _infix_wrapper():
+class _infix_wrapper:
function = None
def __init__(self, left=None, right=None):
@@ -348,7 +348,7 @@ def my_wrap(*args, **kwds):
return my_wrap
-class suboptions():
+class suboptions:
def __init__(self, name, **options):
"""
A decorator for functions which collects all keywords
@@ -433,7 +433,7 @@ def listForNone(l):
return wrapper
-class options():
+class options:
def __init__(self, **options):
"""
A decorator for functions which allows for default options to be
@@ -573,7 +573,7 @@ def reset():
return wrapper
-class rename_keyword():
+class rename_keyword:
def __init__(self, deprecated=None, deprecation=None, **renames):
"""
A decorator which renames keyword arguments and optionally
diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py
index 8a4420f2314..05d7c6b877b 100644
--- a/src/sage/misc/dev_tools.py
+++ b/src/sage/misc/dev_tools.py
@@ -260,7 +260,7 @@ def find_objects_from_name(name, module_name=None, include_lazy_imports=False):
:class:`~sage.misc.lazy_import.LazyImport` objects that are resolving to the
same object may be included in the output::
- sage: dt.find_objects_from_name('RR', include_lazy_imports=True)
+ sage: dt.find_objects_from_name('RR', include_lazy_imports=True) # needs sage.rings.real_mpfr
[Real Field with 53 bits of precision,
...
Real Field with 53 bits of precision,
@@ -680,10 +680,7 @@ def is_ascii(s):
# is a best one (i.e. the object "obj" is contained in the module and
# has name "name")
if name is not None:
- good_modules = []
- for mod in modules:
- if name in modules[mod]:
- good_modules.append(mod)
+ good_modules = [mod for mod in modules if name in modules[mod]]
if len(good_modules) == 1:
answer[good_modules[0]].append((name, name))
@@ -732,9 +729,8 @@ def is_ascii(s):
if lazy:
res.append("from sage.misc.lazy_import import lazy_import")
- for module_name in sorted(answer):
- res.append(import_statement_string(module_name, answer[module_name],
- lazy))
+ res.extend(import_statement_string(module_name, answer[module_name], lazy)
+ for module_name in sorted(answer))
if answer_as_str:
return '\n'.join(res)
diff --git a/src/sage/misc/dist.py b/src/sage/misc/dist.py
deleted file mode 100644
index b7c59ee77f3..00000000000
--- a/src/sage/misc/dist.py
+++ /dev/null
@@ -1,167 +0,0 @@
-"""
-Installing shortcut scripts
-"""
-
-import os
-
-from sage.misc.superseded import deprecation
-
-def install_scripts(directory=None, ignore_existing=False):
- r"""
- This function has been deprecated.
-
- Running ``install_scripts(directory)`` creates scripts in the
- given directory that run various software components included with
- Sage. Each of these scripts essentially just runs ``sage --CMD``
- where ``CMD`` is also the name of the script:
-
- - 'gap' runs GAP
- - 'gp' runs the PARI/GP interpreter
- - 'ipython' runs IPython
- - 'maxima' runs Maxima
- - 'mwrank' runs mwrank
- - 'R' runs R
- - 'singular' runs Singular
- - 'sqlite3' runs SQLite version 3
-
- This command:
-
- - verbosely tells you which scripts it adds, and
-
- - will *not* overwrite any scripts you already have in the given
- directory.
-
- INPUT:
-
- - ``directory`` - string; the directory into which to put the
- scripts. This directory must exist and the user must have write
- and execute permissions.
-
- - ``ignore_existing`` - bool (optional, default False): if True,
- install script even if another version of the program is in your
- path.
-
- OUTPUT: Verbosely prints what it is doing and creates files in
- ``directory`` that are world executable and readable.
-
- .. note::
-
- You may need to run ``sage`` as ``root`` in order to run
- ``install_scripts`` successfully, since the user running
- ``sage`` needs write permissions on ``directory``. Note
- that one good candidate for ``directory`` is
- ``'/usr/local/bin'``, so from the shell prompt, you could run ::
-
- sudo sage -c "install_scripts('/usr/local/bin')"
-
- .. note::
-
- Running ``install_scripts(directory)`` will be most helpful if
- ``directory`` is in your path.
-
- AUTHORS:
-
- - William Stein: code / design
-
- - Arthur Gaer: design
-
- - John Palmieri: revision, 2011-07 (:issue:`11602`)
-
- EXAMPLES::
-
- sage: import tempfile
- sage: from sage.misc.dist import install_scripts
- sage: with tempfile.TemporaryDirectory() as d:
- ....: install_scripts(d, ignore_existing=True)
- doctest:warning...
- the function install_scripts has been deprecated and will be removed in a future version of Sage
- See https://github.com/sagemath/sage/issues/30207 for details.
- Checking that Sage has the command 'gap' installed
- ...
- """
- deprecation(30207, 'the function install_scripts has been deprecated and '
- 'will be removed in a future version of Sage')
-
- if directory is None:
- # We do this since the intended user of install_scripts
- # will likely be pretty clueless about how to use Sage or
- # its help system.
- from . import sagedoc
- print(sagedoc.format(install_scripts.__doc__))
- print("USAGE: install_scripts('directory')")
- return
-
- if not os.path.exists(directory):
- print(f"Error: '{directory}' does not exist.")
- return
-
- if not os.path.isdir(directory):
- print(f"Error: '{directory}' is not a directory.")
- return
-
- if not (os.access(directory, os.W_OK) and os.access(directory, os.X_OK)):
- print(f"Error: you do not have write permission for '{directory}'.")
- return
-
- from sage.misc.sage_ostools import have_program
- from sage.env import SAGE_LOCAL
-
- if not SAGE_LOCAL:
- print(f"Error: This installation of Sage does not use SAGE_LOCAL, so install_scripts makes no sense.")
- return
-
- script_created = False
- SAGE_BIN = os.path.join(SAGE_LOCAL, 'bin')
- # See if 'directory' is already in PATH, and then remove
- # SAGE_LOCAL/bin from PATH so that we can later check whether
- # cmd is available outside of Sage.
- PATH = os.environ['PATH'].split(os.pathsep)
- PATH = [d for d in PATH if os.path.exists(d)]
- dir_in_path = any(os.path.samefile(directory, d) for d in PATH)
- PATH = os.pathsep.join(d for d in PATH
- if not os.path.samefile(d, SAGE_BIN))
- for cmd in ['gap', 'gp', 'ipython', 'maxima',
- 'mwrank', 'R', 'singular', 'sqlite3']:
- print(f"Checking that Sage has the command '{cmd}' installed")
- # Check to see if Sage includes cmd.
- cmd_inside_sage = have_program(cmd, path=SAGE_BIN)
- if not cmd_inside_sage:
- print(f"The command '{cmd}' is not available as part of Sage; " +
- "not creating script.")
- print()
- continue
- cmd_outside_sage = have_program(cmd, path=PATH)
- if cmd_outside_sage:
- print(f"The command '{cmd}' is installed outside of Sage;", end=' ')
- if not ignore_existing:
- print("not creating script.")
- print()
- continue
- print("trying to create script anyway...")
- else:
- print(f"Creating script for '{cmd}'...")
- # Install shortcut.
- target = os.path.join(directory, cmd)
- if os.path.exists(target):
- print(f"The file '{target}' already exists; not adding script.")
- else:
- with open(target, 'w') as o:
- o.write('#!/bin/sh\n')
- o.write('exec sage --%s "$@"\n' % cmd)
- print(f"Created script '{target}'")
- os.system(f'chmod a+rx {target}')
- script_created = True
- print()
-
- if script_created:
- print("Finished creating scripts.")
- print()
- print("You need not do this again even if you upgrade or move Sage.")
- print("The only requirement is that your PATH contains both")
- print("'{}' and the directory containing the command 'sage'.".format(directory))
- if not dir_in_path:
- print()
- print("Warning: '{}' is not currently in your PATH.".format(directory))
- print()
- else:
- print("No scripts created.")
diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py
index 58a3ab15a2a..63232e89a81 100644
--- a/src/sage/misc/edit_module.py
+++ b/src/sage/misc/edit_module.py
@@ -102,7 +102,7 @@ def file_and_line(obj):
filename = sage_getfile(obj)
lineno = sage_getsourcelines(obj)[1] - 1
if filename.endswith('.py'):
- infile = open(filename, 'r')
+ infile = open(filename)
infile.readline()
if infile.readline().find("*autogenerated*") >= 0:
filename = filename[:-3] + '.sage'
@@ -171,7 +171,7 @@ def set_edit_template(template_string):
if not isinstance(template_string, Template):
template_string = Template(template_string)
fields = set(template_fields(template_string))
- if not (fields <= set(['file', 'line']) and ('file' in fields)):
+ if not (fields <= {'file', 'line'} and ('file' in fields)):
raise ValueError("Only ${file} and ${line} are allowed as template variables, and ${file} must occur.")
edit_template = template_string
diff --git a/src/sage/misc/element_with_label.py b/src/sage/misc/element_with_label.py
index e7ae38264be..8ae3d4ee523 100644
--- a/src/sage/misc/element_with_label.py
+++ b/src/sage/misc/element_with_label.py
@@ -14,7 +14,7 @@
from sage.misc.latex import latex
-class ElementWithLabel():
+class ElementWithLabel:
"""
Auxiliary class for showing/viewing :class:`Poset`s with
non-injective labelings.
diff --git a/src/sage/misc/explain_pickle.py b/src/sage/misc/explain_pickle.py
index a0c6323270c..fe62f5ea841 100644
--- a/src/sage/misc/explain_pickle.py
+++ b/src/sage/misc/explain_pickle.py
@@ -318,7 +318,7 @@ def name_is_valid(name):
# This string is used as the representation of a mark.
the_mark = 'mark'
-class PickleObject():
+class PickleObject:
r"""
Pickles have a stack-based virtual machine. The :func:`explain_pickle`
pickle interpreter mostly uses :class:`sage.misc.sage_input.SageInputExpression` objects
@@ -374,7 +374,7 @@ def _sage_input_(self, sib, coerced):
self.immutable = True
return self.expression
-class PickleDict():
+class PickleDict:
r"""
An object which can be used as the value of a :class:`PickleObject`. The items
is a list of key-value pairs, where the keys and values are
@@ -394,7 +394,7 @@ def __init__(self, items):
"""
self.items = items
-class PickleInstance():
+class PickleInstance:
r"""
An object which can be used as the value of a :class:`PickleObject`. Unlike
other possible values of a :class:`PickleObject`, a :class:`PickleInstance` doesn't represent
@@ -412,7 +412,7 @@ def __init__(self, klass):
"""
self.klass = klass
-class PickleExplainer():
+class PickleExplainer:
r"""
An interpreter for the pickle virtual machine, that executes
symbolically and constructs :class:`SageInputExpression` objects instead of
@@ -2681,7 +2681,7 @@ def __hash__(self):
return 0
-class EmptyNewstyleClass():
+class EmptyNewstyleClass:
r"""
A featureless new-style class (inherits from object); used for
testing :func:`explain_pickle`.
@@ -2842,7 +2842,7 @@ def extend(self):
raise NotImplementedError
-class TestAppendNonlist():
+class TestAppendNonlist:
r"""
A list-like class, carefully designed to test exact unpickling
behavior. Used for testing :func:`explain_pickle`.
@@ -2926,7 +2926,7 @@ def __repr__(self):
return repr(self.list)
-class TestBuild():
+class TestBuild:
r"""
A simple class with a :meth:`__getstate__` but no :meth:`__setstate__`. Used for testing
:func:`explain_pickle`.
@@ -2985,7 +2985,7 @@ def __setstate__(self, state):
self.x = state[1]['y']
self.y = state[0]['x']
-class TestGlobalOldName():
+class TestGlobalOldName:
r"""
A featureless new-style class. When you try to unpickle an instance
of this class, it is redirected to create a :class:`TestGlobalNewName` instead.
@@ -3000,7 +3000,7 @@ class TestGlobalOldName():
pass
-class TestGlobalNewName():
+class TestGlobalNewName:
r"""
A featureless new-style class. When you try to unpickle an instance
of :class:`TestGlobalOldName`, it is redirected to create an instance of this
@@ -3032,7 +3032,7 @@ def __repr__(self):
register_unpickle_override('sage.misc.explain_pickle', 'TestGlobalOldName', TestGlobalNewName, call_name=('sage.misc.explain_pickle', 'TestGlobalNewName'))
-class TestGlobalFunnyName():
+class TestGlobalFunnyName:
r"""
A featureless new-style class which has a name that's not a legal
Python identifier.
diff --git a/src/sage/misc/flatten.py b/src/sage/misc/flatten.py
index 5ebc8c2ceaa..889cf2e9d1d 100644
--- a/src/sage/misc/flatten.py
+++ b/src/sage/misc/flatten.py
@@ -23,7 +23,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize):
[1, 1, 1, 2]
sage: flatten([[1,2,3], (4,5), [[[1],[2]]]])
[1, 2, 3, 4, 5, 1, 2]
- sage: flatten([[1,2,3], (4,5), [[[1],[2]]]],max_level=1)
+ sage: flatten([[1,2,3], (4,5), [[[1],[2]]]], max_level=1)
[1, 2, 3, 4, 5, [[1], [2]]]
sage: flatten([[[3],[]]],max_level=0)
[[[3], []]]
@@ -64,7 +64,7 @@ def flatten(in_list, ltypes=(list, tuple), max_level=sys.maxsize):
"""
index = 0
current_level = 0
- new_list = [x for x in in_list]
+ new_list = list(in_list)
level_list = [0] * len(in_list)
while index < len(new_list):
diff --git a/src/sage/misc/function_mangling.pyx b/src/sage/misc/function_mangling.pyx
index 1392fc4f2fd..5f440742dcb 100644
--- a/src/sage/misc/function_mangling.pyx
+++ b/src/sage/misc/function_mangling.pyx
@@ -202,10 +202,10 @@ cdef class ArgumentFixer:
EXAMPLES::
sage: from sage.misc.function_mangling import ArgumentFixer
- sage: def sum3(a,b,c=3,*args,**kwargs):
- ....: return a+b+c
+ sage: def sum3(a, b, c=3, *args, **kwargs):
+ ....: return a + b + c
sage: AF = ArgumentFixer(sum3)
- sage: AF.fix_to_named(1,2,3,4,5,6,f=14,e=16)
+ sage: AF.fix_to_named(1, 2, 3, 4, 5, 6, f=14, e=16)
((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14)))
sage: AF.fix_to_named(1,2,f=14)
((), (('a', 1), ('b', 2), ('c', 3), ('f', 14)))
diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py
index 4ae9ecd6a2c..c70d78800e0 100644
--- a/src/sage/misc/functional.py
+++ b/src/sage/misc/functional.py
@@ -934,7 +934,7 @@ def krull_dimension(x):
1
sage: krull_dimension(ZZ[sqrt(5)]) # needs sage.rings.number_field sage.symbolic
1
- sage: U. = PolynomialRing(ZZ,3); U
+ sage: U. = PolynomialRing(ZZ, 3); U
Multivariate Polynomial Ring in x, y, z over Integer Ring
sage: U.krull_dimension()
4
@@ -1732,9 +1732,9 @@ def quotient(x, y, *args, **kwds):
EXAMPLES::
- sage: quotient(5,6)
+ sage: quotient(5, 6)
5/6
- sage: quotient(5.,6.)
+ sage: quotient(5., 6.)
0.833333333333333
sage: R. = ZZ[]; R
Univariate Polynomial Ring in x over Integer Ring
diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py
index db573d16e66..6b9332686d0 100644
--- a/src/sage/misc/latex.py
+++ b/src/sage/misc/latex.py
@@ -2270,7 +2270,7 @@ def latex_variable_name(x, is_fname=False):
return latex_varify(prefix, is_fname)
-class LatexExamples():
+class LatexExamples:
r"""
A catalogue of Sage objects with complicated ``_latex_`` methods.
Use these for testing :func:`latex`, :func:`view`, the Typeset
diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py
index c21bdefb643..35634b16634 100644
--- a/src/sage/misc/latex_standalone.py
+++ b/src/sage/misc/latex_standalone.py
@@ -290,8 +290,8 @@ class Standalone(SageObject):
"""
def __init__(self, content, document_class_options=None,
- standalone_config=None, usepackage=None, macros=None,
- use_sage_preamble=False):
+ standalone_config=None, usepackage=None, macros=None,
+ use_sage_preamble=False):
r"""
See :class:`Standalone` for full information.
@@ -337,10 +337,10 @@ def _latex_file_header_lines(self):
lines.append(r"\documentclass[{}]{{standalone}}".format(options))
else:
lines.append(r"\documentclass{standalone}")
- for config in self._standalone_config:
- lines.append(r"\standaloneconfig{{{}}}".format(config))
- for package in self._usepackage:
- lines.append(r"\usepackage{{{}}}".format(package))
+ lines.extend(r"\standaloneconfig{{{}}}".format(config)
+ for config in self._standalone_config)
+ lines.extend(r"\usepackage{{{}}}".format(package)
+ for package in self._usepackage)
lines.extend(self._macros)
return lines
@@ -1029,7 +1029,7 @@ def svg(self, filename=None, view=True, program='pdftocairo'):
cmd = ['pdf2svg', temp_filename_pdf, temp_filename_svg]
else:
raise ValueError("program(={}) should be 'pdftocairo' or"
- " 'pdf2svg'".format(program))
+ " 'pdf2svg'".format(program))
# convert to svg
result = run(cmd, capture_output=True, text=True)
@@ -1146,7 +1146,7 @@ def eps(self, filename=None, view=True, program='dvips'):
cmd = ['dvips', '-E', '-o', temp_filename_eps, temp_filename_dvi]
else:
raise ValueError("program(={}) should be 'pdftocairo' or"
- " 'dvips'".format(program))
+ " 'dvips'".format(program))
# convert to eps
result = run(cmd, capture_output=True, text=True)
@@ -1308,6 +1308,7 @@ def save(self, filename, **kwds):
raise ValueError("allowed file extensions for images are "
".pdf, .png, .svg, .eps, .dvi!")
+
class TikzPicture(Standalone):
r"""
A TikzPicture embedded in a LaTeX standalone document class.
@@ -1655,8 +1656,8 @@ def from_graph(cls, graph, merge_multiedges=True,
from sage.graphs.graph import Graph
graph = Graph(edges, format='list_of_edges', loops=loops)
- options = dict(format='dot2tex', edge_labels=True,
- color_by_label=False, prog='dot', rankdir='down')
+ options = {'format': 'dot2tex', 'edge_labels': True,
+ 'color_by_label': False, 'prog': 'dot', 'rankdir': 'down'}
options.update(kwds)
graph.latex_options().set_options(**options)
@@ -1666,7 +1667,7 @@ def from_graph(cls, graph, merge_multiedges=True,
@classmethod
@experimental(issue_number=20343)
def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True,
- merge_label_function=tuple):
+ merge_label_function=tuple):
r"""
Convert a graph with positions defined for vertices to a tikzpicture.
diff --git a/src/sage/misc/lazy_import.pyx b/src/sage/misc/lazy_import.pyx
index d25b24a10dc..79eda9dec43 100644
--- a/src/sage/misc/lazy_import.pyx
+++ b/src/sage/misc/lazy_import.pyx
@@ -1076,7 +1076,8 @@ def lazy_import(module, names, as_=None, *,
....: deprecation=14275)
sage: my_Qp(5) # needs sage.rings.padics
doctest:...: DeprecationWarning:
- Importing my_Qp from here is deprecated; please use "from sage.rings.padics.factory import Qp as my_Qp" instead.
+ Importing my_Qp from here is deprecated;
+ please use "from sage.rings.padics.factory import Qp as my_Qp" instead.
See https://github.com/sagemath/sage/issues/14275 for details.
5-adic Field with capped relative precision 20
@@ -1096,10 +1097,12 @@ def lazy_import(module, names, as_=None, *,
....: feature=PythonModule('ppl', spkg='pplpy', type='standard'))
sage: equation # needs pplpy