diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index cce6c45acac..cdae3a49df8 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -62,10 +62,12 @@ for block in diff_blocks: match = re.search(r'^diff --git a/(.*) b/\1', block, flags=re.MULTILINE) if match: doc = match.group(1) - path = 'html/' + doc file_path = os.path.join('$DOC_REPOSITORY', doc) - with open(file_path, 'r') as file: - content = file.readlines() + try: + with open(file_path, 'r') as file: + content = file.readlines() + except FileNotFoundError: + content = [] count = 0 for line in block.splitlines(): if line.startswith('@@ -'): @@ -77,8 +79,10 @@ for block in diff_blocks: count += 1 content[i] = f'' + content[i] break - with open(file_path, 'w') as file: - file.writelines(content) + if content: + with open(file_path, 'w') as file: + file.writelines(content) + path = 'html/' + doc hunks = ' '.join(f'#{i + 1}' for i in range(count)) out_blocks.append(f'
{doc} ' + hunks + '
' + '\n'
diff --git a/.github/sync_labels.py b/.github/sync_labels.py
index 97ddf039a16..dbd8566c517 100755
--- a/.github/sync_labels.py
+++ b/.github/sync_labels.py
@@ -141,6 +141,7 @@ def __init__(self, url, actor):
self._commits = None
self._commit_date = None
self._bot_login = None
+ self._gh_version = None
s = url.split('/')
self._owner = s[3]
@@ -235,13 +236,30 @@ def bot_login(self):
"""
if self._bot_login:
return self._bot_login
- cmd = 'gh auth status'
from subprocess import run
+ cmd = 'gh version'
+ capt = run(cmd, shell=True, capture_output=True)
+ self._gh_version = str(capt.stdout).split('\\n')[0]
+ info('version: %s' % self._gh_version)
+ cmd = 'gh auth status'
capt = run(cmd, shell=True, capture_output=True)
- l = str(capt.stderr).split()
- if not 'as' in l:
- l = str(capt.stdout).split()
- self._bot_login = l[l.index('as')+1]
+ errtxt = str(capt.stderr)
+ outtxt = str(capt.stdout)
+ debug('auth status err: %s' % errtxt)
+ debug('auth status out: %s' % outtxt)
+ def read_login(txt, position_mark):
+ for t in txt:
+ for p in position_mark:
+ # the output text has changed from as to account
+ # around version 2.40.0
+ l = t.split()
+ if p in l:
+ return l[l.index(p)+1]
+ self._bot_login = read_login([errtxt, outtxt], ['account', 'as'])
+ if not self._bot_login:
+ self._bot_login = default_bot
+ warning('Bot is unknown')
+ return self._bot_login
if self._bot_login.endswith('[bot]'):
self._bot_login = self._bot_login.split('[bot]')[0]
info('Bot is %s' % self._bot_login)
diff --git a/.github/workflows/ci-conda-known-test-failures.json b/.github/workflows/ci-conda-known-test-failures.json
new file mode 100644
index 00000000000..2d828ac98fb
--- /dev/null
+++ b/.github/workflows/ci-conda-known-test-failures.json
@@ -0,0 +1,53 @@
+{
+ "sage_setup.clean": {
+ "failed": true
+ },
+ "sage.combinat.cluster_algebra_quiver.quiver": {
+ "failed": true
+ },
+ "sage.geometry.cone": {
+ "failed": true
+ },
+ "sage.groups.matrix_gps.finitely_generated_gap": {
+ "failed": true
+ },
+ "sage.interfaces.expect": {
+ "failed": true
+ },
+ "sage.libs.gap.element": {
+ "failed": true
+ },
+ "sage.libs.singular.singular": {
+ "failed": true
+ },
+ "sage.matrix.matrix2": {
+ "failed": true
+ },
+ "sage.matrix.matrix_integer_sparse": {
+ "failed": true
+ },
+ "sage.misc.lazy_import": {
+ "failed": true
+ },
+ "sage.misc.weak_dict": {
+ "failed": true
+ },
+ "sage.modular.modform.l_series_gross_zagier": {
+ "failed": true
+ },
+ "sage.rings.function_field.drinfeld_modules.morphism": {
+ "failed": true
+ },
+ "sage.rings.polynomial.multi_polynomial_ideal": {
+ "failed": true
+ },
+ "sage.rings.polynomial.multi_polynomial_libsingular": {
+ "failed": true
+ },
+ "sage.rings.polynomial.skew_polynomial_finite_field": {
+ "failed": true
+ },
+ "sage.tests.gap_packages": {
+ "failed": true
+ }
+}
diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml
index 3b928fef58d..6432b7a0a6a 100644
--- a/.github/workflows/ci-conda.yml
+++ b/.github/workflows/ci-conda.yml
@@ -92,7 +92,7 @@ jobs:
- name: Test
if: success() || failure()
shell: bash -l {0}
- run: ./sage -t --all -p0
+ run: ./sage -t --all --baseline-stats-path=.github/workflows/ci-conda-known-test-failures.json -p0
- name: Print logs
if: always()
diff --git a/CITATION.cff b/CITATION.cff
index d05315fa584..cc293723139 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -4,8 +4,8 @@ title: SageMath
abstract: SageMath is a free open-source mathematics software system.
authors:
- name: "The SageMath Developers"
-version: 10.3.beta2
+version: 10.3.beta3
doi: 10.5281/zenodo.593563
-date-released: 2023-12-13
+date-released: 2023-12-18
repository-code: "https://github.com/sagemath/sage"
url: "https://www.sagemath.org/"
diff --git a/README.md b/README.md
index 4720c2b87ab..422582292af 100644
--- a/README.md
+++ b/README.md
@@ -424,8 +424,8 @@ For installation of `sage` in python using `pip` you need to install `sagemath-s
$ python3 -m pip install sage_conf
$ ls $(sage-config SAGE_SPKG_WHEELS)
- $ python3 -m pip install $(sage-config SAGE_SPKG_WHEELS)/*.whl
- $ python3 -m pip install sagemath-standard
+ $ python3 -m pip install $(sage-config SAGE_SPKG_WHEELS)/*.whl sage_setup
+ $ python3 -m pip install --no-build-isolation sagemath-standard
You need to install `sage_conf`, a wheelhouse of various python packages. You can list the wheels using `ls $(sage-config SAGE_SPKG_WHEELS)`. After manual installation of these wheels, you can install the sage library, `sagemath-standard`.
diff --git a/VERSION.txt b/VERSION.txt
index a6d9f480c1e..ed9ec729cc0 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1 +1 @@
-SageMath version 10.3.beta2, Release Date: 2023-12-13
+SageMath version 10.3.beta3, Release Date: 2023-12-18
diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers
index 2dc56b62466..9def7350a37 100644
--- a/build/bin/sage-dist-helpers
+++ b/build/bin/sage-dist-helpers
@@ -289,8 +289,6 @@ sdh_pip_install() {
sdh_pip_editable_install() {
echo "Installing $PKG_NAME (editable mode)"
- # Until https://github.com/sagemath/sage/issues/34209 switches us to PEP 660 editable wheels
- export SETUPTOOLS_ENABLE_FEATURES=legacy-editable
python3 -m pip install --verbose --no-deps --no-index --no-build-isolation --isolated --editable "$@" || \
sdh_die "Error installing $PKG_NAME"
}
diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg
index ed2e9090cf8..70256370549 100755
--- a/build/bin/sage-spkg
+++ b/build/bin/sage-spkg
@@ -341,7 +341,7 @@ fi
PKG_BASE_VER=`echo $PKG_VER | sed 's/\.p[0-9][0-9]*$//'`
if [ -f "$PKG_SCRIPTS/checksums.ini" ]; then
# Normal/wheel package
- PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini" | sed "s/VERSION/$PKG_BASE_VER/"`
+ PKG_NAME_UPSTREAM=`lookup_param tarball "$PKG_SCRIPTS/checksums.ini"`
fi
# Set the $SAGE_DESTDIR variable to be passed to the spkg-install
@@ -396,11 +396,11 @@ ensure_pkg_src() { ###############################################
if [ ! -f "$PKG_SRC" ]; then
if [ -n "$PKG_NAME_UPSTREAM" ]; then
# Normal or wheel package
- if ! sage-download-file $SAGE_DOWNLOAD_FILE_OPTIONS "$PKG_NAME_UPSTREAM"; then
- error_msg "Error downloading $PKG_NAME_UPSTREAM"
+ PKG_SRC=$(sage-package download $SAGE_DOWNLOAD_FILE_OPTIONS $PKG_BASE)
+ if [ $? != 0 ]; then
+ error_msg "Error downloading tarball of $PKG_BASE"
exit 1
fi
- PKG_SRC="$SAGE_DISTFILES/$PKG_NAME_UPSTREAM"
# Do a final check that PKG_SRC is a file with an absolute path
cd /
if [ ! -f "$PKG_SRC" ]; then
diff --git a/build/pkgs/4ti2/checksums.ini b/build/pkgs/4ti2/checksums.ini
index 275cf157a6a..047f39ece1d 100644
--- a/build/pkgs/4ti2/checksums.ini
+++ b/build/pkgs/4ti2/checksums.ini
@@ -1,5 +1,5 @@
-tarball=4ti2-VERSION.tar.gz
+tarball=4ti2-${VERSION}.tar.gz
sha1=3d41f30ea3ef94c293eae30c087494269fc1a6b9
md5=1215872325ddfc561865ecb22b2bccb2
cksum=2439180289
-upstream_url=https://github.com/4ti2/4ti2/releases/download/Release_1_6_10/4ti2-1.6.10.tar.gz
+upstream_url=https://github.com/4ti2/4ti2/releases/download/Release_${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}/4ti2-${VERSION}.tar.gz
diff --git a/build/pkgs/boost_cropped/spkg-legacy-uninstall b/build/pkgs/boost_cropped/spkg-legacy-uninstall
deleted file mode 100755
index ef170323b6a..00000000000
--- a/build/pkgs/boost_cropped/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "${SAGE_LOCAL}"/include/boost
diff --git a/build/pkgs/brial/spkg-legacy-uninstall b/build/pkgs/brial/spkg-legacy-uninstall
deleted file mode 100755
index 59e01958685..00000000000
--- a/build/pkgs/brial/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-echo "Cleaning out old PolyBoRi and BRiAl installations"
-rm -f "$SAGE_LOCAL"/lib/lib{polybori,brial}*
-rm -rf "$SAGE_LOCAL"/include/polybori*
-rm -rf "$SAGE_LOCAL"/share/polybori
diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini
index deadbf67dc3..bdd96db7026 100644
--- a/build/pkgs/configure/checksums.ini
+++ b/build/pkgs/configure/checksums.ini
@@ -1,4 +1,4 @@
tarball=configure-VERSION.tar.gz
-sha1=7f2fe8137f5998559f8d3a0a7f965adf6d5ebc78
-md5=586738771174a2d26d29ce7ba571a95d
-cksum=1156937644
+sha1=2389d2b093493c568deda190ffc326ff2b835169
+md5=545e80b50deb4efa46f14d0a543ba98f
+cksum=169905223
diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt
index 1596e721179..033bf42aaae 100644
--- a/build/pkgs/configure/package-version.txt
+++ b/build/pkgs/configure/package-version.txt
@@ -1 +1 @@
-2395f7bb45fd7da537a953055df5e0a70dd96c6a
+73e52a419812253c3c3ce72bab7f1a5ddf4c0461
diff --git a/build/pkgs/cunningham_tables/checksums.ini b/build/pkgs/cunningham_tables/checksums.ini
index d6de22c0850..360d9b647b7 100644
--- a/build/pkgs/cunningham_tables/checksums.ini
+++ b/build/pkgs/cunningham_tables/checksums.ini
@@ -2,4 +2,4 @@ tarball=cunningham_tables-VERSION.tar.gz
sha1=8bea1a113d85bb9c37d8f213dd19525d9d026f22
md5=e71b32f12e9a46c1c86e275e8441a06b
cksum=1990403877
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/cunningham_tables-1.0.tar.gz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/cunningham_tables-VERSION.tar.gz
diff --git a/build/pkgs/cvxopt/spkg-install.in b/build/pkgs/cvxopt/spkg-install.in
index 0081c40f819..8179125fc9a 100644
--- a/build/pkgs/cvxopt/spkg-install.in
+++ b/build/pkgs/cvxopt/spkg-install.in
@@ -82,10 +82,6 @@ sdh_pip_install .
if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then
cd doc
- # checking to see if there is previously installed documentation.
- if [ -d $SAGE_LOCAL/share/doc/cvxopt/html ] ; then
- rm -rf $SAGE_LOCAL/share/doc/cvxopt/html
- fi
mkdir -p "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html"
cp -r html/* "${SAGE_DESTDIR}${SAGE_LOCAL}/share/doc/cvxopt/html/"
fi
diff --git a/build/pkgs/ecl/spkg-legacy-uninstall b/build/pkgs/ecl/spkg-legacy-uninstall
deleted file mode 100755
index 375d99ebcf1..00000000000
--- a/build/pkgs/ecl/spkg-legacy-uninstall
+++ /dev/null
@@ -1,6 +0,0 @@
-#! /usr/bin/env bash
-rm -f "$SAGE_LOCAL"/bin/ecl
-rm -rf "$SAGE_LOCAL"/include/ecl
-rm -rf "$SAGE_LOCAL"/lib/ecl
-rm -rf "$SAGE_LOCAL"/lib/ecl-*
-rm -rf "$SAGE_LOCAL"/lib/libecl.*
diff --git a/build/pkgs/eclib/spkg-install.in b/build/pkgs/eclib/spkg-install.in
index 2a83bdb5235..4998cff1676 100644
--- a/build/pkgs/eclib/spkg-install.in
+++ b/build/pkgs/eclib/spkg-install.in
@@ -3,17 +3,6 @@ CXXFLAGS="$CXXFLAGS_O3_NON_NATIVE"
export CFLAGS CXXFLAGS
-
-echo "Deleting old versions of eclib libraries, which"
-echo "would interfere with new builds..."
-# Delete any pre-autotools libraries:
-rm -f "$SAGE_LOCAL"/lib/lib{curvesntl,g0nntl,jcntl,rankntl,mwrank}.*
-# Delete autotools libraries:
-rm -f "$SAGE_LOCAL"/lib/lib{e,j}c.*
-echo "Deleting old include directory..."
-rm -rf "$SAGE_LOCAL"/include/eclib/
-
-
cd src/
#############################################################
diff --git a/build/pkgs/ecm/spkg-install.in b/build/pkgs/ecm/spkg-install.in
index edc6bb2df5f..03c6c109d7d 100644
--- a/build/pkgs/ecm/spkg-install.in
+++ b/build/pkgs/ecm/spkg-install.in
@@ -232,17 +232,6 @@ sdh_configure $SAGE_CONFIGURE_GMP $ECM_CONFIGURE
sdh_make
-###############################################################################
-# Remove old executable/header/libraries/manpage:
-###############################################################################
-
-echo
-echo "Build succeeded. Now removing old binary, header file, manual page and libraries..."
-rm -f "$SAGE_LOCAL"/bin/ecm
-rm -f "$SAGE_LOCAL"/lib/libecm.*
-rm -f "$SAGE_LOCAL"/include/ecm.h
-rm -f "$SAGE_LOCAL"/share/man/man1/ecm.1
-
###############################################################################
# Now install ECM:
###############################################################################
diff --git a/build/pkgs/fplll/spkg-install.in b/build/pkgs/fplll/spkg-install.in
index 68c0cb70ad2..d898db82687 100644
--- a/build/pkgs/fplll/spkg-install.in
+++ b/build/pkgs/fplll/spkg-install.in
@@ -22,7 +22,3 @@ export CXX="$CXX"
sdh_configure $CONFIGUREFLAGS
sdh_make
sdh_make_install
-
-# Pretend that the "libfplll" package is not installed. This is needed to
-# support renaming libfplll -> fplll done on Trac #24042
-rm -f "$SAGE_SPKG_INST/"libfplll-*
diff --git a/build/pkgs/freetype/spkg-install.in b/build/pkgs/freetype/spkg-install.in
index c1615626e72..9d8c0075ec0 100644
--- a/build/pkgs/freetype/spkg-install.in
+++ b/build/pkgs/freetype/spkg-install.in
@@ -4,9 +4,3 @@ cd src
GNUMAKE=${MAKE} sdh_configure --enable-freetype-config $FREETYPE_CONFIGURE
sdh_make
sdh_make_install
-
-# The following file may be present from old builds of freetype, and
-# its presence can break the matplotlib build. So remove it. (The
-# current version is $SAGE_LOCAL/include/freetype2/ftbuild.h.)
-
-rm -f "$SAGE_LOCAL/include/ft2build.h"
diff --git a/build/pkgs/frobby/spkg-install.in b/build/pkgs/frobby/spkg-install.in
index 1a97cc6b682..60ed2c67207 100644
--- a/build/pkgs/frobby/spkg-install.in
+++ b/build/pkgs/frobby/spkg-install.in
@@ -1,4 +1,3 @@
-rm -rf "$SAGE_LOCAL/bin/frobby"
GMP_INC_DIR="$SAGE_LOCAL/include"; export GMP_INC_DIR
ldflags="-Wl,-rpath,$SAGE_LOCAL/lib -L$SAGE_LOCAL/lib/ -lgmpxx -lgmp"; export ldflags
@@ -12,14 +11,4 @@ if [ $? -ne 0 ]; then
exit 1
fi
-if [ ! -f bin/release/frobby ]; then
- echo "Frobby executable not found."
- exit 1
-fi
-
-$CP bin/release/frobby "$SAGE_LOCAL/bin/"
-
-if [ ! -f "$SAGE_LOCAL/bin/frobby" ]; then
- echo "Frobby executable not copied."
- exit 1
-fi
+sdh_install bin/release/frobby "$SAGE_LOCAL/bin"
diff --git a/build/pkgs/frobby/spkg-legacy-uninstall b/build/pkgs/frobby/spkg-legacy-uninstall
new file mode 100755
index 00000000000..a262741b2a9
--- /dev/null
+++ b/build/pkgs/frobby/spkg-legacy-uninstall
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+rm -f "$SAGE_LOCAL/bin/frobby"
diff --git a/build/pkgs/gap/distros/debian.txt b/build/pkgs/gap/distros/debian.txt
index 883a55b9c20..66efa2824a6 100644
--- a/build/pkgs/gap/distros/debian.txt
+++ b/build/pkgs/gap/distros/debian.txt
@@ -1 +1,2 @@
+gap
libgap-dev
diff --git a/build/pkgs/gap/distros/fedora.txt b/build/pkgs/gap/distros/fedora.txt
new file mode 100644
index 00000000000..6c561683bf4
--- /dev/null
+++ b/build/pkgs/gap/distros/fedora.txt
@@ -0,0 +1,5 @@
+gap
+gap-core
+gap-devel
+gap-libs
+libgap
diff --git a/build/pkgs/gap/spkg-configure.m4 b/build/pkgs/gap/spkg-configure.m4
new file mode 100644
index 00000000000..72a0a999da7
--- /dev/null
+++ b/build/pkgs/gap/spkg-configure.m4
@@ -0,0 +1,96 @@
+SAGE_SPKG_CONFIGURE([gap], [
+ # Default to installing the SPKG, if the check is run at all.
+ sage_spkg_install_gap=yes
+
+ m4_pushdef([GAP_MINVER],["4.12.2"])
+
+ SAGE_SPKG_DEPCHECK([ncurses readline zlib], [
+ AC_PATH_PROG(GAP, gap)
+ AS_IF([test -n "${GAP}"], [
+ AC_MSG_CHECKING([for gap version GAP_MINVER or newer])
+
+ # GAP will later add the "user" path to the list of root paths
+ # so long as we don't initialize GAP with -r in Sage. But we
+ # don't want to include it in the hard-coded list.
+ GAPRUN="${GAP} -r -q --bare --nointeract -c"
+ _cmd='Display(GAPInfo.KernelInfo.KERNEL_VERSION);'
+ GAP_VERSION=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+ AX_COMPARE_VERSION(["${GAP_VERSION}"], [ge], [GAP_MINVER], [
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([for gap root paths])
+ _cmd='Display(JoinStringsWithSeparator(GAPInfo.RootPaths,";"));'
+ SYS_GAP_ROOT_PATHS=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+ AC_MSG_RESULT([$SYS_GAP_ROOT_PATHS])
+ AS_IF([test -n "${SYS_GAP_ROOT_PATHS}"], [
+ AC_MSG_CHECKING([for the PrimGrp, SmallGrp, and TransGrp packages])
+ # Check for a very minimal set of packages without which the
+ # sage test suite will fail. The crazy thing below is a
+ # "quadrigraph" for a square bracket.
+ _cmd="Display(@<:@"
+ _cmd="${_cmd} TestPackageAvailability(\"PrimGrp\"),"
+ _cmd="${_cmd} TestPackageAvailability(\"SmallGrp\"),"
+ _cmd="${_cmd} TestPackageAvailability(\"TransGrp\")"
+ _cmd="${_cmd} @:>@);"
+ _output=$( ${GAPRUN} "${_cmd}" 2>/dev/null )
+ AS_IF([test $? -eq 0], [
+ AS_CASE([$_output],
+ [*fail*],[AC_MSG_RESULT([no (at least one package missing)])],[
+ # default case, i.e. no "fail"
+ AC_MSG_RESULT([yes])
+
+ AC_MSG_CHECKING([if we can link against libgap])
+ # That was all for the CLI. Now we check for libgap,
+ # too. There's a long list of headers we need in
+ # src/sage/libs/gap/gap_includes.pxd, but libgap-api.h
+ # combined with the version test above should be
+ # sufficient even on systems where the headers are
+ # packaged separately.
+ _old_libs=$LIBS
+ LIBS="${LIBS} -lgap"
+ AC_LANG_PUSH([C])
+ AC_LINK_IFELSE([
+ AC_LANG_PROGRAM(
+ [[#include ]],
+ [[
+ int main(int argc, char** argv) {
+ GAP_Initialize(0, 0, 0, 0, 0);
+ return 0;
+ }
+ ]])
+ ],[
+ AC_MSG_RESULT([yes])
+ sage_spkg_install_gap=no
+ ],[
+ AC_MSG_RESULT([no])
+ ])
+ AC_LANG_POP
+ LIBS="${_old_libs}"
+ ])
+ ], [
+ # The gap command itself failed
+ AC_MSG_RESULT([no (package check command failed)])
+ ])
+ ])
+ ],[
+ # Version too old
+ AC_MSG_RESULT([no])
+ ])
+ ])
+ ])
+
+ m4_popdef([GAP_MINVER])
+],[],[],[
+ # This is the post-check phase, where we make sage-conf
+ # substitutions, in this case of GAP_ROOT_PATHS. We begin with the
+ # two root paths used by the sage distribution. The '${prefix}' is
+ # a magic string that sage-conf will replace.
+ GAP_ROOT_PATHS='${prefix}/lib/gap;${prefix}/share/gap';
+
+ AS_IF([test "${sage_spkg_install_gap}" = "no"],[
+ # If we're using the system GAP, append the system root
+ # paths to the existing two sage paths.
+ GAP_ROOT_PATHS="${GAP_ROOT_PATHS};${SYS_GAP_ROOT_PATHS}"
+ ])
+
+ AC_SUBST(GAP_ROOT_PATHS, "${GAP_ROOT_PATHS}")
+])
diff --git a/build/pkgs/gap/spkg-install.in b/build/pkgs/gap/spkg-install.in
index 47a7a8700ac..7d207a47a48 100644
--- a/build/pkgs/gap/spkg-install.in
+++ b/build/pkgs/gap/spkg-install.in
@@ -16,46 +16,43 @@ if [ "$SAGE_DEBUG" = yes ] ; then
export CFLAGS="-O0 -g3 -DDEBUG_MASTERPOINTERS -DDEBUG_GLOBAL_BAGS -DDEBUG_FUNCTIONS_BAGS $CFLAGS"
fi
-# LDFLAGS hack below needed by Semigroups package
-sdh_configure $SAGE_CONFIGURE_GMP LDFLAGS="-pthread" --prefix=$SAGE_LOCAL
+sdh_configure $SAGE_CONFIGURE_GMP --prefix=$SAGE_LOCAL
sdh_make
-
sdh_make_install
-# sdh_make install-headers install-libgap
-# The 'packagemanager' package expects this https://github.com/gap-packages/PackageManager/issues/105
-mkdir -p "$SAGE_LOCAL/lib/gap/bin"
# Install only the minimal packages GAP needs to run
sdh_install pkg/gapdoc pkg/primgrp pkg/smallgrp pkg/transgrp "$GAP_ROOT"/pkg
-# Install additional packages that are not strictly required, but that are
-# typically "expected" to be loaded: These are the default packages that are
-# autoloaded at GAP startup (via the PackagesToLoad UserPreference) with an
-# out-of-the-box GAP installation; see
-# https://github.com/sagemath/sage/issues/22626#comment:393 for discussion on this
-#
-# Also include atlasrep which is a dependency of tomlib
+# Install additional packages that are automatically loaded in the
+# default GAP configuration. The list can be found in lib/package.gi
+# as part of the "PackagesToLoad" user preference. Also include
+# atlasrep because it is a dependency of tomlib.
sdh_install \
+ pkg/alnuth \
pkg/atlasrep \
- pkg/autodoc \
pkg/autpgrp \
- pkg/alnuth \
pkg/crisp \
pkg/ctbllib \
pkg/factint \
pkg/fga \
pkg/irredsol \
pkg/laguna \
- pkg/packagemanager \
pkg/polenta \
pkg/polycyclic \
- pkg/radiroot \
pkg/resclasses \
pkg/sophus \
pkg/tomlib \
- pkg/utils \
"$GAP_ROOT"/pkg
+# Finally, install packagemanager for the people who reject both
+# sage's and their system's package managers. We have to create
+# the local bin directory first:
+#
+# https://github.com/gap-packages/PackageManager/issues/105
+#
+mkdir -p "$SAGE_LOCAL/lib/gap/bin"
+sdh_install pkg/packagemanager "$GAP_ROOT"/pkg
+
# TODO: This seems unnecessary--we are already installing all of doc/ to
# GAP_ROOT, which is necessary for some functionality in GAP to work. Do
# we need this? Maybe doc/gap could just be a symlink to gap/doc??
diff --git a/build/pkgs/gap/spkg-legacy-uninstall b/build/pkgs/gap/spkg-legacy-uninstall
deleted file mode 100755
index a8e5c59e1fb..00000000000
--- a/build/pkgs/gap/spkg-legacy-uninstall
+++ /dev/null
@@ -1,11 +0,0 @@
-#! /usr/bin/env bash
-# Remove existing GAP 4.x install(s)
-rm -rf "$SAGE_LOCAL/gap/gap-4."*
-rm -rf "$SAGE_SHARE/gap"
-rm -f "$SAGE_LOCAL/gap/latest"
-rm -f "$SAGE_LOCAL/bin/gap"
-rm -f "$SAGE_LOCAL/bin/gac"
-
-# Remove old libgap headers and library
-rm -rf "$SAGE_LOCAL/include/gap"
-rm -rf "$SAGE_LOCAL/lib/gap"
diff --git a/build/pkgs/gap_packages/SPKG.rst b/build/pkgs/gap_packages/SPKG.rst
index 67d3256637a..96238eef804 100644
--- a/build/pkgs/gap_packages/SPKG.rst
+++ b/build/pkgs/gap_packages/SPKG.rst
@@ -7,6 +7,20 @@ Description
Several "official" and "undeposited" GAP packages available from
https://www.gap-system.org/Packages/packages.html
+Installing this SPKG will install the corresponding GAP packages, but
+before you can use them in Sage, they still have to be loaded into
+either the GAP interface or libgap::
+
+ sage: gap.eval('LoadPackage("Grape")') # optional - gap_packages
+ 'true'
+ sage: libgap.LoadPackage("Grape") # optional - gap_packages
+ true
+
+Those correspond to::
+
+ gap> LoadPackage("Grape");
+
+within the GAP interface and libgap, respectively.
Upstream Contact
----------------
diff --git a/build/pkgs/gap_packages/spkg-install.in b/build/pkgs/gap_packages/spkg-install.in
index 6dff182a48f..7005cc3d322 100644
--- a/build/pkgs/gap_packages/spkg-install.in
+++ b/build/pkgs/gap_packages/spkg-install.in
@@ -1,5 +1,34 @@
-GAP_ROOT="$SAGE_LOCAL/lib/gap"
-PKG_DIR="$GAP_ROOT/pkg"
+# Ask GAP for the directory where sysinfo.gap lives. This is to
+# support system GAP installations. This root-path gathering
+# command is borrowed from gap's spkg-configure.m4 and modified
+# to separate the paths with spaces.
+GAPRUN="gap -r -q --bare --nointeract -c"
+_cmd='Display(JoinStringsWithSeparator(GAPInfo.RootPaths," "));'
+GAP_ROOT_PATHS=$(${GAPRUN} "${_cmd}")
+
+# Loop though GAP_ROOT_PATHS looking for sysinfo.gap
+GAP_ROOT=""
+for grp in $GAP_ROOT_PATHS; do
+ if [ -f "${grp}/sysinfo.gap" ]; then
+ GAP_ROOT=$grp
+ echo "found GAP root $GAP_ROOT"
+ break
+ fi
+done
+
+# Try the old sage default if nothing else worked.
+if [ -z "$GAP_ROOT" ]; then
+ GAP_ROOT="$SAGE_LOCAL/lib/gap"
+ echo "falling back to GAP root $GAP_ROOT"
+fi
+
+# And finally, throw an error ASAP if the build is going to fail anyway.
+if [ ! -f "${GAP_ROOT}/sysinfo.gap" ]; then
+ sdh_die "no sysinfo.gap in your gap root"
+fi
+
+# Where to install these packages
+PKG_DIR="$SAGE_LOCAL/lib/gap/pkg"
PKG_SRC_DIR="$(pwd)/src/pkg"
cd "$PKG_SRC_DIR"
@@ -12,6 +41,7 @@ cd "$PKG_SRC_DIR"
sdh_install \
aclib \
+ autodoc \
corelg \
crime \
cryst \
@@ -31,11 +61,13 @@ sdh_install \
polymaking \
qpa \
quagroup \
+ radiroot \
repsn \
singular \
sla \
sonata \
toric \
+ utils \
"$PKG_DIR"
install_compiled_pkg()
diff --git a/build/pkgs/gf2x/checksums.ini b/build/pkgs/gf2x/checksums.ini
index 10a93c2c1bc..0eaf7657e40 100644
--- a/build/pkgs/gf2x/checksums.ini
+++ b/build/pkgs/gf2x/checksums.ini
@@ -2,4 +2,4 @@ tarball=gf2x-VERSION.tar.gz
sha1=1b9c7e14031afc5488b9aa27f5501f78c90f00b4
md5=842f087ce423c279dced26b85b0fd1d0
cksum=3368093312
-upstream_url=https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-1.3.0.tar.gz
+upstream_url=https://gitlab.inria.fr/gf2x/gf2x/uploads/c46b1047ba841c20d1225ae73ad6e4cd/gf2x-VERSION.tar.gz
diff --git a/build/pkgs/gf2x/spkg-install.in b/build/pkgs/gf2x/spkg-install.in
index 057be0a5953..c8e4c83e6d2 100644
--- a/build/pkgs/gf2x/spkg-install.in
+++ b/build/pkgs/gf2x/spkg-install.in
@@ -1,7 +1,3 @@
-echo "Deleting old gf2x files."
-rm -f "$SAGE_LOCAL"/lib/libgf2x*
-rm -rf "$SAGE_LOCAL"/include/gf2x*
-
cd src
# Use newer version of config.guess and config.sub (see Trac #19727)
diff --git a/build/pkgs/gfan/spkg-legacy-uninstall b/build/pkgs/gfan/spkg-legacy-uninstall
deleted file mode 100755
index 74552b0b71c..00000000000
--- a/build/pkgs/gfan/spkg-legacy-uninstall
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /usr/bin/env bash
-echo "Removing old version of gfan (if any)..."
-rm -f "$SAGE_LOCAL"/bin/gfan*
diff --git a/build/pkgs/giac/spkg-install.in b/build/pkgs/giac/spkg-install.in
index e3eb86e6034..ceed3899be6 100644
--- a/build/pkgs/giac/spkg-install.in
+++ b/build/pkgs/giac/spkg-install.in
@@ -60,24 +60,6 @@ sdh_configure --disable-gui --disable-ao "$DISABLENLS" --enable-png=no --disable
sdh_make
-#############################################################
-# Clean old install
-#############################################################
-echo "Cleaning giac..."
-rm -f ${SAGE_LOCAL}/lib/libgiac*
-rm -f ${SAGE_LOCAL}/bin/icas
-rm -f ${SAGE_LOCAL}/bin/xcas
-rm -f ${SAGE_LOCAL}/bin/cas_help
-rm -f ${SAGE_LOCAL}/bin/pgiac
-rm -f ${SAGE_LOCAL}/bin/en_cas_help
-rm -f ${SAGE_LOCAL}/bin/es_cas_help
-rm -f ${SAGE_LOCAL}/bin/fr_cas_help
-rm -f ${SAGE_LOCAL}/bin/giac
-rm -f ${SAGE_LOCAL}/bin/xcasnew
-rm -rf ${SAGE_LOCAL}/share/giac
-rm -rf ${SAGE_LOCAL}/share/doc/giac
-rm -rf ${SAGE_LOCAL}/include/giac
-
#############################################################
# Install
#############################################################
diff --git a/build/pkgs/glpk/spkg-install.in b/build/pkgs/glpk/spkg-install.in
index a911c46f024..f592a562148 100644
--- a/build/pkgs/glpk/spkg-install.in
+++ b/build/pkgs/glpk/spkg-install.in
@@ -25,8 +25,4 @@ export CFLAGS LDFLAGS CPPFLAGS
sdh_configure --with-gmp --disable-static
sdh_make
-# Remove old libraries to make sure we can downgrade it if needed.
-# See https://github.com/sagemath/sage/issues/23596#comment:4 and later.
-rm -f "$SAGE_LOCAL"/lib/libglpk.*
-
sdh_make_install
diff --git a/build/pkgs/graphs/checksums.ini b/build/pkgs/graphs/checksums.ini
index 285778ef3a7..7ec4a8a6ada 100644
--- a/build/pkgs/graphs/checksums.ini
+++ b/build/pkgs/graphs/checksums.ini
@@ -2,4 +2,4 @@ tarball=graphs-VERSION.tar.bz2
sha1=c3b9fcbc92482efd6b7f6f3a33df5a78e1256aa1
md5=4357919410e8ac2611c9fe643976c8ff
cksum=2340933149
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/graphs-20210214.tar.bz2
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/graphs-VERSION.tar.bz2
diff --git a/build/pkgs/gsl/spkg-legacy-uninstall b/build/pkgs/gsl/spkg-legacy-uninstall
deleted file mode 100755
index 6c42f90b665..00000000000
--- a/build/pkgs/gsl/spkg-legacy-uninstall
+++ /dev/null
@@ -1,3 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "$SAGE_LOCAL"/include/gsl
-rm -rf "$SAGE_LOCAL"/lib/libgsl*
diff --git a/build/pkgs/ipython/spkg-install.in b/build/pkgs/ipython/spkg-install.in
index 5096a907a95..058b1344dc2 100644
--- a/build/pkgs/ipython/spkg-install.in
+++ b/build/pkgs/ipython/spkg-install.in
@@ -1,7 +1,3 @@
-# Old installations of ipython can leave a symlink which can interfere
-# with proper installation.
-rm -f "$SAGE_LOCAL"/bin/ipython
-
cd src
sdh_pip_install .
diff --git a/build/pkgs/jmol/spkg-legacy-uninstall b/build/pkgs/jmol/spkg-legacy-uninstall
deleted file mode 100755
index 6c9bd1a2c08..00000000000
--- a/build/pkgs/jmol/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-# Cleanup of previous installation
-rm -rf "${SAGE_SHARE}/jsmol/"
-rm -rf "${SAGE_SHARE}/jmol/"
-rm -f "${SAGE_LOCAL}/bin/jmol"
diff --git a/build/pkgs/kenzo/checksums.ini b/build/pkgs/kenzo/checksums.ini
index ad2a7c4cc60..7fff82ca271 100644
--- a/build/pkgs/kenzo/checksums.ini
+++ b/build/pkgs/kenzo/checksums.ini
@@ -1,5 +1,5 @@
-tarball=kenzo-1.1.10.tar.gz
-upstream_url=https://github.com/miguelmarco/kenzo/releases/download/1.1.10/kenzo-1.1.10.tar.gz
+tarball=kenzo-VERSION.tar.gz
+upstream_url=https://github.com/miguelmarco/kenzo/releases/download/VERSION/kenzo-VERSION.tar.gz
sha1=76115aae9972090d5d51fee18592fc7a79461474
md5=3a3d5350fb17304f03e614713e585ed4
cksum=2981306888
diff --git a/build/pkgs/lcalc/spkg-legacy-uninstall b/build/pkgs/lcalc/spkg-legacy-uninstall
deleted file mode 100755
index 4cacdfd9367..00000000000
--- a/build/pkgs/lcalc/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -fr "$SAGE_LOCAL"/include/libLfunction
diff --git a/build/pkgs/libbraiding/checksums.ini b/build/pkgs/libbraiding/checksums.ini
index bb76fa5b604..466c33c66a5 100644
--- a/build/pkgs/libbraiding/checksums.ini
+++ b/build/pkgs/libbraiding/checksums.ini
@@ -1,5 +1,5 @@
-tarball=libbraiding-VERSION.tar.gz
-sha1=06610e47eb243b27aea0ad399b41614fcdb179c9
-md5=5466605026b90bdca7ca20852f88b5c5
-cksum=704753563
-upstream_url=https://github.com/miguelmarco/libbraiding/releases/download/1.1/libbraiding-1.1.tar.gz
+tarball=libbraiding-VERSION-actually-VERSION.tar.gz
+sha1=b7e13778784fe1e36e7c0cbd7a4c234a090cd1b2
+md5=0513967c81b783ea66336b7ad0562534
+cksum=3619705925
+upstream_url=https://github.com/miguelmarco/libbraiding/releases/download/VERSION/libbraiding-VERSION.tar.gz
diff --git a/build/pkgs/libgd/spkg-legacy-uninstall b/build/pkgs/libgd/spkg-legacy-uninstall
deleted file mode 100755
index c67f4d2167d..00000000000
--- a/build/pkgs/libgd/spkg-legacy-uninstall
+++ /dev/null
@@ -1,5 +0,0 @@
-#! /usr/bin/env bash
-# Critical to get rid of old versions, since they will break the install, since
-# at some point one of the libraries accidently links against what's in SAGE_LOCAL,
-# instead of what is in the build directory!
-rm "$SAGE_LOCAL"/lib/libgd.*
diff --git a/build/pkgs/m4ri/spkg-install.in b/build/pkgs/m4ri/spkg-install.in
index c3ae2270406..0af3234ed16 100644
--- a/build/pkgs/m4ri/spkg-install.in
+++ b/build/pkgs/m4ri/spkg-install.in
@@ -24,9 +24,6 @@ else
DISABLE_SSE2=""
fi
-# otherwise we might run into problems with old headers
-rm -rf "$SAGE_LOCAL/include/m4ri"
-
cd src
# Configure and build M4RI
diff --git a/build/pkgs/m4rie/spkg-install.in b/build/pkgs/m4rie/spkg-install.in
index 925706f0581..bc0c13ffe1e 100644
--- a/build/pkgs/m4rie/spkg-install.in
+++ b/build/pkgs/m4rie/spkg-install.in
@@ -27,12 +27,6 @@ export CFLAGS
export CPPFLAGS
export LDFLAGS
-# otherwise we might run into problems with old headers
-
-if [ -d "$SAGE_LOCAL/include/m4rie" ]; then
- rm -rf "$SAGE_LOCAL/include/m4rie"
-fi
-
# build M4RIE
cd $ROOT_DIR/src/
diff --git a/build/pkgs/mathjax/spkg-install.in b/build/pkgs/mathjax/spkg-install.in
index 31cdb426992..2eeaf6e84b4 100644
--- a/build/pkgs/mathjax/spkg-install.in
+++ b/build/pkgs/mathjax/spkg-install.in
@@ -1,4 +1,2 @@
TARGET="${SAGE_SHARE}/mathjax"
-# Cleanup installed version
-rm -rf "${TARGET}"
sdh_install src/* "${TARGET}"
diff --git a/build/pkgs/meataxe/spkg-install.in b/build/pkgs/meataxe/spkg-install.in
index 433e5ba7c86..231519fac23 100644
--- a/build/pkgs/meataxe/spkg-install.in
+++ b/build/pkgs/meataxe/spkg-install.in
@@ -1,16 +1,3 @@
-# Delete old (Shared)MeatAxe libraries. This ensures a sane state if
-# installation of this package fails: the mtx library should exist if
-# and only if meataxe is installed. In detail: the build-time check in
-# src/setup.py checks whether or not the "meataxe" package is
-# marked as installed but the run-time check for the matrix_gfpn_dense
-# module checks whether it can be imported. We need to ensure that these
-# two conditions are equivalent, otherwise strange things can happen.
-# See also https://github.com/sagemath/sage/issues/24359#comment:154
-#
-# This also deletes the static library left behind from the installation
-# of MeatAxe (as opposed to SharedMeatAxe).
-rm -f "$SAGE_LOCAL"/lib/libmtx.*
-
# Directory where executables are installed.
export MTXBIN="$SAGE_LOCAL"/bin
@@ -34,4 +21,3 @@ sdh_make_install
if [ "$SAGE_SPKG_INSTALL_DOCS" = yes ] ; then
$MAKE doc || sdh_die "Error documenting SharedMeatAxe"
fi
-
diff --git a/build/pkgs/mpc/spkg-install.in b/build/pkgs/mpc/spkg-install.in
index b260b531f17..835bcc53ab2 100644
--- a/build/pkgs/mpc/spkg-install.in
+++ b/build/pkgs/mpc/spkg-install.in
@@ -12,13 +12,5 @@ EXTRA=""
sdh_configure $SAGE_CONFIGURE_GMP $SAGE_CONFIGURE_MPFR $EXTRA
sdh_make
-# Cleaning
-echo "Deleting old headers"
-rm -f "$SAGE_LOCAL"/include/mpc.h
-# Do not delete old libraries as this causes gcc to break during
-# parallel builds.
-# echo "Deleting old libraries"
-# rm -f "$SAGE_LOCAL"/lib/libmpc.*
-
# Installing
sdh_make_install
diff --git a/build/pkgs/mpfr/spkg-install.in b/build/pkgs/mpfr/spkg-install.in
index a0ecd15ec43..b377e0a93b6 100644
--- a/build/pkgs/mpfr/spkg-install.in
+++ b/build/pkgs/mpfr/spkg-install.in
@@ -145,14 +145,6 @@ mpfr_build()
{
mpfr_configure
sdh_make
-
- echo
- echo "Building MPFR succeeded. Now deleting old headers..."
- rm -f "$SAGE_LOCAL"/include/*mpfr*
- # Do not delete old libraries as this causes gcc to break during
- # parallel builds.
- # rm -f "$SAGE_LOCAL"/lib/libmpfr.*
-
sdh_make_install
}
diff --git a/build/pkgs/mpfrcx/spkg-legacy-uninstall b/build/pkgs/mpfrcx/spkg-legacy-uninstall
deleted file mode 100755
index 3538ccc54e4..00000000000
--- a/build/pkgs/mpfrcx/spkg-legacy-uninstall
+++ /dev/null
@@ -1,4 +0,0 @@
-#! /usr/bin/env bash
-echo "Deleting old headers"
-rm -f "$SAGE_LOCAL"/include/mpfrcx.h
-rm -f "$SAGE_LOCAL"/lib/libmpfrcx.*
diff --git a/build/pkgs/nauty/checksums.ini b/build/pkgs/nauty/checksums.ini
index a9dd1246e91..f699a05e9fc 100644
--- a/build/pkgs/nauty/checksums.ini
+++ b/build/pkgs/nauty/checksums.ini
@@ -1,5 +1,5 @@
-tarball=nautyVERSION.tar.gz
-sha1=10c39117c55c69c18c6a107110e7c08f3d873652
-md5=7a82f4209f5d552da3078c67e5af872e
-cksum=2164796643
-upstream_url=https://pallini.di.uniroma1.it/nauty2_8_6.tar.gz
+tarball=nauty${VERSION}.tar.gz
+sha1=672e9fc9dfd07201af37ee65807a9b493331ed92
+md5=16c6edc1a8747c9281041b7c7092135f
+cksum=2663136901
+upstream_url=https://pallini.di.uniroma1.it/nauty${VERSION_MAJOR}_${VERSION_MINOR}_${VERSION_MICRO}.tar.gz
diff --git a/build/pkgs/nauty/package-version.txt b/build/pkgs/nauty/package-version.txt
index 04e60eaa37a..80803faf1b9 100644
--- a/build/pkgs/nauty/package-version.txt
+++ b/build/pkgs/nauty/package-version.txt
@@ -1 +1 @@
-2.8.6.p1
+2.8.8
diff --git a/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch b/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch
deleted file mode 100644
index 322b25326ee..00000000000
--- a/build/pkgs/nauty/patches/nauty-2.8.6-gentreeg-gentourng.patch
+++ /dev/null
@@ -1,144 +0,0 @@
-From edb0474a4db8e69f971e4eebe18716309f5a7bb3 Mon Sep 17 00:00:00 2001
-From: Michael Orlitzky
-Date: Tue, 17 Jan 2023 19:44:49 -0500
-Subject: [PATCH 1/1] Upstream fixes for gentreeg and gentourng.
-
-https://mailman.anu.edu.au/pipermail/nauty/2023-January/000903.html
----
- gentourng.c | 2 +-
- gentreeg.c | 95 ++++++++++++++++++++++++++++-------------------------
- 2 files changed, 51 insertions(+), 46 deletions(-)
-
-diff --git a/gentourng.c b/gentourng.c
-index 634e5e8..5c7ffff 100644
---- a/gentourng.c
-+++ b/gentourng.c
-@@ -1408,7 +1408,7 @@ PLUGIN_INIT
- (*outproc)(outfile,g,1);
- }
- }
-- else
-+ else if (!connec || maxn != 2)
- {
- makeleveldata();
-
-diff --git a/gentreeg.c b/gentreeg.c
-index 946d5f8..15bf87b 100644
---- a/gentreeg.c
-+++ b/gentreeg.c
-@@ -1,4 +1,4 @@
--/* gentree version 1.3; Brendan McKay Oct 2022 */
-+/* gentree version 1.4; Brendan McKay Dec 2022 */
- /* This program is a wrapper for the program FreeTrees.c written
- * by Gang Li & Frank Ruskey. See below for their original
- * comments. */
-@@ -32,49 +32,54 @@ Counts for n=1..45:
- 1: 1
- 2: 1
- 3: 1
-- 4: 1
-- 5: 2
-- 6: 3
-- 7: 6
-- 8: 11
-- 9: 23
--10: 47
--11: 106
--12: 235
--13: 551
--14: 1301
--15: 3159
--16: 7741
--17: 19320
--18: 48629
--19: 123867
--20: 317955
--21: 823065
--22: 2144505
--23: 5623756
--24: 14828074
--25: 39299897
--26: 104636890
--27: 279793450
--28: 751065460
--29: 2023443032
--30: 5469566585
--31: 14830871802
--32: 40330829030
--33: 109972410221
--34: 300628862480
--35: 823779631721
--36: 2262366343746
--37: 6226306037178
--38: 17169677490714
--39: 47436313524262
--40: 131290543779126
--41: 363990257783343
--42: 1010748076717151
--43: 2810986483493475
--44: 7828986221515605
--45: 21835027912963086
--********************************/
-+ 4: 2
-+ 5: 3
-+ 6: 6
-+ 7: 11
-+ 8: 23
-+ 9: 47
-+10: 106
-+11: 235
-+12: 551
-+13: 1301
-+14: 3159
-+15: 7741
-+16: 19320
-+17: 48629
-+18: 123867
-+19: 317955
-+20: 823065
-+21: 2144505
-+22: 5623756
-+23: 14828074
-+24: 39299897
-+25: 104636890
-+26: 279793450
-+27: 751065460
-+28: 2023443032
-+29: 5469566585
-+30: 14830871802
-+31: 40330829030
-+32: 109972410221
-+33: 300628862480
-+34: 823779631721
-+35: 2262366343746
-+36: 6226306037178
-+37: 17169677490714
-+38: 47436313524262
-+39: 131290543779126
-+40: 363990257783343
-+41: 1010748076717151
-+42: 2810986483493475
-+43: 7828986221515605
-+44: 21835027912963086
-+45: 60978390985918906
-+46: 170508699155987862
-+47: 477355090753926460
-+48: 1337946100045842285
-+49: 3754194185716399992
-+50: 10545233702911509534
-+*******************************/
-
- /* Comments on original program by original authors */
- /*==============================================================*/
-@@ -676,7 +681,7 @@ PLUGIN_INIT
- }
- else if (nv == 2)
- {
-- if (res == 0 && maxdeg >= 1 && mindiam <= 1 && maxdiam >= 2)
-+ if (res == 0 && maxdeg >= 1 && mindiam <= 1 && maxdiam >= 1)
- {
- par[1] = 0;
- par[2] = 1;
---
-2.38.2
-
diff --git a/build/pkgs/ntl/spkg-install.in b/build/pkgs/ntl/spkg-install.in
index a26c3b5026e..d82ddf370e5 100644
--- a/build/pkgs/ntl/spkg-install.in
+++ b/build/pkgs/ntl/spkg-install.in
@@ -112,11 +112,6 @@ ntl_build()
ntl_install()
{
- echo
- echo "Removing old NTL files."
- rm -rf "$SAGE_LOCAL"/lib/libntl*
- rm -rf "$SAGE_LOCAL"/include/NTL
-
echo
echo "Installing NTL."
diff --git a/build/pkgs/numpy/spkg-legacy-uninstall b/build/pkgs/numpy/spkg-legacy-uninstall
deleted file mode 100755
index 9cb6a63a5a3..00000000000
--- a/build/pkgs/numpy/spkg-legacy-uninstall
+++ /dev/null
@@ -1,2 +0,0 @@
-#! /usr/bin/env bash
-rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/numpy
diff --git a/build/pkgs/openblas/spkg-install.in b/build/pkgs/openblas/spkg-install.in
index 2f8aea512bc..dbfcb6c1a75 100644
--- a/build/pkgs/openblas/spkg-install.in
+++ b/build/pkgs/openblas/spkg-install.in
@@ -59,9 +59,6 @@ if ! (sdh_make libs $OPENBLAS_CONFIGURE && sdh_make netlib $OPENBLAS_CONFIGURE &
fi
fi
-# See https://github.com/sagemath/sage/issues/30335
-rm -f "$SAGE_LOCAL/lib/pkgconfig/cblas.pc" "$SAGE_LOCAL/lib/pkgconfig/blas.pc" "$SAGE_LOCAL/lib/pkgconfig/lapack.pc"
-
sdh_make_install PREFIX="$SAGE_LOCAL" NO_STATIC=1 $OPENBLAS_CONFIGURE
cd ..
./write_pc_file.py
diff --git a/build/pkgs/openblas/spkg-preinst.in b/build/pkgs/openblas/spkg-preinst.in
new file mode 100644
index 00000000000..ced306d2d4e
--- /dev/null
+++ b/build/pkgs/openblas/spkg-preinst.in
@@ -0,0 +1,2 @@
+# See https://github.com/sagemath/sage/issues/30335
+rm -f "$SAGE_LOCAL/lib/pkgconfig/cblas.pc" "$SAGE_LOCAL/lib/pkgconfig/blas.pc" "$SAGE_LOCAL/lib/pkgconfig/lapack.pc"
diff --git a/build/pkgs/planarity/checksums.ini b/build/pkgs/planarity/checksums.ini
index 6227637695a..309daa93278 100644
--- a/build/pkgs/planarity/checksums.ini
+++ b/build/pkgs/planarity/checksums.ini
@@ -2,4 +2,4 @@ tarball=planarity-VERSION.tar.gz
sha1=8407bccf33c07bf0dae22d79b5e6ac7d89c62ea3
md5=200116e6a67544c8e94f9de7c3ba1b1a
cksum=4207261512
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/planarity-3.0.1.0.tar.gz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/planarity-VERSION.tar.gz
diff --git a/build/pkgs/plantri/checksums.ini b/build/pkgs/plantri/checksums.ini
index 05dc13d01e7..f999a981a69 100644
--- a/build/pkgs/plantri/checksums.ini
+++ b/build/pkgs/plantri/checksums.ini
@@ -1,5 +1,5 @@
-tarball=plantri53.tar.gz
+tarball=plantri${VERSION_MAJOR}${VERSION_MINOR}.tar.gz
sha1=a04aec2fa90c43f1c9bef59d041a54d8fa5bf562
md5=ea765b3508dd56384f94ad1f032e2dd4
cksum=3200215885
-upstream_url=https://users.cecs.anu.edu.au/~bdm/plantri/plantri53.tar.gz
+upstream_url=https://users.cecs.anu.edu.au/~bdm/plantri/plantri${VERSION_MAJOR}${VERSION_MINOR}.tar.gz
diff --git a/build/pkgs/polytopes_db_4d/checksums.ini b/build/pkgs/polytopes_db_4d/checksums.ini
index edfee6b7841..d87e456c4a8 100644
--- a/build/pkgs/polytopes_db_4d/checksums.ini
+++ b/build/pkgs/polytopes_db_4d/checksums.ini
@@ -2,4 +2,4 @@ tarball=polytopes_db_4d-VERSION.spkg
sha1=c9779821e365df2d7f9bc684f9e2ec0e95fb8650
md5=fe775a26fd7b2afc187e9bfabfb1b86a
cksum=3415837678
-upstream_url=http://ftp.sparcs.org/sage/spkg/huge/polytopes_db_4d-1.0.spkg
+upstream_url=http://ftp.sparcs.org/sage/spkg/huge/polytopes_db_4d-VERSION.spkg
diff --git a/build/pkgs/pycygwin/SPKG.rst b/build/pkgs/pycygwin/SPKG.rst
deleted file mode 100644
index fdf67a08dc1..00000000000
--- a/build/pkgs/pycygwin/SPKG.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-pycygwin: Python bindings for Cygwin's C API
-============================================
-
-Description
------------
-
-Python bindings for Cygwin's C API. Provides some utilities to help with
-the Cygwin port. Naturally, this package should only be installed on
-Cygwin--for other platforms its installation is a no-op.
-
-Website
--------
-
-https://github.com/embray/PyCygwin
diff --git a/build/pkgs/pycygwin/checksums.ini b/build/pkgs/pycygwin/checksums.ini
deleted file mode 100644
index 7ce896982a4..00000000000
--- a/build/pkgs/pycygwin/checksums.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-tarball=PyCygwin-VERSION.tar.gz
-sha1=4021dbf77c353051761d277f5490b38a701ba51b
-md5=12c30c847b144282178bc546d4e2cbcd
-cksum=2254892419
diff --git a/build/pkgs/pycygwin/dependencies b/build/pkgs/pycygwin/dependencies
deleted file mode 100644
index 1db13c07e43..00000000000
--- a/build/pkgs/pycygwin/dependencies
+++ /dev/null
@@ -1,4 +0,0 @@
- cython | $(PYTHON_TOOLCHAIN) $(PYTHON)
-
-----------
-All lines of this file are ignored except the first.
diff --git a/build/pkgs/pycygwin/distros/repology.txt b/build/pkgs/pycygwin/distros/repology.txt
deleted file mode 100644
index eeeab661dc6..00000000000
--- a/build/pkgs/pycygwin/distros/repology.txt
+++ /dev/null
@@ -1 +0,0 @@
-python:pycygwin
diff --git a/build/pkgs/pycygwin/install-requires.txt b/build/pkgs/pycygwin/install-requires.txt
deleted file mode 100644
index 3bec679b60b..00000000000
--- a/build/pkgs/pycygwin/install-requires.txt
+++ /dev/null
@@ -1 +0,0 @@
-pycygwin >=0.1
diff --git a/build/pkgs/pycygwin/package-version.txt b/build/pkgs/pycygwin/package-version.txt
deleted file mode 100644
index 49d59571fbf..00000000000
--- a/build/pkgs/pycygwin/package-version.txt
+++ /dev/null
@@ -1 +0,0 @@
-0.1
diff --git a/build/pkgs/pycygwin/spkg-install.in b/build/pkgs/pycygwin/spkg-install.in
deleted file mode 100644
index eeb8dcdf62c..00000000000
--- a/build/pkgs/pycygwin/spkg-install.in
+++ /dev/null
@@ -1,3 +0,0 @@
-if [ "$UNAME" = "CYGWIN" ]; then
- cd src && sdh_pip_install .
-fi
diff --git a/build/pkgs/pycygwin/type b/build/pkgs/pycygwin/type
deleted file mode 100644
index a6a7b9cd726..00000000000
--- a/build/pkgs/pycygwin/type
+++ /dev/null
@@ -1 +0,0 @@
-standard
diff --git a/build/pkgs/python3/spkg-build.in b/build/pkgs/python3/spkg-build.in
index 0c08814a242..4cd421bf4b8 100644
--- a/build/pkgs/python3/spkg-build.in
+++ b/build/pkgs/python3/spkg-build.in
@@ -62,17 +62,6 @@ fi
# common in Cython-generated code.
export EXTRA_CFLAGS="`testcflags.sh -Wno-unused $OLD_CFLAGS`"
-# Remove old symbolic link: it is not needed and its presence can
-# interfere with the Python build.
-rm -f "$SAGE_LOCAL/lib/python"
-
-# Remove old libraries. We really need to do this before building Python
-# since Python tries to import some modules (e.g. ctypes) at build-time.
-# We need to make sure that the old installed libraries in local/lib are
-# not used for that. See https://github.com/sagemath/sage/issues/24605
-rm -f "$SAGE_LOCAL"/lib/lib"$PKG_BASE"*
-
-
# Note: --without-ensurepip ensures that setuptools+pip are *not* installed
# automatically when installing python3. They will be installed instead by
# the separate setuptools and pip packages; see
diff --git a/build/pkgs/qhull/spkg-install.in b/build/pkgs/qhull/spkg-install.in
index 73810f54625..c3df534ad86 100644
--- a/build/pkgs/qhull/spkg-install.in
+++ b/build/pkgs/qhull/spkg-install.in
@@ -5,9 +5,4 @@ sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \
sdh_make
-# clean old install
-rm -rf "${SAGE_LOCAL}"/include/libqhull
-rm -rf "${SAGE_LOCAL}"/include/qhull
-rm -rf "${SAGE_LOCAL}"/lib/libqhull*
-
sdh_make_install
diff --git a/build/pkgs/readline/spkg-install.in b/build/pkgs/readline/spkg-install.in
index 69b4e686d75..a9322e4bed8 100644
--- a/build/pkgs/readline/spkg-install.in
+++ b/build/pkgs/readline/spkg-install.in
@@ -4,16 +4,6 @@ sdh_configure --with-curses --enable-shared --disable-static
echo "Now building static and shared readline libraries..."
sdh_make
-echo "Build succeedeed. Deleting old readline headers and libs"
-echo "before installing the new ones..."
-# (Note; Actually also readline does this by itself, but doing it
-# here, too, doesn't hurt either.)
-rm -rf "$SAGE_LOCAL"/include/readline/ "$SAGE_LOCAL"/lib/libreadline.*
-if [[ $? -ne 0 ]]; then
- echo >&2 "Error removing old version of readline."
- exit 1
-fi
-
echo "Now installing the new readline headers and libraries..."
sdh_make_install
diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt
index b136f4a2aff..f58f8ff4bbb 100644
--- a/build/pkgs/sage_conf/install-requires.txt
+++ b/build/pkgs/sage_conf/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sage-conf ~= 10.3b2
+sage-conf ~= 10.3b3
diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt
index a9f3dad3be9..74baf1b835d 100644
--- a/build/pkgs/sage_docbuild/install-requires.txt
+++ b/build/pkgs/sage_docbuild/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sage-docbuild ~= 10.3b2
+sage-docbuild ~= 10.3b3
diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt
index 398467294a7..352120f20fb 100644
--- a/build/pkgs/sage_setup/install-requires.txt
+++ b/build/pkgs/sage_setup/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sage-setup ~= 10.3b2
+sage-setup ~= 10.3b3
diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt
index 86ccd89829a..f0a5a420fcb 100644
--- a/build/pkgs/sage_sws2rst/install-requires.txt
+++ b/build/pkgs/sage_sws2rst/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sage-sws2rst ~= 10.3b2
+sage-sws2rst ~= 10.3b3
diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies
index fa6e98df7db..b1ebfd0825e 100644
--- a/build/pkgs/sagelib/dependencies
+++ b/build/pkgs/sagelib/dependencies
@@ -1,4 +1,4 @@
-FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran
+FORCE $(SCRIPTS) boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml importlib_metadata importlib_resources jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy $(PYTHON) requests rw sage_conf singular symmetrica typing_extensions $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup $(PYTHON) pythran
----------
All lines of this file are ignored except the first.
diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt
index 4760acdf3a5..7bf049c6a0c 100644
--- a/build/pkgs/sagelib/install-requires.txt
+++ b/build/pkgs/sagelib/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-standard ~= 10.3b2
+sagemath-standard ~= 10.3b3
diff --git a/build/pkgs/sagelib/spkg-install.in b/build/pkgs/sagelib/spkg-install.in
index 63cfd64fa59..39ffc25f270 100644
--- a/build/pkgs/sagelib/spkg-install.in
+++ b/build/pkgs/sagelib/spkg-install.in
@@ -51,6 +51,8 @@ if [ "$SAGE_EDITABLE" = yes ]; then
# and renamed the distribution to "sagemath-standard"). There is no clean way to uninstall
# them, so we just use rm.
(cd "$SITEPACKAGESDIR" && rm -rf sage sage-[1-9]*.egg-info sage-[1-9]*.dist-info)
+ # Until https://github.com/sagemath/sage/issues/34209 switches us to PEP 660 editable wheels
+ export SETUPTOOLS_ENABLE_FEATURES=legacy-editable
time sdh_pip_editable_install .
if [ "$SAGE_WHEELS" = yes ]; then
diff --git a/build/pkgs/sagemath_bliss/install-requires.txt b/build/pkgs/sagemath_bliss/install-requires.txt
index 78450942be8..0c4fe90f245 100644
--- a/build/pkgs/sagemath_bliss/install-requires.txt
+++ b/build/pkgs/sagemath_bliss/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-bliss ~= 10.3b2
+sagemath-bliss ~= 10.3b3
diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt
index dd5175f57fe..c4de485cea4 100644
--- a/build/pkgs/sagemath_categories/install-requires.txt
+++ b/build/pkgs/sagemath_categories/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-categories ~= 10.3b2
+sagemath-categories ~= 10.3b3
diff --git a/build/pkgs/sagemath_coxeter3/install-requires.txt b/build/pkgs/sagemath_coxeter3/install-requires.txt
index 05f8575b6a0..5f8c399c22f 100644
--- a/build/pkgs/sagemath_coxeter3/install-requires.txt
+++ b/build/pkgs/sagemath_coxeter3/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-coxeter3 ~= 10.3b2
+sagemath-coxeter3 ~= 10.3b3
diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt
index 1eb9fe7e808..336f2357697 100644
--- a/build/pkgs/sagemath_environment/install-requires.txt
+++ b/build/pkgs/sagemath_environment/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-environment ~= 10.3b2
+sagemath-environment ~= 10.3b3
diff --git a/build/pkgs/sagemath_mcqd/install-requires.txt b/build/pkgs/sagemath_mcqd/install-requires.txt
index cc46fb68674..1c73893daab 100644
--- a/build/pkgs/sagemath_mcqd/install-requires.txt
+++ b/build/pkgs/sagemath_mcqd/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-mcqd ~= 10.3b2
+sagemath-mcqd ~= 10.3b3
diff --git a/build/pkgs/sagemath_meataxe/install-requires.txt b/build/pkgs/sagemath_meataxe/install-requires.txt
index 76fc2369cb1..0e070b2b415 100644
--- a/build/pkgs/sagemath_meataxe/install-requires.txt
+++ b/build/pkgs/sagemath_meataxe/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-meataxe ~= 10.3b2
+sagemath-meataxe ~= 10.3b3
diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt
index b1d01c37823..f341b061e8b 100644
--- a/build/pkgs/sagemath_objects/install-requires.txt
+++ b/build/pkgs/sagemath_objects/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-objects ~= 10.3b2
+sagemath-objects ~= 10.3b3
diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt
index ce22d36e261..4a0e58e79ce 100644
--- a/build/pkgs/sagemath_repl/install-requires.txt
+++ b/build/pkgs/sagemath_repl/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-repl ~= 10.3b2
+sagemath-repl ~= 10.3b3
diff --git a/build/pkgs/sagemath_sirocco/install-requires.txt b/build/pkgs/sagemath_sirocco/install-requires.txt
index 40949dd944f..4737ab2e562 100644
--- a/build/pkgs/sagemath_sirocco/install-requires.txt
+++ b/build/pkgs/sagemath_sirocco/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-sirocco ~= 10.3b2
+sagemath-sirocco ~= 10.3b3
diff --git a/build/pkgs/sagemath_tdlib/install-requires.txt b/build/pkgs/sagemath_tdlib/install-requires.txt
index cab1a072642..bbf9579b01b 100644
--- a/build/pkgs/sagemath_tdlib/install-requires.txt
+++ b/build/pkgs/sagemath_tdlib/install-requires.txt
@@ -1,2 +1,2 @@
# This file is updated on every release by the sage-update-version script
-sagemath-tdlib ~= 10.3b2
+sagemath-tdlib ~= 10.3b3
diff --git a/build/pkgs/sagetex/spkg-install.in b/build/pkgs/sagetex/spkg-install.in
index 55f0e9c76e5..058b1344dc2 100644
--- a/build/pkgs/sagetex/spkg-install.in
+++ b/build/pkgs/sagetex/spkg-install.in
@@ -1,6 +1,3 @@
cd src
sdh_pip_install .
-
-echo "Removing old SageTex version(s)"
-rm -rf $SAGE_LOCAL/share/texmf/tex/generic/sagetex
diff --git a/build/pkgs/singular/spkg-install.in b/build/pkgs/singular/spkg-install.in
index 1c24a5d7ef8..c64820f4af1 100644
--- a/build/pkgs/singular/spkg-install.in
+++ b/build/pkgs/singular/spkg-install.in
@@ -11,44 +11,6 @@ if [ "x$SAGE_DEBUG" = "xyes" ]; then
SINGULAR_CONFIGURE="$SINGULAR_CONFIGURE --enable-debug --disable-optimizationflags"
fi
-remove_old_version()
-{
- # the following is a little verbose but it ensures we leave no trace of 3.x
- # _or_ 4.x
- rm -f "$SAGE_LOCAL"/bin/*Singular*
- rm -f "$SAGE_LOCAL"/bin/*singular*
- rm -rf "$SAGE_LOCAL/include/singular" # 3.x and 4.x
- rm -rf "$SAGE_LOCAL/include/factory" # 3.x and 4.x
- rm -f "$SAGE_LOCAL/include/factor.h" # 3.x only
- rm -f "$SAGE_LOCAL/include/factoryconf.h" # 3.x only
- rm -rf "$SAGE_LOCAL/include/omalloc" #4.x only
- rm -f "$SAGE_LOCAL/include/omalloc.h" # 3.x only
- rm -f "$SAGE_LOCAL/include/omlimits.h" # 3.x only
- rm -rf "$SAGE_LOCAL/include/resources" #4.x only
- rm -rf "$SAGE_LOCAL/include/gfanlib" #4.x only
-
- # Clean up all Singular-related libraries
- libs=(
- singular # 3.x with lower case
- singcf # 3.x only additional archives
- singfac # 3.x only additional archives
- Singular # 4.x with upper case
- polys
- factory
- omalloc
- resources # 3.x
- singular_resources # 4.x and up
- gfan
- )
- for name in ${libs[*]}; do
- rm -f "$SAGE_LOCAL"/lib/lib${name}*
- done
-
- rm -f "$SAGE_LOCAL"/lib/p_Procs_Field* # 3.x only
- rm -rf "$SAGE_LOCAL/share/singular"
- rm -f "$SAGE_LOCAL"/share/info/singular*
-}
-
config()
{
# configure notes (dates from Singular 3.x, maybe outdated for 4.x):
@@ -92,7 +54,7 @@ build_singular()
# Actually run all the functions defined above
-for i in remove_old_version config build_singular; do
+for i in config build_singular; do
echo "############### Singular stage $i ###############"
cd "$SRC" && $i
done
diff --git a/build/pkgs/sqlite/checksums.ini b/build/pkgs/sqlite/checksums.ini
index 5cb33394ab3..777aa3953a3 100644
--- a/build/pkgs/sqlite/checksums.ini
+++ b/build/pkgs/sqlite/checksums.ini
@@ -1,5 +1,5 @@
-tarball=sqlite-autoconf-3360000.tar.gz
+tarball=sqlite-autoconf-${VERSION_MAJOR}${VERSION_MINOR}0${VERSION_MICRO}00.tar.gz
sha1=a4bcf9e951bfb9745214241ba08476299fc2dc1e
md5=f5752052fc5b8e1b539af86a3671eac7
cksum=763219165
-upstream_url=https://www.sqlite.org/2021/sqlite-autoconf-3360000.tar.gz
+upstream_url=https://www.sqlite.org/2021/sqlite-autoconf-${VERSION_MAJOR}${VERSION_MINOR}0${VERSION_MICRO}00.tar.gz
diff --git a/build/pkgs/sqlite/spkg-install.in b/build/pkgs/sqlite/spkg-install.in
index d13df2ba1b8..0ddfea7a619 100644
--- a/build/pkgs/sqlite/spkg-install.in
+++ b/build/pkgs/sqlite/spkg-install.in
@@ -1,5 +1,3 @@
-rm -f "$SAGE_LOCAL/bin/sqlite3"
-
cd src
export CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include"
diff --git a/build/pkgs/symengine/checksums.ini b/build/pkgs/symengine/checksums.ini
index 8eb65f05ef9..9ba59940a9f 100644
--- a/build/pkgs/symengine/checksums.ini
+++ b/build/pkgs/symengine/checksums.ini
@@ -1,5 +1,6 @@
tarball=symengine-VERSION.tar.gz
-sha1=a2c8957f2099c9199751b165f107bf93d6823818
-md5=fe3c3fee1bd8dfdb9576fc2d28cb1076
-cksum=3544211225
+sha1=2dfee07108509963f3dbe3d9cad9de76d85e551f
+md5=4074f3c76570bdc2ae9914edafa29eb6
+cksum=3782541135
upstream_url=https://github.com/symengine/symengine/releases/download/vVERSION/symengine-VERSION.tar.gz
+
diff --git a/build/pkgs/symengine/package-version.txt b/build/pkgs/symengine/package-version.txt
index af88ba82486..a8839f70de0 100644
--- a/build/pkgs/symengine/package-version.txt
+++ b/build/pkgs/symengine/package-version.txt
@@ -1 +1 @@
-0.11.1
+0.11.2
\ No newline at end of file
diff --git a/build/pkgs/symengine/spkg-configure.m4 b/build/pkgs/symengine/spkg-configure.m4
index c037d2e5f72..a6a78791dbc 100644
--- a/build/pkgs/symengine/spkg-configure.m4
+++ b/build/pkgs/symengine/spkg-configure.m4
@@ -1,7 +1,7 @@
SAGE_SPKG_CONFIGURE([symengine], [
m4_pushdef(SAGE_SYMENGINE_VERSION_MAJOR, [0])
m4_pushdef(SAGE_SYMENGINE_VERSION_MINOR, [11])
- SAGE_SPKG_DEPCHECK([gmp arb ecm flint mpc mpfr], [
+ SAGE_SPKG_DEPCHECK([gmp ecm flint mpc mpfr], [
AC_CHECK_HEADER([symengine/symengine_config.h], [], [sage_spkg_install_symengine=yes])
AC_MSG_CHECKING([whether we can link a program using symengine])
SYMENGINE_SAVED_LIBS=$LIBS
diff --git a/build/pkgs/symmetrica/checksums.ini b/build/pkgs/symmetrica/checksums.ini
index 5f70ed5363c..6d06ebda462 100644
--- a/build/pkgs/symmetrica/checksums.ini
+++ b/build/pkgs/symmetrica/checksums.ini
@@ -2,4 +2,4 @@ tarball=symmetrica-VERSION.tar.xz
sha1=0044cc087ff04267c246e730c6570d89f6e593af
md5=cd4716c26b5c625a012c22656113ef6f
cksum=1186250347
-upstream_url=http://users.ox.ac.uk/~coml0531/sage/symmetrica-3.0.1.tar.xz
+upstream_url=http://users.ox.ac.uk/~coml0531/sage/symmetrica-VERSION.tar.xz
diff --git a/build/pkgs/sympow/checksums.ini b/build/pkgs/sympow/checksums.ini
index f6ad79b8f93..87bf0086178 100644
--- a/build/pkgs/sympow/checksums.ini
+++ b/build/pkgs/sympow/checksums.ini
@@ -2,4 +2,4 @@ tarball=sympow-vVERSION.tar.gz
sha1=37a909c26009415197b5088a2f1b53dd3558f494
md5=51f2c717c84ec9c2840af740751cf797
cksum=1444149964
-upstream_url=https://github.com/mkoeppe/sympow/releases/download/v2.023.6/sympow-v2.023.6.tar.gz
+upstream_url=https://github.com/mkoeppe/sympow/releases/download/vVERSION/sympow-vVERSION.tar.gz
diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py
index 1b84cbf6461..e182321d7da 100644
--- a/build/sage_bootstrap/cmdline.py
+++ b/build/sage_bootstrap/cmdline.py
@@ -292,6 +292,9 @@ def make_parser():
parser_download.add_argument(
'--on-error', choices=['stop', 'warn'], default='stop',
help='What to do if the tarball cannot be downloaded')
+ parser.add_argument(
+ '--no-check-certificate', action='store_true',
+ help='Do not check SSL certificates for https connections')
parser_upload = subparsers.add_parser(
'upload', epilog=epilog_upload,
@@ -381,6 +384,12 @@ def run():
elif args.subcommand == 'update-latest':
app.update_latest_cls(args.package_name, commit=args.commit)
elif args.subcommand == 'download':
+ if args.no_check_certificate:
+ try:
+ import ssl
+ ssl._create_default_https_context = ssl._create_unverified_context
+ except ImportError:
+ pass
app.download_cls(args.package_name,
allow_upstream=args.allow_upstream,
on_error=args.on_error)
diff --git a/build/sage_bootstrap/package.py b/build/sage_bootstrap/package.py
index f6a5425005a..43bfbffa47c 100644
--- a/build/sage_bootstrap/package.py
+++ b/build/sage_bootstrap/package.py
@@ -121,6 +121,42 @@ def tarball(self):
self.__tarball = Tarball(self.tarball_filename, package=self)
return self.__tarball
+ def _substitute_variables_once(self, pattern):
+ """
+ Substitute (at most) one occurrence of variables in ``pattern`` by the values.
+
+ These variables are ``VERSION``, ``VERSION_MAJOR``, ``VERSION_MINOR``,
+ ``VERSION_MICRO``, either appearing like this or in the form ``${VERSION_MAJOR}``
+ etc.
+
+ Return a tuple:
+ - the string with the substitution done or the original string
+ - whether a substitution was done
+ """
+ for var in ('VERSION_MAJOR', 'VERSION_MINOR', 'VERSION_MICRO', 'VERSION'):
+ # As VERSION is a substring of the other three, it needs to be tested last.
+ dollar_brace_var = '${' + var + '}'
+ if dollar_brace_var in pattern:
+ value = getattr(self, var.lower())
+ return pattern.replace(dollar_brace_var, value, 1), True
+ elif var in pattern:
+ value = getattr(self, var.lower())
+ return pattern.replace(var, value, 1), True
+ return pattern, False
+
+ def _substitute_variables(self, pattern):
+ """
+ Substitute all occurrences of ``VERSION`` in ``pattern`` by the actual version.
+
+ Likewise for ``VERSION_MAJOR``, ``VERSION_MINOR``, ``VERSION_MICRO``,
+ either appearing like this or in the form ``${VERSION}``, ``${VERSION_MAJOR}``,
+ etc.
+ """
+ not_done = True
+ while not_done:
+ pattern, not_done = self._substitute_variables_once(pattern)
+ return pattern
+
@property
def tarball_pattern(self):
"""
@@ -150,7 +186,7 @@ def tarball_filename(self):
"""
pattern = self.tarball_pattern
if pattern:
- return pattern.replace('VERSION', self.version)
+ return self._substitute_variables(pattern)
else:
return None
@@ -177,7 +213,7 @@ def tarball_upstream_url(self):
"""
pattern = self.tarball_upstream_url_pattern
if pattern:
- return pattern.replace('VERSION', self.version)
+ return self._substitute_variables(pattern)
else:
return None
@@ -212,6 +248,39 @@ def version(self):
"""
return self.__version
+ @property
+ def version_major(self):
+ """
+ Return the major version
+
+ OUTPUT:
+
+ String. The package's major version.
+ """
+ return self.version.split('.')[0]
+
+ @property
+ def version_minor(self):
+ """
+ Return the minor version
+
+ OUTPUT:
+
+ String. The package's minor version.
+ """
+ return self.version.split('.')[1]
+
+ @property
+ def version_micro(self):
+ """
+ Return the micro version
+
+ OUTPUT:
+
+ String. The package's micro version.
+ """
+ return self.version.split('.')[2]
+
@property
def patchlevel(self):
"""
diff --git a/build/sage_bootstrap/updater.py b/build/sage_bootstrap/updater.py
index 7c1431967a1..67f1c961124 100644
--- a/build/sage_bootstrap/updater.py
+++ b/build/sage_bootstrap/updater.py
@@ -69,8 +69,11 @@ def _update_version(self, new_version):
def download_upstream(self, download_url=None):
tarball = self.package.tarball
if download_url is None:
+ pattern = self.package.tarball_upstream_url_pattern
+ if pattern and 'VERSION' not in pattern:
+ print('Warning: upstream_url pattern does not use the VERSION variable')
download_url = self.package.tarball_upstream_url
if download_url is None:
raise ValueError("package has no default upstream_url pattern, download_url needed")
- print('Downloading tarball to {0}'.format(tarball.upstream_fqn))
+ print('Downloading tarball from {0} to {1}'.format(download_url, tarball.upstream_fqn))
Download(download_url, tarball.upstream_fqn).run()
diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-conf/VERSION.txt
+++ b/pkgs/sage-conf/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sage-conf/_sage_conf/_conf.py.in b/pkgs/sage-conf/_sage_conf/_conf.py.in
index 895d6572d1e..ccc1c9695fb 100644
--- a/pkgs/sage-conf/_sage_conf/_conf.py.in
+++ b/pkgs/sage-conf/_sage_conf/_conf.py.in
@@ -9,6 +9,11 @@ VERSION = "@PACKAGE_VERSION@"
SAGE_LOCAL = "@prefix@"
SAGE_ROOT = "@SAGE_ROOT@"
+# The semicolon-separated list of GAP root paths. This is the list of
+# locations that are searched for GAP packages. This is passed directly
+# to GAP via the -l flag.
+GAP_ROOT_PATHS = "@GAP_ROOT_PATHS@".replace('${prefix}', SAGE_LOCAL)
+
# The path to the standalone maxima executable.
MAXIMA = "@SAGE_MAXIMA@".replace('${prefix}', SAGE_LOCAL)
diff --git a/pkgs/sage-conf_conda/VERSION.txt b/pkgs/sage-conf_conda/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-conf_conda/VERSION.txt
+++ b/pkgs/sage-conf_conda/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-conf_pypi/VERSION.txt
+++ b/pkgs/sage-conf_pypi/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-docbuild/VERSION.txt
+++ b/pkgs/sage-docbuild/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-setup/VERSION.txt
+++ b/pkgs/sage-setup/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sage-sws2rst/VERSION.txt
+++ b/pkgs/sage-sws2rst/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-bliss/VERSION.txt
+++ b/pkgs/sagemath-bliss/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-categories/VERSION.txt
+++ b/pkgs/sagemath-categories/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-coxeter3/VERSION.txt
+++ b/pkgs/sagemath-coxeter3/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-environment/VERSION.txt
+++ b/pkgs/sagemath-environment/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-mcqd/VERSION.txt
+++ b/pkgs/sagemath-mcqd/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-meataxe/VERSION.txt
+++ b/pkgs/sagemath-meataxe/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-objects/VERSION.txt
+++ b/pkgs/sagemath-objects/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-repl/VERSION.txt
+++ b/pkgs/sagemath-repl/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-sirocco/VERSION.txt
+++ b/pkgs/sagemath-sirocco/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/pkgs/sagemath-tdlib/VERSION.txt
+++ b/pkgs/sagemath-tdlib/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/src/VERSION.txt b/src/VERSION.txt
index 4d40853d20c..0fca3e3265b 100644
--- a/src/VERSION.txt
+++ b/src/VERSION.txt
@@ -1 +1 @@
-10.3.beta2
+10.3.beta3
diff --git a/src/bin/sage b/src/bin/sage
index ba353116e7c..a198ed8f273 100755
--- a/src/bin/sage
+++ b/src/bin/sage
@@ -628,7 +628,9 @@ fi
if [ "$1" = '-gap' -o "$1" = '--gap' ]; then
shift
- exec gap "$@"
+ # Use "-A" to avoid warnings about missing packages. The gap
+ # interface and libgap within sage both already do this.
+ exec gap -A "$@"
fi
if [ "$1" = '-gap3' -o "$1" = '--gap3' ]; then
@@ -1001,6 +1003,7 @@ if [ "$1" = '-tox' -o "$1" = '--tox' ]; then
exec tox -c "$SAGE_SRC" "$@"
else
echo "Run 'sage -i tox' to install"
+ exit 1
fi
else
echo >&2 "error: Sage source directory or tox.ini not available"
@@ -1022,6 +1025,7 @@ if [ "$1" = '-pytest' -o "$1" = '--pytest' ]; then
exec pytest --rootdir="$SAGE_SRC" --doctest-modules "$@" "$SAGE_SRC"
else
echo "Run 'sage -i pytest' to install"
+ exit 1
fi
else
echo >&2 "error: Sage source directory or tox.ini not available"
diff --git a/src/bin/sage-env b/src/bin/sage-env
index 29b96c3c895..8fcfda48fb6 100644
--- a/src/bin/sage-env
+++ b/src/bin/sage-env
@@ -228,7 +228,7 @@ fi
# depending on SAGE_ROOT and SAGE_LOCAL which are already defined.
if [ -n "$SAGE_LOCAL" ]; then
export SAGE_SHARE="$SAGE_LOCAL/share"
- export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed"
+ export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" # deprecated
fi
if [ -n "$SAGE_SHARE" ]; then
export SAGE_DOC="$SAGE_SHARE/doc/sage"
diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh
index 7d38e167623..55fd24dfdf5 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.3.beta2'
-SAGE_RELEASE_DATE='2023-12-13'
-SAGE_VERSION_BANNER='SageMath version 10.3.beta2, Release Date: 2023-12-13'
+SAGE_VERSION='10.3.beta3'
+SAGE_RELEASE_DATE='2023-12-18'
+SAGE_VERSION_BANNER='SageMath version 10.3.beta3, Release Date: 2023-12-18'
diff --git a/src/doc/en/constructions/groups.rst b/src/doc/en/constructions/groups.rst
index 042d37088b1..da90efecfaa 100644
--- a/src/doc/en/constructions/groups.rst
+++ b/src/doc/en/constructions/groups.rst
@@ -180,8 +180,8 @@ Here's another way, working more directly with GAP::
sage: print(gap.eval("normal := NormalSubgroups( G )"))
[ Alt( [ 1 .. 5 ] ), Group(()) ]
sage: G = gap.new("DihedralGroup( 10 )")
- sage: G.NormalSubgroups()
- [ Group( [ f1, f2 ] ), Group( [ f2 ] ), Group( of ... ) ]
+ sage: G.NormalSubgroups().SortedList()
+ [ Group( of ... ), Group( [ f2 ] ), Group( [ f1, f2 ] ) ]
sage: print(gap.eval("G := SymmetricGroup( 4 )"))
Sym( [ 1 .. 4 ] )
sage: print(gap.eval("normal := NormalSubgroups( G );"))
diff --git a/src/doc/en/developer/packaging.rst b/src/doc/en/developer/packaging.rst
index 4ed1762efe6..81b0044b44c 100644
--- a/src/doc/en/developer/packaging.rst
+++ b/src/doc/en/developer/packaging.rst
@@ -1048,9 +1048,11 @@ Sage mirrors when a new release is prepared. On GitHub PRs
upgrading a package, the PR description should no longer contain
the upstream URL to avoid duplication of information.
-Note that, like the ``tarball`` field, the ``tpstream_url`` is a
+Note that, like the ``tarball`` field, the ``upstream_url`` is a
template; the substring ``VERSION`` is substituted with the actual
-version.
+version. It can also be written as ``${VERSION}``, and it is possible
+to refer to the dot-separated components of a version by ``VERSION_MAJOR``,
+``VERSION_MINOR``, and ``VERSION_MICRO``.
For Python packages available from PyPI, you should use an
``upstream_url`` from ``pypi.io``, which follows the format
diff --git a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
index c6f11452ce8..101cbcdb4e7 100644
--- a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
+++ b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst
@@ -83,15 +83,13 @@ rather than just a list of numbers. This can be very powerful.
::
- sage: for K in D.normal_subgroups():
+ sage: len(D.normal_subgroups())
+ 7
+ sage: for K in sorted(D.normal_subgroups()):
....: print(K)
- Subgroup generated by [(1,2,3,4,5,6,7,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
- Subgroup generated by [(1,2,3,4,5,6,7,8), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
- Subgroup generated by [(1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
- Subgroup generated by [(2,8)(3,7)(4,6), (1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
- Subgroup generated by [(1,3,5,7)(2,4,6,8), (1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
- Subgroup generated by [(1,5)(2,6)(3,7)(4,8)] of (Dihedral group of order 16 as a permutation group)
Subgroup generated by [()] of (Dihedral group of order 16 as a permutation group)
+ ...
+ Subgroup generated by [(1,2,3,4,5,6,7,8), (1,8)(2,7)(3,6)(4,5)] of (Dihedral group of order 16 as a permutation group)
We can access specific subgroups if we know the generators as a
permutation group.
diff --git a/src/doc/en/reference/databases/index.rst b/src/doc/en/reference/databases/index.rst
index 56289fa5236..7cff08ee68a 100644
--- a/src/doc/en/reference/databases/index.rst
+++ b/src/doc/en/reference/databases/index.rst
@@ -1,68 +1,40 @@
Databases
=========
-There are numerous specific mathematical databases either included
-in Sage or available as optional packages. Also, Sage includes two
-powerful general database packages.
+Sage includes mathematical databases as standard or optional packages,
+and provides convenient interfaces for easy access to the databases.
-Sage includes the ZOPE object oriented database ZODB, which
-"is a Python object persistence system. It provides transparent object-oriented persistency."
+Standard databases
+------------------
-Sage also includes the powerful relational database SQLite, along
-with a Python interface to SQLite. SQlite is a small C library that
-implements a self-contained, embeddable, zero-configuration SQL
-database engine.
+These databases are available in the standard install of Sage or if Sage can access
+Internet.
+.. toctree::
+ :maxdepth: 1
-- Transactions are atomic, consistent, isolated, and durable
- (ACID) even after system crashes and power failures.
-
-- Zero-configuration - no setup or administration needed.
-
-- Implements most of SQL92. (Features not supported)
-
-- A complete database is stored in a single disk file.
-
-- Database files can be freely shared between machines with
- different byte orders.
-
-- Supports databases up to 2 tebibytes (2^41 bytes) in size.
-
-- Strings and BLOBs up to 2 gibibytes (2^31 bytes) in size.
-
-- Small code footprint: less than 250KiB fully configured or less
- than 150KiB with optional features omitted.
-
-- Faster than popular client/server database engines for most
- common operations.
-
-- Simple, easy to use API.
-
-- TCL bindings included. Bindings for many other languages
- available separately.
-
-- Well-commented source code with over 95% test coverage.
+ sage/databases/conway
+ sage/databases/oeis
+ sage/databases/sloane
+ sage/databases/findstat
-- Self-contained: no external dependencies.
+Optional databases
+------------------
-- Sources are in the public domain. Use for any purpose.
+The following databases require you to install optional packages before full access.
.. toctree::
:maxdepth: 1
+ sage/databases/cunningham_tables
+ sage/databases/knotinfo_db
+ sage/databases/cubic_hecke_db
+ sage/databases/symbolic_data
sage/databases/cremona
sage/databases/stein_watkins
sage/databases/jones
- sage/databases/oeis
- sage/databases/sloane
- sage/databases/findstat
- sage/databases/conway
- sage/databases/odlyzko
- sage/databases/symbolic_data
- sage/databases/cunningham_tables
sage/databases/db_class_polynomials
sage/databases/db_modular_polynomials
- sage/databases/knotinfo_db
- sage/databases/cubic_hecke_db
+ sage/databases/odlyzko
.. include:: ../footer.txt
diff --git a/src/requirements.txt.m4 b/src/requirements.txt.m4
index 346ea3c6301..34c42860cf1 100644
--- a/src/requirements.txt.m4
+++ b/src/requirements.txt.m4
@@ -32,7 +32,6 @@ dnl ... already needed by sage.env
pkgconfig==esyscmd(`printf $(sed "s/[.]p.*//;" ../pkgconfig/package-version.txt)')
pplpy==esyscmd(`printf $(sed "s/[.]p.*//;" ../pplpy/package-version.txt)')
primecountpy==esyscmd(`printf $(sed "s/[.]p.*//;" ../primecountpy/package-version.txt)')
-pycygwin==esyscmd(`printf $(sed "s/[.]p.*//;" ../pycygwin/package-version.txt)'); sys_platform == 'cygwin'
requests==esyscmd(`printf $(sed "s/[.]p.*//;" ../requests/package-version.txt)')
typing_extensions==esyscmd(`printf $(sed "s/[.]p.*//;" ../typing_extensions/package-version.txt)')
diff --git a/src/sage/coding/abstract_code.py b/src/sage/coding/abstract_code.py
index 85cdce652b9..ed84dc12517 100644
--- a/src/sage/coding/abstract_code.py
+++ b/src/sage/coding/abstract_code.py
@@ -512,7 +512,7 @@ def list(self):
(1, 0, 1, 0, 1, 0, 1)
True
"""
- return [x for x in self]
+ return list(self)
def length(self):
r"""
diff --git a/src/sage/coding/ag_code.py b/src/sage/coding/ag_code.py
index f705f75d476..0f189bb69a0 100644
--- a/src/sage/coding/ag_code.py
+++ b/src/sage/coding/ag_code.py
@@ -332,7 +332,7 @@ def designed_distance(self):
"""
Return the designed distance of the AG code.
- If the code is of dimension zero, then a ``ValueError`` is raised.
+ If the code is of dimension zero, then a :class:`ValueError` is raised.
EXAMPLES::
@@ -576,7 +576,7 @@ def designed_distance(self):
"""
Return the designed distance of the differential AG code.
- If the code is of dimension zero, then a ``ValueError`` is raised.
+ If the code is of dimension zero, then a :class:`ValueError` is raised.
EXAMPLES::
diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py
index c1e0eb77022..b192792f11f 100644
--- a/src/sage/coding/code_bounds.py
+++ b/src/sage/coding/code_bounds.py
@@ -192,10 +192,10 @@ def _check_n_q_d(n, q, d, field_based=True):
Check that the length `n`, alphabet size `q` and minimum distance `d` type
check and make sense for a code over a field.
- More precisely, checks that the parameters are positive integers, that `q`
- is a prime power for codes over a field, or, more generally, that
- `q` is of size at least 2, and that `n >= d`. Raises a ``ValueError``
- otherwise.
+ More precisely, this checks that the parameters are positive
+ integers, that `q` is a prime power for codes over a field, or,
+ more generally, that `q` is of size at least 2, and that `n >= d`.
+ This raises a :class:`ValueError` otherwise.
TESTS::
diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py
index d96ad3c7627..6870c28ec87 100644
--- a/src/sage/coding/code_constructions.py
+++ b/src/sage/coding/code_constructions.py
@@ -155,8 +155,8 @@ def _is_a_splitting(S1, S2, n, return_automorphism=False):
This is a special case of Theorem 6.4.3 in [HP2003]_.
"""
R = IntegerModRing(n)
- S1 = set(R(x) for x in S1)
- S2 = set(R(x) for x in S2)
+ S1 = {R(x) for x in S1}
+ S2 = {R(x) for x in S2}
# we first check whether (S1,S2) is a partition of R - {0}
if (len(S1) + len(S2) != n-1 or len(S1) != len(S2) or
diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py
index 3826734c09b..989d67e89d8 100644
--- a/src/sage/coding/databases.py
+++ b/src/sage/coding/databases.py
@@ -174,7 +174,7 @@ def best_linear_code_in_codetables_dot_de(n, k, F, verbose=False):
i = s.find("")
j = s.find("
")
if i == -1 or j == -1:
- raise IOError("Error parsing data (missing pre tags).")
+ raise OSError("Error parsing data (missing pre tags).")
return s[i+5:j].strip()
diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py
index 47c84b2c59b..e25c0a4faf4 100644
--- a/src/sage/coding/delsarte_bounds.py
+++ b/src/sage/coding/delsarte_bounds.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Delsarte (or linear programming) bounds
@@ -24,9 +23,8 @@
# 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/
+# https://www.gnu.org/licenses/
# ****************************************************************************
-from __future__ import print_function, division
def krawtchouk(n, q, l, x, check=True):
@@ -142,7 +140,6 @@ def eberlein(n, w, k, u, check=True):
default. Otherwise, pass it as it is. Use ``check=False`` at
your own risk.
-
EXAMPLES::
sage: codes.bounds.eberlein(24,10,2,6)
@@ -169,13 +166,12 @@ def eberlein(n, w, k, u, check=True):
Traceback (most recent call last):
...
TypeError: either m or x-m must be an integer
-
"""
from sage.arith.misc import binomial
from sage.arith.srange import srange
- if 2*w > n:
- return eberlein(n, n-w, k, u)
+ if 2 * w > n:
+ return eberlein(n, n - w, k, u)
if check:
from sage.rings.integer_ring import ZZ
@@ -185,10 +181,10 @@ def eberlein(n, w, k, u, check=True):
n = n0
return sum([(-1)**j*binomial(u, j)*binomial(w-u, k-j)*binomial(n-w-u, k-j)
- for j in srange(0, k+1)])
+ for j in srange(k + 1)])
-def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0):
+def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0):
r"""
LP builder - common for the two functions; not exported.
@@ -211,18 +207,18 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0):
x_0 is a continuous variable (min=0, max=+oo)
...
x_7 is a continuous variable (min=0, max=+oo)
-
"""
from sage.numerical.mip import MixedIntegerLinearProgram
p = MixedIntegerLinearProgram(maximization=True, solver=solver)
A = p.new_variable(integer=isinteger, nonnegative=True)
- p.set_objective(sum([A[r] for r in range(n+1)]))
+ p.set_objective(sum([A[r] for r in range(n + 1)]))
p.add_constraint(A[0] == 1)
for i in range(1, d):
p.add_constraint(A[i] == 0)
- for j in range(1, n+1):
- rhs = sum([krawtchouk(n, q, j, r, check=False)*A[r] for r in range(n+1)])
+ for j in range(1, n + 1):
+ rhs = sum([krawtchouk(n, q, j, r, check=False) * A[r]
+ for r in range(n + 1)])
p.add_constraint(0 <= rhs)
if j >= d_star:
p.add_constraint(0 <= rhs)
@@ -230,7 +226,7 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc=0):
p.add_constraint(0 == rhs)
if maxc > 0:
- p.add_constraint(sum([A[r] for r in range(n+1)]), max=maxc)
+ p.add_constraint(sum([A[r] for r in range(n + 1)]), max=maxc)
return A, p
@@ -272,10 +268,9 @@ def _delsarte_cwc_LP_building(n, d, w, solver, isinteger):
Variables:
x_0 is a continuous variable (min=0, max=+oo)
x_1 is a continuous variable (min=0, max=+oo)
-
"""
- from sage.numerical.mip import MixedIntegerLinearProgram
from sage.arith.misc import binomial
+ from sage.numerical.mip import MixedIntegerLinearProgram
p = MixedIntegerLinearProgram(maximization=True, solver=solver)
A = p.new_variable(integer=isinteger, nonnegative=True)
@@ -287,12 +282,14 @@ def _q(k, i):
return mu_i*eberlein(n, w, i, k)/v_i
for k in range(1, w+1):
- p.add_constraint(sum([A[2*i]*_q(k, i) for i in range(d//2, w+1)]), min=-1)
+ p.add_constraint(sum([A[2*i]*_q(k, i) for i in range(d//2, w+1)]),
+ min=-1)
return A, p
-def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL", isinteger=False):
+def delsarte_bound_constant_weight_code(n, d, w, return_data=False,
+ solver="PPL", isinteger=False):
r"""
Find the Delsarte bound on a constant weight code.
@@ -337,15 +334,16 @@ def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL"
sage: codes.bounds.delsarte_bound_constant_weight_code(17, 4, 3, isinteger=True)
43
-
"""
from sage.numerical.mip import MIPSolverException
if d < 4:
- raise ValueError("Violated constraint d>=4 for Binary Constant Weight Codes")
+ raise ValueError("Violated constraint d>=4 for "
+ "Binary Constant Weight Codes")
if d >= 2*w or 2*w > n:
- raise ValueError("Violated constraint d<2w<=n for Binary Constant Weight Codes")
+ raise ValueError("Violated constraint d<2w<=n for "
+ "Binary Constant Weight Codes")
# minimum distance is even => if there is an odd lower bound on d we can
# increase it by 1
@@ -356,23 +354,19 @@ def delsarte_bound_constant_weight_code(n, d, w, return_data=False, solver="PPL"
try:
bd = p.solve()
except MIPSolverException as exc:
- print("Solver exception: {}".format(exc))
- if return_data:
- return A, p, False
- return False
+ print(f"Solver exception: {exc}")
+ return (A, p, False) if return_data else False
- if return_data:
- return A, p, bd
- else:
- return int(bd)
+ return (A, p, bd) if return_data else int(bd)
-def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isinteger=False):
+def delsarte_bound_hamming_space(n, d, q, return_data=False,
+ solver="PPL", isinteger=False):
r"""
Find the Delsarte bound on codes in ``H_q^n`` of minimal distance ``d``
- Find the Delsarte bound [De1973]_ on the size of codes in the Hamming space ``H_q^n``
- of minimal distance ``d``.
+ Find the Delsarte bound [De1973]_ on the size of codes in
+ the Hamming space ``H_q^n`` of minimal distance ``d``.
INPUT:
@@ -444,94 +438,92 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isint
try:
bd = p.solve()
except MIPSolverException as exc:
- print("Solver exception: {}".format(exc))
- if return_data:
- return A, p, False
- return False
+ print(f"Solver exception: {exc}")
+ return (A, p, False) if return_data else False
- if return_data:
- return A, p, bd
- else:
- return bd
+ return (A, p, bd) if return_data else bd
-def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_data=False, solver="PPL", isinteger=False):
+def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_data=False,
+ solver="PPL", isinteger=False):
r"""
- Find a modified Delsarte bound on additive codes in Hamming space `H_q^n` of minimal distance `d`
+ Find a modified Delsarte bound on additive codes in Hamming space `H_q^n` of minimal distance `d`.
- Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive codes in
- Hamming space `H_q^n` of minimal distance ``d`` with minimal distance of the dual
- code at least ``d_star``. If ``q_base`` is set to
- non-zero, then ``q`` is a power of ``q_base``, and the code is, formally, linear over
- ``F_{q_base}``. Otherwise it is assumed that ``q_base==q``.
+ Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive
+ codes in Hamming space `H_q^n` of minimal distance ``d`` with
+ minimal distance of the dual code at least ``d_star``. If
+ ``q_base`` is set to non-zero, then ``q`` is a power of
+ ``q_base``, and the code is, formally, linear over
+ ``F_{q_base}``. Otherwise it is assumed that ``q_base==q``.
+ INPUT:
- INPUT:
-
- - ``n`` -- the code length
+ - ``n`` -- the code length
- - ``d`` -- the (lower bound on) minimal distance of the code
+ - ``d`` -- the (lower bound on) minimal distance of the code
- - ``q`` -- the size of the alphabet
+ - ``q`` -- the size of the alphabet
- - ``d_star`` -- the (lower bound on) minimal distance of the dual code;
- only makes sense for additive codes.
+ - ``d_star`` -- the (lower bound on) minimal distance of the dual code;
+ only makes sense for additive codes.
- - ``q_base`` -- if ``0``, the code is assumed to be linear. Otherwise,
- ``q=q_base^m`` and the code is linear over ``F_{q_base}``.
+ - ``q_base`` -- if ``0``, the code is assumed to be linear. Otherwise,
+ ``q=q_base^m`` and the code is linear over ``F_{q_base}``.
- - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is
- a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP
- data. ``W`` need not be a weight distribution of a code, or,
- if ``isinteger==False``, even have integer entries.
+ - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``,
+ where ``W`` is a weights vector, and ``LP`` the Delsarte bound
+ LP; both of them are Sage LP data. ``W`` need not be a weight
+ distribution of a code, or, if ``isinteger==False``, even have
+ integer entries.
- - ``solver`` -- the LP/ILP solver to be used. Defaults to ``'PPL'``. It is arbitrary
- precision, thus there will be no rounding errors. With other solvers
- (see :class:`MixedIntegerLinearProgram` for the list), you are on your own!
+ - ``solver`` -- the LP/ILP solver to be used. Defaults to ``'PPL'``. It is
+ arbitrary precision, thus there will be no rounding errors. With
+ other solvers (see :class:`MixedIntegerLinearProgram` for the
+ list), you are on your own!
- - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather
- that an LP solver. Can be very slow if set to ``True``.
+ - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP),
+ rather that an LP solver. Can be very slow if set to ``True``.
- EXAMPLES:
+ EXAMPLES:
- The bound on dimension of linear `\GF{2}`-codes of length 11 and minimal distance 6::
+ The bound on dimension of linear `\GF{2}`-codes of length 11 and minimal distance 6::
- sage: codes.bounds.delsarte_bound_additive_hamming_space(11, 6, 2)
- 3
- sage: a,p,val = codes.bounds.delsarte_bound_additive_hamming_space(\
- 11, 6, 2, return_data=True)
- sage: [j for i,j in p.get_values(a).items()]
- [1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0]
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(11, 6, 2)
+ 3
+ sage: a,p,val = codes.bounds.delsarte_bound_additive_hamming_space(\
+ 11, 6, 2, return_data=True)
+ sage: [j for i,j in p.get_values(a).items()]
+ [1, 0, 0, 0, 0, 0, 5, 2, 0, 0, 0, 0]
- The bound on the dimension of linear `\GF{4}`-codes of length 11 and minimal distance 3::
+ The bound on the dimension of linear `\GF{4}`-codes of length 11 and minimal distance 3::
- sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4)
- 8
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4)
+ 8
- The bound on the `\GF{2}`-dimension of additive `\GF{4}`-codes of length 11 and minimal
- distance 3::
+ The bound on the `\GF{2}`-dimension of additive `\GF{4}`-codes of length 11 and minimal
+ distance 3::
- sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,q_base=2)
- 16
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,q_base=2)
+ 16
- Such a ``d_star`` is not possible::
+ Such a ``d_star`` is not possible::
- sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,d_star=9)
- Solver exception: PPL : There is no feasible solution
- False
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(11,3,4,d_star=9)
+ Solver exception: PPL : There is no feasible solution
+ False
- TESTS::
+ TESTS::
- sage: a,p,x = codes.bounds.delsarte_bound_additive_hamming_space(\
- 19,15,7,return_data=True,isinteger=True)
- sage: [j for i,j in p.get_values(a).items()]
- [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 1, 34]
- sage: codes.bounds.delsarte_bound_additive_hamming_space(19,15,7,solver='glpk')
- 3
- sage: codes.bounds.delsarte_bound_additive_hamming_space(\
- 19,15,7, isinteger=True, solver='glpk')
- 3
- """
+ sage: a,p,x = codes.bounds.delsarte_bound_additive_hamming_space(\
+ 19,15,7,return_data=True,isinteger=True)
+ sage: [j for i,j in p.get_values(a).items()]
+ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 1, 34]
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(19,15,7,solver='glpk')
+ 3
+ sage: codes.bounds.delsarte_bound_additive_hamming_space(\
+ 19,15,7, isinteger=True, solver='glpk')
+ 3
+ """
from sage.numerical.mip import MIPSolverException
if q_base == 0:
q_base = q
@@ -550,18 +542,18 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_da
m = kk*n # this is to emulate repeat/until block
bd = q**n+1
- while q_base**m < bd: # need to solve the LP repeatedly, as this is a new constraint!
+ while q_base**m < bd:
+ # need to solve the LP repeatedly, as this is a new constraint!
# we might become infeasible. More precisely, after rounding down
# to the closest value of q_base^m, the LP, with the constraint that
# the objective function is at most q_base^m,
- A, p = _delsarte_LP_building(n, d, d_star, q, isinteger, solver, q_base**m)
+ A, p = _delsarte_LP_building(n, d, d_star, q, isinteger,
+ solver, q_base**m)
try:
bd = p.solve()
except MIPSolverException as exc:
print("Solver exception:", exc)
- if return_data:
- return A, p, False
- return False
+ return (A, p, False) if return_data else False
# rounding the bound down to the nearest power of q_base, for q=q_base^m
# bd_r = roundres(log(bd, base=q_base))
m = -1
@@ -570,10 +562,7 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_da
if q_base**(m+1) == bd:
m += 1
- if return_data:
- return A, p, m
- else:
- return m
+ return (A, p, m) if return_data else m
def _delsarte_Q_LP_building(q, d, solver, isinteger):
@@ -644,12 +633,13 @@ def _delsarte_Q_LP_building(q, d, solver, isinteger):
p.add_constraint(A[i] == 0)
for k in range(1, n):
- p.add_constraint(sum([q[k][i]*A[i] for i in range(n)]), min=0)
+ p.add_constraint(sum([q[k][i] * A[i] for i in range(n)]), min=0)
return A, p
-def delsarte_bound_Q_matrix(q, d, return_data=False, solver="PPL", isinteger=False):
+def delsarte_bound_Q_matrix(q, d, return_data=False,
+ solver="PPL", isinteger=False):
r"""
Delsarte bound on a code with Q matrix ``q`` and lower bound on min. dist. ``d``.
@@ -701,25 +691,19 @@ def delsarte_bound_Q_matrix(q, d, return_data=False, solver="PPL", isinteger=Fal
sage: a,p,val = codes.bounds.delsarte_bound_Q_matrix(q_matrix, 6, return_data=True)
sage: [j for i,j in p.get_values(a).items()]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
-
"""
-
- from sage.structure.element import is_Matrix
from sage.numerical.mip import MIPSolverException
+ from sage.structure.element import is_Matrix
if not is_Matrix(q):
- raise ValueError("Input to delsarte_bound_Q_matrix should be a sage Matrix()")
+ raise ValueError("Input to delsarte_bound_Q_matrix "
+ "should be a sage Matrix()")
A, p = _delsarte_Q_LP_building(q, d, solver, isinteger)
try:
bd = p.solve()
except MIPSolverException as exc:
- print("Solver exception: {}".format(exc))
- if return_data:
- return A, p, False
- return False
+ print(f"Solver exception: {exc}")
+ return (A, p, False) if return_data else False
- if return_data:
- return A, p, bd
- else:
- return bd
+ return (A, p, bd) if return_data else bd
diff --git a/src/sage/coding/guruswami_sudan/interpolation.py b/src/sage/coding/guruswami_sudan/interpolation.py
index eea537a115a..62ed9f3a8f1 100644
--- a/src/sage/coding/guruswami_sudan/interpolation.py
+++ b/src/sage/coding/guruswami_sudan/interpolation.py
@@ -130,7 +130,7 @@ def eqs_affine(x0, y0):
eqs = []
for i in range(s):
for j in range(s - i):
- eq = dict()
+ eq = {}
for monomial in monomials:
ihat = monomial[0]
jhat = monomial[1]
diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py
index 0d84996a483..f8f051a00d9 100644
--- a/src/sage/coding/linear_code.py
+++ b/src/sage/coding/linear_code.py
@@ -1224,7 +1224,7 @@ def galois_closure(self, F0):
n = len(G.columns())
k = len(G.rows())
G0 = [[x**q0 for x in g.list()] for g in G.rows()]
- G1 = [[x for x in g.list()] for g in G.rows()]
+ G1 = [list(g.list()) for g in G.rows()]
G2 = G0+G1
MS = MatrixSpace(F,2*k,n)
G3 = MS(G2)
@@ -1342,7 +1342,7 @@ def minimum_distance(self, algorithm=None):
.. NOTE::
- When using GAP, this raises a ``NotImplementedError`` if
+ When using GAP, this raises a :class:`NotImplementedError` if
the base field of the code has size greater than 256 due
to limitations in GAP.
@@ -1657,7 +1657,7 @@ def permutation_automorphism_group(self, algorithm="partition"):
size = Gp.Size()
print("\n Using the %s codewords of weight %s \n Supergroup size: \n %s\n " % (wts[wt], wt, size))
Cwt = filter(lambda c: c.WeightCodeword() == wt, eltsC) # bottleneck 2 (repeated
- matCwt = list(map(lambda c: c.VectorCodeword(), Cwt)) # for each i until stop = 1)
+ matCwt = [c.VectorCodeword() for c in Cwt] # for each i until stop = 1)
if len(matCwt) > 0:
A = libgap(matCwt).MatrixAutomorphisms()
Gp = A.Intersection2(Gp) # bottleneck 3
diff --git a/src/sage/coding/source_coding/huffman.py b/src/sage/coding/source_coding/huffman.py
index 6c08a4d885a..1d41f84d77f 100644
--- a/src/sage/coding/source_coding/huffman.py
+++ b/src/sage/coding/source_coding/huffman.py
@@ -367,9 +367,9 @@ def pop():
# Build the binary tree of a Huffman code, where the root of the tree
# is associated with the empty string.
self._build_code_from_tree(self._tree, d, prefix="")
- self._index = dict((i, s) for i, (s, w) in enumerate(symbols))
- self._character_to_code = dict(
- (s, d[i]) for i, (s, w) in enumerate(symbols))
+ self._index = {i: s for i, (s, w) in enumerate(symbols)}
+ self._character_to_code = {
+ s: d[i] for i, (s, w) in enumerate(symbols)}
def encode(self, string):
r"""
diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py
index 456eea36f44..4f1176c62a8 100644
--- a/src/sage/combinat/integer_vector.py
+++ b/src/sage/combinat/integer_vector.py
@@ -48,7 +48,7 @@
from sage.rings.integer import Integer
-def is_gale_ryser(r,s):
+def is_gale_ryser(r, s):
r"""
Tests whether the given sequences satisfy the condition
of the Gale-Ryser theorem.
@@ -314,20 +314,20 @@ def gale_ryser_theorem(p1, p2, algorithm="gale",
"""
from sage.matrix.constructor import matrix
- if not is_gale_ryser(p1,p2):
+ if not is_gale_ryser(p1, p2):
return False
- if algorithm == "ryser": # ryser's algorithm
+ if algorithm == "ryser": # ryser's algorithm
from sage.combinat.permutation import Permutation
# Sorts the sequences if they are not, and remembers the permutation
# applied
- tmp = sorted(enumerate(p1), reverse=True, key=lambda x:x[1])
+ tmp = sorted(enumerate(p1), reverse=True, key=lambda x: x[1])
r = [x[1] for x in tmp]
r_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()]
m = len(r)
- tmp = sorted(enumerate(p2), reverse=True, key=lambda x:x[1])
+ tmp = sorted(enumerate(p2), reverse=True, key=lambda x: x[1])
s = [x[1] for x in tmp]
s_permutation = [x-1 for x in Permutation([x[0]+1 for x in tmp]).inverse()]
@@ -340,12 +340,12 @@ def gale_ryser_theorem(p1, p2, algorithm="gale",
k = i + 1
while k < m and r[i] == r[k]:
k += 1
- if t >= k - i: # == number rows of the same length
+ if t >= k - i: # == number rows of the same length
for j in range(i, k):
r[j] -= 1
c[j] = 1
t -= k - i
- else: # Remove the t last rows of that length
+ else: # Remove the t last rows of that length
for j in range(k-t, k):
r[j] -= 1
c[j] = 1
@@ -366,17 +366,17 @@ def gale_ryser_theorem(p1, p2, algorithm="gale",
k1, k2 = len(p1), len(p2)
p = MixedIntegerLinearProgram(solver=solver)
b = p.new_variable(binary=True)
- for (i,c) in enumerate(p1):
- p.add_constraint(p.sum([b[i,j] for j in range(k2)]) == c)
- for (i,c) in enumerate(p2):
- p.add_constraint(p.sum([b[j,i] for j in range(k1)]) == c)
+ for (i, c) in enumerate(p1):
+ p.add_constraint(p.sum([b[i, j] for j in range(k2)]) == c)
+ for (i, c) in enumerate(p2):
+ p.add_constraint(p.sum([b[j, i] for j in range(k1)]) == c)
p.set_objective(None)
p.solve()
b = p.get_values(b, convert=ZZ, tolerance=integrality_tolerance)
M = [[0]*k2 for i in range(k1)]
for i in range(k1):
for j in range(k2):
- M[i][j] = b[i,j]
+ M[i][j] = b[i, j]
return matrix(M)
else:
@@ -780,6 +780,43 @@ def __contains__(self, x):
return False
return True
+ def _unrank_helper(self, x, rtn):
+ """
+ Return the element at rank ``x`` by iterating through all integer vectors beginning with ``rtn``.
+
+ INPUT:
+
+ - ``x`` - a nonnegative integer
+ - ``rtn`` - a list of nonnegative integers
+
+
+ EXAMPLES::
+
+ sage: IV = IntegerVectors(k=5)
+ sage: IV._unrank_helper(10, [2,0,0,0,0])
+ [1, 0, 0, 0, 1]
+
+ sage: IV = IntegerVectors(n=7)
+ sage: IV._unrank_helper(100, [7,0,0,0])
+ [2, 0, 0, 5]
+
+ sage: IV = IntegerVectors(n=12, k=7)
+ sage: IV._unrank_helper(1000, [12,0,0,0,0,0,0])
+ [5, 3, 1, 1, 1, 1, 0]
+ """
+ ptr = 0
+ while True:
+ current_rank = self.rank(rtn)
+ if current_rank < x:
+ rtn[ptr+1] = rtn[ptr]
+ rtn[ptr] = 0
+ ptr += 1
+ elif current_rank > x:
+ rtn[ptr] -= 1
+ rtn[ptr-1] += 1
+ else:
+ return self._element_constructor_(rtn)
+
class IntegerVectors_all(UniqueRepresentation, IntegerVectors):
"""
@@ -839,7 +876,10 @@ def __init__(self, n):
sage: TestSuite(IV).run()
"""
self.n = n
- IntegerVectors.__init__(self, category=InfiniteEnumeratedSets())
+ if self.n == 0:
+ IntegerVectors.__init__(self, category=EnumeratedSets())
+ else:
+ IntegerVectors.__init__(self, category=InfiniteEnumeratedSets())
def _repr_(self):
"""
@@ -898,6 +938,68 @@ def __contains__(self, x):
return False
return sum(x) == self.n
+ def rank(self, x):
+ """
+ Return the rank of a given element.
+
+ INPUT:
+
+ - ``x`` -- a list with ``sum(x) == n``
+
+ EXAMPLES::
+
+ sage: IntegerVectors(n=5).rank([5,0])
+ 1
+ sage: IntegerVectors(n=5).rank([3,2])
+ 3
+ """
+ if sum(x) != self.n:
+ raise ValueError("argument is not a member of IntegerVectors({},{})".format(self.n, None))
+
+ n, k, s = self.n, len(x), 0
+ r = binomial(k + n - 1, n + 1)
+ for i in range(k - 1):
+ s += x[k - 1 - i]
+ r += binomial(s + i, i + 1)
+ return r
+
+ def unrank(self, x):
+ """
+ Return the element at given rank x.
+
+ INPUT:
+
+ - ``x`` -- an integer.
+
+ EXAMPLES::
+
+ sage: IntegerVectors(n=5).unrank(2)
+ [4, 1]
+ sage: IntegerVectors(n=10).unrank(10)
+ [1, 9]
+ """
+ rtn = [self.n]
+ while self.rank(rtn) <= x:
+ rtn.append(0)
+ rtn.pop()
+
+ return IntegerVectors._unrank_helper(self, x, rtn)
+
+ def cardinality(self):
+ """
+ Return the cardinality of ``self``.
+
+ EXAMPLES::
+
+ sage: IntegerVectors(n=0).cardinality()
+ 1
+ sage: IntegerVectors(n=10).cardinality()
+ +Infinity
+ """
+ if self.n == 0:
+ return Integer(1)
+ return PlusInfinity()
+
class IntegerVectors_k(UniqueRepresentation, IntegerVectors):
"""
@@ -912,7 +1014,10 @@ def __init__(self, k):
sage: TestSuite(IV).run()
"""
self.k = k
- IntegerVectors.__init__(self, category=InfiniteEnumeratedSets())
+ if self.k == 0:
+ IntegerVectors.__init__(self, category=EnumeratedSets())
+ else:
+ IntegerVectors.__init__(self, category=InfiniteEnumeratedSets())
def _repr_(self):
"""
@@ -968,6 +1073,75 @@ def __contains__(self, x):
return False
return len(x) == self.k
+ def rank(self, x):
+ """
+ Return the rank of a given element.
+
+ INPUT:
+
+ - ``x`` -- a list with ``len(x) == k``
+
+ EXAMPLES::
+
+ sage: IntegerVectors(k=5).rank([0,0,0,0,0])
+ 0
+ sage: IntegerVectors(k=5).rank([1,1,0,0,0])
+ 7
+ """
+ if len(x) != self.k:
+ raise ValueError("argument is not a member of IntegerVectors({},{})".format(None, self.k))
+
+ n, k, s = sum(x), self.k, 0
+ r = binomial(n + k - 1, k)
+ for i in range(k - 1):
+ s += x[k - 1 - i]
+ r += binomial(s + i, i + 1)
+ return r
+
+ def unrank(self, x):
+ """
+ Return the element at given rank x.
+
+ INPUT:
+
+ - ``x`` -- an integer such that x < self.cardinality()``
+
+ EXAMPLES::
+
+ sage: IntegerVectors(k=5).unrank(10)
+ [1, 0, 0, 0, 1]
+ sage: IntegerVectors(k=5).unrank(15)
+ [0, 0, 2, 0, 0]
+ sage: IntegerVectors(k=0).unrank(0)
+ []
+ """
+ if self.k == 0 and x != 0:
+ raise IndexError(f"Index {x} is out of range for the IntegerVector.")
+ rtn = [0]*self.k
+ if self.k == 0 and x == 0:
+ return rtn
+
+ while self.rank(rtn) <= x:
+ rtn[0] += 1
+ rtn[0] -= 1
+
+ return IntegerVectors._unrank_helper(self, x, rtn)
+
+ def cardinality(self):
+ """
+ Return the cardinality of ``self``.
+
+ EXAMPLES::
+
+ sage: IntegerVectors(k=0).cardinality()
+ 1
+ sage: IntegerVectors(k=10).cardinality()
+ +Infinity
+ """
+ if self.k == 0:
+ return Integer(1)
+ return PlusInfinity()
+
class IntegerVectors_nk(UniqueRepresentation, IntegerVectors):
"""
@@ -1010,11 +1184,11 @@ def _list_rec(self, n, k):
res = []
if k == 1:
- return [ (n, ) ]
+ return [(n, )]
for nbar in range(n + 1):
n_diff = n - nbar
- for rest in self._list_rec( nbar , k - 1):
+ for rest in self._list_rec(nbar, k - 1):
res.append((n_diff,) + rest)
return res
@@ -1153,17 +1327,49 @@ def rank(self, x):
if x not in self:
raise ValueError("argument is not a member of IntegerVectors({},{})".format(self.n, self.k))
- n = self.n
- k = self.k
-
- r = 0
+ k, s, r = self.k, 0, 0
for i in range(k - 1):
- k -= 1
- n -= x[i]
- r += binomial(k + n - 1, k)
-
+ s += x[k - 1 - i]
+ r += binomial(s + i, i + 1)
return r
+ def unrank(self, x):
+ """
+ Return the element at given rank x.
+
+ INPUT:
+
+ - ``x`` -- an integer such that ``x < self.cardinality()``
+
+ EXAMPLES::
+
+ sage: IntegerVectors(4,5).unrank(30)
+ [1, 0, 1, 0, 2]
+ sage: IntegerVectors(2,3).unrank(5)
+ [0, 0, 2]
+ """
+ if x >= self.cardinality():
+ raise IndexError(f"Index {x} is out of range for the IntegerVector.")
+ rtn = [0]*self.k
+ rtn[0] = self.n
+ return IntegerVectors._unrank_helper(self, x, rtn)
+
+ def cardinality(self):
+ """
+ Return the cardinality of ``self``.
+
+ EXAMPLES::
+
+ sage: IntegerVectors(3,5).cardinality()
+ 35
+ sage: IntegerVectors(99, 3).cardinality()
+ 5050
+ sage: IntegerVectors(10^9 - 1, 3).cardinality()
+ 500000000500000000
+ """
+ n, k = self.n, self.k
+ return Integer(binomial(n + k - 1, n))
+
class IntegerVectors_nnondescents(UniqueRepresentation, IntegerVectors):
r"""
@@ -1320,11 +1526,11 @@ def __init__(self, n=None, k=None, **constraints):
category = FiniteEnumeratedSets()
else:
category = EnumeratedSets()
- elif k is not None and 'max_part' in constraints: # n is None
+ elif k is not None and 'max_part' in constraints: # n is None
category = FiniteEnumeratedSets()
else:
category = EnumeratedSets()
- IntegerVectors.__init__(self, category=category) # placeholder category
+ IntegerVectors.__init__(self, category=category) # placeholder category
def _repr_(self):
"""
diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py
index 996d73a5efa..7877fedee44 100644
--- a/src/sage/combinat/integer_vectors_mod_permgroup.py
+++ b/src/sage/combinat/integer_vectors_mod_permgroup.py
@@ -1,6 +1,11 @@
# sage.doctest: needs sage.combinat sage.groups
r"""
Integer vectors modulo the action of a permutation group
+
+AUTHORS:
+
+* Nicolas Borie (2010-2012) - original module
+* Jukka Kohonen (2023) - fast cardinality method, :issue:`36787`, :issue:`36681`
"""
# ****************************************************************************
# Copyright (C) 2010-12 Nicolas Borie
@@ -24,6 +29,12 @@
from sage.combinat.integer_vector import IntegerVectors
+from sage.rings.power_series_ring import PowerSeriesRing
+from sage.rings.rational_field import QQ
+from sage.rings.integer import Integer
+from sage.misc.misc_c import prod
+from sage.arith.misc import binomial
+
class IntegerVectorsModPermutationGroup(UniqueRepresentation):
r"""
@@ -42,39 +53,39 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
v = \max_{\text{lex order}} \{g \cdot v | g \in G \}
The action of `G` is on position. This means for example that the
- simple transposition `s_1 = (1, 2)` swaps the first and the second entries
- of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]`
+ simple transposition `s_1 = (1, 2)` swaps the first and the second
+ entries of any integer vector `v = [a_1, a_2, a_3, \dots , a_n]`
.. MATH::
s_1 \cdot v = [a_2, a_1, a_3, \dots , a_n]
- This functions returns a parent which contains a single integer
- vector by orbit under the action of the permutation group `G`. The
- approach chosen here is to keep the maximal integer vector for the
- lexicographic order in each orbit. Such maximal vector will be
- called canonical integer vector under the action of the
- permutation group `G`.
+ This function returns a parent which contains, from each orbit
+ orbit under the action of the permutation group `G`, a single
+ canonical vector. The canonical vector is the one that is maximal
+ within the orbit according to lexicographic order.
INPUT:
- ``G`` - a permutation group
- ``sum`` - (default: None) - a nonnegative integer
- ``max_part`` - (default: None) - a nonnegative integer setting the
- maximum of entries of elements
+ maximum value for every element
- ``sgs`` - (default: None) - a strong generating system of the
group `G`. If you do not provide it, it will be calculated at the
creation of the parent
OUTPUT:
- - If ``sum`` and ``max_part`` are None, it returns the infinite enumerated
- set of all integer vectors (list of integers) maximal in their orbit for
- the lexicographic order.
+ - If ``sum`` and ``max_part`` are None, it returns the infinite
+ enumerated set of all integer vectors (lists of integers) maximal
+ in their orbit for the lexicographic order. Exceptionally, if
+ the domain of ``G`` is empty, the result is a finite enumerated
+ set that contains one element, namely the empty vector.
- - If ``sum`` is an integer, it returns a finite enumerated set containing
- all integer vectors maximal in their orbit for the lexicographic order
- and whose entries sum to ``sum``.
+ - If ``sum`` is an integer, it returns a finite enumerated set
+ containing all integer vectors maximal in their orbit for the
+ lexicographic order and whose entries sum to ``sum``.
EXAMPLES:
@@ -105,7 +116,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
The method
:meth:`~sage.combinat.integer_vectors_mod_permgroup.IntegerVectorsModPermutationGroup_All.is_canonical`
- tests if any integer vector is maximal in its orbit. This method
+ tests if an integer vector is maximal in its orbit. This method
is also used in the containment test::
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]))
@@ -124,7 +135,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
sage: I.is_canonical('bla')
Traceback (most recent call last):
...
- AssertionError: bla should be a list or a integer vector
+ AssertionError: bla should be a list or an integer vector
If you give a value to the extra argument ``sum``, the set returned
will be a finite set containing only canonical vectors whose entries
@@ -153,10 +164,32 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
sage: I.orbit([2,2,2])
{[2, 2, 2]}
+ Even without constraints, for an empty domain the result is
+ a singleton set::
+
+ sage: G = PermutationGroup([], domain=[])
+ sage: sgs = tuple(tuple(s) for s in G.strong_generating_system())
+ sage: list(IntegerVectorsModPermutationGroup(G, sgs=sgs))
+ [[]]
+
+
+ .. WARNING::
+
+ Because of :issue:`36527`, permutation groups that have
+ different domains but similar generators can be erroneously
+ treated as the same group. This will silently produce
+ erroneous results. To avoid this issue, compute a strong
+ generating system for the group as::
+
+ sgs = tuple(tuple(s) for s in G.strong_generating_system())
+
+ and provide it as the optional ``sgs`` argument to the
+ constructor.
+
TESTS:
Let us check that canonical integer vectors of the symmetric group
- are just sorted list of integers::
+ are just nonincreasing lists of integers::
sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time
sage: p = iter(I) # long time
@@ -164,9 +197,9 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
....: v = list(next(p))
....: assert sorted(v, reverse=True) == v
- We now check that there is as much of canonical vectors under the
- symmetric group `S_n` whose entries sum to `d` than partitions of
- `d` of at most `n` parts::
+ We now check that there are as many canonical vectors under the
+ symmetric group `S_n` whose entries sum to `d` as there are
+ partitions of `d` of at most `n` parts::
sage: I = IntegerVectorsModPermutationGroup(SymmetricGroup(5)) # long time
sage: for i in range(10): # long time
@@ -185,15 +218,16 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
18
23
- We present a last corner case: trivial groups. For the trivial
- group ``G`` acting on a list of length `n`, all integer vectors of
- length `n` are canonical::
+ Another corner case is trivial groups. For the trivial group ``G``
+ acting on a list of length `n`, all integer vectors of length `n`
+ are canonical::
sage: # long time
sage: G = PermutationGroup([[(6,)]])
sage: G.cardinality()
1
- sage: I = IntegerVectorsModPermutationGroup(G)
+ sage: sgs = tuple(tuple(s) for s in G.strong_generating_system())
+ sage: I = IntegerVectorsModPermutationGroup(G, sgs=sgs)
sage: for i in range(10):
....: d1 = I.subset(i).cardinality()
....: d2 = IntegerVectors(i,6).cardinality()
@@ -209,6 +243,7 @@ class IntegerVectorsModPermutationGroup(UniqueRepresentation):
792
1287
2002
+
"""
@staticmethod
def __classcall__(cls, G, sum=None, max_part=None, sgs=None):
@@ -225,13 +260,22 @@ def __classcall__(cls, G, sum=None, max_part=None, sgs=None):
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3)]]), 8, max_part=5)
"""
if sum is None and max_part is None:
- return IntegerVectorsModPermutationGroup_All(G, sgs=sgs)
+ # No constraints.
+ if G.domain():
+ # Nonempty domain, infinite set.
+ return IntegerVectorsModPermutationGroup_All(G, sgs=sgs)
+ else:
+ # Empty domain, singleton set.
+ return IntegerVectorsModPermutationGroup_with_constraints(
+ G, 0, max_part=-1, sgs=sgs)
else:
+ # Some constraints, either sum or max_part or both.
if sum is not None:
assert sum == NN(sum)
if max_part is not None:
assert max_part == NN(max_part)
- return IntegerVectorsModPermutationGroup_with_constraints(G, sum, max_part, sgs=sgs)
+ return IntegerVectorsModPermutationGroup_with_constraints(
+ G, sum, max_part, sgs=sgs)
class IntegerVectorsModPermutationGroup_All(UniqueRepresentation, RecursivelyEnumeratedSet_forest):
@@ -435,7 +479,7 @@ def is_canonical(self, v, check=True):
False
"""
if check:
- assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector' % v
+ assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or an integer vector' % v
assert (self.n == len(v)), '%s should be of length %s' % (v, self.n)
for p in v:
assert (p == NN(p)), 'Elements of %s should be integers' % v
@@ -634,17 +678,27 @@ def _repr_(self):
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6); S
Integer vectors of length 4 and of sum 6 enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), 6, max_part=4); S
- Vectors of length 4 and of sum 6 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
+ Vectors of length 4 and of sum 6 whose entries are in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
sage: S = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), max_part=4); S
- Integer vectors of length 4 whose entries is in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
+ Integer vectors of length 4 whose entries are in {0, ..., 4} enumerated up to the action of Permutation Group with generators [(1,2,3,4)]
"""
if self._sum is not None:
if self._max_part >= 0:
- return "Vectors of length %s and of sum %s whose entries is in {0, ..., %s} enumerated up to the action of %s" % (self.n, self._sum, self._max_part, self.permutation_group())
+ return ("Vectors of length %s and of sum %s"
+ " whose entries are in {0, ..., %s}"
+ " enumerated up to the action of %s"
+ % (self.n, self._sum, self._max_part,
+ self.permutation_group()))
else:
- return "Integer vectors of length %s and of sum %s enumerated up to the action of %s" % (self.n, self._sum, self.permutation_group())
+ return ("Integer vectors of length %s"
+ " and of sum %s"
+ " enumerated up to the action of %s"
+ % (self.n, self._sum, self.permutation_group()))
else:
- return "Integer vectors of length %s whose entries is in {0, ..., %s} enumerated up to the action of %s" % (self.n, self._max_part, self.permutation_group())
+ return ("Integer vectors of length %s"
+ " whose entries are in {0, ..., %s}"
+ " enumerated up to the action of %s"
+ % (self.n, self._max_part, self.permutation_group()))
def roots(self):
r"""
@@ -751,6 +805,7 @@ def __iter__(self):
[2, 0, 2, 0]
[2, 0, 1, 1]
[1, 1, 1, 1]
+
sage: I = IntegerVectorsModPermutationGroup(PermutationGroup([[(1,2,3,4)]]), sum=7, max_part=3)
sage: for i in I: i
[3, 3, 1, 0]
@@ -763,18 +818,197 @@ def __iter__(self):
[3, 1, 1, 2]
[3, 0, 2, 2]
[2, 2, 2, 1]
+
+ Check that :issue:`36681` is fixed::
+
+ sage: G = PermutationGroup([], domain=[])
+ sage: I = IntegerVectorsModPermutationGroup(G, sum=0)
+ sage: list(iter(I))
+ [[]]
+
+ Check that :issue:`36681` is fixed::
+
+ sage: G = PermutationGroup([], domain=[])
+ sage: I = IntegerVectorsModPermutationGroup(G, sum=3)
+ sage: list(iter(I))
+ []
+
"""
+ # Special cases when domain is empty.
+ if self.n == 0:
+ if self._sum is not None and self._sum > 0:
+ # No empty vector can have positive sum.
+ return iter(())
+ else:
+ # Sum is allowed to be zero. It does not matter what
+ # the maxpart is, the empty vector is a solution.
+ return iter([self([])])
+
+ # General case, nonempty domain.
if self._max_part < 0:
return self.elements_of_depth_iterator(self._sum)
else:
- SF = RecursivelyEnumeratedSet_forest((self([0]*(self.n), check=False),),
- lambda x : [self(y, check=False) for y in canonical_children(self._sgs, x, self._max_part)],
- algorithm='breadth')
+ SF = RecursivelyEnumeratedSet_forest(
+ (self([0]*(self.n), check=False),),
+ lambda x: [self(y, check=False)
+ for y in canonical_children(
+ self._sgs, x, self._max_part)],
+ algorithm='breadth')
if self._sum is None:
return iter(SF)
else:
return SF.elements_of_depth_iterator(self._sum)
+ def cardinality(self):
+ r"""
+ Return the number of integer vectors in the set.
+
+ The algorithm utilises :wikipedia:`Cycle Index Theorem `, allowing
+ for a faster than a plain enumeration computation.
+
+ EXAMPLES:
+
+ With a trivial group all vectors are canonical::
+
+ sage: G = PermutationGroup([], domain=[1,2,3])
+ sage: IntegerVectorsModPermutationGroup(G, 5).cardinality()
+ 21
+ sage: IntegerVectors(5, 3).cardinality()
+ 21
+
+ With two interchangeable elements, the smaller one
+ ranges from zero to ``sum//2``::
+
+ sage: G = PermutationGroup([(1,2)])
+ sage: IntegerVectorsModPermutationGroup(G, 1000).cardinality()
+ 501
+
+ Binary vectors up to full symmetry are first some ones and
+ then some zeros::
+
+ sage: G = SymmetricGroup(10)
+ sage: I = IntegerVectorsModPermutationGroup(G, max_part=1)
+ sage: I.cardinality()
+ 11
+
+ Binary vectors of constant weight, up to PGL(2,17), which
+ is 3-transitive, but not 4-transitive::
+
+ sage: G=PGL(2,17)
+ sage: I = IntegerVectorsModPermutationGroup(G, sum=3, max_part=1)
+ sage: I.cardinality()
+ 1
+ sage: I = IntegerVectorsModPermutationGroup(G, sum=4, max_part=1)
+ sage: I.cardinality()
+ 3
+
+ TESTS:
+
+ Check that :issue:`36681` is fixed::
+
+ sage: G = PermutationGroup([], domain=[])
+ sage: sgs = tuple(tuple(t) for t in G.strong_generating_system())
+ sage: V = IntegerVectorsModPermutationGroup(G, sum=1, sgs=sgs)
+ sage: V.cardinality()
+ 0
+
+ The case when both ``sum`` and ``max_part`` are specified::
+
+ sage: G = PermutationGroup([(1,2,3)])
+ sage: I = IntegerVectorsModPermutationGroup(G, sum=10, max_part=5)
+ sage: I.cardinality()
+ 7
+
+ All permutation groups of degree 4::
+
+ sage: for G in SymmetricGroup(4).subgroups():
+ ....: sgs = tuple(tuple(t) for t in G.strong_generating_system())
+ ....: I1 = IntegerVectorsModPermutationGroup(G, sum=10, sgs=sgs)
+ ....: assert I1.cardinality() == len(list(I1))
+ ....: I2 = IntegerVectorsModPermutationGroup(G, max_part=3, sgs=sgs)
+ ....: assert I2.cardinality() == len(list(I2))
+ ....: I3 = IntegerVectorsModPermutationGroup(G, sum=10, max_part=3, sgs=sgs)
+ ....: assert I3.cardinality() == len(list(I3))
+
+ Symmetric group with sums 0 and 1::
+
+ sage: S10 = SymmetricGroup(10)
+ sage: IntegerVectorsModPermutationGroup(S10, 0).cardinality()
+ 1
+ sage: IntegerVectorsModPermutationGroup(S10, 1).cardinality()
+ 1
+
+ Trivial group with sums 1 and 100::
+
+ sage: T10 = PermutationGroup([], domain=range(1, 11))
+ sage: IntegerVectorsModPermutationGroup(T10, 1).cardinality()
+ 10
+ sage: IntegerVectorsModPermutationGroup(T10, 100).cardinality()
+ 4263421511271
+
+ """
+ G = self._permgroup
+ k = G.degree() # Vector length
+ d = self._sum # Required sum
+ m = self._max_part # Max of one entry, -1 for no limit
+ if m == -1:
+ m = d # Any entry cannot exceed total
+
+ # Some easy special cases.
+ if k == 0:
+ # Empty vectors. There is only one, and it has zero sum.
+ # Here _max_part does not matter because any _max_part
+ # condition is vacuously true (with no parts).
+ if d == 0 or d is None:
+ return Integer(1)
+ else:
+ return Integer(0)
+ if d == 0 or m == 0:
+ # All-zero vectors. There is only one of them.
+ return Integer(1)
+ if d == 1:
+ # Vectors with one 1 and all other elements zero.
+ # The 1 can be placed in any orbit, and by symmetry
+ # it will be on the first element of the orbit.
+ return Integer(len(G.orbits()))
+ if d is not None and m >= d and G.is_trivial():
+ # Simple calculation with stars and bars.
+ return Integer(binomial(d + k - 1, k - 1))
+
+ # General case.
+ #
+ # Cardinality is computed using the Cycle Index Theorem. We
+ # have two cases. With a fixed sum d we work with power
+ # series and extract the x^d coefficient. Without a fixed sum
+ # we can do with integer arithmetic.
+ Z = G.cycle_index()
+
+ if d is None:
+ # Case 1. Without a fixed sum, the sum can be up to k*m.
+ result = sum(coeff * (m+1)**len(cycle_type)
+ for cycle_type, coeff in Z)
+ # Computed as Rational, but should have an integer value
+ # by now.
+ return Integer(result)
+
+ # Case 2. Fixed sum d. Work with power series with enough
+ # precision that x^d is valid.
+ R = PowerSeriesRing(QQ, 'x', default_prec=d+1)
+ x = R.gen()
+
+ # The figure-counting series, for max_part==m, is (1-t**(m+1))
+ # / (1-t) = 1+t+...+t**m. For the function-counting series,
+ # we substitute x**cycle_length for t.
+ #
+ funcount = sum(
+ coeff * prod((1 - x**((m+1)*cycle_len)) / (1 - x**cycle_len)
+ for cycle_len in cycle_type)
+ for cycle_type, coeff in Z)
+
+ # Extract the d'th degree coefficient. Computed as Rational,
+ # but should have an integer value by now.
+ return Integer(funcount[d])
+
def is_canonical(self, v, check=True):
r"""
Return ``True`` if the integer list ``v`` is maximal in its
@@ -798,7 +1032,7 @@ def is_canonical(self, v, check=True):
True
"""
if check:
- assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or a integer vector' % v
+ assert isinstance(v, (ClonableIntArray, list)), '%s should be a list or an integer vector' % v
assert (self.n == len(v)), '%s should be of length %s' % (v, self.n)
for p in v:
assert (p == NN(p)), 'Elements of %s should be integers' % v
@@ -980,7 +1214,7 @@ class Element(ClonableIntArray):
sage: v = I.element_class(I, [3,2,0,0])
Traceback (most recent call last):
...
- AssertionError: [3, 2, 0, 0] should be a integer vector of sum 4
+ AssertionError: [3, 2, 0, 0] should be an integer vector of sum 4
"""
def check(self):
@@ -1000,7 +1234,7 @@ def check(self):
AssertionError
"""
if self.parent()._sum is not None:
- assert sum(self) == self.parent()._sum, '%s should be a integer vector of sum %s' % (self, self.parent()._sum)
+ assert sum(self) == self.parent()._sum, '%s should be an integer vector of sum %s' % (self, self.parent()._sum)
if self.parent()._max_part >= 0:
assert max(self) <= self.parent()._max_part, 'Entries of %s must be inferior to %s' % (self, self.parent()._max_part)
assert self.parent().is_canonical(self)
diff --git a/src/sage/combinat/root_system/type_relabel.py b/src/sage/combinat/root_system/type_relabel.py
index 350e290b32a..712937d08ce 100644
--- a/src/sage/combinat/root_system/type_relabel.py
+++ b/src/sage/combinat/root_system/type_relabel.py
@@ -10,7 +10,7 @@
from sage.misc.cachefunc import cached_method
from sage.misc.lazy_attribute import lazy_attribute
-from sage.sets.family import FiniteFamily
+from sage.sets.family import Family, FiniteFamily
from sage.combinat.root_system import cartan_type
from sage.combinat.root_system import ambient_space
from sage.combinat.root_system.root_lattice_realizations import RootLatticeRealizations
@@ -173,10 +173,10 @@ def __init__(self, type, relabelling):
sage: rI5 = CartanType(['I',5]).relabel({1:0,2:1})
sage: rI5.root_system().ambient_space()
"""
- assert isinstance(relabelling, FiniteFamily)
cartan_type.CartanType_decorator.__init__(self, type)
- self._relabelling = relabelling._dictionary
- self._relabelling_inverse = relabelling.inverse_family()._dictionary
+ relabelling = Family(relabelling)
+ self._relabelling = dict(relabelling.items())
+ self._relabelling_inverse = dict(relabelling.inverse_family().items())
self._index_set = tuple(sorted(relabelling[i] for i in type.index_set()))
# TODO: design an appropriate infrastructure to handle this
# automatically? Maybe using categories and axioms?
diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py
index d24b87fe5d4..ce6c63f66f3 100644
--- a/src/sage/crypto/lattice.py
+++ b/src/sage/crypto/lattice.py
@@ -211,7 +211,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None,
[-4 -3 2 -5 0 0 0 0 0 1]
]
- sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True)
+ sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) # needs fpylll
Free module of degree 10 and rank 10 over Integer Ring
User basis matrix:
[ 0 0 1 1 0 -1 -1 -1 1 0]
diff --git a/src/sage/databases/cremona.py b/src/sage/databases/cremona.py
index 3e1615e5fd4..7b0a3263a32 100644
--- a/src/sage/databases/cremona.py
+++ b/src/sage/databases/cremona.py
@@ -9,10 +9,11 @@
is included by default with Sage. It contains Weierstrass equations,
rank, and torsion for curves up to conductor 10000.
-The large database includes all curves in John Cremona's tables. It
-also includes data related to the BSD conjecture and modular degrees
-for all of these curves, and generators for the Mordell-Weil
-groups. To install it, run the following in the shell::
+The large database includes all curves in John Cremona's tables. It also
+includes data related to the BSD conjecture and modular degrees for all of
+these curves, and generators for the Mordell-Weil groups. To install it via the
+optional :ref:`database_cremona_ellcurve `
+package, run the following command in the shell ::
sage -i database_cremona_ellcurve
diff --git a/src/sage/databases/cubic_hecke_db.py b/src/sage/databases/cubic_hecke_db.py
index b78e582d91e..d25ef5ca630 100644
--- a/src/sage/databases/cubic_hecke_db.py
+++ b/src/sage/databases/cubic_hecke_db.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
r"""
-Cubic Hecke Database
+Cubic Hecke database
This module contains the class :class:`CubicHeckeDataBase` which serves as an
interface to `Ivan Marin's data files
@@ -31,10 +31,25 @@
of linear forms on the cubic Hecke algebra on at most four strands
satisfying the Markov trace condition for its cubic Hecke subalgebras.
+To use the database, you need to install the optional
+:ref:`database_cubic_hecke ` package by the Sage
+command ::
+
+ sage -i database_cubic_hecke
+
+EXAMPLES::
+
+ sage: # optional - database_cubic_hecke
+ sage: from sage.databases.cubic_hecke_db import CubicHeckeDataBase
+ sage: cha_db = CubicHeckeDataBase()
+ sage: cha_db
+
+
AUTHORS:
-- Sebastian Oehms (May 2020): initial version
-- Sebastian Oehms (March 2022): PyPi version and Markov trace functionality
+- Sebastian Oehms (2020-05): initial version
+- Sebastian Oehms (2022-03): PyPi version and Markov trace functionality
+
"""
##############################################################################
diff --git a/src/sage/databases/cunningham_tables.py b/src/sage/databases/cunningham_tables.py
index 9c23bf31816..94862b98561 100644
--- a/src/sage/databases/cunningham_tables.py
+++ b/src/sage/databases/cunningham_tables.py
@@ -1,5 +1,19 @@
r"""
-Cunningham table
+Cunningham tables
+
+This module provides :func:`cunningham_prime_factors`, which lists the prime
+numbers occurring in the factorization of numbers of type `b^n+1` or `b^n-1`
+with `b \in \{2,3,5,6,7,10,11,12\}`. For an introduction to Cunningham prime
+factors, see :wikipedia:`Cunningham_Project`. The data becomes available if you
+install the optional :ref:`cunningham_tables ` package by
+the command ::
+
+ sage -i cunningham_tables
+
+AUTHORS:
+
+- Yann Laigle-Chapuy (2009-10-18): initial version
+
"""
import os
@@ -16,12 +30,25 @@ def cunningham_prime_factors():
They occur in the factorization of numbers of type `b^n+1` or `b^n-1` with `b \in \{2,3,5,6,7,10,11,12\}`.
- Data from http://cage.ugent.be/~jdemeyer/cunningham/
+ EXAMPLES::
+
+ sage: # optional - cunningham_tables
+ sage: from sage.databases.cunningham_tables import cunningham_prime_factors
+ sage: cunningham_prime_factors()
+ [2,
+ 3,
+ 5,
+ 7,
+ 11,
+ 13,
+ 17,
+ ...
"""
file = os.path.join(SAGE_SHARE,'cunningham_tables','cunningham_prime_factors.sobj')
if os.path.exists(file):
return [Integer(_) for _ in load(file)]
else:
from warnings import warn
- warn("You might consider installing the optional package for factoring Cunningham numbers with the following command: ``sage -i cunningham_tables``")
+ warn("You might consider installing the optional package for factoring Cunningham numbers"
+ " with the following command: ``sage -i cunningham_tables``")
return []
diff --git a/src/sage/databases/db_class_polynomials.py b/src/sage/databases/db_class_polynomials.py
index 642865a5d63..923d3a419ee 100644
--- a/src/sage/databases/db_class_polynomials.py
+++ b/src/sage/databases/db_class_polynomials.py
@@ -1,5 +1,23 @@
"""
-Database of Hilbert Polynomials
+Database of Hilbert polynomials
+
+This module gives access to the database of Hilbert class polynomials. To use
+the database, you need to install the optional :ref:`database_kohel
+` package by the Sage command ::
+
+ sage -i database_kohel
+
+EXAMPLES::
+
+ sage: # optional - database_kohel
+ sage: db = HilbertClassPolynomialDatabase()
+ sage: db[32]
+ x^2 - 52250000*x + 12167000000
+
+AUTHORS:
+
+- David Kohel (2006-08-04): initial version
+
"""
# ****************************************************************************
# Copyright (C) 2006 David Kohel
@@ -45,8 +63,9 @@ def __getitem__(self, disc):
r"""
TESTS::
+ sage: # optional - database_kohel
sage: db = HilbertClassPolynomialDatabase()
- sage: db[32] # optional - database_kohel
+ sage: db[32]
x^2 - 52250000*x + 12167000000
sage: db[123913912]
Traceback (most recent call last):
@@ -66,16 +85,17 @@ class HilbertClassPolynomialDatabase(ClassPolynomialDatabase):
EXAMPLES::
+ sage: # optional - database_kohel
sage: db = HilbertClassPolynomialDatabase()
- sage: db[-4] # optional - database_kohel
+ sage: db[-4]
x - 1728
- sage: db[-7] # optional - database_kohel
+ sage: db[-7]
x + 3375
- sage: f = db[-23]; f # optional - database_kohel
+ sage: f = db[-23]; f
x^3 + 3491750*x^2 - 5151296875*x + 12771880859375
- sage: f.discriminant().factor() # optional - database_kohel
+ sage: f.discriminant().factor()
-1 * 5^18 * 7^12 * 11^4 * 17^2 * 19^2 * 23
- sage: db[-23] # optional - database_kohel
+ sage: db[-23]
x^3 + 3491750*x^2 - 5151296875*x + 12771880859375
"""
model = "Cls"
diff --git a/src/sage/databases/db_modular_polynomials.py b/src/sage/databases/db_modular_polynomials.py
index 4e0422539fc..025682c05ae 100644
--- a/src/sage/databases/db_modular_polynomials.py
+++ b/src/sage/databases/db_modular_polynomials.py
@@ -1,5 +1,26 @@
"""
-Database of Modular Polynomials
+Database of modular polynomials
+
+This module gives access to the database of modular polynomials. To use the
+database, you need to install the optional :ref:`database_kohel
+` package by the Sage command ::
+
+ sage -i database_kohel
+
+EXAMPLES::
+
+ sage: # optional - database_kohel
+ sage: DBMP = ClassicalModularPolynomialDatabase()
+ sage: f = DBMP[29]
+ sage: f.degree()
+ 58
+ sage: f.coefficient([28,28])
+ 400152899204646997840260839128
+
+AUTHORS:
+
+- David Kohel (2006-08-04): initial version
+
"""
# ****************************************************************************
# Copyright (C) 2006 William Stein
@@ -48,8 +69,9 @@ def _dbz_to_integer_list(name):
r"""
TESTS::
+ sage: # optional - database_kohel
sage: from sage.databases.db_modular_polynomials import _dbz_to_integer_list
- sage: _dbz_to_integer_list('PolMod/Atk/pol.002.dbz') # optional - database_kohel
+ sage: _dbz_to_integer_list('PolMod/Atk/pol.002.dbz')
[[3, 0, 1],
[2, 1, -1],
[2, 0, 744],
@@ -58,9 +80,9 @@ def _dbz_to_integer_list(name):
[0, 2, 1],
[0, 1, 7256],
[0, 0, 15252992]]
- sage: _dbz_to_integer_list('PolMod/Cls/pol.001.dbz') # optional - database_kohel
+ sage: _dbz_to_integer_list('PolMod/Cls/pol.001.dbz')
[[1, 0, 1]]
- sage: _dbz_to_integer_list('PolMod/Eta/pol.002.dbz') # optional - database_kohel
+ sage: _dbz_to_integer_list('PolMod/Eta/pol.002.dbz')
[[3, 0, 1], [2, 0, 48], [1, 1, -1], [1, 0, 768], [0, 0, 4096]]
"""
from sage.rings.integer import Integer
@@ -130,14 +152,14 @@ def __getitem__(self, level):
EXAMPLES::
+ sage: # optional - database_kohel
sage: DBMP = ClassicalModularPolynomialDatabase()
- sage: f = DBMP[29] # optional - database_kohel
- sage: f.degree() # optional - database_kohel
+ sage: f = DBMP[29]
+ sage: f.degree()
58
- sage: f.coefficient([28,28]) # optional - database_kohel
+ sage: f.coefficient([28,28])
400152899204646997840260839128
-
- sage: DBMP[50] # optional - database_kohel
+ sage: DBMP[50]
Traceback (most recent call last):
...
ValueError: file not found in the Kohel database
diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py
index 5b8455a49a2..e1e33e2839c 100644
--- a/src/sage/databases/findstat.py
+++ b/src/sage/databases/findstat.py
@@ -1,38 +1,28 @@
# -*- coding: utf-8 -*-
r"""
-FindStat - the Combinatorial Statistic Finder.
+FindStat - the search engine for combinatorial statistics and maps
-The FindStat database can be found at::
+The interface to the FindStat database is ::
sage: findstat()
The Combinatorial Statistic Finder (https://www.findstat.org/)
-Fix the following three notions:
+We use the following three notions
- A *combinatorial collection* is a set `S` with interesting combinatorial properties,
- a *combinatorial map* is a combinatorially interesting map `f: S \to S'` between combinatorial collections, and
- a *combinatorial statistic* is a combinatorially interesting map `s: S \to \ZZ`.
-You can use the sage interface to FindStat to:
+You can use the FindStat interface to
- identify a combinatorial statistic or map given the values on a few small objects,
- obtain more terms, formulae, references, etc. for a given statistic or map,
- edit statistics and maps and submit new statistics.
-AUTHORS:
-
-- Martin Rubey (2015): initial version.
-- Martin Rubey (2020): rewrite, adapt to new FindStat API
-
-The main entry points
----------------------
-.. csv-table::
- :class: contentstable
- :widths: 20, 40
- :delim: |
+The main entry points to the database are
- :func:`findstat` | search for matching statistics.
- :func:`findmap` | search for matching maps.
+- :func:`findstat` to search for matching statistics,
+- :func:`findmap` to search for matching maps.
A guided tour
-------------
@@ -195,8 +185,10 @@ def mapping(sigma):
:meth:`FindStatStatistic.submit` your changes for review by the
FindStat team.
-Classes and methods
--------------------
+AUTHORS:
+
+- Martin Rubey (2015): initial version
+- Martin Rubey (2020): rewrite, adapt to new FindStat API
"""
# ****************************************************************************
@@ -910,7 +902,7 @@ def findstat(query=None, values=None, distribution=None, domain=None,
must be ``None``.
- a list of pairs of the form ``(object, value)``, or a
- dictionary from sage objects to integer values. The keyword
+ dictionary from Sage objects to integer values. The keyword
arguments ``depth`` and ``max_values`` are passed to the
finder, ``values`` and ``distribution`` must be ``None``.
@@ -1163,7 +1155,7 @@ def findmap(*args, **kwargs):
forms:
- a list of pairs of the form ``(object, value)``, or a
- dictionary from sage objects to sage objects.
+ dictionary from Sage objects to Sage objects.
- a list of pairs of the form ``(list of objects, list of
values)``, or a single pair of the form ``(list of objects,
@@ -1763,7 +1755,7 @@ def set_references_raw(self, value):
def sage_code(self):
r"""
- Return the sage code associated with the statistic or map.
+ Return the Sage code associated with the statistic or map.
OUTPUT:
@@ -1842,7 +1834,7 @@ def first_terms(self):
OUTPUT:
- A dictionary from sage objects representing an element of the
+ A dictionary from Sage objects representing an element of the
appropriate collection to integers.
This method is overridden in :class:`FindStatStatisticQuery`.
@@ -2261,7 +2253,7 @@ def set_first_terms(self, values):
INPUT:
- a list of pairs of the form ``(object, value)`` where
- ``object`` is a sage object representing an element of the
+ ``object`` is a Sage object representing an element of the
appropriate collection and ``value`` is an integer.
This information is used when submitting the statistic with
@@ -2298,7 +2290,7 @@ def code(self):
OUTPUT:
- A string. Contributors are encouraged to submit sage code in the form::
+ A string. Contributors are encouraged to submit Sage code in the form::
def statistic(x):
...
@@ -4061,9 +4053,9 @@ class FindStatCollection(Element,
- an integer designating the FindStat id of the collection, or
- - a sage object belonging to a collection, or
+ - a Sage object belonging to a collection, or
- - an iterable producing a sage object belonging to a collection.
+ - an iterable producing a Sage object belonging to a collection.
EXAMPLES::
@@ -4277,7 +4269,7 @@ def in_range(self, element):
INPUT:
- - ``element`` -- a sage object that belongs to the collection.
+ - ``element`` -- a Sage object that belongs to the collection.
OUTPUT:
@@ -4452,7 +4444,7 @@ def from_string(self):
OUTPUT:
- The function that produces the sage object given its FindStat
+ The function that produces the Sage object given its FindStat
representation as a string.
EXAMPLES::
diff --git a/src/sage/databases/jones.py b/src/sage/databases/jones.py
index 26d4dbaa373..aaab1397f0a 100644
--- a/src/sage/databases/jones.py
+++ b/src/sage/databases/jones.py
@@ -1,9 +1,10 @@
r"""
John Jones's tables of number fields
-In order to use the Jones database, the optional database package
-must be installed using the Sage command !sage -i
-database_jones_numfield
+In order to use the Jones database, the optional :ref:`database_jones_numfield
+` package must be installed using the Sage command ::
+
+ sage -i database_jones_numfield
This is a table of number fields with bounded ramification and
degree `\leq 6`. You can query the database for all number
diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py
index df0b8310456..72e39796965 100644
--- a/src/sage/databases/knotinfo_db.py
+++ b/src/sage/databases/knotinfo_db.py
@@ -1,15 +1,29 @@
# -*- coding: utf-8 -*-
r"""
-KnotInfo Database
+KnotInfo database
-This module contains the class :class:`KnotInfoDataBase` and auxiliary classes
-for it which serves as an interface to the lists of named knots and links provided
+This module contains the class :class:`KnotInfoDataBase` and auxiliary classes
+for it, which serves as an interface to the lists of named knots and links provided
at the web-pages `KnotInfo `__ and
`LinkInfo `__.
+To use the database, you need to install the optional :ref:`database_knotinfo
+` package by the Sage command ::
+
+ sage -i database_knotinfo
+
+EXAMPLES::
+
+ sage: # optional - database_knotinfo
+ sage: from sage.databases.knotinfo_db import KnotInfoDataBase
+ sage: ki_db = KnotInfoDataBase()
+ sage: ki_db
+
+
AUTHORS:
-- Sebastian Oehms August 2020: initial version
+- Sebastian Oehms (2020-08): initial version
+
"""
##############################################################################
# Copyright (C) 2020 Sebastian Oehms
diff --git a/src/sage/databases/odlyzko.py b/src/sage/databases/odlyzko.py
index 547baad0625..4ee78b7de0a 100644
--- a/src/sage/databases/odlyzko.py
+++ b/src/sage/databases/odlyzko.py
@@ -1,12 +1,19 @@
"""
-Tables of zeros of the Riemann-Zeta function
+Database of the zeros of the Riemann zeta function
+
+The main access function to the database of the zeros of the Riemann zeta
+function is :func:`zeta_zeros`. In order to use ``zeta_zeros()``, you need to
+install the optional :ref:`database_odlyzko_zeta `
+package::
+
+ sage -i database_odlyzko_zeta
AUTHORS:
- William Stein: initial version
+- Jeroen Demeyer (2015-01-20): converted ``database_odlyzko_zeta`` to new-style
+ package
-- Jeroen Demeyer (2015-01-20): convert ``database_odlyzko_zeta`` to
- new-style package
"""
#*****************************************************************************
@@ -30,27 +37,20 @@ def zeta_zeros():
List of the imaginary parts of the first 2,001,052 zeros of the
Riemann zeta function, accurate to within 4e-9.
- In order to use ``zeta_zeros()``, you will need to
- install the optional Odlyzko database package::
-
- sage -i database_odlyzko_zeta
-
- You can see a list of all available optional packages with
- ``sage --optional``.
-
REFERENCES:
- http://www.dtc.umn.edu/~odlyzko/zeta_tables/index.html
EXAMPLES:
- The following example prints the imaginary part of the 13th
+ The following example shows the imaginary part of the 13th
nontrivial zero of the Riemann zeta function::
- sage: zz = zeta_zeros() # optional - database_odlyzko_zeta
- sage: zz[12] # optional - database_odlyzko_zeta
+ sage: # optional - database_odlyzko_zeta
+ sage: zz = zeta_zeros()
+ sage: zz[12]
59.347044003
- sage: len(zz) # optional - database_odlyzko_zeta
+ sage: len(zz)
2001052
"""
from sage.misc.verbose import verbose
diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py
index b6eeac3499c..50ad342a2b2 100644
--- a/src/sage/databases/oeis.py
+++ b/src/sage/databases/oeis.py
@@ -8,17 +8,6 @@
- identify a sequence from its first terms.
- obtain more terms, formulae, references, etc. for a given sequence.
-AUTHORS:
-
-- Thierry Monteil (2012-02-10 -- 2013-06-21): initial version.
-
-- Vincent Delecroix (2014): modifies continued fractions because of :trac:`14567`
-
-- Moritz Firsching (2016): modifies handling of dead sequence, see :trac:`17330`
-
-- Thierry Monteil (2019): refactorization (unique representation :trac:`28480`,
- laziness :trac:`28627`)
-
EXAMPLES::
sage: oeis
@@ -134,13 +123,14 @@
- Some infinite OEIS sequences are implemented in Sage, via the
:mod:`sloane_functions ` module.
-.. TODO::
+AUTHORS:
- - in case of flood, suggest the user to install the off-line database instead.
- - interface with the off-line database (or reimplement it).
+- Thierry Monteil (2012-02-10 -- 2013-06-21): initial version.
+- Vincent Delecroix (2014): modifies continued fractions because of :trac:`14567`
+- Moritz Firsching (2016): modifies handling of dead sequence, see :trac:`17330`
+- Thierry Monteil (2019): refactorization (unique representation :trac:`28480`,
+ laziness :trac:`28627`)
-Classes and methods
--------------------
"""
# ****************************************************************************
diff --git a/src/sage/databases/sloane.py b/src/sage/databases/sloane.py
index d5802620066..61825257470 100644
--- a/src/sage/databases/sloane.py
+++ b/src/sage/databases/sloane.py
@@ -1,8 +1,8 @@
"""
-Local copy of Sloane On-Line Encyclopedia of Integer Sequences
+Local copy of the On-Line Encyclopedia of Integer Sequences
-The SloaneEncyclopedia object provides access to a local copy of the database
-containing only the sequences and their names. To use this you must download
+The ``SloaneEncyclopedia`` object provides access to a local copy of the database
+containing only the sequences and their names. To use this, you must download
and install the database using ``SloaneEncyclopedia.install()``, or
``SloaneEncyclopedia.install_from_gz()`` if you have already downloaded the
database manually.
@@ -11,14 +11,14 @@
::
- sage: SloaneEncyclopedia[60843] # optional - sloane_database
+ sage: SloaneEncyclopedia[60843] # optional - sloane_database
[1, 6, 21, 107]
To get the name of a sequence, type
::
- sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
+ sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
'Number of groups of order n.'
To search locally for a particular subsequence, type
@@ -33,7 +33,7 @@
::
- sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database
+ sage: SloaneEncyclopedia.find([1,2,3,4,5], 100) # optional - sloane_database
[(15, [1, 2, 3, 4, 5, 7, 7, 8, 9, 11, 11, ...
Results in either case are of the form [ (number, list) ].
@@ -65,11 +65,9 @@
sequence_name() to return the description of a sequence; and changed
the data type for elements of each sequence from int to Integer.
-- Thierry Monteil (2012-02-10): deprecate dead code and update related doc and
+- Thierry Monteil (2012-02-10): deprecated dead code and update related doc and
tests.
-Classes and methods
--------------------
"""
# ****************************************************************************
@@ -265,7 +263,8 @@ def load(self):
try:
file_seq = bz2.BZ2File(self.__file__, 'r')
except IOError:
- raise IOError("The Sloane Encyclopedia database must be installed. Use e.g. 'SloaneEncyclopedia.install()' to download and install it.")
+ raise IOError("The Sloane Encyclopedia database must be installed."
+ " Use e.g. 'SloaneEncyclopedia.install()' to download and install it.")
self.__data__ = {}
@@ -295,7 +294,8 @@ def load(self):
self.__loaded_names__ = True
except KeyError:
# Some sequence in the names file is not in the database
- raise KeyError("Sloane OEIS sequence and name files do not match. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
+ raise KeyError("Sloane OEIS sequence and name files do not match."
+ " Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
except IOError:
# The names database is not installed
self.__loaded_names__ = False
@@ -323,7 +323,8 @@ def sequence_name(self, N):
"""
self.load()
if not self.__loaded_names__:
- raise IOError("The Sloane OEIS names file is not installed. Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
+ raise IOError("The Sloane OEIS names file is not installed."
+ " Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True).")
if N not in self.__data__: # sequence N does not exist
return ''
diff --git a/src/sage/databases/stein_watkins.py b/src/sage/databases/stein_watkins.py
index 6abf39426dd..fa5f338391a 100644
--- a/src/sage/databases/stein_watkins.py
+++ b/src/sage/databases/stein_watkins.py
@@ -1,18 +1,16 @@
r"""
The Stein-Watkins table of elliptic curves
-Sage gives access to the Stein-Watkins table of elliptic curves, via an
-optional package that you must install. This is a huge database of elliptic
-curves. You can install the database (a 2.6GB package) with the command
-
-::
+Sage gives access to the Stein-Watkins table of elliptic curves, via the
+optional :ref:`database_stein_watkins ` package
+that you must install. This is a huge database of elliptic curves. You can
+install the database (a 2.6GB package) with the command ::
sage -i database_stein_watkins
You can also automatically download a small version, which takes much less
-time, using the command
-
-::
+time, via the optional :ref:`database_stein_watkins_mini `
+package using the command ::
sage -i database_stein_watkins_mini
diff --git a/src/sage/databases/symbolic_data.py b/src/sage/databases/symbolic_data.py
index 7703a2216e5..083b3ced668 100644
--- a/src/sage/databases/symbolic_data.py
+++ b/src/sage/databases/symbolic_data.py
@@ -1,16 +1,15 @@
"""
Ideals from the Symbolic Data project
-This file implements a thin wrapper for the optional symbolic data set
-of ideals as published on http://www.symbolicdata.org . From the
-project website:
+This module implements a thin wrapper for the optional symbolic dataset of
+ideals as published on http://www.symbolicdata.org. From the project website:
- For different purposes algorithms and implementations are tested
+ For different purposes, algorithms and implementations are tested
on certified and reliable data. The development of tools and data
for such tests is usually 'orthogonal' to the main
implementation efforts, it requires different skills and
technologies and is not loved by programmers. On the other hand,
- in many cases tools and data could easily be reused - with slight
+ in many cases, tools and data could easily be reused - with slight
modifications - across similar projects. The SymbolicData Project
is set out to coordinate such efforts within the Computer Algebra
Community. Commonly collected certified and reliable data can
@@ -21,32 +20,36 @@
there are not yet well agreed aims of such a
benchmarking. Nevertheless various (often high quality) special
benchmarks are scattered through the literature. During the last
- years efforts toward collection of test data for symbolic
+ years, efforts toward collection of test data for symbolic
computations were intensified. They focused mainly on the creation
of general benchmarks for different areas of symbolic computation
and the collection of such activities on different Web site. For
- further qualification of these efforts it would be of great
+ further qualification of these efforts, it would be of great
benefit to create a commonly available digital archive of these
special benchmark data scattered through the literature. This
would provide the community with an electronic repository of
certified data that could be addressed and extended during further
development.
+In order to use this dataset, you need to install the optional
+:ref:`database_symbolic_data ` package by the Sage
+command ::
+
+ sage -i database_symbolic_data
+
EXAMPLES::
- sage: sd = SymbolicData(); sd # optional - database_symbolic_data
+ sage: # optional - database_symbolic_data
+ sage: sd = SymbolicData(); sd
SymbolicData with 372 ideals
-
- sage: sd.ZeroDim__example_1 # optional - database_symbolic_data
+ sage: sd.ZeroDim__example_1
Ideal (x1^2 + x2^2 - 10, x1^2 + x1*x2 + 2*x2^2 - 16) of Multivariate Polynomial Ring in x1, x2 over Rational Field
-
- sage: sd.Katsura_3 # optional - database_symbolic_data
+ sage: sd.Katsura_3
Ideal (u0 + 2*u1 + 2*u2 + 2*u3 - 1,
u1^2 + 2*u0*u2 + 2*u1*u3 - u2,
2*u0*u1 + 2*u1*u2 + 2*u2*u3 - u1,
u0^2 + 2*u1^2 + 2*u2^2 + 2*u3^2 - u0) of Multivariate Polynomial Ring in u0, u1, u2, u3 over Rational Field
-
- sage: sd.get_ideal('Katsura_3',GF(127),'degrevlex') # optional - database_symbolic_data
+ sage: sd.get_ideal('Katsura_3', GF(127), 'degrevlex')
Ideal (u0 + 2*u1 + 2*u2 + 2*u3 - 1,
u1^2 + 2*u0*u2 + 2*u1*u3 - u2,
2*u0*u1 + 2*u1*u2 + 2*u2*u3 - u1,
@@ -54,8 +57,20 @@
AUTHORS:
-- Martin Albrecht
+- Martin Albrecht (2007-02-19): initial version
+
"""
+
+# ****************************************************************************
+# Copyright (C) 2007 Martin Albrecht
+#
+# 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.
+# https://www.gnu.org/licenses/
+# ****************************************************************************
+
import os
from xml.dom.minidom import parse
from sage.rings.rational_field import QQ
@@ -90,13 +105,11 @@ def get_ideal(self, name, base_ring=QQ, term_order="degrevlex"):
INPUT:
- - ``name`` - name as on the symbolic data website
- - ``base_ring`` - base ring for the polynomial ring (default: ``QQ``)
- - ``term_order`` - term order for the polynomial ring (default: ``degrevlex``)
-
- OUTPUT:
+ - ``name`` -- name as on the symbolic data website
+ - ``base_ring`` -- base ring for the polynomial ring (default: ``QQ``)
+ - ``term_order`` -- term order for the polynomial ring (default: ``degrevlex``)
- ideal as given by ``name`` in ``PolynomialRing(base_ring,vars,term_order)``
+ OUTPUT: ideal as given by ``name`` in ``PolynomialRing(base_ring,vars,term_order)``
EXAMPLES::
diff --git a/src/sage/env.py b/src/sage/env.py
index a221f33bb82..39d09528788 100644
--- a/src/sage/env.py
+++ b/src/sage/env.py
@@ -177,7 +177,8 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
SAGE_LOCAL = var("SAGE_LOCAL", SAGE_VENV)
SAGE_SHARE = var("SAGE_SHARE", join(SAGE_LOCAL, "share"))
SAGE_DOC = var("SAGE_DOC", join(SAGE_SHARE, "doc", "sage"))
-SAGE_SPKG_INST = var("SAGE_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed"))
+SAGE_LOCAL_SPKG_INST = var("SAGE_LOCAL_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed"))
+SAGE_SPKG_INST = var("SAGE_SPKG_INST", join(SAGE_LOCAL, "var", "lib", "sage", "installed")) # deprecated
# source tree of the Sage distribution
SAGE_ROOT = var("SAGE_ROOT") # no fallback for SAGE_ROOT
@@ -198,8 +199,7 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
GRAPHS_DATA_DIR = var("GRAPHS_DATA_DIR", join(SAGE_SHARE, "graphs"))
ELLCURVE_DATA_DIR = var("ELLCURVE_DATA_DIR", join(SAGE_SHARE, "ellcurves"))
POLYTOPE_DATA_DIR = var("POLYTOPE_DATA_DIR", join(SAGE_SHARE, "reflexive_polytopes"))
-GAP_LIB_DIR = var("GAP_LIB_DIR", join(SAGE_LOCAL, "lib", "gap"))
-GAP_SHARE_DIR = var("GAP_SHARE_DIR", join(SAGE_SHARE, "gap"))
+
COMBINATORIAL_DESIGN_DATA_DIR = var("COMBINATORIAL_DESIGN_DATA_DIR", join(SAGE_SHARE, "combinatorial_designs"))
CREMONA_MINI_DATA_DIR = var("CREMONA_MINI_DATA_DIR", join(SAGE_SHARE, "cremona"))
CREMONA_LARGE_DATA_DIR = var("CREMONA_LARGE_DATA_DIR", join(SAGE_SHARE, "cremona"))
@@ -243,11 +243,13 @@ def var(key: str, *fallbacks: Optional[str], force: bool = False) -> Optional[st
# GAP memory and args
SAGE_GAP_MEMORY = var('SAGE_GAP_MEMORY', None)
-_gap_cmd = "gap -r"
-if SAGE_GAP_MEMORY is not None:
- _gap_cmd += " -s " + SAGE_GAP_MEMORY + " -o " + SAGE_GAP_MEMORY
-SAGE_GAP_COMMAND = var('SAGE_GAP_COMMAND', _gap_cmd)
+SAGE_GAP_COMMAND = var('SAGE_GAP_COMMAND', None)
+# The semicolon-separated search path for GAP packages. It is passed
+# directly to GAP via the -l flag.
+GAP_ROOT_PATHS = var("GAP_ROOT_PATHS",
+ ";".join([join(SAGE_LOCAL, "lib", "gap"),
+ join(SAGE_LOCAL, "share", "gap")]))
# post process
if DOT_SAGE is not None and ' ' in DOT_SAGE:
diff --git a/src/sage/ext_data/gap/sage.g b/src/sage/ext_data/gap/sage.g
index 36e131146d6..ddca6e3fb44 100644
--- a/src/sage/ext_data/gap/sage.g
+++ b/src/sage/ext_data/gap/sage.g
@@ -134,3 +134,17 @@ end;
#
# LogTo("/tmp/gapsage.log");
#
+
+
+# Load the GAP packages that GAP itself tries to autoload in the
+# default configuration (see "PackagesToLoad" in lib/package.gi). The
+# combination of passing -A to gap and these LoadPackage statements
+# allows us to load the usual set of packages, but only if they are
+# installed. So most people will get exactly the default behavior,
+# but minimal installations won't throw warnings and fail tests.
+_autoloads := [ "autpgrp", "alnuth", "crisp", "ctbllib", "factint", "fga",
+ "irredsol", "laguna", "polenta", "polycyclic", "resclasses",
+ "sophus", "tomlib" ];
+for p in _autoloads do
+ LoadPackage(p);
+od;
diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py
index be55d44d0c2..d5669c3c9ff 100644
--- a/src/sage/features/__init__.py
+++ b/src/sage/features/__init__.py
@@ -236,7 +236,7 @@ def require(self):
Traceback (most recent call last):
...
FeatureNotPresentError: gap_package_ve1EeThu is not available.
- `TestPackageAvailability("ve1EeThu")` evaluated to `fail` in GAP.
+ `LoadPackage("ve1EeThu")` evaluated to `fail` in GAP.
"""
presence = self.is_present()
if not presence:
@@ -393,22 +393,25 @@ def unhide(self):
EXAMPLES:
- Polycyclic is a standard GAP package since 4.10 (see :trac:`26856`). The
- following test just fails if it is hidden. Thus, in the second
- invocation no optional tag is needed::
+ PolyCyclic is an optional GAP package. The following test
+ fails if it is hidden, regardless of whether it is installed
+ or not::
sage: from sage.features.gap import GapPackage
sage: Polycyclic = GapPackage("polycyclic", spkg="gap_packages")
sage: Polycyclic.hide()
- sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap
+ sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap # optional - gap_packages_polycyclic
Traceback (most recent call last):
...
FeatureNotPresentError: gap_package_polycyclic is not available.
Feature `gap_package_polycyclic` is hidden.
Use method `unhide` to make it available again.
+ After unhiding the feature, the test should pass again if PolyCyclic
+ is installed and loaded::
+
sage: Polycyclic.unhide()
- sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap
+ sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap # optional - gap_packages_polycyclic
Pcp-group with orders [ 0, 3, 4 ]
"""
self._hidden = False
@@ -451,7 +454,7 @@ def __str__(self):
Traceback (most recent call last):
...
FeatureNotPresentError: gap_package_gapZuHoh8Uu is not available.
- `TestPackageAvailability("gapZuHoh8Uu")` evaluated to `fail` in GAP.
+ `LoadPackage("gapZuHoh8Uu")` evaluated to `fail` in GAP.
"""
lines = ["{feature} is not available.".format(feature=self.feature.name)]
if self.reason:
@@ -481,7 +484,7 @@ class FeatureTestResult():
``resolution``::
sage: presence.reason # needs sage.libs.gap
- '`TestPackageAvailability("NOT_A_PACKAGE")` evaluated to `fail` in GAP.'
+ '`LoadPackage("NOT_A_PACKAGE")` evaluated to `fail` in GAP.'
sage: bool(presence.resolution)
False
diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py
index a989dcfc1fb..df5545e9c07 100644
--- a/src/sage/features/gap.py
+++ b/src/sage/features/gap.py
@@ -19,6 +19,9 @@ class GapPackage(Feature):
r"""
A :class:`~sage.features.Feature` describing the presence of a GAP package.
+ A GAP package is "present" if it *can be* loaded, not if it *has
+ been* loaded.
+
.. SEEALSO::
:class:`Feature sage.libs.gap <~sage.features.sagemath.sage__libs__gap>`
@@ -42,9 +45,10 @@ def __init__(self, package, **kwds):
def _is_present(self):
r"""
- Return whether the package is available in GAP.
+ Return whether or not the GAP package is present.
- This does not check whether this package is functional.
+ If the package is installed but not yet loaded, it is loaded
+ first. This does *not* check that the package is functional.
EXAMPLES::
@@ -57,8 +61,11 @@ def _is_present(self):
except ImportError:
return FeatureTestResult(self, False,
reason="sage.libs.gap is not available")
- command = 'TestPackageAvailability("{package}")'.format(package=self.package)
+
+ # This returns "true" even if the package is already loaded.
+ command = 'LoadPackage("{package}")'.format(package=self.package)
presence = libgap.eval(command)
+
if presence:
return FeatureTestResult(self, True,
reason="`{command}` evaluated to `{presence}` in GAP.".format(command=command, presence=presence))
diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py
index 4097d3512b9..dd48c55ee6b 100644
--- a/src/sage/features/sagemath.py
+++ b/src/sage/features/sagemath.py
@@ -311,6 +311,29 @@ def __init__(self):
spkg='sagemath_groups', type='standard')
+class sage__libs__braiding(PythonModule):
+ r"""
+ A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.braiding`.
+
+ EXAMPLES::
+
+ sage: from sage.features.sagemath import sage__libs__braiding
+ sage: sage__libs__braiding().is_present() # needs sage.libs.braiding
+ FeatureTestResult('sage.libs.braiding', True)
+ """
+
+ def __init__(self):
+ r"""
+ TESTS::
+
+ sage: from sage.features.sagemath import sage__libs__braiding
+ sage: isinstance(sage__libs__braiding(), sage__libs__braiding)
+ True
+ """
+ PythonModule.__init__(self, 'sage.libs.braiding',
+ spkg='sagemath_libbraiding', type='standard')
+
+
class sage__libs__ecl(PythonModule):
r"""
A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.ecl`.
@@ -330,7 +353,8 @@ def __init__(self):
sage: isinstance(sage__libs__ecl(), sage__libs__ecl)
True
"""
- PythonModule.__init__(self, 'sage.libs.ecl')
+ PythonModule.__init__(self, 'sage.libs.ecl',
+ spkg='sagemath_symbolics', type='standard')
class sage__libs__flint(JoinFeature):
@@ -1076,6 +1100,7 @@ def all_features():
sage__geometry__polyhedron(),
sage__graphs(),
sage__groups(),
+ sage__libs__braiding(),
sage__libs__ecl(),
sage__libs__flint(),
sage__libs__gap(),
diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx
index 7ca6c5dd124..70ce392a43c 100644
--- a/src/sage/graphs/base/c_graph.pyx
+++ b/src/sage/graphs/base/c_graph.pyx
@@ -624,8 +624,8 @@ cdef class CGraph:
OUTPUT:
- - Raise a ``NotImplementedError``. This method is not implemented in
- this base class. A child class should provide a suitable
+ - Raise a :class:`NotImplementedError`. This method is not implemented
+ in this base class. A child class should provide a suitable
implementation.
.. SEEALSO::
@@ -1267,7 +1267,7 @@ cdef class CGraph:
OUTPUT:
- - Raise ``NotImplementedError``. This method is not implemented at
+ - Raise :class:`NotImplementedError`. This method is not implemented at
the :class:`CGraph` level. A child class should provide a suitable
implementation.
diff --git a/src/sage/graphs/base/graph_backends.pyx b/src/sage/graphs/base/graph_backends.pyx
index 863f61be013..77961c33cf6 100644
--- a/src/sage/graphs/base/graph_backends.pyx
+++ b/src/sage/graphs/base/graph_backends.pyx
@@ -6,7 +6,7 @@ This module implements :class:`GenericGraphBackend` (the base class for
backends).
Any graph backend must redefine the following methods (for which
-:class:`GenericGraphBackend` raises a ``NotImplementedError``)
+:class:`GenericGraphBackend` raises a :class:`NotImplementedError`)
.. csv-table::
:class: contentstable
diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py
index 0a03affa422..6bb65e6ad33 100644
--- a/src/sage/graphs/bipartite_graph.py
+++ b/src/sage/graphs/bipartite_graph.py
@@ -1,5 +1,4 @@
# autopep8: off
-# -*- coding: utf-8 -*-
r"""
Bipartite graphs
diff --git a/src/sage/graphs/bliss.pyx b/src/sage/graphs/bliss.pyx
index eac56e21f0d..c623b5fe402 100644
--- a/src/sage/graphs/bliss.pyx
+++ b/src/sage/graphs/bliss.pyx
@@ -396,9 +396,10 @@ cpdef canonical_form(G, partition=None, return_graph=False, use_edge_labels=True
canonical graph of ``G`` or its set of edges
- ``use_edge_labels`` -- boolean (default: ``True``); whether to consider
- edge labels. The edge labels are assumed to be hashable and sortable. If
- this is not the case (ie a ``TypeError`` is raised), the algorithm will
- consider the string representations of the labels instead of the labels.
+ edge labels. The edge labels are assumed to be hashable and
+ sortable. If this is not the case (ie a :class:`TypeError` is
+ raised), the algorithm will consider the string representations
+ of the labels instead of the labels.
- ``certificate`` -- boolean (default: ``False``); when set to ``True``,
returns the labeling of G into a canonical graph
diff --git a/src/sage/graphs/digraph.py b/src/sage/graphs/digraph.py
index 8ba57d90b3c..a5d43d48085 100644
--- a/src/sage/graphs/digraph.py
+++ b/src/sage/graphs/digraph.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Directed graphs
@@ -3187,9 +3186,9 @@ def topological_sort(self, implementation="default"):
"""
Return a topological sort of the digraph if it is acyclic.
- If the digraph contains a directed cycle, a ``TypeError`` is raised. As
- topological sorts are not necessarily unique, different implementations
- may yield different results.
+ If the digraph contains a directed cycle, a :class:`TypeError`
+ is raised. As topological sorts are not necessarily unique,
+ different implementations may yield different results.
A topological sort is an ordering of the vertices of the digraph such
that each vertex comes before all of its successors. That is, if `u`
@@ -3269,7 +3268,8 @@ def topological_sort_generator(self):
Return an iterator over all topological sorts of the digraph if
it is acyclic.
- If the digraph contains a directed cycle, a ``TypeError`` is raised.
+ If the digraph contains a directed cycle, a :class:`TypeError`
+ is raised.
A topological sort is an ordering of the vertices of the digraph such
that each vertex comes before all of its successors. That is, if u comes
diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py
index 2b29e0e73ef..d182f49afb1 100644
--- a/src/sage/graphs/digraph_generators.py
+++ b/src/sage/graphs/digraph_generators.py
@@ -74,7 +74,7 @@
from sage.graphs.graph import Graph
-class DiGraphGenerators():
+class DiGraphGenerators:
r"""
A class consisting of constructors for several common digraphs,
including orderly generation of isomorphism class representatives.
diff --git a/src/sage/graphs/domination.py b/src/sage/graphs/domination.py
index 7f631fcfe28..6323cdd29e4 100644
--- a/src/sage/graphs/domination.py
+++ b/src/sage/graphs/domination.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Domination
diff --git a/src/sage/graphs/generators/basic.py b/src/sage/graphs/generators/basic.py
index 5dbacb4de26..0dc2c00baef 100644
--- a/src/sage/graphs/generators/basic.py
+++ b/src/sage/graphs/generators/basic.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Basic graphs
@@ -400,7 +399,7 @@ def CompleteGraph(n):
G.set_pos({0: (0, 0)})
else:
G._circle_embedding(list(range(n)), angle=pi/2)
- G.add_edges(((i, j) for i in range(n) for j in range(i + 1, n)))
+ G.add_edges((i, j) for i in range(n) for j in range(i + 1, n))
return G
def CorrelationGraph(seqs, alpha, include_anticorrelation):
diff --git a/src/sage/graphs/generators/chessboard.py b/src/sage/graphs/generators/chessboard.py
index dfe5b15b273..a76d6f98de7 100644
--- a/src/sage/graphs/generators/chessboard.py
+++ b/src/sage/graphs/generators/chessboard.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Chessboard graphs
diff --git a/src/sage/graphs/generators/degree_sequence.py b/src/sage/graphs/generators/degree_sequence.py
index f5e68d89c79..05c021403b0 100644
--- a/src/sage/graphs/generators/degree_sequence.py
+++ b/src/sage/graphs/generators/degree_sequence.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Graphs with a given degree sequence
@@ -98,8 +97,8 @@ def DegreeSequenceBipartite(s1, s2):
True
Some sequences being incompatible if, for example, their sums are different,
- the functions raises a ``ValueError`` when no graph corresponding to the
- degree sequences exists::
+ the functions raises a :class:`ValueError` when no graph corresponding
+ to the degree sequences exists::
sage: g = graphs.DegreeSequenceBipartite([2,2,2,2,1],[5,5]) # needs sage.combinat sage.modules
Traceback (most recent call last):
diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py
index 85ea52a4571..c962378cd25 100644
--- a/src/sage/graphs/generators/families.py
+++ b/src/sage/graphs/generators/families.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Various families of graphs
@@ -594,7 +593,7 @@ def BarbellGraph(n1, n2):
OUTPUT:
- A barbell graph of order ``2*n1 + n2``. A ``ValueError`` is
+ A barbell graph of order ``2*n1 + n2``. A :class:`ValueError` is
returned if ``n1 < 2`` or ``n2 < 0``.
PLOTTING:
@@ -929,7 +928,7 @@ def BubbleSortGraph(n):
OUTPUT:
The bubble sort graph `B(n)` on `n` symbols. If `n < 1`, a
- ``ValueError`` is returned.
+ :class:`ValueError` is returned.
EXAMPLES::
@@ -2714,7 +2713,7 @@ def SwitchedSquaredSkewHadamardMatrixGraph(n):
G = SquaredSkewHadamardMatrixGraph(n).complement()
G.add_vertex((4 * n - 1)**2)
G.seidel_switching(list(range((4 * n - 1) * (2 * n - 1))))
- G.name("switch skewhad^2+*_" + str((n)))
+ G.name("switch skewhad^2+*_" + str(n))
return G
@@ -3920,7 +3919,13 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None):
sage: G.is_strongly_regular(parameters=True) # needs sage.modules sage.rings.finite_rings
(45, 22, 10, 11)
- Supplying ``G`` and ``L`` (constructed from the automorphism group of ``G``). ::
+ Supplying ``G`` and ``L`` (constructed from the automorphism group
+ of ``G``). The entries of L can't be tested directly because
+ there's some unpredictability in the way that GAP chooses a
+ representative in ``NormalSubgroups()``, the function that
+ underlies our own
+ :meth:`~sage.groups.perm_gps.permgroup.PermutationGroup_generic.normal_subgroups`
+ method::
sage: # needs sage.groups sage.libs.gap sage.rings.finite_rings
sage: G = graphs.PaleyGraph(9)
@@ -3931,18 +3936,7 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None):
....: for z in subg]
sage: ff = list(map(lambda y: (y[0]-1,y[1]-1),
....: Permutation(map(lambda x: 1+r.index(x^-1), r)).cycle_tuples()[1:]))
- sage: L = sum(i*(r[a]-r[b]) for i,(a,b) in zip(range(1,len(ff)+1), ff)); L
- [ 0 1 -1 -3 -2 -4 3 4 2]
- [-1 0 1 -4 -3 -2 2 3 4]
- [ 1 -1 0 -2 -4 -3 4 2 3]
- [ 3 4 2 0 1 -1 -3 -2 -4]
- [ 2 3 4 -1 0 1 -4 -3 -2]
- [ 4 2 3 1 -1 0 -2 -4 -3]
- [-3 -2 -4 3 4 2 0 1 -1]
- [-4 -3 -2 2 3 4 -1 0 1]
- [-2 -4 -3 4 2 3 1 -1 0]
-
- sage: # needs sage.groups sage.libs.gap sage.modules sage.rings.finite_rings
+ sage: L = sum(i*(r[a]-r[b]) for i,(a,b) in zip(range(1,len(ff)+1), ff))
sage: G.relabel(range(9))
sage: G3x3 = graphs.MathonPseudocyclicStronglyRegularGraph(2, G=G, L=L)
sage: G3x3.is_strongly_regular(parameters=True)
diff --git a/src/sage/graphs/generators/intersection.py b/src/sage/graphs/generators/intersection.py
index 0d19d30f9ea..0434806fb73 100644
--- a/src/sage/graphs/generators/intersection.py
+++ b/src/sage/graphs/generators/intersection.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Intersection graphs
diff --git a/src/sage/graphs/generators/platonic_solids.py b/src/sage/graphs/generators/platonic_solids.py
index d3bc60a9974..5a572611539 100644
--- a/src/sage/graphs/generators/platonic_solids.py
+++ b/src/sage/graphs/generators/platonic_solids.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
1-skeletons of Platonic solids
diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py
index 22c1f583f6a..e0868b36e21 100644
--- a/src/sage/graphs/generators/random.py
+++ b/src/sage/graphs/generators/random.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Random graphs
diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py
index 3b47f29f2ff..bde0cafc0a8 100644
--- a/src/sage/graphs/generators/smallgraphs.py
+++ b/src/sage/graphs/generators/smallgraphs.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Various small graphs
diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py
index f131aa446e2..76ec2b4f9ae 100644
--- a/src/sage/graphs/generators/world_map.py
+++ b/src/sage/graphs/generators/world_map.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Graphs from the World Map
diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py
index 0f0f995bfbd..ca365540ffb 100644
--- a/src/sage/graphs/generic_graph.py
+++ b/src/sage/graphs/generic_graph.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Generic graphs (common to directed/undirected)
@@ -2933,8 +2932,8 @@ def _check_embedding_validity(self, embedding=None, boolean=True):
``_embedding``
- ``boolean`` -- boolean (default: ``True``); -- whether to return a
- boolean answer or raise a ``ValueError`` exception if the embedding is
- invalid
+ boolean answer or raise a :class:`ValueError` exception
+ if the embedding is invalid
EXAMPLES::
@@ -3443,8 +3442,8 @@ def allow_multiple_edges(self, new, check=True, keep_label='any'):
.. WARNING::
``'min'`` and ``'max'`` only works if the labels can be compared. A
- ``TypeError`` might be raised when working with non-comparable
- objects in Python 3.
+ :class:`TypeError` might be raised when working with non-comparable
+ objects.
EXAMPLES:
@@ -4092,7 +4091,7 @@ def density(self):
if n < 2:
return Rational(0)
if self._directed:
- return Rational(self.size()) / Rational((n ** 2 - n))
+ return Rational(self.size()) / Rational(n ** 2 - n)
return Rational(self.size()) / Rational((n ** 2 - n) / 2)
def is_bipartite(self, certificate=False):
@@ -18894,8 +18893,8 @@ def to_simple(self, to_undirected=True, keep_label='any', immutable=None):
.. WARNING::
``'min'`` and ``'max'`` only works if the labels can be compared. A
- ``TypeError`` might be raised when working with non-comparable
- objects in Python 3.
+ :class:`TypeError` might be raised when working with non-comparable
+ objects.
- ``immutable`` -- boolean (default: ``Non``); whether to create a
mutable/immutable copy. ``immutable=None`` (default) means that the
@@ -25341,7 +25340,7 @@ def graph_isom_equivalent_non_edge_labeled_graph(g, partition=None, standard_lab
for el, part in edge_partition:
# The multiplicity of a label is the number of edges from u to v
# it represents
- m = sum((y[1] for y in el))
+ m = sum(y[1] for y in el)
if m in tmp:
tmp[m].append(part)
else:
diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py
index 8988765d767..adc3c39f43e 100644
--- a/src/sage/graphs/graph.py
+++ b/src/sage/graphs/graph.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Undirected graphs
@@ -3285,8 +3284,8 @@ def bounded_outdegree_orientation(self, bound, solver=None, verbose=False,
OUTPUT:
- A DiGraph representing the orientation if it exists. A ``ValueError``
- exception is raised otherwise.
+ A DiGraph representing the orientation if it exists.
+ A :class:`ValueError` exception is raised otherwise.
ALGORITHM:
diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py
index 7a0c71c2fb1..c994475bd6b 100644
--- a/src/sage/graphs/graph_decompositions/modular_decomposition.py
+++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Modular Decomposition
diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx
index 991f06efcce..803f5a09771 100644
--- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx
+++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx
@@ -1083,37 +1083,31 @@ def label_nice_tree_decomposition(nice_TD, root, directed=False):
EXAMPLES::
sage: from sage.graphs.graph_decompositions.tree_decomposition import make_nice_tree_decomposition, label_nice_tree_decomposition
- sage: bip_one_four = graphs.CompleteBipartiteGraph(1, 4)
- sage: bip_one_four_TD = bip_one_four.treewidth(certificate=True)
- sage: nice_TD = make_nice_tree_decomposition(bip_one_four, bip_one_four_TD)
+ sage: claw = graphs.CompleteBipartiteGraph(1, 3)
+ sage: claw_TD = claw.treewidth(certificate=True)
+ sage: nice_TD = make_nice_tree_decomposition(claw, claw_TD)
sage: root = sorted(nice_TD.vertices())[0]
sage: label_TD = label_nice_tree_decomposition(nice_TD, root, directed=True)
- sage: print(label_TD.name())
- Labelled Nice tree decomposition of Tree decomposition
- sage: for node in sorted(label_TD):
+ sage: label_TD.name()
+ 'Labelled Nice tree decomposition of Tree decomposition'
+ sage: for node in sorted(label_TD): # random
....: print(node, label_TD.get_vertex(node))
(0, {}) forget
(1, {0}) forget
(2, {0, 1}) intro
(3, {0}) forget
- (4, {0, 4}) join
- (5, {0, 4}) intro
- (6, {0, 4}) intro
- (7, {0}) forget
- (8, {0}) forget
- (9, {0, 3}) intro
- (10, {0, 2}) intro
- (11, {3}) intro
- (12, {2}) intro
- (13, {}) leaf
- (14, {}) leaf
+ (4, {0, 3}) intro
+ (5, {0}) forget
+ (6, {0, 2}) intro
+ (7, {2}) intro
+ (8, {}) leaf
"""
from sage.graphs.digraph import DiGraph
from sage.graphs.graph import Graph
directed_TD = DiGraph(nice_TD.breadth_first_search(start=root, edges=True),
format='list_of_edges',
- name='Labelled {}'.format(nice_TD))
+ name='Labelled {}'.format(nice_TD.name()))
# The loop starts from the root node
# We assume the tree decomposition is valid and nice,
diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py
index 0677028e451..3ba5dc6e91f 100644
--- a/src/sage/graphs/graph_generators.py
+++ b/src/sage/graphs/graph_generators.py
@@ -475,7 +475,7 @@ def wrap_name(x):
from . import graph
-class GraphGenerators():
+class GraphGenerators:
r"""
A class consisting of constructors for several common graphs, as well as
orderly generation of isomorphism class representatives. See the
diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py
index 193afc9c4eb..4e08af6a4fc 100644
--- a/src/sage/graphs/graph_input.py
+++ b/src/sage/graphs/graph_input.py
@@ -559,7 +559,7 @@ def from_dict_of_lists(G, D, loops=False, multiedges=False, weighted=False):
for u in D:
if len(set(D[u])) != len(D[u]):
if multiedges is False:
- v = next((v for v in D[u] if D[u].count(v) > 1))
+ v = next(v for v in D[u] if D[u].count(v) > 1)
raise ValueError("non-multigraph got several edges (%s, %s)" % (u, v))
multiedges = True
break
diff --git a/src/sage/graphs/graph_latex.py b/src/sage/graphs/graph_latex.py
index 1cd878bec5a..bff200a1a86 100644
--- a/src/sage/graphs/graph_latex.py
+++ b/src/sage/graphs/graph_latex.py
@@ -625,7 +625,7 @@ def set_option(self, option_name, option_value=None):
- ``option_name`` -- a string for a latex option contained in the list
``sage.graphs.graph_latex.GraphLatex.__graphlatex_options``.
- A ``ValueError`` is raised if the option is not allowed.
+ A :class:`ValueError` is raised if the option is not allowed.
- ``option_value`` -- a value for the option. If omitted, or set to
``None``, the option will use the default value.
diff --git a/src/sage/graphs/hypergraph_generators.py b/src/sage/graphs/hypergraph_generators.py
index 4291e024001..f5218e225f1 100644
--- a/src/sage/graphs/hypergraph_generators.py
+++ b/src/sage/graphs/hypergraph_generators.py
@@ -32,7 +32,7 @@
"""
-class HypergraphGenerators():
+class HypergraphGenerators:
r"""
A class consisting of constructors for common hypergraphs.
"""
diff --git a/src/sage/graphs/lovasz_theta.py b/src/sage/graphs/lovasz_theta.py
index 49bc7e32092..5335a597ebf 100644
--- a/src/sage/graphs/lovasz_theta.py
+++ b/src/sage/graphs/lovasz_theta.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
r"""
Lovász theta-function of graphs
diff --git a/src/sage/graphs/pq_trees.py b/src/sage/graphs/pq_trees.py
index 2ddd39691e8..68063bc8e73 100644
--- a/src/sage/graphs/pq_trees.py
+++ b/src/sage/graphs/pq_trees.py
@@ -558,7 +558,7 @@ def set_contiguous(self, v):
In any case, the sets containing ``v`` are contiguous when this
function ends. If there is no possibility of doing so, the function
- raises a ``ValueError`` exception.
+ raises a :class:`ValueError` exception.
EXAMPLES:
@@ -837,7 +837,7 @@ def set_contiguous(self, v):
In any case, the sets containing ``v`` are contiguous when this
function ends. If there is no possibility of doing so, the function
- raises a ``ValueError`` exception.
+ raises a :class:`ValueError` exception.
EXAMPLES:
diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py
index 40f6d923656..b52dcff829a 100644
--- a/src/sage/graphs/schnyder.py
+++ b/src/sage/graphs/schnyder.py
@@ -555,7 +555,7 @@ def _compute_coordinates(g, x):
g.set_pos(coordinates) # Setting _pos attribute to store coordinates
-class TreeNode():
+class TreeNode:
"""
A class to represent each node in the trees used by ``_realizer`` and
``_compute_coordinates`` when finding a planar geometric embedding in
diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py
index e82150eeb9c..4868577c892 100644
--- a/src/sage/graphs/tutte_polynomial.py
+++ b/src/sage/graphs/tutte_polynomial.py
@@ -218,7 +218,7 @@ def edge_multiplicities(G):
########
-class Ear():
+class Ear:
r"""
An ear is a sequence of vertices
@@ -372,7 +372,7 @@ def removed_from(self, G):
##################
-class EdgeSelection():
+class EdgeSelection:
pass
diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py
index f2e79c92353..66558a9ffb0 100644
--- a/src/sage/groups/abelian_gps/abelian_group.py
+++ b/src/sage/groups/abelian_gps/abelian_group.py
@@ -581,8 +581,8 @@ def is_subgroup(left, right):
sage: G.is_subgroup(G)
True
- sage: H = G.subgroup([G.1]) # needs sage.libs.gap
- sage: H.is_subgroup(G) # needs sage.libs.gap
+ sage: H = G.subgroup([G.1]) # needs sage.libs.gap # optional - gap_package_polycyclic
+ sage: H.is_subgroup(G) # needs sage.libs.gap # optional - gap_package_polycyclic
True
sage: G. = AbelianGroup(2)
@@ -1206,7 +1206,7 @@ def subgroup(self, gensH, names="f"):
EXAMPLES::
- sage: # needs sage.libs.gap
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup(3, [2,3,4]); G
Multiplicative Abelian group isomorphic to C2 x C3 x C4
sage: H = G.subgroup([a*b,a]); H
@@ -1361,7 +1361,7 @@ def number_of_subgroups(self, order=None):
0
sage: AbelianGroup([1,3,1]).number_of_subgroups(order=2)
0
- sage: AbelianGroup([1,3,0,1]).number_of_subgroups(order=3) # needs sage.libs.gap
+ sage: AbelianGroup([1,3,0,1]).number_of_subgroups(order=3) # needs sage.libs.gap # optional - gap_package_polycyclic
1
sage: AbelianGroup([1,3,1]).number_of_subgroups(order=-2)
Traceback (most recent call last):
@@ -1451,12 +1451,12 @@ def subgroups(self, check=False):
EXAMPLES::
- sage: AbelianGroup([2,3]).subgroups() # needs sage.libs.gap
+ sage: AbelianGroup([2,3]).subgroups() # needs sage.libs.gap # optional - gap_package_polycyclic
[Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0*f1^2},
Multiplicative Abelian subgroup isomorphic to C2 generated by {f0},
Multiplicative Abelian subgroup isomorphic to C3 generated by {f1},
Trivial Abelian subgroup]
- sage: len(AbelianGroup([2,4,8]).subgroups()) # needs sage.libs.gap
+ sage: len(AbelianGroup([2,4,8]).subgroups()) # needs sage.libs.gap # optional - gap_package_polycyclic
81
TESTS::
@@ -1467,10 +1467,10 @@ def subgroups(self, check=False):
Check that :trac:`14196` is fixed::
sage: B = AbelianGroup([1,2])
- sage: B.subgroups() # needs sage.libs.gap
+ sage: B.subgroups() # needs sage.libs.gap # optional - gap_package_polycyclic
[Multiplicative Abelian subgroup isomorphic to C2 generated by {f1},
Trivial Abelian subgroup]
- sage: B.subgroups(check=True) # needs sage.libs.gap
+ sage: B.subgroups(check=True) # needs sage.libs.gap # optional - gap_package_polycyclic
[Multiplicative Abelian subgroup isomorphic to C2 generated by {f1},
Trivial Abelian subgroup]
"""
@@ -1534,10 +1534,10 @@ def subgroup_reduced(self, elts, verbose=False):
EXAMPLES::
sage: G = AbelianGroup([4,4])
- sage: G.subgroup( [ G([1,0]), G([1,2]) ]) # needs sage.libs.gap
+ sage: G.subgroup( [ G([1,0]), G([1,2]) ]) # needs sage.libs.gap # optional - gap_package_polycyclic
Multiplicative Abelian subgroup isomorphic to C2 x C4
generated by {f0, f0*f1^2}
- sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ]) # needs sage.libs.gap
+ sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ]) # needs sage.libs.gap # optional - gap_package_polycyclic
Multiplicative Abelian subgroup isomorphic to C2 x C4
generated by {f0^2*f1^2, f0^3}
"""
@@ -1568,7 +1568,7 @@ def torsion_subgroup(self, n=None):
EXAMPLES::
- sage: # needs sage.libs.gap
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup([2, 3])
sage: G.torsion_subgroup()
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated
@@ -1587,7 +1587,7 @@ def torsion_subgroup(self, n=None):
::
sage: G = AbelianGroup([2, 2*3, 2*3*5, 0, 2*3*5*7, 2*3*5*7*11])
- sage: G.torsion_subgroup(5) # needs sage.libs.gap
+ sage: G.torsion_subgroup(5) # needs sage.libs.gap # optional - gap_package_polycyclic
Multiplicative Abelian subgroup isomorphic to C5 x C5 x C5 generated by {f2^6, f4^42, f5^462}
"""
if n is None:
@@ -1620,7 +1620,7 @@ def __init__(self, ambient, gens, names="f", category=None):
"""
EXAMPLES::
- sage: # needs sage.libs.gap
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: F = AbelianGroup(5, [30,64,729], names=list("abcde"))
sage: a,b,c,d,e = F.gens()
sage: F.subgroup([a^3,b])
@@ -1675,23 +1675,23 @@ def __init__(self, ambient, gens, names="f", category=None):
Infinite groups can also be handled::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup([3,4,0], names="abc")
sage: a,b,c = G.gens()
- sage: F = G.subgroup([a, b^2, c]); F # needs sage.libs.gap
+ sage: F = G.subgroup([a, b^2, c]); F
Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z
generated by {a, b^2, c}
-
- sage: F.gens_orders() # needs sage.libs.gap
+ sage: F.gens_orders()
(2, 3, 0)
- sage: F.gens() # needs sage.libs.gap
+ sage: F.gens()
(a, b^2, c)
- sage: F.order() # needs sage.libs.gap
+ sage: F.order()
+Infinity
Testing issue :trac:`18863`::
sage: G = AbelianGroup(5,[2])
- sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) # needs sage.libs.gap
+ sage: G.subgroup([prod(g^k for g,k in zip(G.gens(),[1,-2,3,-4,5]))]) # needs sage.libs.gap # optional - gap_package_polycyclic
Multiplicative Abelian subgroup isomorphic to Z
generated by {f0*f1^-2*f2^3*f3^-4*f4}
"""
@@ -1725,35 +1725,38 @@ def __contains__(self, x):
EXAMPLES::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup(2)
- sage: A = G.subgroup([a]) # needs sage.libs.gap
+ sage: A = G.subgroup([a])
sage: a in G
True
- sage: a in A # needs sage.libs.gap
+ sage: a in A
True
TESTS:
Check that :trac:`32910` is fixed::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup(2, [4, 576])
sage: Hgens = [a^2, a*b^2]
- sage: H = G.subgroup(Hgens) # needs sage.libs.gap
- sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")] # needs sage.libs.gap
+ sage: H = G.subgroup(Hgens)
+ sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")]
[False, False, False, True, False]
Check that :trac:`31507` is fixed::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup(2, gens_orders=[16, 16])
sage: f0, f1 = G.gens()
- sage: H = G.subgroup([f0*f1^3]) # needs sage.libs.gap
- sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)] # needs sage.libs.gap
+ sage: H = G.subgroup([f0*f1^3])
+ sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)]
[False, False, True, False]
sage: G. = AbelianGroup(2)
sage: Hgens = [a*b, a*b^-1]
- sage: H = G.subgroup(Hgens) # needs sage.libs.gap
- sage: b^2 in H # needs sage.libs.gap
+ sage: H = G.subgroup(Hgens) # optional - gap_package_polycyclic
+ sage: b^2 in H # optional - gap_package_polycyclic
True
"""
if not isinstance(x, AbelianGroupElement):
@@ -1781,9 +1784,10 @@ def ambient_group(self):
EXAMPLES::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup([2,3,4])
- sage: H = G.subgroup([a, b^2]) # needs sage.libs.gap
- sage: H.ambient_group() is G # needs sage.libs.gap
+ sage: H = G.subgroup([a, b^2])
+ sage: H.ambient_group() is G
True
"""
return self._ambient_group
@@ -1805,19 +1809,17 @@ def equals(left, right):
EXAMPLES::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G = AbelianGroup(3, [2,3,4], names="abc"); G
Multiplicative Abelian group isomorphic to C2 x C3 x C4
sage: a,b,c = G.gens()
- sage: F = G.subgroup([a,b^2]); F # needs sage.libs.gap
+ sage: F = G.subgroup([a,b^2]); F
Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
- sage: F = AbelianGroup(2)
sage: A = G.subgroup([a])
sage: B = G.subgroup([b])
@@ -1849,8 +1851,8 @@ def _repr_(self):
sage: G. = AbelianGroup(2)
sage: G._repr_()
'Multiplicative Abelian group isomorphic to Z x Z'
- sage: A = G.subgroup([a]) # needs sage.libs.gap
- sage: A._repr_() # needs sage.libs.gap
+ sage: A = G.subgroup([a]) # needs sage.libs.gap # optional - gap_package_polycyclic
+ sage: A._repr_() # needs sage.libs.gap # optional - gap_package_polycyclic
'Multiplicative Abelian subgroup isomorphic to Z generated by {a}'
"""
eldv = self._abinvs
@@ -1872,11 +1874,12 @@ def gens(self):
EXAMPLES::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup(2)
- sage: A = G.subgroup([a]) # needs sage.libs.gap
+ sage: A = G.subgroup([a])
sage: G.gens()
(a, b)
- sage: A.gens() # needs sage.libs.gap
+ sage: A.gens()
(a,)
"""
return self._gens
@@ -1887,9 +1890,10 @@ def gen(self, n):
EXAMPLES::
+ sage: # needs sage.libs.gap # optional - gap_package_polycyclic
sage: G. = AbelianGroup(2)
- sage: A = G.subgroup([a]) # needs sage.libs.gap
- sage: A.gen(0) # needs sage.libs.gap
+ sage: A = G.subgroup([a])
+ sage: A.gen(0)
a
"""
return self._gens[n]
diff --git a/src/sage/groups/abelian_gps/abelian_group_morphism.py b/src/sage/groups/abelian_gps/abelian_group_morphism.py
index e56b1c68afd..e4b7b82b42b 100644
--- a/src/sage/groups/abelian_gps/abelian_group_morphism.py
+++ b/src/sage/groups/abelian_gps/abelian_group_morphism.py
@@ -59,14 +59,14 @@ class AbelianGroupMorphism(Morphism):
sage: x,y = H.gens()
sage: from sage.groups.abelian_gps.abelian_group_morphism import AbelianGroupMorphism
- sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b])
+ sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) # optional - gap_package_polycyclic
TESTS::
sage: G. = AbelianGroup(2,[2,3])
sage: H. = AbelianGroup(3,[2,3,4])
- sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b])
- sage: Hom(G,H) == phi.parent()
+ sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic
+ sage: Hom(G,H) == phi.parent() # optional - gap_package_polycyclic
True
AUTHORS:
@@ -126,11 +126,11 @@ def _libgap_(self):
sage: H = AbelianGroup(2,[2,3],names="xy"); H
Multiplicative Abelian group isomorphic to C2 x C3
sage: x,y = H.gens()
- sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b])
- sage: libgap(phi)
+ sage: phi = AbelianGroupMorphism(H,G,[x,y],[a,b]) # optional - gap_package_polycyclic
+ sage: libgap(phi) # optional - gap_package_polycyclic
[ f1, f2 ] -> [ f1, f2 ]
- sage: phi = AbelianGroupMorphism(H,G,[x,y],[a*c**2,b])
- sage: libgap(phi)
+ sage: phi = AbelianGroupMorphism(H,G,[x,y],[a*c**2,b]) # optional - gap_package_polycyclic
+ sage: libgap(phi) # optional - gap_package_polycyclic
[ f1, f2 ] -> [ f1*f4, f2 ]
"""
G = libgap(self.domain())
@@ -158,16 +158,16 @@ def kernel(self):
sage: G = AbelianGroup(2,[2,3],names="xy"); G
Multiplicative Abelian group isomorphic to C2 x C3
sage: x,y = G.gens()
- sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b])
- sage: phi.kernel()
+ sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic
+ sage: phi.kernel() # optional - gap_package_polycyclic
Group([ ])
sage: H = AbelianGroup(3,[2,2,2],names="abc")
sage: a,b,c = H.gens()
sage: G = AbelianGroup(2,[2,2],names="x")
sage: x,y = G.gens()
- sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,a])
- sage: phi.kernel()
+ sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,a]) # optional - gap_package_polycyclic
+ sage: phi.kernel() # optional - gap_package_polycyclic
Group([ f1*f2 ])
"""
return libgap(self).Kernel()
@@ -186,11 +186,11 @@ def image(self, S):
sage: G = AbelianGroup(2,[2,3],names="xy")
sage: x,y = G.gens()
- sage: subG = G.subgroup([x])
+ sage: subG = G.subgroup([x]) # optional - gap_package_polycyclic
sage: H = AbelianGroup(3,[2,3,4],names="abc")
sage: a,b,c = H.gens()
- sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b])
- sage: phi.image(subG)
+ sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic
+ sage: phi.image(subG) # optional - gap_package_polycyclic
Multiplicative Abelian subgroup isomorphic to C2 generated by {a}
"""
return self.codomain().subgroup([self(g) for g in S.gens()])
@@ -206,10 +206,10 @@ def _call_(self, g):
sage: a,b,c = H.gens()
sage: G = AbelianGroup(2, [2,3], names="xy")
sage: x,y = G.gens()
- sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b])
- sage: phi(y*x)
+ sage: phi = AbelianGroupMorphism(G,H,[x,y],[a,b]) # optional - gap_package_polycyclic
+ sage: phi(y*x) # optional - gap_package_polycyclic
a*b
- sage: phi(y^2)
+ sage: phi(y^2) # optional - gap_package_polycyclic
b^2
"""
# g.word_problem is faster in general than word_problem(g)
diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py
index 85318aa6f83..c6012254630 100644
--- a/src/sage/groups/abelian_gps/dual_abelian_group.py
+++ b/src/sage/groups/abelian_gps/dual_abelian_group.py
@@ -1,3 +1,4 @@
+# sage.doctest: needs sage.rings.number_field
r"""
Dual groups of Finite Multiplicative Abelian Groups
@@ -25,7 +26,6 @@
sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde')
sage: (a, b, c, d, e) = F.gens()
- sage: # needs sage.rings.number_field
sage: Fd = F.dual_group(names='ABCDE')
sage: Fd.base_ring()
Cyclotomic Field of order 2520 and degree 576
@@ -82,7 +82,6 @@ def is_DualAbelianGroup(x):
EXAMPLES::
- sage: # needs sage.rings.number_field
sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroup
sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde"))
sage: Fd = F.dual_group()
@@ -105,7 +104,7 @@ class DualAbelianGroup_class(UniqueRepresentation, AbelianGroupBase):
EXAMPLES::
sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde")
- sage: F.dual_group() # needs sage.rings.number_field
+ sage: F.dual_group()
Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z
over Cyclotomic Field of order 2520 and degree 576
@@ -123,7 +122,7 @@ def __init__(self, G, names, base_ring):
EXAMPLES::
sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde")
- sage: F.dual_group() # needs sage.rings.number_field
+ sage: F.dual_group()
Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z
over Cyclotomic Field of order 2520 and degree 576
"""
@@ -180,9 +179,9 @@ def _repr_(self):
EXAMPLES::
sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde')
- sage: Fd = F.dual_group(names='ABCDE', # needs sage.rings.number_field
+ sage: Fd = F.dual_group(names='ABCDE',
....: base_ring=CyclotomicField(2*5*7*8*9))
- sage: Fd # indirect doctest # needs sage.rings.number_field
+ sage: Fd # indirect doctest
Dual of Abelian Group isomorphic to Z/2Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z
over Cyclotomic Field of order 5040 and degree 1152
sage: Fd = F.dual_group(names='ABCDE', base_ring=CC) # needs sage.rings.real_mpfr
@@ -209,8 +208,8 @@ def _latex_(self):
EXAMPLES::
sage: F = AbelianGroup(3, [2]*3)
- sage: Fd = F.dual_group() # needs sage.rings.number_field
- sage: Fd._latex_() # needs sage.rings.number_field
+ sage: Fd = F.dual_group()
+ sage: Fd._latex_()
'$\\mathrm{DualAbelianGroup}( AbelianGroup ( 3, (2, 2, 2) ) )$'
"""
return r"$\mathrm{DualAbelianGroup}( AbelianGroup ( %s, %s ) )$" % (self.ngens(), self.gens_orders())
@@ -251,7 +250,6 @@ def gen(self, i=0):
EXAMPLES::
- sage: # needs sage.rings.number_field
sage: F = AbelianGroup(3, [1,2,3], names='a')
sage: Fd = F.dual_group(names="A")
sage: Fd.0
@@ -279,8 +277,8 @@ def gens(self):
EXAMPLES::
- sage: F = AbelianGroup([7,11]).dual_group() # needs sage.rings.number_field
- sage: F.gens() # needs sage.rings.number_field
+ sage: F = AbelianGroup([7,11]).dual_group()
+ sage: F.gens()
(X0, X1)
"""
n = self.group().ngens()
@@ -293,8 +291,8 @@ def ngens(self):
EXAMPLES::
sage: F = AbelianGroup([7]*100)
- sage: Fd = F.dual_group() # needs sage.rings.number_field
- sage: Fd.ngens() # needs sage.rings.number_field
+ sage: Fd = F.dual_group()
+ sage: Fd.ngens()
100
"""
return self.group().ngens()
@@ -310,8 +308,8 @@ def gens_orders(self):
EXAMPLES::
sage: F = AbelianGroup([5]*1000)
- sage: Fd = F.dual_group() # needs sage.rings.number_field
- sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field
+ sage: Fd = F.dual_group()
+ sage: invs = Fd.gens_orders(); len(invs)
1000
"""
return self.group().gens_orders()
@@ -325,8 +323,8 @@ def invariants(self):
EXAMPLES::
sage: F = AbelianGroup([5]*1000)
- sage: Fd = F.dual_group() # needs sage.rings.number_field
- sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field
+ sage: Fd = F.dual_group()
+ sage: invs = Fd.gens_orders(); len(invs)
1000
"""
# TODO: deprecate
@@ -340,9 +338,9 @@ def __contains__(self, X):
sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde")
sage: a,b,c,d,e = F.gens()
- sage: Fd = F.dual_group(names="ABCDE") # needs sage.rings.number_field
- sage: A,B,C,D,E = Fd.gens() # needs sage.rings.number_field
- sage: A*B^2*D^7 in Fd # needs sage.rings.number_field
+ sage: Fd = F.dual_group(names="ABCDE")
+ sage: A,B,C,D,E = Fd.gens()
+ sage: A*B^2*D^7 in Fd
True
"""
return X.parent() == self and is_DualAbelianGroupElement(X)
@@ -354,8 +352,8 @@ def order(self):
EXAMPLES::
sage: G = AbelianGroup([2,3,9])
- sage: Gd = G.dual_group() # needs sage.rings.number_field
- sage: Gd.order() # needs sage.rings.number_field
+ sage: Gd = G.dual_group()
+ sage: Gd.order()
54
"""
G = self.group()
@@ -368,10 +366,10 @@ def is_commutative(self):
EXAMPLES::
sage: G = AbelianGroup([2,3,9])
- sage: Gd = G.dual_group() # needs sage.rings.number_field
- sage: Gd.is_commutative() # needs sage.rings.number_field
+ sage: Gd = G.dual_group()
+ sage: Gd.is_commutative()
True
- sage: Gd.is_abelian() # needs sage.rings.number_field
+ sage: Gd.is_abelian()
True
"""
return True
@@ -384,8 +382,8 @@ def list(self):
EXAMPLES::
sage: G = AbelianGroup([2,3], names="ab")
- sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field
- sage: Gd.list() # needs sage.rings.number_field
+ sage: Gd = G.dual_group(names="AB")
+ sage: Gd.list()
(1, B, B^2, A, A*B, A*B^2)
"""
if not self.is_finite():
@@ -400,8 +398,8 @@ def __iter__(self):
EXAMPLES::
sage: G = AbelianGroup([2,3], names="ab")
- sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field
- sage: [X for X in Gd] # needs sage.rings.number_field
+ sage: Gd = G.dual_group(names="AB")
+ sage: [X for X in Gd]
[1, B, B^2, A, A*B, A*B^2]
sage: # needs sage.rings.real_mpfr
diff --git a/src/sage/groups/abelian_gps/dual_abelian_group_element.py b/src/sage/groups/abelian_gps/dual_abelian_group_element.py
index 407323d4f34..6fdb8a68c4e 100644
--- a/src/sage/groups/abelian_gps/dual_abelian_group_element.py
+++ b/src/sage/groups/abelian_gps/dual_abelian_group_element.py
@@ -1,3 +1,4 @@
+# sage.doctest: needs sage.rings.number_field
"""
Elements (characters) of the dual group of a finite Abelian group
@@ -8,13 +9,12 @@
sage: F
Multiplicative Abelian group isomorphic to C2 x C3 x C5 x C7 x C8
- sage: Fd = F.dual_group(names="ABCDE"); Fd # needs sage.rings.number_field
+ sage: Fd = F.dual_group(names="ABCDE"); Fd
Dual of Abelian Group isomorphic to Z/2Z x Z/3Z x Z/5Z x Z/7Z x Z/8Z
over Cyclotomic Field of order 840 and degree 192
The elements of the dual group can be evaluated on elements of the original group::
- sage: # needs sage.rings.number_field
sage: a,b,c,d,e = F.gens()
sage: A,B,C,D,E = Fd.gens()
sage: A*B^2*D^7
@@ -71,10 +71,10 @@ def is_DualAbelianGroupElement(x) -> bool:
EXAMPLES::
sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroupElement
- sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group() # needs sage.rings.number_field
- sage: is_DualAbelianGroupElement(F) # needs sage.rings.number_field
+ sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group()
+ sage: is_DualAbelianGroupElement(F)
False
- sage: is_DualAbelianGroupElement(F.an_element()) # needs sage.rings.number_field
+ sage: is_DualAbelianGroupElement(F.an_element())
True
"""
return isinstance(x, DualAbelianGroupElement)
@@ -96,7 +96,6 @@ def __call__(self, g):
EXAMPLES::
- sage: # needs sage.rings.number_field
sage: F = AbelianGroup(5, [2,3,5,7,8], names="abcde")
sage: a,b,c,d,e = F.gens()
sage: Fd = F.dual_group(names="ABCDE")
@@ -147,7 +146,6 @@ def word_problem(self, words):
EXAMPLES::
- sage: # needs sage.rings.number_field
sage: G = AbelianGroup(5,[3, 5, 5, 7, 8], names="abcde")
sage: Gd = G.dual_group(names="abcde")
sage: a,b,c,d,e = Gd.gens()
@@ -156,7 +154,7 @@ def word_problem(self, words):
sage: w = a^7*b^3*c^5*d^4*e^4
sage: x = a^3*b^2*c^2*d^3*e^5
sage: y = a^2*b^4*c^2*d^4*e^5
- sage: e.word_problem([u,v,w,x,y])
+ sage: e.word_problem([u,v,w,x,y]) # needs sage.libs.gap
[[b^2*c^2*d^3*e^5, 245]]
"""
from sage.libs.gap.libgap import libgap
diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py
index bfcdd282df1..7b5e4695fb1 100644
--- a/src/sage/groups/affine_gps/affine_group.py
+++ b/src/sage/groups/affine_gps/affine_group.py
@@ -204,7 +204,10 @@ def __init__(self, degree, ring):
sage: G = AffineGroup(2, GF(5)); G
Affine Group of degree 2 over Finite Field of size 5
+
+ sage: # needs sage.libs.gap (for gens)
sage: TestSuite(G).run()
+
sage: G.category()
Category of finite groups
@@ -289,8 +292,10 @@ def cardinality(self):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: AffineGroup(6, GF(5)).cardinality()
172882428468750000000000000000
+
sage: AffineGroup(6, ZZ).cardinality()
+Infinity
"""
@@ -464,6 +469,7 @@ def random_element(self):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: G = AffineGroup(4, GF(3))
sage: G.random_element() # random
[2 0 1 2] [1]
@@ -498,6 +504,7 @@ def some_elements(self):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: G = AffineGroup(4,5)
sage: G.some_elements()
[ [2 0 0 0] [1]
diff --git a/src/sage/groups/affine_gps/euclidean_group.py b/src/sage/groups/affine_gps/euclidean_group.py
index 47de04c6544..59fb411b925 100644
--- a/src/sage/groups/affine_gps/euclidean_group.py
+++ b/src/sage/groups/affine_gps/euclidean_group.py
@@ -146,6 +146,8 @@ class EuclideanGroup(AffineGroup):
True
sage: G = EuclideanGroup(2, GF(5)); G
Euclidean Group of degree 2 over Finite Field of size 5
+
+ sage: # needs sage.libs.gap (for gens)
sage: TestSuite(G).run()
REFERENCES:
diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py
index 52fbe9365d6..7df4dc8a69a 100644
--- a/src/sage/groups/affine_gps/group_element.py
+++ b/src/sage/groups/affine_gps/group_element.py
@@ -78,11 +78,14 @@ class AffineGroupElement(MultiplicativeGroupElement):
EXAMPLES::
sage: G = AffineGroup(2, GF(3))
+
+ sage: # needs sage.libs.gap
sage: g = G.random_element()
sage: type(g)
sage: G(g.matrix()) == g
True
+
sage: G(2)
[2 0] [0]
x |-> [0 2] x + [0]
@@ -107,6 +110,7 @@ def __init__(self, parent, A, b=0, convert=True, check=True):
TESTS::
+ sage: # needs sage.libs.gap
sage: G = AffineGroup(4, GF(5))
sage: g = G.random_element()
sage: TestSuite(g).run()
@@ -200,6 +204,7 @@ def matrix(self):
Composition of affine group elements equals multiplication of
the matrices::
+ sage: # needs sage.libs.gap
sage: g1 = G.random_element()
sage: g2 = G.random_element()
sage: g1.matrix() * g2.matrix() == (g1*g2).matrix()
diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py
index 18d95ef86f2..bd787d3c153 100644
--- a/src/sage/groups/braid.py
+++ b/src/sage/groups/braid.py
@@ -72,7 +72,7 @@
from sage.combinat.permutation import Permutation
from sage.combinat.permutation import Permutations
from sage.combinat.subset import Subsets
-from sage.features import PythonModule
+from sage.features.sagemath import sage__libs__braiding
from sage.groups.artin import FiniteTypeArtinGroup, FiniteTypeArtinGroupElement
from sage.groups.finitely_presented import FinitelyPresentedGroup
from sage.groups.finitely_presented import GroupMorphismWithGensImages
@@ -80,7 +80,6 @@
from sage.functions.generalized import sign
from sage.groups.perm_gps.permgroup_named import SymmetricGroup
from sage.groups.perm_gps.permgroup_named import SymmetricGroupElement
-from sage.knots.knot import Knot
from sage.libs.gap.libgap import libgap
from sage.matrix.constructor import identity_matrix, matrix
from sage.misc.lazy_attribute import lazy_attribute
@@ -98,7 +97,8 @@
['leftnormalform', 'rightnormalform', 'centralizer', 'supersummitset', 'greatestcommondivisor',
'leastcommonmultiple', 'conjugatingbraid', 'ultrasummitset',
'thurston_type', 'rigidity', 'sliding_circuits'],
- feature=PythonModule('sage.libs.braiding', spkg='libbraiding', type='standard'))
+ feature=sage__libs__braiding())
+lazy_import('sage.knots.knot', 'Knot')
class Braid(FiniteTypeArtinGroupElement):
diff --git a/src/sage/groups/galois_group.py b/src/sage/groups/galois_group.py
index 5a48380eb21..a7439d18ab3 100644
--- a/src/sage/groups/galois_group.py
+++ b/src/sage/groups/galois_group.py
@@ -10,15 +10,17 @@
- David Roe (2019): initial version
"""
-from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup
from sage.groups.abelian_gps.abelian_group import AbelianGroup_class, AbelianGroup_subgroup
-from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
-from sage.misc.lazy_attribute import lazy_attribute
from sage.misc.abstract_method import abstract_method
from sage.misc.cachefunc import cached_method
-from sage.structure.category_object import normalize_names
+from sage.misc.lazy_attribute import lazy_attribute
+from sage.misc.lazy_import import lazy_import
from sage.rings.integer_ring import ZZ
+lazy_import('sage.groups.galois_group_perm', ['GaloisGroup_perm', 'GaloisSubgroup_perm'])
+lazy_import('sage.groups.perm_gps.permgroup', 'PermutationGroup')
+
+
def _alg_key(self, algorithm=None, recompute=False):
r"""
Return a key for use in cached_method calls.
@@ -40,6 +42,7 @@ def _alg_key(self, algorithm=None, recompute=False):
algorithm = self._get_algorithm(algorithm)
return algorithm
+
class _GMixin:
r"""
This class provides some methods for Galois groups to be used for both permutation groups
@@ -151,6 +154,7 @@ def _gc_map(self):
"""
return self._gcdata[1]
+
class _GaloisMixin(_GMixin):
"""
This class provides methods for Galois groups, allowing concrete instances
@@ -275,6 +279,7 @@ def is_galois(self):
"""
return self.order() == self._field_degree
+
class _SubGaloisMixin(_GMixin):
"""
This class provides methods for subgroups of Galois groups, allowing concrete instances
@@ -339,164 +344,6 @@ def _gcdata(self):
"""
return self._ambient_group._gcdata
-class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic):
- r"""
- The group of automorphisms of a Galois closure of a given field.
-
- INPUT:
-
- - ``field`` -- a field, separable over its base
-
- - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field
-
- - ``gc_numbering`` -- boolean, whether to express permutations in terms of the
- roots of the defining polynomial of the splitting field (versus the defining polynomial
- of the original extension). The default value may vary based on the type of field.
- """
- @abstract_method
- def transitive_number(self, algorithm=None, recompute=False):
- """
- The transitive number (as in the GAP and Magma databases of transitive groups)
- for the action on the roots of the defining polynomial of the top field.
-
- EXAMPLES::
-
- sage: R. = ZZ[]
- sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field
- sage: G = K.galois_group() # needs sage.rings.number_field
- sage: G.transitive_number() # needs sage.rings.number_field
- 2
- """
-
- @lazy_attribute
- def _gens(self):
- """
- The generators of this Galois group as permutations of the roots. It's important that this
- be computed lazily, since it's often possible to compute other attributes (such as the order
- or transitive number) more cheaply.
-
- EXAMPLES::
-
- sage: R. = ZZ[]
- sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
- sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field
- sage: G._gens # needs sage.rings.number_field
- [(1,2,3,5), (1,4,3,2,5)]
- """
- return NotImplemented
-
- def __init__(self, field, algorithm=None, names=None, gc_numbering=False):
- r"""
- EXAMPLES::
-
- sage: R. = ZZ[]
- sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field
- sage: G = K.galois_group() # needs sage.rings.number_field
- sage: TestSuite(G).run() # needs sage.rings.number_field
- """
- self._field = field
- self._default_algorithm = algorithm
- self._base = field.base_field()
- self._gc_numbering = gc_numbering
- if names is None:
- # add a c for Galois closure
- names = field.variable_name() + 'c'
- self._gc_names = normalize_names(1, names)
- # We do only the parts of the initialization of PermutationGroup_generic
- # that don't depend on _gens
- from sage.categories.permutation_groups import PermutationGroups
- category = PermutationGroups().FinitelyGenerated().Finite()
- # Note that we DON'T call the __init__ method for PermutationGroup_generic
- # Instead, the relevant attributes are computed lazily
- super(PermutationGroup_generic, self).__init__(category=category)
-
- @lazy_attribute
- def _deg(self):
- r"""
- The number of moved points in the permutation representation.
-
- This will be the degree of the original number field if `_gc_numbering``
- is ``False``, or the degree of the Galois closure otherwise.
-
- EXAMPLES::
-
- sage: # needs sage.rings.number_field
- sage: R. = ZZ[]
- sage: K. = NumberField(x^5 - 2)
- sage: G = K.galois_group(gc_numbering=False); G
- Galois group 5T3 (5:4) with order 20 of x^5 - 2
- sage: G._deg
- 5
- sage: G = K.galois_group(gc_numbering=True); G._deg
- 20
- """
- if self._gc_numbering:
- return self.order()
- else:
- try:
- return self._field.degree()
- except NotImplementedError: # relative number fields don't support degree
- return self._field.relative_degree()
-
- @lazy_attribute
- def _domain(self):
- r"""
- The integers labeling the roots on which this Galois group acts.
-
- EXAMPLES::
-
- sage: # needs sage.rings.number_field
- sage: R. = ZZ[]
- sage: K. = NumberField(x^5 - 2)
- sage: G = K.galois_group(gc_numbering=False); G
- Galois group 5T3 (5:4) with order 20 of x^5 - 2
- sage: G._domain
- {1, 2, 3, 4, 5}
- sage: G = K.galois_group(gc_numbering=True); G._domain
- {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
- """
- return FiniteEnumeratedSet(range(1, self._deg+1))
-
- @lazy_attribute
- def _domain_to_gap(self):
- r"""
- Dictionary implementing the identity (used by PermutationGroup_generic).
-
- EXAMPLES::
-
- sage: R. = ZZ[]
- sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
- sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field
- sage: G._domain_to_gap[5] # needs sage.rings.number_field
- 5
- """
- return {key: i+1 for i, key in enumerate(self._domain)}
-
- @lazy_attribute
- def _domain_from_gap(self):
- r"""
- Dictionary implementing the identity (used by PermutationGroup_generic).
-
- EXAMPLES::
-
- sage: R. = ZZ[]
- sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
- sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field
- sage: G._domain_from_gap[20] # needs sage.rings.number_field
- 20
- """
- return {i+1: key for i, key in enumerate(self._domain)}
-
- def ngens(self):
- r"""
- Number of generators of this Galois group
-
- EXAMPLES::
-
- sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field
- 1
- """
- return len(self._gens)
class GaloisGroup_ab(_GaloisMixin, AbelianGroup_class):
r"""
@@ -551,7 +398,7 @@ def permutation_group(self):
EXAMPLES::
- sage: GF(3^10).galois_group().permutation_group() # needs sage.rings.finite_rings
+ sage: GF(3^10).galois_group().permutation_group() # needs sage.libs.gap sage.rings.finite_rings
Permutation Group with generators [(1,2,3,4,5,6,7,8,9,10)]
"""
return PermutationGroup(gap_group=self._gap_().RegularActionHomomorphism().Image())
@@ -568,11 +415,12 @@ def transitive_number(self, algorithm=None, recompute=False):
sage: from sage.groups.galois_group import GaloisGroup_ab
sage: Gtest = GaloisGroup_ab(field=None, generator_orders=(2,2,4))
- sage: Gtest.transitive_number()
+ sage: Gtest.transitive_number() # needs sage.libs.gap
2
"""
return ZZ(self.permutation_group()._gap_().TransitiveIdentification())
+
class GaloisGroup_cyc(GaloisGroup_ab):
r"""
Cyclic Galois groups
@@ -614,17 +462,6 @@ def signature(self):
"""
return ZZ(1) if (self._field.degree() % 2) else ZZ(-1)
-class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin):
- """
- Subgroups of Galois groups (implemented as permutation groups), specified
- by giving a list of generators.
-
- Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order
- to enable creation without determining a list of generators,
- we require that generators for a subgroup be specified during initialization,
- as specified in the ``__init__`` method of permutation subgroups.
- """
- pass
class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin):
"""
@@ -633,5 +470,4 @@ class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin):
pass
-GaloisGroup_perm.Subgroup = GaloisSubgroup_perm
GaloisGroup_ab.Subgroup = GaloisSubgroup_ab
diff --git a/src/sage/groups/galois_group_perm.py b/src/sage/groups/galois_group_perm.py
new file mode 100644
index 00000000000..162e5174143
--- /dev/null
+++ b/src/sage/groups/galois_group_perm.py
@@ -0,0 +1,186 @@
+r"""
+Galois groups of field extensions as permutation groups
+"""
+
+from sage.groups.galois_group import _GaloisMixin, _SubGaloisMixin
+from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup
+from sage.misc.abstract_method import abstract_method
+from sage.misc.lazy_attribute import lazy_attribute
+from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
+from sage.structure.category_object import normalize_names
+
+
+class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic):
+ r"""
+ The group of automorphisms of a Galois closure of a given field.
+
+ INPUT:
+
+ - ``field`` -- a field, separable over its base
+
+ - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field
+
+ - ``gc_numbering`` -- boolean, whether to express permutations in terms of the
+ roots of the defining polynomial of the splitting field (versus the defining polynomial
+ of the original extension). The default value may vary based on the type of field.
+ """
+ @abstract_method
+ def transitive_number(self, algorithm=None, recompute=False):
+ """
+ The transitive number (as in the GAP and Magma databases of transitive groups)
+ for the action on the roots of the defining polynomial of the top field.
+
+ EXAMPLES::
+
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field
+ sage: G = K.galois_group() # needs sage.rings.number_field
+ sage: G.transitive_number() # needs sage.rings.number_field
+ 2
+ """
+
+ @lazy_attribute
+ def _gens(self):
+ """
+ The generators of this Galois group as permutations of the roots. It's important that this
+ be computed lazily, since it's often possible to compute other attributes (such as the order
+ or transitive number) more cheaply.
+
+ EXAMPLES::
+
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
+ sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field
+ sage: G._gens # needs sage.rings.number_field
+ [(1,2,3,5), (1,4,3,2,5)]
+ """
+ return NotImplemented
+
+ def __init__(self, field, algorithm=None, names=None, gc_numbering=False):
+ r"""
+ EXAMPLES::
+
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field
+ sage: G = K.galois_group() # needs sage.rings.number_field
+ sage: TestSuite(G).run() # needs sage.rings.number_field
+ """
+ self._field = field
+ self._default_algorithm = algorithm
+ self._base = field.base_field()
+ self._gc_numbering = gc_numbering
+ if names is None:
+ # add a c for Galois closure
+ names = field.variable_name() + 'c'
+ self._gc_names = normalize_names(1, names)
+ # We do only the parts of the initialization of PermutationGroup_generic
+ # that don't depend on _gens
+ from sage.categories.permutation_groups import PermutationGroups
+ category = PermutationGroups().FinitelyGenerated().Finite()
+ # Note that we DON'T call the __init__ method for PermutationGroup_generic
+ # Instead, the relevant attributes are computed lazily
+ super(PermutationGroup_generic, self).__init__(category=category)
+
+ @lazy_attribute
+ def _deg(self):
+ r"""
+ The number of moved points in the permutation representation.
+
+ This will be the degree of the original number field if `_gc_numbering``
+ is ``False``, or the degree of the Galois closure otherwise.
+
+ EXAMPLES::
+
+ sage: # needs sage.rings.number_field
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^5 - 2)
+ sage: G = K.galois_group(gc_numbering=False); G
+ Galois group 5T3 (5:4) with order 20 of x^5 - 2
+ sage: G._deg
+ 5
+ sage: G = K.galois_group(gc_numbering=True); G._deg
+ 20
+ """
+ if self._gc_numbering:
+ return self.order()
+ else:
+ try:
+ return self._field.degree()
+ except NotImplementedError: # relative number fields don't support degree
+ return self._field.relative_degree()
+
+ @lazy_attribute
+ def _domain(self):
+ r"""
+ The integers labeling the roots on which this Galois group acts.
+
+ EXAMPLES::
+
+ sage: # needs sage.rings.number_field
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^5 - 2)
+ sage: G = K.galois_group(gc_numbering=False); G
+ Galois group 5T3 (5:4) with order 20 of x^5 - 2
+ sage: G._domain
+ {1, 2, 3, 4, 5}
+ sage: G = K.galois_group(gc_numbering=True); G._domain
+ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
+ """
+ return FiniteEnumeratedSet(range(1, self._deg+1))
+
+ @lazy_attribute
+ def _domain_to_gap(self):
+ r"""
+ Dictionary implementing the identity (used by PermutationGroup_generic).
+
+ EXAMPLES::
+
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
+ sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field
+ sage: G._domain_to_gap[5] # needs sage.rings.number_field
+ 5
+ """
+ return {key: i+1 for i, key in enumerate(self._domain)}
+
+ @lazy_attribute
+ def _domain_from_gap(self):
+ r"""
+ Dictionary implementing the identity (used by PermutationGroup_generic).
+
+ EXAMPLES::
+
+ sage: R. = ZZ[]
+ sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field
+ sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field
+ sage: G._domain_from_gap[20] # needs sage.rings.number_field
+ 20
+ """
+ return {i+1: key for i, key in enumerate(self._domain)}
+
+ def ngens(self):
+ r"""
+ Number of generators of this Galois group
+
+ EXAMPLES::
+
+ sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field
+ 1
+ """
+ return len(self._gens)
+
+
+class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin):
+ """
+ Subgroups of Galois groups (implemented as permutation groups), specified
+ by giving a list of generators.
+
+ Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order
+ to enable creation without determining a list of generators,
+ we require that generators for a subgroup be specified during initialization,
+ as specified in the ``__init__`` method of permutation subgroups.
+ """
+ pass
+
+
+GaloisGroup_perm.Subgroup = GaloisSubgroup_perm
diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py
index 69bffca1b9f..5e7f23af8d4 100644
--- a/src/sage/groups/generic.py
+++ b/src/sage/groups/generic.py
@@ -550,10 +550,10 @@ def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=No
It also works with matrices::
- sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.rings.finite_rings
+ sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.modules sage.rings.finite_rings
....: [14601, 41019, 30188],
....: [3081, 736, 27092]])
- sage: discrete_log_rho(A^1234567, A) # needs sage.rings.finite_rings
+ sage: discrete_log_rho(A^1234567, A) # needs sage.modules sage.rings.finite_rings
1234567
Beware, the order must be prime::
@@ -1234,7 +1234,7 @@ def order_from_multiple(P, m, plist=None, factorization=None, check=True,
sage: order_from_multiple(w, 230, operation='*')
23
- sage: # needs sage.rings.finite_rings
+ sage: # needs sage.modules sage.rings.finite_rings
sage: F = GF(2^1279,'a')
sage: n = F.cardinality() - 1 # Mersenne prime
sage: order_from_multiple(F.random_element(), n,
diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx
index cd2521e3478..ef639a0c367 100644
--- a/src/sage/groups/group.pyx
+++ b/src/sage/groups/group.pyx
@@ -35,9 +35,9 @@ def is_Group(x):
EXAMPLES::
- sage: F. = FreeGroup() # needs sage.combinat
+ sage: F. = FreeGroup() # needs sage.groups
sage: from sage.groups.group import is_Group
- sage: is_Group(F) # needs sage.combinat
+ sage: is_Group(F) # needs sage.groups
True
sage: is_Group("a string")
False
@@ -140,7 +140,7 @@ cdef class Group(Parent):
EXAMPLES::
- sage: SL(2, 7).is_commutative() # needs sage.modules sage.rings.finite_rings
+ sage: SL(2, 7).is_commutative() # needs sage.libs.gap sage.modules sage.rings.finite_rings
False
"""
return self.is_abelian()
@@ -186,6 +186,39 @@ cdef class Group(Parent):
"""
return self.order() != infinity
+ def is_trivial(self):
+ r"""
+ Return ``True`` if this group is the trivial group.
+
+ A group is trivial, if it consists only of the identity
+ element.
+
+ .. WARNING::
+
+ It is in principle undecidable whether a group is
+ trivial, for example, if the group is given by a finite
+ presentation. Thus, this method may not terminate.
+
+ EXAMPLES::
+
+ sage: groups.presentation.Cyclic(1).is_trivial()
+ True
+
+ sage: G. = FreeGroup('a, b')
+ sage: H = G / (a^2, b^3, a*b*~a*~b)
+ sage: H.is_trivial()
+ False
+
+ A non-trivial presentation of the trivial group::
+
+ sage: F. = FreeGroup()
+ sage: J = F / ((~a)*b*a*(~b)^2, (~b)*a*b*(~a)^2)
+ sage: J.is_trivial()
+ True
+ """
+ return self.order() == 1
+
+
def is_multiplicative(self):
r"""
Returns True if the group operation is given by \* (rather than
@@ -212,8 +245,8 @@ cdef class Group(Parent):
EXAMPLES::
- sage: G = AbelianGroup([2,3,4,5]) # needs sage.groups
- sage: G.an_element() # needs sage.groups
+ sage: G = AbelianGroup([2,3,4,5]) # needs sage.modules
+ sage: G.an_element() # needs sage.modules
f0*f1*f2*f3
"""
return self.prod(self.gens())
diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py
index 6b865805298..d1c647f1286 100644
--- a/src/sage/groups/matrix_gps/coxeter_group.py
+++ b/src/sage/groups/matrix_gps/coxeter_group.py
@@ -79,7 +79,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene
We can create Coxeter groups from Coxeter matrices::
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]]); W
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
[ 1 6 3]
@@ -150,7 +150,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene
graphs, we can input a Coxeter graph. Following the standard convention,
edges with no label (i.e. labelled by ``None``) are treated as 3::
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)])
sage: W = CoxeterGroup(G); W
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
@@ -165,7 +165,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene
Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels
of `\infty` are given by `-1` in the Coxeter matrix::
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)])
sage: W = CoxeterGroup(G)
sage: W.coxeter_matrix()
@@ -183,7 +183,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene
[2 3 1 3 3]
[2 2 3 1 2]
[2 2 3 2 1]
- sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.rings.number_field
+ sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.libs.gap sage.rings.number_field
Finite Coxeter group over
Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?
with Coxeter matrix:
@@ -240,15 +240,15 @@ def __init__(self, coxeter_matrix, base_ring, index_set):
EXAMPLES::
sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])
- sage: TestSuite(W).run() # long time
+ sage: TestSuite(W).run() # long time
- sage: # needs sage.rings.number_field
+ sage: # long time, needs sage.rings.number_field sage.symbolic
sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar)
- sage: TestSuite(W).run() # long time
+ sage: TestSuite(W).run()
sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]])
- sage: TestSuite(W).run(max_runs=30) # long time
+ sage: TestSuite(W).run(max_runs=30)
sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]])
- sage: TestSuite(W).run(max_runs=30) # long time
+ sage: TestSuite(W).run(max_runs=30)
We check that :trac:`16630` is fixed::
@@ -340,7 +340,7 @@ def _repr_(self):
EXAMPLES::
- sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.rings.number_field
+ sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.libs.gap sage.rings.number_field
Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix:
[1 3 2]
[3 1 4]
@@ -383,8 +383,8 @@ def coxeter_matrix(self):
sage: W.coxeter_matrix()
[1 3]
[3 1]
- sage: W = CoxeterGroup(['H',3]) # needs sage.rings.number_field
- sage: W.coxeter_matrix()
+ sage: W = CoxeterGroup(['H',3]) # needs sage.libs.gap sage.rings.number_field
+ sage: W.coxeter_matrix() # needs sage.libs.gap sage.rings.number_field
[1 3 2]
[3 1 5]
[2 5 1]
@@ -423,7 +423,7 @@ def is_finite(self):
EXAMPLES::
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: [l for l in range(2, 9) if
....: CoxeterGroup([[1,3,2],[3,1,l],[2,l,1]]).is_finite()]
[2, 3, 4, 5]
@@ -482,8 +482,8 @@ def order(self):
sage: W = CoxeterGroup([[1,3],[3,1]])
sage: W.order()
6
- sage: W = CoxeterGroup([[1,-1],[-1,1]])
- sage: W.order()
+ sage: W = CoxeterGroup([[1,-1],[-1,1]]) # needs sage.libs.gap
+ sage: W.order() # needs sage.libs.gap
+Infinity
"""
if self.is_finite():
@@ -593,7 +593,7 @@ def positive_roots(self):
sage: W.positive_roots()
((1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1), (0, 1, 1), (0, 0, 1))
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: W = CoxeterGroup(['I',5], implementation='reflection')
sage: W.positive_roots()
((1, 0),
@@ -651,7 +651,7 @@ def roots(self):
(0, -1, -1),
(0, 0, -1))
- sage: # needs sage.rings.number_field
+ sage: # needs sage.libs.gap sage.rings.number_field
sage: W = CoxeterGroup(['I',5], implementation='reflection')
sage: len(W.roots())
10
diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py
index c8098ad578a..ef6ba42a105 100644
--- a/src/sage/groups/matrix_gps/finitely_generated.py
+++ b/src/sage/groups/matrix_gps/finitely_generated.py
@@ -9,7 +9,7 @@
sage: F = GF(3)
sage: gens = [matrix(F, 2, [1,0, -1,1]), matrix(F, 2, [1,1,0,1])]
sage: G = MatrixGroup(gens)
- sage: G.conjugacy_classes_representatives()
+ sage: G.conjugacy_classes_representatives() # needs sage.libs.gap
(
[1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2]
[0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0]
@@ -161,6 +161,8 @@ def QuaternionMatrixGroupGF3():
is the product of `I` and `J`. ::
sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3
+
+ sage: # needs sage.libs.gap
sage: Q = QuaternionMatrixGroupGF3()
sage: Q.order()
8
@@ -176,22 +178,23 @@ def QuaternionMatrixGroupGF3():
TESTS::
- sage: groups.matrix.QuaternionGF3() # needs sage.modules sage.rings.finite_rings
+ sage: groups.matrix.QuaternionGF3()
Matrix group over Finite Field of size 3 with 2 generators (
[1 1] [2 1]
[1 2], [1 1]
)
+ sage: # needs sage.groups
sage: Q = QuaternionMatrixGroupGF3()
sage: QP = Q.as_permutation_group()
sage: QP.is_isomorphic(QuaternionGroup())
True
- sage: H = DihedralGroup(4) # needs sage.groups
- sage: H.order() # needs sage.groups
+ sage: H = DihedralGroup(4)
+ sage: H.order()
8
- sage: QP.is_abelian(), H.is_abelian() # needs sage.groups
+ sage: QP.is_abelian(), H.is_abelian()
(False, False)
- sage: QP.is_isomorphic(H) # needs sage.groups
+ sage: QP.is_isomorphic(H)
False
"""
from sage.rings.finite_rings.finite_field_constructor import FiniteField
@@ -340,6 +343,7 @@ class FinitelyGeneratedMatrixGroup_generic(MatrixGroup_generic):
sage: MatrixGroup(m1, m2) == MatrixGroup(m2, m1)
False
+ sage: # needs sage.libs.gap
sage: G = GL(2, GF(3))
sage: H = G.as_matrix_group()
sage: H == G, G == H
@@ -415,6 +419,7 @@ def gen(self, i):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: H = GL(2, GF(3))
sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]])
sage: G = H.subgroup([h1, h2])
@@ -436,6 +441,7 @@ def ngens(self):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: H = GL(2, GF(3))
sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]])
sage: G = H.subgroup([h1, h2])
diff --git a/src/sage/groups/matrix_gps/finitely_generated_gap.py b/src/sage/groups/matrix_gps/finitely_generated_gap.py
index af676af3673..add6875a802 100644
--- a/src/sage/groups/matrix_gps/finitely_generated_gap.py
+++ b/src/sage/groups/matrix_gps/finitely_generated_gap.py
@@ -120,16 +120,14 @@ def as_permutation_group(self, algorithm=None, seed=None):
21499084800
sage: P = G.as_permutation_group()
sage: Psmaller = G.as_permutation_group(algorithm="smaller", seed=6)
- sage: P == Psmaller
- False
sage: P.cardinality()
21499084800
sage: P.degree()
144
sage: Psmaller.cardinality()
21499084800
- sage: Psmaller.degree() # random
- 80
+ sage: Psmaller.degree() <= P.degree()
+ True
.. NOTE::
diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py
index 65a8c13645c..822bd576afd 100644
--- a/src/sage/groups/matrix_gps/linear.py
+++ b/src/sage/groups/matrix_gps/linear.py
@@ -16,6 +16,8 @@
Special Linear Group of degree 2 over Integer Ring
sage: G = SL(2, GF(3)); G
Special Linear Group of degree 2 over Finite Field of size 3
+
+ sage: # needs sage.libs.gap
sage: G.is_finite()
True
sage: G.conjugacy_classes_representatives()
@@ -94,32 +96,38 @@ def GL(n, R, var='a'):
EXAMPLES::
sage: G = GL(6, GF(5))
- sage: G.order()
- 11064475422000000000000000
sage: G.base_ring()
Finite Field of size 5
sage: G.category()
Category of finite groups
+
+ sage: # needs sage.libs.gap
+ sage: G.order()
+ 11064475422000000000000000
sage: TestSuite(G).run()
sage: G = GL(6, QQ)
sage: G.category()
Category of infinite groups
+
+ sage: # needs sage.libs.gap
sage: TestSuite(G).run()
Here is the Cayley graph of (relatively small) finite General Linear Group::
+ sage: # needs sage.graphs sage.libs.gap
sage: g = GL(2,3)
- sage: d = g.cayley_graph(); d # needs sage.graphs
+ sage: d = g.cayley_graph(); d
Digraph on 48 vertices
- sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.graphs sage.plot
+ sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.plot
....: vertex_labels=False)
Graphics object consisting of 144 graphics primitives
- sage: d.plot3d(color_by_label=True) # long time # needs sage.graphs sage.plot
+ sage: d.plot3d(color_by_label=True) # long time # needs sage.plot
Graphics3d Object
::
+ sage: # needs sage.libs.gap
sage: F = GF(3); MS = MatrixSpace(F, 2, 2)
sage: gens = [MS([[2,0], [0,1]]), MS([[2,1], [2,0]])]
sage: G = MatrixGroup(gens)
@@ -213,10 +221,13 @@ def SL(n, R, var='a'):
Special Linear Group of degree 15 over Finite Field of size 7
sage: G.category()
Category of finite groups
+
+ sage: # needs sage.libs.gap
sage: G.order()
1956712595698146962015219062429586341124018007182049478916067369638713066737882363393519966343657677430907011270206265834819092046250232049187967718149558134226774650845658791865745408000000
sage: len(G.gens())
2
+
sage: G = SL(2, ZZ); G
Special Linear Group of degree 2 over Integer Ring
sage: G.category()
@@ -231,6 +242,8 @@ def SL(n, R, var='a'):
sage: G = SL(3, ZZ); G
Special Linear Group of degree 3 over Integer Ring
+
+ sage: # needs sage.libs.gap
sage: G.gens()
(
[0 1 0] [ 0 1 0] [1 1 0]
diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py
index c0c22456320..192c4e4d4cf 100644
--- a/src/sage/groups/matrix_gps/matrix_group.py
+++ b/src/sage/groups/matrix_gps/matrix_group.py
@@ -5,10 +5,10 @@
Loading, saving, ... works::
+ sage: # needs sage.libs.gap
sage: G = GL(2,5); G
General Linear Group of degree 2 over Finite Field of size 5
sage: TestSuite(G).run()
-
sage: g = G.1; g
[4 1]
[4 0]
@@ -159,7 +159,7 @@ def as_matrix_group(self):
EXAMPLES::
sage: G = SU(4, GF(5)) # needs sage.rings.finite_rings
- sage: G.as_matrix_group() # needs sage.rings.finite_rings
+ sage: G.as_matrix_group() # needs sage.libs.gap sage.rings.finite_rings
Matrix group over Finite Field in a of size 5^2 with 2 generators (
[ a 0 0 0] [ 1 0 4*a + 3 0]
[ 0 2*a + 3 0 0] [ 1 0 0 0]
@@ -167,7 +167,8 @@ def as_matrix_group(self):
[ 0 0 0 3*a], [ 0 3*a + 1 0 0]
)
- sage: G = GO(3,GF(5))
+ sage: # needs sage.libs.gap
+ sage: G = GO(3, GF(5))
sage: G.as_matrix_group()
Matrix group over Finite Field of size 5 with 2 generators (
[2 0 0] [0 1 0]
@@ -316,8 +317,8 @@ def _repr_option(self, key):
EXAMPLES::
- sage: SO3 = groups.matrix.SO(3, QQ) # needs sage.groups sage.modules
- sage: SO3._repr_option('element_ascii_art') # needs sage.groups sage.modules
+ sage: SO3 = groups.matrix.SO(3, QQ)
+ sage: SO3._repr_option('element_ascii_art')
True
"""
if key == 'element_ascii_art':
@@ -479,6 +480,7 @@ def __richcmp__(self, other, op):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: G = GL(2,3)
sage: H = MatrixGroup(G.gens())
sage: H == G
@@ -486,6 +488,7 @@ def __richcmp__(self, other, op):
sage: G == H
True
+ sage: # needs sage.libs.gap
sage: MS = MatrixSpace(QQ, 2, 2)
sage: G = MatrixGroup([MS(1), MS([1,2,3,4])])
sage: G == G
@@ -542,3 +545,34 @@ def __richcmp__(self, other, op):
if lx != rx:
return richcmp_not_equal(lx, rx, op)
return rich_to_bool(op, 0)
+
+ def is_trivial(self):
+ r"""
+ Return ``True`` if this group is the trivial group.
+
+ A group is trivial, if it consists only of the identity
+ element, that is, if all its generators are the identity.
+
+ EXAMPLES::
+
+ sage: MatrixGroup([identity_matrix(3)]).is_trivial()
+ True
+ sage: SL(2, ZZ).is_trivial()
+ False
+ sage: CoxeterGroup(['B',3], implementation="matrix").is_trivial()
+ False
+
+ TESTS::
+
+ sage: CoxeterGroup(['A',0], implementation="matrix").is_trivial()
+ True
+ sage: MatrixGroup([matrix(SR, [[1,x], [0,1]])]).is_trivial()
+ False
+ sage: G = MatrixGroup([identity_matrix(3), identity_matrix(3)])
+ sage: G.ngens()
+ 2
+ sage: G.is_trivial()
+ True
+
+ """
+ return all(g.is_one() for g in self.gens())
diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py
index 4568c43d326..98841d9f0af 100644
--- a/src/sage/groups/matrix_gps/named_group.py
+++ b/src/sage/groups/matrix_gps/named_group.py
@@ -10,6 +10,8 @@
Special Linear Group of degree 2 over Integer Ring
sage: G = SL(2, GF(3)); G
Special Linear Group of degree 2 over Finite Field of size 3
+
+ sage: # needs sage.libs.gap
sage: G.is_finite()
True
sage: G.conjugacy_classes_representatives()
@@ -288,12 +290,13 @@ def __richcmp__(self, other, op):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: G = GL(2,3)
sage: G == MatrixGroup(G.gens())
True
- sage: # needs sage.rings.finite_rings
- sage: G = groups.matrix.GL(4,2) # needs sage.modules
+ sage: # needs sage.libs.gap sage.rings.finite_rings
+ sage: G = groups.matrix.GL(4,2)
sage: H = MatrixGroup(G.gens())
sage: G == H
True
diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py
index 9e90d3ac0dc..8d622f7b14f 100644
--- a/src/sage/groups/matrix_gps/orthogonal.py
+++ b/src/sage/groups/matrix_gps/orthogonal.py
@@ -39,6 +39,8 @@
sage: G = SO(4, GF(7), 1); G
Special Orthogonal Group of degree 4 and form parameter 1
over Finite Field of size 7
+
+ sage: # needs sage.libs.gap
sage: G.random_element() # random
[4 3 5 2]
[6 6 4 0]
@@ -154,7 +156,7 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None):
Check that :trac:`26028` is fixed::
- sage: GO(3,25).order() # indirect doctest # needs sage.rings.finite_rings
+ sage: GO(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings
31200
Check that :trac:`28054` is fixed::
@@ -270,6 +272,8 @@ def GO(n, R, e=0, var='a', invariant_form=None):
sage: GO(3, GF(7))
General Orthogonal Group of degree 3 over Finite Field of size 7
+
+ sage: # needs sage.libs.gap
sage: GO(3, GF(7)).order()
672
sage: GO(3, GF(7)).gens()
@@ -323,6 +327,7 @@ def GO(n, R, e=0, var='a', invariant_form=None):
TESTS::
+ sage: # needs sage.libs.gap
sage: TestSuite(GO3).run()
sage: groups.matrix.GO(2, 3, e=-1)
General Orthogonal Group of degree 2 and form parameter -1 over Finite Field of size 3
@@ -378,6 +383,7 @@ def SO(n, R, e=None, var='a', invariant_form=None):
sage: G = SO(3,GF(5)); G
Special Orthogonal Group of degree 3 over Finite Field of size 5
+ sage: # needs sage.libs.gap
sage: G = SO(3,GF(5))
sage: G.gens()
(
@@ -385,7 +391,6 @@ def SO(n, R, e=None, var='a', invariant_form=None):
[0 3 0] [0 2 0] [4 0 0]
[0 0 1], [0 3 1], [2 0 4]
)
- sage: G = SO(3,GF(5))
sage: G.as_matrix_group()
Matrix group over Finite Field of size 5 with 3 generators (
[2 0 0] [3 2 3] [1 4 4]
@@ -483,6 +488,7 @@ def invariant_bilinear_form(self):
EXAMPLES::
+ sage: # needs sage.libs.gap
sage: GO(2,3,+1).invariant_bilinear_form()
[0 1]
[1 0]
@@ -503,7 +509,7 @@ def invariant_bilinear_form(self):
TESTS::
- sage: GO3m.invariant_form()
+ sage: GO3m.invariant_form() # needs sage.libs.gap
[1 0 0]
[0 2 0]
[0 0 3]
diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py
index e0f3324b645..ec61d8a0634 100644
--- a/src/sage/groups/matrix_gps/symplectic.py
+++ b/src/sage/groups/matrix_gps/symplectic.py
@@ -5,6 +5,8 @@
sage: G = Sp(4, GF(7)); G
Symplectic Group of degree 4 over Finite Field of size 7
+
+ sage: # needs sage.libs.gap
sage: g = prod(G.gens()); g
[3 0 3 0]
[1 0 0 0]
@@ -138,6 +140,7 @@ def Sp(n, R, var='a', invariant_form=None):
sage: groups.matrix.Sp(2, 3) # needs sage.modules sage.rings.finite_rings
Symplectic Group of degree 2 over Finite Field of size 3
+ sage: # needs sage.libs.gap
sage: G = Sp(4,5)
sage: TestSuite(G).run()
"""
diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py
index ed6221cb61d..379d8276571 100644
--- a/src/sage/groups/matrix_gps/unitary.py
+++ b/src/sage/groups/matrix_gps/unitary.py
@@ -8,11 +8,11 @@
sage: # needs sage.rings.finite_rings
sage: G = SU(3,5)
- sage: G.order()
+ sage: G.order() # needs sage.libs.gap
378000
sage: G
Special Unitary Group of degree 3 over Finite Field in a of size 5^2
- sage: G.gens()
+ sage: G.gens() # needs sage.libs.gap
(
[ a 0 0] [4*a 4 1]
[ 0 2*a + 2 0] [ 4 4 0]
@@ -100,7 +100,7 @@ def _UG(n, R, special, var='a', invariant_form=None):
TESTS::
- sage: GU(3,25).order() # indirect doctest # needs sage.rings.finite_rings
+ sage: GU(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings
3961191000000
"""
prefix = 'General'
@@ -195,7 +195,7 @@ def GU(n, R, var='a', invariant_form=None):
sage: G = GU(3, 7); G # needs sage.rings.finite_rings
General Unitary Group of degree 3 over Finite Field in a of size 7^2
- sage: G.gens() # needs sage.rings.finite_rings
+ sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings
(
[ a 0 0] [6*a 6 1]
[ 0 1 0] [ 6 6 0]
@@ -207,7 +207,7 @@ def GU(n, R, var='a', invariant_form=None):
sage: G = GU(3, 5, var='beta') # needs sage.rings.finite_rings
sage: G.base_ring() # needs sage.rings.finite_rings
Finite Field in beta of size 5^2
- sage: G.gens() # needs sage.rings.finite_rings
+ sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings
(
[ beta 0 0] [4*beta 4 1]
[ 0 1 0] [ 4 4 0]
diff --git a/src/sage/groups/pari_group.py b/src/sage/groups/pari_group.py
index bbcb1c0a6c9..4d1ca0bff22 100644
--- a/src/sage/groups/pari_group.py
+++ b/src/sage/groups/pari_group.py
@@ -6,8 +6,10 @@
"""
from sage.libs.pari import pari
+from sage.misc.lazy_import import lazy_import
from sage.rings.integer import Integer
-from sage.groups.perm_gps.permgroup_named import TransitiveGroup
+
+lazy_import('sage.groups.perm_gps.permgroup_named', 'TransitiveGroup')
class PariGroup():
diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py
index 8e2e6d82c10..2d73c8e7cf2 100644
--- a/src/sage/groups/perm_gps/permgroup.py
+++ b/src/sage/groups/perm_gps/permgroup.py
@@ -344,6 +344,7 @@ def PermutationGroup(gens=None, *args, **kwds):
Note that we provide generators for the acting group. The
permutation group we construct is its homomorphic image::
+ sage: # needs sage.combinat
sage: a = lambda g, x: vector(g*x, immutable=True)
sage: X = [vector(x, immutable=True) for x in GF(3)^2]
sage: G = SL(2,3); G.gens()
@@ -1365,6 +1366,38 @@ def ngens(self):
"""
return len(self.gens())
+ def is_trivial(self):
+ r"""
+ Return ``True`` if this group is the trivial group.
+
+ A permutation group is trivial, if it consists only of the
+ identity element, that is, if it has no generators or only
+ trivial generators.
+
+ EXAMPLES::
+
+ sage: G = PermutationGroup([], domain=["a", "b", "c"])
+ sage: G.is_trivial()
+ True
+ sage: SymmetricGroup(0).is_trivial()
+ True
+ sage: SymmetricGroup(1).is_trivial()
+ True
+ sage: SymmetricGroup(2).is_trivial()
+ False
+ sage: DihedralGroup(1).is_trivial()
+ False
+
+ TESTS::
+
+ sage: G = PermutationGroup([[], []], canonicalize=False)
+ sage: G.ngens()
+ 2
+ sage: G.is_trivial()
+ True
+ """
+ return all(g.is_one() for g in self._gens)
+
@cached_method
def one(self):
"""
@@ -1547,6 +1580,7 @@ def representative_action(self,x,y):
TESTS::
+ sage: # needs sage.graphs
sage: g = graphs.PetersenGraph()
sage: g.relabel(list("abcdefghik"))
sage: g.vertices(sort=True)
@@ -2337,13 +2371,14 @@ def socle(self):
EXAMPLES::
sage: G = SymmetricGroup(4)
- sage: G.socle()
+ sage: s = G.socle(); s
Subgroup generated by [(1,2)(3,4), (1,4)(2,3)] of
(Symmetric group of order 4! as a permutation group)
- sage: G.socle().socle()
- Subgroup generated by [(1,2)(3,4), (1,4)(2,3)] of
- (Subgroup generated by [(1,2)(3,4), (1,4)(2,3)]
- of (Symmetric group of order 4! as a permutation group))
+
+ The socle of the socle is, essentially, the socle::
+
+ sage: s.socle() == s.subgroup(s.gens())
+ True
"""
return self.subgroup(gap_group=self._gap_().Socle())
@@ -3706,6 +3741,7 @@ def has_regular_subgroup(self, return_group=False):
But the automorphism group of Petersen's graph does not::
+ sage: # needs sage.graphs
sage: G = graphs.PetersenGraph().automorphism_group()
sage: G.has_regular_subgroup()
False
@@ -3767,6 +3803,7 @@ def blocks_all(self, representatives=True):
Picking an interesting group::
+ sage: # needs sage.graphs
sage: g = graphs.DodecahedralGraph()
sage: g.is_vertex_transitive()
True
@@ -3776,12 +3813,12 @@ def blocks_all(self, representatives=True):
Computing its blocks representatives::
- sage: ag.blocks_all()
+ sage: ag.blocks_all() # needs sage.graphs
[[0, 15]]
Now the full block::
- sage: sorted(ag.blocks_all(representatives = False)[0])
+ sage: sorted(ag.blocks_all(representatives=False)[0]) # needs sage.graphs
[[0, 15], [1, 16], [2, 12], [3, 13], [4, 9],
[5, 10], [6, 11], [7, 18], [8, 17], [14, 19]]
@@ -3977,6 +4014,7 @@ def minimal_generating_set(self):
EXAMPLES::
+ sage: # needs sage.graphs
sage: g = graphs.CompleteGraph(4)
sage: g.relabel(['a','b','c','d'])
sage: mgs = g.automorphism_group().minimal_generating_set(); len(mgs)
diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py
index 0a8087a20d5..26afa44edda 100644
--- a/src/sage/interfaces/gap.py
+++ b/src/sage/interfaces/gap.py
@@ -177,7 +177,7 @@
gap(...), x.[tab], and docs, e.g., gap.function? and x.function?
"""
-#*****************************************************************************
+# ****************************************************************************
# Copyright (C) 2005 William Stein
#
# Distributed under the terms of the GNU General Public License (GPL)
@@ -189,13 +189,13 @@
#
# The full text of the GPL is available at:
#
-# http://www.gnu.org/licenses/
-#*****************************************************************************
+# https://www.gnu.org/licenses/
+# ****************************************************************************
from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction
from .gap_workspace import gap_workspace_file, prepare_workspace_dir
from sage.cpython.string import bytes_to_str
-from sage.env import SAGE_EXTCODE, SAGE_GAP_COMMAND, SAGE_GAP_MEMORY
+from sage.env import SAGE_EXTCODE, SAGE_GAP_COMMAND, SAGE_GAP_MEMORY, GAP_ROOT_PATHS
from sage.misc.misc import is_in_string
from sage.misc.cachefunc import cached_method
from sage.misc.instancedoc import instancedoc
@@ -217,7 +217,17 @@
first_try = True
-gap_cmd = SAGE_GAP_COMMAND
+if SAGE_GAP_COMMAND is None:
+ # Passing -A allows us to use a minimal GAP installation without
+ # producing errors at start-up. The files sage.g and sage.gaprc are
+ # used to load any additional packages that may be available.
+ gap_cmd = f'gap -A -l "{GAP_ROOT_PATHS}"'
+ if SAGE_GAP_MEMORY is not None:
+ gap_cmd += " -s " + SAGE_GAP_MEMORY + " -o " + SAGE_GAP_MEMORY
+else:
+ gap_cmd = SAGE_GAP_COMMAND
+
+
if platform.processor() == 'ia64' and os.path.exists('/usr/bin/prctl'):
# suppress unaligned access to 0x..., ip=0x... warnings
gap_cmd = 'prctl --unaligned=silent ' + gap_cmd
@@ -234,7 +244,7 @@ def gap_command(use_workspace_cache=True, local=True):
return gap_cmd, True
-############ Classes with methods for both the GAP3 and GAP4 interface
+# ########### Classes with methods for both the GAP3 and GAP4 interface
class Gap_generic(ExtraTabCompletion, Expect):
r"""
@@ -277,10 +287,10 @@ def _synchronize(self, timeout=0.5, cmd='%s;'):
E = self._expect
from sage.misc.prandom import randrange
rnd = randrange(2147483647)
- cmd = str(rnd)+';'
+ cmd = str(rnd) + ';'
try:
E.sendline(cmd)
- E.expect(r'@[nf][@J\s>]*'+str(rnd), timeout=timeout)
+ E.expect(r'@[nf][@J\s>]*' + str(rnd), timeout=timeout)
E.send(' ')
E.expect('@i', timeout=timeout)
except pexpect.TIMEOUT:
@@ -443,7 +453,7 @@ def load_package(self, pkg, verbose=False):
print("Loading GAP package {}".format(pkg))
x = self.eval('LoadPackage("{}")'.format(pkg))
if x == 'fail':
- raise RuntimeError("Error loading Gap package "+str(pkg)+". " +
+ raise RuntimeError("Error loading Gap package " + str(pkg) + ". " +
"You may want to install gap_packages SPKG.")
def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds):
@@ -497,10 +507,10 @@ def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds):
' -\n\\\\-'
"""
# '"
- #We remove all of the comments: On each line, we try
- #to find a pound sign. If we find it, we check to see if
- #it is occurring in a string. If it is not in a string, we
- #strip off the comment.
+ # We remove all of the comments: On each line, we try
+ # to find a pound sign. If we find it, we check to see if
+ # it is occurring in a string. If it is not in a string, we
+ # strip off the comment.
if not split_lines:
input_line = str(x)
else:
@@ -510,17 +520,17 @@ def eval(self, x, newlines=False, strip=True, split_lines=True, **kwds):
while pound_position != -1:
if not is_in_string(line, pound_position):
line = line[:pound_position]
- pound_position = line.find('#',pound_position+1)
- input_line += " "+line
+ pound_position = line.find('#', pound_position + 1)
+ input_line += " " + line
if not input_line.endswith(';'):
input_line += ';'
result = Expect.eval(self, input_line, **kwds)
if not newlines:
- result = result.replace("\\\n","")
+ result = result.replace("\\\n", "")
return result.rstrip()
def _execute_line(self, line, wait_for_prompt=True, expect_eof=False):
- if self._expect is None: # interface is down
+ if self._expect is None: # interface is down
self._start()
E = self._expect
try:
@@ -530,9 +540,9 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False):
except OSError:
raise RuntimeError("Error evaluating %s in %s" % (line, self))
if not wait_for_prompt:
- return (b'',b'')
+ return (b'', b'')
if len(line) == 0:
- return (b'',b'')
+ return (b'', b'')
try:
terminal_echo = [] # to be discarded
normal_outputs = [] # GAP stdout
@@ -546,43 +556,43 @@ def _execute_line(self, line, wait_for_prompt=True, expect_eof=False):
warnings.warn(
"possibly wrong version of GAP package "
"interface. Crossing fingers and continuing.")
- elif x == 1: #@@
+ elif x == 1: # @@
current_outputs.append(b'@')
- elif x == 2: #special char
+ elif x == 2: # special char
c = ord(E.after[1:2]) - ord(b'A') + 1
s = bytes([c])
current_outputs.append(s)
- elif x == 3: # garbage collection info, ignore
+ elif x == 3: # garbage collection info, ignore
pass
- elif x == 4: # @e -- break loop
+ elif x == 4: # @e -- break loop
E.sendline("quit;")
- elif x == 5: # @c completion, doesn't seem to happen when -p is in use
+ elif x == 5: # @c completion, doesn't seem to happen when -p is in use
warnings.warn("I didn't think GAP could do this")
- elif x == 6: # @f GAP error message
+ elif x == 6: # @f GAP error message
current_outputs = error_outputs
- elif x == 7: # @h help text, but this stopped happening with new help
+ elif x == 7: # @h help text, but this stopped happening with new help
warnings.warn("I didn't think GAP could do this")
- elif x == 8: # @i awaiting normal input
+ elif x == 8: # @i awaiting normal input
break
- elif x == 9: # @m finished running a child
+ elif x == 9: # @m finished running a child
pass # there is no need to do anything
- elif x == 10: #@n normal output line
+ elif x == 10: # @n normal output line
current_outputs = normal_outputs
- elif x == 11: #@r echoing input
+ elif x == 11: # @r echoing input
current_outputs = terminal_echo
- elif x == 12: #@sN shouldn't happen
+ elif x == 12: # @sN shouldn't happen
warnings.warn("this should never happen")
- elif x == 13: #@w GAP is trying to send a Window command
+ elif x == 13: # @w GAP is trying to send a Window command
warnings.warn("this should never happen")
- elif x == 14: #@x seems to be safely ignorable
+ elif x == 14: # @x seems to be safely ignorable
pass
- elif x == 15:#@z GAP starting a subprocess
+ elif x == 15: # @z GAP starting a subprocess
pass # there is no need to do anything
except pexpect.EOF:
if not expect_eof:
- raise RuntimeError("Unexpected EOF from %s executing %s" % (self,line))
+ raise RuntimeError("Unexpected EOF from %s executing %s" % (self, line))
except IOError:
- raise RuntimeError("IO Error from %s executing %s" % (self,line))
+ raise RuntimeError("IO Error from %s executing %s" % (self, line))
return (b"".join(normal_outputs), b"".join(error_outputs))
def _keyboard_interrupt(self):
@@ -687,8 +697,8 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if
error += "\nRunning gap_reset_workspace()..."
self.quit()
gap_reset_workspace()
- error = error.replace('\r','')
- raise RuntimeError("%s produced error output\n%s\n executing %s" % (self, error,line))
+ error = error.replace('\r', '')
+ raise RuntimeError("%s produced error output\n%s\n executing %s" % (self, error, line))
if not normal:
return ''
@@ -762,7 +772,7 @@ def _contains(self, v1, v2):
sage: 2 in gap('Integers')
True
"""
- return self.eval('%s in %s' % (v1,v2)) == "true"
+ return self.eval('%s in %s' % (v1, v2)) == "true"
def _true_symbol(self):
"""
@@ -858,20 +868,21 @@ def function_call(self, function, args=None, kwds=None):
args, kwds = self._convert_args_kwds(args, kwds)
self._check_valid_function_name(function)
- #Here we have to do some magic because not all GAP
- #functions return a value. If you try to store their
- #results to a variable, then GAP will complain. Thus, before
- #we evaluate the function, we make it so that the marker string
- #is in the 'last' variable in GAP. If the function returns a
- #value, then that value will be in 'last', otherwise it will
- #be the marker.
+ # Here we have to do some magic because not all GAP
+ # functions return a value. If you try to store their
+ # results to a variable, then GAP will complain. Thus, before
+ # we evaluate the function, we make it so that the marker string
+ # is in the 'last' variable in GAP. If the function returns a
+ # value, then that value will be in 'last', otherwise it will
+ # be the marker.
marker = '__SAGE_LAST__:="__SAGE_LAST__";;'
cmd = "%s(%s);;" % (function, ",".join([s.name() for s in args] +
- ['%s=%s' % (key,value.name()) for key, value in kwds.items()]))
+ [f'{key}={value.name()}'
+ for key, value in kwds.items()]))
if len(marker) + len(cmd) <= self._eval_using_file_cutoff:
# We combine the two commands so we only run eval() once and the
# only output would be from the second command
- res = self.eval(marker+cmd)
+ res = self.eval(marker + cmd)
else:
self.eval(marker)
res = self.eval(cmd)
@@ -1035,7 +1046,8 @@ def _matrix_(self, R):
from sage.matrix.matrix_space import MatrixSpace
M = MatrixSpace(R, n, m)
- entries = [[R(self[r,c]) for c in range(1,m+1)] for r in range(1,n+1)]
+ entries = [[R(self[r, c]) for c in range(1, m + 1)]
+ for r in range(1, n + 1)]
return M(entries)
@@ -1089,7 +1101,7 @@ def __init__(self, max_workspace_size=None,
self.__seq = 0
self._seed = seed
- def set_seed(self,seed=None):
+ def set_seed(self, seed=None):
"""
Set the seed for gap interpreter.
@@ -1194,8 +1206,8 @@ def _start(self):
self.save_workspace()
# Now, as self._expect exists, we can compile some useful pattern:
self._compiled_full_pattern = self._expect.compile_pattern_list([
- r'@p\d+\.','@@','@[A-Z]',r'@[123456!"#$%&][^+]*\+',
- '@e','@c','@f','@h','@i','@m','@n','@r',r'@s\d',r'@w.*\+','@x','@z'])
+ r'@p\d+\.', '@@', '@[A-Z]', r'@[123456!"#$%&][^+]*\+',
+ '@e', '@c', '@f', '@h', '@i', '@m', '@n', '@r', r'@s\d', r'@w.*\+', '@x', '@z'])
# read everything up to the first "ready" prompt
self._expect.expect("@i")
@@ -1236,10 +1248,9 @@ def cputime(self, t=None):
"""
if t is not None:
return self.cputime() - t
- else:
- self.eval('_r_ := Runtimes();')
- r = sum(eval(self.eval('[_r_.user_time, _r_.system_time, _r_.user_time_children, _r_.system_time_children]')))
- return r/1000.0
+ self.eval('_r_ := Runtimes();')
+ r = sum(eval(self.eval('[_r_.user_time, _r_.system_time, _r_.user_time_children, _r_.system_time_children]')))
+ return r / 1000.0
def save_workspace(self):
r"""
@@ -1339,7 +1350,7 @@ def set(self, var, value):
sage: gap.get('x')
'2'
"""
- cmd = ('%s:=%s;;' % (var, value)).replace('\n','')
+ cmd = ('%s:=%s;;' % (var, value)).replace('\n', '')
self._eval_line(cmd, allow_use_file=True)
def get(self, var, use_file=False):
@@ -1356,10 +1367,10 @@ def get(self, var, use_file=False):
tmp = self._local_tmpfile()
if os.path.exists(tmp):
os.unlink(tmp)
- self.eval('PrintTo("%s", %s);' % (tmp,var), strip=False)
+ self.eval('PrintTo("%s", %s);' % (tmp, var), strip=False)
with open(tmp) as f:
r = f.read()
- r = r.strip().replace("\\\n","")
+ r = r.strip().replace("\\\n", "")
os.unlink(tmp)
return r
else:
@@ -1470,7 +1481,7 @@ def _tab_completion(self):
True
"""
names = eval(self.eval('NamesSystemGVars()')) + \
- eval(self.eval('NamesUserGVars()'))
+ eval(self.eval('NamesUserGVars()'))
return [n for n in names if n[0] in string.ascii_letters]
@@ -1498,20 +1509,21 @@ def gap_reset_workspace(max_workspace_size=None, verbose=False):
We temporarily need to change the worksheet filename, and to set
``first_try=True`` to ensure that the new workspace is created::
+ sage: # long time
sage: ORIGINAL_WORKSPACE = sage.interfaces.gap.WORKSPACE
sage: saved_first_try = sage.interfaces.gap.first_try
sage: sage.interfaces.gap.first_try = True
sage: sage.interfaces.gap.WORKSPACE = tmp_filename()
sage: from multiprocessing import Process
sage: import time
- sage: gap = Gap() # long time (reset GAP session)
+ sage: gap = Gap() # reset GAP session
sage: P = [Process(target=gap, args=("14242",)) for i in range(4)]
- sage: for p in P: # long time, indirect doctest
+ sage: for p in P: # indirect doctest
....: p.start()
....: time.sleep(float(0.2))
- sage: for p in P: # long time
+ sage: for p in P:
....: p.join()
- sage: os.unlink(sage.interfaces.gap.WORKSPACE) # long time
+ sage: os.unlink(sage.interfaces.gap.WORKSPACE)
sage: sage.interfaces.gap.WORKSPACE = ORIGINAL_WORKSPACE
sage: sage.interfaces.gap.first_try = saved_first_try
"""
@@ -1572,8 +1584,8 @@ def _latex_(self):
P = self._check_valid()
try:
s = P.eval('LaTeXObj(%s)' % self.name())
- s = s.replace('\\\\','\\').replace('"','')
- s = s.replace('%\\n',' ')
+ s = s.replace('\\\\', '\\').replace('"', '')
+ s = s.replace('%\\n', ' ')
return s
except RuntimeError:
return str(self)
@@ -1581,7 +1593,7 @@ def _latex_(self):
@cached_method
def _tab_completion(self):
"""
- Return additional tab completion entries
+ Return additional tab completion entries.
OUTPUT:
@@ -1595,10 +1607,11 @@ def _tab_completion(self):
"""
P = self.parent()
v = P.eval(r'\$SAGE.OperationsAdmittingFirstArgument(%s)' % self.name())
- v = v.replace('Tester(','').replace('Setter(','').replace(')','').replace('\n', '')
+ v = v.replace('Tester(', '').replace('Setter(', '').replace(')', '').replace('\n', '')
v = v.split(',')
- v = [ oper.split('"')[1] for oper in v ]
- v = [ oper for oper in v if all(ch in string.ascii_letters for ch in oper) ]
+ v = (oper.split('"')[1] for oper in v)
+ v = [oper for oper in v
+ if all(ch in string.ascii_letters for ch in oper)]
return sorted(set(v))
@@ -1708,13 +1721,13 @@ def gfq_gap_to_sage(x, F):
return F(0)
i1 = s.index("(")
i2 = s.index(")")
- q = eval(s[i1+1:i2].replace('^','**'))
+ q = eval(s[i1 + 1:i2].replace('^', '**'))
if not F.cardinality().is_power_of(q):
raise ValueError('%r has no subfield of size %r' % (F, q))
if s.find(')^') == -1:
e = 1
else:
- e = int(s[i2+2:])
+ e = int(s[i2 + 2:])
if F.degree() == 1:
g = F(gap.eval('Int(Z(%s))' % q))
elif F.is_conway():
@@ -1724,6 +1737,7 @@ def gfq_gap_to_sage(x, F):
raise ValueError('%r is not prime or defined by a Conway polynomial' % F)
return g**e
+
def intmod_gap_to_sage(x):
r"""
INPUT:
@@ -1827,5 +1841,5 @@ def gap_console():
if not get_display_manager().is_in_terminal():
raise RuntimeError('Can use the console only in the terminal. Try %%gap magics instead.')
cmd, _ = gap_command(use_workspace_cache=False)
- cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','console.g')
+ cmd += ' ' + os.path.join(SAGE_EXTCODE, 'gap', 'console.g')
os.system(cmd)
diff --git a/src/sage/interfaces/gap_workspace.py b/src/sage/interfaces/gap_workspace.py
index c104664143a..a2c97260077 100644
--- a/src/sage/interfaces/gap_workspace.py
+++ b/src/sage/interfaces/gap_workspace.py
@@ -17,7 +17,7 @@
import time
import hashlib
import subprocess
-from sage.env import DOT_SAGE, HOSTNAME, GAP_LIB_DIR, GAP_SHARE_DIR
+from sage.env import DOT_SAGE, HOSTNAME, GAP_ROOT_PATHS
def gap_workspace_file(system="gap", name="workspace", dir=None):
@@ -60,8 +60,12 @@ def gap_workspace_file(system="gap", name="workspace", dir=None):
if dir is None:
dir = os.path.join(DOT_SAGE, 'gap')
- data = f'{GAP_LIB_DIR}:{GAP_SHARE_DIR}'
- for path in GAP_LIB_DIR, GAP_SHARE_DIR:
+ data = f'{GAP_ROOT_PATHS}'
+ for path in GAP_ROOT_PATHS.split(";"):
+ if not path:
+ # If GAP_ROOT_PATHS begins or ends with a semicolon,
+ # we'll get one empty path.
+ continue
sysinfo = os.path.join(path, "sysinfo.gap")
if os.path.exists(sysinfo):
data += subprocess.getoutput(f'. "{sysinfo}" && echo ":$GAP_VERSION:$GAParch"')
diff --git a/src/sage/interfaces/jmoldata.py b/src/sage/interfaces/jmoldata.py
index 59a9a1e26a5..e7354e05c70 100644
--- a/src/sage/interfaces/jmoldata.py
+++ b/src/sage/interfaces/jmoldata.py
@@ -85,10 +85,6 @@ def jmolpath(self):
"""
jmolpath = os.path.join(JMOL_DIR, "JmolData.jar")
- if sys.platform == 'cygwin':
- import cygwin
- jmolpath = cygwin.cygpath(jmolpath, 'w')
-
return jmolpath
def is_jmol_available(self):
@@ -129,8 +125,6 @@ def export_image(self,
- datafile -- full path to the data file Jmol can read or
text of a script telling Jmol what to read or load.
- If it is a script and the platform is cygwin, the filenames in
- the script should be in native windows format.
- datafile_cmd -- (default ``'script'``) ``'load'`` or ``'script'``
should be ``"load"`` for a data file.
@@ -180,10 +174,6 @@ def export_image(self,
sage: archive = NamedTemporaryFile(suffix=".zip")
sage: D.export_jmol(archive.name) # needs sage.plot
sage: archive_native = archive.name
- sage: import sys
- sage: if sys.platform == 'cygwin':
- ....: import cygwin
- ....: archive_native = cygwin.cygpath(archive_native, 'w')
sage: script = f'set defaultdirectory "f{archive_native}"\n'
sage: script += 'script SCRIPT\n'
sage: with NamedTemporaryFile(suffix=".png") as testfile: # optional - java, needs sage.plot
@@ -198,12 +188,6 @@ def export_image(self,
jmolpath = self.jmolpath()
target_native = targetfile
- if sys.platform == 'cygwin':
- import cygwin
- target_native = cygwin.cygpath(target_native, 'w')
- if datafile_cmd != 'script':
- datafile = cygwin.cygpath(datafile, 'w')
-
launchscript = ""
if (datafile_cmd != 'script'):
launchscript = "load "
diff --git a/src/sage/interfaces/kenzo.py b/src/sage/interfaces/kenzo.py
index e403b04dab7..793b8f257f7 100644
--- a/src/sage/interfaces/kenzo.py
+++ b/src/sage/interfaces/kenzo.py
@@ -131,11 +131,12 @@ def Sphere(n):
EXAMPLES::
- sage: from sage.interfaces.kenzo import Sphere # optional - kenzo
- sage: s2 = Sphere(2) # optional - kenzo
- sage: s2 # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import Sphere
+ sage: s2 = Sphere(2)
+ sage: s2
[K1 Simplicial-Set]
- sage: [s2.homology(i) for i in range(8)] # optional - kenzo
+ sage: [s2.homology(i) for i in range(8)]
[Z, 0, Z, 0, 0, 0, 0, 0]
"""
kenzosphere = __sphere__(n)
@@ -162,11 +163,12 @@ def MooreSpace(m, n):
EXAMPLES::
- sage: from sage.interfaces.kenzo import MooreSpace # optional - kenzo
- sage: m24 = MooreSpace(2,4) # optional - kenzo
- sage: m24 # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import MooreSpace
+ sage: m24 = MooreSpace(2,4)
+ sage: m24
[K10 Simplicial-Set]
- sage: [m24.homology(i) for i in range(8)] # optional - kenzo
+ sage: [m24.homology(i) for i in range(8)]
[Z, 0, 0, 0, C2, 0, 0, 0]
"""
kenzomoore = __moore__(m, n)
@@ -387,10 +389,11 @@ def table(self, p, i1, i2, j1, j2):
EXAMPLES::
- sage: from sage.interfaces.kenzo import Sphere # optional - kenzo
- sage: S2 = Sphere(2) # optional - kenzo
- sage: EMS = S2.em_spectral_sequence() # optional - kenzo
- sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import Sphere
+ sage: S2 = Sphere(2)
+ sage: EMS = S2.em_spectral_sequence()
+ sage: EMS.table(0, -2, 2, -2, 2)
0 Z 0 0 0
0 0 0 0 0
0 0 Z 0 0
@@ -431,11 +434,12 @@ def homology(self, n):
EXAMPLES::
- sage: from sage.interfaces.kenzo import Sphere # optional - kenzo
- sage: s2 = Sphere(2) # optional - kenzo
- sage: s2 # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import Sphere
+ sage: s2 = Sphere(2)
+ sage: s2
[K1 Simplicial-Set]
- sage: s2.homology(2) # optional - kenzo
+ sage: s2.homology(2)
Z
"""
echcm1 = __echcm__(self._kenzo)
@@ -490,15 +494,16 @@ def basis(self, dim):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: for i in range(6): # optional - kenzo
+ sage: for i in range(6):
....: print("Basis in dimension %i: %s" % (i, kenzo_chcm.basis(i)))
Basis in dimension 0: ['G0G0', 'G0G1', 'G0G2']
Basis in dimension 1: ['G1G0', 'G1G1']
@@ -506,7 +511,6 @@ def basis(self, dim):
Basis in dimension 3: ['G3G0', 'G3G1']
Basis in dimension 4: ['G4G0', 'G4G1']
Basis in dimension 5: ['G5G0', 'G5G1', 'G5G2']
-
"""
return __basis_aux1__(self._kenzo, dim).python()
@@ -603,26 +607,27 @@ def differential(self, dim=None, comb=None):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: kenzo_chcm.basis(4) # optional - kenzo
+ sage: kenzo_chcm.basis(4)
['G4G0', 'G4G1']
- sage: kenzo_chcm.differential(4, [1, 'G4G0']) # optional - kenzo
+ sage: kenzo_chcm.differential(4, [1, 'G4G0'])
----------------------------------------------------------------------{CMBN 3}
<1 * G3G0>
<3 * G3G1>
------------------------------------------------------------------------------
- sage: kenzo_chcm.basis(5) # optional - kenzo
+ sage: kenzo_chcm.basis(5)
['G5G0', 'G5G1', 'G5G2']
- sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 4}
<6 * G4G0>
@@ -748,10 +753,11 @@ def homotopy_group(self, n):
EXAMPLES::
- sage: from sage.interfaces.kenzo import Sphere # optional - kenzo
- sage: s2 = Sphere(2) # optional - kenzo
- sage: p = s2.cartesian_product(s2) # optional - kenzo
- sage: p.homotopy_group(3) # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import Sphere
+ sage: s2 = Sphere(2)
+ sage: p = s2.cartesian_product(s2)
+ sage: p.homotopy_group(3)
Multiplicative Abelian group isomorphic to Z x Z
@@ -780,10 +786,11 @@ def em_spectral_sequence(self):
EXAMPLES::
- sage: from sage.interfaces.kenzo import Sphere # optional - kenzo
- sage: S2 = Sphere(2) # optional - kenzo
- sage: EMS = S2.em_spectral_sequence() # optional - kenzo
- sage: EMS.table(0, -2, 2, -2, 2) # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import Sphere
+ sage: S2 = Sphere(2)
+ sage: EMS = S2.em_spectral_sequence()
+ sage: EMS.table(0, -2, 2, -2, 2)
0 Z 0 0 0
0 0 0 0 0
0 0 Z 0 0
@@ -1090,15 +1097,16 @@ def KChainComplex(chain_complex):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: kenzo_chcm.homology(5) # optional - kenzo
+ sage: kenzo_chcm.homology(5)
Z x Z
"""
d = chain_complex.differential()
@@ -1242,8 +1250,9 @@ def KFiniteSimplicialSet(sset):
EXAMPLES::
+ sage: # optional - kenzo
sage: from sage.topology.simplicial_set import AbstractSimplex, SimplicialSet
- sage: from sage.interfaces.kenzo import KFiniteSimplicialSet # optional - kenzo
+ sage: from sage.interfaces.kenzo import KFiniteSimplicialSet
sage: s0 = AbstractSimplex(0, name='s0')
sage: s1 = AbstractSimplex(0, name='s1')
sage: s2 = AbstractSimplex(0, name='s2')
@@ -1253,15 +1262,15 @@ def KFiniteSimplicialSet(sset):
sage: s012 = AbstractSimplex(2, name='s012')
sage: Triangle = SimplicialSet({s01: (s1, s0),\
....: s02: (s2, s0), s12: (s2, s1)}, base_point = s0)
- sage: KTriangle = KFiniteSimplicialSet(Triangle) # optional - kenzo
- sage: KTriangle.homology(1) # optional - kenzo
+ sage: KTriangle = KFiniteSimplicialSet(Triangle)
+ sage: KTriangle.homology(1)
Z
- sage: KTriangle.basis(1) # optional - kenzo
+ sage: KTriangle.basis(1)
['CELL_1_0', 'CELL_1_1', 'CELL_1_2']
sage: S1 = simplicial_sets.Sphere(1)
sage: S3 = simplicial_sets.Sphere(3)
- sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3)) # optional - kenzo
- sage: KS1vS3.homology(3) # optional - kenzo
+ sage: KS1vS3 = KFiniteSimplicialSet(S1.wedge(S3))
+ sage: KS1vS3.homology(3)
Z
"""
from sage.topology.simplicial_set_constructions import ProductOfSimplicialSets
@@ -1389,18 +1398,19 @@ def source_complex(self):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo
- sage: differential_morphism # optional - kenzo
+ sage: differential_morphism = kenzo_chcm.differential()
+ sage: differential_morphism
[K... Morphism (degree -1): K... -> K...]
- sage: differential_morphism.source_complex() # optional - kenzo
+ sage: differential_morphism.source_complex()
[K... Chain-Complex]
"""
return KenzoChainComplex(__sorc_aux__(self._kenzo))
@@ -1415,18 +1425,19 @@ def target_complex(self):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo
- sage: differential_morphism # optional - kenzo
+ sage: differential_morphism = kenzo_chcm.differential()
+ sage: differential_morphism
[K... Morphism (degree -1): K... -> K...]
- sage: differential_morphism.target_complex() # optional - kenzo
+ sage: differential_morphism.target_complex()
[K... Chain-Complex]
"""
return KenzoChainComplex(__trgt_aux__(self._kenzo))
@@ -1441,22 +1452,23 @@ def degree(self):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree=-1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo
- sage: differential_morphism # optional - kenzo
+ sage: differential_morphism = kenzo_chcm.differential()
+ sage: differential_morphism
[K... Morphism (degree -1): K... -> K...]
- sage: differential_morphism.degree() # optional - kenzo
+ sage: differential_morphism.degree()
-1
- sage: differential_morphism.composite(differential_morphism).degree() # optional - kenzo
+ sage: differential_morphism.composite(differential_morphism).degree()
-2
- sage: kenzo_chcm.null_morphism().degree() # optional - kenzo
+ sage: kenzo_chcm.null_morphism().degree()
0
"""
return __degr_aux__(self._kenzo).python()
@@ -1485,51 +1497,52 @@ def evaluation(self, dim, comb):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: differential_morphism = kenzo_chcm.differential() # optional - kenzo
- sage: differential_morphism # optional - kenzo
+ sage: differential_morphism = kenzo_chcm.differential()
+ sage: differential_morphism
[K... Morphism (degree -1): K... -> K...]
- sage: dif_squared = differential_morphism.composite(differential_morphism) # optional - kenzo
- sage: dif_squared # optional - kenzo
+ sage: dif_squared = differential_morphism.composite(differential_morphism)
+ sage: dif_squared
[K... Morphism (degree -2): K... -> K...]
- sage: kenzo_chcm.basis(5) # optional - kenzo
+ sage: kenzo_chcm.basis(5)
['G5G0', 'G5G1', 'G5G2']
- sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: kenzo_chcm.differential(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 4}
<6 * G4G0>
<-3 * G4G1>
------------------------------------------------------------------------------
- sage: differential_morphism.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: differential_morphism.evaluation(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 4}
<6 * G4G0>
<-3 * G4G1>
------------------------------------------------------------------------------
- sage: dif_squared.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: dif_squared.evaluation(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 3}
------------------------------------------------------------------------------
- sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo
- sage: idx2 = idnt.sum(idnt) # optional - kenzo
- sage: idnt.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: idnt = kenzo_chcm.identity_morphism()
+ sage: idx2 = idnt.sum(idnt)
+ sage: idnt.evaluation(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 5}
<1 * G5G0>
<2 * G5G2>
------------------------------------------------------------------------------
- sage: idx2.evaluation(5, [1, 'G5G0', 2, 'G5G2']) # optional - kenzo
+ sage: idx2.evaluation(5, [1, 'G5G0', 2, 'G5G2'])
----------------------------------------------------------------------{CMBN 5}
<2 * G5G0>
@@ -1556,30 +1569,31 @@ def opposite(self):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo
- sage: idnt # optional - kenzo
+ sage: idnt = kenzo_chcm.identity_morphism()
+ sage: idnt
[K... Morphism (degree 0): K... -> K...]
- sage: opps_id = idnt.opposite() # optional - kenzo
- sage: opps_id # optional - kenzo
+ sage: opps_id = idnt.opposite()
+ sage: opps_id
[K... Morphism (degree 0): K... -> K...]
- sage: kenzo_chcm.basis(4) # optional - kenzo
+ sage: kenzo_chcm.basis(4)
['G4G0', 'G4G1']
- sage: idnt.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: idnt.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<2 * G4G0>
<-5 * G4G1>
------------------------------------------------------------------------------
- sage: opps_id.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: opps_id.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<-2 * G4G0>
@@ -1653,36 +1667,37 @@ def sum(self, object=None):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo
- sage: idnt # optional - kenzo
+ sage: idnt = kenzo_chcm.identity_morphism()
+ sage: idnt
[K... Morphism (degree 0): K... -> K...]
- sage: opps_id = idnt.opposite() # optional - kenzo
- sage: opps_id # optional - kenzo
+ sage: opps_id = idnt.opposite()
+ sage: opps_id
[K... Morphism (degree 0): K... -> K...]
- sage: null = kenzo_chcm.null_morphism() # optional - kenzo
- sage: null # optional - kenzo
+ sage: null = kenzo_chcm.null_morphism()
+ sage: null
[K... Morphism (degree 0): K... -> K...]
- sage: idx2 = idnt.sum(idnt) # optional - kenzo
- sage: idx5 = idx2.sum( # optional - kenzo
+ sage: idx2 = idnt.sum(idnt)
+ sage: idx5 = idx2.sum(
....: (opps_id, idnt, idnt, null, idx2.sum(idnt), opps_id))
- sage: kenzo_chcm.basis(4) # optional - kenzo
+ sage: kenzo_chcm.basis(4)
['G4G0', 'G4G1']
- sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<4 * G4G0>
<-10 * G4G1>
------------------------------------------------------------------------------
- sage: idx5.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: idx5.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<10 * G4G0>
@@ -1719,36 +1734,37 @@ def substract(self, object=None):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplex # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplex
sage: m1 = matrix(ZZ, 3, 2, [-1, 1, 3, -4, 5, 6])
sage: m4 = matrix(ZZ, 2, 2, [1, 2, 3, 6])
sage: m5 = matrix(ZZ, 2, 3, [2, 2, 2, -1, -1, -1])
- sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1) # optional - kenzo
- sage: kenzo_chcm = KChainComplex(sage_chcm) # optional - kenzo
- sage: kenzo_chcm # optional - kenzo
+ sage: sage_chcm = ChainComplex({1: m1, 4: m4, 5: m5}, degree = -1)
+ sage: kenzo_chcm = KChainComplex(sage_chcm)
+ sage: kenzo_chcm
[K... Chain-Complex]
- sage: idnt = kenzo_chcm.identity_morphism() # optional - kenzo
- sage: idnt # optional - kenzo
+ sage: idnt = kenzo_chcm.identity_morphism()
+ sage: idnt
[K... Morphism (degree 0): K... -> K...]
- sage: opps_id = idnt.opposite() # optional - kenzo
- sage: opps_id # optional - kenzo
+ sage: opps_id = idnt.opposite()
+ sage: opps_id
[K... Morphism (degree 0): K... -> K...]
- sage: null = kenzo_chcm.null_morphism() # optional - kenzo
- sage: null # optional - kenzo
+ sage: null = kenzo_chcm.null_morphism()
+ sage: null
[K... Morphism (degree 0): K... -> K...]
- sage: idx2 = idnt.substract(opps_id) # optional - kenzo
- sage: opps_idx2 = idx2.substract( # optional - kenzo
+ sage: idx2 = idnt.substract(opps_id)
+ sage: opps_idx2 = idx2.substract(
....: (opps_id, idnt, idnt, null, idx2.substract(opps_id)))
- sage: kenzo_chcm.basis(4) # optional - kenzo
+ sage: kenzo_chcm.basis(4)
['G4G0', 'G4G1']
- sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<4 * G4G0>
<-10 * G4G1>
------------------------------------------------------------------------------
- sage: opps_idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1']) # optional - kenzo
+ sage: opps_idx2.evaluation(4, [2, 'G4G0', -5, 'G4G1'])
----------------------------------------------------------------------{CMBN 4}
<-4 * G4G0>
@@ -1954,16 +1970,16 @@ def KChainComplexMorphism(morphism):
EXAMPLES::
- sage: from sage.interfaces.kenzo import KChainComplexMorphism # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import KChainComplexMorphism
sage: C = ChainComplex({0: identity_matrix(ZZ, 1)})
sage: D = ChainComplex({0: zero_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)})
sage: f = Hom(C,D)({0: identity_matrix(ZZ, 1), 1: zero_matrix(ZZ, 1)})
- sage: g = KChainComplexMorphism(f) # optional - kenzo
- sage: g # optional - kenzo
+ sage: g = KChainComplexMorphism(f); g
[K... Morphism (degree 0): K... -> K...]
- sage: g.source_complex() # optional - kenzo
+ sage: g.source_complex()
[K... Chain-Complex]
- sage: g.target_complex() # optional - kenzo
+ sage: g.target_complex()
[K... Chain-Complex]
"""
source = KChainComplex(morphism.domain())
@@ -2017,21 +2033,22 @@ def BicomplexSpectralSequence(l):
EXAMPLES::
- sage: from sage.interfaces.kenzo import BicomplexSpectralSequence # optional - kenzo
+ sage: # optional - kenzo
+ sage: from sage.interfaces.kenzo import BicomplexSpectralSequence
sage: C1 = ChainComplex({1: matrix(ZZ, 0, 2, [])}, degree_of_differential=-1)
sage: C2 = ChainComplex({1: matrix(ZZ, 1, 2, [1, 0])},degree_of_differential=-1)
sage: C3 = ChainComplex({0: matrix(ZZ, 0,2 , [])},degree_of_differential=-1)
sage: M1 = Hom(C2,C1)({1: matrix(ZZ, 2, 2, [2, 0, 0, 2])})
sage: M2 = Hom(C3,C2)({0: matrix(ZZ, 1, 2, [2, 0])})
sage: l = [M1, M2]
- sage: E = BicomplexSpectralSequence(l) # optional - kenzo
- sage: E.group(2,0,1) # optional - kenzo
+ sage: E = BicomplexSpectralSequence(l)
+ sage: E.group(2,0,1)
Additive abelian group isomorphic to Z/2 + Z
- sage: E.table(3,0,2,0,2) # optional - kenzo
+ sage: E.table(3,0,2,0,2)
0 0 0
Z/2 + Z/4 0 0
0 0 Z
- sage: E.matrix(2,2,0) # optional - kenzo
+ sage: E.matrix(2,2,0)
[ 0 0]
[-4 0]
"""
diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py
index 7efc51038ea..01ae082cca6 100644
--- a/src/sage/interfaces/qepcad.py
+++ b/src/sage/interfaces/qepcad.py
@@ -833,7 +833,7 @@ def __init__(self, formula,
x^2 + x*y + 3*x*z + 2*y*z + 2*z^2 - x - z < 0, \
-2*x + 1 < 0, -x*y - x*z - 2*y*z - 2*z^2 + z < 0, \
x + 3*y + 3*z - 1 < 0]
- sage: qepcad(conds, memcells=2000000) # optional - qepcad
+ sage: qepcad(conds, memcells=3000000) # optional - qepcad
2 x - 1 > 0 /\ z > 0 /\ z - y < 0 /\ 3 z + 3 y + x - 1 < 0
"""
self._cell_cache = {}
diff --git a/src/sage/interfaces/scilab.py b/src/sage/interfaces/scilab.py
index 3229fd3d82c..a5cb2c1abb9 100644
--- a/src/sage/interfaces/scilab.py
+++ b/src/sage/interfaces/scilab.py
@@ -249,11 +249,12 @@ def set_seed(self, seed=None):
EXAMPLES::
- sage: from sage.interfaces.scilab import Scilab # optional - scilab
- sage: s = Scilab() # optional - scilab
- sage: s.set_seed(1) # optional - scilab
+ sage: # optional - scilab
+ sage: from sage.interfaces.scilab import Scilab
+ sage: s = Scilab()
+ sage: s.set_seed(1)
1
- sage: [s.rand() for i in range(5)] # optional - scilab
+ sage: [s.rand() for i in range(5)]
[
0.6040239,
@@ -535,7 +536,7 @@ def scilab_console():
EXAMPLES::
- sage: from sage.interfaces.scilab import scilab_console # optional - scilab
+ sage: from sage.interfaces.scilab import scilab_console # optional - scilab
sage: scilab_console() # optional - scilab; not tested
___________________________________________
scilab-5.0.3
@@ -569,7 +570,7 @@ def scilab_version():
EXAMPLES::
- sage: from sage.interfaces.scilab import scilab_version # optional - scilab
+ sage: from sage.interfaces.scilab import scilab_version # optional - scilab
sage: scilab_version() # optional - scilab
'scilab-...'
"""
diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py
index a2f48a996ed..0ef601e124c 100644
--- a/src/sage/knots/knotinfo.py
+++ b/src/sage/knots/knotinfo.py
@@ -97,26 +97,26 @@
If you have `SnapPy `__ installed inside
-Sage you can obtain an instance of :class:`~spherogram.links.links_base.Link`,
+Sage, you can obtain an instance of :class:`~spherogram.links.links_base.Link`,
too::
+ sage: # optional - snappy
sage: L6 = KnotInfo.L6a1_0
- sage: l6s = L6.link(snappy=True); l6s # optional - snappy
+ sage: l6s = L6.link(snappy=True); l6s
Plink failed to import tkinter.
-
- sage: type(l6s) # optional - snappy
+ sage: type(l6s)
sage: l6 = L6.link()
- sage: l6 == l6s.sage_link() # optional - snappy
+ sage: l6 == l6s.sage_link()
True
- sage: L6.link(L6.items.name, snappy=True) # optional - snappy
+ sage: L6.link(L6.items.name, snappy=True)
- sage: l6sn = _ # optional - snappy
- sage: l6s == l6sn # optional - snappy
+ sage: l6sn = _
+ sage: l6s == l6sn
False
- sage: l6m = l6.mirror_image() # optional - snappy
- sage: l6sn.sage_link().is_isotopic(l6m) # optional - snappy
+ sage: l6m = l6.mirror_image()
+ sage: l6sn.sage_link().is_isotopic(l6m)
True
But observe that the name conversion to SnapPy does not distinguish orientation
@@ -238,14 +238,16 @@
from enum import Enum
from sage.misc.cachefunc import cached_method
+from sage.misc.lazy_import import lazy_import
from sage.misc.sage_eval import sage_eval
from sage.structure.sage_object import SageObject
from sage.structure.unique_representation import UniqueRepresentation
from sage.rings.integer_ring import ZZ
-from sage.groups.braid import BraidGroup
from sage.knots.knot import Knots
from sage.databases.knotinfo_db import KnotInfoColumns, db
+lazy_import('sage.groups.braid', 'BraidGroup')
+
def eval_knotinfo(string, locals={}, to_tuple=True):
r"""
@@ -694,8 +696,7 @@ def braid_length(self):
@cached_method
def braid(self):
r"""
- Return the braid notation of self as an instance of :class:`~sage.groups.braid.Braid`.
-
+ Return the braid notation of ``self`` as an instance of :class:`~sage.groups.braid.Braid`.
EXAMPLES::
diff --git a/src/sage/libs/gap/libgap.pyx b/src/sage/libs/gap/libgap.pyx
index 3803f32b191..63400adab4c 100644
--- a/src/sage/libs/gap/libgap.pyx
+++ b/src/sage/libs/gap/libgap.pyx
@@ -46,7 +46,7 @@ equivalent::
sage: type(_)
- sage: libgap.eval('5/3 + 7*E(3)').sage()
+ sage: libgap.eval('5/3 + 7*E(3)').sage() # needs sage.rings.number_field
7*zeta3 + 5/3
sage: gens_of_group = libgap.AlternatingGroup(4).GeneratorsOfGroup()
@@ -265,7 +265,7 @@ class Gap(Parent):
sage: libgap.has_coerce_map_from(ZZ)
True
- sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y'])
+ sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y']) # needs sage.rings.number_field
True
"""
return True
@@ -362,11 +362,12 @@ class Gap(Parent):
We gracefully handle the case that the conversion fails (:trac:`18039`)::
- sage: F. = GF(9, modulus="first_lexicographic")
- sage: libgap(Matrix(F, [[a]]))
+ sage: F. = GF(9, modulus="first_lexicographic") # needs sage.rings.finite_rings
+ sage: libgap(Matrix(F, [[a]])) # needs sage.rings.finite_rings
Traceback (most recent call last):
...
- NotImplementedError: conversion of (Givaro) finite field element to GAP not implemented except for fields defined by Conway polynomials.
+ NotImplementedError: conversion of (Givaro) finite field element to GAP
+ not implemented except for fields defined by Conway polynomials.
"""
ring = M.base_ring()
try:
diff --git a/src/sage/libs/gap/sage.gaprc b/src/sage/libs/gap/sage.gaprc
index 39c878f2329..258db942a98 100644
--- a/src/sage/libs/gap/sage.gaprc
+++ b/src/sage/libs/gap/sage.gaprc
@@ -1,3 +1,31 @@
# This file is run by Sage when initializing libgap via GAP_Initialize, and may
# contain bug fixes/workarounds and/or any Sage-specific patches necessary for
# Sage's libgap interface.
+
+
+# Load the GAP packages that GAP itself tries to autoload in the
+# default configuration (see "PackagesToLoad" in lib/package.gi). The
+# combination of passing -A to gap and these LoadPackage statements
+# allows us to load the usual set of packages, but only if they are
+# installed. So most people will get exactly the default behavior,
+# but minimal installations won't throw warnings and fail tests.
+#
+# We also temporarily lower the InfoLevel of the InfoWarning class so
+# that e.g.,
+#
+# #I polycyclic package is not available. Check that the name is correct
+# #I and it is present in one of the GAP root directories (see '??RootPaths')
+#
+# is not output to the console.
+#
+_orig_warn_level := InfoLevel(InfoWarning);
+SetInfoLevel(InfoWarning, 0);
+
+_autoloads := [ "autpgrp", "alnuth", "crisp", "ctbllib", "factint", "fga",
+ "irredsol", "laguna", "polenta", "polycyclic", "resclasses",
+ "sophus", "tomlib" ];
+for p in _autoloads do
+ LoadPackage(p);
+od;
+
+SetInfoLevel(InfoWarning, _orig_warn_level);
diff --git a/src/sage/libs/gap/saved_workspace.py b/src/sage/libs/gap/saved_workspace.py
index 7636707f557..fdaf18f4644 100644
--- a/src/sage/libs/gap/saved_workspace.py
+++ b/src/sage/libs/gap/saved_workspace.py
@@ -8,7 +8,7 @@
import os
import glob
-from sage.env import GAP_LIB_DIR
+from sage.env import GAP_ROOT_PATHS
from sage.interfaces.gap_workspace import gap_workspace_file
@@ -31,7 +31,13 @@ def timestamp():
"""
libgap_dir = os.path.dirname(__file__)
libgap_files = glob.glob(os.path.join(libgap_dir, '*'))
- gap_packages = glob.glob(os.path.join(GAP_LIB_DIR, 'pkg', '*'))
+ gap_packages = []
+ for d in GAP_ROOT_PATHS.split(";"):
+ if d:
+ # If GAP_ROOT_PATHS begins or ends with a semicolon,
+ # we'll get one empty d.
+ gap_packages += glob.glob(os.path.join(d, 'pkg', '*'))
+
files = libgap_files + gap_packages
if len(files) == 0:
print('Unable to find LibGAP files.')
diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx
index d37fe84f029..8685dc08fa5 100644
--- a/src/sage/libs/gap/util.pyx
+++ b/src/sage/libs/gap/util.pyx
@@ -217,28 +217,29 @@ cdef initialize() noexcept:
# initialize GAP.
cdef char* argv[16]
argv[0] = "sage"
- argv[1] = "-l"
- s = str_to_bytes(sage.env.GAP_LIB_DIR + ";" + sage.env.GAP_SHARE_DIR, FS_ENCODING, "surrogateescape")
- argv[2] = s
-
- argv[3] = "-m"
- argv[4] = "64m"
-
- argv[5] = "-q" # no prompt!
- argv[6] = "-E" # don't use readline as this will interfere with Python
- argv[7] = "--nointeract" # Implies -T
- argv[8] = "-x" # set the "screen" width so that GAP is less likely to
- argv[9] = "4096" # insert newlines when printing objects
+ argv[1] = "-A"
+ argv[2] = "-l"
+ s = str_to_bytes(sage.env.GAP_ROOT_PATHS, FS_ENCODING, "surrogateescape")
+ argv[3] = s
+
+ argv[4] = "-m"
+ argv[5] = "64m"
+
+ argv[6] = "-q" # no prompt!
+ argv[7] = "-E" # don't use readline as this will interfere with Python
+ argv[8] = "--nointeract" # Implies -T
+ argv[9] = "-x" # set the "screen" width so that GAP is less likely to
+ argv[10] = "4096" # insert newlines when printing objects
# 4096 unfortunately is the hard-coded max, but should
# be long enough for most cases
- cdef int argc = 10 # argv[argc] must be NULL
+ cdef int argc = 11 # argv[argc] must be NULL
gap_mem = sage.env.SAGE_GAP_MEMORY
if gap_mem is not None:
argc += 2
- argv[10] = "-s"
+ argv[11] = "-s"
s1 = str_to_bytes(gap_mem, FS_ENCODING, "surrogateescape")
- argv[11] = s1
- argv[4] = s1
+ argv[12] = s1
+ argv[5] = s1
from sage.libs.gap.saved_workspace import workspace
workspace, workspace_is_up_to_date = workspace()
diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py
index dcae7d0c0fc..cbc36ea65b9 100644
--- a/src/sage/misc/package.py
+++ b/src/sage/misc/package.py
@@ -390,7 +390,7 @@ def _spkg_inst_dirs():
"""
Generator for the installation manifest directories as resolved paths.
- It yields first ``SAGE_SPKG_INST``, then ``SAGE_VENV_SPKG_INST``,
+ It yields first ``SAGE_LOCAL_SPKG_INST``, then ``SAGE_VENV_SPKG_INST``,
if defined; but it both resolve to the same directory, it only yields
one element.
@@ -402,7 +402,7 @@ def _spkg_inst_dirs():
"""
last_inst_dir = None
- for inst_dir in (sage.env.SAGE_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST):
+ for inst_dir in (sage.env.SAGE_LOCAL_SPKG_INST, sage.env.SAGE_VENV_SPKG_INST):
if inst_dir:
inst_dir = Path(inst_dir).resolve()
if inst_dir.is_dir() and inst_dir != last_inst_dir:
diff --git a/src/sage/modular/arithgroup/congroup_gamma0.py b/src/sage/modular/arithgroup/congroup_gamma0.py
index 5c57f12a511..aecf1582f98 100644
--- a/src/sage/modular/arithgroup/congroup_gamma0.py
+++ b/src/sage/modular/arithgroup/congroup_gamma0.py
@@ -390,13 +390,13 @@ def gamma_h_subgroups(self):
EXAMPLES::
sage: G = Gamma0(11)
- sage: G.gamma_h_subgroups()
+ sage: G.gamma_h_subgroups() # optional - gap_package_polycyclic
[Congruence Subgroup Gamma0(11),
Congruence Subgroup Gamma_H(11) with H generated by [3],
Congruence Subgroup Gamma_H(11) with H generated by [10],
Congruence Subgroup Gamma1(11)]
sage: G = Gamma0(12)
- sage: G.gamma_h_subgroups()
+ sage: G.gamma_h_subgroups() # optional - gap_package_polycyclic
[Congruence Subgroup Gamma0(12),
Congruence Subgroup Gamma_H(12) with H generated by [7],
Congruence Subgroup Gamma_H(12) with H generated by [11],
diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py
index 60df7b2add9..9310ecae804 100644
--- a/src/sage/modular/arithgroup/congroup_gammaH.py
+++ b/src/sage/modular/arithgroup/congroup_gammaH.py
@@ -352,7 +352,7 @@ def __richcmp__(self, other, op):
sage: Gamma0(2) == Gamma1(2)
True
- sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())]
+ sage: [x._list_of_elements_in_H() for x in sorted(Gamma0(24).gamma_h_subgroups())] # optional - gap_package_polycyclic
[[1],
[1, 5],
[1, 7],
@@ -1055,7 +1055,7 @@ def index(self):
EXAMPLES::
- sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()]
+ sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()] # optional - gap_package_polycyclic
[72, 144, 144, 144, 144, 288, 288, 288, 288, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 288, 576, 1152]
"""
from .all import Gamma1
@@ -1068,7 +1068,7 @@ def nu2(self):
EXAMPLES::
- sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]
+ sage: [H.nu2() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] # optional - gap_package_polycyclic
[1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]
sage: GammaH(33,[2]).nu2()
0
@@ -1095,7 +1095,7 @@ def nu3(self):
EXAMPLES::
- sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()]
+ sage: [H.nu3() for n in [1..10] for H in Gamma0(n).gamma_h_subgroups()] # optional - gap_package_polycyclic
[1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
sage: GammaH(33,[2]).nu3()
0
@@ -1279,7 +1279,7 @@ def image_mod_n(self):
TESTS::
- sage: for n in [2..20]:
+ sage: for n in [2..20]: # optional - gap_package_polycyclic
....: for g in Gamma0(n).gamma_h_subgroups():
....: G = g.image_mod_n()
....: assert G.order() == Gamma(n).index() / g.index()
diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp
index 5c912b46ca9..209391676de 100644
--- a/src/sage/modular/arithgroup/farey.cpp
+++ b/src/sage/modular/arithgroup/farey.cpp
@@ -29,7 +29,7 @@
#include
#include "farey.hpp"
-#include "farey_symbol.h"
+#include "sage/modular/arithgroup/farey_symbol.h"
using namespace std;
diff --git a/src/sage/numerical/mip.pxd b/src/sage/numerical/mip.pxd
index 612324a5424..e73cc408d09 100644
--- a/src/sage/numerical/mip.pxd
+++ b/src/sage/numerical/mip.pxd
@@ -3,8 +3,11 @@ cdef extern from *:
cdef int REAL = -1
cdef int INTEGER = 0
+from sage.sets.family cimport FiniteFamily
from sage.structure.sage_object cimport SageObject
from sage.numerical.backends.generic_backend cimport GenericBackend
+
+
cdef class MIPVariable
@@ -26,10 +29,8 @@ cdef class MixedIntegerLinearProgram(SageObject):
cpdef sum(self, L) noexcept
-cdef class MIPVariable(SageObject):
+cdef class MIPVariable(FiniteFamily):
cdef MixedIntegerLinearProgram _p
- cdef dict _dict
- cdef bint _dynamic_indices
cdef int _vtype
cdef str _name
cdef object _lower_bound
diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx
index 1e6e6caa7bd..803724bf29b 100644
--- a/src/sage/numerical/mip.pyx
+++ b/src/sage/numerical/mip.pyx
@@ -3237,7 +3237,7 @@ class MIPSolverException(RuntimeError):
pass
-cdef class MIPVariable(SageObject):
+cdef class MIPVariable(FiniteFamily):
r"""
``MIPVariable`` is a variable used by the class
``MixedIntegerLinearProgram``.
@@ -3286,17 +3286,16 @@ cdef class MIPVariable(SageObject):
MIPVariable with 0 real components, >= 0
"""
- self._dict = {}
+ super().__init__({})
self._p = mip
self._vtype = vtype
self._lower_bound = lower_bound
self._upper_bound = upper_bound
self._name = name
- self._dynamic_indices = True
if indices is not None:
for i in indices:
self[i] # creates component
- self._dynamic_indices = False
+ self._keys = indices
def __copy__(self):
r"""
@@ -3398,9 +3397,9 @@ cdef class MIPVariable(SageObject):
"""
cdef int j
- if i in self._dict:
- return self._dict[i]
- if not self._dynamic_indices:
+ if i in self._dictionary:
+ return self._dictionary[i]
+ if self._keys is not None:
raise IndexError("{} does not index a component of {}".format(i, self))
zero = self._p._backend.zero()
name = self._name + "[" + str(i) + "]" if self._name else None
@@ -3415,7 +3414,7 @@ cdef class MIPVariable(SageObject):
name=name)
v = self._p.linear_functions_parent()({j : 1})
self._p._variables[v] = j
- self._dict[i] = v
+ self._dictionary[i] = v
return v
def copy_for_mip(self, mip):
@@ -3461,8 +3460,8 @@ cdef class MIPVariable(SageObject):
"""
cdef MIPVariable cp = type(self)(mip, self._vtype, self._name,
self._lower_bound, self._upper_bound)
- cp._dict = copy(self._dict)
- cp._dynamic_indices = self._dynamic_indices
+ cp._dictionary = copy(self._dictionary)
+ cp._keys = self._keys
return cp
def set_min(self, min):
@@ -3501,7 +3500,7 @@ cdef class MIPVariable(SageObject):
"""
self._lower_bound = min
- for v in self._dict.values():
+ for v in self._dictionary.values():
self._p.set_min(v,min)
def set_max(self, max):
@@ -3537,7 +3536,7 @@ cdef class MIPVariable(SageObject):
True
"""
self._upper_bound = max
- for v in self._dict.values():
+ for v in self._dictionary.values():
self._p.set_max(v,max)
def _repr_(self):
@@ -3570,9 +3569,9 @@ cdef class MIPVariable(SageObject):
"""
s = 'MIPVariable{0} with {1} {2} component{3}'.format(
" " + self._name if self._name else "",
- len(self._dict),
+ len(self._dictionary),
{0:"binary", -1:"real", 1:"integer"}[self._vtype],
- "s" if len(self._dict) != 1 else "")
+ "s" if len(self._dictionary) != 1 else "")
if (self._vtype != 0) and (self._lower_bound is not None):
s += ', >= {0}'.format(self._lower_bound)
if (self._vtype != 0) and (self._upper_bound is not None):
@@ -3591,11 +3590,11 @@ cdef class MIPVariable(SageObject):
sage: sorted(v.keys())
[0, 1]
"""
- return self._dict.keys()
+ return self._dictionary.keys()
def items(self):
r"""
- Return the pairs (keys,value) contained in the dictionary.
+ Return the pairs (keys, value) contained in the dictionary.
EXAMPLES::
@@ -3605,7 +3604,7 @@ cdef class MIPVariable(SageObject):
sage: sorted(v.items())
[(0, x_0), (1, x_1)]
"""
- return self._dict.items()
+ return self._dictionary.items()
def values(self):
r"""
@@ -3619,7 +3618,7 @@ cdef class MIPVariable(SageObject):
sage: sorted(v.values(), key=str)
[x_0, x_1]
"""
- return self._dict.values()
+ return self._dictionary.values()
def mip(self):
r"""
diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx
index 7588cde2e27..8f69ed49725 100644
--- a/src/sage/plot/plot3d/base.pyx
+++ b/src/sage/plot/plot3d/base.pyx
@@ -285,13 +285,8 @@ cdef class Graphics3d(SageObject):
tachyon.png.save_as(preview_png)
else:
# Java needs absolute paths
- # On cygwin, they should be native ones
scene_native = scene_zip
- if sys.platform == 'cygwin':
- import cygwin
- scene_native = cygwin.cygpath(scene_native, 'w')
-
script = '''set defaultdirectory "{0}"\nscript SCRIPT\n'''.format(scene_native)
jdata.export_image(targetfile=preview_png, datafile=script,
image_type="PNG",
diff --git a/src/sage/quadratic_forms/quadratic_form__automorphisms.py b/src/sage/quadratic_forms/quadratic_form__automorphisms.py
index 89b2c079478..175f447bbea 100644
--- a/src/sage/quadratic_forms/quadratic_form__automorphisms.py
+++ b/src/sage/quadratic_forms/quadratic_form__automorphisms.py
@@ -349,7 +349,7 @@ def automorphisms(self):
48
sage: 2^3 * factorial(3)
48
- sage: len(Q.automorphisms())
+ sage: len(Q.automorphisms()) # needs sage.libs.gap
48
::
@@ -357,14 +357,14 @@ def automorphisms(self):
sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
sage: Q.number_of_automorphisms()
16
- sage: aut = Q.automorphisms()
- sage: len(aut)
+ sage: aut = Q.automorphisms() # needs sage.libs.gap
+ sage: len(aut) # needs sage.libs.gap
16
- sage: all(Q(M) == Q for M in aut)
+ sage: all(Q(M) == Q for M in aut) # needs sage.libs.gap
True
sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3])
- sage: sorted(Q.automorphisms())
+ sage: sorted(Q.automorphisms()) # needs sage.libs.gap
[
[-1 0 0] [1 0 0]
[ 0 -1 0] [0 1 0]
diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py
index 10ccdc0c2c8..ba17b9244b4 100644
--- a/src/sage/repl/rich_output/backend_ipython.py
+++ b/src/sage/repl/rich_output/backend_ipython.py
@@ -419,13 +419,6 @@ def threejs_offline_scripts(self):
script = os.path.join(THREEJS_DIR, '{}/three.min.js'.format(_required_threejs_version()))
- if sys.platform == 'cygwin':
- import cygwin
-
- def normpath(p):
- return 'file:///' + cygwin.cygpath(p, 'w').replace('\\', '/')
- script = normpath(script)
-
return '\n'.format(script)
diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py
index 0bb4ce21f4b..eb35543a20e 100644
--- a/src/sage/rings/finite_rings/integer_mod_ring.py
+++ b/src/sage/rings/finite_rings/integer_mod_ring.py
@@ -617,13 +617,13 @@ def multiplicative_subgroups(self):
EXAMPLES::
sage: # needs sage.groups
- sage: Integers(5).multiplicative_subgroups()
+ sage: Integers(5).multiplicative_subgroups() # optional - gap_package_polycyclic
((2,), (4,), ())
- sage: Integers(15).multiplicative_subgroups()
+ sage: Integers(15).multiplicative_subgroups() # optional - gap_package_polycyclic
((11, 7), (11, 4), (2,), (11,), (14,), (7,), (4,), ())
- sage: Integers(2).multiplicative_subgroups()
+ sage: Integers(2).multiplicative_subgroups() # optional - gap_package_polycyclic
((),)
- sage: len(Integers(341).multiplicative_subgroups())
+ sage: len(Integers(341).multiplicative_subgroups()) # optional - gap_package_polycyclic
80
TESTS::
@@ -632,7 +632,7 @@ def multiplicative_subgroups(self):
((),)
sage: IntegerModRing(2).multiplicative_subgroups() # needs sage.groups
((),)
- sage: IntegerModRing(3).multiplicative_subgroups() # needs sage.groups
+ sage: IntegerModRing(3).multiplicative_subgroups() # needs sage.groups # optional - gap_package_polycyclic
((2,), ())
"""
return tuple(tuple(g.value() for g in H.gens())
diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py
index 2e6b876e541..9ea602dc1aa 100644
--- a/src/sage/rings/number_field/galois_group.py
+++ b/src/sage/rings/number_field/galois_group.py
@@ -10,9 +10,9 @@
"""
from sage.structure.sage_object import SageObject
-from sage.groups.galois_group import _alg_key, GaloisGroup_perm, GaloisSubgroup_perm
+from sage.groups.galois_group import _alg_key
+from sage.groups.galois_group_perm import GaloisGroup_perm, GaloisSubgroup_perm
from sage.groups.perm_gps.permgroup import standardize_generator
-
from sage.groups.perm_gps.permgroup_element import PermutationGroupElement
from sage.misc.superseded import deprecation
from sage.misc.cachefunc import cached_method
diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py
index e34606e085b..77048cd3b79 100644
--- a/src/sage/rings/number_field/number_field.py
+++ b/src/sage/rings/number_field/number_field.py
@@ -371,18 +371,20 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None,
One can embed into any other field::
- sage: K. = NumberField(x^3-2, embedding=CC.gen()-0.6)
+ sage: K. = NumberField(x^3 - 2, embedding=CC.gen() - 0.6)
sage: CC(a)
-0.629960524947436 + 1.09112363597172*I
- sage: L = Qp(5) # needs sage.rings.padics
+
+ sage: # needs sage.rings.padics
+ sage: L = Qp(5)
sage: f = polygen(L)^3 - 2
- sage: K. = NumberField(x^3-2, embedding=f.roots()[0][0])
+ sage: K. = NumberField(x^3 - 2, embedding=f.roots()[0][0])
sage: a + L(1)
4 + 2*5^2 + 2*5^3 + 3*5^4 + 5^5 + 4*5^6 + 2*5^8 + 3*5^9 + 4*5^12
+ 4*5^14 + 4*5^15 + 3*5^16 + 5^17 + 5^18 + 2*5^19 + O(5^20)
- sage: L. = NumberField(x^6-x^2+1/10, embedding=1)
- sage: K. = NumberField(x^3-x+1/10, embedding=b^2)
- sage: a+b
+ sage: L. = NumberField(x^6 - x^2 + 1/10, embedding=1)
+ sage: K. = NumberField(x^3 - x + 1/10, embedding=b^2)
+ sage: a + b
b^2 + b
sage: CC(a) == CC(b)^2
True
@@ -409,7 +411,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None,
Note that the codomain of the embedding must be ``QQbar`` or ``AA`` for this to work
(see :trac:`20184`)::
- sage: N. = NumberField(x^3 + 2,embedding=1)
+ sage: N. = NumberField(x^3 + 2, embedding=1)
sage: 1 < g
False
sage: g > 1
@@ -493,7 +495,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None,
The following has been fixed in :trac:`8800`::
sage: P. = QQ[]
- sage: K. = NumberField(x^3 - 5,embedding=0)
+ sage: K. = NumberField(x^3 - 5, embedding=0)
sage: L. = K.extension(x^2 + a)
sage: F, R = L.construction()
sage: F(R) == L # indirect doctest
@@ -1561,7 +1563,7 @@ def construction(self):
::
sage: P. = QQ[]
- sage: K. = NumberField(x^3-5,embedding=0)
+ sage: K. = NumberField(x^3-5, embedding=0)
sage: L. = K.extension(x^2+a)
sage: a*b
a*b
@@ -3391,23 +3393,23 @@ def dirichlet_group(self):
sage: K. = NumberField(x^3 + x^2 - 36*x - 4)
sage: K.conductor()
109
- sage: K.dirichlet_group()
+ sage: K.dirichlet_group() # optional - gap_package_polycyclic
[Dirichlet character modulo 109 of conductor 1 mapping 6 |--> 1,
Dirichlet character modulo 109 of conductor 109 mapping 6 |--> zeta3,
Dirichlet character modulo 109 of conductor 109 mapping 6 |--> -zeta3 - 1]
sage: K = CyclotomicField(44)
sage: L = K.subfields(5)[0][0]
- sage: X = L.dirichlet_group()
- sage: X
+ sage: X = L.dirichlet_group() # optional - gap_package_polycyclic
+ sage: X # optional - gap_package_polycyclic
[Dirichlet character modulo 11 of conductor 1 mapping 2 |--> 1,
Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5,
Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^2,
Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3,
Dirichlet character modulo 11 of conductor 11 mapping 2 |--> -zeta5^3 - zeta5^2 - zeta5 - 1]
- sage: X[4]^2
+ sage: X[4]^2 # optional - gap_package_polycyclic
Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3
- sage: X[4]^2 in X
+ sage: X[4]^2 in X # optional - gap_package_polycyclic
True
"""
# todo : turn this into an abelian group rather than a list.
@@ -8224,7 +8226,7 @@ def _coerce_from_other_number_field(self, x):
The following was fixed in :trac:`8800`::
sage: P. = QQ[]
- sage: K. = NumberField(x^3 - 5,embedding=0)
+ sage: K. = NumberField(x^3 - 5, embedding=0)
sage: L. = K.extension(x^2 + a)
sage: F,R = L.construction()
sage: F(R) == L #indirect doctest
@@ -12711,12 +12713,12 @@ def _splitting_classes_gens_(K, m, d):
sage: L = K.subfields(20)[0][0]
sage: L.conductor()
101
- sage: _splitting_classes_gens_(L,101,20) # needs sage.libs.gap
+ sage: _splitting_classes_gens_(L,101,20) # needs sage.libs.gap # optional - gap_package_polycyclic
[95]
sage: K = CyclotomicField(44)
sage: L = K.subfields(4)[0][0]
- sage: _splitting_classes_gens_(L,44,4) # needs sage.libs.gap
+ sage: _splitting_classes_gens_(L,44,4) # needs sage.libs.gap # optional - gap_package_polycyclic
[37]
sage: K = CyclotomicField(44)
@@ -12728,7 +12730,7 @@ def _splitting_classes_gens_(K, m, d):
with zeta44_0 = 3.837971894457990?
sage: L.conductor()
11
- sage: _splitting_classes_gens_(L,11,5) # needs sage.libs.gap
+ sage: _splitting_classes_gens_(L,11,5) # needs sage.libs.gap # optional - gap_package_polycyclic
[10]
"""
diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py
index 19c9e95ed39..28105221ac7 100644
--- a/src/sage/rings/number_field/number_field_ideal.py
+++ b/src/sage/rings/number_field/number_field_ideal.py
@@ -1002,6 +1002,7 @@ def is_maximal(self):
EXAMPLES::
+ sage: x = polygen(ZZ)
sage: K. = NumberField(x^3 + 3); K
Number Field in a with defining polynomial x^3 + 3
sage: K.ideal(5).is_maximal()
@@ -1017,6 +1018,7 @@ def is_prime(self):
EXAMPLES::
+ sage: x = polygen(ZZ)
sage: K. = NumberField(x^2 - 17); K
Number Field in a with defining polynomial x^2 - 17
sage: K.ideal(5).is_prime() # inert prime
@@ -1031,7 +1033,7 @@ def is_prime(self):
Check that we do not factor the norm of the ideal, this used
to take half an hour, see :trac:`33360`::
- sage: K. = NumberField([x^2-2,x^2-3,x^2-5])
+ sage: K. = NumberField([x^2 - 2, x^2 - 3, x^2 - 5])
sage: t = (((-2611940*c + 1925290/7653)*b - 1537130/7653*c
....: + 10130950)*a + (1343014/7653*c - 8349770)*b
....: + 6477058*c - 2801449990/4002519)
@@ -1112,6 +1114,7 @@ def _cache_bnfisprincipal(self, proof=None, gens=False):
Check that no warnings are triggered from PARI/GP (see :trac:`30801`)::
+ sage: x = polygen(ZZ)
sage: K. = NumberField(x^2 - x + 112941801)
sage: I = K.ideal((112941823, a + 49942513))
sage: I.is_principal()
@@ -1494,7 +1497,7 @@ def decomposition_group(self):
EXAMPLES::
- sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group()
+ sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() # needs sage.groups
Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23)
"""
return self.number_field().galois_group().decomposition_group(self)
@@ -1510,9 +1513,9 @@ def ramification_group(self, v):
EXAMPLES::
- sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0)
+ sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) # needs sage.groups
Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23)
- sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1)
+ sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) # needs sage.groups
Subgroup generated by [()] of (Galois group 2T1 (S2) with order 2 of x^2 + 23)
"""
@@ -1528,7 +1531,7 @@ def inertia_group(self):
EXAMPLES::
- sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group()
+ sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() # needs sage.groups
Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23)
"""
return self.ramification_group(0)
@@ -1594,7 +1597,7 @@ def artin_symbol(self):
EXAMPLES::
- sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol()
+ sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol() # needs sage.groups
(1,2)
"""
return self.number_field().galois_group().artin_symbol(self)
diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py
index af44569f167..47ee18456f7 100644
--- a/src/sage/rings/number_field/totallyreal_rel.py
+++ b/src/sage/rings/number_field/totallyreal_rel.py
@@ -51,7 +51,7 @@
sage: [ f[0] for f in ls ]
[725, 1125, 1600, 2000, 2225, 2525, 3600, 4225, 4400, 4525, 5125, 5225, 5725, 6125, 7225, 7600, 7625, 8000, 8525, 8725, 9225]
- sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls]
+ sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls] # needs sage.groups
[False, True, True, True, False, False, True, True, False, False, False, False, False, True, True, False, False, True, False, False, False]
Eight out of 21 such fields are Galois (with Galois group `C_4`
diff --git a/src/sage/sets/family.pxd b/src/sage/sets/family.pxd
new file mode 100644
index 00000000000..f5d8f755ecc
--- /dev/null
+++ b/src/sage/sets/family.pxd
@@ -0,0 +1,11 @@
+from sage.structure.parent cimport Parent
+
+
+cdef class AbstractFamily(Parent):
+ cdef public __custom_name
+ cdef dict __dict__ # enables Python attributes as needed for EnumeratedSets()
+
+
+cdef class FiniteFamily(AbstractFamily):
+ cdef public dict _dictionary
+ cdef public object _keys
diff --git a/src/sage/sets/family.py b/src/sage/sets/family.pyx
similarity index 96%
rename from src/sage/sets/family.py
rename to src/sage/sets/family.pyx
index f3c3d0a7556..d1c102261a8 100644
--- a/src/sage/sets/family.py
+++ b/src/sage/sets/family.pyx
@@ -22,10 +22,16 @@
Category of finite enumerated sets
"""
-# ****************************************************************************
-# Copyright (C) 2008 Nicolas Thiery ,
-# Mike Hansen ,
-# Florent Hivert
+# *****************************************************************************
+# Copyright (C) 2008-2017 Nicolas Thiery
+# 2008-2009 Mike Hansen
+# 2008-2010 Florent Hivert
+# 2013-2021 Travis Scrimshaw
+# 2014 Nathann Cohen
+# 2017 Erik M. Bray
+# 2018 Frédéric Chapoton
+# 2019 Markus Wageringel
+# 2022-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
@@ -38,19 +44,18 @@
from pprint import pformat, saferepr
from collections.abc import Iterable
-from sage.misc.abstract_method import abstract_method
-from sage.misc.cachefunc import cached_method
-from sage.structure.parent import Parent
from sage.categories.enumerated_sets import EnumeratedSets
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
-from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
-from sage.misc.lazy_import import lazy_import
-from sage.rings.integer import Integer
+from sage.misc.cachefunc import cached_method
from sage.misc.call import AttrCallObject
-from sage.sets.non_negative_integers import NonNegativeIntegers
+from sage.misc.lazy_import import LazyImport
from sage.rings.infinity import Infinity
-lazy_import('sage.combinat.combinat', 'CombinatorialClass')
+from sage.rings.integer import Integer
+from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
+from sage.sets.non_negative_integers import NonNegativeIntegers
+
+CombinatorialClass = LazyImport('sage.combinat.combinat', 'CombinatorialClass')
def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=False, name=None):
@@ -396,8 +401,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa
return TrivialFamily(indices)
if isinstance(indices, (FiniteFamily, LazyFamily, TrivialFamily)):
return indices
- if (indices in EnumeratedSets()
- or isinstance(indices, CombinatorialClass)):
+ if indices in EnumeratedSets():
return EnumeratedFamily(indices)
if isinstance(indices, Iterable):
return TrivialFamily(indices)
@@ -418,7 +422,7 @@ def Family(indices, function=None, hidden_keys=[], hidden_function=None, lazy=Fa
keys=indices)
-class AbstractFamily(Parent):
+cdef class AbstractFamily(Parent):
"""
The abstract class for family
@@ -436,7 +440,6 @@ def hidden_keys(self):
"""
return []
- @abstract_method
def keys(self):
"""
Return the keys of the family.
@@ -447,8 +450,8 @@ def keys(self):
sage: sorted(f.keys())
[3, 4, 7]
"""
+ raise NotImplementedError
- @abstract_method(optional=True)
def values(self):
"""
Return the elements (values) of this family.
@@ -459,6 +462,7 @@ def values(self):
sage: sorted(f.values())
['aa', 'bb', 'cc']
"""
+ raise NotImplementedError
def items(self):
"""
@@ -535,7 +539,8 @@ def inverse_family(self):
return Family({self[k]: k for k in self.keys()})
-class FiniteFamily(AbstractFamily):
+
+cdef class FiniteFamily(AbstractFamily):
r"""
A :class:`FiniteFamily` is an associative container which models a finite
family `(f_i)_{i \in I}`. Its elements `f_i` are therefore its
@@ -712,8 +717,8 @@ def __eq__(self, other):
False
"""
return (isinstance(other, self.__class__) and
- self._keys == other._keys and
- self._dictionary == other._dictionary)
+ self._keys == ( other)._keys and
+ self._dictionary == ( other)._dictionary)
def _repr_(self):
"""
@@ -869,15 +874,17 @@ def __getitem__(self, i):
...
KeyError
"""
- if i in self._dictionary:
- return self._dictionary[i]
-
- if i not in self.hidden_dictionary:
- if i not in self._hidden_keys:
- raise KeyError
- self.hidden_dictionary[i] = self.hidden_function(i)
-
- return self.hidden_dictionary[i]
+ try:
+ return FiniteFamily.__getitem__(self, i)
+ except KeyError:
+ try:
+ return self.hidden_dictionary[i]
+ except KeyError:
+ if i not in self._hidden_keys:
+ raise KeyError
+ v = self.hidden_function(i)
+ self.hidden_dictionary[i] = v
+ return v
def hidden_keys(self):
"""
@@ -902,11 +909,11 @@ def __getstate__(self):
"""
from sage.misc.fpickle import pickle_function
f = pickle_function(self.hidden_function)
- return {'dictionary': self._dictionary,
- 'hidden_keys': self._hidden_keys,
- 'hidden_dictionary': self.hidden_dictionary,
- 'hidden_function': f,
- 'keys': self._keys}
+ state = super().__getstate__()
+ state.update({'hidden_keys': self._hidden_keys,
+ 'hidden_dictionary': self.hidden_dictionary,
+ 'hidden_function': f})
+ return state
def __setstate__(self, d):
"""
@@ -922,7 +929,7 @@ def __setstate__(self, d):
6
"""
hidden_function = d['hidden_function']
- if isinstance(hidden_function, str):
+ if isinstance(hidden_function, (str, bytes)):
# Let's assume that hidden_function is an unpickled function.
from sage.misc.fpickle import unpickle_function
hidden_function = unpickle_function(hidden_function)
@@ -1074,7 +1081,7 @@ def _repr_(self):
"""
if self.function_name is not None:
name = self.function_name + "(i)"
- elif isinstance(self.function, type(lambda x: 1)):
+ elif isinstance(self.function, types.LambdaType):
name = self.function.__name__
name = name + "(i)"
else:
diff --git a/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py b/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py
index e54352bafe9..5f8cd284b52 100644
--- a/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py
+++ b/src/sage/tests/books/judson-abstract-algebra/cyclic-sage.py
@@ -252,19 +252,19 @@
~~~~~~~~~~~~~~~~~~~~~~ ::
- sage: H = G.subgroup([a^2])
+ sage: H = G.subgroup([a^2]) # optional - gap_package_polycyclic
sage: H.order()
7
~~~~~~~~~~~~~~~~~~~~~~ ::
- sage: K = G.subgroup([a^12])
+ sage: K = G.subgroup([a^12]) # optional - gap_package_polycyclic
sage: K.order()
7
~~~~~~~~~~~~~~~~~~~~~~ ::
- sage: allsg = G.subgroups(); allsg
+ sage: allsg = G.subgroups(); allsg # optional - gap_package_polycyclic
[Multiplicative Abelian subgroup isomorphic to C2 x C7 generated by {a},
Multiplicative Abelian subgroup isomorphic to C7 generated by {a^2},
Multiplicative Abelian subgroup isomorphic to C2 generated by {a^7},
@@ -272,8 +272,8 @@
~~~~~~~~~~~~~~~~~~~~~~ ::
- sage: sub = allsg[2]
- sage: sub.order()
+ sage: sub = allsg[2] # optional - gap_package_polycyclic
+ sage: sub.order() # optional - gap_package_polycyclic
2
~~~~~~~~~~~~~~~~~~~~~~ ::
diff --git a/src/sage/tests/gap_packages.py b/src/sage/tests/gap_packages.py
index b13bba24a83..cf02fa5cf6e 100644
--- a/src/sage/tests/gap_packages.py
+++ b/src/sage/tests/gap_packages.py
@@ -10,11 +10,11 @@
Status Package GAP Output
+--------+---------+------------+
- sage: test_packages(['atlasrep', 'tomlib'])
+ sage: test_packages(['primgrp', 'smallgrp'])
Status Package GAP Output
+--------+----------+------------+
- atlasrep true
- tomlib true
+ primgrp true
+ smallgrp true
"""
import os
@@ -116,6 +116,16 @@ def all_installed_packages(ignore_dot_gap=False, gap=None):
else:
paths = [str(p) for p in gap('GAPInfo.RootPaths')]
+ # When GAP_ROOT_PATHS begins or ends with a semicolon (to append
+ # or prepend to the default list), the list of "gap" root paths
+ # will sometimes contain duplicates while the list for libgap will
+ # not. I don't know why this is: the appending/prepending does
+ # work as intended, even for libgap, so the issue is not that
+ # appending/prepending don't work at all for libgap. For lack of a
+ # better idea, we deduplicate here to avoid listing the same
+ # packages twice for the non-lib "gap" interface.
+ paths = set(paths)
+
packages = []
for path in paths:
if ignore_dot_gap and path.endswith('/.gap/'):
diff --git a/src/sage/topology/simplicial_complex_morphism.py b/src/sage/topology/simplicial_complex_morphism.py
index 3d4fc063198..0030c4edee8 100644
--- a/src/sage/topology/simplicial_complex_morphism.py
+++ b/src/sage/topology/simplicial_complex_morphism.py
@@ -244,20 +244,43 @@ def __call__(self, x, orientation=False):
(0, 1)
sage: g(Simplex([0,1]), orientation=True) # needs sage.modules
((0, 1), -1)
+
+ TESTS:
+
+ Test that the problem in :issue:`36849` has been fixed::
+
+ sage: S = SimplicialComplex([[1,2]],is_mutable=False).barycentric_subdivision()
+ sage: T = SimplicialComplex([[1,2],[2,3],[1,3]],is_mutable=False).barycentric_subdivision()
+ sage: f = {x[0]:x[0] for x in S.cells()[0]}
+ sage: H = Hom(S,T)
+ sage: z = H(f)
+ sage: z.associated_chain_complex_morphism()
+ Chain complex morphism:
+ From: Chain complex with at most 2 nonzero terms over Integer Ring
+ To: Chain complex with at most 2 nonzero terms over Integer Ring
"""
dim = self.domain().dimension()
if not isinstance(x, Simplex) or x.dimension() > dim or x not in self.domain().faces()[x.dimension()]:
raise ValueError("x must be a simplex of the source of f")
tup = x.tuple()
- fx = []
- for j in tup:
- fx.append(self._vertex_dictionary[j])
+ fx = [self._vertex_dictionary[j] for j in tup]
if orientation:
from sage.algebras.steenrod.steenrod_algebra_misc import convert_perm
from sage.combinat.permutation import Permutation
if len(set(fx)) == len(tup):
- oriented = Permutation(convert_perm(fx)).signature()
+ # We need to compare the image simplex, as given in
+ # the order specified by self, with its orientation in
+ # the codomain.
+ image = Simplex(set(fx))
+ Y_faces = self.codomain()._n_cells_sorted(image.dimension())
+ idx = Y_faces.index(image)
+ actual_image = Y_faces[idx]
+ # The signature of the permutation specified by self:
+ sign_image = Permutation(convert_perm(fx)).signature()
+ # The signature of the permutation of the simplex in the domain:
+ sign_simplex = Permutation(convert_perm(actual_image)).signature()
+ oriented = sign_image * sign_simplex
else:
oriented = 1
return (Simplex(set(fx)), oriented)
diff --git a/src/sage/version.py b/src/sage/version.py
index 92ef577e70a..30dfeeace6a 100644
--- a/src/sage/version.py
+++ b/src/sage/version.py
@@ -1,5 +1,5 @@
# Sage version information for Python scripts
# This file is auto-generated by the sage-update-version script, do not edit!
-version = '10.3.beta2'
-date = '2023-12-13'
-banner = 'SageMath version 10.3.beta2, Release Date: 2023-12-13'
+version = '10.3.beta3'
+date = '2023-12-18'
+banner = 'SageMath version 10.3.beta3, Release Date: 2023-12-18'
diff --git a/tox.ini b/tox.ini
index 6c928aa2623..4415cab94c2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -206,6 +206,7 @@ setenv =
#
# https://hub.docker.com/_/ubuntu?tab=description
# as of 2023-05, latest=jammy=22.04, rolling=lunar=23.04, devel=mantic=23.10
+ # ubuntu-focal does not have libgap-dev
#
ubuntu: SYSTEM=debian
ubuntu: BASE_IMAGE=ubuntu
@@ -221,6 +222,7 @@ setenv =
ubuntu-bionic: BASE_TAG=bionic
ubuntu-bionic: IGNORE_MISSING_SYSTEM_PACKAGES=yes
ubuntu-focal: BASE_TAG=focal
+ ubuntu-focal: IGNORE_MISSING_SYSTEM_PACKAGES=yes
ubuntu-jammy: BASE_TAG=jammy
ubuntu-lunar: BASE_TAG=lunar
ubuntu-mantic: BASE_TAG=mantic